Get started with ambre

library(ambre)
set.seed(2024)
library(dplyr)
library(purrr)

What ambre does, and for whom

If you plan to irrigate crops with treated wastewater (water reuse, or REUT), you need to know whether the water is safe. ambre answers that question with QMRA – Quantitative Microbial Risk Assessment – estimating the health risk of a programme for the reuse of treated wastewater for exposed populations: field workers, local residents and consumers of the products.

The benchmark is the World Health Organization target of no more than 1e-6 DALY per person per year (one micro disability-adjusted life-year). ambre follows the multi-barrier idea: pathogens are cut down not only by the wastewater-treatment plant but also by on-field practices (drip instead of sprinklers, buffer distances, washing, peeling…), all modelled from one shared log-reduction database. This article is the guided tour; the other vignettes go deeper on each piece.

Installation and the bundled example data

ambre lives on the INRAE forge. Install it once with remotes:

remotes::install_git(
  "https://forge.inrae.fr/reversaal/reut/ambre-package.git"
)

You do not need your own data to follow along. The package ships four example input files – ready-made Excel scenarios – that every function in this tour reads through system.file():

list.files(system.file(package = "ambre"), pattern = "\\.xlsx$")
#> [1] "input_1culture_2pop.xlsx"            
#> [2] "input_cas_apprentissage_complet.xlsx"

They grow in size from a single crop and population (input_1culture_1pop.xlsx) to a full learning case of 35 exposed populations (input_cas_apprentissage_complet.xlsx). We will use the two-row file input_1culture_2pop.xlsx throughout.

The scenario object

Everything in ambre revolves around one table, the scenario. You build it from an Excel file with create_scenario():

scenario <- create_scenario(
  system.file("input_1culture_2pop.xlsx", package = "ambre")
)
dim(scenario)
#> [1]  2 24

Each row is one combination of crop x exposed population x exposure path. The example has two rows, so two situations to assess. Let’s look at the columns that matter most:

dplyr::glimpse(
  scenario[, c("CropName", "PopulationName", "PathName",
               "CropID", "PopulationID", "PathID", "config")]
)
#> Rows: 2
#> Columns: 7
#> $ CropName       <chr> "Tomato", "Corn seed"
#> $ PopulationName <chr> "Irrigation staff", "Maintenance staff"
#> $ PathName       <chr> "Ingestion of water droplets during maintenance of the …
#> $ CropID         <dbl> 6, 1
#> $ PopulationID   <dbl> 1, 2
#> $ PathID         <dbl> 1, 4
#> $ config         <list> [[<spec_tbl_df[3 x 10]>], [<spec_tbl_df[38 x 13]>], [[<…

The human-readable names you typed in Excel (CropName, PopulationName, PathName) are each resolved to an integer key – CropID, PopulationID, PathID – that ambre uses to look values up in its internal database.

The last column, config, is the interesting one: it is a list-column holding, on every row, a full private copy of the bundled config_ambre database. As the QMRA pipeline runs, each step writes its results back into that per-row config, so a scenario carries both its inputs and its growing outputs in a single object. You rarely touch config by hand – the run functions below do it for you.

Your first run

The fastest way to a result is run_qmra_treatment(). Give it a scenario file and one or more pathogens; it runs the whole workflow – simulate inflow concentration and exposure volume to calculate the initial dose, simulate log-reduction from the treatment scheme to calculate the final dose of exposure, calculate infection and illness probability, and DALYs – and returns a set of figures.

The function prints a running commentary as it works, so we send that noise to nowhere with results = "hide" and look at the pictures afterwards:

library(dplyr)
scenario <- create_scenario(system.file("input_1culture_2pop.xlsx", package = "ambre"))
regulation_reduction <- config_ambre$regulation$regulation_value |> filter(Country == "France") |>
                          select(-c(Concentration, Country, RegulationID))
regulation_concentration <- config_ambre$regulation$regulation_value |> filter(Country == "France") |>
                          select(-c(Country, RegulationID, Reduction))
plots <- run_qmra_initial_situation(scenario = scenario, 
                     pathogen = c("Campylobacter jejuni", "Norovirus"),
                     regulationLog = regulation_reduction,
                     regulationConcentration = regulation_concentration)

What comes back is a named list of five ggplot objects:

names(plots)
#> [1] "logreduction"               "dalys"                     
#> [3] "regul_matrix_log"           "regul_matrix_concentration"

