Skip to content

Commit 6977e09

Browse files
committed
Update calibration vignettes and docs with instructions on optional args
1 parent 6c99236 commit 6977e09

2 files changed

Lines changed: 94 additions & 28 deletions

File tree

R/calib_sofun.R

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
#' calibration of SOFUN model parameters.
55
#'
66
#' @param drivers A data frame with driver data. See \code{\link{p_model_drivers}}
7-
#' for a description of the data structure.
7+
#' for a description of the data structure. Within \code{calib_sofun}, additional
8+
#' columns can optionally be added to \code{drivers} to control e.g. the
9+
#' processing within a personalized cost function.
810
#' @param obs A data frame containing observational data used for model
911
#' calibration. See \code{\link{p_model_validation}} for a description of the data
10-
#' structure.
12+
#' structure. Within \code{calib_sofun}, additional columns can optionally be
13+
#' added to \code{obs} to control e.g. the processing within a personalized cost
14+
#' function.
1115
#' @param settings A list containing model calibration settings.
1216
#' See the 'P-model usage' vignette for more information and examples.
1317
#' \describe{
@@ -31,8 +35,7 @@
3135
#' }
3236
#' @param optim_out A logical indicating whether the function returns the raw
3337
#' output of the optimization functions (defaults to TRUE).
34-
#' @param ... Optional arguments passed on to the cost function specified as
35-
#' \code{settings$metric}.
38+
#' @param ... Optional arguments passed on to the cost function.
3639
#' .
3740
#' @return A named list containing the calibrated parameter vector `par` and
3841
#' the output object from the optimization `mod`. For more details on this

vignettes/new_cost_function.Rmd

Lines changed: 87 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,21 @@ library(dplyr)
1919
library(ggplot2)
2020
```
2121

22-
The `rsofun` package allows to calibrate parameters of the `pmodel` and `biomee` models via the `calib_sofun()` function. The implementation of the calibration is fairly flexible and can be adapted to a specific use-case via a cost function (used as metrics for the optimization routines in `calib_sofun()`). The package provides a set of standard cost functions named `cost_*`, which can be used for a variety of calibrations (different sets of model parameters, using various target variables, etc.). Alternatively, it's possible to write a more specific new cost function to be used together with `calib_sofun()`.
22+
The `rsofun` package allows to calibrate parameters of the `pmodel` and `biomee` models via the `calib_sofun()` function. The implementation of the calibration is fairly flexible and can be adapted to a specific use-case via a tailor-made cost function (used as metrics for the optimization routines in `calib_sofun()`). The package provides a set of standard cost functions named `cost_*`, which can be used for a variety of calibrations (different sets of model parameters, using various target variables, etc.). Alternatively, it's possible to write a more specific new cost function to be used together with `calib_sofun()`.
2323

24-
In this vignette, we go over some examples on how to use the `rsofun` cost functions for parameter calibration and how to write your own custom one from scratch.
24+
In this vignette, we go over some examples on how to use the `rsofun` cost functions for parameter calibration with `calib_sofun()` and how to write your own custom one from scratch.
2525

2626
### Calibration to GPP using RMSE and GenSA optimizer
2727

2828
A simple approach to parameter calibration is to find the parameter values that lead to the best prediction performance, in terms of the RMSE (root mean squared error). The function `cost_rmse_pmodel()` runs the P-model internally to calculate the RMSE between predicted target values (in this case GPP) and the corresponding observations.
2929

30-
The implementation of `cost_rmse_pmodel()` allows flexibility in various ways. We can simultaneously calibrate a subset of model parameters and also replicate the different calibration setups in Stocker et al., 2020 GMD. For example, following the `ORG` setup, only parameter `kphio` is calibrated. Furthermore, the standard cost functions allow to calibrate to several targets (fluxes and leaf traits predicted by the P-model) simultaneously and to parallelize the simulations. Since the P-model is run internally to make predictions, we must always specify which values the model parameters should take, i.e. the parameters that aren't calibrated (via argument `par_fixed`).
30+
The implementations of `cost_rmse_pmodel()` and `calib_sofun()` allows flexibility in various ways. We can simultaneously calibrate a subset of model parameters and also replicate the different calibration setups in Stocker et al., 2020 GMD, simply by providing the appropriate inputs as `settings`, `par_fixed` and `targets` to `calib_sofun()`. Since the P-model is run internally to make predictions, we must always specify the values of the model parameters that aren't calibrated (via argument `par_fixed`). For example, following the `ORG` setup, only parameter `kphio` is calibrated with below code:
3131

32-
The syntax to run the calibration routine is as follows:
33-
```{r eval = FALSE}
32+
```{r run GenSA calibration, eval = TRUE}
3433
# Define calibration settings and parameter ranges from previous work
3534
settings_rmse <- list(
3635
method = 'GenSA', # minimizes the RMSE
37-
metric = cost_rmse_pmodel, # our cost function
36+
metric = cost_rmse_pmodel, # our cost function returning the RMSE
3837
control = list( # control parameters for optimizer GenSA
3938
maxit = 100),
4039
par = list( # bounds for the parameter space
@@ -63,16 +62,20 @@ pars_calib_rmse <- calib_sofun(
6362
),
6463
targets = "gpp" # define target variable GPP
6564
)
65+
66+
pars_calib_rmse
6667
```
6768

68-
The output of `calib_sofun()` is a list containing the calibrated parameter values and the raw optimization output from the optimizer (here from `GenSA` or, as we see next, from `BayesianTools::runMCMC`).
69+
The output of `calib_sofun()` is a list containing the calibrated parameter values (element `par`) and the raw optimization output from the optimizer (element `mod`; here from `GenSA` or, as we see next, from `BayesianTools::runMCMC`).
70+
71+
Note that the standard cost functions allow to calibrate to several targets (fluxes and leaf traits predicted by the P-model) simultaneously and to parallelize the simulations.
6972

7073
### Calibration to GPP using a simple likelihood function and BayesianTools
7174

72-
Let's calibrate the parameters involved in the temperature dependency of the quantum yield efficiency, `kphio`, `kphio_par_a` and `kphio_par_b`, taking a Bayesian calibration approach. We assume that the target variable (`'gpp'`) follows a normal distribution centered at the observations and with its standard deviation being a new calibratable parameter (`'err_gpp'`). We also assume a uniform prior distribution for all calibratable parameters.
73-
By maximizing the normal log-likelihood, the MAP (maximum a posteriori) estimators for all 4 parameters are computed. With the function `cost_likelihood_pmodel()`, we can easily perform this calibration, as follows:
75+
Let's calibrate the parameters involved in the temperature dependency of the quantum yield efficiency, `kphio`, `kphio_par_a` and `kphio_par_b`. Taking a Bayesian calibration approach, we need to define a likelihood as cost function (we'll use `cost_likelihood_pmodel()`). We assume that the target variable (`'gpp'`) follows a normal distribution centered at the observations and with its _unknown_ standard deviation (`'err_gpp'`), that we need to add to the calibratable parameters. We also assume a uniform prior distribution for all calibratable parameters.
76+
By maximizing the log-likelihood, the MAP (maximum a posteriori) estimators for all 4 parameters are computed. With the functions `cost_likelihood_pmodel()` and `calib_sofun()`, we can easily perform this type of calibration:
7477

75-
```{r eval = FALSE}
78+
```{r run Bayesian calibration, eval = TRUE}
7679
# Define calibration settings
7780
settings_likelihood <- list(
7881
method = 'BayesianTools',
@@ -109,15 +112,17 @@ pars_calib_likelihood <- calib_sofun(
109112
),
110113
targets = "gpp"
111114
)
115+
116+
pars_calib_likelihood
112117
```
113118

114119
Furthermore, there are equivalent cost functions available for the BiomeE model. Check out the reference pages for more details on how to use `cost_likelihood_biomee()` and `cost_rmse_biomee()`.
115120

116121
### Calibration to GPP and Vcmax25 using the joint log-likelihood and BayesianTools
117122

118-
You may be interested in calibrating the model to different target variables simultaneously, like flux and leaf trait measurements. Here we present an example, where we use `cost_likelihood_pmodel()` to compute the joint normal likelihood of all the targets specified (that is, by summing the log-likelihoods of GPP and Vcmax25) and ultimately calibrate the `kc_jmax` parameter. It would be possible to follow this workflow for several-target calibration also with RMSE as the optimization metric, using `cost_rmse_pmodel()` and `GenSA` optimization.
123+
You may be interested in calibrating the model to different target variables simultaneously, like flux and leaf trait measurements. Here we present an example, where we use `cost_likelihood_pmodel()` to compute the joint likelihood of all the targets specified (that is, by summing the log-likelihoods of GPP and Vcmax25) and ultimately calibrate the `kc_jmax` parameter. It would be possible to follow this workflow for several-target calibration also with RMSE as the optimization metric, using `cost_rmse_pmodel()` and `GenSA` optimization.
119124

120-
```{r eval = FALSE}
125+
```{r run concatenated calibration, eval = FALSE}
121126
# Define calibration settings for two targets
122127
settings_joint_likelihood <- list(
123128
method = "BayesianTools",
@@ -153,13 +158,31 @@ par_calib_join <- calib_sofun(
153158
),
154159
targets = c('gpp', 'vcmax25')
155160
)
161+
par_calib_join
156162
```
157163

158-
Note that GPP predictions are directly compared to GPP observations on that day, but Vcmax25 predicted by the P-model (being a leaf trait) is averaged over the growing season and compared to a single Vcmax25 observation taken per site. The cost functions provided in the package tell apart fluxes and leaf traits by the presence of a `"date"` column in the nested validation data frames `p_model_validation` and `p_model_validation_vcmax25`.
164+
Note that GPP predictions are directly compared to GPP observations on that day, but Vcmax25 predicted by the P-model (being a leaf trait) is averaged over the growing season and compared to a single Vcmax25 observation taken per site.
165+
166+
The cost functions provided in the package tell apart fluxes and leaf traits by the presence of a `"date"` column in the nested validation data frames `p_model_validation` and `p_model_validation_vcmax25`.
159167

160168
### Write your custom cost function
161169

162-
If the RMSE or normal log-likelihood (for one or several targets) cost functions that we provide do not fit your use case, you can easily write a custom one. In this section, we drive you through the main ideas with an example.
170+
If the RMSE or log-likelihood (for one or several targets) cost functions that we provide do not fit your use case, you can easily write a custom one. In this section, we drive you through the main ideas with an example.
171+
To run the calibration, you can still use `calib_sofun()` in combination with
172+
your custom cost function.
173+
174+
The routine `calib_sofun()` requires `drivers`, `obs`
175+
and `settings` as mandatory arguments. These provide data.frames with driver and
176+
observational data, as well as settings for the calibration.
177+
The optional argument `optim_out` defines if the raw optimization output should
178+
be returned.
179+
All other (optional) arguments to `calib_sofun()` are passed through to the cost
180+
function (e.g. `par_fixed` in above example).
181+
They can be used freely inside of your custom cost function, e.g. to control
182+
the simulation setup or the processing. On top of these optional arguments, it
183+
is also possible to extend the `drivers` and `obs` data.frames with additional
184+
columns that can be used freely for fine-grained control within your custom cost
185+
function.
163186

164187
All cost functions must take at least three arguments:
165188

@@ -175,16 +198,50 @@ Below we'll walk you through the definition of a custom cost function.
175198
In this example, we'll calibrate the soil moisture stress parameters and use the
176199
mean absolute error (MAE) as custom cost function.
177200

178-
Since we are calibrating the parameters based on model outputs, the cost function runs the P-model and compare its output to observed validation data.
179-
```{r, eval = FALSE}
180-
function(par, obs, drivers){
201+
Since we are calibrating the parameters based on model outputs, the cost
202+
function will eventually need to run the P-model and compare its output to
203+
observed validation data.
204+
205+
To get started we suggest to write a dummy cost function and use it together
206+
with `calib_sofun()` as shown below. Note that one way of developing the cost
207+
function would be to use a `browser()` statement during the development. It
208+
allows you to explore the variables that you have access to from within the cost
209+
function.
210+
```{r eval = FALSE}
211+
# Define the custom cost function to be used
212+
cost_mae <- function(par, obs, drivers, my_own_message){
181213
# Your code
214+
browser() # can facilitate the development, remove afterwards
182215
}
216+
217+
# Define calibration settings and parameter ranges
218+
settings_mae <- list(
219+
method = 'GenSA',
220+
metric = cost_mae, # directly uses the custom cost function
221+
control = list(
222+
maxit = 100
223+
),
224+
par = list(
225+
soilm_thetastar = list(lower=0.0, upper=3000, init=0.6*240),
226+
soilm_betao = list(lower=0, upper=1, init=0.2)
227+
)
228+
)
229+
230+
# Calibrate the model and optimize the free parameters
231+
pars_calib_mae <- calib_sofun(
232+
drivers = p_model_drivers,
233+
obs = p_model_validation,
234+
settings = settings_mae,
235+
# optional arguments if needed in the cost function
236+
my_own_message = "Hi from inside the cost_mae function."
237+
)
238+
239+
pars_calib_mae
183240
```
184241

185-
In the optimization procedure, the cost function only takes as argument the parameters `par` that are fed to `calib_sofun()` via `settings$par` (see previous sections). Nevertheless, within the cost function we call `runread_pmodel_f()` and this function needs a full set of model parameters. Therefore, the parameters that aren't being calibrated must be hard coded inside the cost function (or passed as an argument like the `rsofun` cost functions). In this example, we only want to calibrate the soil moisture stress parameters.
242+
During the optimization procedure, the cost function receives as argument a suggestion of the parameters `par`. This might be just a subset of all needed parameters (defined via `settings$par`). Thus to call `runread_pmodel_f()` within the cost function, a full set of model parameters is needed. Here, we'll hardcode the parameters that aren't being calibrated inside the cost function. (Note, that in above examples they were passed as an additional argument `par_fixed`).
186243
```{r, eval = FALSE}
187-
function(par, obs, drivers){
244+
cost_mae <- function(par, obs, drivers, my_own_message){
188245
189246
# Set values for the list of calibrated and non-calibrated model parameters
190247
params_modl <- list(
@@ -208,11 +265,13 @@ function(par, obs, drivers){
208265
)
209266
210267
# Your code to compute the cost
268+
print(my_own_message) # useless, but showcases how to use additional arguments
269+
browser() # can facilitate the development, remove afterwards
211270
}
212271
```
213272

214-
The following chunk defines the final function. We clean the observations and model output and align the data according to site and date, to compute the mean absolute error (MAE) on GPP. Finally, the function should return a scalar value, in this case the MAE, which we want to minimize. Keep in mind that the GenSA optimization will minimize the cost, but with the BayesianTools method the cost is always maximized.
215-
```{r}
273+
The following chunk defines the final function. We clean the observations and model output and align the data according to site and date, to compute the mean absolute error (MAE) on GPP. Finally, the function should return a scalar value, in this case the MAE, which we want to minimize. Keep in mind that the GenSA optimization will minimize the cost, but with the BayesianTools method the cost (i.e. the likelihood) is always maximized.
274+
```{r define custom cost function, eval = TRUE}
216275
cost_mae <- function(par, obs, drivers){
217276
218277
# Set values for the list of calibrated and non-calibrated model parameters
@@ -233,7 +292,7 @@ cost_mae <- function(par, obs, drivers){
233292
df <- runread_pmodel_f(
234293
drivers = drivers,
235294
par = params_modl,
236-
makecheck = TRUE,
295+
makecheck = FALSE,
237296
parallel = FALSE
238297
)
239298
@@ -256,11 +315,12 @@ cost_mae <- function(par, obs, drivers){
256315
257316
# Return the computed cost
258317
return(cost)
318+
# browser() # can facilitate the development, remove afterwards
259319
}
260320
```
261321

262322
As a last step, let's verify that the calibration procedure runs using this cost function.
263-
```{r eval = FALSE}
323+
```{r run custom calibration, eval=TRUE}
264324
# Define calibration settings and parameter ranges
265325
settings_mae <- list(
266326
method = 'GenSA',
@@ -279,4 +339,7 @@ pars_calib_mae <- calib_sofun(
279339
obs = p_model_validation,
280340
settings = settings_mae
281341
)
282-
```
342+
343+
pars_calib_mae
344+
```
345+

0 commit comments

Comments
 (0)