arbocartoR package and app: deterministic ODE model for Aedes dynamics and control

Usefull materials and links:

Tutorial: How to use arbocartoR

This tutorial introduces the arbocartoR package, which simulates:

  • Spatial and temporal dynamics of Aedes mosquito populations under natural and controlled conditions.
  • Epidemiological dynamics of arboviruses (dengue, zika, chikungunya) across human and vector populations.

Setup

Install arbocartoR package:

In [1]:
# remotes::install_gitlab("astre/arbocartoR", host = "https://gitlab.cirad.fr")

Load required packages:

In [14]:
suppressPackageStartupMessages({
  suppressWarnings({
    library(arbocartoR)
    library(data.table)
    library(magrittr)
    library(tidyterra)
    library(ggplot2)
    library(sf)
    library(TDLM)
  })
})

# Please use the RGIS kernel and add conda's bin directory to the front of PATH
Sys.setenv(PATH = paste("/opt/conda/envs/RGIS/bin",
                        Sys.getenv("PATH"), sep = ":"))

Sys.setenv(PROJ_LIB = "/opt/conda/envs/RGIS/share/proj")

General Data

The package requires two main datasets:

  • parcels: each row is a node/parcel with attributes (ID: parcel unique identifier, POP: resident population, Kfix: fix/anthropogenic carrying capacity of the parcel, Kvar: variable/rainfall-dependant carying capacity, etc.).
  • meteo: meteorological data (ID, DATE, rainfall RR - in mm, temperature TP - in degrees).

The structure and attributes of the data are detailled in the helpers.

In [3]:
# ? parcels
In [4]:
# ? meteo

An example for both datasets is provided in the package:

In [2]:
data(parcels)
data(meteo)

head(parcels)
head(meteo)
A data.table: 6 × 9
IDNOM_COMSURF_HASTATIONDIFF_ALTKfixKvarPOPALT_M
<chr><chr><dbl><chr><dbl><dbl><dbl><dbl><dbl>
060010000Aiglun 1538.77916081001-707.06636 387428952 94 817.93364
060020000Amirat 1296.68226081001-515.94537 266423437 541009.05463
060030000Andon 5435.68426081001-279.323731544298069 6481245.67627
060040101Antibes 218.01646004009 69.94415 8699163651334 101.94415
060040102Antibes 119.22016004004 116.39238 5470100672447 129.39238
060040103Antibes 386.62996004002 -41.711583468265470 838 33.28842
A data.table: 6 × 7
IDDATERRTPALT_MLONLAT
<chr><date><dbl><dbl><dbl><dbl><dbl>
40060052021-01-016.2-1.301517.101989432.86355921
40060052021-01-020.0-1.051517.101989432.86355921
40060052021-01-030.0-2.051517.101989432.86355921
40060052021-01-042.2-3.601517.101989432.86355921
40060052021-01-050.0-4.501517.101989432.86355921
40060052021-01-060.0-5.501517.101989432.86355921

Select a subset of parcels

Lets use only 4 parcels to run quick simulations.

In [3]:
parcels <- parcels[startsWith(ID, "06") & (grepl("Gatt", NOM_COM) | grepl("Saint-Jeannet", NOM_COM) | grepl("La Gaude", NOM_COM)), ]

Run a simple simulation

Select the species of interest, inform the function with your parcels and meteo objects, as well as the temporal boundaries of the simulation. The period must be covered by the meteorological dataset and must last at least a year (for initialization purposes). Note that the vector population dynamics is deterministic, running multiple simulations would produce identical outputs.

In [4]:
traj <- run_arbocartoR(
  vector = "Ae. aegypti",
  parcels = parcels,
  meteo = meteo,
  n_sim = 1,
  start_date = min(meteo$DATE) %>% as.Date,
  end_date = max(meteo$DATE) %>% as.Date
)
Parameters were based on Ae. aegypti ecology

Simulation will run for 4 parcels from 2021-01-01 to 2022-11-30

No human mobility considered (mMov = NULL)

## Generating parameters for all patches can take time, please be patient. ##