These five steps trace the journey of raw water through to its effects on health. (This run takes into account only treatment-type barriers; to also include agricultural practice-type barriers, see run_qmra_barrier() and the comparative vignette below.)

Reading the four figures

Print each element to see it. Here is what each one tells you.

Log-reduction – how many log10 units each barrier in the train removes according to pathogen group simulated (Bacteria, Viruses, Protozoa); a taller box means a more effective barrier.

plots$logreduction
#> $Bacteria

#> 
#> $Viruses

DALYs – the disability-adjusted life-years lost per person per year, on a log scale. The red line marks the objective: boxes sitting below it meet the tolerable-risk goal, boxes crossing above it do not. The default value of 1e-6 DALY corresponds to the WHO target.

plots$dalys
#> $`Campylobacter jejuni`

#> 
#> $Norovirus

Each plot is a boxplot for a reason: ambre is a Monte-Carlo engine that repeats the calculation many times, so a result is a range of plausible values, not a single number. Reading that spread is the subject of its own vignette.

Regulation matrix

The outcome of an exposure scenario can be assessed in terms of regulatory compliance with regard to: - Log‑reduction (the decrease in pathogen concentration expressed in log10 units: regulation_matrix_log()

plots$regul_matrix_log
Crop name Target water class Bacteria Viruses
Tomato B 12 8
Corn seed D 12 8
plots$regul_matrix_concentration
Crop name Target water class Campylobacter jejuni Norovirus
Corn seed D 0 100
Tomato B 0 100

The background color indicates whether the reduction results are sufficient in relation to the regulations (green: complies with or falls below regulatory requirements; red: fails to meet regulatory requirements)

Other figures

Other figures can be generated :

Inflow pathogen concentration

The plot_inflow() function generates a violin plot showing the distribution of pathogen inflow concentrations simulated in a scenario. Each violin represents the variability of the inflow concentration for one pathogen, while colors distinguish pathogen groups.

scenario_with_inflow <- inflow_concentration(scenario = scenario,
 pathogenName = c("Campylobacter jejuni", "Norovirus"))
#> Simulated pathogen: Campylobacter jejuni
#> Create 1000 random distribution(s): uniform (n: 60, min: 100.000000, max: 5000.000000)
#> Simulated pathogen: Norovirus
#> Create 1000 random distribution(s): uniform (n: 60, min: 0.000000, max: 1000000.000000)
#> Providing inflow events ... ok. (0.00 secs) 
#> Providing inflow paras ... ok. (0.00 secs) 
#> Simulated pathogen: Campylobacter jejuni
#> Create 1000 random distribution(s): uniform (n: 48, min: 100.000000, max: 5000.000000)
#> Simulated pathogen: Norovirus
#> Create 1000 random distribution(s): uniform (n: 48, min: 0.000000, max: 1000000.000000)
#> Providing inflow events ... ok. (0.00 secs) 
#> Providing inflow paras ... ok. (0.00 secs)
plot_inflow(scenario_with_inflow)

A violin plot combines the features of a boxplot and a kernel density plot to visualize the distribution of simulated values.

Because the y-axis is displayed on a log₁₀ scale, equal vertical distances correspond to multiplicative (rather than additive) differences in concentration. This scaling is particularly useful for microbial concentrations, which often span several orders of magnitude.

When interpreting the figure, wider violins indicate a higher probability of observing concentrations within a given range, while taller violins indicate a broader overall variability in simulated inflow concentrations.

Volume of exposure

The plot_volumes() function generates boxplots of the exposure volumes simulated for each combination of exposed population and exposure pathway. The plots are faceted by crop, making it possible to compare exposure volumes across crops, populations, and transmission pathways.

scenario_volume <-
  scenario_with_inflow |>                     
  mutate(
    volume = map(
      .x = config,                                         
      .f = ~ simulate_exposure(config = .x)              
    )
  )
#> Simulated exposure: volume per event
#> Distribution set from 'triangle' to 'uniform' because 'min' equals 'max'
#> Create 1000 random distribution(s): uniform (n: 60, min: 0.001000, max: 0.001000)
#> Simulated exposure: volume per event
#> Distribution set from 'triangle' to 'uniform' because 'min' equals 'max'
#> Create 1000 random distribution(s): uniform (n: 48, min: 0.001000, max: 0.001000)

plot_volumes(scenario_volume)

Each boxplot summarizes the distribution of simulated exposure volumes:

Comparing boxplots allows users to assess differences in the typical exposure volume, the variability of simulated events, and the presence of extreme values among populations, crops, and exposure pathways.

Probability of infection

The plot_infection_probability() function generates boxplots of the simulated annual probability of infection for each pathogen. Separate plots are produced for each pathogen, allowing comparisons of infection probabilities across exposed populations and crops.

scenario_dose_ini <- initial_dose_calculation(scenario_volume)

scenario_scheme <- update_treatment_scheme(scenario_dose_ini)

scenario_with_logreduc_and_co <- scenario_scheme |> 
  mutate( log_reduction = map(config, simulate_treatment))
#> Create 1000 random distribution(s): uniform (n: 60, min: 1.000000, max: 2.000000)
#> Create 1000 random distribution(s): uniform (n: 60, min: 1.000000, max: 2.000000)
#> Create 1000 random distribution(s): uniform (n: 60, min: 1.000000, max: 3.000000)
#> Create 1000 random distribution(s): uniform (n: 60, min: 1.000000, max: 2.000000)
#> Create 1000 random distribution(s): uniform (n: 60, min: 5.000000, max: 7.000000)
#> Create 1000 random distribution(s): uniform (n: 60, min: 2.000000, max: 4.000000)
#> Simulated treatment: Q.1 - Activated Sludge for Bacteria
#> Simulated treatment: Q.1 - Activated Sludge for Viruses
#> Simulated treatment: Q.2 - Maturation Pond for Bacteria
#> Simulated treatment: Q.2 - Maturation Pond for Viruses
#> Simulated treatment: Q.6 - Chlorination for Bacteria
#> Simulated treatment: Q.6 - Chlorination for Viruses
#> Simulated treatment: Q.1 - Activated Sludge for Bacteria
#> Simulated treatment: Q.1 - Activated Sludge for Viruses
#> Simulated treatment: Q.2 - Maturation Pond for Bacteria
#> Simulated treatment: Q.2 - Maturation Pond for Viruses
#> Simulated treatment: Q.6 - Chlorination for Bacteria
#> Simulated treatment: Q.6 - Chlorination for Viruses
#> Create 1000 random distribution(s): uniform (n: 48, min: 1.000000, max: 2.000000)
#> Create 1000 random distribution(s): uniform (n: 48, min: 1.000000, max: 2.000000)
#> Create 1000 random distribution(s): uniform (n: 48, min: 1.000000, max: 3.000000)
#> Create 1000 random distribution(s): uniform (n: 48, min: 1.000000, max: 2.000000)
#> Create 1000 random distribution(s): uniform (n: 48, min: 5.000000, max: 7.000000)
#> Create 1000 random distribution(s): uniform (n: 48, min: 2.000000, max: 4.000000)
#> Simulated treatment: Q.1 - Activated Sludge for Bacteria
#> Simulated treatment: Q.1 - Activated Sludge for Viruses
#> Simulated treatment: Q.2 - Maturation Pond for Bacteria
#> Simulated treatment: Q.2 - Maturation Pond for Viruses
#> Simulated treatment: Q.6 - Chlorination for Bacteria
#> Simulated treatment: Q.6 - Chlorination for Viruses
#> Simulated treatment: Q.1 - Activated Sludge for Bacteria
#> Simulated treatment: Q.1 - Activated Sludge for Viruses
#> Simulated treatment: Q.2 - Maturation Pond for Bacteria
#> Simulated treatment: Q.2 - Maturation Pond for Viruses
#> Simulated treatment: Q.6 - Chlorination for Bacteria
#> Simulated treatment: Q.6 - Chlorination for Viruses

scenario_final_dose_and_co_test <- final_dose_calculation(scenario_with_logreduc_and_co)

scenario_inf_proba_and_co <- infection_probability_calculation(scenario_final_dose_and_co_test)

scenario_illness_proba_and_co <- illness_probability_calculation(scenario_inf_proba_and_co)

scenario_dalys_and_co <- dalys_calculation(scenario_illness_proba_and_co)

scenario_risk_total_and_co <-get_risk_total(scenario_dalys_and_co)

plot_infection_probability(scenario_risk_total_and_co)
#> $`Campylobacter jejuni`

#> 
#> $Norovirus

Each boxplot summarizes the distribution of simulated annual infection probabilities. Comparing boxplots allows users to identify differences in both the magnitude and the variability of annual infection probabilities among exposed populations and crops for each pathogen simulated.

Where to go next

You now have the mental map. From here, pick the thread you need – open any of these articles with vignette(), or browse them on the package website: