diff --git a/DESCRIPTION b/DESCRIPTION
index 3396705b..ab68ccb0 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: malariasimulation
Title: An individual based model for malaria
-Version: 1.6.0
+Version: 1.6.1
Authors@R: c(
person(
given = "Giovanni",
@@ -70,12 +70,12 @@ Remotes:
mrc-ide/malariaEquilibrium,
mrc-ide/individual
Imports:
- individual (>= 0.1.7),
+ individual (>= 0.1.16),
malariaEquilibrium (>= 1.0.1),
Rcpp,
statmod,
MASS,
- dqrng,
+ dqrng (>= 0.4),
sitmo,
BH,
R6,
@@ -91,7 +91,7 @@ Suggests:
ggplot2,
covr,
mgcv
-RoxygenNote: 7.2.3
+RoxygenNote: 7.3.1
Roxygen: list(markdown = TRUE)
LinkingTo:
Rcpp,
diff --git a/NAMESPACE b/NAMESPACE
index 4fd1b1da..4ba1e15d 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -18,6 +18,7 @@ export(rtss_profile)
export(run_metapop_simulation)
export(run_simulation)
export(run_simulation_with_repetitions)
+export(set_antimalarial_resistance)
export(set_bednets)
export(set_carrying_capacity)
export(set_clinical_treatment)
diff --git a/NEWS.md b/NEWS.md
index 826bf0d0..860567da 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,10 @@
+# malariasimulation 1.6.1 (wip)
+
+ * Fix bug in competing hazards between mass and EPI vaccines. Where individuals
+ can be enrolled onto both strategies if applied on the same timestep.
+ * Fix bug with min_wait. Min wait was working off of the final primary dose. It
+ now works of of the first dose.
+
# malariasimulation 1.6.0
* Fix MDA bug where undetectable asymptomatics are treated
diff --git a/R/RcppExports.R b/R/RcppExports.R
index 73a51dd0..ad658f2c 100644
--- a/R/RcppExports.R
+++ b/R/RcppExports.R
@@ -9,6 +9,14 @@ adult_mosquito_model_update <- function(model, mu, foim, susceptible, f) {
invisible(.Call(`_malariasimulation_adult_mosquito_model_update`, model, mu, foim, susceptible, f))
}
+adult_mosquito_model_save_state <- function(model) {
+ .Call(`_malariasimulation_adult_mosquito_model_save_state`, model)
+}
+
+adult_mosquito_model_restore_state <- function(model, state) {
+ invisible(.Call(`_malariasimulation_adult_mosquito_model_restore_state`, model, state))
+}
+
create_adult_solver <- function(model, init, r_tol, a_tol, max_steps) {
.Call(`_malariasimulation_create_adult_solver`, model, init, r_tol, a_tol, max_steps)
}
@@ -37,10 +45,18 @@ rainfall <- function(t, g0, g, h, floor) {
.Call(`_malariasimulation_rainfall`, t, g0, g, h, floor)
}
+exponential_process_cpp <- function(variable, rate) {
+ .Call(`_malariasimulation_exponential_process_cpp`, variable, rate)
+}
+
solver_get_states <- function(solver) {
.Call(`_malariasimulation_solver_get_states`, solver)
}
+solver_set_states <- function(solver, t, state) {
+ invisible(.Call(`_malariasimulation_solver_set_states`, solver, t, state))
+}
+
solver_step <- function(solver) {
invisible(.Call(`_malariasimulation_solver_step`, solver))
}
@@ -57,10 +73,26 @@ timeseries_push <- function(timeseries, value, timestep) {
invisible(.Call(`_malariasimulation_timeseries_push`, timeseries, value, timestep))
}
+timeseries_save_state <- function(timeseries) {
+ .Call(`_malariasimulation_timeseries_save_state`, timeseries)
+}
+
+timeseries_restore_state <- function(timeseries, state) {
+ invisible(.Call(`_malariasimulation_timeseries_restore_state`, timeseries, state))
+}
+
random_seed <- function(seed) {
invisible(.Call(`_malariasimulation_random_seed`, seed))
}
+random_save_state <- function() {
+ .Call(`_malariasimulation_random_save_state`)
+}
+
+random_restore_state <- function(state) {
+ invisible(.Call(`_malariasimulation_random_restore_state`, state))
+}
+
bernoulli_multi_p_cpp <- function(p) {
.Call(`_malariasimulation_bernoulli_multi_p_cpp`, p)
}
diff --git a/R/antimalarial_resistance.R b/R/antimalarial_resistance.R
new file mode 100644
index 00000000..121de96c
--- /dev/null
+++ b/R/antimalarial_resistance.R
@@ -0,0 +1,146 @@
+#' @title Parameterise antimalarial resistance
+#' @description
+#' Parameterise antimalarial resistance
+#'
+#' @param parameters the model parameters
+#' @param drug the index of the drug which resistance is being set, as set by the set_drugs() function, in the parameter list
+#' @param timesteps vector of time steps for each update to resistance proportion and resistance outcome probability
+#' @param artemisinin_resistance_proportion vector of updates to the proportions of infections that are artemisinin resistant at time t
+#' @param partner_drug_resistance_proportion vector of updates to the proportions of infections that are partner-drug resistant at time t
+#' @param slow_parasite_clearance_probability vector of updates to the proportion of artemisinin-resistant infections that result in early treatment failure
+#' @param early_treatment_failure_probability vector of updates to the proportion of artemisinin-resistant infections that result in slow parasite clearance
+#' @param late_clinical_failure_probability vector of updates to the proportion of partner-drug-resistant infections that result in late clinical failure
+#' @param late_parasitological_failure_probability vector of updates to the proportion of partner-drug-resistant infections that result in late parasitological failure
+#' @param reinfection_during_prophylaxis_probability vector of updates to the proportion of partner-drug-resistant infections that result in reinfection during prophylaxis
+#' @param slow_parasite_clearance_time single value representing the mean time individual's experiencing slow parasite clearance reside in the treated state
+#' @export
+set_antimalarial_resistance <- function(parameters,
+ drug,
+ timesteps,
+ artemisinin_resistance_proportion,
+ partner_drug_resistance_proportion,
+ slow_parasite_clearance_probability,
+ early_treatment_failure_probability,
+ late_clinical_failure_probability,
+ late_parasitological_failure_probability,
+ reinfection_during_prophylaxis_probability,
+ slow_parasite_clearance_time) {
+
+ if(any(partner_drug_resistance_proportion > 0,
+ late_clinical_failure_probability > 0,
+ late_parasitological_failure_probability > 0,
+ reinfection_during_prophylaxis_probability > 0)) {
+ stop("Parameters set for unimplemented feature - late clinical failure, late parasitological failure, or reinfection during prophylaxis")
+ }
+
+ if(any(c(length(artemisinin_resistance_proportion),
+ length(partner_drug_resistance_proportion),
+ length(slow_parasite_clearance_probability),
+ length(early_treatment_failure_probability),
+ length(late_clinical_failure_probability),
+ length(late_parasitological_failure_probability),
+ length(reinfection_during_prophylaxis_probability)) != length(timesteps))) {
+ stop("Length of one or more resistance parameter vectors does not match time steps specified for update")
+ }
+
+ if(any(artemisinin_resistance_proportion < 0 | artemisinin_resistance_proportion > 1 |
+ partner_drug_resistance_proportion < 0 | partner_drug_resistance_proportion > 1)) {
+ stop("Artemisinin and partner-drug resistance proportions must fall between 0 and 1")
+ }
+
+ if(any(slow_parasite_clearance_probability < 0 | slow_parasite_clearance_probability > 1 |
+ early_treatment_failure_probability < 0 | early_treatment_failure_probability > 1 |
+ late_clinical_failure_probability < 0 | late_clinical_failure_probability > 1 |
+ late_parasitological_failure_probability < 0 | late_parasitological_failure_probability > 1 |
+ reinfection_during_prophylaxis_probability < 0 | reinfection_during_prophylaxis_probability > 1)) {
+ stop("Resistance outcome probabilities must fall between 0 and 1")
+ }
+
+ if(length(slow_parasite_clearance_time) != 1) {
+ stop("Error: length of slow_parasite_clearance_time not equal to 1")
+ }
+
+ if(slow_parasite_clearance_time <= 0) {
+ stop("Error: slow_parasite_clearance_time is non-positive")
+ }
+
+ parameters$antimalarial_resistance <- TRUE
+
+ n_drugs <- length(parameters$drug_efficacy)
+
+ if (drug < 1 | drug > n_drugs) {
+ stop('Drug index is invalid, please set drugs using set_drugs')
+ }
+
+ drug_index <- which(parameters$antimalarial_resistance_drug == drug)
+
+ if (length(drug_index) == 0) {
+ drug_index <- length(parameters$antimalarial_resistance_drug) + 1
+ }
+
+ parameters$antimalarial_resistance_drug[[drug_index]] <- drug
+ parameters$antimalarial_resistance_timesteps[[drug_index]] <- timesteps
+ parameters$artemisinin_resistance_proportion[[drug_index]] <- artemisinin_resistance_proportion
+ parameters$partner_drug_resistance_proportion[[drug_index]] <- partner_drug_resistance_proportion
+ parameters$slow_parasite_clearance_probability[[drug_index]] <- slow_parasite_clearance_probability
+ parameters$early_treatment_failure_probability[[drug_index]] <- early_treatment_failure_probability
+ parameters$late_clinical_failure_probability[[drug_index]] <- late_clinical_failure_probability
+ parameters$late_parasitological_failure_probability[[drug_index]] <- late_parasitological_failure_probability
+ parameters$reinfection_during_prophylaxis_probability[[drug_index]] <- reinfection_during_prophylaxis_probability
+ parameters$dt_slow_parasite_clearance[[drug_index]] <- slow_parasite_clearance_time
+
+ return(parameters)
+
+}
+
+#' @title Retrieve resistance parameters
+#' @description
+#' Retrieve the resistance parameters associated with the drug each individual receiving clinical
+#' treatment has been administered in the current time step.
+#'
+#' @param parameters the model parameters
+#' @param drugs vector of integers representing the drugs administered to each individual receiving treatment
+#' @param timestep the current time step
+get_antimalarial_resistance_parameters <- function(parameters, drugs, timestep) {
+
+ if(!parameters$antimalarial_resistance) {
+ stop("Error: Antimalarial resistance has not been parameterised; antimalarial_resistance = FALSE")
+ }
+
+ blank_vector <- numeric(length = length(drugs))
+ artemisinin_resistance_proportion <- blank_vector
+ partner_drug_resistance_proportion <- blank_vector
+ slow_parasite_clearance_probability <- blank_vector
+ early_treatment_failure_probability <- blank_vector
+ late_clinical_failure_probability <- blank_vector
+ late_parasitological_failure_probability <- blank_vector
+ reinfection_during_prophylaxis_probability <- blank_vector
+ dt_slow_parasite_clearance <- rep(parameters$dt, length = length(drugs))
+
+ for(i in seq_along(parameters$antimalarial_resistance_drug)) {
+ drug <- parameters$antimalarial_resistance_drug[[i]]
+ treated_with_drug <- which(drugs == drug)
+ resistance_timestep <- match_timestep(ts = parameters$antimalarial_resistance_timesteps[[i]], t = timestep)
+ artemisinin_resistance_proportion[treated_with_drug] <- parameters$artemisinin_resistance_proportion[[i]][resistance_timestep]
+ partner_drug_resistance_proportion[treated_with_drug] <- parameters$partner_drug_resistance_proportion[[i]][resistance_timestep]
+ slow_parasite_clearance_probability[treated_with_drug] <- parameters$slow_parasite_clearance_probability[[i]][resistance_timestep]
+ early_treatment_failure_probability[treated_with_drug] <- parameters$early_treatment_failure_probability[[i]][resistance_timestep]
+ late_clinical_failure_probability[treated_with_drug] <- parameters$late_clinical_failure_probability[[i]][resistance_timestep]
+ late_parasitological_failure_probability[treated_with_drug] <- parameters$late_parasitological_failure_probability[[i]][resistance_timestep]
+ reinfection_during_prophylaxis_probability[treated_with_drug] <- parameters$reinfection_during_prophylaxis_probability[[i]][resistance_timestep]
+ dt_slow_parasite_clearance[treated_with_drug] <- parameters$dt_slow_parasite_clearance[[i]]
+ }
+
+ resistance_parameters <- list()
+ resistance_parameters$artemisinin_resistance_proportion <- artemisinin_resistance_proportion
+ resistance_parameters$partner_drug_resistance_proportion <- partner_drug_resistance_proportion
+ resistance_parameters$slow_parasite_clearance_probability <- slow_parasite_clearance_probability
+ resistance_parameters$early_treatment_failure_probability <- early_treatment_failure_probability
+ resistance_parameters$late_clinical_failure_probability <- late_clinical_failure_probability
+ resistance_parameters$late_parasitological_failure_probability <- late_parasitological_failure_probability
+ resistance_parameters$reinfection_during_prophylaxis_probability <- reinfection_during_prophylaxis_probability
+ resistance_parameters$dt_slow_parasite_clearance <- dt_slow_parasite_clearance
+
+ return(resistance_parameters)
+
+}
diff --git a/R/biting_process.R b/R/biting_process.R
index ec09620d..279802ea 100644
--- a/R/biting_process.R
+++ b/R/biting_process.R
@@ -103,7 +103,7 @@ simulate_bites <- function(
for (s_i in seq_along(parameters$species)) {
species_name <- parameters$species[[s_i]]
- solver_states <- solver_get_states(solvers[[s_i]])
+ solver_states <- solvers[[s_i]]$get_states()
p_bitten <- prob_bitten(timestep, variables, s_i, parameters)
Q0 <- parameters$Q0[[s_i]]
W <- average_p_successful(p_bitten$prob_bitten_survives, .pi, Q0)
@@ -167,7 +167,7 @@ simulate_bites <- function(
if (parameters$individual_mosquitoes) {
# update the ODE with stats for ovoposition calculations
aquatic_mosquito_model_update(
- models[[s_i]],
+ models[[s_i]]$.model,
species_index$size(),
f,
mu
@@ -189,7 +189,7 @@ simulate_bites <- function(
)
} else {
adult_mosquito_model_update(
- models[[s_i]],
+ models[[s_i]]$.model,
mu,
foim,
solver_states[[ADULT_ODE_INDICES['Sm']]],
@@ -210,9 +210,7 @@ simulate_bites <- function(
calculate_eir <- function(species, solvers, variables, parameters, timestep) {
a <- human_blood_meal_rate(species, variables, parameters, timestep)
infectious <- calculate_infectious(species, solvers, variables, parameters)
- age <- get_age(variables$birth$get_values(), timestep)
- psi <- unique_biting_rate(age, parameters)
- infectious * a * mean(psi)
+ infectious * a
}
effective_biting_rates <- function(a, .pi, p_bitten) {
@@ -235,7 +233,7 @@ calculate_infectious <- function(species, solvers, variables, parameters) {
)
)
}
- calculate_infectious_compartmental(solver_get_states(solvers[[species]]))
+ calculate_infectious_compartmental(solvers[[species]]$get_states())
}
calculate_infectious_individual <- function(
diff --git a/R/compartmental.R b/R/compartmental.R
index edba33a6..4e16c7c1 100644
--- a/R/compartmental.R
+++ b/R/compartmental.R
@@ -16,7 +16,7 @@ parameterise_mosquito_models <- function(parameters, timesteps) {
for(j in 1:length(parameters$carrying_capacity_timesteps)){
timeseries_push(
k_timeseries,
- parameters$carrying_capacity_values[j,i],
+ parameters$carrying_capacity_scalers[j,i] * k0,
parameters$carrying_capacity_timesteps[j]
)
}
@@ -50,16 +50,16 @@ parameterise_mosquito_models <- function(parameters, timesteps) {
m
)[ADULT_ODE_INDICES['Sm']]
return(
- create_adult_mosquito_model(
+ AdultMosquitoModel$new(create_adult_mosquito_model(
growth_model,
parameters$mum[[i]],
parameters$dem,
susceptible * parameters$init_foim,
parameters$init_foim
- )
+ ))
)
}
- growth_model
+ AquaticMosquitoModel$new(growth_model)
}
)
}
@@ -72,22 +72,22 @@ parameterise_solvers <- function(models, parameters) {
init <- initial_mosquito_counts(parameters, i, parameters$init_foim, m)
if (!parameters$individual_mosquitoes) {
return(
- create_adult_solver(
- models[[i]],
+ Solver$new(create_adult_solver(
+ models[[i]]$.model,
init,
parameters$r_tol,
parameters$a_tol,
parameters$ode_max_steps
- )
+ ))
)
}
- create_aquatic_solver(
- models[[i]],
+ Solver$new(create_aquatic_solver(
+ models[[i]]$.model,
init[ODE_INDICES],
parameters$r_tol,
parameters$a_tol,
parameters$ode_max_steps
- )
+ ))
}
)
}
@@ -103,7 +103,7 @@ create_compartmental_rendering_process <- function(renderer, solvers, parameters
counts <- rep(0, length(indices))
for (s_i in seq_along(solvers)) {
if (parameters$species_proportions[[s_i]] > 0) {
- row <- solver_get_states(solvers[[s_i]])
+ row <- solvers[[s_i]]$get_states()
} else {
row <- rep(0, length(indices))
}
@@ -128,8 +128,67 @@ create_solver_stepping_process <- function(solvers, parameters) {
function(timestep) {
for (i in seq_along(solvers)) {
if (parameters$species_proportions[[i]] > 0) {
- solver_step(solvers[[i]])
+ solvers[[i]]$step()
}
}
}
}
+
+Solver <- R6::R6Class(
+ 'Solver',
+ private = list(
+ .solver = NULL
+ ),
+ public = list(
+ initialize = function(solver) {
+ private$.solver <- solver
+ },
+ step = function() {
+ solver_step(private$.solver)
+ },
+ get_states = function() {
+ solver_get_states(private$.solver)
+ },
+
+ # This is the same as `get_states`, just exposed under the interface that
+ # is expected of stateful objects.
+ save_state = function() {
+ solver_get_states(private$.solver)
+ },
+ restore_state = function(t, state) {
+ solver_set_states(private$.solver, t, state)
+ }
+ )
+)
+
+AquaticMosquitoModel <- R6::R6Class(
+ 'AquaticMosquitoModel',
+ public = list(
+ .model = NULL,
+ initialize = function(model) {
+ self$.model <- model
+ },
+
+ # The aquatic mosquito model doesn't have any state to save or restore (the
+ # state of the ODE is stored separately). We still provide these methods to
+ # conform to the expected interface.
+ save_state = function() { NULL },
+ restore_state = function(t, state) { }
+ )
+)
+
+AdultMosquitoModel <- R6::R6Class(
+ 'AdultMosquitoModel',
+ public = list(
+ .model = NULL,
+ initialize = function(model) {
+ self$.model <- model
+ },
+ save_state = function() {
+ adult_mosquito_model_save_state(self$.model)
+ },
+ restore_state = function(t, state) {
+ adult_mosquito_model_restore_state(self$.model, state)
+ }
+ )
+)
diff --git a/R/correlation.R b/R/correlation.R
index 41ea6a82..bb98fb3b 100644
--- a/R/correlation.R
+++ b/R/correlation.R
@@ -9,7 +9,26 @@ INTS <- c(
)
#' Class: Correlation parameters
-#' Describes an event in the simulation
+#'
+#' This class implements functionality that allows interventions to be
+#' correlated, positively or negatively. By default, interventions are applied
+#' independently and an individual's probability of receiving two interventions
+#' (either two separate interventions or two rounds of the same one) is the
+#' product of the probability of receiving each one.
+#'
+#' By setting a positive correlation between two interventions, we can make it
+#' so that the individuals that receive intervention A are more likely to
+#' receive intervention B. Conversely, a negative correlation will make it such
+#' that individuals that receive intervention A are less likely to also receive
+#' intervention B.
+#'
+#' Broadly speaking, the implementation works by assigning at startup a weight
+#' to each individual and intervention pair, reflecting how likely an individual
+#' is to receive that intervention. Those weights are derived stochastically
+#' from the configured correlation parameters.
+#'
+#' For a detailed breakdown of the calculations, see Protocol S2 of
+#' Griffin et al. (2010).
CorrelationParameters <- R6::R6Class(
'CorrelationParameters',
private = list(
@@ -19,16 +38,49 @@ CorrelationParameters <- R6::R6Class(
rho_matrix = NULL,
rho = function() diag(private$rho_matrix),
.sigma = NULL,
- .mvnorm = NULL
+ .mvnorm = NULL,
+
+ #' Derive the mvnorm from the configured correlations.
+ #'
+ #' If a \code{restored_mvnorm} is specified, its columns (corresponding to
+ #' restored interventions) will be re-used as is. Missing columns (for new
+ #' interventions) are derived in accordance with the restored data.
+ calculate_mvnorm = function(restored_mvnorm = matrix(ncol=0, nrow=private$population)) {
+ sigma <- self$sigma()
+ V <- outer(sigma, sigma) * private$rho_matrix
+ diag(V) <- sigma ^ 2
+
+ restored_interventions <- match(colnames(restored_mvnorm), private$interventions)
+ new_interventions <- setdiff(seq_along(private$interventions), restored_interventions)
+
+ mvnorm <- matrix(
+ nrow = private$population,
+ ncol = length(private$interventions),
+ dimnames = list(NULL, private$interventions)
+ )
+ mvnorm[,restored_interventions] <- restored_mvnorm
+ if (length(new_interventions) > 0) {
+ mvnorm[,new_interventions] <- rcondmvnorm(
+ private$population,
+ mean = rep(0, length(private$interventions)),
+ sigma = V,
+ given = restored_mvnorm,
+ dependent.ind = new_interventions,
+ given.ind = restored_interventions
+ )
+ }
+
+ mvnorm
+ }
),
public = list(
#' @description initialise correlation parameters
- #' @param parameters model parameters
- initialize = function(parameters) {
- # Find a list of enabled interventions
- enabled <- vlapply(INTS, function(name) parameters[[name]])
- private$interventions <- INTS[enabled]
+ #' @param population popularion size
+ #' @param interventions character vector with the name of enabled interventions
+ initialize = function(population, interventions) {
+ private$population <- population
+ private$interventions <- interventions
# Initialise a rho matrix for our interventions
n_ints <- private$n_ints()
@@ -38,9 +90,6 @@ CorrelationParameters <- R6::R6Class(
ncol = n_ints,
dimnames = list(private$interventions, private$interventions)
)
-
- # Store population for mvnorm draws
- private$population <- parameters$human_population
},
#' @description Add rho between rounds
@@ -48,6 +97,8 @@ CorrelationParameters <- R6::R6Class(
#' @param rho value between 0 and 1 representing the correlation between rounds of
#' the intervention
inter_round_rho = function(int, rho) {
+ stopifnot(is.null(private$.sigma) && is.null(private$.mvnorm))
+
if (!(int %in% private$interventions)) {
stop(paste0('invalid intervention name: ', int))
}
@@ -58,8 +109,6 @@ CorrelationParameters <- R6::R6Class(
rho <- 1 - .Machine$double.eps
}
private$rho_matrix[[int, int]] <- rho
- private$.sigma <- NULL
- private$.mvnorm <- NULL
},
#' @description Add rho between interventions
@@ -69,6 +118,8 @@ CorrelationParameters <- R6::R6Class(
#' @param rho value between -1 and 1 representing the correlation between rounds of
#' the intervention
inter_intervention_rho = function(int_1, int_2, rho) {
+ stopifnot(is.null(private$.sigma) && is.null(private$.mvnorm))
+
if (!(int_1 %in% private$interventions)) {
stop(paste0('invalid intervention name: ', int_1))
}
@@ -86,8 +137,6 @@ CorrelationParameters <- R6::R6Class(
}
private$rho_matrix[[int_1, int_2]] <- rho
private$rho_matrix[[int_2, int_1]] <- rho
- private$.sigma <- NULL
- private$.mvnorm <- NULL
},
#' @description Standard deviation of each intervention between rounds
@@ -101,20 +150,36 @@ CorrelationParameters <- R6::R6Class(
},
#' @description multivariate norm draws for these parameters
- #' @importFrom MASS mvrnorm
mvnorm = function() {
if (is.null(private$.mvnorm)) {
- sigma <- self$sigma()
- V <- outer(sigma, sigma) * private$rho_matrix
- diag(V) <- sigma ^ 2
- private$.mvnorm <- mvrnorm(
- private$population,
- rep(0, length(private$interventions)),
- V
- )
- dimnames(private$.mvnorm)[[2]] <- private$interventions
+ private$.mvnorm <- private$calculate_mvnorm()
}
private$.mvnorm
+ },
+
+ #' @description Save the correlation state.
+ save_state = function() {
+ # mvnorm is sampled at random lazily on its first use. We need to save it
+ # in order to restore the same value when resuming the simulation,
+ # otherwise we would be drawing a new, probably different, value.
+ # The rest of the object is derived deterministically from the parameters
+ # and does not need saving.
+ list(mvnorm=self$mvnorm())
+ },
+
+ #' @description Restore the correlation state.
+ #'
+ #' Only the randomly drawn weights are restored. The object needs to be
+ #' initialized with the same rhos.
+ #'
+ #' @param timestep the timestep at which simulation is resumed. This
+ #' parameter's value is ignored, it only exists to conform to a uniform
+ #' interface.
+ #' @param state a previously saved correlation state, as returned by the
+ #' save_state method.
+ restore_state = function(timestep, state) {
+ stopifnot(is.null(private$.sigma) && is.null(private$.mvnorm))
+ private$.mvnorm <- private$calculate_mvnorm(state$mvnorm)
}
)
)
@@ -139,7 +204,7 @@ CorrelationParameters <- R6::R6Class(
#' min_wait = 0,
#' min_ages = 100,
#' max_ages = 1000,
-#' booster_timestep = numeric(0),
+#' booster_spacing = numeric(0),
#' booster_coverage = numeric(0),
#' booster_profile = NULL
#' )
@@ -164,7 +229,10 @@ CorrelationParameters <- R6::R6Class(
#'
#' # You can now pass the correlation parameters to the run_simulation function
get_correlation_parameters <- function(parameters) {
- CorrelationParameters$new(parameters)
+ # Find a list of enabled interventions
+ enabled <- vlapply(INTS, function(name) parameters[[name]])
+
+ CorrelationParameters$new(parameters$human_population, INTS[enabled])
}
#' @title Sample a population to intervene in given the correlation parameters
@@ -181,3 +249,85 @@ sample_intervention <- function(target, intervention, p, correlations) {
z <- rnorm(length(target))
u0 + correlations$mvnorm()[target, intervention] + z < 0
}
+
+#' Simulate from a conditional multivariate normal distribution.
+#'
+#' Given a multidimensional variable Z which follows a multivariate normal
+#' distribution, this function allows one to draw samples for a subset of Z,
+#' while putting conditions on the values of the rest of Z.
+#'
+#' This effectively allows one to grow a MVN distributed matrix (with columns as
+#' the dimensions and a row per sampled vector), adding new dimensions after the
+#' fact. The existing columns are used as the condition set on the distribution,
+#' and the values returned by this function are used as the new dimensions.
+#'
+#' The maths behind the implementation are described in various online sources:
+#' - https://statproofbook.github.io/P/mvn-cond.html
+#' - https://www.stats.ox.ac.uk/~doucet/doucet_simulationconditionalgaussian.pdf
+#' - https://en.wikipedia.org/wiki/Multivariate_normal_distribution#Conditional_distributions
+#'
+#' @param n the number of samples to simulate
+#' @param mean the mean vector of the distribution, including both given and
+#' dependent variables
+#' @param sigma the variance-covariance matrix of the distribution, including
+#' both given and dependent variables
+#' @param given a matrix of given values used as conditions when simulating the
+#' distribution. The matrix should have \code{n} rows, each one specifying a
+#' different set of values for the given variables.
+#' @param dependent.ind the indices within \code{mean} and \code{sigma} of the
+#' variables to simulate.
+#' @param given.ind the indices within \code{mean} and \code{sigma} of the
+#' variables for which conditions are given. The length of this vector must be
+#' equal to the number of columns of the \code{given} matrix. If empty or NULL,
+#' this function is equivalent to simulating from an unconditional multivariate
+#' normal distribution.
+#' @return a matrix with \code{n} rows and \code{length(dependent.ind)} columns,
+#' containing the simulated value.
+#' @importFrom MASS mvrnorm
+#' @noRd
+rcondmvnorm <- function(n, mean, sigma, given, dependent.ind, given.ind) {
+ stopifnot(length(mean) == nrow(sigma))
+ stopifnot(length(mean) == ncol(sigma))
+ stopifnot(nrow(given) == n)
+ stopifnot(ncol(given) == length(given.ind))
+
+ sigma11 <- sigma[dependent.ind, dependent.ind, drop=FALSE]
+ sigma12 <- sigma[dependent.ind, given.ind, drop=FALSE]
+ sigma21 <- sigma[given.ind, dependent.ind, drop=FALSE]
+ sigma22 <- sigma[given.ind, given.ind, drop=FALSE]
+
+ if (all(sigma22 == 0)) {
+ # This covers two cases: there were no given variables and therefore their
+ # variance-covariance matrix is empty, or there were given variables but
+ # they had a variance of zero. The general formula can't support the latter
+ # case since it tries to invert the matrix, but we can safely ignore the
+ # values since they are all equal to their mean and don't influence the
+ # dependent variables.
+ #
+ # In both cases we revert to a standard MVN with no condition.
+ mvrnorm(n, mean[dependent.ind], sigma11)
+ } else {
+ # Available implementations of the conditional multivariate normal assume
+ # every sample is drawn using the same condition on the given variables.
+ # This is not true in our usecase, where every individual has already had an
+ # independent vector of values drawn for the given variable. We are
+ # effectively drawing from as many different distributions as there are
+ # individuals. Thankfully the same conditional covariance matrix can be
+ # used for all the distributions, only the mean vector needs to be
+ # different. We draw the underlying samples from the MVN at mean 0, and
+ # offset that later on a per-individual basis.
+ #
+ # To work over all the vectors directly they need to be as columns, which
+ # is why we start by transposing `given`. R will recycle the `m` matrix and
+ # `mean` vectors across all the columns. The last step is to transpose the
+ # result back into the expected configuration.
+
+ m <- sigma12 %*% solve(sigma22)
+ residual <- t(given) - mean[given.ind]
+ cond_mu <- t(m %*% residual + mean[dependent.ind])
+ cond_sigma <- sigma11 - m %*% sigma21
+
+ samples <- mvrnorm(n, rep(0, length(dependent.ind)), cond_sigma)
+ samples + cond_mu
+ }
+}
diff --git a/R/disease_progression.R b/R/disease_progression.R
index 05367252..d9ac11e4 100644
--- a/R/disease_progression.R
+++ b/R/disease_progression.R
@@ -19,15 +19,27 @@ update_infection <- function(
}
create_progression_process <- function(
- state,
- from_state,
- to_state,
- rate,
- infectivity,
- new_infectivity
- ) {
+ state,
+ from_state,
+ to_state,
+ rate,
+ infectivity,
+ new_infectivity
+) {
function(timestep) {
- to_move <- state$get_index_of(from_state)$sample(1/rate)
+
+ # Retrieve the indices of all individuals in the to_move state:
+ index <- state$get_index_of(from_state)
+
+ # If the length of rate is greater than 1 (when it's a variable):
+ if (inherits(rate, "DoubleVariable")) {
+ rate <- rate$get_values(index)
+ }
+
+ # Sample the individuals to be moved into a new Bitset using the transition rate(s):
+ to_move <- index$sample(1/rate)
+
+ # Update the infection status of those individuals who are moving:
update_infection(
state,
to_state,
diff --git a/R/events.R b/R/events.R
index 879d72bd..de0c7c1f 100644
--- a/R/events.R
+++ b/R/events.R
@@ -1,11 +1,11 @@
create_events <- function(parameters) {
events <- list(
# MDA events
- mda_administer = individual::Event$new(),
- smc_administer = individual::Event$new(),
+ mda_administer = individual::Event$new(restore=FALSE),
+ smc_administer = individual::Event$new(restore=FALSE),
# TBV event
- tbv_vaccination = individual::Event$new(),
+ tbv_vaccination = individual::Event$new(restore=FALSE),
# Bednet events
throw_away_net = individual::TargetedEvent$new(parameters$human_population)
@@ -18,10 +18,10 @@ create_events <- function(parameters) {
function(.) individual::TargetedEvent$new(parameters$human_population)
)
mass_pev_boosters <- lapply(
- seq_along(parameters$mass_pev_booster_timestep),
+ seq_along(parameters$mass_pev_booster_spacing),
function(.) individual::TargetedEvent$new(parameters$human_population)
)
- events$mass_pev <- individual::Event$new()
+ events$mass_pev <- individual::Event$new(restore=FALSE)
events$mass_pev_doses <- mass_pev_doses
events$mass_pev_boosters <- mass_pev_boosters
}
@@ -33,7 +33,7 @@ create_events <- function(parameters) {
function(.) individual::TargetedEvent$new(parameters$human_population)
)
pev_epi_boosters <- lapply(
- seq_along(parameters$pev_epi_booster_timestep),
+ seq_along(parameters$pev_epi_booster_spacing),
function(.) individual::TargetedEvent$new(parameters$human_population)
)
events$pev_epi_doses <- pev_epi_doses
@@ -63,16 +63,16 @@ initialise_events <- function(events, variables, parameters) {
# Initialise scheduled interventions
if (!is.null(parameters$mass_pev_timesteps)) {
- events$mass_pev$schedule(parameters$mass_pev_timesteps[[1]] - 1)
+ events$mass_pev$schedule(parameters$mass_pev_timesteps - 1)
}
if (parameters$mda) {
- events$mda_administer$schedule(parameters$mda_timesteps[[1]] - 1)
+ events$mda_administer$schedule(parameters$mda_timesteps - 1)
}
if (parameters$smc) {
- events$smc_administer$schedule(parameters$smc_timesteps[[1]] - 1)
+ events$smc_administer$schedule(parameters$smc_timesteps - 1)
}
if (parameters$tbv) {
- events$tbv_vaccination$schedule(parameters$tbv_timesteps[[1]] - 1)
+ events$tbv_vaccination$schedule(parameters$tbv_timesteps - 1)
}
}
@@ -129,9 +129,10 @@ attach_event_listeners <- function(
attach_pev_dose_listeners(
variables,
parameters,
+ parameters$mass_pev_timesteps,
events$mass_pev_doses,
events$mass_pev_boosters,
- parameters$mass_pev_booster_timestep,
+ parameters$mass_pev_booster_spacing,
parameters$mass_pev_booster_coverage,
parameters$mass_pev_profile_indices,
'mass',
@@ -143,9 +144,10 @@ attach_event_listeners <- function(
attach_pev_dose_listeners(
variables,
parameters,
+ parameters$pev_epi_timesteps,
events$pev_epi_doses,
events$pev_epi_boosters,
- parameters$pev_epi_booster_timestep,
+ parameters$pev_epi_booster_spacing,
parameters$pev_epi_booster_coverage,
parameters$pev_epi_profile_indices,
'epi',
@@ -156,7 +158,6 @@ attach_event_listeners <- function(
if (parameters$mda == 1) {
events$mda_administer$add_listener(create_mda_listeners(
variables,
- events$mda_administer,
parameters$mda_drug,
parameters$mda_timesteps,
parameters$mda_coverages,
@@ -172,7 +173,6 @@ attach_event_listeners <- function(
if (parameters$smc == 1) {
events$smc_administer$add_listener(create_mda_listeners(
variables,
- events$smc_administer,
parameters$smc_drug,
parameters$smc_timesteps,
parameters$smc_coverages,
diff --git a/R/human_infection.R b/R/human_infection.R
index b623a2d8..d62b16f0 100644
--- a/R/human_infection.R
+++ b/R/human_infection.R
@@ -129,9 +129,10 @@ calculate_infections <- function(
# calculate vaccine efficacy
vaccine_efficacy <- rep(0, length(source_vector))
- vaccine_times <- variables$pev_timestep$get_values(source_vector)
- vaccinated <- vaccine_times > -1
+ vaccine_times <- variables$last_eff_pev_timestep$get_values(source_vector)
pev_profile <- variables$pev_profile$get_values(source_vector)
+ # get vector of individuals who have received their 3rd dose
+ vaccinated <- vaccine_times > -1
pev_profile <- pev_profile[vaccinated]
if (length(vaccinated) > 0) {
antibodies <- calculate_pev_antibodies(
@@ -262,23 +263,27 @@ update_severe_disease <- function(
#' @param renderer simulation renderer
#' @noRd
calculate_treated <- function(
- variables,
- clinical_infections,
- parameters,
- timestep,
- renderer
- ) {
+ variables,
+ clinical_infections,
+ parameters,
+ timestep,
+ renderer
+) {
+
+ if(clinical_infections$size() == 0) {
+ return(individual::Bitset$new(parameters$human_population))
+ }
+
treatment_coverages <- get_treatment_coverages(parameters, timestep)
ft <- sum(treatment_coverages)
-
+
if (ft == 0) {
return(individual::Bitset$new(parameters$human_population))
}
-
+
renderer$render('ft', ft, timestep)
seek_treatment <- sample_bitset(clinical_infections, ft)
n_treat <- seek_treatment$size()
-
renderer$render('n_treated', n_treat, timestep)
drugs <- as.numeric(parameters$clinical_treatment_drugs[
@@ -289,27 +294,80 @@ calculate_treated <- function(
replace = TRUE
)
])
-
- successful <- bernoulli_multi_p(parameters$drug_efficacy[drugs])
- treated_index <- bitset_at(seek_treatment, successful)
-
- # Update those who have been treated
- if (treated_index$size() > 0) {
- variables$state$queue_update('Tr', treated_index)
+
+ #+++ DRUG EFFICACY +++#
+ #+++++++++++++++++++++#
+ effectively_treated_index <- bernoulli_multi_p(parameters$drug_efficacy[drugs])
+ effectively_treated <- bitset_at(seek_treatment, effectively_treated_index)
+ drugs <- drugs[effectively_treated_index]
+ n_drug_efficacy_failures <- n_treat - effectively_treated$size()
+ renderer$render('n_drug_efficacy_failures', n_drug_efficacy_failures, timestep)
+
+ #+++ ANTIMALARIAL RESISTANCE +++#
+ #+++++++++++++++++++++++++++++++#
+ if(parameters$antimalarial_resistance) {
+ resistance_parameters <- get_antimalarial_resistance_parameters(
+ parameters = parameters,
+ drugs = drugs,
+ timestep = timestep
+ )
+
+ #+++ EARLY TREATMENT FAILURE +++#
+ #+++++++++++++++++++++++++++++++#
+ early_treatment_failure_probability <- resistance_parameters$artemisinin_resistance_proportion * resistance_parameters$early_treatment_failure_probability
+ successfully_treated_indices <- bernoulli_multi_p(p = 1 - early_treatment_failure_probability)
+ successfully_treated <- bitset_at(effectively_treated, successfully_treated_indices)
+ n_early_treatment_failure <- effectively_treated$size() - successfully_treated$size()
+ renderer$render('n_early_treatment_failure', n_early_treatment_failure, timestep)
+ drugs <- drugs[successfully_treated_indices]
+ dt_slow_parasite_clearance <- resistance_parameters$dt_slow_parasite_clearance[successfully_treated_indices]
+
+ #+++ SLOW PARASITE CLEARANCE +++#
+ #+++++++++++++++++++++++++++++++#
+ slow_parasite_clearance_probability <- resistance_parameters$artemisinin_resistance_proportion[successfully_treated_indices] *
+ resistance_parameters$slow_parasite_clearance_probability[successfully_treated_indices]
+ slow_parasite_clearance_indices <- bernoulli_multi_p(p = slow_parasite_clearance_probability)
+ slow_parasite_clearance_individuals <- bitset_at(successfully_treated, slow_parasite_clearance_indices)
+ renderer$render('n_slow_parasite_clearance', slow_parasite_clearance_individuals$size(), timestep)
+ non_slow_parasite_clearance_individuals <- successfully_treated$copy()$set_difference(slow_parasite_clearance_individuals)
+ renderer$render('n_successfully_treated', successfully_treated$size(), timestep)
+ dt_slow_parasite_clearance <- dt_slow_parasite_clearance[slow_parasite_clearance_indices]
+
+ } else {
+
+ successfully_treated <- effectively_treated
+ renderer$render('n_successfully_treated', successfully_treated$size(), timestep)
+
+ }
+
+ if (successfully_treated$size() > 0) {
+ variables$state$queue_update("Tr", successfully_treated)
variables$infectivity$queue_update(
- parameters$cd * parameters$drug_rel_c[drugs[successful]],
- treated_index
+ parameters$cd * parameters$drug_rel_c[drugs],
+ successfully_treated
)
variables$drug$queue_update(
- drugs[successful],
- treated_index
+ drugs,
+ successfully_treated
)
variables$drug_time$queue_update(
timestep,
- treated_index
+ successfully_treated
)
+ if(parameters$antimalarial_resistance) {
+ variables$dt$queue_update(
+ parameters$dt,
+ non_slow_parasite_clearance_individuals
+ )
+ variables$dt$queue_update(
+ dt_slow_parasite_clearance,
+ slow_parasite_clearance_individuals
+ )
+ }
}
- treated_index
+
+ successfully_treated
+
}
#' @title Schedule infections
diff --git a/R/lag.R b/R/lag.R
index 35e47026..de8d1653 100644
--- a/R/lag.R
+++ b/R/lag.R
@@ -14,6 +14,14 @@ LaggedValue <- R6::R6Class(
get = function(timestep) {
timeseries_at(private$history, timestep, TRUE)
+ },
+
+ save_state = function() {
+ timeseries_save_state(private$history)
+ },
+
+ restore_state = function(t, state) {
+ timeseries_restore_state(private$history, state)
}
)
)
diff --git a/R/mda_parameters.R b/R/mda_parameters.R
index a07d7e70..f84b86e1 100644
--- a/R/mda_parameters.R
+++ b/R/mda_parameters.R
@@ -6,7 +6,6 @@
#' round
#' @param min_ages a vector of minimum ages of the target population for each round exclusive (in timesteps)
#' @param max_ages a vector of maximum ages of the target population for each round exclusive (in timesteps)
-#' drug
#' @export
set_mda <- function(
parameters,
@@ -77,9 +76,9 @@ set_smc <- function(
#' @title Parameterise a perennial malaria chemoprevention (PMC, formerly IPIi)
#' @param parameters a list of parameters to modify
#' @param drug the index of the drug to administer
-#' @param timesteps a vector of timesteps for each round of PMC
-#' @param coverages a vector of the proportion of the target population who receive each
-#' round
+#' @param timesteps a vector of timesteps for each change in coverage
+#' @param coverages a vector of proportions of the target population to receive
+#' the intervention
#' @param ages a vector of ages at which PMC is administered (in timesteps)
#' @export
set_pmc <- function(
diff --git a/R/mda_processes.R b/R/mda_processes.R
index 42960be4..72cbe779 100644
--- a/R/mda_processes.R
+++ b/R/mda_processes.R
@@ -1,6 +1,5 @@
#' @title Create listeners for MDA events
#' @param variables the variables available in the model
-#' @param administer_event the event schedule for drug administration
#' @param drug the drug to administer
#' @param timesteps timesteps for each round
#' @param coverages the coverage for each round
@@ -14,7 +13,6 @@
#' @noRd
create_mda_listeners <- function(
variables,
- administer_event,
drug,
timesteps,
coverages,
@@ -78,11 +76,6 @@ create_mda_listeners <- function(
variables$drug$queue_update(drug, to_move)
variables$drug_time$queue_update(timestep, to_move)
}
-
- # Schedule next round
- if (time_index < length(timesteps)) {
- administer_event$schedule(timesteps[[time_index + 1]] - timestep)
- }
}
}
diff --git a/R/model.R b/R/model.R
index 48af9af9..67f79758 100644
--- a/R/model.R
+++ b/R/model.R
@@ -74,6 +74,10 @@
#' susceptible
#' * net_usage: the number people protected by a bed net
#' * mosquito_deaths: number of adult female mosquitoes who die this timestep
+#' * n_drug_efficacy_failures: number of clinically treated individuals whose treatment failed due to drug efficacy
+#' * n_early_treatment_failure: number of clinically treated individuals who experienced early treatment failure
+#' * n_successfully_treated: number of clinically treated individuals who are treated successfully (includes individuals who experience slow parasite clearance)
+#' * n_slow_parasite_clearance: number of clinically treated individuals who experienced slow parasite clearance
#'
#' @param timesteps the number of timesteps to run the simulation for (in days)
#' @param parameters a named list of parameters to use
@@ -84,6 +88,28 @@ run_simulation <- function(
timesteps,
parameters = NULL,
correlations = NULL
+) {
+ run_resumable_simulation(timesteps, parameters, correlations)$data
+}
+
+#' @title Run the simulation in a resumable way
+#'
+#' @description this function accepts an initial simulation state as an argument, and returns the
+#' final state after running all of its timesteps. This allows one run to be resumed, possibly
+#' having changed some of the parameters.
+#' @param timesteps the timestep at which to stop the simulation
+#' @param parameters a named list of parameters to use
+#' @param correlations correlation parameters
+#' @param initial_state the state from which the simulation is resumed
+#' @param restore_random_state if TRUE, restore the random number generator's state from the checkpoint.
+#' @return a list with two entries, one for the dataframe of results and one for the final
+#' simulation state.
+run_resumable_simulation <- function(
+ timesteps,
+ parameters = NULL,
+ correlations = NULL,
+ initial_state = NULL,
+ restore_random_state = FALSE
) {
random_seed(ceiling(runif(1) * .Machine$integer.max))
if (is.null(parameters)) {
@@ -105,7 +131,26 @@ run_simulation <- function(
)
vector_models <- parameterise_mosquito_models(parameters, timesteps)
solvers <- parameterise_solvers(vector_models, parameters)
- individual::simulation_loop(
+
+ lagged_eir <- create_lagged_eir(variables, solvers, parameters)
+ lagged_infectivity <- create_lagged_infectivity(variables, parameters)
+
+ stateful_objects <- list(
+ RandomState$new(restore_random_state),
+ correlations,
+ vector_models,
+ solvers,
+ lagged_eir,
+ lagged_infectivity)
+
+ if (!is.null(initial_state)) {
+ individual::restore_object_state(
+ initial_state$timesteps,
+ stateful_objects,
+ initial_state$malariasimulation)
+ }
+
+ individual_state <- individual::simulation_loop(
processes = create_processes(
renderer,
variables,
@@ -114,14 +159,31 @@ run_simulation <- function(
vector_models,
solvers,
correlations,
- list(create_lagged_eir(variables, solvers, parameters)),
- list(create_lagged_infectivity(variables, parameters))
+ list(lagged_eir),
+ list(lagged_infectivity),
+ timesteps
),
variables = variables,
- events = unlist(events),
- timesteps = timesteps
+ events = events,
+ timesteps = timesteps,
+ state = initial_state$individual,
+ restore_random_state = restore_random_state
)
- renderer$to_dataframe()
+
+ final_state <- list(
+ timesteps = timesteps,
+ individual = individual_state,
+ malariasimulation = individual::save_object_state(stateful_objects)
+ )
+
+ data <- renderer$to_dataframe()
+ if (!is.null(initial_state)) {
+ # Drop the timesteps we didn't simulate from the data.
+ # It would just be full of NA.
+ data <- data[-(1:initial_state$timesteps),]
+ }
+
+ list(data=data, state=final_state)
}
#' @title Run a metapopulation model
@@ -206,6 +268,7 @@ run_metapop_simulation <- function(
correlations[[i]],
lagged_eir,
lagged_infectivity,
+ timesteps,
mixing[i,],
i
)
@@ -248,4 +311,4 @@ run_simulation_with_repetitions <- function(
}
)
do.call("rbind", dfs)
-}
\ No newline at end of file
+}
diff --git a/R/mortality_processes.R b/R/mortality_processes.R
index 65b090cb..1e7446d9 100644
--- a/R/mortality_processes.R
+++ b/R/mortality_processes.R
@@ -27,7 +27,7 @@ create_mortality_process <- function(variables, events, renderer, parameters) {
died <- individual::Bitset$new(pop)$insert(bernoulli_multi_p(deathrates))
renderer$render('natural_deaths', died$size(), timestep)
}
- reset_target(variables, events, died, 'S', timestep)
+ reset_target(variables, events, died, 'S', parameters, timestep)
sample_maternal_immunity(variables, died, timestep, parameters)
}
}
@@ -66,7 +66,7 @@ sample_maternal_immunity <- function(variables, target, timestep, parameters) {
# set their maternal immunities
birth_icm <- variables$ica$get_values(mothers) * parameters$pcm
- birth_ivm <- variables$ica$get_values(mothers) * parameters$pvm
+ birth_ivm <- variables$iva$get_values(mothers) * parameters$pvm
variables$icm$queue_update(birth_icm, target_group)
variables$ivm$queue_update(birth_ivm, target_group)
}
@@ -74,7 +74,7 @@ sample_maternal_immunity <- function(variables, target, timestep, parameters) {
}
}
-reset_target <- function(variables, events, target, state, timestep) {
+reset_target <- function(variables, events, target, state, parameters, timestep) {
if (target$size() > 0) {
# clear events
to_clear <- c(
@@ -106,13 +106,22 @@ reset_target <- function(variables, events, target, state, timestep) {
variables$drug_time$queue_update(-1, target)
# vaccination
- variables$pev_timestep$queue_update(-1, target)
+ variables$last_pev_timestep$queue_update(-1, target)
+ variables$last_eff_pev_timestep$queue_update(-1, target)
variables$pev_profile$queue_update(-1, target)
variables$tbv_vaccinated$queue_update(-1, target)
+ # spraying
+ variables$spray_time$queue_update(-1, target)
+
# onwards infectiousness
variables$infectivity$queue_update(0, target)
+ # treated compartment residence time:
+ if(!is.null(variables$dt)) {
+ variables$dt$queue_update(parameters$dt, target)
+ }
+
# zeta and zeta group and vector controls survive rebirth
}
}
diff --git a/R/mosquito_biology.R b/R/mosquito_biology.R
index 381450d9..4225a46f 100644
--- a/R/mosquito_biology.R
+++ b/R/mosquito_biology.R
@@ -220,7 +220,7 @@ create_mosquito_emergence_process <- function(
p_counts <- vnapply(
solvers,
function(solver) {
- solver_get_states(solver)[[ODE_INDICES[['P']]]]
+ solver$get_states()[[ODE_INDICES[['P']]]]
}
)
n <- sum(p_counts) * rate
diff --git a/R/parameters.R b/R/parameters.R
index 8a96989a..647b78b2 100644
--- a/R/parameters.R
+++ b/R/parameters.R
@@ -11,7 +11,7 @@
#' fixed state transitions:
#'
#' * dd - the delay for humans to move from state D to A; default = 5
-#' * dt - the delay for humans to move from state Tr to Ph; default = 5
+#' * dt - the delay for humans to move from state Tr to S; default = 5
#' * da - the delay for humans to move from state A to U; default = 195
#' * du - the delay for humans to move from state U to S; default = 110
#' * del - the delay for mosquitoes to move from state E to L; default = 6.64
@@ -192,6 +192,22 @@
#' * tbv_tra_mu - transmission reduction parameter; default = 12.63
#' * tbv_gamma1 - transmission reduction parameter; default = 2.5
#' * tbv_gamma2 - transmission reduction parameter; default = 0.06
+#'
+#' Antimalarial resistance parameters:
+#' please set antimalarial resistance parameters with the convenience functions in
+#' `antimalarial_resistance.R:set_antimalarial_resistance`
+#'
+#' * antimalarial_resistance - boolean for if antimalarial resistance is enabled; default = FALSE
+#' * antimalarial_resistance_drug - vector of drugs for which resistance can be parameterised; default = NULL
+#' * antimalarial_resistance_timesteps - vector of time steps on which resistance updates occur; default = NULL
+#' * artemisinin_resistant_proportion - vector of proportions of infections resistant to the artemisinin component of a given drug; default = NULL
+#' * partner_drug_resistance_proportion - vector of proportions of infections resistant to the parter drug component of a given drug; default = NULL
+#' * slow_parasite_clearance_probability - vector of probabilities of slow parasite clearance for a given drug; default = NULL
+#' * early_treatment_failure_probability - vector of probabilities of early treatment failure for a given drug; default = NULL
+#' * late_clinical_failure_probability - vector of probabilities of late clinical failure for a given drug; default = NULL
+#' * late_parasitological_failure_probability - vector of probabilities of late parasitological failure for a given drug; default = NULL
+#' * reinfection_during_prophylaxis_probability - vector of probabilities of reinfection during prophylaxis for a given drug; default = NULL
+#' * dt_slow_parasite_clearance - the delay for humans experiencing slow parasite clearance to move from state Tr to S; default = NULL
#'
#' rendering:
#' All values are in timesteps and all ranges are inclusive
@@ -377,6 +393,18 @@ get_parameters <- function(overrides = list()) {
tbv_timesteps = NULL,
tbv_coverages = NULL,
tbv_ages = NULL,
+ # antimalarial resistance
+ antimalarial_resistance = FALSE,
+ antimalarial_resistance_drug = NULL,
+ antimalarial_resistance_timesteps = NULL,
+ artemisinin_resistance_proportion = NULL,
+ partner_drug_resistance_proportion = NULL,
+ slow_parasite_clearance_probability = NULL,
+ early_treatment_failure_probability = NULL,
+ late_clinical_failure_probability = NULL,
+ late_parasitological_failure_probability = NULL,
+ reinfection_during_prophylaxis_probability = NULL,
+ dt_slow_parasite_clearance = NULL,
# flexible carrying capacity
carrying_capacity = FALSE,
carrying_capacity_timesteps = NULL,
diff --git a/R/pev.R b/R/pev.R
index 4c735552..e460e5c3 100644
--- a/R/pev.R
+++ b/R/pev.R
@@ -29,10 +29,18 @@ create_epi_pev_process <- function(
to_vaccinate <- variables$birth$get_index_of(
set = timestep - parameters$pev_epi_age
)
+
+ #ignore those who are scheduled for mass vaccination
+ if (!is.null(events$mass_pev_doses)) {
+ to_vaccinate <- to_vaccinate$and(
+ events$mass_pev_doses[[1]]$get_scheduled()$not()
+ )
+ }
+
if (parameters$pev_epi_min_wait == 0) {
target <- to_vaccinate$to_vector()
} else {
- not_recently_vaccinated <- variables$pev_timestep$get_index_of(
+ not_recently_vaccinated <- variables$last_pev_timestep$get_index_of(
a = max(timestep - parameters$pev_epi_min_wait, 0),
b = timestep
)$not(TRUE)
@@ -48,6 +56,9 @@ create_epi_pev_process <- function(
)
]
+ # Update the latest vaccination time
+ variables$last_pev_timestep$queue_update(timestep, target)
+
schedule_vaccination(
target,
events,
@@ -81,13 +92,22 @@ create_mass_pev_listener <- function(
in_age_group$or(variables$birth$get_index_of(a = min_birth, b = max_birth))
}
if (parameters$mass_pev_min_wait == 0) {
- target <- in_age_group$to_vector()
+ target <- in_age_group
} else {
- not_recently_vaccinated <- variables$pev_timestep$get_index_of(
+ not_recently_vaccinated <- variables$last_pev_timestep$get_index_of(
a = max(timestep - parameters$mass_pev_min_wait, 0),
b = timestep
)$not(TRUE)
- target <- in_age_group$and(not_recently_vaccinated)$to_vector()
+ target <- in_age_group$and(not_recently_vaccinated)
+ }
+
+ #ignore those who are scheduled for EPI vaccination
+ if (!is.null(events$pev_epi_doses)) {
+ target <- target$and(
+ events$pev_epi_doses[[1]]$get_scheduled()$not()
+ )$to_vector()
+ } else {
+ target <- target$to_vector()
}
time_index = which(parameters$mass_pev_timesteps == timestep)
@@ -99,17 +119,17 @@ create_mass_pev_listener <- function(
correlations
)
]
+
+ # Update the latest vaccination time
+ variables$last_pev_timestep$queue_update(timestep, target)
+
+ # Schedule future doses
schedule_vaccination(
target,
events,
parameters,
events$mass_pev_doses
)
- if (time_index < length(parameters$mass_pev_timesteps)) {
- events$mass_pev$schedule(
- parameters$mass_pev_timesteps[[time_index + 1]] - timestep
- )
- }
}
}
@@ -145,7 +165,7 @@ schedule_vaccination <- function(
create_pev_efficacy_listener <- function(variables, pev_profile_index) {
function(timestep, target) {
if (target$size() > 0) {
- variables$pev_timestep$queue_update(timestep, target)
+ variables$last_eff_pev_timestep$queue_update(timestep, target)
variables$pev_profile$queue_update(pev_profile_index, target)
}
}
@@ -154,6 +174,7 @@ create_pev_efficacy_listener <- function(variables, pev_profile_index) {
create_pev_booster_listener <- function(
variables,
coverage,
+ pev_distribution_timesteps,
booster_number,
pev_profile_index,
next_booster_event,
@@ -167,8 +188,13 @@ create_pev_booster_listener <- function(
force(next_booster_delay)
force(coverage)
function(timestep, target) {
- target <- sample_bitset(target, coverage)
- variables$pev_timestep$queue_update(timestep, target)
+ cov_t <- coverage[
+ match_timestep(pev_distribution_timesteps, timestep),
+ booster_number
+ ]
+ target <- sample_bitset(target, cov_t)
+ variables$last_pev_timestep$queue_update(timestep, target)
+ variables$last_eff_pev_timestep$queue_update(timestep, target)
variables$pev_profile$queue_update(pev_profile_index, target)
renderer$render(render_name, target$size(), timestep)
@@ -210,6 +236,7 @@ create_dosage_renderer <- function(renderer, strategy, dose) {
attach_pev_dose_listeners <- function(
variables,
parameters,
+ pev_distribution_timesteps,
dose_events,
booster_events,
booster_delays,
@@ -223,6 +250,12 @@ attach_pev_dose_listeners <- function(
dose_events[[d]]$add_listener(
create_dosage_renderer(renderer, strategy, d)
)
+ # update last vaccination on every primary dose
+ dose_events[[d]]$add_listener(
+ function(t, target) {
+ variables$last_pev_timestep$queue_update(t, target)
+ }
+ )
if (d == length(dose_events)) {
dose_events[[d]]$add_listener(
create_pev_efficacy_listener(
@@ -270,7 +303,8 @@ attach_pev_dose_listeners <- function(
booster_events[[b]]$add_listener(
create_pev_booster_listener(
variables = variables,
- coverage = booster_coverages[[b]],
+ coverage = booster_coverages,
+ pev_distribution_timesteps = pev_distribution_timesteps,
booster_number = b,
pev_profile_index = pev_profile_indices[[b + 1]],
next_booster_event = next_booster_event,
diff --git a/R/pev_parameters.R b/R/pev_parameters.R
index aeb6bd5b..a9509f96 100644
--- a/R/pev_parameters.R
+++ b/R/pev_parameters.R
@@ -62,9 +62,9 @@ rtss_booster_profile <- create_pev_profile(
#' age. Efficacy will take effect after the last dose
#'
#' @param parameters a list of parameters to modify
-#' @param profile primary vaccine profile of type PEVProfile
+#' @param profile a list of details for the vaccine profile, create with `create_pev_profile`
#' @param coverages a vector of coverages for the primary doses
-#' @param timesteps a vector of timesteps associated with coverages
+#' @param timesteps a vector of timesteps for each change in coverage
#' @param age the age when an individual will receive the first dose of the
#' vaccine (in timesteps)
#' @param min_wait the minimum acceptable time since the last vaccination (in
@@ -72,12 +72,11 @@ rtss_booster_profile <- create_pev_profile(
#' between an individual receiving the final dose and the first booster. When using
#' both set_mass_pev and set_pev_epi, this represents the minimum time between
#' an individual being vaccinated under one scheme and vaccinated under another.
-#' @param booster_timestep the timesteps (following the final dose) at which booster vaccinations are administered
-#' @param booster_coverage the proportion of the vaccinated population relative to the last vaccination (whether a previous booster or the primary series)
-#' @param booster_profile list of booster vaccine profiles, of type
-#' PEVProfile, for each timestep in booster_timeteps
+#' @param booster_spacing the timesteps (following the final primary dose) at which booster vaccinations are administered
+#' @param booster_coverage a matrix of coverages (timesteps x boosters) specifying the proportion the previously vaccinated population to continue receiving booster doses. The rows of the matrix must be the same size as `timesteps`. The columns of the matrix must be the same size as `booster_spacing`.
+#' @param booster_profile list of lists representing each booster profile, the outer list must be the same length as `booster_spacing`. Create vaccine profiles with `create_pev_profile`
#' @param seasonal_boosters logical, if TRUE the first booster timestep is
-#' relative to the start of the year, otherwise they are relative to the last dose
+#' relative to the start of the year, otherwise they are relative to the last primary dose
#' @export
set_pev_epi <- function(
parameters,
@@ -86,33 +85,47 @@ set_pev_epi <- function(
timesteps,
age,
min_wait,
- booster_timestep,
+ booster_spacing,
booster_coverage,
booster_profile,
seasonal_boosters = FALSE
) {
stopifnot(all(coverages >= 0) && all(coverages <= 1))
+ stopifnot(is.matrix(booster_coverage))
# Check that the primary timing parameters make sense
if(length(coverages) != length(timesteps)){
stop("coverages and timesteps must align")
}
+ # Check that booster_spacing are monotonically increasing
+ if (length(booster_spacing) > 1) {
+ if (!all(diff(booster_spacing) > 0)) {
+ stop('booster_spacing must be monotonically increasing')
+ }
+ }
+
# Check that seasonal booster parameters make sense
stopifnot(min_wait >= 0)
stopifnot(age >= 0)
stopifnot(is.logical(seasonal_boosters))
if (seasonal_boosters) {
- if(booster_timestep[[1]] < 0) {
- booster_timestep <- booster_timestep + 365
+ if(booster_spacing[[1]] < 0) {
+ booster_spacing <- booster_spacing + 365
}
}
# Check that the booster timing parameters make sense
- stopifnot((length(booster_timestep) == 0) || all(booster_timestep > 0))
+ stopifnot((length(booster_spacing) == 0) || all(booster_spacing > 0))
stopifnot((length(booster_coverage)) == 0 || all(booster_coverage >= 0 & booster_coverage <= 1))
- if (!all(c(length(booster_coverage), length(booster_timestep), length(booster_profile)) == length(booster_timestep))) {
- stop('booster_timestep and booster_coverage and booster_profile does not align')
+ if (!all(c(ncol(booster_coverage), length(booster_profile)) == length(booster_spacing))) {
+ stop('booster_spacing, booster_coverage and booster_profile do not align')
+ }
+ # Check that booster_coverage and timesteps align
+ if (length(booster_coverage) > 0) {
+ if (nrow(booster_coverage) != length(timesteps)) {
+ stop('booster_coverage and timesteps do not align')
+ }
}
# Index the new vaccine profiles
@@ -125,7 +138,7 @@ set_pev_epi <- function(
parameters$pev_epi_coverages <- coverages
parameters$pev_epi_timesteps <- timesteps
parameters$pev_epi_age <- age
- parameters$pev_epi_booster_timestep <- booster_timestep
+ parameters$pev_epi_booster_spacing <- booster_spacing
parameters$pev_epi_min_wait <- min_wait
parameters$pev_epi_booster_coverage <- booster_coverage
parameters$pev_epi_profile_indices <- profile_indices
@@ -139,7 +152,7 @@ set_pev_epi <- function(
#' Efficacy will take effect after the last dose
#'
#' @param parameters a list of parameters to modify
-#' @param profile primary vaccine profile of type PEVProfile
+#' @param profile a list of details for the vaccine profile, create with `create_pev_profile`
#' @param timesteps a vector of timesteps for each round of vaccinations
#' @param coverages the coverage for each round of vaccinations
#' @param min_wait the minimum acceptable time since the last vaccination (in timesteps);
@@ -147,10 +160,9 @@ set_pev_epi <- function(
#' time between an individual being vaccinated under one scheme and vaccinated under another.
#' @param min_ages for the target population, inclusive (in timesteps)
#' @param max_ages for the target population, inclusive (in timesteps)
-#' @param booster_timestep the timesteps (following the initial vaccination) at which booster vaccinations are administered
-#' @param booster_coverage the proportion of the vaccinated population relative to the last vaccination (whether a previous booster or the primary series)
-#' @param booster_profile list of booster vaccine profiles, of type
-#' PEVProfile, for each timestep in booster_timeteps
+#' @param booster_spacing the timesteps (following the final primary dose) at which booster vaccinations are administered
+#' @param booster_coverage a matrix of coverages (timesteps x boosters) specifying the proportion the previously vaccinated population to continue receiving booster doses. The rows of the matrix must be the same size as `timesteps`. The columns of the matrix must be the same size as `booster_spacing`.
+#' @param booster_profile list of lists representing each booster profile, the outer list must be the same length as `booster_spacing`. Create vaccine profiles with `create_pev_profile`
#' @export
set_mass_pev <- function(
parameters,
@@ -160,7 +172,7 @@ set_mass_pev <- function(
min_ages,
max_ages,
min_wait,
- booster_timestep,
+ booster_spacing,
booster_coverage,
booster_profile
) {
@@ -168,13 +180,28 @@ set_mass_pev <- function(
stopifnot(min_wait >= 0)
stopifnot(all(coverages >= 0) && all(coverages <= 1))
stopifnot(all(min_ages >= 0 & max_ages >= 0))
- stopifnot(all(booster_timestep > 0))
+ stopifnot(all(booster_spacing > 0))
stopifnot(all(booster_coverage >= 0 & booster_coverage <= 1))
if (length(min_ages) != length(max_ages)) {
stop('min and max ages do not align')
}
- if (!all(c(length(booster_coverage), length(booster_timestep), length(booster_profile)) == length(booster_timestep))) {
- stop('booster_timestep, booster_coverage and booster_profile does not align')
+
+ # Check that booster_spacing are monotonically increasing
+ if (length(booster_spacing) > 1) {
+ if (!all(diff(booster_spacing) > 0)) {
+ stop('booster_spacing must be monotonically increasing')
+ }
+ }
+
+ stopifnot((length(booster_coverage)) == 0 || all(booster_coverage >= 0 & booster_coverage <= 1))
+ if (!all(c(ncol(booster_coverage), length(booster_profile)) == length(booster_spacing))) {
+ stop('booster_spacing, booster_coverage and booster_profile do not align')
+ }
+ # Check that booster_coverage and timesteps align
+ if (length(booster_coverage) > 0) {
+ if (nrow(booster_coverage) != length(timesteps)) {
+ stop('booster_coverage and timesteps do not align')
+ }
}
# Index the new vaccine profiles
@@ -189,29 +216,8 @@ set_mass_pev <- function(
parameters$mass_pev_min_ages <- min_ages
parameters$mass_pev_max_ages <- max_ages
parameters$mass_pev_min_wait <- min_wait
- parameters$mass_pev_booster_timestep <- booster_timestep
+ parameters$mass_pev_booster_spacing <- booster_spacing
parameters$mass_pev_booster_coverage <- booster_coverage
parameters$mass_pev_profile_indices <- profile_indices
parameters
}
-
-#' @title Parameterise an TBV strategy
-#' @param parameters a list of parameters to modify
-#' @param timesteps a vector of timesteps for each round of vaccinations
-#' @param coverages the coverage for each round of vaccinations
-#' @param ages for each round (in years)
-#' vaccine
-#' @export
-set_tbv <- function(
- parameters,
- timesteps,
- coverages,
- ages
- ) {
- stopifnot(all(coverages >= 0) && all(coverages <= 1))
- parameters$tbv <- TRUE
- parameters$tbv_timesteps <- timesteps
- parameters$tbv_coverages <- coverages
- parameters$tbv_ages <- ages
- parameters
-}
diff --git a/R/processes.R b/R/processes.R
index 6109490f..1a7b2ebb 100644
--- a/R/processes.R
+++ b/R/processes.R
@@ -14,6 +14,7 @@
#' population and species in the simulation
#' @param lagged_infectivity a list of LaggedValue objects for FOIM for each population
#' in the simulation
+#' @param timesteps Number of timesteps
#' @param mixing a vector of mixing coefficients for the lagged transmission
#' values (default: 1)
#' @param mixing_index an index for this population's position in the
@@ -29,6 +30,7 @@ create_processes <- function(
correlations,
lagged_eir,
lagged_infectivity,
+ timesteps,
mixing = 1,
mixing_index = 1
) {
@@ -46,7 +48,7 @@ create_processes <- function(
create_exponential_decay_process(variables$iva, parameters$rva),
create_exponential_decay_process(variables$id, parameters$rid)
)
-
+
if (parameters$individual_mosquitoes) {
processes <- c(
processes,
@@ -59,7 +61,7 @@ create_processes <- function(
)
)
}
-
+
# ==============================
# Biting and mortality processes
# ==============================
@@ -80,7 +82,6 @@ create_processes <- function(
mixing,
mixing_index
),
- create_mortality_process(variables, events, renderer, parameters),
create_asymptomatic_progression_process(
variables$state,
parameters$dd,
@@ -102,17 +103,37 @@ create_processes <- function(
parameters$du,
variables$infectivity,
0
- ),
+ )
+ )
+
+ # =======================
+ # Antimalarial Resistance
+ # =======================
+ # Add an a new process which governs the transition from Tr to S when
+ # antimalarial resistance is simulated. The rate of transition switches
+ # from a parameter to a variable when antimalarial resistance == TRUE.
+
+ # Assign the dt input to a separate object with the default single parameter value:
+ dt_input <- parameters$dt
+
+ # If antimalarial resistance is switched on, assign dt variable values to the
+ if(parameters$antimalarial_resistance) {
+ dt_input <- variables$dt
+ }
+
+ # Create the progression process for Tr --> S specifying dt_input as the rate:
+ processes <- c(
+ processes,
create_progression_process(
variables$state,
'Tr',
'S',
- parameters$dt,
+ dt_input,
variables$infectivity,
0
)
)
-
+
# ===============
# ODE integration
# ===============
@@ -120,9 +141,9 @@ create_processes <- function(
processes,
create_solver_stepping_process(solvers, parameters)
)
-
+
# =========
- # RTS,S EPI
+ # PEV EPI
# =========
if (!is.null(parameters$pev_epi_coverage)) {
processes <- c(
@@ -137,7 +158,7 @@ create_processes <- function(
)
)
}
-
+
# =========
# PMC
# =========
@@ -156,7 +177,7 @@ create_processes <- function(
)
)
}
-
+
# =========
# Rendering
# =========
@@ -186,7 +207,7 @@ create_processes <- function(
),
create_compartmental_rendering_process(renderer, solvers, parameters)
)
-
+
if (parameters$individual_mosquitoes) {
processes <- c(
processes,
@@ -208,11 +229,11 @@ create_processes <- function(
)
)
}
-
+
# ======================
# Intervention processes
# ======================
-
+
if (parameters$bednets) {
processes <- c(
processes,
@@ -225,14 +246,14 @@ create_processes <- function(
net_usage_renderer(variables$net_time, renderer)
)
}
-
+
if (parameters$spraying) {
processes <- c(
processes,
- indoor_spraying(variables$spray_time, parameters, correlations)
+ indoor_spraying(variables$spray_time, renderer, parameters, correlations)
)
}
-
+
# ======================
# Progress bar process
# ======================
@@ -242,7 +263,12 @@ create_processes <- function(
create_progress_process(timesteps)
)
}
-
+
+ # Mortality step
+ processes <- c(
+ processes,
+ create_mortality_process(variables, events, renderer, parameters))
+
processes
}
@@ -259,14 +285,15 @@ create_processes <- function(
#' @param rate the exponential rate
#' @noRd
create_exponential_decay_process <- function(variable, rate) {
+ stopifnot(inherits(variable, "DoubleVariable"))
decay_rate <- exp(-1/rate)
- function(timestep) variable$queue_update(variable$get_values() * decay_rate)
+ exponential_process_cpp(variable$.variable, decay_rate)
}
#' @title Create and initialise lagged_infectivity object
#'
#' @param variables model variables for initialisation
-#' @param parameters model parameters
+#' @param parameters model parameters
#' @noRd
create_lagged_infectivity <- function(variables, parameters) {
age <- get_age(variables$birth$get_values(), 0)
@@ -283,7 +310,7 @@ create_lagged_infectivity <- function(variables, parameters) {
#'
#' @param variables model variables for initialisation
#' @param solvers model differential equation solvers
-#' @param parameters model parameters
+#' @param parameters model parameters
#' @noRd
create_lagged_eir <- function(variables, solvers, parameters) {
lapply(
diff --git a/R/render.R b/R/render.R
index 3fcf4882..60cb5b73 100644
--- a/R/render.R
+++ b/R/render.R
@@ -146,7 +146,7 @@ create_total_M_renderer_compartmental <- function(renderer, solvers, parameters)
function(timestep) {
total_M <- 0
for (i in seq_along(solvers)) {
- row <- solver_get_states(solvers[[i]])
+ row <- solvers[[i]]$get_states()
species_M <- sum(row[ADULT_ODE_INDICES])
total_M <- total_M + species_M
renderer$render(paste0('total_M_', parameters$species[[i]]), species_M, timestep)
diff --git a/R/tbv.R b/R/tbv.R
index 27c29dfb..d0f75de1 100644
--- a/R/tbv.R
+++ b/R/tbv.R
@@ -75,11 +75,6 @@ create_tbv_listener <- function(variables, events, parameters, correlations, ren
to_vaccinate
)
}
- if (time_index < length(parameters$tbv_timesteps)) {
- events$tbv_vaccination$schedule(
- parameters$tbv_timesteps[[time_index + 1]] - timestep
- )
- }
}
}
diff --git a/R/tbv_parameters.R b/R/tbv_parameters.R
new file mode 100644
index 00000000..089455a2
--- /dev/null
+++ b/R/tbv_parameters.R
@@ -0,0 +1,23 @@
+#' @title Parameterise an TBV strategy
+#' @param parameters a list of parameters to modify
+#' @param timesteps a vector of timesteps for each round of vaccinations
+#' @param coverages the coverage for each round of vaccinations
+#' @param ages a vector of ages of the target population (in years)
+#' @export
+set_tbv <- function(
+ parameters,
+ timesteps,
+ coverages,
+ ages
+ ) {
+ stopifnot(all(coverages >= 0) && all(coverages <= 1))
+ if(length(coverages) != length(timesteps)){
+ stop("coverages and timesteps do no align")
+ }
+
+ parameters$tbv <- TRUE
+ parameters$tbv_timesteps <- timesteps
+ parameters$tbv_coverages <- coverages
+ parameters$tbv_ages <- ages
+ parameters
+}
diff --git a/R/utils.R b/R/utils.R
index f25244bd..2262c939 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -64,3 +64,27 @@ rtexp <- function(n, m, t) { itexp(runif(n), m, t) }
match_timestep <- function(ts, t) {
min(sum(ts <= t), length(ts))
}
+
+#' @title a placeholder class to save the random number generator class.
+#' @description the class integrates with the simulation loop to save and
+#' restore the random number generator class when appropriate.
+#' @noRd
+RandomState <- R6::R6Class(
+ 'RandomState',
+ private = list(
+ restore_random_state = NULL
+ ),
+ public = list(
+ initialize = function(restore_random_state) {
+ private$restore_random_state <- restore_random_state
+ },
+ save_state = function() {
+ random_save_state()
+ },
+ restore_state = function(t, state) {
+ if (private$restore_random_state) {
+ random_restore_state(state)
+ }
+ }
+ )
+)
diff --git a/R/variables.R b/R/variables.R
index 0931b3f7..092a6431 100644
--- a/R/variables.R
+++ b/R/variables.R
@@ -1,4 +1,4 @@
-#' @title Define model variables
+#' @title Define model variables
#' @description
#' create_variables creates the human and mosquito variables for
#' the model. Variables are used to track real data for each individual over
@@ -18,10 +18,13 @@
#' * ID - Acquired immunity to detectability
#' * zeta - Heterogeneity of human individuals
#' * zeta_group - Discretised heterogeneity of human individuals
-#' * pev_timestep - The timestep of the last pev vaccination (-1 if there
-#' haven't been any)
-#' * pev_profile - The index of the profile of the last administered pev vaccine
-#' (-1 if there haven't been any)
+#' * last_pev_timestep - The timestep of the last pev vaccination (-1 if there
+#' * last_eff_pev_timestep - The timestep of the last efficacious pev
+#' vaccination, including final primary dose and booster doses (-1 if there have not been any)
+#' * pev_profile - The index of the efficacy profile of any pev vaccinations.
+#' Not set until the final primary dose.
+#' This is only set on the final primary dose and subsequent booster doses
+#' (-1 otherwise)
#' * tbv_vaccinated - The timstep of the last tbv vaccination (-1 if there
#' haven't been any
#' * net_time - The timestep when a net was last put up (-1 if never)
@@ -30,6 +33,9 @@
#' * drug - The last prescribed drug
#' * drug_time - The timestep of the last drug
#'
+#' Antimalarial resistance variables are:
+#' * dt - the delay for humans to move from state Tr to state S
+#'
#' Mosquito variables are:
#' * mosquito_state - the state of the mosquito, a category Sm|Pm|Im|NonExistent
#' * species - the species of mosquito, this is a category gamb|fun|arab
@@ -188,7 +194,8 @@ create_variables <- function(parameters) {
drug <- individual::IntegerVariable$new(rep(0, size))
drug_time <- individual::IntegerVariable$new(rep(-1, size))
- pev_timestep <- individual::IntegerVariable$new(rep(-1, size))
+ last_pev_timestep <- individual::IntegerVariable$new(rep(-1, size))
+ last_eff_pev_timestep <- individual::IntegerVariable$new(rep(-1, size))
pev_profile <- individual::IntegerVariable$new(rep(-1, size))
tbv_vaccinated <- individual::DoubleVariable$new(rep(-1, size))
@@ -215,12 +222,22 @@ create_variables <- function(parameters) {
infectivity = infectivity,
drug = drug,
drug_time = drug_time,
- pev_timestep = pev_timestep,
+ last_pev_timestep = last_pev_timestep,
+ last_eff_pev_timestep = last_eff_pev_timestep,
pev_profile = pev_profile,
tbv_vaccinated = tbv_vaccinated,
net_time = net_time,
spray_time = spray_time
)
+
+ # Add variables for antimalarial resistance state residency times (dt)
+ if(parameters$antimalarial_resistance) {
+ dt <- individual::DoubleVariable$new(rep(parameters$dt, size))
+ variables <- c(
+ variables,
+ dt = dt
+ )
+ }
# Add variables for individual mosquitoes
if (parameters$individual_mosquitoes) {
diff --git a/R/vector_control.R b/R/vector_control.R
index 29c92d35..10d90c90 100644
--- a/R/vector_control.R
+++ b/R/vector_control.R
@@ -105,7 +105,8 @@ prob_bitten <- function(
#' @param parameters the model parameters
#' @param correlations correlation parameters
#' @noRd
-indoor_spraying <- function(spray_time, parameters, correlations) {
+indoor_spraying <- function(spray_time, render, parameters, correlations) {
+ renderer$set_default('n_spray', 0)
function(timestep) {
matches <- timestep == parameters$spraying_timesteps
if (any(matches)) {
@@ -116,6 +117,7 @@ indoor_spraying <- function(spray_time, parameters, correlations) {
correlations
))
spray_time$queue_update(timestep, target)
+ renderer$render('n_spray', length(target), timestep)
}
}
}
diff --git a/R/vector_control_parameters.R b/R/vector_control_parameters.R
index 97c3ce56..43c7c854 100644
--- a/R/vector_control_parameters.R
+++ b/R/vector_control_parameters.R
@@ -143,23 +143,24 @@ set_spraying <- function(
#'
#' @param parameters the model parameters
#' @param timesteps vector of timesteps for each rescale change
-#' @param carrying_capacity matrix of baseline carrying_capacity for each species
-#' With nrows = length(timesteps), ncols = length(species)
+#' @param carrying_capacity_scalers matrix of scaling factors to scale the baseline
+#' carrying capacity for each species with nrows = length(timesteps),
+#' ncols = length(species)
#'
#' @export
set_carrying_capacity <- function(
parameters,
timesteps,
- carrying_capacity
+ carrying_capacity_scalers
){
- stopifnot(nrow(carrying_capacity) == length(timesteps))
- stopifnot(ncol(carrying_capacity) == length(parameters$species))
+ stopifnot(nrow(carrying_capacity_scalers) == length(timesteps))
+ stopifnot(ncol(carrying_capacity_scalers) == length(parameters$species))
stopifnot(min(timesteps) > 0)
- stopifnot(min(carrying_capacity) >= 0)
+ stopifnot(min(carrying_capacity_scalers) >= 0)
parameters$carrying_capacity <- TRUE
parameters$carrying_capacity_timesteps <- timesteps
- parameters$carrying_capacity_values <- carrying_capacity
+ parameters$carrying_capacity_scalers <- carrying_capacity_scalers
parameters
}
diff --git a/man/CorrelationParameters.Rd b/man/CorrelationParameters.Rd
index c2e6ada7..0aebf688 100644
--- a/man/CorrelationParameters.Rd
+++ b/man/CorrelationParameters.Rd
@@ -2,14 +2,37 @@
% Please edit documentation in R/correlation.R
\name{CorrelationParameters}
\alias{CorrelationParameters}
-\title{Class: Correlation parameters
-Describes an event in the simulation}
+\title{Class: Correlation parameters}
\description{
Class: Correlation parameters
-Describes an event in the simulation
Class: Correlation parameters
-Describes an event in the simulation
+}
+\details{
+This class implements functionality that allows interventions to be
+correlated, positively or negatively. By default, interventions are applied
+independently and an individual's probability of receiving two interventions
+(either two separate interventions or two rounds of the same one) is the
+product of the probability of receiving each one.
+
+By setting a positive correlation between two interventions, we can make it
+so that the individuals that receive intervention A are more likely to
+receive intervention B. Conversely, a negative correlation will make it such
+that individuals that receive intervention A are less likely to also receive
+intervention B.
+
+Broadly speaking, the implementation works by assigning at startup a weight
+to each individual and intervention pair, reflecting how likely an individual
+is to receive that intervention. Those weights are derived stochastically
+from the configured correlation parameters.
+
+For a detailed breakdown of the calculations, see Protocol S2 of
+Griffin et al. (2010).
+Derive the mvnorm from the configured correlations.
+
+If a \code{restored_mvnorm} is specified, its columns (corresponding to
+restored interventions) will be re-used as is. Missing columns (for new
+interventions) are derived in accordance with the restored data.
}
\section{Methods}{
\subsection{Public methods}{
@@ -19,6 +42,8 @@ Describes an event in the simulation
\item \href{#method-CorrelationParameters-inter_intervention_rho}{\code{CorrelationParameters$inter_intervention_rho()}}
\item \href{#method-CorrelationParameters-sigma}{\code{CorrelationParameters$sigma()}}
\item \href{#method-CorrelationParameters-mvnorm}{\code{CorrelationParameters$mvnorm()}}
+\item \href{#method-CorrelationParameters-save_state}{\code{CorrelationParameters$save_state()}}
+\item \href{#method-CorrelationParameters-restore_state}{\code{CorrelationParameters$restore_state()}}
\item \href{#method-CorrelationParameters-clone}{\code{CorrelationParameters$clone()}}
}
}
@@ -28,13 +53,15 @@ Describes an event in the simulation
\subsection{Method \code{new()}}{
initialise correlation parameters
\subsection{Usage}{
-\if{html}{\out{
}}\preformatted{CorrelationParameters$new(parameters)}\if{html}{\out{
}}
+\if{html}{\out{}}\preformatted{CorrelationParameters$new(population, interventions)}\if{html}{\out{
}}
}
\subsection{Arguments}{
\if{html}{\out{}}
\describe{
-\item{\code{parameters}}{model parameters}
+\item{\code{population}}{popularion size}
+
+\item{\code{interventions}}{character vector with the name of enabled interventions}
}
\if{html}{\out{
}}
}
@@ -101,6 +128,41 @@ multivariate norm draws for these parameters
\if{html}{\out{}}\preformatted{CorrelationParameters$mvnorm()}\if{html}{\out{
}}
}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-CorrelationParameters-save_state}{}}}
+\subsection{Method \code{save_state()}}{
+Save the correlation state.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{CorrelationParameters$save_state()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-CorrelationParameters-restore_state}{}}}
+\subsection{Method \code{restore_state()}}{
+Restore the correlation state.
+
+Only the randomly drawn weights are restored. The object needs to be
+initialized with the same rhos.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{CorrelationParameters$restore_state(timestep, state)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{timestep}}{the timestep at which simulation is resumed. This
+parameter's value is ignored, it only exists to conform to a uniform
+interface.}
+
+\item{\code{state}}{a previously saved correlation state, as returned by the
+save_state method.}
+}
+\if{html}{\out{
}}
+}
}
\if{html}{\out{
}}
\if{html}{\out{}}
diff --git a/man/get_antimalarial_resistance_parameters.Rd b/man/get_antimalarial_resistance_parameters.Rd
new file mode 100644
index 00000000..71de6961
--- /dev/null
+++ b/man/get_antimalarial_resistance_parameters.Rd
@@ -0,0 +1,19 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/antimalarial_resistance.R
+\name{get_antimalarial_resistance_parameters}
+\alias{get_antimalarial_resistance_parameters}
+\title{Retrieve resistance parameters}
+\usage{
+get_antimalarial_resistance_parameters(parameters, drugs, timestep)
+}
+\arguments{
+\item{parameters}{the model parameters}
+
+\item{drugs}{vector of integers representing the drugs administered to each individual receiving treatment}
+
+\item{timestep}{the current time step}
+}
+\description{
+Retrieve the resistance parameters associated with the drug each individual receiving clinical
+treatment has been administered in the current time step.
+}
diff --git a/man/get_correlation_parameters.Rd b/man/get_correlation_parameters.Rd
index 21995b91..068aed64 100644
--- a/man/get_correlation_parameters.Rd
+++ b/man/get_correlation_parameters.Rd
@@ -27,7 +27,7 @@ parameters <- set_mass_pev(
min_wait = 0,
min_ages = 100,
max_ages = 1000,
- booster_timestep = numeric(0),
+ booster_spacing = numeric(0),
booster_coverage = numeric(0),
booster_profile = NULL
)
diff --git a/man/get_parameters.Rd b/man/get_parameters.Rd
index 54188c01..e43792d5 100644
--- a/man/get_parameters.Rd
+++ b/man/get_parameters.Rd
@@ -13,7 +13,7 @@ The parameters are defined below.
fixed state transitions:
\itemize{
\item dd - the delay for humans to move from state D to A; default = 5
-\item dt - the delay for humans to move from state Tr to Ph; default = 5
+\item dt - the delay for humans to move from state Tr to S; default = 5
\item da - the delay for humans to move from state A to U; default = 195
\item du - the delay for humans to move from state U to S; default = 110
\item del - the delay for mosquitoes to move from state E to L; default = 6.64
@@ -214,6 +214,23 @@ please set TBV parameters with the convenience functions in
\item tbv_gamma2 - transmission reduction parameter; default = 0.06
}
+Antimalarial resistance parameters:
+please set antimalarial resistance parameters with the convenience functions in
+\code{antimalarial_resistance.R:set_antimalarial_resistance}
+\itemize{
+\item antimalarial_resistance - boolean for if antimalarial resistance is enabled; default = FALSE
+\item antimalarial_resistance_drug - vector of drugs for which resistance can be parameterised; default = NULL
+\item antimalarial_resistance_timesteps - vector of time steps on which resistance updates occur; default = NULL
+\item artemisinin_resistant_proportion - vector of proportions of infections resistant to the artemisinin component of a given drug; default = NULL
+\item partner_drug_resistance_proportion - vector of proportions of infections resistant to the parter drug component of a given drug; default = NULL
+\item slow_parasite_clearance_probability - vector of probabilities of slow parasite clearance for a given drug; default = NULL
+\item early_treatment_failure_probability - vector of probabilities of early treatment failure for a given drug; default = NULL
+\item late_clinical_failure_probability - vector of probabilities of late clinical failure for a given drug; default = NULL
+\item late_parasitological_failure_probability - vector of probabilities of late parasitological failure for a given drug; default = NULL
+\item reinfection_during_prophylaxis_probability - vector of probabilities of reinfection during prophylaxis for a given drug; default = NULL
+\item dt_slow_parasite_clearance - the delay for humans experiencing slow parasite clearance to move from state Tr to S; default = NULL
+}
+
rendering:
All values are in timesteps and all ranges are inclusive
\itemize{
diff --git a/man/run_resumable_simulation.Rd b/man/run_resumable_simulation.Rd
new file mode 100644
index 00000000..8991fd1c
--- /dev/null
+++ b/man/run_resumable_simulation.Rd
@@ -0,0 +1,34 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/model.R
+\name{run_resumable_simulation}
+\alias{run_resumable_simulation}
+\title{Run the simulation in a resumable way}
+\usage{
+run_resumable_simulation(
+ timesteps,
+ parameters = NULL,
+ correlations = NULL,
+ initial_state = NULL,
+ restore_random_state = FALSE
+)
+}
+\arguments{
+\item{timesteps}{the timestep at which to stop the simulation}
+
+\item{parameters}{a named list of parameters to use}
+
+\item{correlations}{correlation parameters}
+
+\item{initial_state}{the state from which the simulation is resumed}
+
+\item{restore_random_state}{if TRUE, restore the random number generator's state from the checkpoint.}
+}
+\value{
+a list with two entries, one for the dataframe of results and one for the final
+simulation state.
+}
+\description{
+this function accepts an initial simulation state as an argument, and returns the
+final state after running all of its timesteps. This allows one run to be resumed, possibly
+having changed some of the parameters.
+}
diff --git a/man/run_simulation.Rd b/man/run_simulation.Rd
index 64cac9c4..383b38f6 100644
--- a/man/run_simulation.Rd
+++ b/man/run_simulation.Rd
@@ -90,5 +90,9 @@ subpatent
susceptible
\item net_usage: the number people protected by a bed net
\item mosquito_deaths: number of adult female mosquitoes who die this timestep
+\item n_drug_efficacy_failures: number of clinically treated individuals whose treatment failed due to drug efficacy
+\item n_early_treatment_failure: number of clinically treated individuals who experienced early treatment failure
+\item n_successfully_treated: number of clinically treated individuals who are treated successfully (includes individuals who experience slow parasite clearance)
+\item n_slow_parasite_clearance: number of clinically treated individuals who experienced slow parasite clearance
}
}
diff --git a/man/set_antimalarial_resistance.Rd b/man/set_antimalarial_resistance.Rd
new file mode 100644
index 00000000..56627125
--- /dev/null
+++ b/man/set_antimalarial_resistance.Rd
@@ -0,0 +1,46 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/antimalarial_resistance.R
+\name{set_antimalarial_resistance}
+\alias{set_antimalarial_resistance}
+\title{Parameterise antimalarial resistance}
+\usage{
+set_antimalarial_resistance(
+ parameters,
+ drug,
+ timesteps,
+ artemisinin_resistance_proportion,
+ partner_drug_resistance_proportion,
+ slow_parasite_clearance_probability,
+ early_treatment_failure_probability,
+ late_clinical_failure_probability,
+ late_parasitological_failure_probability,
+ reinfection_during_prophylaxis_probability,
+ slow_parasite_clearance_time
+)
+}
+\arguments{
+\item{parameters}{the model parameters}
+
+\item{drug}{the index of the drug which resistance is being set, as set by the set_drugs() function, in the parameter list}
+
+\item{timesteps}{vector of time steps for each update to resistance proportion and resistance outcome probability}
+
+\item{artemisinin_resistance_proportion}{vector of updates to the proportions of infections that are artemisinin resistant at time t}
+
+\item{partner_drug_resistance_proportion}{vector of updates to the proportions of infections that are partner-drug resistant at time t}
+
+\item{slow_parasite_clearance_probability}{vector of updates to the proportion of artemisinin-resistant infections that result in early treatment failure}
+
+\item{early_treatment_failure_probability}{vector of updates to the proportion of artemisinin-resistant infections that result in slow parasite clearance}
+
+\item{late_clinical_failure_probability}{vector of updates to the proportion of partner-drug-resistant infections that result in late clinical failure}
+
+\item{late_parasitological_failure_probability}{vector of updates to the proportion of partner-drug-resistant infections that result in late parasitological failure}
+
+\item{reinfection_during_prophylaxis_probability}{vector of updates to the proportion of partner-drug-resistant infections that result in reinfection during prophylaxis}
+
+\item{slow_parasite_clearance_time}{single value representing the mean time individual's experiencing slow parasite clearance reside in the treated state}
+}
+\description{
+Parameterise antimalarial resistance
+}
diff --git a/man/set_carrying_capacity.Rd b/man/set_carrying_capacity.Rd
index eb453800..cf585c6b 100644
--- a/man/set_carrying_capacity.Rd
+++ b/man/set_carrying_capacity.Rd
@@ -4,15 +4,16 @@
\alias{set_carrying_capacity}
\title{Parameterise custom baseline carrying capacity}
\usage{
-set_carrying_capacity(parameters, timesteps, carrying_capacity)
+set_carrying_capacity(parameters, timesteps, carrying_capacity_scalers)
}
\arguments{
\item{parameters}{the model parameters}
\item{timesteps}{vector of timesteps for each rescale change}
-\item{carrying_capacity}{matrix of baseline carrying_capacity for each species
-With nrows = length(timesteps), ncols = length(species)}
+\item{carrying_capacity_scalers}{matrix of scaling factors to scale the baseline
+carrying capacity for each species with nrows = length(timesteps),
+ncols = length(species)}
}
\description{
Allows the user to set a completely flexible and custom
diff --git a/man/set_mass_pev.Rd b/man/set_mass_pev.Rd
index 7c28547f..ef3263e5 100644
--- a/man/set_mass_pev.Rd
+++ b/man/set_mass_pev.Rd
@@ -12,7 +12,7 @@ set_mass_pev(
min_ages,
max_ages,
min_wait,
- booster_timestep,
+ booster_spacing,
booster_coverage,
booster_profile
)
@@ -20,7 +20,7 @@ set_mass_pev(
\arguments{
\item{parameters}{a list of parameters to modify}
-\item{profile}{primary vaccine profile of type PEVProfile}
+\item{profile}{a list of details for the vaccine profile, create with \code{create_pev_profile}}
\item{timesteps}{a vector of timesteps for each round of vaccinations}
@@ -34,12 +34,11 @@ set_mass_pev(
When using both set_mass_pev and set_pev_epi, this represents the minimum
time between an individual being vaccinated under one scheme and vaccinated under another.}
-\item{booster_timestep}{the timesteps (following the initial vaccination) at which booster vaccinations are administered}
+\item{booster_spacing}{the timesteps (following the final primary dose) at which booster vaccinations are administered}
-\item{booster_coverage}{the proportion of the vaccinated population relative to the last vaccination (whether a previous booster or the primary series)}
+\item{booster_coverage}{a matrix of coverages (timesteps x boosters) specifying the proportion the previously vaccinated population to continue receiving booster doses. The rows of the matrix must be the same size as \code{timesteps}. The columns of the matrix must be the same size as \code{booster_spacing}.}
-\item{booster_profile}{list of booster vaccine profiles, of type
-PEVProfile, for each timestep in booster_timeteps}
+\item{booster_profile}{list of lists representing each booster profile, the outer list must be the same length as \code{booster_spacing}. Create vaccine profiles with \code{create_pev_profile}}
}
\description{
distribute pre-erythrocytic vaccine to a population in an age range.
diff --git a/man/set_mda.Rd b/man/set_mda.Rd
index 0b6f81c0..5437faeb 100644
--- a/man/set_mda.Rd
+++ b/man/set_mda.Rd
@@ -18,8 +18,7 @@ round}
\item{min_ages}{a vector of minimum ages of the target population for each round exclusive (in timesteps)}
-\item{max_ages}{a vector of maximum ages of the target population for each round exclusive (in timesteps)
-drug}
+\item{max_ages}{a vector of maximum ages of the target population for each round exclusive (in timesteps)}
}
\description{
Parameterise a Mass Drug Administration
diff --git a/man/set_pev_epi.Rd b/man/set_pev_epi.Rd
index 4dea14ed..077811c3 100644
--- a/man/set_pev_epi.Rd
+++ b/man/set_pev_epi.Rd
@@ -11,7 +11,7 @@ set_pev_epi(
timesteps,
age,
min_wait,
- booster_timestep,
+ booster_spacing,
booster_coverage,
booster_profile,
seasonal_boosters = FALSE
@@ -20,11 +20,11 @@ set_pev_epi(
\arguments{
\item{parameters}{a list of parameters to modify}
-\item{profile}{primary vaccine profile of type PEVProfile}
+\item{profile}{a list of details for the vaccine profile, create with \code{create_pev_profile}}
\item{coverages}{a vector of coverages for the primary doses}
-\item{timesteps}{a vector of timesteps associated with coverages}
+\item{timesteps}{a vector of timesteps for each change in coverage}
\item{age}{the age when an individual will receive the first dose of the
vaccine (in timesteps)}
@@ -35,15 +35,14 @@ between an individual receiving the final dose and the first booster. When using
both set_mass_pev and set_pev_epi, this represents the minimum time between
an individual being vaccinated under one scheme and vaccinated under another.}
-\item{booster_timestep}{the timesteps (following the final dose) at which booster vaccinations are administered}
+\item{booster_spacing}{the timesteps (following the final primary dose) at which booster vaccinations are administered}
-\item{booster_coverage}{the proportion of the vaccinated population relative to the last vaccination (whether a previous booster or the primary series)}
+\item{booster_coverage}{a matrix of coverages (timesteps x boosters) specifying the proportion the previously vaccinated population to continue receiving booster doses. The rows of the matrix must be the same size as \code{timesteps}. The columns of the matrix must be the same size as \code{booster_spacing}.}
-\item{booster_profile}{list of booster vaccine profiles, of type
-PEVProfile, for each timestep in booster_timeteps}
+\item{booster_profile}{list of lists representing each booster profile, the outer list must be the same length as \code{booster_spacing}. Create vaccine profiles with \code{create_pev_profile}}
\item{seasonal_boosters}{logical, if TRUE the first booster timestep is
-relative to the start of the year, otherwise they are relative to the last dose}
+relative to the start of the year, otherwise they are relative to the last primary dose}
}
\description{
distribute vaccine when an individual becomes a certain
diff --git a/man/set_pmc.Rd b/man/set_pmc.Rd
index 62625c04..2985e29a 100644
--- a/man/set_pmc.Rd
+++ b/man/set_pmc.Rd
@@ -11,10 +11,10 @@ set_pmc(parameters, drug, timesteps, coverages, ages)
\item{drug}{the index of the drug to administer}
-\item{timesteps}{a vector of timesteps for each round of PMC}
+\item{timesteps}{a vector of timesteps for each change in coverage}
-\item{coverages}{a vector of the proportion of the target population who receive each
-round}
+\item{coverages}{a vector of proportions of the target population to receive
+the intervention}
\item{ages}{a vector of ages at which PMC is administered (in timesteps)}
}
diff --git a/man/set_tbv.Rd b/man/set_tbv.Rd
index 34a641aa..23149e6d 100644
--- a/man/set_tbv.Rd
+++ b/man/set_tbv.Rd
@@ -1,5 +1,5 @@
% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/pev_parameters.R
+% Please edit documentation in R/tbv_parameters.R
\name{set_tbv}
\alias{set_tbv}
\title{Parameterise an TBV strategy}
@@ -13,8 +13,7 @@ set_tbv(parameters, timesteps, coverages, ages)
\item{coverages}{the coverage for each round of vaccinations}
-\item{ages}{for each round (in years)
-vaccine}
+\item{ages}{a vector of ages of the target population (in years)}
}
\description{
Parameterise an TBV strategy
diff --git a/src/Random.cpp b/src/Random.cpp
index fad22963..a4c55a7a 100644
--- a/src/Random.cpp
+++ b/src/Random.cpp
@@ -60,8 +60,8 @@ void Random::prop_sample_bucket(
// all probabilities are the same
if (heavy == n) {
- for (auto i = 0; i < size; ++i) {
- *result = (*rng)(n);
+ for (size_t i = 0; i < size; ++i) {
+ *result = (*rng)((uint64_t)n);
++result;
}
return;
@@ -121,11 +121,22 @@ void Random::prop_sample_bucket(
}
// sample
- for (auto i = 0; i < size; ++i) {
- size_t bucket = (*rng)(n);
- double acceptance = dqrng::uniform01((*rng)());
+ for (size_t i = 0; i < size; ++i) {
+ size_t bucket = (*rng)((uint64_t)n);
+ double acceptance = rng->uniform01();
*result = (acceptance < dividing_probs[bucket]) ? bucket :
alternative_index[bucket];
++result;
}
}
+
+std::string Random::save_state() {
+ std::ostringstream stream;
+ stream << *rng;
+ return stream.str();
+}
+
+void Random::restore_state(std::string state) {
+ std::istringstream stream(state);
+ stream >> *rng;
+}
diff --git a/src/Random.h b/src/Random.h
index b6e15039..e796fb9a 100644
--- a/src/Random.h
+++ b/src/Random.h
@@ -58,6 +58,9 @@ class Random : public RandomInterface {
Random(Random &&other) = delete;
Random& operator=(const Random &other) = delete;
Random& operator=(Random &&other) = delete;
+
+ std::string save_state();
+ void restore_state(std::string state);
private:
Random() : rng(dqrng::generator(42)) {};
};
diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp
index f5c226fd..e2a6b9d6 100644
--- a/src/RcppExports.cpp
+++ b/src/RcppExports.cpp
@@ -40,6 +40,28 @@ BEGIN_RCPP
return R_NilValue;
END_RCPP
}
+// adult_mosquito_model_save_state
+std::vector adult_mosquito_model_save_state(Rcpp::XPtr model);
+RcppExport SEXP _malariasimulation_adult_mosquito_model_save_state(SEXP modelSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< Rcpp::XPtr >::type model(modelSEXP);
+ rcpp_result_gen = Rcpp::wrap(adult_mosquito_model_save_state(model));
+ return rcpp_result_gen;
+END_RCPP
+}
+// adult_mosquito_model_restore_state
+void adult_mosquito_model_restore_state(Rcpp::XPtr model, std::vector state);
+RcppExport SEXP _malariasimulation_adult_mosquito_model_restore_state(SEXP modelSEXP, SEXP stateSEXP) {
+BEGIN_RCPP
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< Rcpp::XPtr >::type model(modelSEXP);
+ Rcpp::traits::input_parameter< std::vector >::type state(stateSEXP);
+ adult_mosquito_model_restore_state(model, state);
+ return R_NilValue;
+END_RCPP
+}
// create_adult_solver
Rcpp::XPtr create_adult_solver(Rcpp::XPtr model, std::vector init, double r_tol, double a_tol, size_t max_steps);
RcppExport SEXP _malariasimulation_create_adult_solver(SEXP modelSEXP, SEXP initSEXP, SEXP r_tolSEXP, SEXP a_tolSEXP, SEXP max_stepsSEXP) {
@@ -157,6 +179,18 @@ BEGIN_RCPP
return rcpp_result_gen;
END_RCPP
}
+// exponential_process_cpp
+Rcpp::XPtr exponential_process_cpp(Rcpp::XPtr variable, const double rate);
+RcppExport SEXP _malariasimulation_exponential_process_cpp(SEXP variableSEXP, SEXP rateSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< Rcpp::XPtr >::type variable(variableSEXP);
+ Rcpp::traits::input_parameter< const double >::type rate(rateSEXP);
+ rcpp_result_gen = Rcpp::wrap(exponential_process_cpp(variable, rate));
+ return rcpp_result_gen;
+END_RCPP
+}
// solver_get_states
std::vector solver_get_states(Rcpp::XPtr solver);
RcppExport SEXP _malariasimulation_solver_get_states(SEXP solverSEXP) {
@@ -168,6 +202,18 @@ BEGIN_RCPP
return rcpp_result_gen;
END_RCPP
}
+// solver_set_states
+void solver_set_states(Rcpp::XPtr solver, double t, std::vector state);
+RcppExport SEXP _malariasimulation_solver_set_states(SEXP solverSEXP, SEXP tSEXP, SEXP stateSEXP) {
+BEGIN_RCPP
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< Rcpp::XPtr >::type solver(solverSEXP);
+ Rcpp::traits::input_parameter< double >::type t(tSEXP);
+ Rcpp::traits::input_parameter< std::vector >::type state(stateSEXP);
+ solver_set_states(solver, t, state);
+ return R_NilValue;
+END_RCPP
+}
// solver_step
void solver_step(Rcpp::XPtr solver);
RcppExport SEXP _malariasimulation_solver_step(SEXP solverSEXP) {
@@ -215,6 +261,28 @@ BEGIN_RCPP
return R_NilValue;
END_RCPP
}
+// timeseries_save_state
+Rcpp::List timeseries_save_state(Rcpp::XPtr timeseries);
+RcppExport SEXP _malariasimulation_timeseries_save_state(SEXP timeseriesSEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< Rcpp::XPtr >::type timeseries(timeseriesSEXP);
+ rcpp_result_gen = Rcpp::wrap(timeseries_save_state(timeseries));
+ return rcpp_result_gen;
+END_RCPP
+}
+// timeseries_restore_state
+void timeseries_restore_state(Rcpp::XPtr timeseries, Rcpp::List state);
+RcppExport SEXP _malariasimulation_timeseries_restore_state(SEXP timeseriesSEXP, SEXP stateSEXP) {
+BEGIN_RCPP
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< Rcpp::XPtr >::type timeseries(timeseriesSEXP);
+ Rcpp::traits::input_parameter< Rcpp::List >::type state(stateSEXP);
+ timeseries_restore_state(timeseries, state);
+ return R_NilValue;
+END_RCPP
+}
// random_seed
void random_seed(size_t seed);
RcppExport SEXP _malariasimulation_random_seed(SEXP seedSEXP) {
@@ -225,6 +293,26 @@ BEGIN_RCPP
return R_NilValue;
END_RCPP
}
+// random_save_state
+std::string random_save_state();
+RcppExport SEXP _malariasimulation_random_save_state() {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ rcpp_result_gen = Rcpp::wrap(random_save_state());
+ return rcpp_result_gen;
+END_RCPP
+}
+// random_restore_state
+void random_restore_state(std::string state);
+RcppExport SEXP _malariasimulation_random_restore_state(SEXP stateSEXP) {
+BEGIN_RCPP
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< std::string >::type state(stateSEXP);
+ random_restore_state(state);
+ return R_NilValue;
+END_RCPP
+}
// bernoulli_multi_p_cpp
std::vector bernoulli_multi_p_cpp(const std::vector p);
RcppExport SEXP _malariasimulation_bernoulli_multi_p_cpp(SEXP pSEXP) {
@@ -260,11 +348,13 @@ BEGIN_RCPP
END_RCPP
}
-RcppExport SEXP run_testthat_tests();
+RcppExport SEXP run_testthat_tests(void);
static const R_CallMethodDef CallEntries[] = {
{"_malariasimulation_create_adult_mosquito_model", (DL_FUNC) &_malariasimulation_create_adult_mosquito_model, 5},
{"_malariasimulation_adult_mosquito_model_update", (DL_FUNC) &_malariasimulation_adult_mosquito_model_update, 5},
+ {"_malariasimulation_adult_mosquito_model_save_state", (DL_FUNC) &_malariasimulation_adult_mosquito_model_save_state, 1},
+ {"_malariasimulation_adult_mosquito_model_restore_state", (DL_FUNC) &_malariasimulation_adult_mosquito_model_restore_state, 2},
{"_malariasimulation_create_adult_solver", (DL_FUNC) &_malariasimulation_create_adult_solver, 5},
{"_malariasimulation_create_aquatic_mosquito_model", (DL_FUNC) &_malariasimulation_create_aquatic_mosquito_model, 18},
{"_malariasimulation_aquatic_mosquito_model_update", (DL_FUNC) &_malariasimulation_aquatic_mosquito_model_update, 4},
@@ -272,12 +362,18 @@ static const R_CallMethodDef CallEntries[] = {
{"_malariasimulation_carrying_capacity", (DL_FUNC) &_malariasimulation_carrying_capacity, 8},
{"_malariasimulation_eggs_laid", (DL_FUNC) &_malariasimulation_eggs_laid, 3},
{"_malariasimulation_rainfall", (DL_FUNC) &_malariasimulation_rainfall, 5},
+ {"_malariasimulation_exponential_process_cpp", (DL_FUNC) &_malariasimulation_exponential_process_cpp, 2},
{"_malariasimulation_solver_get_states", (DL_FUNC) &_malariasimulation_solver_get_states, 1},
+ {"_malariasimulation_solver_set_states", (DL_FUNC) &_malariasimulation_solver_set_states, 3},
{"_malariasimulation_solver_step", (DL_FUNC) &_malariasimulation_solver_step, 1},
{"_malariasimulation_create_timeseries", (DL_FUNC) &_malariasimulation_create_timeseries, 2},
{"_malariasimulation_timeseries_at", (DL_FUNC) &_malariasimulation_timeseries_at, 3},
{"_malariasimulation_timeseries_push", (DL_FUNC) &_malariasimulation_timeseries_push, 3},
+ {"_malariasimulation_timeseries_save_state", (DL_FUNC) &_malariasimulation_timeseries_save_state, 1},
+ {"_malariasimulation_timeseries_restore_state", (DL_FUNC) &_malariasimulation_timeseries_restore_state, 2},
{"_malariasimulation_random_seed", (DL_FUNC) &_malariasimulation_random_seed, 1},
+ {"_malariasimulation_random_save_state", (DL_FUNC) &_malariasimulation_random_save_state, 0},
+ {"_malariasimulation_random_restore_state", (DL_FUNC) &_malariasimulation_random_restore_state, 1},
{"_malariasimulation_bernoulli_multi_p_cpp", (DL_FUNC) &_malariasimulation_bernoulli_multi_p_cpp, 1},
{"_malariasimulation_bitset_index_cpp", (DL_FUNC) &_malariasimulation_bitset_index_cpp, 2},
{"_malariasimulation_fast_weighted_sample", (DL_FUNC) &_malariasimulation_fast_weighted_sample, 2},
diff --git a/src/adult_mosquito_eqs.cpp b/src/adult_mosquito_eqs.cpp
index ad5553eb..8126c7d7 100644
--- a/src/adult_mosquito_eqs.cpp
+++ b/src/adult_mosquito_eqs.cpp
@@ -17,7 +17,7 @@ AdultMosquitoModel::AdultMosquitoModel(
) : growth_model(growth_model), mu(mu), tau(tau), foim(foim)
{
for (auto i = 0u; i < tau; ++i) {
- lagged_incubating.push(incubating);
+ lagged_incubating.push_back(incubating);
}
}
@@ -82,12 +82,30 @@ void adult_mosquito_model_update(
model->foim = foim;
model->growth_model.f = f;
model->growth_model.mum = mu;
- model->lagged_incubating.push(susceptible * foim);
+ model->lagged_incubating.push_back(susceptible * foim);
if (model->lagged_incubating.size() > 0) {
- model->lagged_incubating.pop();
+ model->lagged_incubating.pop_front();
}
}
+//[[Rcpp::export]]
+std::vector adult_mosquito_model_save_state(
+ Rcpp::XPtr model
+ ) {
+ // Only the lagged_incubating needs to be saved. The rest of the model
+ // state is reset at each time step by a call to update before it gets
+ // used.
+ return {model->lagged_incubating.begin(), model->lagged_incubating.end()};
+}
+
+//[[Rcpp::export]]
+void adult_mosquito_model_restore_state(
+ Rcpp::XPtr model,
+ std::vector state
+ ) {
+ model->lagged_incubating.assign(state.begin(), state.end());
+}
+
//[[Rcpp::export]]
Rcpp::XPtr create_adult_solver(
Rcpp::XPtr model,
diff --git a/src/adult_mosquito_eqs.h b/src/adult_mosquito_eqs.h
index 6fc30501..c3e0ac8b 100644
--- a/src/adult_mosquito_eqs.h
+++ b/src/adult_mosquito_eqs.h
@@ -28,7 +28,7 @@ enum class AdultState : size_t {S = 3, E = 4, I = 5};
*/
struct AdultMosquitoModel {
AquaticMosquitoModel growth_model;
- std::queue lagged_incubating; //last tau values for incubating mosquitos
+ std::deque lagged_incubating; //last tau values for incubating mosquitos
double mu; //death rate for adult female mosquitoes
const double tau; //extrinsic incubation period
double foim; //force of infection towards mosquitoes
diff --git a/src/processes.cpp b/src/processes.cpp
new file mode 100644
index 00000000..b5db4bad
--- /dev/null
+++ b/src/processes.cpp
@@ -0,0 +1,117 @@
+#include
+#include
+
+/**
+ * An iterator adaptor which yields the same values as the underlying iterator,
+ * but scaled by a pre-determined factor.
+ *
+ * This is used by the exponential_process below to scale an std::vector by a
+ * constant.
+ *
+ * There are two straightforward ways of performing the operation. The first is
+ * to create an empty vector, use `reserve(N)` to pre-allocate the vector and
+ * then call `push_back` with each new value. The second way would be to create
+ * a zero-initialised vector of size N and then use `operator[]` to fill in the
+ * values.
+ *
+ * Unfortunately both approaches have significant overhead. In the former, the
+ * use of `push_back` requires repeated checks as to whether the vector needs
+ * growing, despite the prior reserve call. These calls inhibits optimizations
+ * such as unrolling and auto-vectorization of the loop. The latter approach
+ * requires an initial memset when zero-initializing the vector, even though the
+ * vector then gets overwritten entirely. Sadly gcc fails to optimize out either
+ * of those. Ideally we want a way to create a pre-sized but uninitialised
+ * std::vector we can write to ourselves, but there is no API in the standard
+ * library to do this. All existing workarounds end up with an std::vector with
+ * non-default item type or allocators.
+ *
+ * There is however a way out! std::vector has a constructor which accepts a
+ * pair of iterators and fills the vector with values from the iterators. Using
+ * `std::distance` on the iterator pair it can even pre-allocate the vector to
+ * the right size. No zero-initialisation or no capacity checks, just one
+ * allocation and a straightforward easily optimizable loop. All we need is an
+ * iterator yielding the right values, hence `scale_iterator`. In C++20 we would
+ * probably be able to use the new ranges library as our iterators.
+ *
+ * How much does this matter? On microbenchmarks, for small and medium sized
+ * vector (<= 1M doubles), this version is about 30% faster than the
+ * zero-initialising implementation and 60% faster than the one which uses
+ * push_back. For larger vector sizes the difference is less pronounced,
+ * possibly because caches become saturated. At the time of writing, on a
+ * real-word run of malariasimulation with a population size of 1M the overall
+ * speedup is about 2-3%.
+ *
+ * https://wolchok.org/posts/cxx-trap-1-constant-size-vector/
+ * https://codingnest.com/the-little-things-the-missing-performance-in-std-vector/
+ * https://lemire.me/blog/2012/06/20/do-not-waste-time-with-stl-vectors/
+ */
+template
+struct scale_iterator {
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = typename std::iterator_traits::difference_type;
+ using value_type = typename std::iterator_traits::value_type;
+ using pointer = typename std::iterator_traits::pointer;
+
+ // We skirt the rules a bit by returning a prvalue from `operator*`, even
+ // though the C++17 (and prior) standard says forward iterators are supposed
+ // to return a reference type (ie. a glvalue). Because the scaling is
+ // applied on the fly, there is no glvalue we could return a reference to.
+ //
+ // An input iterator would be allowed to return a prvalue, but the
+ // std::vector constructor wouldn't be able to figure out the length ahead
+ // of time if we were an input iterator.
+ //
+ // C++20 actually introduces parallel definitions of input and forward
+ // iterators, which relax this requirement, so under that classification our
+ // implementation in correct.
+ //
+ // In practice though, this does not really matter. We only use this
+ // iterator in one specific context, and the vector constructor doesn't do
+ // anything elaborate that we would be upsetting.
+ using reference = value_type;
+
+ scale_iterator(underlying_iterator it, value_type factor) : it(it), factor(factor) {}
+ reference operator*() {
+ return factor * (*it);
+ }
+ bool operator==(const scale_iterator& other) {
+ return it == other.it;
+ }
+ bool operator!=(const scale_iterator& other) {
+ return it != other.it;
+ }
+ scale_iterator& operator++() {
+ it++;
+ return *this;
+ }
+ scale_iterator operator++(int) {
+ return scale_iterator(it++, factor);
+ }
+
+ private:
+ underlying_iterator it;
+ value_type factor;
+};
+
+template
+scale_iterator make_scale_iterator(T&& it, typename std::iterator_traits::value_type scale) {
+ return scale_iterator(std::forward(it), scale);
+}
+
+//[[Rcpp::export]]
+Rcpp::XPtr exponential_process_cpp(
+ Rcpp::XPtr variable,
+ const double rate
+){
+ return Rcpp::XPtr(
+ new process_t([=](size_t t){
+ const std::vector& values = variable->get_values();
+ std::vector new_values(
+ make_scale_iterator(values.cbegin(), rate),
+ make_scale_iterator(values.cend(), rate));
+
+ variable->queue_update(std::move(new_values), std::vector());
+ }),
+ true
+ );
+}
diff --git a/src/solver.cpp b/src/solver.cpp
index 7cb8b9f4..4c3f182f 100644
--- a/src/solver.cpp
+++ b/src/solver.cpp
@@ -47,6 +47,12 @@ std::vector solver_get_states(Rcpp::XPtr solver) {
return solver->state;
}
+//[[Rcpp::export]]
+void solver_set_states(Rcpp::XPtr solver, double t, std::vector state) {
+ solver->t = t;
+ solver->state = state;
+}
+
//[[Rcpp::export]]
void solver_step(Rcpp::XPtr solver) {
solver->step();
diff --git a/src/timeseries.cpp b/src/timeseries.cpp
index 2383ac73..51a494de 100644
--- a/src/timeseries.cpp
+++ b/src/timeseries.cpp
@@ -15,18 +15,18 @@ Timeseries::Timeseries(size_t max_size, double default_value)
: max_size(max_size), has_default(true), default_value(default_value) {}
void Timeseries::push(double value, double time) {
- values.insert({time, value});
+ _values.insert({time, value});
if (max_size != -1) {
- while(values.size() > max_size) {
- values.erase(values.begin());
+ while(_values.size() > max_size) {
+ _values.erase(_values.begin());
}
}
}
double Timeseries::at(double time, bool linear) const {
- auto it = values.lower_bound(time);
- if (it == values.end()) {
- if (values.size() > 0 && !linear) {
+ auto it = _values.lower_bound(time);
+ if (it == _values.end()) {
+ if (_values.size() > 0 && !linear) {
it--;
return it->second;
}
@@ -45,7 +45,7 @@ double Timeseries::at(double time, bool linear) const {
auto after_element = *it;
while(it->first > time) {
// Check if we're at the start of the timeseries
- if (it == values.begin()) {
+ if (it == _values.begin()) {
if (has_default) {
return default_value;
}
@@ -64,6 +64,14 @@ double Timeseries::at(double time, bool linear) const {
return it->second;
}
+const std::map& Timeseries::values() {
+ return _values;
+}
+
+void Timeseries::set_values(std::map values) {
+ _values = std::move(values);
+}
+
//[[Rcpp::export]]
Rcpp::XPtr create_timeseries(size_t size, double default_value) {
return Rcpp::XPtr(new Timeseries(size, default_value), true);
@@ -78,3 +86,32 @@ double timeseries_at(Rcpp::XPtr timeseries, double timestep, bool li
void timeseries_push(Rcpp::XPtr timeseries, double value, double timestep) {
return timeseries->push(value, timestep);
}
+
+//[[Rcpp::export]]
+Rcpp::List timeseries_save_state(Rcpp::XPtr timeseries) {
+ std::vector timesteps;
+ std::vector values;
+ for (const auto& entry: timeseries->values()) {
+ timesteps.push_back(entry.first);
+ values.push_back(entry.second);
+ }
+ return Rcpp::DataFrame::create(
+ Rcpp::Named("timestep") = timesteps,
+ Rcpp::Named("value") = values
+ );
+}
+
+//[[Rcpp::export]]
+void timeseries_restore_state(Rcpp::XPtr timeseries, Rcpp::List state) {
+ std::vector timesteps = state["timestep"];
+ std::vector values = state["value"];
+ if (timesteps.size() != values.size()) {
+ Rcpp::stop("Bad size");
+ }
+
+ std::map values_map;
+ for (size_t i = 0; i < timesteps.size(); i++) {
+ values_map.insert({timesteps[i], values[i]});
+ }
+ timeseries->set_values(std::move(values_map));
+}
diff --git a/src/timeseries.h b/src/timeseries.h
index c9e7c32f..78f3780b 100644
--- a/src/timeseries.h
+++ b/src/timeseries.h
@@ -12,7 +12,7 @@
class Timeseries {
private:
- std::map values;
+ std::map _values;
size_t max_size;
void clean();
bool has_default;
@@ -23,6 +23,9 @@ class Timeseries {
Timeseries(size_t, double);
void push(double, double);
double at(double, bool = true) const;
+
+ const std::map& values();
+ void set_values(std::map state);
};
#endif /* SRC_TIMESERIES_ */
diff --git a/src/utils.cpp b/src/utils.cpp
index 838a9530..d2b36043 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -8,6 +8,16 @@ void random_seed(size_t seed) {
Random::get_instance().seed(seed);
}
+//[[Rcpp::export]]
+std::string random_save_state() {
+ return Random::get_instance().save_state();
+}
+
+//[[Rcpp::export]]
+void random_restore_state(std::string state) {
+ return Random::get_instance().restore_state(state);
+}
+
//[[Rcpp::export]]
std::vector bernoulli_multi_p_cpp(const std::vector p) {
auto values = Random::get_instance().bernoulli_multi_p(p);
diff --git a/tests/testthat/helper-integration.R b/tests/testthat/helper-integration.R
index b69b7dcb..22c7dd63 100644
--- a/tests/testthat/helper-integration.R
+++ b/tests/testthat/helper-integration.R
@@ -88,6 +88,13 @@ mock_event <- function(event) {
)
}
+mock_solver <- function(states) {
+ list(
+ get_states = mockery::mock(states),
+ step = mockery::mock()
+ )
+}
+
expect_bitset_update <- function(mock, value, index, call = 1) {
expect_equal(mockery::mock_args(mock)[[call]][[1]], value)
expect_equal(mockery::mock_args(mock)[[call]][[2]]$to_vector(), index)
diff --git a/tests/testthat/test-antimalarial-resistance.R b/tests/testthat/test-antimalarial-resistance.R
new file mode 100644
index 00000000..ae45a20e
--- /dev/null
+++ b/tests/testthat/test-antimalarial-resistance.R
@@ -0,0 +1,323 @@
+test_that('set_antimalarial_resistance() toggles resistance on', {
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ coverages = 1)
+ set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.5,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0.5,
+ early_treatment_failure_probability = 0.6,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 10) -> parameters
+ expect_identical(object = parameters$antimalarial_resistance, expected = TRUE)
+})
+
+test_that('set_antimalarial_resistance() errors if parameter inputs of different length to timesteps', {
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ coverages = 1)
+ expect_error(object = set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = c(1, 10),
+ artemisinin_resistance_proportion = 0.5,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0.5,
+ early_treatment_failure_probability = 0.6,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 10))
+})
+
+test_that('set_antimalarial_resistance() errors if resistance proportions outside of range 0-1', {
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ coverages = 1)
+ expect_error(object = set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 1.01,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0.5,
+ early_treatment_failure_probability = 0.6,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 10),
+ regexp = "Artemisinin and partner-drug resistance proportions must fall between 0 and 1")
+})
+
+test_that('set_antimalarial_resistance() errors if resistance phenotype probabilities outside bound of 0-1', {
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ coverages = 1)
+ expect_error(object = set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.4,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = -0.5,
+ early_treatment_failure_probability = 0.6,
+ late_clinical_failure_probability = 0.2,
+ late_parasitological_failure_probability = 0.3,
+ reinfection_during_prophylaxis_probability = 0.4,
+ slow_parasite_clearance_time = 5))
+})
+
+test_that('set_antimalarial_resistance() errors if drug index > than number of drugs assigned using set_drugs()', {
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ coverages = 1)
+ expect_error(object = set_antimalarial_resistance(parameters = parameters,
+ drug = 2,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.4,
+ partner_drug_resistance_proportion = 0.3,
+ slow_parasite_clearance_probability = 0.5,
+ early_treatment_failure_probability = 0.6,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0.4,
+ slow_parasite_clearance_time = 10))
+})
+
+test_that('set_antimalarial_resistance() assigns parameters correctly despite order in which resistance parameters are specified', {
+
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(AL_params, SP_AQ_params, DHA_PQP_params))
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 2, timesteps = 1, coverages = 0.2)
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 1, timesteps = 1, coverages = 0.1)
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 3, timesteps = 1, coverages = 0.4)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 2,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.5,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0.41,
+ early_treatment_failure_probability = 0.2,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 5)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 3,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0,
+ early_treatment_failure_probability = 0,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 10)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.27,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0.23,
+ early_treatment_failure_probability = 0.9,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 20)
+
+ expect_identical(parameters$antimalarial_resistance, TRUE)
+ expect_identical(unlist(parameters$antimalarial_resistance_drug), c(2, 3, 1))
+ expect_identical(unlist(parameters$antimalarial_resistance_timesteps), rep(1, 3))
+ expect_identical(unlist(parameters$artemisinin_resistance_proportion), c(0.5, 0, 0.27))
+ expect_identical(unlist(parameters$partner_drug_resistance_proportion), c(0, 0, 0))
+ expect_identical(unlist(parameters$slow_parasite_clearance_probability), c(0.41, 0, 0.23))
+ expect_identical(unlist(parameters$early_treatment_failure_probability), c(0.2, 0, 0.9))
+ expect_identical(unlist(parameters$late_clinical_failure_probability), c(0, 0, 0))
+ expect_identical(unlist(parameters$late_parasitological_failure_probability), c(0, 0, 0))
+ expect_identical(unlist(parameters$reinfection_during_prophylaxis_probability), c(0, 0, 0))
+ expect_identical(unlist(parameters$dt_slow_parasite_clearance), c(5, 10, 20))
+
+})
+
+test_that("set_antimalarial_resistance errors if length slow_parasite_clearance_time > 1", {
+
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters,
+ drug = 1,
+ timesteps = c(0, 10),
+ coverages = c(0.1, 0.2))
+
+ expect_error(
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = c(0, 10),
+ artemisinin_resistance_proportion = c(0.4, 0.8),
+ partner_drug_resistance_proportion = c(0, 0),
+ slow_parasite_clearance_probability = c(0.2, 0.4),
+ early_treatment_failure_probability = c(0, 0.45),
+ late_clinical_failure_probability = c(0, 0),
+ late_parasitological_failure_probability = c(0, 0),
+ reinfection_during_prophylaxis_probability = c(0, 0),
+ slow_parasite_clearance_time = c(10 ,11)),
+ "Error: length of slow_parasite_clearance_time not equal to 1")
+})
+
+test_that("set_antimalarial_resistance errors if slow_parasite_clearance_time not positive", {
+
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters,
+ drug = 1,
+ timesteps = c(0, 10),
+ coverages = c(0.1, 0.2))
+
+ expect_error(
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = c(0, 10),
+ artemisinin_resistance_proportion = c(0.4, 0.8),
+ partner_drug_resistance_proportion = c(0, 0),
+ slow_parasite_clearance_probability = c(0.2, 0.4),
+ early_treatment_failure_probability = c(0, 0.45),
+ late_clinical_failure_probability = c(0, 0),
+ late_parasitological_failure_probability = c(0, 0),
+ reinfection_during_prophylaxis_probability = c(0, 0),
+ slow_parasite_clearance_time = c(0)),
+ "Error: slow_parasite_clearance_time is non-positive")
+})
+
+test_that('get_antimalarial_resistance_parameters() correctly retrieves parameters when multiple drugs assigned', {
+
+ get_parameters(overrides = list(human_population = 10000)) |>
+ set_drugs(drugs = list(AL_params, SP_AQ_params, DHA_PQP_params)) |>
+ set_clinical_treatment(drug = 1, timesteps = 1, coverages = 0.4) |>
+ set_clinical_treatment(drug = 2, timesteps = 1, coverages = 0.3) |>
+ set_clinical_treatment(drug = 3, timesteps = 1, coverages = 0.2) |>
+ set_equilibrium(init_EIR = 20) |>
+ set_antimalarial_resistance(drug = 2,
+ timesteps = c(0, 20),
+ artemisinin_resistance_proportion = c(0.02, 0.2),
+ partner_drug_resistance_proportion = c(0, 0),
+ slow_parasite_clearance_probability = c(0.02, 0.2),
+ early_treatment_failure_probability = c(0.02, 0.2),
+ late_clinical_failure_probability = c(0, 0),
+ late_parasitological_failure_probability = c(0, 0),
+ reinfection_during_prophylaxis_probability = c(0, 0),
+ slow_parasite_clearance_time = 20) |>
+ set_antimalarial_resistance(drug = 1,
+ timesteps = c(0, 10),
+ artemisinin_resistance_proportion = c(0.01, 0.1),
+ partner_drug_resistance_proportion = c(0, 0),
+ slow_parasite_clearance_probability = c(0.01, 0.1),
+ early_treatment_failure_probability = c(0.01, 0.1),
+ late_clinical_failure_probability = c(0, 0),
+ late_parasitological_failure_probability = c(0, 0),
+ reinfection_during_prophylaxis_probability = c(0, 0),
+ slow_parasite_clearance_time = 10) |>
+ set_antimalarial_resistance(drug = 3,
+ timesteps = c(0, 30),
+ artemisinin_resistance_proportion = c(0.03, 0.3),
+ partner_drug_resistance_proportion = c(0, 0),
+ slow_parasite_clearance_probability = c(0.03, 0.3),
+ early_treatment_failure_probability = c(0.03, 0.3),
+ late_clinical_failure_probability = c(0, 0),
+ late_parasitological_failure_probability = c(0, 0),
+ reinfection_during_prophylaxis_probability = c(0, 0),
+ slow_parasite_clearance_time = 30) -> parameters
+
+ drugs <- c(1, 3, 2, 1, 2, 3, 3, 3, 2, 1, 3, 1, 2, 3, 2)
+ timestep <- 25
+
+ resistance_parameters <- get_antimalarial_resistance_parameters(parameters = parameters,
+ drugs = drugs,
+ timestep = timestep)
+
+ expected_resistance_parameters <- list()
+ expected_resistance_parameters$artemisinin_resistance_proportion <- c(0.1, 0.03, 0.2, 0.1, 0.2, 0.03, 0.03, 0.03, 0.2, 0.1, 0.03, 0.1, 0.2, 0.03, 0.2)
+ expected_resistance_parameters$partner_drug_resistance_proportion <- rep(0, 15)
+ expected_resistance_parameters$slow_parasite_clearance_probability <- c(0.1, 0.03, 0.2, 0.1, 0.2, 0.03, 0.03, 0.03, 0.2, 0.1, 0.03, 0.1, 0.2, 0.03, 0.2)
+ expected_resistance_parameters$early_treatment_failure_probability <- c(0.1, 0.03, 0.2, 0.1, 0.2, 0.03, 0.03, 0.03, 0.2, 0.1, 0.03, 0.1, 0.2, 0.03, 0.2)
+ expected_resistance_parameters$late_clinical_failure_probability <- rep(0, 15)
+ expected_resistance_parameters$late_parasitological_failure_probability <- rep(0, 15)
+ expected_resistance_parameters$reinfection_during_prophylaxis_probability <- rep(0, 15)
+ expected_resistance_parameters$dt_slow_parasite_clearance <- c(10, 30, 20, 10, 20, 30, 30, 30, 20, 10, 30, 10, 20, 30, 20)
+
+ expect_identical(resistance_parameters, expected = expected_resistance_parameters)
+
+})
+
+test_that('get_antimalarial_resistance_parameters() correctly retrieves parameters when not all drugs assigned resistance', {
+
+ get_parameters(overrides = list(human_population = 10000)) %>%
+ set_drugs(drugs = list(AL_params, SP_AQ_params, DHA_PQP_params)) %>%
+ set_clinical_treatment(drug = 1, timesteps = 1, coverages = 0.4) %>%
+ set_clinical_treatment(drug = 2, timesteps = 1, coverages = 0.3) %>%
+ set_clinical_treatment(drug = 3, timesteps = 1, coverages = 0.2) %>%
+ set_equilibrium(init_EIR = 20) %>%
+ set_antimalarial_resistance(drug = 2,
+ timesteps = c(0, 20),
+ artemisinin_resistance_proportion = c(0.02, 0.2),
+ partner_drug_resistance_proportion = c(0, 0),
+ slow_parasite_clearance_probability = c(0.02, 0.2),
+ early_treatment_failure_probability = c(0.02, 0.2),
+ late_clinical_failure_probability = c(0, 0),
+ late_parasitological_failure_probability = c(0, 0),
+ reinfection_during_prophylaxis_probability = c(0, 0),
+ slow_parasite_clearance_time = 20) -> parameters
+
+ drugs <- c(1, 3, 2, 1, 2, 3, 3, 3, 2, 1, 3, 1, 2, 3, 2)
+ timestep <- 25
+
+ resistance_parameters <- get_antimalarial_resistance_parameters(parameters = parameters,
+ drugs = drugs,
+ timestep = timestep)
+
+ expected_resistance_parameters <- list()
+ expected_resistance_parameters$artemisinin_resistance_proportion <- c(0, 0, 0.2, 0, 0.2, 0, 0, 0, 0.2, 0, 0, 0, 0.2, 0, 0.2)
+ expected_resistance_parameters$partner_drug_resistance_proportion <- rep(0, 15)
+ expected_resistance_parameters$slow_parasite_clearance_probability <- c(0, 0, 0.2, 0, 0.2, 0, 0, 0, 0.2, 0, 0, 0, 0.2, 0, 0.2)
+ expected_resistance_parameters$early_treatment_failure_probability <- c(0, 0, 0.2, 0, 0.2, 0, 0, 0, 0.2, 0, 0, 0, 0.2, 0, 0.2)
+ expected_resistance_parameters$late_clinical_failure_probability <- rep(0, 15)
+ expected_resistance_parameters$late_parasitological_failure_probability <- rep(0, 15)
+ expected_resistance_parameters$reinfection_during_prophylaxis_probability <- rep(0, 15)
+ expected_resistance_parameters$dt_slow_parasite_clearance <- c(5, 5, 20, 5, 20, 5, 5, 5, 20, 5, 5, 5, 20, 5, 20)
+
+ expect_identical(resistance_parameters, expected = expected_resistance_parameters)
+
+})
+
+test_that('get_antimalarial_resistance_parameters() returns an error when antimalarial resistance has not been parameterised', {
+
+ get_parameters(overrides = list(human_population = 10000)) %>%
+ set_drugs(drugs = list(AL_params, SP_AQ_params, DHA_PQP_params)) %>%
+ set_clinical_treatment(drug = 1, timesteps = 1, coverages = 0.4) %>%
+ set_clinical_treatment(drug = 2, timesteps = 1, coverages = 0.3) %>%
+ set_clinical_treatment(drug = 3, timesteps = 1, coverages = 0.2) %>%
+ set_equilibrium(init_EIR = 20) -> parameters
+
+ drugs <- c(1, 3, 2, 1, 2, 3, 3, 3, 2, 1, 3, 1, 2, 3, 2)
+ timestep <- 25
+
+
+ expect_error(get_antimalarial_resistance_parameters(parameters = parameters,
+ drugs = drugs,
+ timestep = timestep),
+ "Error: Antimalarial resistance has not been parameterised; antimalarial_resistance = FALSE")
+})
diff --git a/tests/testthat/test-biology.R b/tests/testthat/test-biology.R
index 5895d45b..e429a48f 100644
--- a/tests/testthat/test-biology.R
+++ b/tests/testthat/test-biology.R
@@ -16,10 +16,7 @@ test_that('total_M and EIR functions are consistent with equilibrium EIR', {
vector_models <- parameterise_mosquito_models(parameters, 1)
solvers <- parameterise_solvers(vector_models, parameters)
estimated_eir <- calculate_eir(1, solvers, variables, parameters, 0)
- age <- get_age(variables$birth$get_values(), 0)
- psi <- unique_biting_rate(age, parameters)
- omega <- mean(psi)
- mean(estimated_eir) / omega / population * 365
+ mean(estimated_eir) / population * 365
})
expect_equal(
@@ -44,10 +41,7 @@ test_that('total_M and EIR functions are consistent with equilibrium EIR (with h
vector_models <- parameterise_mosquito_models(parameters, 1)
solvers <- parameterise_solvers(vector_models, parameters)
estimated_eir <- calculate_eir(1, solvers, variables, parameters, 0)
- age <- get_age(variables$birth$get_values(), 0)
- psi <- unique_biting_rate(age, parameters)
- omega <- mean(psi)
- mean(estimated_eir) / omega / population * 365
+ mean(estimated_eir) / population * 365
})
expect_equal(
@@ -92,13 +86,13 @@ test_that('FOIM is consistent with equilibrium', {
psi <- unique_biting_rate(age, parameters)
zeta <- variables$zeta$get_values()
.pi <- human_pi(psi, zeta)
- calculate_foim(a, sum(.pi * variables$infectivity$get_values()))
+ calculate_foim(a, sum(.pi * variables$infectivity$get_values()), 1.)
}
)
expect_equal(
expected_foim,
actual_foim,
- tolerance = 1e-4
+ tolerance = 1e-3
)
})
diff --git a/tests/testthat/test-biting-integration.R b/tests/testthat/test-biting-integration.R
index 66bf5b72..7da79b66 100644
--- a/tests/testthat/test-biting-integration.R
+++ b/tests/testthat/test-biting-integration.R
@@ -131,7 +131,7 @@ test_that('simulate_bites integrates eir calculation and mosquito side effects',
expect_equal(effects_args[[1]][[8]], parameters)
expect_equal(effects_args[[1]][[9]], timestep)
- mockery::expect_args(eqs_update, 1, models[[1]], 25, f, parameters$mum)
+ mockery::expect_args(eqs_update, 1, models[[1]]$.model, 25, f, parameters$mum)
mockery::expect_args(
pois_mock,
1,
diff --git a/tests/testthat/test-compartmental.R b/tests/testthat/test-compartmental.R
index a4fb3b6a..9958c7a5 100644
--- a/tests/testthat/test-compartmental.R
+++ b/tests/testthat/test-compartmental.R
@@ -11,9 +11,14 @@ test_that('ODE stays at equilibrium with a constant total_M', {
counts <- c()
for (t in seq(timesteps)) {
- counts <- rbind(counts, c(t, solver_get_states(solvers[[1]])))
- aquatic_mosquito_model_update(models[[1]], total_M, f, parameters$mum)
- solver_step(solvers[[1]])
+ counts <- rbind(counts, c(t, solvers[[1]]$get_states()))
+ aquatic_mosquito_model_update(
+ models[[1]]$.model,
+ total_M,
+ f,
+ parameters$mum
+ )
+ solvers[[1]]$step()
}
expected <- c()
@@ -41,16 +46,16 @@ test_that('Adult ODE stays at equilibrium with a constant foim and mu', {
counts <- c()
for (t in seq(timesteps)) {
- states <- solver_get_states(solvers[[1]])
+ states <- solvers[[1]]$get_states()
counts <- rbind(counts, c(t, states))
adult_mosquito_model_update(
- models[[1]],
+ models[[1]]$.model,
parameters$mum,
parameters$init_foim,
states[ADULT_ODE_INDICES['Sm']],
f
)
- solver_step(solvers[[1]])
+ solvers[[1]]$step()
}
expected <- c()
@@ -82,9 +87,14 @@ test_that('ODE stays at equilibrium with low total_M', {
counts <- c()
for (t in seq(timesteps)) {
- counts <- rbind(counts, c(t, solver_get_states(solvers[[1]])))
- aquatic_mosquito_model_update(models[[1]], total_M, f, parameters$mum)
- solver_step(solvers[[1]])
+ counts <- rbind(counts, c(t, solvers[[1]]$get_states()))
+ aquatic_mosquito_model_update(
+ models[[1]]$.model,
+ total_M,
+ f,
+ parameters$mum
+ )
+ solvers[[1]]$step()
}
expected <- c()
@@ -121,14 +131,19 @@ test_that('Changing total_M stabilises', {
counts <- c()
for (t in seq(timesteps)) {
- counts <- rbind(counts, c(t, solver_get_states(solvers[[1]])))
+ counts <- rbind(counts, c(t, solvers[[1]]$get_states()))
if (t < change) {
total_M <- total_M_0
} else {
total_M <- total_M_1
}
- aquatic_mosquito_model_update(models[[1]], total_M, f, parameters$mum)
- solver_step(solvers[[1]])
+ aquatic_mosquito_model_update(
+ models[[1]]$.model,
+ total_M,
+ f,
+ parameters$mum
+ )
+ solvers[[1]]$step()
}
initial_eq <- initial_mosquito_counts(
diff --git a/tests/testthat/test-correlation.R b/tests/testthat/test-correlation.R
index 880221ba..5653daaa 100644
--- a/tests/testthat/test-correlation.R
+++ b/tests/testthat/test-correlation.R
@@ -1,105 +1,354 @@
test_that('1 correlation between rounds gives sensible samples', {
pop <- 1e6
target <- seq(pop)
- vaccine_coverage <- .2
- parameters <- get_parameters(list(
- human_population = pop,
- pev = TRUE
- ))
- correlations <- get_correlation_parameters(parameters)
+
+ coverage_1 <- .2
+ coverage_2 <- .4
+
+ correlations <- CorrelationParameters$new(pop, c('pev'))
correlations$inter_round_rho('pev', 1)
- round_1 <- sample_intervention(target, 'pev', vaccine_coverage, correlations)
- round_2 <- sample_intervention(target, 'pev', vaccine_coverage, correlations)
- expect_equal(sum(round_1), pop * .2, tolerance=1e2)
- expect_equal(sum(round_2), pop * .2, tolerance=1e2)
- expect_equal(sum(round_1 & round_2), pop * .2, tolerance=1e2)
+
+ round_1 <- sample_intervention(target, 'pev', coverage_1, correlations)
+ round_2 <- sample_intervention(target, 'pev', coverage_2, correlations)
+
+ expect_equal(sum(round_1), pop * coverage_1, tolerance=.1)
+ expect_equal(sum(round_2), pop * coverage_2, tolerance=.1)
+
+ expect_equal(
+ sum(round_1 & round_2),
+ pop * min(coverage_1, coverage_2),
+ tolerance=.1)
+
+ expect_equal(
+ sum(round_1 | round_2),
+ pop * max(coverage_1, coverage_2),
+ tolerance=.1)
})
test_that('0 correlation between rounds gives sensible samples', {
pop <- 1e6
target <- seq(pop)
- vaccine_coverage <- .5
- parameters <- get_parameters(list(
- human_population = pop,
- pev = TRUE
- ))
- correlations <- get_correlation_parameters(parameters)
+
+ coverage_1 <- .2
+ coverage_2 <- .4
+
+ correlations <- CorrelationParameters$new(pop, c('pev'))
correlations$inter_round_rho('pev', 0)
- round_1 <- sample_intervention(target, 'pev', vaccine_coverage, correlations)
- round_2 <- sample_intervention(target, 'pev', vaccine_coverage, correlations)
- expect_equal(
- length(intersect(which(round_1), which(round_2))),
- pop * .5,
- tolerance=1e2
- )
- expect_equal(sum(round_1), sum(round_2), tolerance=1e2)
- expect_equal(sum(round_1), pop * .5, tolerance=1e2)
+
+ round_1 <- sample_intervention(target, 'pev', coverage_1, correlations)
+ round_2 <- sample_intervention(target, 'pev', coverage_2, correlations)
+
+ expect_equal(sum(round_1), pop * coverage_1, tolerance=.1)
+ expect_equal(sum(round_2), pop * coverage_2, tolerance=.1)
+
+ expect_equal(
+ sum(round_1 & round_2),
+ pop * coverage_1 * coverage_2,
+ tolerance=.1)
+
+ expect_equal(
+ sum(round_1 | round_2),
+ pop * (coverage_1 + coverage_2 - (coverage_1 * coverage_2)),
+ tolerance=.1)
})
test_that('1 correlation between interventions gives sensible samples', {
pop <- 1e6
target <- seq(pop)
- vaccine_coverage <- .2
- mda_coverage <- .2
- parameters <- get_parameters(list(
- human_population = pop,
- pev = TRUE,
- mda = TRUE
- ))
- correlations <- get_correlation_parameters(parameters)
+
+ pev_coverage <- .2
+ mda_coverage <- .4
+
+ correlations <- CorrelationParameters$new(pop, c('pev', 'mda'))
correlations$inter_round_rho('pev', 1)
correlations$inter_round_rho('mda', 1)
correlations$inter_intervention_rho('pev', 'mda', 1)
- vaccine_sample <- sample_intervention(target, 'pev', vaccine_coverage, correlations)
+
+ pev_sample <- sample_intervention(target, 'pev', pev_coverage, correlations)
mda_sample <- sample_intervention(target, 'mda', mda_coverage, correlations)
- expect_equal(sum(vaccine_sample), pop * .2, tolerance=1e2)
- expect_equal(sum(mda_sample), pop * .2, tolerance=1e2)
- expect_equal(sum(vaccine_sample & mda_sample), pop * .2, tolerance=1e2)
+ expect_equal(sum(pev_sample), pop * pev_coverage, tolerance=.1)
+ expect_equal(sum(mda_sample), pop * mda_coverage, tolerance=.1)
+
+ expect_equal(
+ sum(pev_sample & mda_sample),
+ pop * min(pev_coverage, mda_coverage),
+ tolerance=.1)
+
+ expect_equal(
+ sum(pev_sample | mda_sample),
+ pop * max(pev_coverage, mda_coverage),
+ tolerance=.1)
})
test_that('0 correlation between interventions gives sensible samples', {
pop <- 1e6
target <- seq(pop)
- vaccine_coverage <- .2
- mda_coverage <- .2
- parameters <- get_parameters(list(
- human_population = pop,
- pev = TRUE,
- mda = TRUE
- ))
- correlations <- get_correlation_parameters(parameters)
+
+ pev_coverage <- .2
+ mda_coverage <- .4
+
+ correlations <- CorrelationParameters$new(pop, c('pev', 'mda'))
correlations$inter_round_rho('pev', 1)
correlations$inter_round_rho('mda', 1)
correlations$inter_intervention_rho('pev', 'mda', 0)
- vaccine_sample <- sample_intervention(target, 'pev', vaccine_coverage, correlations)
+
+ pev_sample <- sample_intervention(target, 'pev', pev_coverage, correlations)
mda_sample <- sample_intervention(target, 'mda', mda_coverage, correlations)
+
+ expect_equal(sum(pev_sample), pop * pev_coverage, tolerance=.1)
+ expect_equal(sum(mda_sample), pop * mda_coverage, tolerance=.1)
+
+ expect_equal(
+ sum(pev_sample & mda_sample),
+ pop * pev_coverage * mda_coverage,
+ tolerance=.1)
+
expect_equal(
- length(intersect(which(vaccine_sample), which(mda_sample))),
- pop * .5,
- tolerance=1e2
- )
- expect_equal(sum(vaccine_sample), sum(mda_sample), tolerance=1e2)
- expect_equal(sum(vaccine_sample), pop * .5, tolerance=1e2)
+ sum(pev_sample | mda_sample),
+ pop * (pev_coverage + mda_coverage - (pev_coverage * mda_coverage)),
+ tolerance=.1)
})
test_that('-1 correlation between interventions gives sensible samples', {
pop <- 1e6
target <- seq(pop)
- vaccine_coverage <- .2
- mda_coverage <- .2
- parameters <- get_parameters(list(
- human_population = pop,
- pev = TRUE,
- mda = TRUE
- ))
- correlations <- get_correlation_parameters(parameters)
+
+ pev_coverage <- .2
+ mda_coverage <- .4
+
+ correlations <- CorrelationParameters$new(pop, c('pev', 'mda'))
correlations$inter_round_rho('pev', 1)
correlations$inter_round_rho('mda', 1)
correlations$inter_intervention_rho('pev', 'mda', -1)
- vaccine_sample <- sample_intervention(target, 'pev', vaccine_coverage, correlations)
+
+ pev_sample <- sample_intervention(target, 'pev', pev_coverage, correlations)
mda_sample <- sample_intervention(target, 'mda', mda_coverage, correlations)
- expect_equal(length(intersect(which(vaccine_sample), which(mda_sample))), 0)
- expect_equal(sum(vaccine_sample), .2 * pop, tolerance=1e2)
- expect_equal(sum(mda_sample), .2 * pop, tolerance=1e2)
+
+ expect_equal(sum(pev_sample), pop * pev_coverage, tolerance=.1)
+ expect_equal(sum(mda_sample), pop * mda_coverage, tolerance=.1)
+
+ expect_equal(sum(pev_sample & mda_sample), 0, tolerance=.1)
+ expect_equal(
+ sum(pev_sample | mda_sample),
+ pop * (pev_coverage + mda_coverage),
+ tolerance=.1)
+})
+
+test_that('correlation between rounds is preserved when adding interventions', {
+ pop <- 1e6
+ target <- seq(pop)
+
+ pev_coverage_1 <- .2
+ pev_coverage_2 <- .4
+
+ initial <- CorrelationParameters$new(pop, c('pev'))
+ initial$inter_round_rho('pev', 1)
+
+ restored <- CorrelationParameters$new(pop, c('pev', 'mda'))
+ restored$inter_round_rho('pev', 1)
+ restored$inter_round_rho('mda', 1)
+ restored$inter_intervention_rho('pev', 'mda', 1)
+ restored$restore_state(0, initial$save_state())
+
+ round_1 <- sample_intervention(target, 'pev', pev_coverage_1, initial)
+ round_2 <- sample_intervention(target, 'pev', pev_coverage_2, restored)
+
+ expect_equal(sum(round_1), pop * pev_coverage_1, tolerance=.1)
+ expect_equal(sum(round_2), pop * pev_coverage_2, tolerance=.1)
+
+ expect_equal(
+ sum(round_1 & round_2),
+ pop * min(pev_coverage_1, pev_coverage_2),
+ tolerance=.1)
+
+ expect_equal(
+ sum(round_1 | round_2),
+ pop * max(pev_coverage_1, pev_coverage_2),
+ tolerance=.1)
+})
+
+test_that('1 correlation between interventions gives sensible samples when restored', {
+ pop <- 1e6
+ target <- seq(pop)
+
+ pev_coverage <- .2
+ mda_coverage <- .2
+
+ initial <- CorrelationParameters$new(pop, c('pev'))
+ initial$inter_round_rho('pev', 1)
+
+ restored <- CorrelationParameters$new(pop, c('pev', 'mda'))
+ restored$inter_round_rho('pev', 1)
+ restored$inter_round_rho('mda', 1)
+ restored$inter_intervention_rho('pev', 'mda', 1)
+ restored$restore_state(0, initial$save_state())
+
+ pev_sample <- sample_intervention(target, 'pev', pev_coverage, initial)
+ mda_sample <- sample_intervention(target, 'mda', mda_coverage, restored)
+
+ expect_equal(sum(pev_sample), pop * pev_coverage, tolerance=.1)
+ expect_equal(sum(mda_sample), pop * mda_coverage, tolerance=.1)
+
+ expect_equal(
+ sum(pev_sample & mda_sample),
+ pop * min(pev_coverage, mda_coverage),
+ tolerance=.1)
+
+ expect_equal(
+ sum(pev_sample | mda_sample),
+ pop * max(pev_coverage, mda_coverage),
+ tolerance=.1)
+})
+
+test_that('0 correlation between interventions gives sensible samples when restored', {
+ pop <- 1e6
+ target <- seq(pop)
+
+ pev_coverage <- .2
+ mda_coverage <- .2
+
+ initial <- CorrelationParameters$new(pop, c('pev'))
+ initial$inter_round_rho('pev', 1)
+
+ restored <- CorrelationParameters$new(pop, c('pev', 'mda'))
+ restored$inter_round_rho('pev', 1)
+ restored$inter_round_rho('mda', 1)
+ restored$inter_intervention_rho('pev', 'mda', 0)
+ restored$restore_state(0, initial$save_state())
+
+ pev_sample <- sample_intervention(target, 'pev', pev_coverage, initial)
+ mda_sample <- sample_intervention(target, 'mda', mda_coverage, restored)
+
+ expect_equal(sum(pev_sample), pop * pev_coverage, tolerance=.1)
+ expect_equal(sum(mda_sample), pop * mda_coverage, tolerance=.1)
+
+ expect_equal(
+ sum(pev_sample & mda_sample),
+ pop * pev_coverage * mda_coverage,
+ tolerance=.1)
+
+ expect_equal(
+ sum(pev_sample | mda_sample),
+ pop * (pev_coverage + mda_coverage - (pev_coverage * mda_coverage)),
+ tolerance=.1)
+})
+
+test_that('-1 correlation between interventions gives sensible samples when restored', {
+ pop <- 1e6
+ target <- seq(pop)
+ pev_coverage <- .2
+ mda_coverage <- .2
+
+ initial <- CorrelationParameters$new(pop, c('pev'))
+ initial$inter_round_rho('pev', 1)
+
+ restored <- CorrelationParameters$new(pop, c('pev', 'mda'))
+ restored$inter_round_rho('pev', 1)
+ restored$inter_round_rho('mda', 1)
+ restored$inter_intervention_rho('pev', 'mda', -1)
+ restored$restore_state(0, initial$save_state())
+
+ pev_sample <- sample_intervention(target, 'pev', pev_coverage, initial)
+ mda_sample <- sample_intervention(target, 'mda', mda_coverage, restored)
+
+ expect_equal(sum(pev_sample), pop * pev_coverage, tolerance=.1)
+ expect_equal(sum(mda_sample), pop * mda_coverage, tolerance=.1)
+
+ expect_equal(sum(pev_sample & mda_sample), 0, tolerance=.1)
+ expect_equal(
+ sum(pev_sample | mda_sample),
+ pop * (pev_coverage + mda_coverage),
+ tolerance=.1)
+})
+
+test_that("rcondmvnorm has correct distribution", {
+ set.seed(123)
+
+ pop <- 1e6
+
+ # These are completely arbitrary values. The statistics from the simulated
+ # sample will get compared back to this.
+ means <- c(-5, 7, 0, 0.3)
+ variance <- c(0.3, 0.6, 0.9, 0.2)
+ correlation <- matrix(0, ncol=4, nrow=4)
+ correlation[1,2] <- correlation[2,1] <- -0.6
+ correlation[1,3] <- correlation[3,1] <- 0.4
+ correlation[1,4] <- correlation[4,1] <- 1
+ correlation[2,3] <- correlation[3,2] <- 0
+ correlation[2,4] <- correlation[4,2] <- 0.1
+ correlation[3,4] <- correlation[4,3] <- 0.5
+
+ covariance <- outer(variance, variance) * correlation
+ diag(covariance) <- variance
+
+ # These are indices of variables that get simulated together.
+ wy.ind <- c(1, 3)
+ xz.ind <- c(2, 4)
+
+ # Simulate from the MVN for a subset of the dimensions. We intentionally pass
+ # in a subset of the mean and covariance matrices, as the rest of the
+ # parameters are not needed and may not be known. `dependent.ind` is relative
+ # to the subsetted mean and covariance, and therefore is set the first two
+ # indices as opposed to `wy.ind`.
+ wy <- rcondmvnorm(pop, means[wy.ind], covariance[wy.ind, wy.ind], given=NULL,
+ dependent.ind=c(1,2), given.ind=NULL)
+ expect_equal(dim(wy), c(pop, 2))
+ expect_equal(apply(wy, 2, mean), means[wy.ind], tolerance=0.001)
+ expect_equal(cov(wy), covariance[wy.ind, wy.ind], tolerance=0.001)
+
+ # Now simulate some more, but conditional on the existing values.
+ # The call to rcondmvnorm needs all the mean and covariance parameters,
+ # including the ones that have already been simulated.
+ xz <- rcondmvnorm(pop, means, covariance, given=wy,
+ dependent.ind=xz.ind, given.ind=wy.ind)
+ expect_equal(dim(xz), c(pop, 2))
+ expect_equal(apply(xz, 2, mean), means[xz.ind], tolerance=0.001)
+ expect_equal(cov(xz), covariance[xz.ind, xz.ind], tolerance=0.001)
+
+ # Stitch the variables back together and make sure the covariance across the
+ # separately simulated values match the expected one.
+ values <- cbind(wy[,1], xz[,1], wy[,2], xz[,2])
+ expect_equal(apply(values, 2, mean), means, tolerance=0.001)
+ expect_equal(cov(values), covariance, tolerance=0.001)
+})
+
+# This would usually be considered an uninteresting edge case, since null
+# variance just yields a constant vector. The way we use the function in the
+# simulation though, a null variance is actually the default and most common
+# case, as it is used where the different rounds of an intervention have no
+# correlation.
+#
+# We need to make sure the other variables are still correctly simulated.
+test_that("rcondmvnorm allows null variance of given variables", {
+ set.seed(123)
+
+ pop <- 1e6
+
+ means <- c(-5, 7, 0)
+ variance <- c(0.3, 0, 0.9)
+ correlation <- matrix(0, ncol=3, nrow=3)
+ correlation[1,3] <- 0.4
+ correlation[3,1] <- 0.4
+
+ covariance <- outer(variance, variance) * correlation
+ diag(covariance) <- variance
+
+ # These are indices of variables that get simulated together.
+ y.ind <- 2
+ xz.ind <- c(1,3)
+
+ # Simulate one dimension. Because its variance was null, the result is a
+ # constant vector.
+ y <- rcondmvnorm(pop, means, covariance, given=NULL,
+ dependent.ind=y.ind, given.ind=NULL)
+ expect_equal(as.vector(y), rep(means[y.ind], pop))
+
+ # Simulate the rest. The original dimension has no influence on the result.
+ xz <- rcondmvnorm(pop, means, covariance, given=y,
+ dependent.ind=xz.ind, given.ind=y.ind)
+
+ xyz <- cbind(xz[,1], y, xz[,2])
+ expect_equal(apply(xyz, 2, mean), means, tolerance=0.001)
+ expect_equal(cov(xyz), covariance, tolerance=0.001)
})
diff --git a/tests/testthat/test-emergence-integration.R b/tests/testthat/test-emergence-integration.R
index fa7464be..bf452d2f 100644
--- a/tests/testthat/test-emergence-integration.R
+++ b/tests/testthat/test-emergence-integration.R
@@ -8,22 +8,19 @@ test_that('emergence process fails when there are not enough individuals', {
c('gamb'),
rep('gamb', 2000)
)
+ solvers <- list(
+ mock_solver(c(1000, 500, 100)),
+ mock_solver(c(1000, 500, 100))
+ )
+
emergence_process <- create_mosquito_emergence_process(
- list(),
+ solvers,
state,
species,
c('gamb'),
parameters$dpl
)
- mockery::stub(
- emergence_process,
- 'solver_get_states',
- mockery::mock(
- c(1000, 500, 100),
- c(1000, 500, 100)
- )
- )
- expect_error(emergence_process(0), '*')
+ expect_error(emergence_process(0), 'Not enough mosquitoes')
})
test_that('emergence_process creates the correct number of susceptables', {
@@ -36,23 +33,19 @@ test_that('emergence_process creates the correct number of susceptables', {
c('a', 'b'),
c('a', 'b')
)
+ solvers <- list(
+ mock_solver(c(100000, 50000, 10000)),
+ mock_solver(c(1000, 5000, 1000))
+ )
+
emergence_process <- create_mosquito_emergence_process(
- list(mockery::mock(), mockery::mock()),
+ solvers,
state,
species,
c('a', 'b'),
parameters$dpl
)
- mockery::stub(
- emergence_process,
- 'solver_get_states',
- mockery::mock(
- c(100000, 50000, 10000),
- c(10000, 5000, 1000)
- )
- )
-
emergence_process(0)
expect_bitset_update(
diff --git a/tests/testthat/test-infection-integration.R b/tests/testthat/test-infection-integration.R
index a8e6033e..83d7def9 100644
--- a/tests/testthat/test-infection-integration.R
+++ b/tests/testthat/test-infection-integration.R
@@ -6,7 +6,7 @@ test_that('simulate_infection integrates different types of infection and schedu
))
events <- create_events(parameters)
renderer <- mock_render(timestep)
-
+
age <- c(20, 24, 5, 39, 20, 24, 5, 39) * 365
immunity <- c(.2, .3, .5, .9, .2, .3, .5, .9)
asymptomatics <- mockery::mock()
@@ -15,7 +15,7 @@ test_that('simulate_infection integrates different types of infection and schedu
id = individual::DoubleVariable$new(immunity),
state = list(get_index_of = mockery::mock(asymptomatics))
)
-
+
bitten <- individual::Bitset$new(population)$insert(c(1, 3, 5, 7))
boost_immunity_mock <- mockery::mock()
infected <- individual::Bitset$new(population)$insert(c(1, 3, 5))
@@ -27,7 +27,7 @@ test_that('simulate_infection integrates different types of infection and schedu
treated <- individual::Bitset$new(population)$insert(3)
treated_mock <- mockery::mock(treated)
schedule_mock <- mockery::mock()
-
+
mockery::stub(simulate_infection, 'boost_immunity', boost_immunity_mock)
mockery::stub(simulate_infection, 'calculate_infections', infection_mock)
mockery::stub(simulate_infection, 'calculate_clinical_infections', clinical_infection_mock)
@@ -45,7 +45,7 @@ test_that('simulate_infection integrates different types of infection and schedu
timestep,
renderer
)
-
+
mockery::expect_args(
boost_immunity_mock,
1,
@@ -55,7 +55,7 @@ test_that('simulate_infection integrates different types of infection and schedu
5,
parameters$ub
)
-
+
mockery::expect_args(
infection_mock,
1,
@@ -65,7 +65,7 @@ test_that('simulate_infection integrates different types of infection and schedu
renderer,
timestep
)
-
+
mockery::expect_args(
clinical_infection_mock,
1,
@@ -75,7 +75,7 @@ test_that('simulate_infection integrates different types of infection and schedu
renderer,
timestep
)
-
+
mockery::expect_args(
severe_infection_mock,
1,
@@ -85,7 +85,7 @@ test_that('simulate_infection integrates different types of infection and schedu
parameters,
renderer
)
-
+
mockery::expect_args(
treated_mock,
1,
@@ -95,7 +95,7 @@ test_that('simulate_infection integrates different types of infection and schedu
timestep,
renderer
)
-
+
mockery::expect_args(
schedule_mock,
1,
@@ -121,11 +121,11 @@ test_that('calculate_infections works various combinations of drug and vaccinati
min_ages = 0,
max_ages = 100 * 365,
min_wait = 0,
- booster_timestep = 365,
- booster_coverage = 1,
+ booster_spacing = 365,
+ booster_coverage = matrix(1),
booster_profile = list(rtss_booster_profile)
)
-
+
variables <- list(
state = individual::CategoricalVariable$new(
c('D', 'S', 'A', 'U', 'Tr'),
@@ -133,11 +133,11 @@ test_that('calculate_infections works various combinations of drug and vaccinati
),
drug = individual::DoubleVariable$new(c(1, 2, 0, 0)),
drug_time = individual::DoubleVariable$new(c(20, 30, -1, -1)),
- pev_timestep = individual::DoubleVariable$new(c(-1, 10, 40, -1)),
+ last_eff_pev_timestep = individual::DoubleVariable$new(c(-1, 10, 40, -1)),
pev_profile = individual::IntegerVariable$new(c(-1, 1, 2, -1)),
ib = individual::DoubleVariable$new(c(.2, .3, .5, .9))
)
-
+
immunity_mock <- mockery::mock(c(.2, .3, .4))
weibull_mock <- mockery::mock(.2)
vaccine_antibodies_mock <- mockery::mock(c(2, 3))
@@ -148,7 +148,7 @@ test_that('calculate_infections works various combinations of drug and vaccinati
mockery::stub(calculate_infections, 'calculate_pev_antibodies', vaccine_antibodies_mock)
mockery::stub(calculate_infections, 'calculate_pev_efficacy', vaccine_efficacy_mock)
mockery::stub(calculate_infections, 'bernoulli_multi_p', bernoulli_mock)
-
+
# remove randomness from vaccine parameters
mockery::stub(
calculate_infections,
@@ -158,9 +158,9 @@ test_that('calculate_infections works various combinations of drug and vaccinati
},
depth = 4
)
-
+
bitten_humans <- individual::Bitset$new(4)$insert(c(1, 2, 3, 4))
-
+
infections <- calculate_infections(
variables,
bitten_humans,
@@ -168,9 +168,9 @@ test_that('calculate_infections works various combinations of drug and vaccinati
mock_render(timestep),
timestep
)
-
+
expect_equal(infections$to_vector(), 3)
-
+
mockery::expect_args(immunity_mock, 1, c(.3, .5, .9), parameters)
mockery::expect_args(
weibull_mock,
@@ -179,7 +179,7 @@ test_that('calculate_infections works various combinations of drug and vaccinati
parameters$drug_prophylaxis_shape[[2]],
parameters$drug_prophylaxis_scale[[2]]
)
-
+
mockery::expect_args(
vaccine_antibodies_mock,
1,
@@ -203,14 +203,13 @@ test_that('calculate_infections works various combinations of drug and vaccinati
1,
c(.2 * .8 * .8, .3 * .7, .4)
)
-
+
})
-
test_that('calculate_clinical_infections correctly samples clinically infected', {
timestep <- 5
parameters <- get_parameters()
-
+
variables <- list(
ica = individual::DoubleVariable$new(c(.2, .3, .5, .9)),
icm = individual::DoubleVariable$new(c(.2, .3, .5, .9)),
@@ -218,25 +217,25 @@ test_that('calculate_clinical_infections correctly samples clinically infected',
last_boosted_ica = individual::DoubleVariable$new(c(-1, -1, 1, -1)),
last_boosted_id = individual::DoubleVariable$new(c(-1, -1, 1, -1))
)
-
+
immunity_mock <- mockery::mock(c(.2, .3, .4))
boost_mock <- mockery::mock()
mockery::stub(calculate_clinical_infections, 'boost_immunity', boost_mock)
-
+
mockery::stub(calculate_clinical_infections, 'clinical_immunity', immunity_mock)
bernoulli_mock <- mockery::mock(c(1, 3))
mockery::stub(calculate_clinical_infections, 'bernoulli_multi_p', bernoulli_mock)
-
+
infections <- individual::Bitset$new(4)$insert(c(2, 3, 4))
-
+
clinical_infections <- calculate_clinical_infections(
variables,
infections,
parameters
)
-
+
expect_equal(clinical_infections$to_vector(), c(2, 4))
-
+
mockery::expect_args(
immunity_mock,
1,
@@ -244,7 +243,7 @@ test_that('calculate_clinical_infections correctly samples clinically infected',
c(.3, .5, .9),
parameters
)
-
+
mockery::expect_args(
bernoulli_mock,
1,
@@ -265,10 +264,10 @@ test_that('calculate_treated correctly samples treated and updates the drug stat
drug = list(queue_update = mockery::mock()),
drug_time = list(queue_update = mockery::mock())
)
-
+
recovery_mock <- mockery::mock()
mockery::stub(calculate_treated, 'recovery$schedule', recovery_mock)
-
+
seek_treatment <- individual::Bitset$new(4)$insert(c(1, 2, 4))
mockery::stub(
calculate_treated,
@@ -280,7 +279,7 @@ test_that('calculate_treated correctly samples treated and updates the drug stat
bernoulli_mock <- mockery::mock(c(1, 3))
mockery::stub(calculate_treated, 'bernoulli_multi_p', bernoulli_mock)
mockery::stub(calculate_treated, 'log_uniform', mockery::mock(c(3, 4)))
-
+
clinical_infections <- individual::Bitset$new(4)
clinical_infections$insert(c(1, 2, 3, 4))
calculate_treated(
@@ -290,7 +289,7 @@ test_that('calculate_treated correctly samples treated and updates the drug stat
timestep,
mock_render(timestep)
)
-
+
mockery::expect_args(
sample_mock,
1,
@@ -299,13 +298,13 @@ test_that('calculate_treated correctly samples treated and updates the drug stat
c(.25, .25),
TRUE
)
-
+
mockery::expect_args(
bernoulli_mock,
1,
parameters$drug_efficacy[c(2, 1, 1, 1)]
)
-
+
expect_bitset_update(variables$state$queue_update, 'Tr', c(1, 4))
expect_bitset_update(
variables$infectivity$queue_update,
@@ -316,20 +315,288 @@ test_that('calculate_treated correctly samples treated and updates the drug stat
expect_bitset_update(variables$drug_time$queue_update, 5, c(1, 4))
})
+test_that('calculate_treated correctly samples treated and updates the drug state when resistance set', {
+
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(AL_params, SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 1, timesteps = 1, coverages = 0.25)
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 2, timesteps = 1, coverages = 0.25)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.5,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0,
+ early_treatment_failure_probability = 0.2,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 10)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 2,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.3,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0,
+ early_treatment_failure_probability = 0.9,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 15)
+
+ clinical_infections <- individual::Bitset$new(20)$insert(1:20)
+ timestep <- 5
+ events <- create_events(parameters)
+ variables <- list(
+ state = list(queue_update = mockery::mock()),
+ infectivity = list(queue_update = mockery::mock()),
+ drug = list(queue_update = mockery::mock()),
+ drug_time = list(queue_update = mockery::mock()),
+ dt = list(queue_update = mockery::mock())
+ )
+ renderer <- individual::Render$new(timesteps = timestep)
+
+ # Set up seek_treatment mock and instruct calculate_treated() to return it when sample_bitset() called:
+ seek_treatment <- individual::Bitset$new(20)$insert(c(1:10))
+ seek_treatment_mock <- mockery::mock(seek_treatment)
+ mockery::stub(where = calculate_treated, what = 'sample_bitset', how = seek_treatment_mock)
+
+ # Set up drugs mock and instruct it to return it when sample.int() called:
+ mock_drugs <- mockery::mock(c(2, 1, 1, 1, 2, 2, 2, 1, 2, 1))
+ mockery::stub(calculate_treated, 'sample.int', mock_drugs)
+
+ # Set up bernoulli mock and instruct calculate_treated to return it when bernoulli_multi_p() called:
+ bernoulli_mock <- mockery::mock(c(1, 2, 3, 4, 5, 6, 7, 8, 9),
+ c(1, 2, 3, 4, 5, 6, 7),
+ c(1))
+ mockery::stub(calculate_treated, 'bernoulli_multi_p', bernoulli_mock)
+
+ calculate_treated(
+ variables,
+ clinical_infections,
+ parameters,
+ timestep,
+ renderer
+ )
+
+ mockery::expect_args(
+ mock_drugs,
+ 1,
+ 2,
+ 10,
+ c(.25, .25),
+ TRUE
+ )
+
+ mockery::expect_args(
+ seek_treatment_mock,
+ 1,
+ clinical_infections,
+ 0.5
+ )
+
+ mockery::expect_args(
+ bernoulli_mock,
+ 1,
+ c(0.9, 0.95, 0.95, 0.95, 0.9, 0.9, 0.9, 0.95, 0.9, 0.95)
+ )
+
+ mockery::expect_args(
+ bernoulli_mock,
+ 2,
+ c(0.73, 0.9, 0.9, 0.9, 0.73, 0.73, 0.73, 0.9, 0.73)
+ )
+
+ mockery::expect_args(
+ bernoulli_mock,
+ 2,
+ 1 - (unlist(parameters$artemisinin_resistance_proportion[c(2, 1, 1, 1, 2, 2, 2, 1, 2)]) * unlist(parameters$early_treatment_failure_probability[c(2, 1, 1, 1, 2, 2, 2, 1, 2)]))
+ )
+
+ mockery::expect_args(
+ bernoulli_mock,
+ 3,
+ unlist(parameters$artemisinin_resistance_proportion[c(2, 1, 1, 1, 2, 2, 2)]) * unlist(parameters$slow_parasite_clearance_probability[c(2, 1, 1, 1, 2, 2, 2)])
+ )
+
+ expect_bitset_update(variables$state$queue_update, 'Tr', c(1, 2, 3, 4, 5, 6, 7))
+ expect_bitset_update(
+ variables$infectivity$queue_update,
+ parameters$cd * parameters$drug_rel_c[c(2, 1, 1, 1, 2, 2, 2)],
+ c(1, 2, 3, 4, 5, 6, 7)
+ )
+ expect_bitset_update(variables$drug$queue_update, c(2, 1, 1, 1, 2, 2, 2), c(1, 2, 3, 4, 5, 6, 7))
+ expect_bitset_update(variables$drug_time$queue_update, 5, c(1, 2, 3, 4, 5, 6, 7))
+ expect_bitset_update(variables$dt$queue_update, 5, c(2, 3, 4, 5, 6, 7), 1)
+ expect_bitset_update(variables$dt$queue_update, 15, c(1), 2)
+})
+
+test_that('calculate_treated correctly samples treated and updates the drug state when resistance not set for all drugs', {
+
+ # Establish the parameters
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(AL_params, SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 1, timesteps = 1, coverages = 0.25)
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 2, timesteps = 1, coverages = 0.25)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 2,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.8,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0.2,
+ early_treatment_failure_probability = 0.3,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 20)
+
+ # Establish Bitset of clinically infected individuals
+ clinical_infections <- individual::Bitset$new(20)$insert(1:20)
+
+ # Set the timestep to 5:
+ timestep <- 5
+
+ # Establish the events:
+ events <- create_events(parameters)
+
+ # Establish list of variables used in calculate_treated() using mocks:
+ variables <- list(
+ state = list(queue_update = mockery::mock()),
+ infectivity = list(queue_update = mockery::mock()),
+ drug = list(queue_update = mockery::mock()),
+ drug_time = list(queue_update = mockery::mock()),
+ dt = list(queue_update = mockery::mock())
+ )
+
+ # Create a Bitset of individuals seeking treatment individuals:
+ seek_treatment <- individual::Bitset$new(20)$insert(c(1:10))
+
+ # Create a mock of seek_treatment:
+ seek_treatment_mock <- mockery::mock(seek_treatment)
+
+ # Specify that, when calculate_treated() calls sample_bitset(), return the seek_treatment_mock:
+ mockery::stub(where = calculate_treated, what = 'sample_bitset', how = seek_treatment_mock)
+
+ # Create a mock_drugs object (5 of each drug):
+ mock_drugs <- mockery::mock(c(2, 1, 1, 1, 2, 2, 2, 1, 2, 1))
+
+ # Specify that when calculate_treated() calls sample.int(), it returns mock_drugs:
+ mockery::stub(calculate_treated, 'sample.int', mock_drugs)
+
+ # Create a bernoulli_mock of i) individuals susceptible, and ii) individuals successfully treated:
+ bernoulli_mock <- mockery::mock(c(1, 2, 3, 4, 5, 6, 7, 8, 9),
+ c(1, 2, 3, 4, 5, 6, 7),
+ c(1))
+
+ # Specify that when calculate_treated() calls bernoulli_multi_p() it returns the bernoulli_mock:
+ mockery::stub(calculate_treated, 'bernoulli_multi_p', bernoulli_mock)
+
+ # Run the calculate_treated() function now the mocks and stubs are established:
+ calculate_treated(
+ variables,
+ clinical_infections,
+ parameters,
+ timestep,
+ mock_render(timestep)
+ )
+
+ # Check that mock_drugs was called only once, and that the arguments used in the function call
+ # mock_drugs() was used in (sample.int()) match those expected:
+ mockery::expect_args(
+ mock_drugs,
+ 1,
+ 2,
+ 10,
+ c(.25, .25),
+ TRUE
+ )
+
+ # Check that seek_treatment_mock was called only once, and that the arguments used in the function
+ # call mock_drugs() was used in (sample_bitset()) match those expected:
+ mockery::expect_args(
+ seek_treatment_mock,
+ 1,
+ clinical_infections,
+ 0.5
+ )
+
+ # Check that the first time bernoulli_mock was called the arguments used in the function
+ # call bernoulli_mock was involved in (bernoulli_multi_p()) match those expected:
+ mockery::expect_args(
+ bernoulli_mock,
+ 1,
+ parameters$drug_efficacy[c(2, 1, 1, 1, 2, 2, 2, 1, 2, 1)]
+ )
+
+ # Check that the secnd time bernoulli_mock was called (bernoulli_multi_p()) the arguments used in
+ # the function it was called in are as expected:
+ mockery::expect_args(
+ bernoulli_mock,
+ 2,
+ c(0.76, 1, 1, 1, 0.76, 0.76, 0.76, 1, 0.76)
+ )
+
+ # Check that update queued that updates the state of successfully treated individuals to "Tr"
+ expect_bitset_update(
+ variables$state$queue_update,
+ 'Tr',
+ c(1, 2, 3, 4, 5, 6, 7)
+ )
+
+ # Check that update queued that updates the infectivity of successfully treated individuals to "Tr"
+ # to their new infectivity (drug concentration x infectivity of "D" compartment)
+ expect_bitset_update(
+ variables$infectivity$queue_update,
+ parameters$cd * parameters$drug_rel_c[c(2, 1, 1, 1, 2, 2, 2)],
+ c(1, 2, 3, 4, 5, 6, 7)
+ )
+
+ # Check that update queued that updates the drug of successfully treated individuals to the drug
+ # they took:
+ expect_bitset_update(
+ variables$drug$queue_update,
+ c(2, 1, 1, 1, 2, 2, 2),
+ c(1, 2, 3, 4, 5, 6, 7)
+ )
+
+ # Check that update queued that updates the drug time of successfully treated individuals to the
+ # simulated/mocked time step (5)
+ expect_bitset_update(
+ variables$drug_time$queue_update,
+ 5,
+ c(1, 2, 3, 4, 5, 6, 7)
+ )
+
+ # Check that update queued for dt for the non-slow parasite clearance individuals is correct:
+ expect_bitset_update(
+ variables$dt$queue_update,
+ parameters$dt,
+ c(2, 3, 4, 5, 6, 7),
+ 1)
+
+ # Check that update queued for dt for the slow parasite clearance individuals is correct:
+ expect_bitset_update(
+ variables$dt$queue_update,
+ unlist(parameters$dt_slow_parasite_clearance),
+ c(1),
+ 2)
+
+})
+
test_that('schedule_infections correctly schedules new infections', {
parameters <- get_parameters(list(human_population = 20))
variables <- create_variables(parameters)
-
+
infections <- individual::Bitset$new(20)$insert(1:20)
clinical_infections <- individual::Bitset$new(20)$insert(5:15)
treated <- individual::Bitset$new(20)$insert(7:12)
-
+
infection_mock <- mockery::mock()
asymp_mock <- mockery::mock()
-
+
mockery::stub(schedule_infections, 'update_infection', infection_mock)
mockery::stub(schedule_infections, 'update_to_asymptomatic_infection', asymp_mock)
-
+
schedule_infections(
variables,
clinical_infections,
@@ -338,15 +605,15 @@ test_that('schedule_infections correctly schedules new infections', {
parameters,
42
)
-
+
actual_infected <- mockery::mock_args(infection_mock)[[1]][[5]]$to_vector()
actual_asymp_infected <- mockery::mock_args(asymp_mock)[[1]][[4]]$to_vector()
-
+
expect_equal(
actual_infected,
c(5, 6, 13, 14, 15)
)
-
+
expect_equal(
actual_asymp_infected,
c(1, 2, 3, 4, 16, 17, 18, 19, 20)
@@ -358,7 +625,7 @@ test_that('prophylaxis is considered for medicated humans', {
parameters <- set_drugs(parameters, list(AL_params, DHA_PQP_params))
events <- create_events(parameters)
timestep <- 50
-
+
variables = list(
state = individual::CategoricalVariable$new(
c('D', 'S', 'A', 'U', 'Tr'),
@@ -366,15 +633,15 @@ test_that('prophylaxis is considered for medicated humans', {
),
drug = individual::DoubleVariable$new(c(0, 2, 1, 0)),
drug_time = individual::DoubleVariable$new(c(-1, 49, 40, -1)),
- pev_timestep = individual::DoubleVariable$new(c(-1, -1, -1, -1)),
+ last_eff_pev_timestep = individual::DoubleVariable$new(c(-1, -1, -1, -1)),
pev_profile = individual::IntegerVariable$new(c(-1, -1, -1, -1)),
ib = individual::DoubleVariable$new(c(.2, .3, .5, .9))
)
-
+
bitten <- individual::Bitset$new(4)$insert(seq(4))
m <- mockery::mock(seq(3))
mockery::stub(calculate_infections, 'bernoulli_multi_p', m)
-
+
calculate_infections(
variables,
bitten,
@@ -382,7 +649,7 @@ test_that('prophylaxis is considered for medicated humans', {
mock_render(timestep),
timestep
)
-
+
expect_equal(
mockery::mock_args(m)[[1]][[1]],
c(2.491951e-07, 2.384032e-01, 5.899334e-01),
@@ -394,17 +661,17 @@ test_that('boost_immunity respects the delay period', {
level <- c(2.4, 1.2, 0., 4.)
immunity <- individual::DoubleVariable$new(level)
last_boosted <- individual::DoubleVariable$new(c(11, 5, 1, 13))
-
+
level_mock <- mockery::mock()
mockery::stub(boost_immunity, 'immunity_variable$queue_update', level_mock)
-
+
last_mock <- mockery::mock()
mockery::stub(boost_immunity, 'last_boosted_variable$queue_update', last_mock)
-
+
index <- individual::Bitset$new(4)$insert(seq(4))
timestep <- 15
delay <- 4
-
+
boost_immunity(
immunity,
index,
@@ -412,14 +679,14 @@ test_that('boost_immunity respects the delay period', {
timestep,
delay
)
-
+
mockery::expect_args(
level_mock,
1,
c(3.4, 2.2, 1),
seq(3)
)
-
+
mockery::expect_args(
last_mock,
1,
@@ -432,18 +699,18 @@ test_that('boost_immunity respects the delay period', {
level <- c(2.4, 1.2, 0., 4., 0.)
immunity <- individual::DoubleVariable$new(level)
last_boosted <- individual::DoubleVariable$new(c(11, 5, 1, 13, -1))
-
+
index <- individual::Bitset$new(5)
index$insert(seq(5))
timestep <- 15
delay <- 4
-
+
level_mock <- mockery::mock()
mockery::stub(boost_immunity, 'immunity_variable$queue_update', level_mock)
-
+
last_mock <- mockery::mock()
mockery::stub(boost_immunity, 'last_boosted_variable$queue_update', last_mock)
-
+
boost_immunity(
immunity,
index,
@@ -451,14 +718,14 @@ test_that('boost_immunity respects the delay period', {
timestep,
delay
)
-
+
mockery::expect_args(
level_mock,
1,
c(3.4, 2.2, 1, 1),
c(seq(3), 5)
)
-
+
mockery::expect_args(
last_mock,
1,
@@ -471,18 +738,18 @@ test_that('boost_immunity does not update when there is no-one to update', {
level <- c(2.4, 1.2, 0., 4., 0.)
immunity <- individual::DoubleVariable$new(level)
last_boosted <- individual::DoubleVariable$new(c(11, 5, 1, 13, -1))
-
+
index <- individual::Bitset$new(5)
index$insert(seq(5))
timestep <- 15
delay <- 4
-
+
level_mock <- mockery::mock()
mockery::stub(boost_immunity, 'immunity$queue_update', level_mock)
-
+
last_mock <- mockery::mock()
mockery::stub(boost_immunity, 'last_boosted$queue_update', last_mock)
-
+
boost_immunity(
immunity,
index,
@@ -504,11 +771,11 @@ test_that('update_severe_disease renders with no infections', {
severe_incidence_rendering_max_ages = 5 * 365
))
variables <- create_variables(parameters)
-
+
render_function <- mockery::mock()
mockery::stub(update_severe_disease, 'incidence_renderer', render_function)
empty <- individual::Bitset$new(population)
-
+
update_severe_disease(
timestep,
empty,
@@ -516,7 +783,7 @@ test_that('update_severe_disease renders with no infections', {
parameters,
renderer
)
-
+
mockery::expect_args(
render_function,
1,
@@ -531,3 +798,199 @@ test_that('update_severe_disease renders with no infections', {
timestep
)
})
+
+test_that('calculate_treated returns empty Bitset when there is no clinical treatment coverage', {
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(AL_params))
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 1, timesteps = 1, coverages = 0)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.5,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0,
+ early_treatment_failure_probability = 0.2,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 10)
+ clinical_infections <- individual::Bitset$new(20)$insert(1:20)
+ timestep <- 5
+ events <- create_events(parameters)
+ variables <- list(
+ state = list(queue_update = mockery::mock()),
+ infectivity = list(queue_update = mockery::mock()),
+ drug = list(queue_update = mockery::mock()),
+ drug_time = list(queue_update = mockery::mock())
+ )
+ renderer <- individual::Render$new(timesteps = 10)
+
+ treated <- calculate_treated(variables = variables,
+ clinical_infections = clinical_infections,
+ parameters = parameters,
+ timestep = timestep,
+ renderer = renderer)
+
+ expect_identical(object = treated$size(), expected = 0, info = "Error: calculate_treated() returning non-zero number of treated individuals
+ in the absence of clinical treatment")
+})
+
+test_that('calculate_treated returns empty Bitset when the clinically_infected input is an empty Bitset', {
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(AL_params))
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 1, timesteps = 1, coverages = 1)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.5,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0,
+ early_treatment_failure_probability = 0.2,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 10)
+ clinical_infections <- individual::Bitset$new(20)
+ timestep <- 5
+ events <- create_events(parameters)
+ variables <- list(
+ state = list(queue_update = mockery::mock()),
+ infectivity = list(queue_update = mockery::mock()),
+ drug = list(queue_update = mockery::mock()),
+ drug_time = list(queue_update = mockery::mock())
+ )
+ renderer <- individual::Render$new(timesteps = 10)
+
+ treated <- calculate_treated(variables = variables,
+ clinical_infections = clinical_infections,
+ parameters = parameters,
+ timestep = timestep,
+ renderer = renderer)
+
+ expect_identical(object = treated$size(), expected = 0, info = "Error: calculate_treated() returning non-zero number of treated individuals
+ in the absence of clinically infected individuals")
+})
+
+test_that('calculate_treated() returns an empty Bitset when the parameter list contains no clinical
+ treatment or resistance parameters', {
+ parameters <- get_parameters()
+ clinical_infections <- individual::Bitset$new(20)$insert(1:20)
+ timestep <- 5
+ events <- create_events(parameters)
+ variables <- list(
+ state = list(queue_update = mockery::mock()),
+ infectivity = list(queue_update = mockery::mock()),
+ drug = list(queue_update = mockery::mock()),
+ drug_time = list(queue_update = mockery::mock())
+ )
+ renderer <- individual::Render$new(timesteps = 10)
+
+ treated <- calculate_treated(variables = variables,
+ clinical_infections = clinical_infections,
+ parameters = parameters,
+ timestep = timestep,
+ renderer = renderer)
+
+ expect_identical(object = treated$size(), expected = 0, info = "Error: calculate_treated() returning non-zero number of treated individuals
+ in the absence of clinical treatment or resistance parameters")
+})
+
+test_that('Number of treatment failures matches number of individuals treated when artemisinin resistance
+ proportion and early treatment failure probability both set to 1', {
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(AL_params, SP_AQ_params))
+ parameters <- set_clinical_treatment(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ coverages = round(runif(1, 0, 1/2),
+ digits = 2))
+ parameters <- set_clinical_treatment(parameters = parameters,
+ drug = 2,
+ timesteps = 1,
+ coverages = round(runif(1, 0, 1/2),
+ digits = 2))
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 1,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0,
+ early_treatment_failure_probability = 1,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 10)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 2,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 1,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0,
+ early_treatment_failure_probability = 1,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 20)
+
+ clinical_infections <- individual::Bitset$new(100)
+ clinical_infections$insert(sample.int(n = 100, size = round(runif(n = 1, min = 10, max = 100)), replace = FALSE))
+ timestep <- 5
+ events <- create_events(parameters)
+ variables <- create_variables(parameters = parameters)
+ renderer <- individual::Render$new(timesteps = 10)
+
+ treated <- calculate_treated(variables = variables,
+ clinical_infections = clinical_infections,
+ parameters = parameters,
+ timestep = timestep,
+ renderer = renderer)
+
+ expect_identical(renderer$to_dataframe()[timestep,'n_early_treatment_failure'], renderer$to_dataframe()[timestep,'n_treated'] - renderer$to_dataframe()[timestep,'n_drug_efficacy_failures'], info = "Error: Number of
+ early treatment failures does not match number of treated individuals (minus drug efficacy failures) when artemisinin resistance proportion and
+ and early treatment failure probability both equal 1")
+})
+
+test_that('calculate_treated() successfully adds additional resistance columns to the renderer', {
+ parameters <- get_parameters()
+ parameters <- set_drugs(parameters = parameters, drugs = list(AL_params))
+ parameters <- set_clinical_treatment(parameters = parameters, drug = 1, timesteps = 1, coverages = 1)
+ parameters <- set_antimalarial_resistance(parameters = parameters,
+ drug = 1,
+ timesteps = 1,
+ artemisinin_resistance_proportion = 0.5,
+ partner_drug_resistance_proportion = 0,
+ slow_parasite_clearance_probability = 0,
+ early_treatment_failure_probability = 0.5,
+ late_clinical_failure_probability = 0,
+ late_parasitological_failure_probability = 0,
+ reinfection_during_prophylaxis_probability = 0,
+ slow_parasite_clearance_time = 10)
+
+ clinical_infections <- individual::Bitset$new(20)$insert(1:20)
+ timestep <- 5
+ events <- create_events(parameters)
+ variables <- list(
+ state = list(queue_update = mockery::mock()),
+ infectivity = list(queue_update = mockery::mock()),
+ drug = list(queue_update = mockery::mock()),
+ drug_time = list(queue_update = mockery::mock()),
+ dt = list(queue_update = mockery::mock())
+ )
+ renderer <- individual::Render$new(timesteps = 10)
+
+ treated <- calculate_treated(variables = variables,
+ clinical_infections = clinical_infections,
+ parameters = parameters,
+ timestep = timestep,
+ renderer = renderer)
+
+ calculate_treated_column_names <- c("ft",
+ "n_treated",
+ "n_drug_efficacy_failures",
+ "n_early_treatment_failure",
+ "n_slow_parasite_clearance",
+ "n_successfully_treated")
+ expect_identical(sum(calculate_treated_column_names %in% colnames(renderer$to_dataframe())), length(calculate_treated_column_names),
+ "calculate_treated() not renderering all resistance columns when resistance is present, clinical treatment coverage
+ is non-zero, and the Bitset of clinically_infected individuals input is of non-zero length.")
+})
diff --git a/tests/testthat/test-mda.R b/tests/testthat/test-mda.R
index bf92ae3a..1ee440f6 100644
--- a/tests/testthat/test-mda.R
+++ b/tests/testthat/test-mda.R
@@ -81,11 +81,8 @@ test_that('MDA moves the diseased and non-diseased population correctly', {
drug = mock_double(c(1, 2, 1, 2))
)
- events$mda_administer <- mock_event(events$mda_administer)
-
listener <- create_mda_listeners(
variables,
- events$mda_administer,
parameters$mda_drug,
parameters$mda_timesteps,
parameters$mda_coverages,
@@ -141,12 +138,6 @@ test_that('MDA moves the diseased and non-diseased population correctly', {
50,
c(3, 4)
)
-
- mockery::expect_args(
- events$mda_administer$schedule,
- 1,
- 100
- )
})
test_that('MDA moves the diseased and non-diseased population correctly - second round, varying age range', {
@@ -176,11 +167,8 @@ test_that('MDA moves the diseased and non-diseased population correctly - second
drug = mock_double(c(1, 2, 1, 2))
)
- events$mda_administer <- mock_event(events$mda_administer)
-
listener <- create_mda_listeners(
variables,
- events$mda_administer,
parameters$mda_drug,
parameters$mda_timesteps,
parameters$mda_coverages,
@@ -265,11 +253,8 @@ test_that('MDA ignores non-detectable asymptomatics', {
drug = mock_double(c(1, 2, 1, 2))
)
- events$mda_administer <- mock_event(events$mda_administer)
-
listener <- create_mda_listeners(
variables,
- events$mda_administer,
parameters$mda_drug,
parameters$mda_timesteps,
parameters$mda_coverages,
diff --git a/tests/testthat/test-pev-epi.R b/tests/testthat/test-pev-epi.R
index 4eea8445..6555700c 100644
--- a/tests/testthat/test-pev-epi.R
+++ b/tests/testthat/test-pev-epi.R
@@ -7,8 +7,8 @@ test_that('pev epi strategy parameterisation works', {
timesteps = c(10, 100),
min_wait = 0,
age = 5 * 30,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8, .9, .8), nrow=2, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
)
expect_equal(parameters$pev, TRUE)
@@ -16,7 +16,7 @@ test_that('pev epi strategy parameterisation works', {
expect_equal(parameters$pev_epi_timesteps, c(10, 100))
expect_equal(parameters$pev_epi_age, 5 * 30)
expect_equal(parameters$pev_epi_min_wait, 0)
- expect_equal(parameters$pev_epi_booster_timestep, c(18, 36) * 30)
+ expect_equal(parameters$pev_epi_booster_spacing, c(18, 36) * 30)
expect_equal(parameters$pev_profiles, list(rtss_profile, rtss_booster_profile, rtss_booster_profile))
expect_equal(parameters$pev_epi_profile_indices, seq(3))
@@ -28,8 +28,8 @@ test_that('pev epi strategy parameterisation works', {
timesteps = 10,
min_wait = 0,
age = 5 * 30,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8, .9, .8), nrow=2, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
), "all(coverages >= 0) && all(coverages <= 1) is not TRUE",
fixed = TRUE
@@ -43,30 +43,53 @@ test_that('pev epi strategy parameterisation works', {
timesteps = 10,
min_wait = 0,
age = 5 * 30,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8, .9, .8), nrow=2, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
), "all(coverages >= 0) && all(coverages <= 1) is not TRUE",
fixed = TRUE
)
})
-test_that('pev epi fails pre-emptively with unaligned booster parameters', {
+test_that('set_pev_epi checks booster coverage matrix shape', {
parameters <- get_parameters()
expect_error(
- set_pev_epi(
+ parameters <- set_pev_epi(
+ parameters,
+ profile = rtss_profile,
+ coverages = c(0.1, 0.8),
+ timesteps = c(10, 100),
+ min_wait = 0,
+ age = 5 * 30,
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=2, ncol=1),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile)
+ ),
+ 'booster_spacing, booster_coverage and booster_profile do not align',
+ fixed = TRUE
+ )
+})
+
+test_that('set_pev_epi checks that booster_spacing are increasing', {
+ parameters <- get_parameters()
+ expect_error(
+ parameters <- set_pev_epi(
+ parameters,
profile = rtss_profile,
coverages = c(0.1, 0.8),
timesteps = c(10, 100),
min_wait = 0,
age = 5 * 30,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = .9,
+ booster_spacing = c(5, 5) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=2, ncol=1),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
- )
+ ),
+ 'booster_spacing must be monotonically increasing',
+ fixed = TRUE
)
})
+
test_that('pev epi targets correct age and respects min_wait', {
timestep <- 5*365
parameters <- get_parameters(list(human_population = 5))
@@ -77,8 +100,8 @@ test_that('pev epi targets correct age and respects min_wait', {
coverages = 0.8,
min_wait = 2*365,
age = 18 * 365,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
)
events <- create_events(parameters)
@@ -86,51 +109,120 @@ test_that('pev epi targets correct age and respects min_wait', {
variables$birth <- individual::IntegerVariable$new(
-c(18, 18, 2.9, 18, 18) * 365 + timestep
)
- variables$pev_timestep <- mock_integer(
+ variables$last_pev_timestep <- mock_integer(
c(50, -1, -1, 4*365, -1)
)
+ variables$pev_profile <- mock_integer(
+ c(1, -1, -1, 1, -1)
+ )
- events$pev_epi_doses <- lapply(events$pev_epi_doses, mock_event)
-
+ correlations <- get_correlation_parameters(parameters)
process <- create_epi_pev_process(
variables,
events,
parameters,
- get_correlation_parameters(parameters),
+ correlations,
parameters$pev_epi_coverages,
parameters$pev_epi_timesteps
)
+ sample_mock <- mockery::mock(c(TRUE, TRUE, FALSE))
mockery::stub(
process,
'sample_intervention',
- mockery::mock(c(TRUE, TRUE, FALSE))
+ sample_mock
)
process(timestep)
mockery::expect_args(
- events$pev_epi_doses[[1]]$schedule,
+ sample_mock,
1,
- c(1, 2),
- parameters$pev_doses[[1]]
+ c(1, 2, 5),
+ 'pev',
+ .8,
+ correlations
)
mockery::expect_args(
- events$pev_epi_doses[[2]]$schedule,
+ variables$last_pev_timestep$queue_update_mock(),
1,
- c(1, 2),
- parameters$pev_doses[[2]]
+ timestep,
+ c(1, 2)
)
+})
+
+test_that('EPI ignores individuals scheduled for mass vaccination', {
+ timestep <- 100
+ parameters <- get_parameters(list(human_population = 5))
+ parameters <- set_mass_pev(
+ parameters,
+ profile = rtss_profile,
+ timesteps = c(50, 100),
+ coverages = rep(0.8, 2),
+ min_wait = 0,
+ min_ages = c(1, 2, 3, 18) * 365,
+ max_ages = (c(1, 2, 3, 18) + 1) * 365 - 1,
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8, .9, .8), nrow=2, ncol=2),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile)
+ )
+ parameters <- set_pev_epi(
+ parameters,
+ profile = rtss_profile,
+ timesteps = 10,
+ coverages = 0.8,
+ min_wait = 0,
+ age = 18 * 365,
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile)
+ )
+ events <- create_events(parameters)
+ variables <- create_variables(parameters)
+ variables$birth <- individual::IntegerVariable$new(
+ -c(18, 8, 2.9, 3.2, 18) * 365 + 100
+ )
+ variables$pev_timestep <- mock_integer(
+ c(-1, -1, -1, 50, 50)
+ )
+
+ correlations <- get_correlation_parameters(parameters)
+
+ process <- create_epi_pev_process(
+ variables,
+ events,
+ parameters,
+ correlations,
+ parameters$pev_epi_coverages,
+ parameters$pev_epi_timesteps
+ )
+
+ sample_mock <- mockery::mock(c(TRUE))
+
+ mockery::stub(
+ process,
+ 'sample_intervention',
+ sample_mock
+ )
+
+ # schedule id #1 for epi vaccination
+ events$mass_pev_doses[[1]]$schedule(1, 0)
+
+ process(timestep)
+
mockery::expect_args(
- events$pev_epi_doses[[3]]$schedule,
+ sample_mock,
1,
- c(1, 2),
- parameters$pev_doses[[3]]
+ 5,
+ 'pev',
+ .8,
+ correlations
)
})
+
test_that('pev EPI respects min_wait when scheduling seasonal boosters', {
timestep <- 5 * 365
parameters <- get_parameters(list(human_population = 5))
@@ -141,8 +233,8 @@ test_that('pev EPI respects min_wait when scheduling seasonal boosters', {
coverages = 0.8,
min_wait = 6 * 30,
age = 18 * 365,
- booster_timestep = c(3, 12) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(3, 12) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile),
seasonal_boosters = TRUE
)
@@ -177,8 +269,8 @@ test_that('pev EPI schedules for the following year with seasonal boosters', {
coverages = 0.8,
min_wait = 6 * 30,
age = 18 * 365,
- booster_timestep = c(3, 12) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(3, 12) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile),
seasonal_boosters = TRUE
)
diff --git a/tests/testthat/test-pev.R b/tests/testthat/test-pev.R
index 5cb09c5d..09c4cf7d 100644
--- a/tests/testthat/test-pev.R
+++ b/tests/testthat/test-pev.R
@@ -8,8 +8,8 @@ test_that('Mass vaccination strategy parameterisation works', {
min_wait = 0,
min_ages = 5 * 30,
max_ages = 17 * 30,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
)
expect_equal(parameters$pev, TRUE)
@@ -17,7 +17,7 @@ test_that('Mass vaccination strategy parameterisation works', {
expect_equal(parameters$mass_pev_coverages, .8)
expect_equal(parameters$mass_pev_min_ages, 5 * 30)
expect_equal(parameters$mass_pev_max_ages, 17 * 30)
- expect_equal(parameters$mass_pev_booster_timestep, c(18, 36) * 30)
+ expect_equal(parameters$mass_pev_booster_spacing, c(18, 36) * 30)
expect_equal(parameters$pev_profiles, list(rtss_profile, rtss_booster_profile, rtss_booster_profile))
expect_equal(parameters$mass_pev_profile_indices, seq(3))
@@ -30,8 +30,8 @@ test_that('Mass vaccination strategy parameterisation works', {
min_wait = 0,
min_ages = 5 * 30,
max_ages = 17 * 30,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
), "all(coverages >= 0) && all(coverages <= 1) is not TRUE",
fixed = TRUE
@@ -46,14 +46,54 @@ test_that('Mass vaccination strategy parameterisation works', {
min_wait = 0,
min_ages = 5 * 30,
max_ages = 17 * 30,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
), "all(coverages >= 0) && all(coverages <= 1) is not TRUE",
fixed = TRUE
)
})
+test_that('set_mass_pev checks booster coverage matrix shape', {
+ parameters <- get_parameters()
+ expect_error(
+ parameters <- set_mass_pev(
+ parameters,
+ profile = rtss_profile,
+ coverages = c(0.1),
+ timesteps = c(10),
+ min_wait = 0,
+ min_ages = 5 * 30,
+ max_ages = 17 * 30,
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=2, ncol=1),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile)
+ ),
+ 'booster_spacing, booster_coverage and booster_profile do not align',
+ fixed = TRUE
+ )
+})
+
+test_that('set_mass_pev checks booster_spacing is increasing', {
+ parameters <- get_parameters()
+ expect_error(
+ parameters <- set_mass_pev(
+ parameters,
+ profile = rtss_profile,
+ coverages = c(0.1),
+ timesteps = c(10),
+ min_wait = 0,
+ min_ages = 5 * 30,
+ max_ages = 17 * 30,
+ booster_spacing = c(5, 5) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=2, ncol=1),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile)
+ ),
+ 'booster_spacing must be monotonically increasing',
+ fixed = TRUE
+ )
+})
+
test_that('Mass vaccination fails pre-emptively for unaligned booster parameters', {
parameters <- get_parameters()
expect_error(
@@ -65,8 +105,8 @@ test_that('Mass vaccination fails pre-emptively for unaligned booster parameters
min_wait = 0,
min_ages = 5 * 30,
max_ages = 17 * 30,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(.9, nrow=1, ncol=1),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
)
)
@@ -83,8 +123,8 @@ test_that('Infection considers pev efficacy', {
min_wait = 0,
min_ages = 5 * 30,
max_ages = 17 * 30,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
)
events <- create_events(parameters)
@@ -97,7 +137,7 @@ test_that('Infection considers pev efficacy', {
variables$birth <- individual::IntegerVariable$new(
-c(8, 2.9, 3.2, 18.4) * 365 - 100
)
- variables$pev_timestep <- individual::IntegerVariable$new(
+ variables$last_eff_pev_timestep <- individual::IntegerVariable$new(
c(-1, -1, 50, 50 + 30)
)
variables$pev_profile <- individual::IntegerVariable$new(
@@ -150,8 +190,8 @@ test_that('Mass vaccinations update vaccination time', {
min_wait = 0,
min_ages = c(1, 2, 3, 18) * 365,
max_ages = (c(1, 2, 3, 18) + 1) * 365 - 1,
- booster_timestep = c(18, 36) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8, .9, .8), nrow=2, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
)
events <- create_events(parameters)
@@ -163,7 +203,6 @@ test_that('Mass vaccinations update vaccination time', {
c(-1, -1, -1, 50, 50)
)
- events$mass_pev <- mock_event(events$mass_pev)
events$mass_pev_doses <- lapply(events$mass_pev_doses, mock_event)
listener <- create_mass_pev_listener(
@@ -201,14 +240,76 @@ test_that('Mass vaccinations update vaccination time', {
c(1, 3),
parameters$pev_doses[[3]]
)
+})
+
+test_that('Mass vaccinations ignore EPI individuals', {
+ timestep <- 100
+ parameters <- get_parameters(list(human_population = 5))
+ parameters <- set_mass_pev(
+ parameters,
+ profile = rtss_profile,
+ timesteps = c(50, 100),
+ coverages = rep(0.8, 2),
+ min_wait = 0,
+ min_ages = c(1, 2, 3, 18) * 365,
+ max_ages = (c(1, 2, 3, 18) + 1) * 365 - 1,
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8, .9, .8), nrow=2, ncol=2),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile)
+ )
+ parameters <- set_pev_epi(
+ parameters,
+ profile = rtss_profile,
+ timesteps = 10,
+ coverages = 0.8,
+ min_wait = 2*365,
+ age = 18 * 365,
+ booster_spacing = c(18, 36) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile)
+ )
+ events <- create_events(parameters)
+ variables <- create_variables(parameters)
+ variables$birth <- individual::IntegerVariable$new(
+ -c(18.3, 8, 2.9, 3.2, 18.4) * 365 + 100
+ )
+ variables$pev_timestep <- mock_integer(
+ c(-1, -1, -1, 50, 50)
+ )
+
+ correlations <- get_correlation_parameters(parameters)
+
+ listener <- create_mass_pev_listener(
+ variables,
+ events,
+ parameters,
+ correlations
+ )
+
+ sample_mock <- mockery::mock(c(TRUE, TRUE, FALSE, FALSE))
+
+ mockery::stub(
+ listener,
+ 'sample_intervention',
+ sample_mock
+ )
+
+ # schedule id #1 for epi vaccination
+ events$pev_epi_doses[[1]]$schedule(1, 0)
+
+ listener(timestep)
mockery::expect_args(
- events$mass_pev$schedule,
+ sample_mock,
1,
- 365
+ c(3, 4, 5),
+ 'pev',
+ .8,
+ correlations
)
})
+
test_that('Mass boosters update profile params and reschedule correctly', {
parameters <- get_parameters()
parameters <- set_mass_pev(
@@ -219,8 +320,8 @@ test_that('Mass boosters update profile params and reschedule correctly', {
min_wait = 0,
min_ages = c(1, 2, 3, 18) * 365,
max_ages = (c(1, 2, 3, 18) + 1) * 365 - 1,
- booster_timestep = c(1, 6) * 30,
- booster_coverage = c(1, 1),
+ booster_spacing = c(1, 6) * 30,
+ booster_coverage = matrix(1, nrow=2, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
)
events <- create_events(parameters)
@@ -231,7 +332,10 @@ test_that('Mass boosters update profile params and reschedule correctly', {
variables$birth <- individual::IntegerVariable$new(
-c(2.9, 3.2, 18.4) * 365 + 100
)
- variables$pev_timestep <- mock_double(
+ variables$last_pev_timestep <- mock_double(
+ c(50, 50, 50)
+ )
+ variables$last_eff_pev_timestep <- mock_double(
c(50, 50, 50)
)
variables$pev_profile <- mock_integer(
@@ -241,7 +345,8 @@ test_that('Mass boosters update profile params and reschedule correctly', {
listener <- create_pev_booster_listener(
variables = variables,
- coverage = 1,
+ coverage = parameters$mass_pev_booster_coverage,
+ parameters$mass_pev_timesteps,
booster_number = 1,
pev_profile_index = 2,
next_booster_event = events$mass_pev_boosters[[2]],
@@ -258,7 +363,13 @@ test_that('Mass boosters update profile params and reschedule correctly', {
listener(timestep, individual::Bitset$new(3)$insert(c(1, 2, 3)))
expect_bitset_update(
- variables$pev_timestep$queue_update_mock(),
+ variables$last_pev_timestep$queue_update_mock(),
+ timestep,
+ c(1, 2, 3)
+ )
+
+ expect_bitset_update(
+ variables$last_eff_pev_timestep$queue_update_mock(),
timestep,
c(1, 2, 3)
)
@@ -286,8 +397,8 @@ test_that('Mass booster coverages sample subpopulations correctly', {
min_ages = c(1, 2, 3, 18) * 365,
max_ages = (c(1, 2, 3, 18) + 1) * 365 - 1,
min_wait = 0,
- booster_timestep = c(1, 6) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(1, 6) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
)
@@ -300,7 +411,10 @@ test_that('Mass booster coverages sample subpopulations correctly', {
variables$birth <- individual::IntegerVariable$new(
-c(2.9, 3.2, 18.4) * 365 + 100
)
- variables$pev_timestep <- mock_double(
+ variables$last_pev_timestep <- mock_double(
+ c(50, 50, 50)
+ )
+ variables$last_eff_pev_timestep <- mock_double(
c(50, 50, 50)
)
variables$pev_profile <- mock_integer(
@@ -309,7 +423,8 @@ test_that('Mass booster coverages sample subpopulations correctly', {
listener <- create_pev_booster_listener(
variables = variables,
- coverage = .9,
+ coverage = parameters$mass_pev_booster_coverage,
+ pev_distribution_timesteps = parameters$mass_pev_timesteps,
booster_number = 1,
pev_profile_index = 2,
next_booster_event = events$mass_pev_boosters[[2]],
@@ -327,7 +442,13 @@ test_that('Mass booster coverages sample subpopulations correctly', {
mockery::expect_args(sample_mock, 1, target, .9)
expect_bitset_update(
- variables$pev_timestep$queue_update_mock(),
+ variables$last_pev_timestep$queue_update_mock(),
+ timestep,
+ c(2, 3)
+ )
+
+ expect_bitset_update(
+ variables$last_eff_pev_timestep$queue_update_mock(),
timestep,
c(2, 3)
)
@@ -345,6 +466,68 @@ test_that('Mass booster coverages sample subpopulations correctly', {
)
})
+test_that('mass pev targets correct age and respects min_wait', {
+ timestep <- 5*365
+ parameters <- get_parameters(list(human_population = 5))
+ parameters <- set_mass_pev(
+ parameters,
+ profile = rtss_profile,
+ timesteps = c(4, 5) * 365,
+ coverages = c(0.8, 0.8),
+ min_ages = 0,
+ max_ages = 19 * 365,
+ min_wait = 2*365,
+ booster_spacing = c(1, 6) * 30,
+ booster_coverage = matrix(c(.9, .8, .9, .8), nrow=2, ncol=2),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile)
+ )
+ events <- create_events(parameters)
+ variables <- create_variables(parameters)
+ variables$birth <- individual::IntegerVariable$new(
+ -c(18, 18, 30, 18, 18) * 365 + timestep
+ )
+ variables$last_pev_timestep <- mock_integer(
+ c(50, -1, -1, 4*365, -1)
+ )
+
+ variables$pev_profile <- mock_integer(
+ c(1, -1, -1, 1, -1)
+ )
+
+ correlations <- get_correlation_parameters(parameters)
+ listener <- create_mass_pev_listener(
+ variables,
+ events,
+ parameters,
+ get_correlation_parameters(parameters)
+ )
+
+ sample_mock <- mockery::mock(c(TRUE, TRUE, FALSE))
+ mockery::stub(
+ listener,
+ 'sample_intervention',
+ sample_mock
+ )
+
+ listener(timestep)
+
+ mockery::expect_args(
+ sample_mock,
+ 1,
+ c(1, 2, 5),
+ 'pev',
+ .8,
+ correlations
+ )
+
+ mockery::expect_args(
+ variables$last_pev_timestep$queue_update_mock(),
+ 1,
+ timestep,
+ c(1, 2)
+ )
+})
+
test_that('Mass efficacy listener works correctly', {
timestep <- 50
parameters <- get_parameters()
@@ -356,13 +539,13 @@ test_that('Mass efficacy listener works correctly', {
min_ages = c(1, 2, 3, 18) * 365,
max_ages = (c(1, 2, 3, 18) + 1) * 365 - 1,
min_wait = 0,
- booster_timestep = c(1, 6) * 30,
- booster_coverage = c(.9, .8),
+ booster_spacing = c(1, 6) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
booster_profile = list(rtss_booster_profile, rtss_booster_profile)
)
variables <- create_variables(parameters)
- variables$pev_timestep <- mock_integer(c(-1, -1, -1))
+ variables$last_eff_pev_timestep <- mock_integer(c(-1, -1, -1))
variables$pev_profile <- mock_integer(c(-1, -1, -1))
listener <- create_pev_efficacy_listener(variables, 1)
@@ -370,7 +553,7 @@ test_that('Mass efficacy listener works correctly', {
# vaccinated time
expect_bitset_update(
- variables$pev_timestep$queue_update_mock(),
+ variables$last_eff_pev_timestep$queue_update_mock(),
timestep,
c(1, 2, 3)
)
@@ -394,8 +577,8 @@ test_that('Mass dose events are not ruined by lazy evaluation', {
min_ages = c(1, 2, 3, 18) * 365,
max_ages = (c(1, 2, 3, 18) + 1) * 365 - 1,
min_wait = 0,
- booster_timestep = c(1, 6, 12) * 30,
- booster_coverage = c(.9, .8, .7),
+ booster_spacing = c(1, 6, 12) * 30,
+ booster_coverage = matrix(c(.9, .8, .7), nrow=1, ncol=3),
booster_profile = list(rtss_booster_profile, rtss_booster_profile, rtss_booster_profile)
)
@@ -405,9 +588,10 @@ test_that('Mass dose events are not ruined by lazy evaluation', {
attach_pev_dose_listeners(
variables = variables,
parameters = parameters,
+ pev_distribution_timesteps = parameters$mass_pev_timesteps,
dose_events = events$mass_pev_doses,
booster_events = events$mass_pev_boosters,
- booster_delays = parameters$mass_pev_booster_timestep,
+ booster_delays = parameters$mass_pev_booster_spacing,
booster_coverages = parameters$mass_pev_booster_coverage,
pev_profile_indices = parameters$mass_pev_profile_indices,
strategy = 'mass',
@@ -429,8 +613,8 @@ test_that('Mass dose events are not ruined by lazy evaluation', {
expect_equal(
as.list(environment(
events$mass_pev_boosters[[1]]$.listeners[[1]]
- ))$coverage,
- .9
+ ))$booster_number,
+ 1
)
})
@@ -464,4 +648,101 @@ test_that('Efficacies are calculated correctly', {
)
})
+test_that('pev timed booster coverage can select the first coverage for the first booster', {
+ timestep <- 5 * 365
+ parameters <- get_parameters(list(human_population = 5))
+ parameters <- set_pev_epi(
+ parameters,
+ profile = rtss_profile,
+ timesteps = 10,
+ coverages = 0.8,
+ min_wait = 6 * 30,
+ age = 18 * 365,
+ booster_spacing = c(3, 12) * 30,
+ booster_coverage = matrix(c(.9, .8), nrow=1, ncol=2),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile),
+ )
+ events <- create_events(parameters)
+
+ booster_event <- mock_event(events$pev_epi_boosters[[1]])
+
+ listener <- create_pev_booster_listener(
+ variables = create_variables(parameters),
+ coverage = parameters$pev_epi_booster_coverage,
+ pev_distribution_timesteps = parameters$pev_epi_timesteps,
+ booster_number = 1,
+ pev_profile_index = 2,
+ next_booster_event = booster_event,
+ next_booster_delay = 9 * 30,
+ renderer = mock_render(timestep),
+ strategy = 'epi'
+ )
+
+ target <- individual::Bitset$new(5)$insert(seq(5))
+
+ mock_sample_bitset = mockery::mock(individual::Bitset$new(5)$insert(c(1, 2)))
+ mockery::stub(
+ listener,
+ 'sample_bitset',
+ mock_sample_bitset
+ )
+
+ listener(timestep, target)
+
+ mockery::expect_args(
+ mock_sample_bitset,
+ 1,
+ target,
+ .9
+ )
+})
+
+test_that('pev boosters can select the second coverage for the first booster', {
+ timestep <- 5 * 365
+ parameters <- get_parameters(list(human_population = 5))
+ parameters <- set_pev_epi(
+ parameters,
+ profile = rtss_profile,
+ timesteps = c(10, 30),
+ coverages = c(0.8, 0.4),
+ min_wait = 6 * 30,
+ age = 18 * 365,
+ booster_spacing = c(3, 12) * 30,
+ booster_coverage = matrix(c(.9, .45, .8, .8), nrow=2, ncol=2),
+ booster_profile = list(rtss_booster_profile, rtss_booster_profile),
+ )
+ events <- create_events(parameters)
+
+ booster_event <- mock_event(events$pev_epi_boosters[[1]])
+
+ listener <- create_pev_booster_listener(
+ variables = create_variables(parameters),
+ coverage = parameters$pev_epi_booster_coverage,
+ pev_distribution_timesteps = parameters$pev_epi_timesteps,
+ booster_number = 1,
+ pev_profile_index = 2,
+ next_booster_event = booster_event,
+ next_booster_delay = 9 * 30,
+ renderer = mock_render(timestep),
+ strategy = 'epi'
+ )
+
+ target <- individual::Bitset$new(5)$insert(seq(5))
+
+ mock_sample_bitset = mockery::mock(individual::Bitset$new(5)$insert(c(1, 2)))
+ mockery::stub(
+ listener,
+ 'sample_bitset',
+ mock_sample_bitset
+ )
+
+ listener(timestep, target)
+
+ mockery::expect_args(
+ mock_sample_bitset,
+ 1,
+ target,
+ .45
+ )
+})
diff --git a/tests/testthat/test-process-integration.R b/tests/testthat/test-process-integration.R
index f259be5e..1b07f127 100644
--- a/tests/testthat/test-process-integration.R
+++ b/tests/testthat/test-process-integration.R
@@ -19,6 +19,28 @@ test_that('create_processes makes valid process functions', {
}
})
+test_that('create_processes makes valid process functions when progress bar specified', {
+ parameters <- get_parameters()
+ parameters$progress_bar <- TRUE
+ events <- create_events(parameters)
+ variables <- create_variables(parameters)
+ vector_models <- parameterise_mosquito_models(parameters, 1)
+ solvers <- parameterise_solvers(vector_models, parameters)
+ renderer <- individual::Render$new(1)
+ processes <- create_processes(
+ renderer,
+ variables,
+ events,
+ parameters,
+ vector_models,
+ solvers,
+ timesteps = 100
+ )
+ for (process in processes) {
+ expect(is.function(process) || inherits(process, 'externalptr'), 'Process is not a function')
+ }
+})
+
test_that('attach_event_listeners makes valid listeners', {
parameters <- get_parameters()
events <- create_events(parameters)
diff --git a/tests/testthat/test-processes.R b/tests/testthat/test-processes.R
new file mode 100644
index 00000000..29f0d6b0
--- /dev/null
+++ b/tests/testthat/test-processes.R
@@ -0,0 +1,23 @@
+test_that("exponential_decay_process works as expected", {
+ # This rate gives a halving at every timestep
+ rate <- -1 / log(0.5)
+
+ v <- individual::DoubleVariable$new(c(0,0.5,1,2,4,10))
+ p <- create_exponential_decay_process(v, rate)
+
+ individual:::execute_any_process(p, 1)
+ v$.update()
+
+ expect_equal(v$get_values(), c(0, 0.25, 0.5, 1, 2, 5))
+
+ individual:::execute_any_process(p, 2)
+ v$.update()
+
+ expect_equal(v$get_values(), c(0, 0.125, 0.25, 0.5, 1, 2.5))
+})
+
+test_that("exponential_decay_process fails on IntegerVariable", {
+ rate <- -1 / log(0.5)
+ v <- individual::IntegerVariable$new(c(0,1,2,3))
+ expect_error(create_exponential_decay_process(v, rate))
+})
diff --git a/tests/testthat/test-resume.R b/tests/testthat/test-resume.R
new file mode 100644
index 00000000..1fb7ff87
--- /dev/null
+++ b/tests/testthat/test-resume.R
@@ -0,0 +1,303 @@
+#' Test simulation saving and restoring for a given parameter set.
+#'
+#' This function runs the simulation twice. A first, continuous and uninterrupted
+#' run of the simulation is used as a reference. The second run is split into
+#' two phases. Between the two phases, the simulation state is saved and
+#' restored. Optionally, the initial warmup phase can use a different set of
+#' parameters, by specifying a value for warmup_parameters.
+test_resume <- function(
+ parameters,
+ timesteps = 200,
+ warmup_parameters = parameters,
+ warmup_timesteps = 50
+ ) {
+
+ # This function is only used with null correlations. However a null
+ # correlation involves sampling random numbers during initialization, which
+ # disrupts the global RNG and affects the reproducibility if the size of the
+ # matrix is not always the same.
+ #
+ # We use a single correlation object, that we initialize eagerly, such that
+ # the simulation can run undisturbed.
+ correlations <- get_correlation_parameters(parameters)
+ correlations$mvnorm()
+
+ set.seed(123)
+ uninterrupted_run <- run_simulation(
+ timesteps,
+ parameters = parameters,
+ correlations = correlations)
+
+ set.seed(123)
+ first_phase <- run_resumable_simulation(
+ warmup_timesteps,
+ warmup_parameters,
+ correlations = correlations)
+ second_phase <- run_resumable_simulation(
+ timesteps,
+ parameters,
+ initial_state = first_phase$state,
+ restore_random_state = TRUE)
+
+ expect_equal(nrow(first_phase$data), warmup_timesteps)
+ expect_equal(nrow(second_phase$data), timesteps - warmup_timesteps)
+
+ # The order of columns isn't always identical, hence why mapequal needs to be
+ # used.
+ expect_mapequal(
+ second_phase$data,
+ uninterrupted_run[-(1:warmup_timesteps),])
+
+ invisible(second_phase$data)
+}
+
+test_that('Simulation can be resumed', {
+ test_resume(get_parameters(overrides=list(
+ individual_mosquitoes = FALSE,
+ model_seasonality = TRUE
+ )))
+ test_resume(get_parameters(overrides=list(
+ individual_mosquitoes = TRUE,
+ model_seasonality = TRUE
+ )))
+ test_resume(get_parameters(overrides=list(
+ individual_mosquitoes = FALSE,
+ model_seasonality = TRUE
+ )))
+ test_resume(get_parameters(overrides=list(
+ individual_mosquitoes = TRUE,
+ model_seasonality = TRUE
+ )))
+})
+
+test_that('PEV intervention can be added when resuming', {
+ set_default_mass_pev <- function(parameters, timesteps) {
+ n <- length(timesteps)
+ set_mass_pev(
+ parameters,
+ profile = rtss_profile,
+ timesteps = timesteps,
+ coverages = rep(0.5, n),
+ min_wait = 5,
+ min_ages = 365*10,
+ max_ages = 365*60,
+ booster_spacing = NULL,
+ booster_coverage = NULL,
+ booster_profile = NULL)
+ }
+ base <- get_parameters(overrides=list(pev_doses=c(0, 45, 90)))
+
+ data <- test_resume(
+ warmup_parameters = base,
+ parameters = base %>% set_default_mass_pev(100))
+ expect_equal(data[data$n_pev_mass_dose_1 > 0, "timestep"], 100)
+ expect_equal(data[data$n_pev_mass_dose_2 > 0, "timestep"], 145)
+ expect_equal(data[data$n_pev_mass_dose_3 > 0, "timestep"], 190)
+
+ # Add a second mass PEV intervention when resuming the simulation.
+ data <- test_resume(
+ warmup_parameters = base %>% set_default_mass_pev(25),
+ parameters = base %>% set_default_mass_pev(c(25, 100)))
+
+ # The first dose, at time step 25, happens during the warmup and is not
+ # returned by test_resume, hence why we don't see it in the data. Follow-up
+ # doses do show up, even though they we scheduled during warmup.
+ expect_equal(data[data$n_pev_mass_dose_1 > 0, "timestep"], c(100))
+ expect_equal(data[data$n_pev_mass_dose_2 > 0, "timestep"], c(70, 145))
+ expect_equal(data[data$n_pev_mass_dose_3 > 0, "timestep"], c(115, 190))
+})
+
+test_that("TBV intervention can be added when resuming", {
+ set_default_tbv <- function(parameters, timesteps) {
+ set_tbv(
+ parameters,
+ timesteps=timesteps,
+ coverage=rep(1, length(timesteps)),
+ ages=5:60)
+ }
+
+ base <- get_parameters()
+
+ data <- test_resume(
+ warmup_parameters = base,
+ parameters = base %>% set_default_tbv(100))
+ expect_equal(data[!is.na(data$n_vaccinated_tbv), "timestep"], 100)
+
+ data <- test_resume(
+ warmup_parameters = base %>% set_default_tbv(25),
+ parameters = base %>% set_default_tbv(c(25, 100)))
+ expect_equal(data[!is.na(data$n_vaccinated_tbv), "timestep"], 100)
+})
+
+test_that("MDA intervention can be added when resuming", {
+ set_default_mda <- function(parameters, timesteps) {
+ parameters %>% set_drugs(list(SP_AQ_params)) %>% set_mda(
+ drug = 1,
+ timesteps = timesteps,
+ coverages = rep(0.8, length(timesteps)),
+ min_ages = rep(0, length(timesteps)),
+ max_ages = rep(60*365, length(timesteps)))
+ }
+
+ base <- get_parameters()
+
+ data <- test_resume(
+ warmup_parameters = base,
+ parameters = base %>% set_default_mda(100))
+ expect_equal(data[data$n_mda_treated > 0, "timestep"], 100)
+
+ data <- test_resume(
+ warmup_parameters = base %>% set_default_mda(25),
+ parameters = base %>% set_default_mda(c(25, 100)))
+ expect_equal(data[data$n_mda_treated > 0, "timestep"], 100)
+})
+
+test_that("Bednets intervention can be added when resuming", {
+ set_default_bednets <- function(parameters, timesteps) {
+ n <- length(timesteps)
+ set_bednets(
+ parameters,
+ timesteps = timesteps,
+ coverages = rep(0.5, n),
+ retention = 25,
+ dn0 = matrix(rep(0.533, n), ncol=1),
+ rn = matrix(rep(0.56, n), ncol=1),
+ rnm = matrix(rep(0.24, n), ncol=1),
+ gamman = rep(2.64 * 365, n))
+ }
+
+ base <- get_parameters()
+
+ data <- test_resume(
+ warmup_parameters = base,
+ parameters = base %>% set_default_bednets(100))
+ expect_equal(data[diff(data$n_use_net) > 0, "timestep"], 100)
+
+ data <- test_resume(
+ warmup_parameters = base %>% set_default_bednets(25),
+ parameters = base %>% set_default_bednets(c(25, 100)))
+ expect_equal(data[diff(data$n_use_net) > 0, "timestep"], 100)
+})
+
+test_that("Correlations can be set when resuming with new interventions", {
+ set.seed(123)
+
+ # When adding a new intervention with a non-zero correlation, we cannot
+ # ensure that an uninterrupted run matches the stopped-and-resumed simulation
+ # exactly, as the correlation matrix ends up being randomly sampled in a
+ # different order. This stops us from using the `test_resume` used throughout
+ # the rest of this file. Instead we'll only do stopped-and-resumed simulations
+ #' and check its behaviour.
+ #
+ # We first do a warmup phase with only TBV enabled. We then resume that
+ # simulation three times, each time with MDA enabled. Each time we resume the
+ # simulation, we set a different correlation parameter between the TBV and
+ # MDA interventions, with values -1, 0 and 1.
+ #
+ # We look at the output data and confirm that the correlation worked as
+ # expected. For this we need not only how many people got each intervention,
+ # but also how many received both and how many received at least one. This is
+ # not normally exposed, so we add an extra process to render these values.
+ #
+ # For simplicity, for each intervention, we remove any selection process other
+ # than overall coverage, such as age range (set to 0-200years) and drug
+ # efficacy (set to 100%).
+ #
+ # We need a large population to make the statistical assertions succeed. We'll
+ # only simulate 3 timesteps to keep execution time down: one timestep for
+ # warmup during which TBV takes place, one in which MDA takes place and one
+ # final timestep to collect the updated variables.
+ population <- 10000
+ tbv_coverage <- 0.2
+ mda_coverage <- 0.4
+
+ warmup_parameters <- get_parameters(overrides=list(human_population=population)) %>%
+ set_tbv(
+ timesteps=1,
+ coverage=tbv_coverage,
+ ages=0:200)
+
+ drug <- SP_AQ_params
+ drug[1] <- 1. # Override the drug efficacy to 100%
+ parameters <- warmup_parameters %>%
+ set_drugs(list(drug)) %>%
+ set_mda(
+ drug = 1,
+ timesteps = 2,
+ coverages = mda_coverage,
+ min_ages = 0,
+ max_ages = 200*365)
+
+ create_processes_stub <- function(renderer, variables, events, parameters, ...) {
+ p <- function(t) {
+ pop <- parameters$human_population
+ tbv <- variables$tbv_vaccinated$get_index_of(a=-1, b=0)$not()
+ mda <- variables$drug_time$get_index_of(-1)$not()
+
+ renderer$render("total_tbv", tbv$size(), t)
+ renderer$render("total_mda", mda$size(), t)
+ renderer$render("total_tbv_and_mda", tbv$copy()$and(mda)$size(), t)
+ renderer$render("total_tbv_or_mda", tbv$copy()$or(mda)$size(), t)
+ }
+ c(create_processes(renderer, variables, events, parameters, ...), p)
+ }
+
+ mockery::stub(run_resumable_simulation, 'create_processes', create_processes_stub)
+
+ warmup_correlations <- get_correlation_parameters(warmup_parameters)
+ warmup_correlations$inter_round_rho('tbv', 1)
+
+ warmup <- run_resumable_simulation(1,
+ parameters=warmup_parameters,
+ correlations=warmup_correlations)
+
+ zero_correlation <- get_correlation_parameters(parameters)
+ zero_correlation$inter_round_rho('tbv', 1)
+ zero_correlation$inter_round_rho('mda', 1)
+
+ positive_correlation <- get_correlation_parameters(parameters)
+ positive_correlation$inter_round_rho('tbv', 1)
+ positive_correlation$inter_round_rho('mda', 1)
+ positive_correlation$inter_intervention_rho('tbv', 'mda', 1)
+
+ negative_correlation <- get_correlation_parameters(parameters)
+ negative_correlation$inter_round_rho('tbv', 1)
+ negative_correlation$inter_round_rho('mda', 1)
+ negative_correlation$inter_intervention_rho('tbv', 'mda', -1)
+
+ data <- run_resumable_simulation(
+ 3,
+ initial_state=warmup$state,
+ parameters=parameters,
+ correlations=zero_correlation)$data %>% tail(1)
+ expect_equal(data$total_tbv, population * tbv_coverage, tolerance = 0.1)
+ expect_equal(data$total_mda, population * mda_coverage, tolerance = 0.1)
+ expect_equal(
+ data$total_tbv_and_mda,
+ population * (tbv_coverage * mda_coverage),
+ tolerance = 0.1)
+ expect_equal(
+ data$total_tbv_or_mda,
+ population * (tbv_coverage + mda_coverage - tbv_coverage * mda_coverage),
+ tolerance = 0.1)
+
+ data <- run_resumable_simulation(
+ 3,
+ initial_state=warmup$state,
+ parameters=parameters,
+ correlations=positive_correlation)$data %>% tail(1)
+ expect_equal(data$total_tbv, population * tbv_coverage, tolerance = 0.1)
+ expect_equal(data$total_mda, population * mda_coverage, tolerance = 0.1)
+ expect_equal(data$total_tbv_and_mda, min(data$total_tbv, data$total_mda))
+ expect_equal(data$total_tbv_or_mda, max(data$total_tbv, data$total_mda))
+
+ data <- run_resumable_simulation(
+ 3,
+ initial_state=warmup$state,
+ parameters=parameters,
+ correlations=negative_correlation)$data %>% tail(1)
+ expect_equal(data$total_tbv, population * tbv_coverage, tolerance = 0.1)
+ expect_equal(data$total_mda, population * mda_coverage, tolerance = 0.1)
+ expect_equal(data$total_tbv_and_mda, 0)
+ expect_equal(data$total_tbv_or_mda, data$total_tbv + data$total_mda)
+})
diff --git a/tests/testthat/test-seasonality.R b/tests/testthat/test-seasonality.R
index 01302d8d..f600ebf4 100644
--- a/tests/testthat/test-seasonality.R
+++ b/tests/testthat/test-seasonality.R
@@ -15,14 +15,14 @@ test_that('Seasonality correctly affects P', {
counts <- c()
for (t in seq(timesteps)) {
- counts <- rbind(counts, c(t, solver_get_states(solvers[[1]])))
+ counts <- rbind(counts, c(t, solvers[[1]]$get_states()))
aquatic_mosquito_model_update(
- models[[1]],
+ models[[1]]$.model,
total_M,
parameters$blood_meal_rates,
parameters$mum
)
- solver_step(solvers[[1]])
+ solvers[[1]]$step()
}
burn_in <- 20
diff --git a/tests/testthat/test-tbv.R b/tests/testthat/test-tbv.R
index 0e4451a1..e8af946e 100644
--- a/tests/testthat/test-tbv.R
+++ b/tests/testthat/test-tbv.R
@@ -1,3 +1,14 @@
+test_that('TBV checks arguments', {
+ parameters <- get_parameters()
+ expect_error(
+ parameters <- set_tbv(
+ parameters,
+ timesteps = c(50, 150),
+ coverages = 1,
+ ages = 5:60
+ ), "coverages and timesteps do no align")
+})
+
test_that('TBV strategy parameterisation works', {
parameters <- get_parameters()
parameters <- set_tbv(
diff --git a/tests/testthat/test-vector-control.R b/tests/testthat/test-vector-control.R
index 34f9d6ef..8c2e2d49 100644
--- a/tests/testthat/test-vector-control.R
+++ b/tests/testthat/test-vector-control.R
@@ -372,18 +372,18 @@ test_that('set_carrying_capacity works',{
species = "gamb",
carrying_capacity = TRUE,
carrying_capacity_timesteps = 1,
- carrying_capacity_values = matrix(0.1)
+ carrying_capacity_scalers = matrix(0.1)
)
)
expect_error(
set_carrying_capacity(p, 1, matrix(c(0.1, 0.1), nrow = 2)),
- "nrow(carrying_capacity) == length(timesteps) is not TRUE",
+ "nrow(carrying_capacity_scalers) == length(timesteps) is not TRUE",
fixed = TRUE
)
expect_error(
set_carrying_capacity(p, 1, matrix(c(0.1, 0.1), ncol = 2)),
- "ncol(carrying_capacity) == length(parameters$species) is not TRUE",
+ "ncol(carrying_capacity_scalers) == length(parameters$species) is not TRUE",
fixed = TRUE
)
expect_error(
@@ -393,7 +393,7 @@ test_that('set_carrying_capacity works',{
)
expect_error(
set_carrying_capacity(p, 1, matrix(-1)),
- "min(carrying_capacity) >= 0",
+ "min(carrying_capacity_scalers) >= 0",
fixed = TRUE
)
})
diff --git a/vignettes/Antimalarial_Resistance.Rmd b/vignettes/Antimalarial_Resistance.Rmd
new file mode 100644
index 00000000..5b6acd40
--- /dev/null
+++ b/vignettes/Antimalarial_Resistance.Rmd
@@ -0,0 +1,410 @@
+---
+title: "Antimalarial Resistance"
+output: rmarkdown::html_vignette
+vignette: >
+ %\VignetteIndexEntry{Antimalarial Resistance}
+ %\VignetteEngine{knitr::rmarkdown}
+ %\VignetteEncoding{UTF-8}
+---
+
+```{r, include = FALSE}
+knitr::opts_chunk$set(
+ collapse = TRUE,
+ comment = "#>"
+)
+```
+
+```{r setup}
+
+# Load the requisite packages:
+library(malariasimulation)
+
+# Set colour palette:
+cols <- c("#E69F00", "#56B4E9", "#009E73", "#CC79A7","#F0E442", "#0072B2", "#D55E00")
+
+```
+
+## Introduction
+One of the major threats to the continued success of efforts to reduce the burden of malaria is the evolution and spread of resistance to the antimalarial drugs used to treat uncomplicated cases of malaria. The most effective frontline antimalarials are a class of drugs called artemisinin combination therapies (ACTs). ACTs combine a fast-acting, short-lived artemisinin derivative (e.g. artemether), with a slower-acting, longer-lasting partner drug (e.g. lumefantrine) that clears the remaining parasites from the patient's system. Efforts to understand the effect of resistance to ACTs on malaria morbidity and mortality, and to develop strategies to control or mitigate the spread of resistance, would benefit from insights derived from mathematical modelling. Building on the model developed by Slater et al. (2016), `malariasimulation` provides the functionality to simulate the effects of resistance to the artemisinin and/or partner drug components to multiple, independent ACTs, on malaria transmission dynamics.
+
+Resistance to the artemisinin component of an ACT can result either in slow parasite clearance (SPC), in which treatment with an ACT takes longer than 3 days to fully clear patients with resistant parasites, or early treatment failure (ETF), in which the ACT fails to clear the infection and the individual develops a clinical infection. Resistance to the partner drug, where the partner drug fails to clear the parasite after the artemisinin derivative is depleted, results in infections recrudescing to either clinical (D) or asymptomatic infections (A). Resistance to the partner drug can also result in individuals developing a novel, resistant infection following treatment, as the prophylaxis provided by the ACT fails to protect the individual against reinfection by a resistant strain. In the following vignette, we illustrate how to parameterise and run `malariasimulation` simulations with resistance to ACTs deployed as a clinical treatment
+
+## Using set_antimalarial_resistance() to parameterise resistance
+Simulations capturing the effects of resistance to clinical treatment using antimalarial drugs are parameterised using the `set_antimalarial_resistance()` function. This function appends user-defined resistance parameters to a `malariasimulation` parameter list and accepts ten inputs. The first is a list of `malariasimulation` parameters to append the resistance parameters to, and the second the index of the `drug` for which resistance is being parameterised, as set using the `set_drugs()` function. The `set_antimalarial_resistance()` function requires the `timesteps`, `artemisinin_resistance_proportion`, `partner_drug_resistance_proportion_proportion`, `slow_parasite_clearance_probability`, `early_treatment_failure_probability`, `late_clinical_failure_probability`, `late_parasitological_failure_prob`, and `reinfection_during_prophylaxis_probability` inputs to be of equal length so that, for each time step in which an update occurs, a value is available for each parameter. Finally, the `slow_parasite_clearance_time` parameter represents the mean residence time, in days, for artemisinin-resistant individuals experiencing slow parasite clearance (SPC) in the Treated compartment, and must be input as a single, positive value.
+
+## Simulating static resistance
+To illustrate how to parameterise resistance to an ACT using the `set_antimalarial_resistance()` function, we'll set-up and run three simulations. The first simulates malaria transmission in the absence of interventions or resistance. The second simulates a simple regime of clinical treatment in which 80% of clinical cases are treated with artemether lumefantrine (AL), beginning after one year, in the absence of antimalarial resistance. The third simulates the same clinical treatment programme but with resistance to the artemisinin component of AL emerging after two years. For illustrative purposes, we assume that the proportion of infections resistant to the artemisinin component of AL increases from 0% to 80%, and that these infections have a 90% chance of resulting in early treatment failure.
+
+### Parameterisation
+First, we load the default `malariasimulation` model parameters, using the `overrides` argument to increase the human population. The human and mosquito population parameters are then calibrated to a user-specified initial EIR using the `set_equilibrium()` function. Next, we load the in-built parameters for the antimalarial drug AL and append them to the parameter list using `set_drugs()`. We can then use `set_clinical_treatment()` to specify a clinical treatment regime, beginning after one year, that treats, on average, 80% of the clinical cases of malaria with AL (`AL_params`). The `set_antimalarial_resistance()` function is then used to specify a scenario in which resistance is initially absent from the population, but after two years the proportion of malaria infections that are resistant to the artemisinin component of AL rises to 80%. We also set the probability that artemisinin-resistant infections result in early treatment failure to 0.9. In the current instance, we've set the proportion of infections resistant to the AL partner drug to 0% and the probabilities of other resistant infection outcomes to zero for simplicity.
+
+```{r, eval = TRUE}
+
+# Specify the time steps over which to simulate:
+timesteps <- 365 * 3
+
+# Specify an initial EIR to calibrate to:
+init_EIR <- 8
+
+# Specify a time step for treatment to begin:
+treatment_start <- (1 * 365) + 1
+
+# Specify a time step for resistance to emerge:
+resistance_start <- (2 * 365) + 1
+
+# Load the base malariasimulation parameter set:
+simparams <- get_parameters(
+ overrides = list(
+ human_population = 10000)
+)
+
+# Calibrate to the initial EIR:
+simparams <- set_equilibrium(parameters = simparams, init_EIR = init_EIR)
+
+# Append the parameters for artemether lumefantrine (AL) to the parameter list:
+simparams_clin_treatment <- set_drugs(parameters = simparams, drugs = list(AL_params))
+
+# Use the set_clinical_treatment() function to specify a treatment regime for AL:
+simparams_clin_treatment <- set_clinical_treatment(parameters = simparams_clin_treatment,
+ drug = 1,
+ timesteps = treatment_start,
+ coverages = 0.8)
+
+# Use the set_antimalarial_resistance() function to specify resistance to AL:
+simparams_resistance <- set_antimalarial_resistance(parameters = simparams_clin_treatment,
+ drug = 1,
+ timesteps = c(0, resistance_start),
+ artemisinin_resistance_proportion = c(0, 0.8),
+ partner_drug_resistance_proportion = rep(0, 2),
+ slow_parasite_clearance_probability = rep(0, 2),
+ early_treatment_failure_probability = c(0, 0.9),
+ late_clinical_failure_probability = rep(0, 2),
+ late_parasitological_failure_probability = rep(0, 2),
+ reinfection_during_prophylaxis_probability = rep(0, 2),
+ slow_parasite_clearance_time = 10)
+
+```
+
+### Simulation
+We can now use the `run_simulation()` to simulate the three scenarios for which we have established parameter lists above.
+
+```{r, eval = TRUE}
+
+# Baseline: No-intervention, no resistance simulation:
+sim_out_baseline <- run_simulation(timesteps = timesteps, parameters = simparams)
+
+# Clinical treatment with no antimalarial drug resistance:
+sim_out_clin_treatment <- run_simulation(timesteps = timesteps, parameters = simparams_clin_treatment)
+
+# Clinical treatment with antimalarial drug resistance:
+sim_out_resistance <- run_simulation(timesteps = timesteps, parameters = simparams_resistance)
+
+```
+
+### Visualisation
+With the outputs from the `run_simulation()` function, we can visualise the effect of resistance on malaria transmission by plotting the prevalence of malaria in children aged 2-10 years old (*Pf*PR~2-10~) over time.
+
+```{r, fig.align = 'center', eval = TRUE}
+
+# In each timestep, calculate PfPR_2-10 and add it to as a new column for each simulation output:
+sim_out_baseline$pfpr210 = sim_out_baseline$n_detect_730_3650/sim_out_baseline$n_730_3650
+sim_out_clin_treatment$pfpr210 = sim_out_clin_treatment$n_detect_730_3650/sim_out_clin_treatment$n_730_3650
+sim_out_resistance$pfpr210 = sim_out_resistance$n_detect_730_3650/sim_out_resistance$n_730_3650
+
+# Plot the prevalence through time in each of the three simulated scenarios:
+plot.new(); par(mar = c(4, 4, 1, 1), new = TRUE)
+plot(x = sim_out_baseline$timestep, y = sim_out_baseline$pfpr210,
+ xlab = "Time (days)",
+ ylab = expression(paste(italic(Pf),"PR"[2-10])), cex = 0.7,
+ ylim = c(0, 1), type = "l", lwd = 2, xaxs = "i", yaxs = "i",
+ col = cols[3])
+
+# Add a line for the clinical treatment scenario:
+lines(x = sim_out_clin_treatment$timestep,
+ y = sim_out_clin_treatment$pfpr210,
+ col = cols[4])
+
+# Add a line for the clinical treatment with resistance scenario:
+lines(x = sim_out_resistance$timestep,
+ y = sim_out_resistance$pfpr210,
+ col = cols[7])
+
+# Add lines to indicate the initiation of treatment and resistance:
+abline(v = treatment_start, lty = "dashed")
+abline(v = resistance_start, lty = "dashed")
+
+# Annotate the added vlines:
+text(x = treatment_start + 10, y = 0.9, labels = "Start of\nTreatment", adj = 0, cex = 0.7)
+text(x = resistance_start + 10, y = 0.9, labels = "Start of\nResistance", adj = 0, cex = 0.7)
+
+# Add gridlines:
+grid(lty = 2, col = "grey80", nx = NULL, ny = NULL, lwd = 0.5); box()
+
+# Add a legend:
+legend(x = 20, y = 0.99, legend = c("Baseline", "Treatment", "Resistance"),
+ col= c(cols[3:4], cols[7]), box.col = "white",
+ lwd = 1, lty = c(1, 1), cex = 0.7)
+
+```
+
+In the absence of clinical treatment or resistance, prevalence is comparable between all three scenarios for the first year. Following the initiation of clinical treatment at the beginning of the second year, *Pf*PR~2-10~ approximately halves relative to the no-intervention baseline. However, following the introduction of artemisinin resistance at the beginning of the third year, early treatment failure causes the *Pf*PR~2-10~ to increase to an intermediate level in the resistance scenario.
+
+## Simulating dynamic resistance
+We can also capture scenarios in which resistance to a drug changes through time. To illustrate, we'll establish and simulate a scenario in which resistance to sulfadoxine-pyrimethamine amodiaquine (SP-AQ) is absent from the population in the first year, but emerges in the third year and doubles in proportion each year thereafter until 100% of infections are artemisinin resistant. For simplicity, we'll assume only artemisinin resistance is present in the population, and resistance to artemisinin results only, and always, in early treatment failure.
+
+### Parameterisation
+First, we store in vectors the artemisinin resistance proportions and the time steps on which they will be updated in the simulation. We also create a vector of early treatment failure probabilities which, for simplicity, we assume remain at 1 for each update. Next, we load the default `malariasimulation` parameter set, specifying a larger population size and seasonal transmission, and append the parameters for SP-AQ (`SP_AQ_params`) using the `set_drugs()` function. We'll specify a simple treatment regimen using `set_clinical_treatment()` where, on average, 80% of clinical cases are treated with SP-AQ, beginning after one year. We then specify a resistance schedule in which artemisinin resistance is introduced at a proportion of 0.2 after 3 years, and doubles each year thereafter until all infections are artemisinin resistant. Finally, we calibrate the human and mosquito population parameters to a defined entomological inoculation rate (EIR) and are ready to run the simulation.
+
+```{r, eval = TRUE}
+
+# Specify the time steps over which to simulate:
+timesteps <- 365 * 8
+
+# Set an initial EIR value:
+initial_eir <- 8
+
+# Specify the proportion of infections that are artemisinin resistant:
+resistance_updates <- c(0, 0.2, 0.4, 0.8, 1)
+
+# Specify the time steps in which to update the resistance parameters:
+resistance_update_timesteps <- c(0, seq(3*365, 6*365, by = 365))
+
+# Specify the probability artemisinin resistance infections result in early treatment failure:
+early_treatment_failure_updates <- rep(1, length(resistance_update_timesteps))
+
+# Load the base malariasimulation parameter set, with seasonal transmission:
+simparams <- get_parameters(
+ overrides = list(
+ human_population = 1000,
+ model_seasonality = TRUE,
+ g0 = 0.284596,
+ g = c(-0.317878,-0.0017527,0.116455),
+ h = c(-0.331361,0.293128,-0.0617547)
+))
+
+# Append the parameters for sulfadomamine pyrimethaine (SP-AQ) to the parameter list:
+simparams <- set_drugs(parameters = simparams, drugs = list(SP_AQ_params))
+
+# Use the set_clinical_treatment() function to specify a treatment regime for SP-AQ:
+simparams <- set_clinical_treatment(parameters = simparams,
+ drug = 1,
+ timesteps = 365 * 1,
+ coverages = 0.8)
+
+# Use the set_antimalarial_resistance() function to specify resistance to SP-AQ:
+simparams <- set_antimalarial_resistance(parameters = simparams,
+ drug = 1,
+ timesteps = resistance_update_timesteps,
+ artemisinin_resistance_proportion = resistance_updates,
+ partner_drug_resistance_proportion = rep(0, length(resistance_update_timesteps)),
+ slow_parasite_clearance_probability = rep(0, length(resistance_update_timesteps)),
+ early_treatment_failure_probability = early_treatment_failure_updates,
+ late_clinical_failure_probability = rep(0, length(resistance_update_timesteps)),
+ late_parasitological_failure_probability = rep(0, length(resistance_update_timesteps)),
+ reinfection_during_prophylaxis_probability = rep(0, length(resistance_update_timesteps)),
+ slow_parasite_clearance_time = 10)
+
+# Calibrate the parameters to an initial EIR:
+simparams <- set_equilibrium(parameters = simparams, init_EIR = initial_eir)
+
+```
+
+### Simulation
+We can now use our parameter list to run the simulation using the the `run_simulation()` function.
+
+```{r, eval = TRUE}
+
+# Run the simulation:
+dynamic_resistance_output <- run_simulation(timesteps = timesteps, parameters = simparams)
+
+```
+
+### Visualisation
+We can visualise the effect of increasing resistance through time by plotting the *Pf*PR~2-10~. We've added vertical lines to indicate when clinical treatment begins, and when the proportion of infections resistant to artemisinin is updated.
+
+```{r, fig.align = 'center', eval = TRUE}
+
+# Calculate the prevalence (PfPR210) through time:
+dynamic_resistance_output$pfpr210 <- dynamic_resistance_output$n_detect_730_3650/dynamic_resistance_output$n_730_3650
+
+# Open a new plotting window and add a grid:
+plot.new(); par(mar = c(4, 4, 1, 1), new = TRUE)
+plot(x = dynamic_resistance_output$timestep,
+ y = dynamic_resistance_output$pfpr210,
+ xaxt = "n",
+ xlab = "Time (years)",
+ ylab = expression(paste(italic(Pf),"PR"[2-10])), cex = 0.8,
+ ylim = c(0, 1), type = "l", lwd = 2, xaxs = "i", yaxs = "i",
+ col = cols[3])
+
+# Specify x-axis ticks and labels:
+axis(1, at = seq(0, 8 * 365, by = 365), labels = seq(0, 8 * 365, by = 365)/365)
+
+# Add a line indicating the start of the clinical treatment:
+abline(v = 365, lty = "dotted")
+
+# Add lines indicating when resistance is updated:
+abline(v = resistance_update_timesteps, lty = "dashed")
+
+# Add a line highlighting the maximum PfPR_2-10 value prior to treatment or resistance:
+abline(h = max(dynamic_resistance_output$pfpr210[1:365]), col = "red")
+
+# Add annotations for the vlines:
+text(x = 365 + 30, y = 0.6, labels = "Clin. treat. begins", adj = 0, cex = 0.8, srt = 90)
+text(x = resistance_update_timesteps[2:5] + 30, y = 0.6, labels = paste0("Art. Res. = ", resistance_updates[2:5]),
+ adj = 0, cex = 0.8, srt = 90)
+
+```
+
+Looking at the figure, we can see that the *Pf*PR~2-10~ decreases over the two years following the onset of clinical treatment in the absence of artemisinin resistance. However, as resistance is introduced and increases through time, the *Pf*PR~2-10~ increases towards the pre-intervention seasonal peak as SP-AQ becomes increasingly ineffective in the treatment of clinical cases of malaria.
+
+## Simulating antimalarial resistance with multiple resistance outcomes
+
+As we've discussed, resistance to an ACT can manifest in multiple ways. For instance, resistance to the artemisinin component of an ACT can result in either early treatment failure or slow parasite clearance.
+
+Using `malariasimulation`, we can simulate the effects of multiple potential outcomes of resistance on malaria transmission dynamics. To illustrate, we'll parameterise and run a series of simulations in is i) no clinical treatment, ii) no resistance, iii) resistance with early treatment failure, iv) resistance with slow parasite clearance, and v) resistance with early treatment failure and slow parasite clearance.
+
+### Parameterisation
+```{r}
+
+# Determine the number of timesteps to run for:
+timesteps <- 365 * 6
+
+# Set up a list to store the simulation parameter lists in:
+simulation_parameters <- list()
+
+# Establish a list of the base parameters with no clinical treatment or antimalarial resistance:
+get_parameters(overrides = list(human_population = 1000)) -> simulation_parameters$base
+
+# Establish a parameter list with clinical treatment starting after one year:
+simulation_parameters$base |>
+ set_drugs(drugs = list(AL_params)) |>
+ set_clinical_treatment(drug = 1, timesteps = (365 * 1) + 1, coverages = c(0.8)) |>
+ set_equilibrium(init_EIR = 16) -> simulation_parameters$treatment
+
+# Set the equilibrium for the base parameters:
+simulation_parameters$base |>
+ set_equilibrium(init_EIR = 16) -> simulation_parameters$base
+
+# Establish a parameter list with clinical treatment and early treatment failure
+simulation_parameters$treatment |>
+ set_antimalarial_resistance(drug = 1,
+ timesteps = c(0, (365 * 3) + 1),
+ artemisinin_resistance_proportion = c(0, 0.8),
+ partner_drug_resistance_proportion = c(0, 0),
+ slow_parasite_clearance_probability = c(0, 0),
+ early_treatment_failure_probability = c(0, 0.8),
+ late_clinical_failure_probability = c(0, 0),
+ late_parasitological_failure_probability = c(0, 0),
+ reinfection_during_prophylaxis_probability = c(0, 0),
+ slow_parasite_clearance_time = 10) -> simulation_parameters$etf
+
+# Establish a parameter list with clinical treatment and slow parasite clearance
+simulation_parameters$treatment |>
+ set_antimalarial_resistance(drug = 1,
+ timesteps = c(0, (365 * 3) + 1),
+ artemisinin_resistance_proportion = c(0, 0.8),
+ partner_drug_resistance_proportion = c(0, 0),
+ slow_parasite_clearance_probability = c(0, 0.8),
+ early_treatment_failure_probability = c(0, 0),
+ late_clinical_failure_probability = c(0, 0),
+ late_parasitological_failure_probability = c(0, 0),
+ reinfection_during_prophylaxis_probability = c(0, 0),
+ slow_parasite_clearance_time = 10) -> simulation_parameters$spc
+
+# Establish a parameter list with clinical treatment, early treatment failure and slow parasite clearance:
+simulation_parameters$treatment |>
+ set_antimalarial_resistance(drug = 1,
+ timesteps = c(0, (365 * 3) + 1),
+ artemisinin_resistance_proportion = c(0, 0.8),
+ partner_drug_resistance_proportion = c(0, 0),
+ slow_parasite_clearance_probability = c(0, 0.8),
+ early_treatment_failure_probability = c(0, 0.8),
+ late_clinical_failure_probability = c(0, 0),
+ late_parasitological_failure_probability = c(0, 0),
+ reinfection_during_prophylaxis_probability = c(0, 0),
+ slow_parasite_clearance_time = 10) -> simulation_parameters$etf_spc
+
+```
+
+### Simulation
+We can now use our lists of `malariasimulation` parameters to run the simulations.
+
+```{r}
+
+# Open a list to store the simulation outputs in:
+simulation_outputs <- list()
+
+# Run the simulations
+for(i in seq(length(simulation_parameters))) {
+
+ # Run the i-th simulation
+ simulation_temp <- run_simulation(timesteps = timesteps,
+ parameters = simulation_parameters[[i]])
+
+ # Append the simulation identifier:
+ simulation_temp$identifier <- names(simulation_parameters)[i]
+
+ # Append the ith simulation outputs to the combined simulation dataframe:
+ simulation_outputs[[names(simulation_parameters)[i]]] <- simulation_temp
+
+ # Print the number of columns in the i-th simulation outputs dataframe:
+ print(ncol(simulation_temp))
+}
+
+```
+
+### Visualisation
+
+We can compare the effects of independent resistance outcomes with combined resistance outcomes visually. In the following plot, we compare the *Pf*PR~2-10~ between a baseline without any clinical treatment or antimalarial resistance, a clinical-treatment only run, a clinical treatment with early treatment failure run, a clinical treatment with slow parasite clearance run, and a clinical treatment with both early treatment failure and slow parasite clearance run.
+
+```{R}
+
+# Open a new plotting window and add a grid:
+plot.new(); par(mar = c(4, 4, 1, 1), new = TRUE)
+
+# Plot malaria prevalence in 2-10 years through time:
+plot(x = simulation_outputs$base$timestep,
+ y = simulation_outputs$base$n_detect_730_3650/simulation_outputs$base$n_730_3650,
+ xlab = "Time (days)",
+ ylab = expression(paste(italic(Pf),"PR"[2-10])), cex = 0.8,
+ ylim = c(0, 1), type = "l", lwd = 2, xaxs = "i", yaxs = "i",
+ col = cols[3])
+
+# Add the dynamics for no-intervention simulation
+lines(x = simulation_outputs$treatment$timestep,
+ y = simulation_outputs$treatment$n_detect_730_3650/simulation_outputs$treatment$n_730_3650,
+ col = cols[4])
+
+lines(x = simulation_outputs$etf$timestep,
+ y = simulation_outputs$etf$n_detect_730_3650/simulation_outputs$etf$n_730_3650,
+ col = cols[5])
+
+lines(x = simulation_outputs$spc$timestep,
+ y = simulation_outputs$spc$n_detect_730_3650/simulation_outputs$spc$n_730_3650,
+ col = cols[6])
+
+lines(x = simulation_outputs$etf_spc$timestep,
+ y = simulation_outputs$etf_spc$n_detect_730_3650/simulation_outputs$etf_spc$n_730_3650,
+ col = cols[7])
+
+# Add vlines to indicate when SP-AQ were administered:
+abline(v = 365 + 1, lty = "dashed", lwd = 1)
+text(x = (365 * 1) - 40, y = 0.6, labels = "Treatment Introduced", adj = 0, cex = 0.8, srt = 90)
+
+abline(v = (365 * 3) + 1, lty = "dashed", lwd = 1)
+text(x = (365 * 3) - 40, y = 0.6, labels = "Resistance Introduced", adj = 0, cex = 0.8, srt = 90)
+
+# Add gridlines:
+grid(lty = 2, col = "grey80", nx = NULL, ny = NULL, lwd = 0.5); box()
+
+# Add a legend:
+legend(x = 3000, y = 0.99, legend = c("Baseline", "Treatment", "ETF-only", "SPC-only", "ETF and SPC"),
+ col= c(cols[3:7]), box.col = "white",
+ lwd = 1, lty = c(1, 1), cex = 0.8)
+
+
+```
+
+## References
+Slater, H.C., Griffin, J.T., Ghani, A.C. and Okell, L.C., 2016. Assessing the potential impact of artemisinin and partner drug resistance in sub-Saharan Africa. Malaria journal, 15(1), pp.1-11.
\ No newline at end of file
diff --git a/vignettes/Carrying-capacity.Rmd b/vignettes/Carrying-capacity.Rmd
index c0cfcf7c..39558355 100644
--- a/vignettes/Carrying-capacity.Rmd
+++ b/vignettes/Carrying-capacity.Rmd
@@ -81,17 +81,13 @@ lines(s_seasonal$pfpr ~ s_seasonal$timestep, col = "darkorchid3")
## Custom modification of the mosquito carrying capacity
In malariasimulation, we can also modify the carrying capacity over time in a more
-bespoke manner. Firstly it is helpful to start with our baseline (equilibrium)
-carrying capacity. We can access this for our baseline parameters with:
+bespoke manner. We can use the `set_carrying_capacity()` helper function to
+specify scalars that modify the species-specific carrying capacity through time,
+relative to the baseline carrying capacity, at defined time steps. The baseline
+carrying capacity is generated when calling `set_equilibrium()`, to match the
+specified EIR and population size.
-```{r}
-cc <- get_init_carrying_capacity(p)
-cc
-```
-
-We can then use this in combination with `set_carrying_capacity()` to provide
-modified, species-specific carrying capacity values at given timepoints. This
-allows us to model a number of useful things:
+This functionality allows us to model a number of useful things:
### Larval source management
@@ -102,10 +98,10 @@ can be used to specify this intervention
```{r, fig.width = 7, fig.height = 4, out.width='100%'}
# Specify the LSM coverage
lsm_coverage <- 0.8
-# Set LSM by reducing the carrying capacity by (1 - coverage)
+# Set LSM by scaling the carrying capacity by (1 - coverage)
p_lsm <- p |>
set_carrying_capacity(
- carrying_capacity = matrix(cc * (1 - lsm_coverage), ncol = 1),
+ carrying_capacity_scalers = matrix(1 - lsm_coverage, ncol = 1),
timesteps = 365
)
set.seed(123)
@@ -124,7 +120,7 @@ abline(v = 365, lty = 2, col = "grey60")
### Invasive species
An invasive species may exploit a new niche, increase the carrying
-capacity at the point of invasion. The functions
+capacity at the point of invasion. The function
`set_carrying_capacity()` gives complete freedom to allow
changes to the carrying capacity to be made. Here
we will demonstrate using carrying capacity rescaling to capture the
@@ -136,13 +132,13 @@ invasion of _Anopheles stephensi_.
p_stephensi <- p |>
set_species(list(gamb_params, steph_params), c(0.995, 1 - 0.995)) |>
set_equilibrium(init_EIR = 5)
-cc_invasive <- get_init_carrying_capacity(p_stephensi)
+
# Next, at the time point of invasion, we scale up the carrying capacity for
-# the invasive species by a factor that will be dependent on the proporties of
+# the invasive species by a factor that will be dependent on the properties of
# the invasion.
p_stephensi <- p_stephensi |>
set_carrying_capacity(
- carrying_capacity = matrix(cc_invasive * c(1, 2000), ncol = 2),
+ carrying_capacity_scalers = matrix(c(1, 2000), ncol = 2),
timesteps = 365
)
@@ -169,7 +165,7 @@ time
```{r, fig.width = 7, fig.height = 4, out.width='100%'}
p_flexible <- p |>
set_carrying_capacity(
- carrying_capacity = cc * matrix(c(0.1, 2, 0.1, 0.5, 0.9), ncol = 1),
+ carrying_capacity = matrix(c(0.1, 2, 0.1, 0.5, 0.9), ncol = 1),
timesteps = seq(100, 500, 100)
)
diff --git a/vignettes/Vaccines.Rmd b/vignettes/Vaccines.Rmd
index 51bffd0e..02eb9358 100644
--- a/vignettes/Vaccines.Rmd
+++ b/vignettes/Vaccines.Rmd
@@ -158,8 +158,8 @@ rtssmassparams <- set_mass_pev(
min_wait = 0, # The minimum acceptable time since the last vaccination is 0 because in our case we are only implementing one round of vaccination.
min_ages = 5 * month, # The minimum age for the target population to be vaccinated.
max_ages = 50 * year, # The maximum age for the target population to be vaccinated.
- booster_timestep = 12 * month, # The booster is given at 12 months after the primary series.
- booster_coverage = 0.95, # Coverage of the booster dose is 95%.
+ booster_spacing = 12 * month, # The booster is given at 12 months after the primary series.
+ booster_coverage = matrix(0.95), # Coverage of the booster dose is 95%.
booster_profile = list(rtss_booster_profile) # We will model implementation of the RTSS booster.
)
@@ -228,8 +228,8 @@ seasmass_simparams <- set_mass_pev(
min_ages = 5 * month, # The minimum age for the target population to be vaccinated.
max_ages = 50 * year, # The maximum age for the target population to be vaccinated.
min_wait = 0, # There is no minimum wait between the last vaccination.
- booster_timestep = round(c(12 * month + 2 * month)), # The booster is given 14 months after the first dose.
- booster_coverage = 1, # 100% of the vaccinated population is boosted.
+ booster_spacing = round(c(12 * month + 2 * month)), # The booster is given 14 months after the first dose.
+ booster_coverage = matrix(1), # 100% of the vaccinated population is boosted.
booster_profile = list(rtss_booster_profile) # We will model implementation of the RTSS booster.
)
@@ -269,8 +269,8 @@ rtssepiparams <- set_pev_epi(
coverages = 1, # Vaccine coverage is 100%.
min_wait = 0, # There is no minimum wait since the last vaccination.
age = 5 * month, # Individuals will be vaccinated once they reach 5 months of age.
- booster_timestep = 12 * month, # The booster is administered 12 months following the third dose.
- booster_coverage = 0.95, # 95% of those vaccinated with the primary series will be boosted.
+ booster_spacing = 12 * month, # The booster is administered 12 months following the third dose.
+ booster_coverage = matrix(0.95), # 95% of those vaccinated with the primary series will be boosted.
booster_profile = list(rtss_booster_profile) # We will model implementation of the RTSS booster.
)
@@ -322,8 +322,8 @@ rtssepiseasonalparams <- set_pev_epi(
coverages = 1, # Vaccine coverage is 100%.
min_wait = 6 * month, # When seasonal_boosters = TRUE, this is the minimum time between an individual receiving the final dose and the first booster.
age = 5 * month, # Individuals will be vaccinated once they reach 5 months of age.
- booster_timestep = peak - month * 3.5 , # Because seasonal_boosters = TRUE, the timestep here is relative to the start of the year. Here, we will give a booster at 3.5 months prior to peak transmission.
- booster_coverage = 0.95, # 95% of the vaccinated population is boosted.
+ booster_spacing = peak - month * 3.5 , # Because seasonal_boosters = TRUE, the timestep here is relative to the start of the year. Here, we will give a booster at 3.5 months prior to peak transmission.
+ booster_coverage = matrix(0.95), # 95% of the vaccinated population is boosted.
seasonal_boosters = TRUE, # Boosters will be given based on a seasonal schedule, so the timing in the boosters= argument above will be relative to the start of the year instead of relative to the 3rd dose.
booster_profile = list(rtss_booster_profile) # We will model implementation of the RTSS booster.
)
@@ -362,8 +362,8 @@ rtssepiparams2 <- set_pev_epi(
coverages = 1, # Vaccine coverage is 100%.
age = 5 * month, # Individuals will be vaccinated once they reach 5 months of age.
min_wait = 0, # When seasonal_boosters = FALSE, this is the minimum time between doses.
- booster_timestep = c(12 * month, 24 * month), # Here, we are testing a strategy with 2 boosters, one at 1 year after the 3rd dose and the second 2 years after the 3rd dose.
- booster_coverage = c(1, 1), # For each of the two boosters, coverage is 100%.
+ booster_spacing = c(12 * month, 24 * month), # Here, we are testing a strategy with 2 boosters, one at 1 year after the 3rd dose and the second 2 years after the 3rd dose.
+ booster_coverage = matrix(c(1, 1), nrow=1, ncol=2), # For each of the two boosters, coverage is 100%.
booster_profile = list(rtss_booster_profile, rtss_booster_profile) # We will model implementation of the RTSS booster.
)