The rsofun package and framework includes two main models. The pmodel and biomee (which in part relies on pmodel component). Here we give a short example on how to run the biomee model on the included demo datasets to familiarize yourself with both the data structure and the outputs.

Demo data

The package includes two demo datasets to run. These files can be directly loaded into your workspace by typing:

library(rsofun)

biomee_gs_leuning_drivers
biomee_p_model_drivers
biomee_validation

These are real data from the Swiss CH-Lae fluxnet site. We can use these data to run the model, together with observations of GPP we can also parameterize biomee parameters.

Two model approaches

BiomeE is a cohort-based vegetation model which simulates vegetation dynamics and biogeochemical processes (Weng et al., 2015). The model is able to link photosynthesis standard models (Farquhar et al., 1980) with tree allometry. In our formulation we retain the original model structure with the standard photosynthesis formulation (i.e. “gs_leuning”) as well as an alternative “p-model” approach. Both model structures operate at different time scales, where the original input has an hourly time step our alternative p-model approach uses a daily time step. Hence, we have two different datasets as driver data (with the BiomeE p-model input being an aggregate of the high resolution hourly data).

Running the BiomeE model with standard photosynthesis

With all data prepared we can run the model using runread_biomee_f(). This function takes the nested data structure and runs the model site by site, returning nested model output results matching the input drivers. In our case only one site will be evaluated.

# print parameter settings
biomee_gs_leuning_drivers$params_siml
#> [[1]]
#> # A tibble: 1 × 14
#>   spinup spinupyears recycle firstyeartrend nyeartrend outputhourly outputdaily
#>   <lgl>        <dbl>   <dbl>          <dbl>      <dbl> <lgl>        <lgl>      
#> 1 TRUE           250       1           2009          1 TRUE         TRUE       
#> # ℹ 7 more variables: do_U_shaped_mortality <lgl>, update_annualLAImax <lgl>,
#> #   do_closedN_run <lgl>, do_reset_veg <lgl>, dist_frequency <dbl>,
#> #   method_photosynth <chr>, method_mortality <chr>

# print forcing
head(biomee_gs_leuning_drivers$forcing)
#> [[1]]
#> # A tibble: 8,760 × 19
#>    date        year   doy  hour   temp temp_soil      prec snow  vpd      rh
#>    <date>     <dbl> <dbl> <dbl>  <dbl>     <dbl>     <dbl> <lgl> <lgl> <dbl>
#>  1 2009-01-01  2009     1     0 0.728       3.16 0.0000184 NA    NA     91.6
#>  2 2009-01-01  2009     1     1 0.780       3.20 0.0000116 NA    NA     91.5
#>  3 2009-01-01  2009     1     2 0.519       3.23 0.0000116 NA    NA     93.2
#>  4 2009-01-01  2009     1     3 0.476       3.28 0.0000116 NA    NA     92.5
#>  5 2009-01-01  2009     1     4 0.336       3.30 0.0000140 NA    NA     92.9
#>  6 2009-01-01  2009     1     5 0.278       3.30 0.0000140 NA    NA     93.8
#>  7 2009-01-01  2009     1     6 0.0966      3.28 0.0000140 NA    NA     95.4
#>  8 2009-01-01  2009     1     7 0.172       3.30 0.0000211 NA    NA     95.4
#>  9 2009-01-01  2009     1     8 0.236       3.33 0.0000211 NA    NA     95.2
#> 10 2009-01-01  2009     1     9 0.152       3.41 0.0000211 NA    NA     96.1
#> # ℹ 8,750 more rows
#> # ℹ 9 more variables: ppfd <dbl>, par <dbl>, patm <dbl>, wind <dbl>,
#> #   ccov_int <lgl>, ccov <lgl>, co2 <dbl>, swc <dbl>,
#> #   `lubridate::hour(datehour)` <int>
set.seed(2023)

# run the model
biomee_gs_leuning_output <- runread_biomee_f(
     biomee_gs_leuning_drivers,
     makecheck = TRUE,
     parallel = FALSE
     )

# split out the annual data
biomee_gs_leuning_output <- biomee_gs_leuning_output$data[[1]]$output_annual_tile

Plotting output

We can now visualize the model output.

# we only have one site so we'll unnest
# the main model output
biomee_gs_leuning_output |>
  ggplot() +
  geom_line(aes(x = year, y = GPP)) +
  theme_classic()+labs(x = "Year", y = "GPP")


biomee_gs_leuning_output |>
  ggplot() +
  geom_line(aes(x = year, y = plantC)) +
  theme_classic()+labs(x = "Year", y = "plantC")

Running the BiomeE model with P-model photosynthesis

Running the fast P-model implementation.

# print parameter settings
biomee_p_model_drivers$params_siml
#> [[1]]
#> # A tibble: 1 × 14
#>   spinup spinupyears recycle firstyeartrend nyeartrend outputhourly outputdaily
#>   <lgl>        <dbl>   <dbl>          <dbl>      <dbl> <lgl>        <lgl>      
#> 1 TRUE           250       1           2009          1 TRUE         TRUE       
#> # ℹ 7 more variables: do_U_shaped_mortality <lgl>, update_annualLAImax <lgl>,
#> #   do_closedN_run <lgl>, do_reset_veg <lgl>, dist_frequency <dbl>,
#> #   method_photosynth <chr>, method_mortality <chr>

