Simulates a malariasimulation parameter list one day at a time, resolving
each day in the order malariasimulation does: immunity decay, biting and
infection, one competing draw per person between infection and progression,
a day of the mosquito model, and deaths replaced by births. The first three
arguments match malariasimulation::run_simulation(). vignette("model")
specifies the model.
Usage
run_simulation_ode(
timesteps,
parameters = NULL,
correlations = NULL,
tuning = ode_tuning(),
...
)Arguments
- timesteps
number of days to simulate, a single whole number of at least 1. The output has one row per day,
1..timesteps, asmalariasimulation's does: rowtholds the state at the start of daytand the incidence during it, so row 1 is the seed.- parameters
a
malariasimulation::get_parameters()list, with any interventions added by theset_*()functions. Required. Everything is read from the list, with no extra arguments: clinical treatment (time-varying), antimalarial resistance, bed nets, IRS, MDA, SMC and PMC, PEV (EPI and mass) and TBV vaccines, seasonality and flexible carrying capacity, and custom demography (set_demography(): age-specific mortality and the resulting equilibrium age structure).The parasite is
parameters$parasite, as inmalariasimulation: a list fromget_parameters(parasite = "vivax")runs the P. vivax model, with hypnozoite batches and relapse, radical cure with liver-stage protection (CQ_PQ_params_vivax,CQ_TQ_params_vivax) and no severe disease. MDA, SMC and PMC are refused under P. vivax, becausemalariasimulationitself fails on them. The model is always compartmental, so the individual-mosquito option does not apply.vignette("using")lists the mean-field approximations and when each one matters.The target EIR is
parameters$init_EIR, wheremalariasimulationstores and reads it; a list without it is an error. Set it as for the IBM, before the run:parameters <- malariasimulation::set_equilibrium(parameters, init_EIR = 20). That call also storeseq_params, whichfleetuses if present. Settingparameters$init_EIRby hand works but skips that step.Call
set_equilibrium()last. It stores all 56 equilibrium parameters, 51 of them translated from the IBM list, at the values they held when it ran, andfleetmerges that stored set over the live list, taking onlyiv0from the live list. An edit made afterwards to anything in the set, by anotherset_*()function or by hand (parameters$du <- 10), has no effect, and the run is bit-identical to one without it. The IBM reads those fields directly and would apply the edit, so this is a real difference between the two models.fleetwarns when a live value disagrees with the stored one. If a calibration loop varies any of them, callset_equilibrium()again on each iteration.Four optional fields, which are not
malariasimulationparameters, control how the mean field mirrors the IBM. Each defaults to the validated choice, and is normally left unset:bite_dedup(default1; P. falciparum only, since the P. vivax model counts every bite, as the IBM does): reproduce the IBM's per-timestep bite deduplication.malariasimulationcollects the day's bitten individuals in a bitset, so a person bitten several times in one timestep is infected at most once, and the daily infection probability is(1 - exp(-EPS)) * b, which saturates.0lets every bite infect independently,1 - exp(-b * EPS), which over-predicts infection where exposure approaches one bite per person per day (seasonal peaks, high-zetastrata). Neither makes themalariaEquilibriumseed an exact fixed point; the initial-conditions section ofvignette("model")lists the departures from that solution. Leave it at1for any scientific run.acquired_immunity_offset(default0for P. falciparum,0.5for P. vivax): the IBM adds 0.5 to positive acquired immunity inside theb,phiandthetaHill functions, a per-individual detail. The falciparum model reads its curves at a stratum mean, where the offset does not carry over and an A/B comparison against the IBM ensemble mean favours0. The vivax model reads them at quadrature nodes that stand for individuals (seeimmunity_spread), where it does, so it takes the IBM's0.5, or0withimmunity_spread = FALSE, back at the cell mean.immunity_spread(defaultTRUE; P. vivax only): model the spread of immunity among people who share an age, heterogeneity group and hypnozoite batch count. In the IBM they differ by batch history, and the vivax immunity curves are steep enough that the mean probability of clinical disease can be several times the probability at the mean immunity.fleetcarries each cell's second moment of immunity and averages the curves over a gamma distribution with that mean and variance.FALSEevaluates them at the cell mean, which under-predicts clinical incidence in school-age children and young adults by up to 40% once the IBM has built up its spread.hold_init_EIR(defaultFALSE): matters only withset_demography().malariasimulation::set_equilibrium()sizes the mosquito population from the equilibrium under the default exponential age structure, so under a custom demography the IBM drifts to whatever transmission that density supports. By defaultfleetdoes the same: it takes the IBM's mosquito density and seeds at the EIR that its own equilibrium under the custom age structure supports, which is generally notinit_EIR.TRUEseeds atinit_EIRexactly. Either way the seed is themalariaEquilibriumsolution at that EIR, and it relaxes slightly, as every seed does.
- correlations
accepted so that the first three arguments match
malariasimulation::run_simulation(timesteps, parameters, correlations). Intervention correlation is an individual-level feature with no mean-field analogue, so a non-NULLvalue is ignored with a warning, and interventions are assumed independent. Becausetuningis the fourth argument, anode_tuning()object, or a list of only its fields, passed here is an error rather than being silently discarded.- tuning
discretisation settings: an
ode_tuning()object, or a named list of the fields to change, the rest keeping their defaults. These are numerical settings only; model parameters stay onparameters.- ...
not used. Arguments that were top-level before the signature was reduced to
malariasimulation::run_simulation()'s (init_EIR, the fields now intuning, and the retired ODE-solver settings) are caught and reported with the call that replaces them. Any other argument is an error.
Value
A data frame in malariasimulation's layout, one row per day. A
parameter list gives the columns the IBM would give it, with the same names
and meanings, as far as a mean field carries them:
timestep: the output day,1..timesteps.Age-banded counts, one set per band in the parameter list's rendering lists and none for an empty list:
n_detect_lm_*,p_detect_lm_*(not for P. vivax) andn_detect_pcr_*overprevalence_rendering_*;n_inc_*andp_inc_*overincidence_rendering_*;n_inc_clinical_*andp_inc_clinical_*overclinical_incidence_rendering_*;n_inc_severe_*andp_inc_severe_*oversevere_incidence_rendering_*; andn_age_*(population) over all of those andage_group_rendering_*together. The tag is<lower>_<upper>in days, formatted asmalariasimulationformats it. A band[lower, upper]holds the whole-day ageslowertoupper, both included, as in the IBM, so0_1824and1825_5474partition the age axis while0_1825and1825_5475share one day's cohort.In the IBM an `n_*` column is a sampled count and its `p_*` partner the expected count behind it: `p_detect_lm_*` is the clinically detected count plus each asymptomatic person's probability of detection, a count and not a prevalence. A mean field carries expectations, so here the two are the same number. The exception is `p_inc_*`, which is 0 because `malariasimulation` 3.0.0 renders it from `incidence_min_ages`, a field no parameter list sets, and so returns 0 for it too. Prevalence is `n_detect_lm_*` / `n_age_*`, as `postie` computes it. A band is aggregated by exact overlap with the model's age groups, so it need not align with the `age_lower` grid. The exception is the absorbing top group, which holds everyone from its lower edge upward: a band is given all of it if the band reaches above that edge and none of it otherwise. A band ending inside it (60-85 years on the default grid, whose top group starts at 80) is therefore given all of it, and a band ending exactly on its lower edge none of it, which warns, as does a band that selects no age group at all. \item `n_infections` (new infections at all ages) and, when clinical treatment is deployed, `ft` (the fraction of clinical cases treated), as in the IBM. \item *P. vivax* only: `n_relapses` (the day's relapse infections) and `n_with_hypnozoites` (people carrying at least one batch), and by age `n_inc_relapse_*` over `incidence_relapse_rendering_*` and `n_with_hypnozoites_*`, with the band's population as `n_*`, over `n_with_hypnozoites_rendering_*`. A *P. vivax* `A` is LM-detectable by definition, so `n_detect_lm_*` counts all of it, and the IBM renders no `p_detect_lm_*` for *P. vivax*. A severe band on a *P. vivax* list renders as 0, as the IBM's does. \item Population totals by infection state, `S_count`, `A_count`, `D_count`, `U_count` and `Tr_count`, counted as the IBM counts them: someone a drug is protecting is uninfected, and so in `S_count`. \item Immunity means over the population: `ica_mean`, `icm_mean`, `ib_mean`, `iva_mean`, `ivm_mean` and `id_mean` for *P. falciparum*; `ica_mean`, `icm_mean`, `iaa_mean`, `iam_mean` and `hypnozoites_mean` (the mean batch count) for *P. vivax*; and each by age, `<name>_mean_*` with the band's population as `n_*`, over its `<name>_rendering_*`. The IBM seeds falciparum maternal immunity from the equilibrium bin 0.1 years older than each person, so for the first months its `icm_mean` and `ivm_mean` are below `fleet`'s, whose births take their mothers'. \item Three columns `malariasimulation` does not have: `EIR` (infectious bites per adult per year), `FOIM` (the force of infection on the first mosquito species) and `Ph_count` (how many of `S_count` a drug is protecting).
malariasimulation columns this table does not carry: n_bitten,
infectivity, EIR_<species>, FOIM_<species>, mu_<species>, the
mosquito compartment counts and total_M_<species>, natural_deaths, and
the treatment and intervention tallies (n_treated, n_smc_treated and
the rest). The IBM renders n_inc_relapse_* only on days with a relapse in
the band, and NA otherwise; a mean field's expected count is never zero.
Incidence columns are daily counts, so do not thin rows before passing the
table to postie::get_rates() or postie::get_prevalence(), which accept
it directly.
Details
The seed is where malariasimulation seeds: the
malariaEquilibrium solution for init_EIR (malariaEquilibriumVivax for
P. vivax), with the humans under the treatment coverage in force at
timestep 0 and the mosquitoes sized under the coverage at timestep 1, as
set_equilibrium() and the IBM's initialisation do. It is close to the
daily model's fixed point but not on it, so outputs relax over the first
years. A seasonal run is seeded at the annual-mean (aseasonal) equilibrium
and oscillates around a limit cycle rather than holding flat. Use a
burned-in window for calibration and comparison; vignette("using") gives
the relaxation and settling times.
A run in which any compartment falls below zero stops with an error naming
the day and the age group. Each day an age group loses its ageing and
mortality fraction out of the same stock as its infections, and several
consecutive groups a day or two wide can lose more than they hold at an EIR
in the hundreds; widen them in age_lower (ode_tuning()).
See also
ode_tuning() for the discretisation settings.
Examples
if (FALSE) { # \dontrun{
# the same call as malariasimulation::run_simulation()
p <- malariasimulation::set_equilibrium(
malariasimulation::get_parameters(), init_EIR = 20)
out <- run_simulation_ode(timesteps = 3650, parameters = p)
head(out[, c("timestep", "n_age_730_3650", "n_detect_lm_730_3650", "EIR")])
# the same run on a 53-group age grid, over twice as fast
coarse <- run_simulation_ode(3650, p,
tuning = list(age_lower = default_age_lower(n_group = 53)))
} # }
