authors: DS Falster, A O’Reilly-Nugent, I Towers, F Robinson date: 2022
This page contains a brief introduction to get you started with
plant
.
At a high level, plant
has two key use cases - modelling
individuals or modelling patches. Modelling either is dependent on the
species parameters, the environment and the intial conditions. By
modifying these inputs you can grow different individuals or patches of
species.
XXXX diagram here
More extensive information and tutorials are available, such as:
Details of the modelling approaches (concepts):
Modelling
demography of individuals, patches and metapopulations
The
FF16
physiological strategy model
Details of using plant
in R:
First, load plant, tidyverse and purrr, which will be useful (for working with list objects):
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
For a full list of available functions run library(help=plant), or see the online reference.
Strategies are the corner stone of plant
, describing the
system of dynamical equations that determine an individuals vital rates
– i.e. growth, reproduction and mortality. The plant
package is setup to accept different stratgies. Think of these as sets
of rules which determine how biological foundation of your model.
To get started, we’ll use the FF16
strategy.
You can feed these strategies into either individuals or patches to alter the solver outcomes.
For definitions of each of these parameters see Concepts: FF16 Strategy tables.
For further information on how to adjust the strategies see Modifying parameters of strategies
## List of 37
## $ lma : num 0.198
## $ rho : num 608
## $ hmat : num 16.6
## $ omega : num 3.8e-05
## $ eta : num 12
## $ theta : num 0.000214
## $ a_l1 : num 5.44
## $ a_l2 : num 0.306
## $ a_r1 : num 0.07
## $ a_b1 : num 0.17
## $ r_s : num 6.6
## $ r_b : num 13.2
## $ r_r : num 217
## $ r_l : num 198
## $ a_y : num 0.7
## $ a_bio : num 0.0245
## $ k_l : num 0.457
## $ k_b : num 0.2
## $ k_s : num 0.2
## $ k_r : num 1
## $ a_p1 : num 151
## $ a_p2 : num 0.205
## $ a_f3 : num 0.000114
## $ a_f1 : num 1
## $ a_f2 : num 50
## $ S_D : num 0.25
## $ a_d0 : num 0.1
## $ d_I : num 0.01
## $ a_dG1 : num 5.5
## $ a_dG2 : num 20
## $ k_I : num 0.5
## $ recruitment_decay : num 0
## $ control :List of 18
## ..$ function_integration_rule : num 21
## ..$ offspring_production_tol : num 1e-08
## ..$ offspring_production_iterations: num 1000
## ..$ node_gradient_eps : num 1e-06
## ..$ node_gradient_direction : int 1
## ..$ node_gradient_richardson : logi FALSE
## ..$ node_gradient_richardson_depth : num 4
## ..$ ode_step_size_initial : num 1e-06
## ..$ ode_step_size_min : num 1e-06
## ..$ ode_step_size_max : num 0.1
## ..$ ode_tol_rel : num 1e-06
## ..$ ode_tol_abs : num 1e-06
## ..$ ode_a_y : num 1
## ..$ ode_a_dydt : num 0
## ..$ schedule_nsteps : num 20
## ..$ schedule_eps : num 0.001
## ..$ schedule_verbose : logi FALSE
## ..$ save_RK45_cache : logi FALSE
## ..- attr(*, "class")= chr "Control"
## $ collect_all_auxiliary : logi FALSE
## $ birth_rate_x : num(0)
## $ birth_rate_y : num 1
## $ is_variable_birth_rate: logi FALSE
## - attr(*, "class")= chr "FF16_Strategy"
We can define one or more individuals for a given strategy.
These are accessed using the Individual
class:
Noting that FF16_Individual
uses the
FF16_Strategy
by default (see ?FF16_Individual
for more information).
Our individual also shares the FF16_Environment
(more on
that soon) and a number of rates and functions for us to explore. The Individuals
vignette describes the nuts and bolts of all these functions, for
now we’re only going to grow and plot our individual’s height.
## Classes 'Individual<FF16,FF16_Env>', 'Individual', 'R6' <Individual<FF16,FF16_Env>>
## Inherits from: <Individual>
## Public:
## .ptr: externalptr
## aux: function (name)
## aux_names: active binding
## aux_size: active binding
## clone: function (deep = FALSE)
## compute_competition: function (h)
## compute_rates: function (environment)
## establishment_probability: function (environment)
## initialize: function (ptr)
## internals: active binding
## mortality_probability: active binding
## net_mass_production_dt: function (environment)
## ode_names: active binding
## ode_rates: active binding
## ode_size: active binding
## ode_state: active binding
## rate: function (name)
## reset_mortality: function ()
## resource_compensation_point: function ()
## set_state: function (name, v)
## state: function (name)
## strategy: active binding
## strategy_name: active binding
First we set a fixed environment (here 1.0
represents
full light exposure in an open canopy) then use the
grow_individual_to_time
function to grow our individual for
a range of time steps.
env <- FF16_fixed_environment(1.0)
times <- seq(0, 50, length.out = 101)
result <- grow_individual_to_time(ind, times, env) %>%
tidy_individual()
The tidy_indivdual function puts the outputs from
grow_individual_to_time into tidy format
, as defined in the
tidyverse. Examining our result, we see a matrix of our state variables
at each timestep
## # A tibble: 6 × 7
## step time height mortality fecundity area_heartwood mass_heartwood
## <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 0 0.344 0 0 0 0
## 2 2 0.5 0.545 0.00500 7.69e-22 0.00000000611 0.00000153
## 3 3 1 0.819 0.0100 7.23e-21 0.0000000308 0.0000109
## 4 4 1.5 1.16 0.0150 6.35e-20 0.000000115 0.0000569
## 5 5 2 1.57 0.0200 5.66e-19 0.000000354 0.000237
## 6 6 2.5 2.03 0.0250 5.10e-18 0.000000942 0.000817
Which we can plot against time
#need to create a function to convert individual results to tidy-format
#just do ggplot for now
result %>%
ggplot() +
geom_line(aes(x=time, y=height), linewidth =1.5) +
theme_classic()
You can find out more about how to grow individuals under the Individual plants example OR if you’re wanting to learn more about the concept and equations underlying individuals you can find them under Concepts: demography of plants, patches and metacommunities
While we can torture our individual into all sorts of shapes and sizes, it’s far more interesting to see how many individuals interact.
The most important feature of plant
is enabling easy
modeling of size-distributions for multiple competing species within a
patch.
A patch is simply an area in which a collection of individuals grow
and compete for resources (see Concepts:
patches). By size distribution we mean the distribution the
relationship for density of individuals against their size (see
Concepts-> XXX). By default, plant
starts with a
bare/empty patch, but you can also start with an existing size
distribution (see XXXX).
(XXX figure: Example size distribution (stylised)
Modelling a patch involves
Plant uses a particular mathematical method to model size distributions, which assumes large populations and continuous size distribution. The dynamics are solved by integrating along so-called “characteristics” of a partial differential equation (see Concepts-> XXX).
The output from modelling a patch is size distribution at a series of times during patch development. At each time, the model reports states and rates for example individuals along the characteristics. From this, you can calculate a wide variety of metrics for the individual, population, community or environment within a patch at a given time.
Below we provide some examples to get you started. More worked examples are available at XXXXX.
First, let’s activate the logger (to keep us updated on what’s happening):
## [2025-03-13 07:23:07.074] Activating logging to console
Then start by specifying the model rules and load some basic controls. Here we’ll use the FF16 ruleset:
We can then need to specify the particular species by specifying
their traits and birth rates. Here we use lma
but other
traits are possible (see Examples -> XXX).
Then we run plant
solver (SCM) to solve the size
distribution over time. There are two steps
# 1. Resolve number of characteristics needed for specified accuracy
species_parameters <- build_schedule(species_parameters)
## [2025-03-13 07:23:07.846] schedule> 1: Splitting {20} times (141)
## [2025-03-13 07:23:08.594] schedule> 2: Splitting {9} times (161)
# 2. Rerun, gathering outputs at each time step
result <-
run_scm_collect(species_parameters) %>%
tidy_patch()
If you’re working through this yourself, the SCM solver should be blazingly fast. This is quite cool, given that this model has hundreds of coupled differential equations veing stepped through time, wtih complex compeititve interactions at each step.
The results is a list with a number of objects, which are explained in full at XXXX, and include
## List of 6
## $ time : num [1:171] 0e+00 1e-05 2e-05 3e-05 4e-05 ...
## $ species : tibble [29,070 × 13] (S3: tbl_df/tbl/data.frame)
## $ env :List of 1
## $ offspring_production: num 20
## $ p :List of 9
## ..- attr(*, "class")= chr [1:2] "Parameters<FF16,FF16_Env>" "Parameters"
## ..- attr(*, "offspring_production")= num 20
## $ n_spp : int 1
For easy usage, the tidy_patch
function puts the
species
and env
outputs into
tidy format
, as defined in the tidyverse. The species
object looks like this:
## # A tibble: 14,705 × 13
## step time patch_density species node height mortality fecundity
## <int> <dbl> <dbl> <chr> <int> <dbl> <dbl> <dbl>
## 1 1 0 0.0333 1 1 0.392 0.0107 0
## 2 2 0.00001 0.0333 1 1 0.392 0.0107 7.36e-27
## 3 2 0.00001 0.0333 1 2 0.392 0.0107 0
## 4 3 0.00002 0.0333 1 1 0.392 0.0107 1.47e-26
## 5 3 0.00002 0.0333 1 2 0.392 0.0107 7.36e-27
## 6 3 0.00002 0.0333 1 3 0.392 0.0107 0
## 7 4 0.00003 0.0333 1 1 0.392 0.0107 2.21e-26
## 8 4 0.00003 0.0333 1 2 0.392 0.0107 1.47e-26
## 9 4 0.00003 0.0333 1 3 0.392 0.0107 7.36e-27
## 10 4 0.00003 0.0333 1 4 0.392 0.0107 0
## # ℹ 14,695 more rows
## # ℹ 5 more variables: area_heartwood <dbl>, mass_heartwood <dbl>,
## # offspring_produced_survival_weighted <dbl>, log_density <dbl>,
## # density <dbl>
Let’s look at height. Each line represents the height of an example individual growing along a characteristic curve over time, beginning from the point at which the example individual recruited into the patch. Notably, the first example individuals follow much the same growth curve as our individual above, but subsequent individuals have a bumpier ride, with growth slowing as the canopy closes over. Another variable in the species output is the log density of the example individual, which we can use to weight the growth trajectories to show thinning. For example, by following the growth of an example individual we can see that the relative density of these individuals is initially high after recruitment into the patch and declines progressively through time.
To see the change in canopy openness over time we can explore the patch Environment. Lets look at year 20 first, which corresponds to the 99th timestep in our model (not all timesteps are equal!) Our FF16 environment is described in terms of canopy openness, with 1.0 being completely open and 0.0 being completely shaded. We see that the shortest individuals experience intense shading while taller individuals enjoy full sunlight:
result %>%
pluck("env", "light_availability") %>%
filter(step == "99") %>%
ggplot() +
geom_line(aes(x=height, y = light_availability), linewidth =1.5) +
theme_classic()
If we look at the light environment at the forest floor (height = 0.0)
we can see that it varies through time as older individuals thin out and
gaps form
result %>%
pluck("env", "light_availability") %>%
filter(height == 0) %>%
ggplot() +
geom_line(aes(x= time, y=light_availability), linewidth=1.5) +
theme_classic()
We can now look at the size distributions of two species competing in a patch. We can use the trait matrix function to specify multiple values for a given trait, each representing a different species in the community, or, as below, we can build upon the one-patch example above by using the expand_parameters function, including species_parameters as an argument.
As above in the single-species patch, we then determine the appropriate number of characteristics and rerun the solver, collecting the output from the patch-level dynamics.
## [2025-03-13 07:23:12.976] schedule> 1: Splitting {1,7} times (170,170)
We can now plot the size distributions of these two species through time
🚧 Coming soon 🚧