# print forcing for P-model
head(biomee_p_model_drivers$forcing)
#> [[1]]
#> # A tibble: 365 × 18
#>    date        year   doy  hour    temp temp_soil       prec snow  vpd      rh
#>    <date>     <dbl> <dbl> <dbl>   <dbl>     <dbl>      <dbl> <lgl> <lgl> <dbl>
#>  1 2009-01-01  2009     1  11.5  0.384       3.39 0.0000166  NA    NA     93.7
#>  2 2009-01-02  2009     2  11.5 -1.64        2.55 0.0000232  NA    NA     92.5
#>  3 2009-01-03  2009     3  11.5 -2.51        1.95 0.00000371 NA    NA     85.1
#>  4 2009-01-04  2009     4  11.5 -1.82        2.20 0.0000130  NA    NA     83.5
#>  5 2009-01-05  2009     5  11.5 -1.34        2.23 0.0000223  NA    NA     87.8
#>  6 2009-01-06  2009     6  11.5 -0.450       2.75 0.0000219  NA    NA     90.9
#>  7 2009-01-07  2009     7  11.5  0.266       3.50 0.0000136  NA    NA     89.7
#>  8 2009-01-08  2009     8  11.5  0.504       3.52 0.0000113  NA    NA     86.1
#>  9 2009-01-09  2009     9  11.5  0.0869      3.49 0.0000186  NA    NA     90.3
#> 10 2009-01-10  2009    10  11.5 -0.404       3.44 0.0000125  NA    NA     90.1
#> # ℹ 355 more rows
#> # ℹ 8 more variables: ppfd <dbl>, par <dbl>, patm <dbl>, wind <dbl>,
#> #   ccov_int <lgl>, ccov <lgl>, co2 <dbl>, swc <dbl>
# run the model
biomee_p_model_output <- runread_biomee_f(
     biomee_p_model_drivers,
     makecheck = TRUE,
     parallel = FALSE
     )

# split out the annual data for visuals
biomee_p_model_output <- biomee_p_model_output$data[[1]]$output_annual_tile

Plotting output

We can now visualize the model output.

# we only have one site so we'll unnest
# the main model output
biomee_p_model_output %>% 
  ggplot() +
  geom_line(aes(x = year, y = GPP)) +
  theme_classic()+labs(x = "Year", y = "GPP")


biomee_p_model_output %>% 
  ggplot() +
  geom_line(aes(x = year, y = plantC)) +
  theme_classic()+labs(x = "Year", y = "plantC")

Calibrating model parameters

To optimize new parameters based upon driver data and a validation dataset we must first specify an optimization strategy and settings, as well as parameter ranges. In this example, we use as cost the root mean squared error (RMSE) between simulated and observed targets (GPP, LAI, Density and Biomass) and we minimize it using the GenSA optimizer.

# Mortality as DBH
settings <- list(
  method              = "GenSA",
  metric              = cost_rmse_biomee,
  control = list(
    maxit = 10
  ),
  par = list(
      phiRL = list(lower=0.5, upper=5, init=3.5),
      LAI_light = list(lower=2, upper=5, init=3.5),
      tf_base = list(lower=0.5, upper=1.5, init=1),
      par_mort = list(lower=0.1, upper=2, init=1))
)

pars <- calib_sofun(
  drivers = biomee_gs_leuning_drivers,
  obs = biomee_validation,
  settings = settings
)

Using the calibrated parameter values, we can run again the BiomeE simulations.

# replace parameter values by calibration output
drivers <- biomee_p_model_drivers
drivers$params_species[[1]]$phiRL[]  <- pars$par[1]
drivers$params_species[[1]]$LAI_light[]  <- pars$par[2]
drivers$params_tile[[1]]$tf_base <- pars$par[3]
drivers$params_tile[[1]]$par_mort <- pars$par[4]

# run the model with new parameter values
biomee_p_model_output_calib <- runread_biomee_f(
     drivers,
     makecheck = TRUE,
     parallel = FALSE
     )

# split out the annual data
biomee_p_model_output_calib <- biomee_p_model_output_calib$data[[1]]$output_annual_tile

Finally, we can visually compare the two model runs. We plot the original model run in black and the run using the calibrated parameters in grey. The dashed line represents the validation data, i.e. the observed GPP and Biomass.

# unnest model output for our single site
ggplot() +
  geom_line(data = biomee_p_model_output,
            aes(x = year, y = GPP)) +
  geom_line(data = biomee_p_model_output_calib,
            aes(x = year, y = GPP),
            color = "grey50") +
  theme_classic() + 
  labs(x = "Year", y = "GPP")


ggplot() +
  geom_line(data = biomee_p_model_output,
            aes(x = year, y = plantC)) +
  geom_line(data = biomee_p_model_output_calib,
            aes(x = year, y = plantC),
            color = "grey50") +
  theme_classic() + 
  labs(x = "Year", y = "plantC")