fleet is not yet validated for real use; see the status
note.
fleet carries the same mechanisms as the
individual-based model (IBM) and, for most outcomes, lands inside its
replicate spread. A stratum mean is not a population of individuals,
however, and some effects do not survive the averaging. This article
covers what to do because of that, which settings to leave alone, which
results to treat with caution, and what a run costs.
Section references such as §B.4 are to
vignette("model"), and function links go to
vignette("parameters"). The measurements behind the claims
here are in fleetcheck, which is
kept current as the model changes; the figures quoted here are not.
Recommended practice
Call set_equilibrium() last
set_equilibrium() stores the equilibrium parameters on
the list as eq_params, and fleet uses that
stored set in place of the live values, except iv0. An edit
made afterwards, by another set_*() function or by hand
(parameters$du <- 10), therefore 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. fleet warns when a live value disagrees
with the stored one. If a calibration loop varies any of these
parameters, call set_equilibrium() again on each iteration.
?run_simulation_ode has the details.
Burn in
The seed is a close approximation to the model’s fixed point, not the fixed point itself (§F). Falciparum prevalence relaxes by under 0.5%, and incidence by a few per cent over the first years, more at high transmission (+6% for under-5 clinical incidence at EIR 120).
A P. vivax run moves further, as the IBM’s does, because
both build up a within-cell spread of immunity that the seed does not
have. Clinical incidence settles about 20% above the seed and the
realised EIR 4 to 17% above init_EIR (more at lower EIR),
within about eight years at EIR 3 but over two decades at EIR 0.3.
init_EIR is therefore not the EIR a P. vivax run
realises.
A run whose treatment starts at timestep 1 starts untreated, as the
IBM’s does, and settles onto its treated state over the first months. A
seasonal run takes about 10 years to settle onto its limit cycle, and
its annual-mean EIR is about 7% below the aseasonal
init_EIR target through nonlinear averaging. Discard the
transient.
Match the age grid to the demography
Under set_demography(), if the oldest model age group
runs past the top of deathrate_agegroups,
fleet applies the top death rate to those ages, where the
IBM removes them. A warning is given. Do not ignore it: it changes the
equilibrium age structure the whole run is seeded on (set_demography()).
Make the two match, either by extending the death rates or by lowering
the grid’s top age group below the top death-rate edge. For death rates
that stop at 60 years:
run_simulation_ode(t, p, tuning = list(age_lower = default_age_lower(max_age = 59)))Output
postie treats every column as an independent age
stratum, so overlapping bands are double-counted by any
person-day-weighted aggregate (§G). Make each family’s bands a
partition. A band [lower, upper] includes both end days, as
in the IBM, so in a partition each band ends the day before the next one
starts:
p$prevalence_rendering_min_ages <- c(0, 2, 10) * 365
p$prevalence_rendering_max_ages <- c(2, 10, 100) * 365 - 1A family whose list is empty renders nothing, in fleet
as in the IBM: the defaults give the 2-10 prevalence band and no
incidence.
Row
of the output is day
,
the state at its start and the incidence during it, exactly as
malariasimulation renders timestep
,
so the two tables line up row for row.
Prevalence is n_detect_lm_* / n_age_*, as
postie computes it. p_detect_lm_* is the IBM’s
expected number LM-detected, a count like n_detect_lm_*,
not a prevalence.
EIR is per adult per year, the value biting humans
today. The IBM reports the same lagged quantity as a per-species daily
total, so the like-for-like comparison is
EIR_<species> / human_population * 365.
S_count + D_count + A_count + U_count + Tr_count should
equal human_population to numerical tolerance.
Ph_count is the drug-protected part of
S_count, so it is not added again.
Settings to leave alone
-
acquired_immunity_offset: leave it unset. It defaults to 0 for falciparum, whose curves are read at a stratum mean, where the IBM’s per-individual 0.5 is a worse match to the IBM ensemble mean (§B.3); to 0.5 for P. vivax, whose curves are read at quadrature nodes that stand for people (§V.3); and to 0 for P. vivax withimmunity_spread = FALSE, back at the cell mean. -
bite_dedup: leave it at 1 for any scientific falciparum run. Setting 0 lets every bite infect independently, which the IBM does not do for falciparum (§B.2). P. vivax counts every bite, as the IBM does, and ignores it. -
n_sub: the default of 32 mosquito sub-steps per day integrates each day about as accurately as the IBM’s own mosquito solver, and the model’s lags are the IBM’s own delay lines, so there is nothing to sharpen. -
n_ph,n_phc: the prophylaxis chains are sized from the drug’s Weibull by default (§B.4) and capped at 20 stages. Each stage adds states, and no chain can have more stages than its mean has days. -
parameterise_total_M(),parameterise_mosquito_equilibrium(): these have no effect.fleetre-derivestotal_Mitself, frominit_EIRunder the default demography and as the IBM’s own value under a custom one (set_equilibrium()).
Limitations
Severe incidence and the age grid
Severe incidence is the output most sensitive to the age grid. sits on the convex tail of its Hill function from early childhood (§B.3 has the numbers), and a group’s severe incidence is evaluated at its mean immunity. So wherever immunity changes across a group, as it does fastest in the young groups where severe disease concentrates, the group returns too small a value. The error is first order in the group width. On the default 118 groups, severe incidence at EIR 120 is 2% below the IBM median; on 53 groups, at under half the cost, it is 7% below, and clinical incidence in young children a few per cent low. A result that rests on the level of severe incidence, or of clinical incidence in under-5s at high transmission, needs the default grid or a finer one; a coarser grid is adequate otherwise:
run_simulation_ode(t, p, tuning = list(age_lower = default_age_lower(n_group = 53)))The same convexity within an (age,
)
stratum, where the IBM holds a spread of infection histories and the
mean field one value, is a much smaller effect for falciparum. The
measurements are the severe-allage-eir and
age-profile-clinical claims in fleetcheck.
is also a post-hoc multiplier: severe counts are times an infection count the state equations have already determined (§G), so nothing downstream absorbs an error in it. , by contrast, splits the infection inflow and feeds back through infectivity, treatment and immunity.
P. vivax where days without a bite are common
malariasimulation 3.0.0 evaluates vivax relapses only on
days when at least one person is bitten, so wherever days without a bite
are common the IBM loses relapses that fleet keeps, and
runs below it, sometimes to elimination. This is a defect in the IBM,
which fleet does not copy; comparing the two there compares
fleet with the defect. The probability of a day without a
bite in the IBM is about
for a daily EIR per adult, mean relative biting rate
and population
.
In a 10,000-person run it exceeds 5% only below an EIR of about 0.14,
and in a 50,000-person run below about 0.03. It therefore matters in
small populations, at the lowest transmission, and where vector control
drives the day’s bites down. It does not explain fleet’s
excess over the IBM on the vivax site files, whose 50,000-person runs
almost never go a day without a bite; that excess is not yet
explained.
P. vivax at school age
The vivax immunity curves are steep, and the IBM’s immunity-dependent transitions sort people within an age, heterogeneity and batch cell: those with more anti-parasite immunity clear sub-patent infections sooner, so the uninfected hold more of the immune, and the clinical draw takes the least immune. A mean field with one immunity distribution per cell does not carry that sorting (§V.7), and at EIR 10 its LM prevalence and clinical incidence in school-age children are 1–3% above the IBM’s.
Vector control
Population-averaged coverage loses the correlation of protection
within individuals across bites, so fleet tends to sit
above the IBM while nets or IRS are active (§2.2).
Repeated mass campaigns
A campaign’s protection ages with the cohort it vaccinated, and
repeated rounds combine as the most recent dose, as in the IBM. What the
mean field cannot carry is who was reached before: each round reaches a
person independently of the last, and min_wait
re-vaccination exclusion is not applied (set_mass_pev(), set_tbv()). A single
campaign is unaffected.
Run times
Seconds for one complete run_simulation_ode() call:
building the inputs, seeding the equilibrium, stepping the days and
rendering the outputs. Single core, ode_tuning() defaults,
default 118-group age grid. Falciparum at EIR 20, the SMC scenarios at
EIR 15:
| Scenario | 5 years | 10 years | 30 years |
|---|---|---|---|
| No interventions | 0.30 s | 0.59 s | 1.65 s |
| Seasonal | 0.31 s | 0.60 s | 1.67 s |
| Seasonal + treatment (AL) | 0.33 s | 0.62 s | 1.75 s |
| Seasonal + nets + IRS | 0.31 s | 0.61 s | 1.79 s |
| Seasonal + SMC (4 rounds/yr) | 0.42 s | 0.75 s | 2.15 s |
| All of the above + RTS,S | 0.43 s | 0.81 s | 2.34 s |
Cost is proportional to the horizon, because every day costs the same: the no-intervention row is 0.05 s per simulated year on an intercept of a few hundredths of a second, so building the inputs and solving the seed are a small fixed charge. Seasonality adds 1% at 30 years: the update does the same work each day however fast the solution is moving, and rainfall only changes the day’s carrying capacity. Chemoprevention is the most expensive intervention, SMC adding 29% to a seasonal run and everything together, vaccination included, 40%. SMC adds state, the two chemoprevention protection chains and the treated phase, about 600 states per stage on the default grid, and each round pauses the run for its pulse.
A P. vivax run costs about 13 times a falciparum one: every compartment carries the hypnozoite-batch dimension, eight levels for eleven batch counts (§V.1), and each cell carries the spread of its immunity. At EIR 3, with 40% of clinical cases treated where a drug is named:
| Scenario | 5 years | 10 years | 30 years |
|---|---|---|---|
| No interventions | 3.7 s | 6.9 s | 21.8 s |
| Treatment (chloroquine) | 4.6 s | 8.3 s | 22.2 s |
| Radical cure (CQ + primaquine) | 5.6 s | 10.3 s | 28.5 s |
| Radical cure (CQ + tafenoquine) | 16.6 s | 30.5 s | 88.2 s |
Radical cure adds the day’s radical-cure transfers: with primaquine, 1.3 times the cost of treatment alone at 30 years. Tafenoquine costs 4.0 times as much: its liver-stage protection outlasts the blood-stage drug’s, so it adds eleven liver-stage-protected levels to the batch dimension, whereas primaquine’s ends inside chloroquine’s and adds none (§V.5).
Population size does not affect run time: the compartments are
per-capita densities, and human_population only rescales
the count columns on output.
| Population | 1,000 | 10,000 | 100,000 | 1,000,000 | 10,000,000 |
|---|---|---|---|---|---|
| 30-year seasonal run | 1.74 s | 1.73 s | 1.70 s | 1.72 s | 1.70 s |
Of the discretisation settings, the mosquito sub-steps barely affect
cost, since the mosquito model is a small part of the day: a 30-year
seasonal run takes 1.70 s at n_sub = 8, 1.72 s at the
default 32 and 1.75 s at 64. The age grid is the setting that costs, a
little more than in proportion to its number of groups: on the 53 groups
of default_age_lower(n_group = 53) the same run takes 0.70
s.
Times are the minimum of five repeats (two for P. vivax),
since contention can only add time, on R 4.5.2,
aarch64-w64-mingw32. They are indicative: one machine, one
core, and a laptop under load will be slower. The 30-year seasonal run
appears three times above, at 1.67 to 1.75 s; that spread is the
machine’s, not the model’s. For a comparison with the IBM’s cost, see
the speed claims in fleetcheck.
Index of approximations
Every mechanism labelled Approximate in
vignette("parameters"), and when it matters. The
Where column links to the argument-by-argument account; §
refers to vignette("model").
| Mechanism | Where | Matters when |
|---|---|---|
Ages above the top deathrate_agegroups take the top
death rate |
set_demography() |
A custom demography whose age grid stops short of the model’s oldest group; the IBM removes those people instead. Warned |
| Weibull prophylaxis → stage chain (moment-matched), protecting fully while it lasts |
set_drugs(),
§B.4 |
SMC and MDA at high transmission, where the chain’s all-or-nothing protection shields dosed children a little more than the IBM’s partial, fading protection does; the far tail of chemoprevention protection; for post-treatment protection the stage in front of the chain spreads AL’s sharp 10-day protection, reproducing its integral rather than its shape |
| Multi-drug blend rather than parallel sub-populations, weighted by coverage where the IBM’s treated are weighted by success | set_clinical_treatment() |
A genuinely mixed first line, especially with resistance on one drug (a switch is modelled fine) |
| Single scalar slow-clearance rate blended across drugs |
set_antimalarial_resistance(),
§B.4 |
High artemisinin resistance with several treatment drugs |
| Population-averaged nets | set_bednets() |
Any nets era, the largest routine gap |
| Population-averaged IRS | set_spraying() |
Any IRS era |
| Carrying-capacity floor at | set_carrying_capacity() |
Modelling complete vector elimination |
| Chemoprevention as pulses; target bands half-open, where the IBM’s hold both end days |
set_mda(),
set_smc(), §E |
Very narrow target bands |
| PMC age trigger → monthly pulses | set_pmc() |
PMC dose timing specifically |
| Seasonal PEV boosters → fixed delay | set_pev_epi() |
Seasonally-timed booster campaigns (warned) |
Repeated campaigns: no min_wait, each round reaching
people independently |
set_mass_pev(), set_tbv()
|
Repeated mass PEV or TBV rounds |
| The absorbing top age group taken whole, or not at all, by a rendering band |
set_epi_outputs(),
§G |
A band ending inside the top group (above 80 years on the default grid), which is given all of it |
| Refractory boosting of , , as a quasi-steady closure | §B.5 | Exposure changing sharply within a week |
| One immunity value per age group |
default_age_lower(),
§B.3 |
Severe incidence, and clinical incidence in young children at high transmission, on a grid coarser than the default |
| Vivax: one immunity distribution per age, heterogeneity and batch cell, shared by every disease state | §V.7 | School-age LM prevalence and clinical incidence, 1–3% high at EIR 10 |
| Vivax: IAA’s 44-day refractory window as four 11-day stages | §V.3 | Infants’ IAA, 2.7% of itself; nothing else by more than 0.7% |
Vivax: hypnozoite batch counts 4 to 9 sharing levels in pairs, a
Poisson inside each; ode_tuning(batch_bins = FALSE) gives
every count its own |
ode_tuning(),
§V.1 |
No output of the fleetcheck vivax suite by more than
0.4% |
| Vivax: no liver-stage protection after primaquine, whose protection ends inside chloroquine’s blood-stage protection | §V.5 | Primaquine radical cure, below 0.2% |
| Vivax: prophylaxis chains protecting fully while they last, Tr included |
set_drugs(),
§V.4–V.5 |
The shape of liver-stage protection after radical cure; under a switch from primaquine to tafenoquine, tafenoquine’s protection keeps its mean and widens its spread |
| Vivax: the day’s bites averaged before the competing draw | §V.2, §V.7 | D’s infection probability 0.1–0.5% low, and the bite share of infections 0.2–1% low |
| Vivax: refractory people infected at their cell’s average rate, Tr included | §V.3, §V.7 | IAA boosts in young children under treatment, up to an estimated 1.6% too few |
| Vivax: IAA’s within-cell spread from a renewal closure | §V.3, §V.7 | Clinical incidence and LM prevalence where daily infection probabilities pass about 0.05, at high EIR |
