Data

library(dplyr)
library(tidyr)
library(ggplot2)
library(rsofun)
library(here)
library(lubridate)
library(FluxDataKit)

# if(!require(devtools)){install.packages(devtools)}
# devtools::install_github("geco-bern/rsofun")
library(rsofun)

Read nice file from FluxDataKit. This contains meteorological data, remotely sensed LAI and fAPAR, and observations-based GPP from an extended set of FLUXNET sites.

path_forcingfile <- "~/data_2/FluxDataKit/v3.4/zenodo_upload/rsofun_driver_data_v3.4.2.rds"
driver <- readRDS(path_forcingfile)

Get data from all sites except:

sites <- FluxDataKit::fdk_site_info |>
  dplyr::filter(!(sitename %in% c("MX-Tes", "US-KS3"))) |>  # failed sites
  dplyr::filter(!(igbp_land_use %in% c("CRO", "WET"))) |> 
  left_join(
    fdk_site_fullyearsequence,
    by = "sitename"
  ) |> 
  filter(!drop_gpp) |> 
  filter(nyears_gpp >= 5)

Cut driver data to what’s filtered above.

driver_forcing_new <- driver |>
  select(sitename, forcing) |> 
  filter(sitename %in% sites$sitename) |> 
  left_join(
    sites |> 
      select(
        sitename, 
        year_start = start_gpp, 
        year_end = end_gpp),
    by = join_by(sitename)
  ) |> 
  unnest(forcing) |> 
  mutate(year = lubridate::year(date)) |> 
  mutate(keep = ifelse(year >= year_start & year <= year_end, TRUE, FALSE)) |> 
  dplyr::select(-year_start, -year_end, -year, -keep) |> 
  group_by(sitename) |> 
  nest()
  
driver <- driver |> 
  filter(sitename %in% sites$sitename)

driver$forcing <- driver_forcing_new$data

Model runs

Run the P-model (rsofun package) assuming 400 ppm for all sites and dates.

# values from sofunCalVal reference run
params_modl <- list(
  kphio              = 0.046857840,
  kphio_par_a        = -0.001836235,
  kphio_par_b        = 19.866210640,
  soilm_thetastar    = 28.055517785,
  soilm_betao        = 0.006899699,
  beta_unitcostratio = 146.0,
  rd_to_vcmax        = 0.014,
  tau_acclim         = 30.0,
  kc_jmax            = 0.41
)

# run the model
output_constco2 <- rsofun::runread_pmodel_f(
  driver |> 
    mutate(forcing = purrr::map(forcing, ~mutate(., co2 = 400))),
  par = params_modl
)

# some simulations failed due to missing values in the forcing. If that happens,
# the number of rows in the output data is 1.
output_constco2 <- output_constco2 |>
  mutate(len = purrr::map_int(data, ~nrow(.))) |>
  filter(len >= 1) |> # at least 3 years data
  select(-len)

Model run with varying CO2 as observed.

# run the model
output_varco2 <- rsofun::runread_pmodel_f(
  driver,
  par = params_modl
)

# some simulations failed due to missing values in the forcing. If that happens,
# the number of rows in the output data is 1.
output_varco2 <- output_varco2 |>
  mutate(len = purrr::map_int(data, ~nrow(.))) |>
  filter(len >= 1) |> # at least 3 years data
  select(-len)

This yields data and model outputs for 216 sites. Let’s look for trends…