This workflow runs the P-model as implemented in {rsofun} with its latest version.
if ("rsofun" %in% installed.packages()) remove.packages("rsofun") devtools::install_github( "geco-bern/rsofun", ref = "HEAD", upgrade = "never", force = TRUE ) # Load packages library(rpmodel) library(rsofun) library(dplyr) library(tidyr) library(readr) library(ggplot2) library(lubridate) library(knitr) library(ggthemes) library(here) # Load functions source(here::here("R/eval_sofun.R")) source(here::here("R/get_stats.R")) source(here::here("R/analyse_modobs2.R")) source(here::here("R/create_data_split.R")) source(here::here("R/calibrate_rsofun.R")) source(here::here("R/align_events.R")) source(here::here("R/eval_droughtresponse.R")) # site selection: good-quality sites from analysis of Stocker et al. (2018) New Phyt. df_flue <- readr::read_csv(here("data/flue_stocker18nphyt.csv")) # Set seed set.seed(1982)
Before compiling this vignette, run
# for multiple folds # source(here::here("analysis/01_submit_calibration_current_run.R")) # for single fold source(here::here("analysis/rscript_calibrate_current.R"))
to calibrate parameters for five folds, split by site for spatial cross-validation. This creates five files containing calibrated parameters and the {BayesianTools} model object returned from the MCMC. Read the files.
# source(here::here("analysis/01_submit_calibration_current_run.R")) par_calib <- readRDS(here::here("data/par_calib_light_current.rds"))
knitr::kable(t(par_calib))
# load driver data for all sites drivers <- readRDS(here::here("data/drivers.rds")) # Combine fixed and calibrated model parameters # NOTE: this must be consistent with values specified in calibrate_rsofun(). par_fixed <- list( beta_unitcostratio = 146.0, kc_jmax = 0.41, rd_to_vcmax = 0.014, tau_acclim = 20.0 ) par <- c(par_calib, par_fixed) output <- runread_pmodel_f( drivers = drivers, par = par )
This runs the eval_sofun()
routine included in the sofunCalVal package, and outputs daily, monthly and annual summary statistics comparing observed with simulated values. We'll retake this same routine when running on the latest release (i.e. the workflow is equivalent but for the release of the rsofun
package)
obs_eval <- read_rds(here::here("data/obs_eval.rds")) evalsites <- output |> mutate(ntsteps = purrr::map_dbl(data, ~nrow(.))) |> dplyr::filter(ntsteps > 0) |> pull(sitename) settings_eval <- list( benchmark = list( gpp = c("fluxnet"), le = c("fluxnet") ), sitenames = evalsites, agg = 8 ) ## Evaluate model ---- out_eval <- eval_sofun( output, settings_eval, obs_eval = obs_eval, overwrite = TRUE, light = FALSE )
out_eval$gpp$fluxnet$metrics |> bind_rows(.id = "Level") |> kable()
out_eval$gpp$fluxnet$plot$gg_modobs_xdaily out_eval$gpp$fluxnet$plot$gg_modobs_spatial_annual
out_eval$gpp$fluxnet$data$meandoydf_byclim |> dplyr::filter(climatezone %in% c( "Aw south", "Am south", "BSk north", "Bsk north", "Cfa north", "Cfb north", "Cfb south", "Csa north", "Csb north", "Dfb north", "Dfc north", "ET north" ) ) |> dplyr::filter(koeppen_code != "-") |> pivot_longer( c(obs_mean, mod_mean), names_to = "source", values_to = "gpp" ) |> ggplot() + geom_ribbon( aes(x = doy, ymin = obs_min, ymax = obs_max), fill = "black", alpha = 0.2 ) + geom_line(aes(x = doy, y = gpp, color = source), size = 0.4) + labs(y = expression( paste("Simulated GPP (g C m"^-2, " d"^-1, ")" ) ), x = "DOY") + facet_wrap( ~climatezone ) + theme_gray() + theme(legend.position = "bottom") + scale_color_manual( name="Setup: ", values=c("red", "black") )
df_dday_agg <- eval_droughtresponse( df = out_eval$gpp$fluxnet$data$ddf |> rename(site = sitename), df_flue, before = 20, after = 105, leng_threshold = 10, nbins = 10, do_norm = TRUE ) df_dday_agg |> ggplot() + geom_hline( yintercept = 0, color = "black", linetype = "dotted" ) + geom_vline( xintercept = 0, color = "black", linetype = "dotted" ) + geom_line( aes( x = dday, y = median ), size = 0.9) + geom_ribbon( aes( x = dday, ymin = q33, ymax = q66 ), alpha = 0.3) + scale_color_manual( values = c( "BRC" = "black", "FULL" = "royalblue" ), name = "Setup" ) + scale_fill_manual( values = c( "BRC" = "black", "FULL" = "royalblue" ), name = "Setup") + ylim(-1.2, 2.2) + xlim(-20, 105) + scale_x_continuous( expand = c(0,0) ) + scale_y_continuous( expand = c(0,0) ) + labs( x = "Days after drought onset", y = expression( paste( "Bias (g C m"^{-1}, " d"^{-1}, ")")) ) + theme_classic()
out_eval$le$fluxnet$metrics |> bind_rows(.id = "Level") |> kable()
out_eval$le$fluxnet$plot$gg_modobs_xdaily out_eval$le$fluxnet$plot$gg_modobs_spatial_annual
out_eval$le$fluxnet$data$meandoydf_byclim |> dplyr::filter(climatezone %in% c( "Aw south", "Am south", "BSk north", "Bsk north", "Cfa north", "Cfb north", "Cfb south", "Csa north", "Csb north", "Dfb north", "Dfc north", "ET north" ) ) |> dplyr::filter(koeppen_code != "-") |> pivot_longer( c(obs_mean, mod_mean), names_to = "source", values_to = "le" ) |> ggplot() + geom_ribbon( aes(x = doy, ymin = obs_min, ymax = obs_max), fill = "black", alpha = 0.2 ) + geom_line(aes(x = doy, y = le, color = source), size = 0.4) + labs(y = expression( paste("Simulated GPP (g C m"^-2, " d"^-1, ")" ) ), x = "DOY") + facet_wrap( ~climatezone ) + theme_gray() + theme(legend.position = "bottom") + scale_color_manual( name="Setup: ", values=c("royalblue", "black") )
df_dday_agg <- eval_droughtresponse( df = out_eval$le$fluxnet$data$ddf |> rename(site = sitename), df_flue, before = 20, after = 105, leng_threshold = 10, nbins = 10, do_norm = TRUE ) df_dday_agg |> ggplot() + geom_hline( yintercept = 0, color = "black", linetype = "dotted" ) + geom_vline( xintercept = 0, color = "black", linetype = "dotted" ) + geom_line( aes( x = dday, y = median ), size = 0.9) + geom_ribbon( aes( x = dday, ymin = q33, ymax = q66 ), alpha = 0.3) + scale_color_manual( values = c( "BRC" = "black", "FULL" = "royalblue" ), name = "Setup" ) + scale_fill_manual( values = c( "BRC" = "black", "FULL" = "royalblue" ), name = "Setup") + ylim(-1.2, 2.2) + xlim(-20, 105) + scale_x_continuous( expand = c(0,0) ) + scale_y_continuous( expand = c(0,0) ) + labs( x = "Days after drought onset", y = expression( paste( "Bias (g C m"^{-1}, " d"^{-1}, ")")) ) + theme_classic()
Since rsofun takes time series forcing, overwrite forcing with constant values corresponding to the arguments provided to rpmodel::rpmodel()
.
out_pmodel <- rpmodel( tc = 20, # temperature, deg C vpd = 1000, # Pa, co2 = 400, # ppm, fapar = 1, # fraction , ppfd = 30, # mol/m2/d, elv = 0, # m.a.s.l., kphio = par$kphio, # quantum yield efficiency as calibrated beta = par$beta_unitcostratio, # unit cost ratio a/b, c4 = FALSE, method_jmaxlim = "wang17", do_ftemp_kphio = FALSE, # corresponding to setup ORG do_soilmstress = FALSE, # corresponding to setup ORG verbose = TRUE ) |> as_tibble() ## overwrite forcing with constant conditions (for one site) drivers_rpmodel <- drivers |> slice(1) |> mutate(forcing = purrr::map( forcing, ~mutate( ., temp = 20, vpd = 1000, ppfd = 30 / (60*60*24), patm = 101325, fapar = 1.0, co2 = 400, tmin = 20, tmax = 20 ) )) |> mutate(params_siml = purrr::map( params_siml, ~mutate( ., soilmstress = FALSE, tempstress = FALSE )) ) out_rsofun <- runread_pmodel_f( drivers_rpmodel, par = par ) |> dplyr::select(data) |> unnest(data) |> slice(1)
print("Are values equivalent for:") paste("- ci:ca:", all.equal(out_pmodel$chi, out_rsofun$chi, tolerance = 1e-5)) paste("- GPP:", all.equal(out_pmodel$gpp, out_rsofun$gpp, tolerance = 1e-5)) paste("- Vcmax:", all.equal(out_pmodel$vcmax / (60*60*24), out_rsofun$vcmax, tolerance = 1e-5)) paste("- Vcmax25:", all.equal(out_pmodel$vcmax25 / (60*60*24), out_rsofun$vcmax25, tolerance = 1e-5)) paste("- Jmax:", all.equal(out_pmodel$jmax / (60*60*24), out_rsofun$jmax, tolerance = 1e-5)) paste("- Jmax25:", all.equal(out_pmodel$jmax25 / (60*60*24), out_rsofun$jmax25, tolerance = 1e-5))
sessionInfo()
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.