--- title: "Getting started with apsimeval" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting started with apsimeval} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE) ``` ## The interface Everything below can also be done from a browser interface: ```{r} library(apsimeval) run_app() # opens on the working directory run_app(out_dir = "~/apsim/sundarbans/outputs") run_app(out_dir = "outputs", project = "sundarbans.apsimproj") run_app(out_dir = "outputs", poll_ms = 1000, launch.browser = FALSE) ``` `run_app()` needs the optional packages: `install.packages(c("shiny", "DT", "bslib"))`. Leave it open while APSIM runs — with **Live sync** on it re-reads only the files that changed and redraws, keeping your current settings. ## Reading output ```{r} sim <- read_out_dir("outputs") # every .out in the folder apsim_vars("outputs/N120.out") # variables and units, header only sim <- read_out_dir("outputs", vars = c("wagt", "wrr")) ``` ## Treatments from simulation names ```{r} detect_factors(sim$sim) # guesses the separator and fields preview_factors(sim$sim, parts = c("site", "crop", "water", "N")) sim <- set_factors(sim, parts = c("site", "crop", "water", "N"), numeric_from = "N") ``` Irregular names take a regex, hopeless ones a lookup table: ```{r} sim <- set_factors(sim, regex = "(?[A-Za-z]+)[-_]N(?\\d+)") map <- factor_template(sim$sim, factors = c("water", "N")) # fill in and pass back sim <- set_factors(sim, map = map) ``` ## Calibration and validation ```{r} obs <- map_obs(read_obs_raw("observed.xlsx"), date_col = "Sampling_Date", value_cols = c("Grain_yield", "Biomass"), treatment_col = "Treatment", # Spreadsheet headings are not APSIM variable names. Without # this, nothing pairs and nothing plots. var_map = c(Grain_yield = "wrr", Biomass = "wagt")) check_variables(obs, sim) # catches variable name mismatches check_levels(obs, sim) # catches treatment label mismatches long <- as_long(sim, vars = "wrr") pr <- pair_obs(long, obs, tol_days = 3) cut <- suggest_cutoff(pr) # first season calibrates pr <- split_phase(pr, "date", at = cut) # Statistics are never pooled across variables: RMSE would mix units and R2 # would be inflated by the gap between the variables rather than model skill. gof("obs", "pred", data = pr, by = "variable") gof("obs", "pred", data = pr, by = c("variable", "phase")) gof("obs", "pred", data = pr, by = c("variable", "phase", "N")) ``` The series stays continuous across the cutoff: ```{r} plot_ts(long, obs = obs, cutoff = cut) # Colouring is a presentation choice and does not affect the statistics; the # corner box follows `stats_by`, which defaults to the faceting column. plot_one2one(pr, group = "N", stats_by = character(0)) # pooled box plot_one2one(pr, group = "N", facet = "phase", sd_band = TRUE) ``` ## Scenarios and sensitivity ```{r} ag <- aggregate_sim(sim, by = c("sim", "year", "water", "N"), vars = c(wrr = "max"), crop_only = TRUE) plot_exceedance(ag, "wrr", group = "N", facet = "water", ref = 7000) plot_bar(ag, "wrr", "N", letters = TRUE) sens_anova(ag, "wrr", c("site", "water", "N"), order = 2) sens_oat(ag, "wrr", c("site", "water", "N")) ``` ## Figures ```{r} set_labels(list(wrr = "Grain yield")) set_palette(c(N0 = "#D55E00", N120 = "#0072B2")) set_colour_mode("grey") # "colour", "grey", or "mono" fig <- build_figure(list( panel_spec("one2one", group = "N", facet = "phase"), panel_spec("exceedance", var = "wrr", group = "N", ref = 7000), panel_spec("bar", var = "wrr", group = "N", letters = TRUE) ), sim = sim, paired = pr, ncol = 3) save_pub(fig, "figure_2", width = "double", height = 80, dpi = 600, format = c("tiff", "pdf")) ``` ## Projects ```{r} p <- new_project("sundarbans_rice", "outputs") p <- set_project_factors(p, sim) p$labels <- list(wrr = "Grain yield"); p$colour_mode <- "grey" save_project(p, "sundarbans_rice.apsimproj") p <- load_project("sundarbans_rice.apsimproj") sim <- apply_factors(read_out_dir("outputs"), p) # after a re-run ``` ## Replicated observed data Field data usually arrives with one row per plot: | Sampling_Date | Site | Treatment | Replication | Biomass | |---|---|---|---|---| | 2016-08-01 | Gosaba | N120 | 1 | 1000 | | 2016-08-01 | Gosaba | N120 | 2 | 1100 | Name the replication column and the replicates are averaged per date and treatment, keeping the spread for error bars: ```{r} raw <- read_obs_raw("observed.xlsx") attr(raw, "guess") # suggested date / treatment / rep / value columns obs <- map_obs(raw, date_col = "Sampling_Date", value_cols = "Biomass", treatment_col = c("Site", "Treatment"), # joined to match sim names rep_col = "Replication") ``` `obs` gains `obs_sd` and `obs_n`. A date with one replicate gets `obs_sd = NA` rather than zero, so no misleading zero-length bar is drawn. ```{r} plot_ts(long, obs = obs, cutoff = cut) # error bars drawn automatically plot_ts(long, obs = obs, cutoff = cut, obs_sd = NULL) # suppress them plot_one2one(pr, obs_sd = "obs_sd") # horizontal bars on the scatter ``` If your sheet already holds means and a separate SD column (`Biomass_SD`), leave `rep_col` empty: the SD column is detected and paired automatically. ## Timeline graphs `plot_series()` handles several variables and several treatments at once. Different units go on a secondary axis: ```{r} long <- as_long(sim, vars = c("wagt", "pond"), keep = c("water", "N")) plot_series(long, vars = c("wagt", "pond"), secondary = "pond") ``` Or give each variable its own row, which avoids the dual-axis problem entirely: ```{r} plot_series(long, vars = c("wagt", "wrr", "pond"), layout = "stacked") ``` All treatments side by side — the usual way to judge a treatment effect, since the shapes are compared rather than just the endpoints: ```{r} plot_series(long, vars = c("wagt", "pond"), secondary = "pond", facet_by = "sim", ncol = 2) # every simulation plot_series(long, vars = "wagt", facet_by = "water") # or a factor column plot_series(long, vars = "wagt", facet_by = "water", colour_by = "treatment") # colour instead of panels ``` Combine layouts for a variable-by-treatment grid, and add the cutoff, observed points and replicate error bars as usual: ```{r} plot_series(long, vars = c("wagt", "wrr"), layout = "stacked", facet_by = "sim") plot_series(long, vars = "wagt", facet_by = "sim", obs = obs, cutoff = cut) ``` Note `keep =` in `as_long()`: treatment columns have to survive the pivot or there is nothing left to facet by. A caution on the secondary axis. Two series with different units can be made to look correlated by the scaling alone, so `layout = "stacked"` is the safer choice when the variables are not meant to be compared point by point. The right axis is always labelled in original units, and legend entries drawn against it are marked. ### Observed data on a subset of the variables Observations rarely cover every plotted variable — biomass and yield are measured, AWD ponding usually is not. Pass whatever you have; variables without observations are drawn as simulated-only lines and the omission is reported: ```{r} plot_series(long, vars = c("wagt", "wrr", "pond"), secondary = "pond", obs = obs) #> No observed data for: pond (simulated only). ``` Each variable's points carry their own marker shape and their own replicate error bars, computed exactly as elsewhere in the package (`rep_col` in `map_obs()`, or an existing `obs_sd` column). Points belonging to a secondary axis variable are rescaled with the line, and their error bars are rescaled as lengths rather than positions. When panelling by treatment, observed records need to know which panel they belong to. If the observed data has no column matching `facet_by`, it is derived from the simulation label: ```{r} plot_series(long, vars = "wagt", facet_by = "water", obs = obs) # obs has only `sim` ``` Records matching no simulation are dropped and counted in a message rather than being silently discarded.