Example analysis

example

This is the flagship end-to-end example: set up parameters, run a patch with the size-structured model, then summarise, integrate, and interpolate the results.

NoteRunning the TF24 model needs a configured soil-water environment

The TF24 strategy couples plant growth to a multi-layer soil-water balance, so — unlike FF16 — it cannot run against a bare Environment("TF24"). The soil column must be given a number of layers and an initial moisture state, and the rainfall driver must keep the mean high enough that a layer never dries to zero (where the retention curve psi_from_soil_moist goes non-finite and the run aborts with set_physiology received non-finite psi_soil). We wrap that setup in make_tf24_env() below. Runtime is dominated by the hydraulic solver and scales with max_patch_lifetime, so this page pins it short (max_patch_lifetime = 25) to keep the build fast; raise it for production runs. The recipe follows the test-root vignette in the plant package.

installation

library(plant)
library(dplyr)
library(tidyr)
library(ggplot2)
library(patchwork)

Activate logger

plant_log_console()
[2026-06-21 17:27:25.060] Activating logging to console

We pin the patch lifetime short and define a helper that builds a TF24 environment with an initialised soil column (15 layers at moisture 0.2) and a seasonal rainfall driver with mean 1.0 — see the note above for why this is required.

max_patch_lifetime <- 25

make_tf24_env <- function(max_patch_lifetime, n_layers = 15, theta0 = 0.2) {
  env <- Environment("TF24")
  env$set_soil_number_of_depths(n_layers)
  env$set_soil_water_state(rep(theta0, n_layers))

  # seasonal rainfall; mean 1.0 keeps the column from drying to a non-finite psi
  x <- seq(0, max_patch_lifetime, length.out = 1000)
  y <- 0.25 * sin(2 * pi * x) + 1.0
  env$extrinsic_drivers_set_variable("rainfall", x = x, y = y)

  env
}

Very minimalist

p0 <- scm_base_parameters("TF24", "TF24_Env")
p0$max_patch_lifetime <- max_patch_lifetime

traits <- trait_matrix(c(0.07), c("lma"))

p1 <- expand_parameters(traits, p0)

env <- make_tf24_env(max_patch_lifetime)

results <- run_scm(p1, env = env, refine_schedule = TRUE)$parameters
results <- run_scm(results, env = env, collect = TRUE)

This uses the default parameters in plant. But you may want alter some of the defaults. For that we recommend setting up some functions to set base_parameters.

base_parameters <- function() {
  p0 <- scm_base_parameters("TF24", "TF24_Env")
  p0$max_patch_lifetime <- max_patch_lifetime

  p0$strategy_default$hmat <- 15
  p0$strategy_default$rho <- 700
  p0$strategy_default$a_l1 <- 2.17
  p0$strategy_default$a_l2 <- 0.5

  p0
}

We can now create a second function called run_mypatch which takes a number of a arguments, including traits which we assign a trait matrix. The expand_parameters function takes this trait matrix as an argument which allows us to simulate patches with multiple functional strategies, in addition to the base_parameters and any newly defined hyperparameters such as B_lf1. We also provide a latitude as an argument, as well as whether or not we want to run the patch to equilibrium birth rate.

run_mypatch <- function(
  traits =  trait_matrix(c(0.07), c("lma")),
  birth_rate_list = c(10, 10),
  B_lf1 = 1,
  p0 = base_parameters(),
  optimise_schedule = FALSE,
  latitude = 28.182
  ) {

  hyper_par_fn = make_TF24_hyperpar(B_lf1 = B_lf1, latitude = latitude)
  p1 <- expand_parameters(traits, p0, hyper_par_fn,  birth_rate_list = birth_rate_list)

  env <- make_tf24_env(p0$max_patch_lifetime)

  if(optimise_schedule)
    result <- run_scm(p1, env = env, refine_schedule = TRUE)$parameters
  else
    result <- p1

  # gather outputs at each time step
  run_scm(result, env = env, collect = TRUE)
}

Run patch for two species. Here, we have provided two values of lma, being thin leaves (0.07) or thick leaves (0.24). You could run this patch for just one species by defining just one lma value, or none if you just want to use the default trait values.

results <-
  run_mypatch(traits=trait_matrix(c(0.07, 0.24), c("lma")))

run_scm(..., collect = TRUE) in run_mypatch already returns the results in a tidy format (collecting and tidying the patch state at every step), so we can use the output directly.

results_tidy <- results

results_tidy
$steps
# A tibble: 102 × 3
    step      time patch_density
   <int>     <dbl>         <dbl>
 1     1 0                 0.140
 2     2 0.00001           0.140
 3     3 0.00002           0.140
 4     4 0.00003           0.140
 5     5 0.00004           0.140
 6     6 0.00005           0.140
 7     7 0.00006           0.140
 8     8 0.00007           0.140
 9     9 0.00008           0.140
10    10 0.0000953         0.140
# ℹ 92 more rows

$n_spp
[1] 2