Explore the simulated trajectories compiled as a list of data.table with one data.table per simulation.

In [5]:
head(traj[[1]])
A data.table: 6 × 23
IDDATEtimeShEmnewEggsLmPmAemmA1hmA2gmA2omR0interv_AmztemperatureRR_dayRR_7dayskLkP
<chr><date><int><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
0606400002021-01-013664173 910570174960 555679 80389 49542119 5793413300013.90687813.917.13106631066
0606501012021-01-0136639952176470421320121790718737012054515612744032210014.83965413.917.16046860468
0606501022021-01-0136630151583608380580 862468138864 92623692 8985328600016.78469523.830.54531645316
0612200002021-01-01366424616542543159601078820147412 8725374411259823900013.15301313.917.16455764557
0606400002021-01-023674173 807279122700 564914 78507 44361759 5733114960014.356878 5.819.93253432534
0606501012021-01-0236739951931970297420122376118298010793428012626036340015.289654 5.819.96288462884

Use plot_TS function to visualize the trajectiories.

In [6]:
plot_TS(traj, stage = c("newEggs", "Em"))

Explore and update parameters

Default parameters used by arbocartoR for both Ae. aegypti and Ae. albopictus are detailed in Bonnin et al. 2022.

The parameters and functions are also described in the helper of the build_gdata function.

Explore the default parameters for aegypti.

In [7]:
# ?build_gdata()
gdata_default <- build_gdata(vector = "Ae. aegypti")
Parameters were based on Ae. aegypti ecology

You can update those parameters using the build_gdata function which will build and format the set of ecological and epidemiological parameters required by the model.

Try to update the egg mortality rate (muE) and transmission rate from mosquitoes to host (bMH) based on Ae. aegypti default parameters.

In [8]:
#Change parameters
gdata_updated <- build_gdata(vector = "Ae. aegypti",
            bMH = 0.5,
            muE = 0.001)
Parameters were based on Ae. aegypti ecology

Compare them:

In [9]:
gdata_default[c("bMH","muE","mu1L")]

gdata_updated[c("bMH","muE","mu1L")]
$bMH
0.33
$muE
0.01
$mu1L
7e-04
$bMH
0.5
$muE
0.001
$mu1L
7e-04

Run a simulation with those data

In [10]:
traj <- run_arbocartoR(
  vector = "Ae. aegypti",
  parcels = parcels,
  meteo = meteo,
  gdata = gdata_updated,
  n_sim = 1,
  start_date = min(meteo$DATE) %>% as.Date,
  end_date = max(meteo$DATE) %>% as.Date
)

head(traj[[1]])

plot_TS(traj, stage = "Em")
Simulation will run for 4 parcels from 2021-01-01 to 2022-11-30

No human mobility considered (mMov = NULL)

## Generating parameters for all patches can take time, please be patient. ##

A data.table: 6 × 23
IDDATEtimeShEmnewEggsLmPmAemmA1hmA2gmA2omR0interv_AmztemperatureRR_dayRR_7dayskLkP
<chr><date><int><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
0606400002021-01-013664173 948884175920 567273 82160 50352153 5808513330013.90687813.917.13106631066
0606501012021-01-0136639952265663423360124294219135212230523112767732260014.83965413.917.16046860468
0606501022021-01-0136630151650168382200 882101141760 93883740 8999128640016.78469523.830.54531645316
0612200002021-01-01366424617253363178801101608150724 8876380911294923970013.15301313.917.16455764557
0606400002021-01-023674173 845344123420 577208 80231 45081788 5748815000014.356878 5.819.93253432534
0606501012021-01-0236739952020786298860124949818686110952434212651336410015.289654 5.819.96288462884

Local Preventive Control

Initialize dataset for preventive control actions. Preventive control measures should be described in a data.frame or data.table using the mandatory following columns: 'action', 'loc', 'start', 'end', 'p'.

Define control actions

In [11]:
prev_control <- data.table(
  action = character(),
  loc = factor(),
  start = structure(numeric(0), class = "Date"),
  end = structure(numeric(0), class = "Date"),
  p = numeric()
)

# Reduce carrying capacity (breeding sites destruction)
prev_control <- rbindlist(list(prev_control, data.table(
  action = "K",
  loc = "060640000",
  start = as.Date("2022/07/01"),
  end = as.Date("2022/07/07"),
  p = 0.2
)), fill = TRUE)

# Increase larvae mortality (larviciding)
prev_control <- rbindlist(list(prev_control, data.table(
  action = "L",
  loc = "060640000",
  start = as.Date("2022/07/01"),
  end = as.Date("2022/07/07"),
  p = 0.2
)), fill = TRUE)

prev_control <- rbindlist(list(prev_control, data.table(
  action = "L",
  loc = "061220000",
  start = as.Date("2022/07/07"),
  end = as.Date("2022/07/15"),
  p = 0.5
)), fill = TRUE)

# Increase adult mortality (fumigation)
prev_control <- rbindlist(list(prev_control, data.table(
  action = "A",
  loc = "061220000",
  start = as.Date("2022/07/01"),
  end = as.Date("2022/07/07"),
  p = 0.2
)), fill = TRUE)

prev_control <- rbindlist(list(prev_control, data.table(
  action = "A",
  loc = "060640000",
  start = as.Date("2022/07/01"),
  end = as.Date("2022/07/07"),
  p = 0.8
)), fill = TRUE)

You can also use build_prev_control() function to format it using GPS coordinates and buffer width (area covered by the control activities). A SpatVector object is required to calculate the spatial intersection between the parcels and the area covered by the control activities.

In [15]:
SpatVec <- terra::vect("shape/SpatVec.shp")
SpatVec <- SpatVec[SpatVec$ID %in% parcels$ID, ]

prev_control_func <- build_prev_control(action = c("K", "L"),
                   lon = 1034406, lat = 6306132,
                   start = as.Date("2022/07/01"),
                   end = as.Date("2022/07/07"),
                   p = c(0.2, 0.8),
                   SpatVec = SpatVec,
                   buffer_width = 100)

Run simulation with preventive control

In [16]:
traj <- run_arbocartoR(
  vector = "Ae. aegypti",
  parcels = parcels,
  meteo = meteo,
  n_sim = 1,
  start_date = min(meteo$DATE) %>% as.Date,
  end_date = max(meteo$DATE) %>% as.Date,
  prev_control = prev_control
)

traj_noControl <- run_arbocartoR(
  vector = "Ae. aegypti",
  parcels = parcels,
  meteo = meteo,
  n_sim = 1,
  start_date = min(meteo$DATE) %>% as.Date,
  end_date = max(meteo$DATE) %>% as.Date
)

prev_control
names(traj[[1]])

traj[[1]]$Lm_nocontrol <- traj_noControl[[1]]$Lm
plot_TS(traj, stage = c("Lm", "Lm_nocontrol"))

traj[[1]]$newEggs_nocontrol <- traj_noControl[[1]]$newEggs
plot_TS(traj, stage = c("newEggs", "newEggs_nocontrol"))
Parameters were based on Ae. aegypti ecology

Simulation will run for 4 parcels from 2021-01-01 to 2022-11-30

No human mobility considered (mMov = NULL)

## Generating parameters for all patches can take time, please be patient. ##

Parameters were based on Ae. aegypti ecology

Simulation will run for 4 parcels from 2021-01-01 to 2022-11-30

No human mobility considered (mMov = NULL)

## Generating parameters for all patches can take time, please be patient. ##

A data.table: 5 × 8
actionlocstartendpstart_timeend_timeid
<chr><fct><date><date><dbl><dbl><dbl><int>
K0606400002022-07-012022-07-070.29129181
L0606400002022-07-012022-07-070.29129182
L0612200002022-07-072022-07-150.59189263
A0612200002022-07-012022-07-070.29129184
A0606400002022-07-012022-07-070.89129185
  1. 'ID'
  2. 'DATE'
  3. 'time'
  4. 'Sh'
  5. 'Em'
  6. 'newEggs'
  7. 'Lm'
  8. 'Pm'
  9. 'Aemm'
  10. 'A1hm'
  11. 'A1gm'
  12. 'A1om'
  13. 'A2hm'
  14. 'A2gm'
  15. 'A2om'
  16. 'R0'
  17. 'interv_Am'
  18. 'z'
  19. 'temperature'
  20. 'RR_day'
  21. 'RR_7days'
  22. 'kL'
  23. 'kP'

Introduction of Exposed Individuals

Define an introduction event modifying the epidemiological state of the population at a given time.

In [17]:
introduction_pts <- build_E_random(
  period_start = as.Date("2022/08/01"),
  period_end   = as.Date("2022/09/15"),
  n_ind = 10,
  n_events = 1,
  stage = "Eh",
  loc = parcels$ID
)

introduction_pts

traj <- run_arbocartoR(
  vector = "Ae. aegypti",
  parcels = parcels,
  meteo = meteo,
  n_sim = 5,
  start_date = as.Date("2022/01/01"),
  end_date = as.Date("2022/11/30"),
  introduction_pts = introduction_pts,
  initMosq = 1e5
)

plot_TS(traj, 
        stage = c("Eh", "Ih", "Rh", 'A2hmE'), 
        parcels_ids = introduction_pts$dest)
A data.frame: 1 × 4
timedestnselect
<date><fct><int><fct>
2022-09-0606064000010Eh
Parameters were based on Ae. aegypti ecology

Simulation will run for 4 parcels from 2022-01-01 to 2022-11-30

No human mobility considered (mMov = NULL)

## Generating parameters for all patches can take time, please be patient. ##

Human Mobility

Introduce human mobility between parcels based on the spatial structure of the area of interest, distribution law based on distance between parcels and resident population.

In [18]:
SpatVec <- terra::vect("shape/SpatVec.shp")
SpatVec <- SpatVec[SpatVec$ID %in% parcels$ID, ]

mMov <- build_mMov(SpatVec,
                   law = "NGravExp",
                   p2move = 0.715,
                   verbose = TRUE)

SpatVec$pMovij <- mMov$proba_ij[rownames(mMov$proba_ij) == "060650102",]
SpatVec$pMovji <- mMov$proba_ji[rownames(mMov$proba_ji) == "060650102",]

ggplot(SpatVec) +
  geom_spatvector(aes(fill = pMovij), color = NA) +
  scale_fill_continuous(low = "white", high = "#590003") +
  ggtitle("Probability to visit a parcel (residents of 061220000)") +
  labs(fill = "")

ggplot(SpatVec) +
  geom_spatvector(aes(fill = pMovji), color = NA) +
  scale_fill_continuous(low = "white", high = "#590003") +
  ggtitle("Probability of visiting 061220000 (depending on residence)") +
  labs(fill = "")
Checks

Turn to sf

Build mi, mj, Oi

Compute distance between patches

  |                                                                      |   0%
The inputs passed the format_and_names checks successfully!

Compute travelling distributions

Normalize probabilities

Run the simulation with mobility

In [19]:
traj_mMov <- run_arbocartoR(
  vector = "Ae. aegypti",
  parcels = parcels,
  meteo = meteo,
  mMov = mMov,
  n_sim = 1,
  start_date = as.Date("2022/01/01"),
  end_date = as.Date("2022/11/30"),
  introduction_pts = introduction_pts,
  initMosq = 1e5
)
Parameters were based on Ae. aegypti ecology

Simulation will run for 4 parcels from 2022-01-01 to 2022-11-30

## Generating parameters for all patches can take time, please be patient. ##

Explore and compare the output

In [20]:
message("Parcels of introduction")
introduction_pts

message("Infected parcels without mobility")
traj[[1]][Eh > 0, unique(ID)] 

message("Infected parcels with mobility")
traj_mMov[[1]][Eh > 0, unique(ID)]
Parcels of introduction

A data.frame: 1 × 4
timedestnselect
<date><fct><int><fct>
2022-09-0606064000010Eh
Infected parcels without mobility

'060640000'
Infected parcels with mobility

  1. '060640000'
  2. '061220000'
  3. '060650101'
  4. '060650102'
In [ ]: