Extrinsic drivers

guide

library(plant)

Introduction

All models in plant take extrinsic drivers as inputs. At their simplest these are constants. Or they could be variables that change over time. The Extrinsic Drivers class is setup to enable verstaile input.

Extrinsic Drivers are configured as functions over time that are used as inputs in the plant solver (SCM). Due to the flexibility of the implementation, the functions can be set up as either constants or time-varying inputs. This document goes through examples of setting up Extrinsic Drivers throughout plant.

When handling functions that vary over time, Extrinsic Drivers are created via interpolation. Control points are passed as input, and the system creates a smooth function that interpolates the control points:

Example of polynomial interpolation

Under the hood, every driver is stored under a name ("rainfall", "ca", "birth_rate", …) as either a constant value or a spline built from x/y control points — the same two-case Function object regardless of which driver it backs. By default a spline will not extrapolate past the range of its control points (evaluating outside that range is an error); this can be relaxed with an extrapolation toggle. The rest of this guide walks through setting drivers of both kinds, starting with rainfall and then covering the rest of the set.

Rainfall

Rainfall is handled inside TF24_Environment, and is set upon construction. Rainfall drives the soil-water balance that the TF24 strategy couples growth to, via the leaf-level water economy described in Assimilation & hydraulics. If nothing is passed to the Environment() constructor, rainfall will default to a value of 1:

env <- Environment("TF24")

Otherwise, it can either be a constant:

env <- Environment("TF24")
env$extrinsic_drivers_set_constant("rainfall", 3.14)

… or varying over time, by providing a list with x and y control points (ie the point pairs are (x[i], y[i])):

x_pts <- seq(0, 200, 1)
rain <- list(
  x = x_pts,
  y = 1 + sin(x_pts)
)
env <- Environment("TF24")
env$extrinsic_drivers_set_variable("rainfall", rain$x, rain$y)

The resulting function of rainfall over time can then be evaluated at any time. Note how the point of evaluation was not one of the control points passed in - we are able to get rainfall at time 5.634 because of the smooth interpolated function.

env$extrinsic_drivers_evaluate("rainfall", 5.634)
[1] 0.3977089

We can also evaluate the function at a list of times, and get a list of rainfall values at those times as a result:

env$extrinsic_drivers_evaluate_range("rainfall", c(5, 1.2, 78.345))
[1] 0.04107573 1.93048445 1.19379529

The full climate driver set

rainfall is one of a whole family of named drivers that TF24 (and its acclimation variant, TF24f) register on the environment via extrinsic_drivers_set_constant() at construction time, with the defaults below:

Driver Meaning TF24 default
PPFD Photosynthetic photon flux density, i.e. light (\(\mu\)mol m\(^{-2}\) s\(^{-1}\)) 1800
rainfall Rainfall, driving the soil-water balance 1
atm_vpd Atmospheric vapour-pressure deficit (kPa) 1
ca Atmospheric CO\(_2\) partial pressure (Pa — so the default 40 is \(\approx\) 400 ppm) 40
leaf_temp Leaf temperature (\(^\circ\)C) 25
atm_o2_kpa Atmospheric O\(_2\) partial pressure (kPa) 21
atm_kpa Atmospheric pressure (kPa) 100.5

Alongside these, birth_rate is a further named driver — set per species rather than on the environment — covered in Birth rates below.

Each of these is configured exactly the same way as rainfall above: a constant with extrinsic_drivers_set_constant(), or a time-varying spline with extrinsic_drivers_set_variable(). For example, to hold CO2 fixed at a different concentration than the TF24 default:

env <- Environment("TF24")
env$extrinsic_drivers_set_constant("ca", 60)

… or to drive it with a spline over time, passing x/y control points the same way as the sinusoidal rainfall example:

x_pts <- seq(0, 200, 1)
ca_ramp <- list(
  x = x_pts,
  y = 40 + 0.2 * x_pts   # a rising atmospheric CO2 trajectory
)
env <- Environment("TF24")
env$extrinsic_drivers_set_variable("ca", ca_ramp$x, ca_ramp$y)
env$extrinsic_drivers_evaluate("ca", 100)

Because every driver shares this same constant-or-spline mechanism, the same recipe covers any climate-forced scenario: a steadily rising ca spline for a CO2 ramp, a rainfall spline that dips and stays low for a drought pulse, or a leaf_temp spline for a warming trajectory. For instance, a drought pulse superimposed on an otherwise steady rainfall regime might look like:

x_pts <- seq(0, 200, 1)
drought_y <- ifelse(x_pts >= 80 & x_pts <= 120, 0.1, 1)
env <- Environment("TF24")
env$extrinsic_drivers_set_variable("rainfall", x_pts, drought_y)

Only the TF24 and TF24f strategies read the atmospheric/leaf drivers (atm_vpd, ca, leaf_temp, atm_o2_kpa, atm_kpa) and couple them into the leaf-level water economy described in Assimilation & hydraulics. FF16 and K93 are light-only strategies: they read PPFD (light) and birth_rate, but setting the other climate drivers on their environment has no effect on growth.

Birth rates

Per species birth rates set the seed rain arriving into each patch — the boundary condition of the size-structured PDE that the solver integrates. They are set via the add_strategies() function. Each object in the list is either a constant number or an embedded list with x and y control points. There must be the same number of items in the list as there are species:

p0 <- scm_base_parameters("TF24")
p0$max_patch_lifetime <- 25      # short patch keeps the hydraulic solve fast

# two species
lmas <- trait_matrix(c(0.0825, 0.125), "lma")

x_pts <- seq(0, 200)
birth_rates <- list(
    species1 = list(x = x_pts, y = 1 + sin(x_pts)),
    species2 = 3.14
  )

p1 <- add_strategies(p0, lmas, TF24_hyperpar,
          keep_existing = FALSE,
          birth_rate = birth_rates)

# TF24 needs a configured soil-water environment: layered soil with an initial
# moisture state and a rainfall driver whose mean stays above zero. A bare
# `Environment("TF24")` would halt with `psi_soil = nan`. See the
# [Example analysis](example-analysis.qmd) guide for the full recipe.
env <- Environment("TF24")
env$set_soil_number_of_depths(15)
env$set_soil_water_state(rep(0.2, 15))
rain_t <- seq(0, p0$max_patch_lifetime, length.out = 1000)
env$extrinsic_drivers_set_variable("rainfall", rain_t, 0.25 * sin(2 * pi * rain_t) + 1.0)

ctrl <- Control()
out <- run_scm(p1, env, ctrl)

Evaluating the birth rate can be done with the scm

out$patch$species[[1]]$extrinsic_drivers$evaluate("birth_rate", 7)
[1] 1.656987