$species
# A tibble: 10,506 × 24
   species   time  step patch_density  node density log_density height mortality fecundity area_heartwood mass_heartwood offspring_produced_s…¹ competition_effect height_inverse net_mass_production_dt
   <chr>    <dbl> <int>         <dbl> <int>   <dbl>       <dbl>  <dbl>     <dbl>     <dbl>          <dbl>          <dbl>                  <dbl>              <dbl>          <dbl>                  <dbl>
 1 1         0        1         0.140     1    24.5        3.20 0.0351  0.000840  0              0              0                      0                  0.000261           28.5               0.000901
 2 1         1e-5     2         0.140     1    24.5        3.20 0.0351  0.000841  1.29e-26       1.12e-13       2.44e-12               1.28e-26           0.000261           28.5               0.000901
 3 1         1e-5     2         0.140     2    24.5        3.20 0.0351  0.000840  0              0              0                      0                  0.000261           28.5               0.000901
 4 1         2e-5     3         0.140     1    24.5        3.20 0.0351  0.000841  2.57e-26       2.24e-13       4.87e-12               2.57e-26           0.000261           28.5               0.000902
 5 1         2e-5     3         0.140     2    24.5        3.20 0.0351  0.000840  1.29e-26       1.12e-13       2.44e-12               1.28e-26           0.000261           28.5               0.000901
 6 1         2e-5     3         0.140     3    24.5        3.20 0.0351  0.000840  0              0              0                      0                  0.000261           28.5               0.000901
 7 1         3e-5     4         0.140     1    24.5        3.20 0.0351  0.000841  3.86e-26       3.36e-13       7.31e-12               3.85e-26           0.000261           28.5               0.000902
 8 1         3e-5     4         0.140     2    24.5        3.20 0.0351  0.000840  2.57e-26       2.24e-13       4.87e-12               2.57e-26           0.000261           28.5               0.000902
 9 1         3e-5     4         0.140     3    24.5        3.20 0.0351  0.000840  1.29e-26       1.12e-13       2.44e-12               1.28e-26           0.000261           28.5               0.000902
10 1         3e-5     4         0.140     4    24.5        3.20 0.0351  0.000840  0              0              0                      0                  0.000261           28.5               0.000901
# ℹ 10,496 more rows
# ℹ abbreviated name: ¹​offspring_produced_survival_weighted
# ℹ 8 more variables: root_mass <dbl>, opt_psi_stem <dbl>, opt_root_psi <dbl>, transpiration <dbl>, E_up_ <dbl>, profit <dbl>, stom_cond_CO2 <dbl>, assimilation <dbl>

$env
$env$light_availability
# A tibble: 6,718 × 5
    time  step patch_density  height light_availability
   <dbl> <int>         <dbl>   <dbl>              <dbl>
 1     0     1         0.140 0                        1
 2     0     1         0.140 0.00110                  1
 3     0     1         0.140 0.00219                  1
 4     0     1         0.140 0.00329                  1
 5     0     1         0.140 0.00438                  1
 6     0     1         0.140 0.00548                  1
 7     0     1         0.140 0.00658                  1
 8     0     1         0.140 0.00767                  1
 9     0     1         0.140 0.00877                  1
10     0     1         0.140 0.00986                  1
# ℹ 6,708 more rows

$env$soil_moist
# A tibble: 1,530 × 4
    time  step patch_density soil_moist
   <dbl> <int>         <dbl>      <dbl>
 1     0     1         0.140        0.2
 2     0     1         0.140        0.2
 3     0     1         0.140        0.2
 4     0     1         0.140        0.2
 5     0     1         0.140        0.2
 6     0     1         0.140        0.2
 7     0     1         0.140        0.2
 8     0     1         0.140        0.2
 9     0     1         0.140        0.2
10     0     1         0.140        0.2
# ℹ 1,520 more rows

$env$soil_depth
# A tibble: 1,530 × 4
    time  step patch_density soil_depth
   <dbl> <int>         <dbl>      <dbl>
 1     0     1         0.140        0.1
 2     0     1         0.140        0.2
 3     0     1         0.140        0.3
 4     0     1         0.140        0.4
 5     0     1         0.140        0.5
 6     0     1         0.140        0.6
 7     0     1         0.140        0.7
 8     0     1         0.140        0.8
 9     0     1         0.140        0.9
10     0     1         0.140        1  
# ℹ 1,520 more rows

$env$soil_moist_cumulative_flux
# A tibble: 102 × 7
        time  step patch_density sum_rainfall sum_infiltration  sum_drainage sum_resource_depletion
       <dbl> <int>         <dbl>        <dbl>            <dbl>         <dbl>                  <dbl>
 1 0             1         0.140    0               0          0                           0       
 2 0.00001       2         0.140    0.0000100       0.00000998 0.00000000758               0       
 3 0.00002       3         0.140    0.0000200       0.0000200  0.0000000152                3.97e-13
 4 0.00003       4         0.140    0.0000300       0.0000299  0.0000000227                1.23e-12
 5 0.00004       5         0.140    0.0000400       0.0000399  0.0000000303                2.51e-12
 6 0.00005       6         0.140    0.0000500       0.0000499  0.0000000379                4.23e-12
 7 0.00006       7         0.140    0.0000600       0.0000599  0.0000000455                6.39e-12
 8 0.00007       8         0.140    0.0000700       0.0000698  0.0000000530                8.98e-12
 9 0.00008       9         0.140    0.0000800       0.0000798  0.0000000606                1.20e-11
10 0.0000953    10         0.140    0.0000953       0.0000950  0.0000000722                1.73e-11
# ℹ 92 more rows


$offspring_production
[1] 2.940627e-10 1.649677e-06

$net_reproduction_ratios
[1] 2.940627e-11 1.649677e-07

$p
$patch_area
[1] 1

$n_patches
[1] 1

$patch_type
[1] "meta-population"

$max_patch_lifetime
[1] 25

$strategies
$strategies[[1]]
$lma
[1] 0.07

$rho
[1] 700

$hmat
[1] 15

$omega
[1] 3.8e-05

$eta
[1] 12

$theta
[1] 0.0002141786

$a_l1
[1] 2.17

$a_l2
[1] 0.5

$a_r1
[1] 0.07

$a_b1
[1] 0.17

$r_s
[1] 5.731429

$r_b
[1] 11.46286

$r_r
[1] 217

$r_l
[1] 561

$a_y
[1] 0.7

$a_bio
[1] 0.0245

$k_l
[1] 2.699283

$k_b
[1] 0.2

$k_s
[1] 0.2

$k_r
[1] 1

$a_p1
[1] 186.4642

$a_p2
[1] 0.2655352

$a_f3
[1] 0.000114

$a_f1
[1] 1

$a_f2
[1] 50

$S_D
[1] 0.25

$a_d0
[1] 0.1

$d_I
[1] 0.01

$a_dG1
[1] 5.5

$a_dG2
[1] 20

$k_I
[1] 0.5

$vcmax_25
[1] 96

$p_50
[1] 1.85

$K_s
[1] 1

$c
[1] 1.089985

$b
[1] 2.589437

$psi_crit
[1] 7.085493

$beta1
[1] 20000

$beta2
[1] 1.5

$jmax_25
[1] 157.44

$hk_s
[1] 4

$a
[1] 0.3

$curv_fact_elec_trans
[1] 0.7

$curv_fact_colim
[1] 0.99

$var_sapwood_volume_cost
[1] 1

$nmass_l
[1] 0.013

$nmass_s
[1] 0.00198

$nmass_b
[1] 0.0034

$nmass_r
[1] 0.00335

$dmass_dN
[1] 0

$recruitment_decay
[1] 0

$control
$function_integration_rule
[1] 21

$shading_model
[1] ""

$ppa_layer_optical_depth
[1] 0.5

$ppa_layer_smoothing
[1] 0.3

$offspring_production_tol
[1] 1e-08

$offspring_production_iterations
[1] 1000

$node_gradient_eps
[1] 1e-06

$node_gradient_direction
[1] -1

$node_gradient_richardson
[1] FALSE

$node_gradient_richardson_depth
[1] 4

$ode_step_size_initial
[1] 1e-06

$ode_step_size_min
[1] 1e-06

$ode_step_size_max
[1] 5

$ode_tol_rel
[1] 1e-04

$ode_tol_abs
[1] 1e-04

$ode_a_y
[1] 1

$ode_a_dydt
[1] 0

$fixed_time_step
[1] 0

$schedule_nsteps
[1] 20

$schedule_eps
[1] 0.005

$schedule_verbose
[1] FALSE

$save_RK45_cache
[1] FALSE

$GSS_tol_abs
[1] 0.001

$vulnerability_curve_ncontrol
[1] 100

$ci_abs_tol
[1] 0.001

$ci_niter
[1] 1000

attr(,"class")
[1] "Control"

$collect_all_auxiliary
[1] FALSE

$birth_rate_x
numeric(0)

$birth_rate_y
[1] 10

$is_variable_birth_rate
[1] FALSE

attr(,"class")
[1] "TF24_Strategy"

$strategies[[2]]
$lma
[1] 0.24

$rho
[1] 700

$hmat
[1] 15

$omega
[1] 3.8e-05

$eta
[1] 12

$theta
[1] 0.0002141786

$a_l1
[1] 2.17

$a_l2
[1] 0.5

$a_r1
[1] 0.07

$a_b1
[1] 0.17

$r_s
[1] 5.731429

$r_b
[1] 11.46286

$r_r
[1] 217

$r_l
[1] 163.625

$a_y
[1] 0.7

$a_bio
[1] 0.0245

$k_l
[1] 0.32825

$k_b
[1] 0.2

$k_s
[1] 0.2

$k_r
[1] 1

$a_p1
[1] 186.4642

$a_p2
[1] 0.2655352

$a_f3
[1] 0.000114

$a_f1
[1] 1

$a_f2
[1] 50

$S_D
[1] 0.25

$a_d0
[1] 0.1

$d_I
[1] 0.01

$a_dG1
[1] 5.5

$a_dG2
[1] 20

$k_I
[1] 0.5

$vcmax_25
[1] 96

$p_50
[1] 1.85

$K_s
[1] 1

$c
[1] 1.089985

$b
[1] 2.589437

$psi_crit
[1] 7.085493

$beta1
[1] 20000

$beta2
[1] 1.5

$jmax_25
[1] 157.44

$hk_s
[1] 4

$a
[1] 0.3

$curv_fact_elec_trans
[1] 0.7

$curv_fact_colim
[1] 0.99

$var_sapwood_volume_cost
[1] 1

$nmass_l
[1] 0.013

$nmass_s
[1] 0.00198

$nmass_b
[1] 0.0034

$nmass_r
[1] 0.00335

$dmass_dN
[1] 0

$recruitment_decay
[1] 0

$control
$function_integration_rule
[1] 21

$shading_model
[1] ""

$ppa_layer_optical_depth
[1] 0.5

$ppa_layer_smoothing
[1] 0.3

$offspring_production_tol
[1] 1e-08

$offspring_production_iterations
[1] 1000

$node_gradient_eps
[1] 1e-06

$node_gradient_direction
[1] -1

$node_gradient_richardson
[1] FALSE

$node_gradient_richardson_depth
[1] 4

$ode_step_size_initial
[1] 1e-06

$ode_step_size_min
[1] 1e-06

$ode_step_size_max
[1] 5

$ode_tol_rel
[1] 1e-04

$ode_tol_abs
[1] 1e-04

$ode_a_y
[1] 1

$ode_a_dydt
[1] 0

$fixed_time_step
[1] 0

$schedule_nsteps
[1] 20

$schedule_eps
[1] 0.005

$schedule_verbose
[1] FALSE

$save_RK45_cache
[1] FALSE

$GSS_tol_abs
[1] 0.001

$vulnerability_curve_ncontrol
[1] 100

$ci_abs_tol
[1] 0.001

$ci_niter
[1] 1000

attr(,"class")
[1] "Control"

$collect_all_auxiliary
[1] FALSE

$birth_rate_x
numeric(0)

$birth_rate_y
[1] 10

$is_variable_birth_rate
[1] FALSE

attr(,"class")
[1] "TF24_Strategy"


$strategy_default
$lma
[1] 0.1978791

$rho
[1] 700

$hmat
[1] 15

$omega
[1] 3.8e-05

$eta
[1] 12

$theta
[1] 0.0002141786

$a_l1
[1] 2.17

$a_l2
[1] 0.5

$a_r1
[1] 0.07

$a_b1
[1] 0.17

$r_s
[1] 6.598684

$r_b
[1] 13.19737

$r_r
[1] 217

$r_l
[1] 198.4545

$a_y
[1] 0.7

$a_bio
[1] 0.0245

$k_l
[1] 0.4565855

$k_b
[1] 0.2

$k_s
[1] 0.2

$k_r
[1] 1

$a_p1
[1] 151.1778

$a_p2
[1] 0.2047162

$a_f3
[1] 0.000114

$a_f1
[1] 1

$a_f2
[1] 50

$S_D
[1] 0.25

$a_d0
[1] 0.1

$d_I
[1] 0.01

$a_dG1
[1] 5.5

$a_dG2
[1] 20

$k_I
[1] 0.5

$vcmax_25
[1] 96

$p_50
[1] 1.85

$K_s
[1] 1

$c
[1] 1.089985

$b
[1] 2.589437

$psi_crit
[1] 7.085493

$beta1
[1] 20000

$beta2
[1] 1.5

$jmax_25
[1] 157.44

$hk_s
[1] 4

$a
[1] 0.3

$curv_fact_elec_trans
[1] 0.7

$curv_fact_colim
[1] 0.99

$var_sapwood_volume_cost
[1] 1

$nmass_l
[1] 0.013

$nmass_s
[1] 0.00198

$nmass_b
[1] 0.0034

$nmass_r
[1] 0.00335

$dmass_dN
[1] 0

$recruitment_decay
[1] 0

$control
$function_integration_rule
[1] 21

$shading_model
[1] ""

$ppa_layer_optical_depth
[1] 0.5

$ppa_layer_smoothing
[1] 0.3

$offspring_production_tol
[1] 1e-08

$offspring_production_iterations
[1] 1000

$node_gradient_eps
[1] 1e-06

$node_gradient_direction
[1] -1

$node_gradient_richardson
[1] FALSE

$node_gradient_richardson_depth
[1] 4

$ode_step_size_initial
[1] 1e-06

$ode_step_size_min
[1] 1e-06

$ode_step_size_max
[1] 5

$ode_tol_rel
[1] 1e-04

$ode_tol_abs
[1] 1e-04

$ode_a_y
[1] 1

$ode_a_dydt
[1] 0

$fixed_time_step
[1] 0

$schedule_nsteps
[1] 20

$schedule_eps
[1] 0.005

$schedule_verbose
[1] FALSE

$save_RK45_cache
[1] FALSE

$GSS_tol_abs
[1] 0.001

$vulnerability_curve_ncontrol
[1] 100

$ci_abs_tol
[1] 0.001

$ci_niter
[1] 1000

attr(,"class")
[1] "Control"

$collect_all_auxiliary
[1] FALSE

$birth_rate_x
numeric(0)

$birth_rate_y
[1] 1

$is_variable_birth_rate
[1] FALSE

attr(,"class")
[1] "TF24_Strategy"

$node_schedule_times_default
  [1] 0.000000e+00 1.000000e-05 2.000000e-05 3.000000e-05 4.000000e-05 5.000000e-05 6.000000e-05 7.000000e-05 8.000000e-05 9.525879e-05 1.105176e-04 1.257764e-04 1.410352e-04 1.562939e-04 1.868115e-04
 [16] 2.173291e-04 2.478467e-04 2.783643e-04 3.088818e-04 3.699170e-04 4.309521e-04 4.919873e-04 5.530225e-04 6.140576e-04 7.361279e-04 8.581982e-04 9.802686e-04 1.102339e-03 1.224409e-03 1.468550e-03
 [31] 1.712690e-03 1.956831e-03 2.200972e-03 2.445112e-03 2.933394e-03 3.421675e-03 3.909956e-03 4.398237e-03 4.886519e-03 5.863081e-03 6.839644e-03 7.816206e-03 8.792769e-03 9.769331e-03 1.172246e-02
 [46] 1.367558e-02 1.562871e-02 1.758183e-02 1.953496e-02 2.344121e-02 2.734746e-02 3.125371e-02 3.515996e-02 3.906621e-02 4.687871e-02 5.469121e-02 6.250371e-02 7.031621e-02 7.812871e-02 9.375371e-02
 [61] 1.093787e-01 1.250037e-01 1.406287e-01 1.562537e-01 1.875037e-01 2.187537e-01 2.500037e-01 2.812537e-01 3.125037e-01 3.750037e-01 4.375037e-01 5.000037e-01 5.625037e-01 6.250037e-01 7.500037e-01
 [76] 8.750037e-01 1.000004e+00 1.125004e+00 1.250004e+00 1.500004e+00 1.750004e+00 2.000004e+00 2.250004e+00 2.500004e+00 3.000004e+00 3.500004e+00 4.000004e+00 4.500004e+00 5.000004e+00 6.000004e+00
 [91] 7.000004e+00 8.000004e+00 9.000004e+00 1.000000e+01 1.200000e+01 1.400000e+01 1.600000e+01 1.800000e+01 2.000000e+01 2.200000e+01 2.400000e+01

$node_schedule_times
$node_schedule_times[[1]]
  [1] 0.000000e+00 1.000000e-05 2.000000e-05 3.000000e-05 4.000000e-05 5.000000e-05 6.000000e-05 7.000000e-05 8.000000e-05 9.525879e-05 1.105176e-04 1.257764e-04 1.410352e-04 1.562939e-04 1.868115e-04
 [16] 2.173291e-04 2.478467e-04 2.783643e-04 3.088818e-04 3.699170e-04 4.309521e-04 4.919873e-04 5.530225e-04 6.140576e-04 7.361279e-04 8.581982e-04 9.802686e-04 1.102339e-03 1.224409e-03 1.468550e-03
 [31] 1.712690e-03 1.956831e-03 2.200972e-03 2.445112e-03 2.933394e-03 3.421675e-03 3.909956e-03 4.398237e-03 4.886519e-03 5.863081e-03 6.839644e-03 7.816206e-03 8.792769e-03 9.769331e-03 1.172246e-02
 [46] 1.367558e-02 1.562871e-02 1.758183e-02 1.953496e-02 2.344121e-02 2.734746e-02 3.125371e-02 3.515996e-02 3.906621e-02 4.687871e-02 5.469121e-02 6.250371e-02 7.031621e-02 7.812871e-02 9.375371e-02
 [61] 1.093787e-01 1.250037e-01 1.406287e-01 1.562537e-01 1.875037e-01 2.187537e-01 2.500037e-01 2.812537e-01 3.125037e-01 3.750037e-01 4.375037e-01 5.000037e-01 5.625037e-01 6.250037e-01 7.500037e-01
 [76] 8.750037e-01 1.000004e+00 1.125004e+00 1.250004e+00 1.500004e+00 1.750004e+00 2.000004e+00 2.250004e+00 2.500004e+00 3.000004e+00 3.500004e+00 4.000004e+00 4.500004e+00 5.000004e+00 6.000004e+00
 [91] 7.000004e+00 8.000004e+00 9.000004e+00 1.000000e+01 1.200000e+01 1.400000e+01 1.600000e+01 1.800000e+01 2.000000e+01 2.200000e+01 2.400000e+01

$node_schedule_times[[2]]
  [1] 0.000000e+00 1.000000e-05 2.000000e-05 3.000000e-05 4.000000e-05 5.000000e-05 6.000000e-05 7.000000e-05 8.000000e-05 9.525879e-05 1.105176e-04 1.257764e-04 1.410352e-04 1.562939e-04 1.868115e-04
 [16] 2.173291e-04 2.478467e-04 2.783643e-04 3.088818e-04 3.699170e-04 4.309521e-04 4.919873e-04 5.530225e-04 6.140576e-04 7.361279e-04 8.581982e-04 9.802686e-04 1.102339e-03 1.224409e-03 1.468550e-03
 [31] 1.712690e-03 1.956831e-03 2.200972e-03 2.445112e-03 2.933394e-03 3.421675e-03 3.909956e-03 4.398237e-03 4.886519e-03 5.863081e-03 6.839644e-03 7.816206e-03 8.792769e-03 9.769331e-03 1.172246e-02
 [46] 1.367558e-02 1.562871e-02 1.758183e-02 1.953496e-02 2.344121e-02 2.734746e-02 3.125371e-02 3.515996e-02 3.906621e-02 4.687871e-02 5.469121e-02 6.250371e-02 7.031621e-02 7.812871e-02 9.375371e-02
 [61] 1.093787e-01 1.250037e-01 1.406287e-01 1.562537e-01 1.875037e-01 2.187537e-01 2.500037e-01 2.812537e-01 3.125037e-01 3.750037e-01 4.375037e-01 5.000037e-01 5.625037e-01 6.250037e-01 7.500037e-01
 [76] 8.750037e-01 1.000004e+00 1.125004e+00 1.250004e+00 1.500004e+00 1.750004e+00 2.000004e+00 2.250004e+00 2.500004e+00 3.000004e+00 3.500004e+00 4.000004e+00 4.500004e+00 5.000004e+00 6.000004e+00
 [91] 7.000004e+00 8.000004e+00 9.000004e+00 1.000000e+01 1.200000e+01 1.400000e+01 1.600000e+01 1.800000e+01 2.000000e+01 2.200000e+01 2.400000e+01


$ode_times
numeric(0)

attr(,"class")
[1] "Parameters<TF24,TF24_Env>" "Parameters"               

By pulling out the ‘species’ object from our list of tidied result outputs, we can plot the size distribution for each of the species in the patch. In this plot, each line represents the height over time of a characteristic originating at a given height (given by seed mass) at each step. Note that steps in this case are not equivalent to years, where the distance between steps is instead determined by the node spacing algorithm. We can take advantage of varying line transparency in ggplot2 to plot the relative log(Density) of each characteristic the size distribution of each species in the patch.

results_tidy$species %>%
  drop_na() %>%
  plot_size_distribution()

Calculate totals by species for each step

For each species, we can integrate over the height distribution to get the total value of the state variable per \(m^2\)

data_species_tot <-
  results_tidy$species %>% integrate_over_size_distribution()

data_species_tot
# A tibble: 202 × 24
    step    time patch_density species   density min_height log_density     height    mortality fecundity area_heartwood mass_heartwood offspring_produced_survival_…¹ competition_effect height_inverse
   <int>   <dbl>         <dbl> <chr>       <dbl>      <dbl>       <dbl>      <dbl>        <dbl>     <dbl>          <dbl>          <dbl>                          <dbl>              <dbl>          <dbl>
 1     2 0.00001         0.140 1       0.0000999     0.0351    0.000320 0.00000350 0.0000000840  6.42e-31       5.59e-18       1.22e-16                       6.42e-31       0.0000000261        0.00285
 2     2 0.00001         0.140 2       0.0000999     0.0239    0.000430 0.00000239 0.0000000787  2.96e-31       2.59e-18       3.84e-17                       2.96e-31       0.0000000121        0.00418
 3     3 0.00002         0.140 1       0.000200      0.0351    0.000639 0.00000701 0.000000168   2.57e-30       2.24e-17       4.87e-16                       2.57e-30       0.0000000522        0.00570
 4     3 0.00002         0.140 2       0.000200      0.0239    0.000860 0.00000477 0.000000157   1.19e-30       1.04e-17       1.54e-16                       1.18e-30       0.0000000242        0.00837
 5     4 0.00003         0.140 1       0.000300      0.0351    0.000958 0.0000105  0.000000252   5.78e-30       5.03e-17       1.10e-15                       5.78e-30       0.0000000783        0.00854
 6     4 0.00003         0.140 2       0.000300      0.0239    0.00129  0.00000716 0.000000236   2.67e-30       2.33e-17       3.46e-16                       2.66e-30       0.0000000363        0.0126 
 7     5 0.00004         0.140 1       0.000400      0.0351    0.00128  0.0000140  0.000000336   1.03e-29       8.95e-17       1.95e-15                       1.03e-29       0.000000104         0.0114 
 8     5 0.00004         0.140 2       0.000400      0.0239    0.00172  0.00000955 0.000000315   4.74e-30       4.15e-17       6.15e-16                       4.74e-30       0.0000000484        0.0167 
 9     6 0.00005         0.140 1       0.000500      0.0351    0.00160  0.0000175  0.000000420   1.61e-29       1.40e-16       3.04e-15                       1.60e-29       0.000000131         0.0142 
10     6 0.00005         0.140 2       0.000500      0.0239    0.00215  0.0000119  0.000000394   7.41e-30       6.48e-17       9.60e-16                       7.40e-30       0.0000000605        0.0209 
# ℹ 192 more rows
# ℹ abbreviated name: ¹​offspring_produced_survival_weighted
# ℹ 9 more variables: net_mass_production_dt <dbl>, root_mass <dbl>, opt_psi_stem <dbl>, opt_root_psi <dbl>, transpiration <dbl>, E_up_ <dbl>, profit <dbl>, stom_cond_CO2 <dbl>, assimilation <dbl>

For example, the number of individuals per \(m^2\) is found by integrating the density of each characteristic at a given step over height.

data_species_tot %>%
  ggplot(aes(time, density, colour=species)) +
  geom_line()

We can use the function TF24_expand_state to capture a large number of additional state variables (e.g.leaf area)

results_tidy_expand <- results_tidy %>% expand_state()

results_tidy_expand$species
# A tibble: 10,506 × 36
   species   time  step patch_density  node density log_density height mortality fecundity area_heartwood mass_heartwood offspring_produced_s…¹ competition_effect height_inverse net_mass_production_dt
   <chr>    <dbl> <int>         <dbl> <int>   <dbl>       <dbl>  <dbl>     <dbl>     <dbl>          <dbl>          <dbl>                  <dbl>              <dbl>          <dbl>                  <dbl>
 1 1         0        1         0.140     1    24.5        3.20 0.0351  0.000840  0              0              0                      0                  0.000261           28.5               0.000901
 2 1         1e-5     2         0.140     1    24.5        3.20 0.0351  0.000841  1.29e-26       1.12e-13       2.44e-12               1.28e-26           0.000261           28.5               0.000901
 3 1         1e-5     2         0.140     2    24.5        3.20 0.0351  0.000840  0              0              0                      0                  0.000261           28.5               0.000901
 4 1         2e-5     3         0.140     1    24.5        3.20 0.0351  0.000841  2.57e-26       2.24e-13       4.87e-12               2.57e-26           0.000261           28.5               0.000902
 5 1         2e-5     3         0.140     2    24.5        3.20 0.0351  0.000840  1.29e-26       1.12e-13       2.44e-12               1.28e-26           0.000261           28.5               0.000901
 6 1         2e-5     3         0.140     3    24.5        3.20 0.0351  0.000840  0              0              0                      0                  0.000261           28.5               0.000901
 7 1         3e-5     4         0.140     1    24.5        3.20 0.0351  0.000841  3.86e-26       3.36e-13       7.31e-12               3.85e-26           0.000261           28.5               0.000902
 8 1         3e-5     4         0.140     2    24.5        3.20 0.0351  0.000840  2.57e-26       2.24e-13       4.87e-12               2.57e-26           0.000261           28.5               0.000902
 9 1         3e-5     4         0.140     3    24.5        3.20 0.0351  0.000840  1.29e-26       1.12e-13       2.44e-12               1.28e-26           0.000261           28.5               0.000902
10 1         3e-5     4         0.140     4    24.5        3.20 0.0351  0.000840  0              0              0                      0                  0.000261           28.5               0.000901
# ℹ 10,496 more rows
# ℹ abbreviated name: ¹​offspring_produced_survival_weighted
# ℹ 20 more variables: root_mass <dbl>, opt_psi_stem <dbl>, opt_root_psi <dbl>, transpiration <dbl>, E_up_ <dbl>, profit <dbl>, stom_cond_CO2 <dbl>, assimilation <dbl>, area_leaf <dbl>,
#   mass_leaf <dbl>, area_sapwood <dbl>, mass_sapwood <dbl>, area_bark <dbl>, mass_bark <dbl>, area_stem <dbl>, diameter_stem <dbl>, mass_root <dbl>, mass_live <dbl>, mass_total <dbl>,
#   mass_above_ground <dbl>

In the same way as individuals above, we can have a look at the total leaf area over time by intergrating across the size (i.e. height) distribution for a given step or time. This involves finding the product of the density of each of the characteristics present multiplied by the state variable in question (i.e. leaf area) before integrating across height.

data_species_tot <-
  results_tidy_expand$species %>%
  integrate_over_size_distribution()

data_species_tot %>%
  ggplot(aes(time, area_leaf, colour = species)) +
  geom_line()

In this case, species 1 with cheap, thin leaves briefly has a much higher total leaf area deployed comapred across the height distribution before eventually being overtaken by species with thicker, more expensive leaves.

We can convert these integrated total state variables into their respective biomass components to get an idea of how total biomass and relative biomass allocation varies through time for each species.

v <- c("mass_heartwood", "mass_sapwood", "mass_bark", "mass_leaf")

data_long <-
  data_species_tot %>%
  select(time,species, one_of(v)) %>%
  pivot_longer(cols=starts_with("mass"), names_to = "tissue")

data_long$tissue <- factor(data_long$tissue, levels = v)

species_names <- tibble(species = unique(data_long$species),
                      species_name = c("Species_1","Species_2"))

data_long %>%
  left_join(species_names) %>%
  ggplot(aes(time, value, fill=tissue)) +
  geom_area() +
  labs(x = "Patch age (yr)", y = "Above ground mass (kg/m2)") +
  theme_classic() +
  xlim(c(0,100)) +
  facet_wrap(~species_name)

Interpolate to specific times

The node spacing algorithim determines the optimum introduction times for characterstics, which means that the time values associated with steps may not correspond to known times that we might like to extract information about. For example, what the total amount of biomass per \(m^2\) will be at 50 years. By doing this, it also means that we can integrate between defined times. Let’s work with our results again by running a patch again and then tidying.

results <- run_mypatch(traits =  trait_matrix(c(0.07, 0.24), c("lma")))

results_tidy <- results

Lets interpolate our results from the run_mypatch above for timepoints 1,5,10 using the function interpolate_to_times. Note that time in units of years.

times <- c(1, 5, 10)

tidy_species_data <- results_tidy$species

tidy_species_new <- interpolate_to_times(tidy_species_data, times)

Gives interpolated values for state variables at new time points.

tidy_species_new %>% drop_na()
# A tibble: 507 × 23
   species  node  time patch_density  density   log_density height     mortality fecundity area_heartwood mass_heartwood offspring_produced_s…¹ competition_effect height_inverse net_mass_production_dt
   <chr>   <int> <dbl>         <dbl>    <dbl>         <dbl>  <dbl>         <dbl>     <dbl>          <dbl>          <dbl>                  <dbl>              <dbl>          <dbl>                  <dbl>
 1 1           1     1        0.138   1.74e+0         0.553   2.99        0.0103  1.27e-14      0.0000301         0.0460               7.05e-15               1.90         0.334                    1.82
 2 1           1     5        0.0953  1.61e-6   -396184.      7.54   396184.      2.67e- 7      0.00105           3.95                 4.32e- 8              12.1          0.133                   -2.44
 3 1           1    10        0.0298 -2.97e-9 -14577869.     10.2  14577870.      1.96e- 3      0.00277          13.4                  4.32e- 8              22.0          0.0983                  -1.49
 4 1           2     1        0.138   1.74e+0         0.553   2.99        0.0103  1.27e-14      0.0000301         0.0460               7.05e-15               1.90         0.334                    1.82
 5 1           2     5        0.0953  1.60e-6   -395951.      7.54   395951.      2.66e- 7      0.00105           3.95                 4.32e- 8              12.1          0.133                   -2.44
 6 1           2    10        0.0298 -2.95e-9 -14576643.     10.2  14576644.      1.96e- 3      0.00277          13.4                  4.32e- 8              22.0          0.0983                  -1.49
 7 1           3     1        0.138   1.74e+0         0.553   2.99        0.0103  1.27e-14      0.0000301         0.0460               7.05e-15               1.90         0.334                    1.82
 8 1           3     5        0.0953  1.62e-6   -395718.      7.54   395718.      2.66e- 7      0.00105           3.95                 4.32e- 8              12.1          0.133                   -2.44
 9 1           3    10        0.0298 -2.99e-9 -14575416.     10.2  14575417.      1.96e- 3      0.00277          13.4                  4.32e- 8              22.0          0.0983                  -1.49
10 1           4     1        0.138   1.74e+0         0.553   2.99        0.0103  1.27e-14      0.0000301         0.0460               7.04e-15               1.90         0.334                    1.82
# ℹ 497 more rows
# ℹ abbreviated name: ¹​offspring_produced_survival_weighted
# ℹ 8 more variables: root_mass <dbl>, opt_psi_stem <dbl>, opt_root_psi <dbl>, transpiration <dbl>, E_up_ <dbl>, profit <dbl>, stom_cond_CO2 <dbl>, assimilation <dbl>

We can now assess their correspondence with the values already predicted in the original results output. We can see in this case that the points (represneting the interpolated values) fall along the curves which indicates good correspondence bewteen our interpolation and the original simulation.

data_combined <-
  tidy_species_data %>%
  bind_rows(tidy_species_new) %>%
  arrange(species, node, time) %>%
  filter(node %in% seq(1, 101, by=20))

data_combined_long <-
  data_combined  %>%
  select(node, time, step, density, height, species) %>%
  pivot_longer(cols = c("density", "height"))

data_combined_long %>%
  ggplot(aes(time, value, group=node,colour=node)) +
  geom_line() +
  geom_point(data = data_combined_long %>% filter(is.na(step)), col=2) +
  scale_y_log10() +
  xlim(c(0, 20)) +
  facet_grid(name~species, scales="free") +
  theme_classic()

Interpolate to specific heights

Similar to above, we can interpolate across nodes at given time to sepcific heights, using the function interpolate_to_heights

heights <- c(1, 5, 10)

tidy_species_data <- results_tidy$species

tidy_species_new <- interpolate_to_heights(tidy_species_data, heights)

Gives predictions at new height.

tidy_species_new %>% drop_na()
# A tibble: 46 × 23
   species  time  step patch_density density log_density height mortality fecundity area_heartwood mass_heartwood offspring_produced_survival…¹ competition_effect height_inverse net_mass_production_dt
   <chr>   <dbl> <int>         <dbl>   <dbl>       <dbl>  <dbl>     <dbl>     <dbl>          <dbl>          <dbl>                         <dbl>              <dbl>          <dbl>                  <dbl>
 1 1       0.438    71         0.140    2.17       0.776      1   0.00478  1.06e-18    0.000000798       0.000354                      1.05e-18              0.212          1.00                 0.695  
 2 1       0.500    72         0.140    2.16       0.769      1   0.00482  1.06e-18    0.000000817       0.000364                      1.05e-18              0.212          1.000                0.667  
 3 1       0.563    73         0.140    2.14       0.760      1   0.00490  1.06e-18    0.000000853       0.000382                      1.05e-18              0.212          1.000                0.619  
 4 1       0.625    74         0.140    2.12       0.750      1   0.00503  1.06e-18    0.000000916       0.000414                      1.05e-18              0.212          1.00                 0.544  
 5 1       0.750    75         0.139    2.05       0.717      1   0.00569  1.06e-18    0.00000137        0.000673                      1.05e-18              0.212          0.999                0.0982 
 6 2       2.25     83         0.130    2.02       0.703      1   0.354    1.60e-18    0.00000799        0.00365                       1.13e-18              0.212          1.00                 0.158  
 7 2       2.50     84         0.127    1.86       0.619      1   0.384    1.60e-18    0.00000869        0.00400                       1.08e-18              0.212          1.000                0.0610 
 8 2       3.00     85         0.122    1.83       0.607      1   0.429    1.60e-18    0.00000898        0.00397                       9.71e-19              0.212          1.000                0.133  
 9 2       3.50     86         0.116    1.61       0.479      1   0.566    1.60e-18    0.00000897        0.00396                       9.65e-19              0.212          1.000                0.00931
10 2       4.00     87         0.110    1.32       0.276      1   0.847    1.60e-18    0.00000855        0.00358                       6.99e-19              0.212          1.000                0.167  
# ℹ 36 more rows
# ℹ abbreviated name: ¹​offspring_produced_survival_weighted
# ℹ 8 more variables: root_mass <dbl>, opt_psi_stem <dbl>, opt_root_psi <dbl>, transpiration <dbl>, E_up_ <dbl>, profit <dbl>, stom_cond_CO2 <dbl>, assimilation <dbl>

To see how good these are, plot together with old points

data_combined <-
  tidy_species_data %>%
  bind_rows(tidy_species_new) %>%
  arrange(species, time, height) %>%
  filter(step %in% c(100, 142))

data_combined_long <-
  data_combined  %>%
  select(node, time, step, height, density, mortality, area_heartwood, species) %>%
  pivot_longer(cols = c("density", "mortality", "area_heartwood"))

data_combined_long %>%
    filter(!is.na(node)) %>%
  ggplot() +
  geom_line(aes(height, value, group=step, colour=step)) +
  geom_point(data = data_combined_long %>% filter(is.na(node)), aes(x=height, y=value), col="red")+
  facet_grid(name~species, scales="free") +
  theme_classic()