| Title: | Forest Informed Neural Networks |
|---|---|
| Description: | A hybrid dynamic forest (gap) model (FINN) that can be configured as a fully mechanistic, process-based model, like classic forest gap models, or with its demographic processes (growth, mortality, regeneration) replaced by deep neural networks (DNNs), or any combination of the two. Provides functions to define a model and its mechanistic or empirical components, calibrate it to forest inventory data, and interpret the calibrated processes. FINN is implemented with the 'torch' package, which supplies GPU support and the automatic differentiation used to calibrate the model by stochastic gradient descent; no knowledge of 'torch' is required. The hybrid modeling approach is described in Pichler and Käber (2026) <doi:10.1111/2041-210x.70347>. |
| Authors: | Yannek Käber [aut, cre] (ORCID: <https://orcid.org/0000-0002-7041-9849>), Maximilian Pichler [aut] (ORCID: <https://orcid.org/0000-0003-2252-8327>) |
| Maintainer: | Yannek Käber <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.2.0 |
| Built: | 2026-09-22 15:42:52 UTC |
| Source: | https://github.com/finnverse/finn |
Calculates accumulated local effects (ALE) for the three processes
ALE( model, env = NULL, init_cohort = NULL, env_autoscale = TRUE, sim_seed = 42L, plot = TRUE, process = NULL, scale = FALSE, ... )ALE( model, env = NULL, init_cohort = NULL, env_autoscale = TRUE, sim_seed = 42L, plot = TRUE, process = NULL, scale = FALSE, ... )
model |
( |
env |
( |
init_cohort |
( |
env_autoscale |
( |
sim_seed |
( |
plot |
( |
process |
( |
scale |
( |
... |
Not supported yet. |
A list with one table per process (e.g. $growth, $mortality,
$regeneration). Each table gives the accumulated local effect (ale) of
every driver (var) across its observed range (x), per species. When
plot = TRUE the effects are also drawn.
Re-applies the constants learned by compute_env_scaling() to env.
The transformation uses the stored mean/sd only and never recomputes them
from env, so calibration and prediction use an identical transformation
(the usual pitfall of scale() inside a model formula is avoided).
apply_env_scaling(env, scaling)apply_env_scaling(env, scaling)
env |
A |
scaling |
The |
env with the scaled predictor columns, as a data.table.
# reproduce the standardization a model fit with env_autoscale = TRUE used: env <- data.frame(siteID = 1:3, year = 1L, temp = c(4, 6, 8), prec = c(700, 850, 1000)) scaling <- compute_env_scaling(env) apply_env_scaling(env, scaling)# reproduce the standardization a model fit with env_autoscale = TRUE used: env <- data.frame(siteID = 1:3, year = 1L, temp = c(4, 6, 8), prec = c(700, 850, 1000)) scaling <- compute_env_scaling(env) apply_env_scaling(env, scaling)
This function transforms arrays of species, dbh, and trees back into an observation data table.
array2obsDF(obs_array)array2obsDF(obs_array)
obs_array |
A list containing three arrays: species, dbh, and trees. |
A data.frame with columns siteID, patchID, cohortID, species, dbh, and trees.
obs_array <- list( species = array(c("A", "B"), dim = c(2, 2, 2)), dbh = array(c(10, 20, 30, 40), dim = c(2, 2, 2)), trees = array(c(100, 200, 150, 250), dim = c(2, 2, 2))) result <- array2obsDF(obs_array)obs_array <- list( species = array(c("A", "B"), dim = c(2, 2, 2)), dbh = array(c(10, 20, 30, 40), dim = c(2, 2, 2)), trees = array(c(100, 200, 150, 250), dim = c(2, 2, 2))) result <- array2obsDF(obs_array)
Summarises the conditional effects into a per process x species x variable
average marginal effect: the mean local derivative (mean_effect, an
approximate linear effect). Derived cheaply from the cached conditional
effects.
averageConditionalEffects( model, env = NULL, init_cohort = NULL, env_autoscale = TRUE, sim_seed = 42L, env_only = TRUE )averageConditionalEffects( model, env = NULL, init_cohort = NULL, env_autoscale = TRUE, sim_seed = 42L, env_only = TRUE )
model |
( |
env |
( |
init_cohort |
( |
env_autoscale |
( |
sim_seed |
( |
env_only |
( |
a named list (one entry per process) of data.frames with columns
species, variable, mean_effect.
This function calculates the basal area of a stand based on the diameter at breast height (dbh), the number of trees, and the patch size in hectares.
BA_stand(dbh, trees, patch_size_ha)BA_stand(dbh, trees, patch_size_ha)
dbh |
A torch tensor or numeric vector representing the diameter at breast height of the trees in centimeters. |
trees |
A torch tensor or numeric vector representing the number of trees. |
patch_size_ha |
A numeric value representing the size of the patch in hectares. |
The basal area of a stand is the cross-sectional area of all trees in a stand per unit area. This function calculates the basal area per ha using the formula:
The formula takes into account the diameter at breast height (dbh) in centimeters, the number of trees, and the size of the patch in hectares to calculate the basal area in square meters per hectare.
This plot illustrates the basal area for different combinations of dbh and number of trees.
Sensitivity of basal area for different combinations of dbh, number of trees to patch size.
A numeric value representing the basal area of the stand in square meters per hectare.
# BA_stand operates on torch tensors, so this runs only where the torch # backend (libtorch) is available. dbh_vec <- seq(1, 200, 1) trees_vec <- c(0:500, 10^(seq(2, 4, length.out = 20))) # Generate test data for a patch size of 0.1 patch_size <- 0.1 cohort_df1 <- expand.grid( trees_ha = trees_vec, patch_size_ha = patch_size, dbh = dbh_vec ) cohort_df1 <- data.frame( patchID = 1, cohortID = 1, species = 1, cohort_df1 ) cohort_df1$siteID <- 1:nrow(cohort_df1) cohort_df1$trees <- round(cohort_df1$trees_ha * patch_size) cohort <- CohortMat$new(obs_df = cohort_df1) cohort_df1$basal_area <- torch::as_array( BA_stand(cohort$dbh, cohort$trees, patch_size_ha = patch_size)) # View the first few rows of the resulting data frame head(cohort_df1)# BA_stand operates on torch tensors, so this runs only where the torch # backend (libtorch) is available. dbh_vec <- seq(1, 200, 1) trees_vec <- c(0:500, 10^(seq(2, 4, length.out = 20))) # Generate test data for a patch size of 0.1 patch_size <- 0.1 cohort_df1 <- expand.grid( trees_ha = trees_vec, patch_size_ha = patch_size, dbh = dbh_vec ) cohort_df1 <- data.frame( patchID = 1, cohortID = 1, species = 1, cohort_df1 ) cohort_df1$siteID <- 1:nrow(cohort_df1) cohort_df1$trees <- round(cohort_df1$trees_ha * patch_size) cohort <- CohortMat$new(obs_df = cohort_df1) cohort_df1$basal_area <- torch::as_array( BA_stand(cohort$dbh, cohort$trees, patch_size_ha = patch_size)) # View the first few rows of the resulting data frame head(cohort_df1)
This function calculates the basal area of a tree given the diameter at breast height (dbh).
BA_stem(dbh)BA_stem(dbh)
dbh |
torch.Tensor The diameter at breast height of the tree. |
torch.Tensor The basal area of the tree.
dbh = torch::torch_tensor(50) basal_area = BA_stem(dbh) print(basal_area)dbh = torch::torch_tensor(50) basal_area = BA_stem(dbh) print(basal_area)
Draw binomial counts from per-trial Bernoulli probabilities
binomial_from_bernoulli(n, p)binomial_from_bernoulli(n, p)
n |
torch.Tensor Number of Bernoulli trials per element. |
p |
torch.Tensor Success probability per element. |
torch.Tensor The number of successes per element.
Reshapes a long-format climate table into the site x year x variable array FINN uses internally, auto-detecting the time resolution from the columns.
climateDF2array(climate_dt, env_vars)climateDF2array(climate_dt, env_vars)
climate_dt |
A data.frame/data.table of climate values in long format,
with |
env_vars |
character Names of the environmental variable columns to extract. |
A numeric array of environmental values indexed by site, time and variable.
Initialize the CohortMat class
CohortMat( obs_df = NULL, dbh = NULL, trees = NULL, species = NULL, dims = c(50, 30, 10), sp = 10, device = "cpu" )CohortMat( obs_df = NULL, dbh = NULL, trees = NULL, species = NULL, dims = c(50, 30, 10), sp = 10, device = "cpu" )
obs_df |
A data frame containing columns "siteID", "patchID", "species", "dbh", and "trees". If provided, it will be used to initialize the tensors. |
dbh |
A tensor or array representing the diameter at breast height. Defaults to |
trees |
A tensor or array representing the number of trees. Defaults to |
species |
A tensor or array representing the species. Defaults to |
dims |
A numeric vector representing the dimensions of the arrays (sites, patches, cohorts). Defaults to |
sp |
An integer representing the number of species. Defaults to |
device |
A character string specifying the device to use ('cpu' or 'cuda'). Defaults to Convert the tensors to a data frame format |
An R6 class for managing cohorts of trees in forest models. This class allows for the initialization, transformation, and manipulation of cohorts represented by arrays of dbh, trees, and species.
data.table object
dbhA tensor representing the diameter at breast height for each cohort.
treesA tensor representing the number of trees in each cohort.
speciesA tensor representing the species of each cohort.
dimsA vector representing the dimensions of the arrays (sites, patches, cohorts).
spAn integer representing the number of species.
deviceA character string specifying the device to use ('cpu' or 'cuda').
dbh_rA numeric array representing the diameter at breast height in R array format.
trees_rA numeric array representing the number of trees in R array format.
species_rAn integer array representing the species in R array format.
device_rA character string specifying the device in R format ('cpu' or 'cuda').
obsDF2arraysTransforms data.table into array
This function calculates the fraction of available light for each cohort of trees based on their diameter at breast height (dbh), species, number of trees, and global parameters.
competition( dbh, species, trees, parComp, h = NULL, patch_size_ha, ba = NULL, cohortHeights = NULL, n_quantiles = 10, continuous = FALSE )competition( dbh, species, trees, parComp, h = NULL, patch_size_ha, ba = NULL, cohortHeights = NULL, n_quantiles = 10, continuous = FALSE )
dbh |
torch.Tensor Diameter at breast height for each cohort. |
species |
torch.Tensor species index for each cohort. |
trees |
torch.Tensor Number of trees in each cohort. |
parComp |
torch.Tensor Competition / height-allometry parameters per species. |
h |
torch.Tensor (Optional) Height of each cohort. Defaults to NULL. |
patch_size_ha |
numeric Patch size in hectares. |
ba |
torch.Tensor (Optional) Pre-computed basal area. Defaults to NULL. |
cohortHeights |
torch.Tensor (Optional) Pre-computed cohort heights. Defaults to NULL. |
n_quantiles |
integer Number of height quantiles used when |
continuous |
logical Use the continuous competition formulation. Defaults to FALSE. |
torch.Tensor Fraction of available light (light) for each cohort.
Computes the centering and scaling constants of every numeric environmental
predictor in env (all columns except the keys siteID and
year), so they can be re-applied unchanged to new data at prediction
time. Every supported mode is an affine map (x - center) / scale, so the
stored (variable, center, scale) table is applied by
apply_env_scaling and inverted by ALE unchanged, whatever the
mode. A predictor with (near-)zero standard deviation is given scale = 1
(centred only) to avoid division by zero.
compute_env_scaling(env, spec = TRUE)compute_env_scaling(env, spec = TRUE)
env |
A |
spec |
how to scale each predictor. One of:
|
A data.frame with columns variable, center,
scale; or NULL if there are no numeric predictors. This is the
object stored on a fitted model as model$env_scaling when it is fit
with env_autoscale = TRUE.
Computes (and caches on the model) the conditional effects — the local
derivatives of each demographic process (growth, mortality, regeneration)
with respect to its inputs, per process and per species. This is the shared
primitive behind ALE(), averageConditionalEffects() and the analytical
variable importance in summary.finn_class().
conditionalEffects( model, env = NULL, init_cohort = NULL, env_autoscale = TRUE, sim_seed = 42L )conditionalEffects( model, env = NULL, init_cohort = NULL, env_autoscale = TRUE, sim_seed = 42L )
model |
( |
env |
( |
init_cohort |
( |
env_autoscale |
( |
sim_seed |
( |
an object of class FINNconditionalEffects (also cached on model$conditional_effects).
Configures a process (growth, mortality, or regeneration) in which the entire
process equation – not just its environmental-response function – is
replaced by a deep neural network (DNN), for use as growth_process,
mortality_process, or regeneration_process in finn(). This is the
second ("Level 2") level of hybridization described by Pichler & Käber (2026);
to replace only the environmental-response function while keeping the rest of
the process mechanistic ("Level 1"), use createProcess() instead.
competition_process must always be created with createProcess(), as
the competition (light availability) process does not support full
replacement by a DNN.
createHybrid( formula = NULL, optimize = TRUE, dispersion_parameter = 1, NN = NULL, dropout = 0.3, encoder_layers = 1L, hidden = c(50L, 50L), sample_regeneration = TRUE, transformer = TRUE, emb_dim = 20L, dim_feedforward = 256L )createHybrid( formula = NULL, optimize = TRUE, dispersion_parameter = 1, NN = NULL, dropout = 0.3, encoder_layers = 1L, hidden = c(50L, 50L), sample_regeneration = TRUE, transformer = TRUE, emb_dim = 20L, dim_feedforward = 256L )
formula |
( |
optimize |
( |
dispersion_parameter |
( |
NN |
( |
dropout |
( |
encoder_layers |
( |
|
( |
|
sample_regeneration |
( |
transformer |
( |
emb_dim |
( |
dim_feedforward |
( |
The network receives the same cohort- and site-level information that the corresponding mechanistic process equation would use – diameter at breast height, number of trees, available light, species identity and the site's environmental predictors (plus the growth rate, for the mortality process) – and predicts the process output directly. Species identity is passed through a learned embedding layer rather than treated as a categorical covariate with per-species coefficients, so the network can learn low-dimensional "contrasts" between species or plant functional types (PFTs). An inverse-link function appropriate to each process is applied to the network's output: a sigmoid for mortality (a per-tree death probability, as for mortality), and an exponential for growth and regeneration (always-positive rates; for regeneration this is the mean of the negative binomial distribution from which recruits are drawn, as for regeneration).
Two network architectures are available, selected with transformer. The
default (transformer = TRUE) is a small transformer encoder (encoder_layers
layers, embedding dimension emb_dim, feed-forward dimension
dim_feedforward) that embeds each cohort, its species and the site's
environment and attends across the cohorts of a patch. Setting
transformer = FALSE instead uses a feed-forward network with hidden layers
hidden (default two layers of 50 units each) and dropout, matching the
architecture used by Pichler & Käber (2026) for the Barro Colorado Island case
study (where dropout was set to 10%).
As with createProcess(), hybrid processes are calibrated jointly,
end-to-end, with the remaining mechanistic or hybrid processes via
fit(), rather than pre-trained in isolation and plugged in afterwards.
optimize controls whether the network's weights are estimated during fitting
or kept fixed at their (random) initial values, and dispersion_parameter/
sample_regeneration have the same meaning as in createProcess() and
are only used when this object is the regeneration process.
A list of class "hybrid" containing the process definition and
associated parameters, to be passed as growth_process, mortality_process,
or regeneration_process to finn().
Pichler, M., & Käber, Y. (2026). Inferring processes within dynamic forest models using hybrid modelling. Methods in Ecology and Evolution. doi:10.1111/2041-210x.70347
growth_process <- createHybrid(formula = ~temperature + precipitation)growth_process <- createHybrid(formula = ~temperature + precipitation)
Configures one demographic process (growth, mortality, regeneration or
competition) for use as mortality_process, growth_process,
regeneration_process, or competition_process in finn(), either as a
fully mechanistic process with an explicit, interpretable functional form, or
– if hidden is supplied – as the first ("Level 1") level of hybridization
described by Pichler & Käber (2026): only the process' environmental-response
function is replaced by a small feed-forward neural network, while the rest of
the process equation remains mechanistic. To replace the entire process
equation with a neural network ("Level 2"), use createHybrid() instead.
createProcess( formula = NULL, func, initSpecies = NULL, initEnv = NULL, hidden = NULL, optimizeSpecies = FALSE, optimizeEnv = TRUE, inputNN = NULL, outputNN = NULL, dispersion_parameter = 1, NN = NULL, upper = NULL, lower = NULL, dropout = 0, sample_regeneration = TRUE, n_quantiles = 10L, continuous = FALSE, custom_parameters = NULL )createProcess( formula = NULL, func, initSpecies = NULL, initEnv = NULL, hidden = NULL, optimizeSpecies = FALSE, optimizeEnv = TRUE, inputNN = NULL, outputNN = NULL, dispersion_parameter = 1, NN = NULL, upper = NULL, lower = NULL, dropout = 0, sample_regeneration = TRUE, n_quantiles = 10L, continuous = FALSE, custom_parameters = NULL )
formula |
( |
func |
( |
initSpecies |
( |
initEnv |
( |
|
( |
|
optimizeSpecies |
( |
optimizeEnv |
( |
inputNN |
( |
outputNN |
( |
dispersion_parameter |
( |
NN |
( |
upper |
( |
lower |
( |
dropout |
( |
sample_regeneration |
( |
n_quantiles |
( |
continuous |
( |
custom_parameters |
( |
Each demographic process in FINN is the product of (i) a process equation
func that operates on the cohort state (dbh, number of trees, available
light, species) and species-specific process parameters, and (ii) a
species- and process-specific environmental-response function that maps
site-level environmental predictors to a scalar effect on the process (see
finn() for the underlying equations). createProcess() configures
both parts:
func implements the mechanistic process equation itself. The package's
default process functions (growth, mortality,
regeneration, competition) reproduce the equations described
by Pichler & Käber (2026); a custom function with the same arguments can be
passed instead to use a different functional form while keeping the process
embedded in, and jointly calibrated with, the rest of the model.
The environmental-response function is, by default (hidden = NULL), a
linear/logistic niche function with one coefficient per environmental
covariate (named in formula) and per species, comparable to a classic
species distribution model. Setting hidden to a vector of hidden-layer
sizes (e.g. c(25L)) instead replaces this function with a feed-forward
neural network, while func and the species-specific process parameters
remain mechanistic – the "Level 1" hybridization described in the paper.
formula selects which columns of the env data (passed to fit() or
predict.finn_class()) enter the environmental-response function;
initSpecies/initEnv allow supplying custom starting values for the process
parameters/environmental-response model instead of the package's random
initialization; optimizeSpecies/optimizeEnv control whether these
parameters are estimated during fit() or held fixed at their initial
values; and upper/lower set box constraints (on the natural
process-parameter scale) within which the species-specific process parameters
are constrained during optimization.
A list of class "process" containing the process definition and
associated parameters, to be passed as mortality_process, growth_process,
regeneration_process, or competition_process to finn().
Pichler, M., & Käber, Y. (2026). Inferring processes within dynamic forest models using hybrid modelling. Methods in Ecology and Evolution. doi:10.1111/2041-210x.70347
growth_process <- createProcess(formula = ~temperature + precipitation, func = growth)growth_process <- createProcess(formula = ~temperature + precipitation, func = growth)
Computes basal area in m^2 from diameter at breast height in cm:
dbh2ba(dbh)dbh2ba(dbh)
dbh |
numeric vector of diameters (cm). |
numeric vector of basal areas (m^2).
Permutes each environmental predictor of a process and measures how strongly the permutation shifts that process's predicted rate, per process and per species. Unlike the analytical ALE-variance importance, this re-simulates the model, so it captures the full dynamical response (feedback through the stand state) rather than the process response function alone. It does NOT use the conditional-effects cache.
feature_importance( model, env = NULL, init_cohort = NULL, nperm = 20L, method = c("rmse", "sobol"), seed = NULL, sim_seed = 42L, env_autoscale = TRUE, ... )feature_importance( model, env = NULL, init_cohort = NULL, nperm = 20L, method = c("rmse", "sobol"), seed = NULL, sim_seed = 42L, env_autoscale = TRUE, ... )
model |
( |
env |
( |
init_cohort |
( |
nperm |
( |
method |
( |
seed |
( |
sim_seed |
( |
env_autoscale |
( |
... |
passed to |
Two scorings via method:
"rmse" — RMSE between the unpermuted and permuted rate, in units of that
species' rate SD. Unbounded; larger = more important.
"sobol" — total-effect estimator 0.5 * mean(MSE_shift) / Var(rate);
dimensionless (only bounded in [0, 1] under independent predictors — FINN's
climate predictors are usually correlated, so treat it as relative).
Predictors are read per-process from the model formulas, so processes with
different formulas get different variable sets. Common random numbers
(sim_seed, applied to the torch simulation RNG) make the stochastic
mortality/regeneration draws shared across the reference and permuted runs,
so a driver with no effect returns ~0.
a named list (one per process) of data.frames with columns
species, variable, importance, sorted within species.
Climate per site and year for the fia_obs_dt training sites (fia_env_dt)
and the fia_obs_test holdout sites (fia_env_test), in natural units
(not standardised – fit() z-scales internally with env_autoscale).
Year 0 is the initial state and years 1 and 2 the two censuses, so every
site x year in the observation tables is covered.
fia_env_dt fia_env_testfia_env_dt fia_env_test
A data.table with 2400 rows and 8 columns: 200 sites x 3 years,
each repeated 4 times (one row per patch of the source plot, all identical
– climate is a site property). fit() only needs one row per site x year.
siteID, year
site index and year (0, 1, 2).
temp, tempmax, tempmin
annual mean, warmest-month maximum and coldest-month minimum temperature, degrees C (bioclim 1, 5, 6).
prec, precwarmq
annual and warmest-quarter precipitation, mm (bioclim 12, 18).
precseasprecipitation seasonality, coefficient of variation (bioclim 15).
An object of class data.table (inherits from data.frame) with 2400 rows and 8 columns.
Subsampled by dev/make_extdata.R (set.seed(42)) from the Oregon
FIA plots prepared in the (private) FINNverse/FINN-fia analysis repo:
tree records via the FIA
database, climate from WorldClim bioclim variables attached per plot. The
full provenance chain is in data-raw/README.md.
Other fia datasets:
fia_init_trees,
fia_obs_dt,
fia_species_dt
The year-0 tree list of the fia_obs_dt training sites (fia_init_trees)
and the fia_obs_test holdout sites (fia_init_test): what a site looked
like at the first census, from which makeInitCohorts() builds the initial
cohorts a fit or simulation starts from
(makeInitCohorts(fia_init_trees, Nspecies = 11)).
fia_init_trees fia_init_testfia_init_trees fia_init_test
A data.table with one row per tree (6039 / 6083 rows) and 28
columns. The ones FINN uses:
siteID, patchID, year
site and patch index; year is 0.
species, species_name
species index 1..11 and name, coded
as in fia_species_dt.
dbhdiameter at breast height, cm.
treestrees represented by the record (1; NA for records
that are not living stems).
treeNameunique tree identifier.
The remaining columns (siteName, patchName, OrigYear, FIA's SPCD
and STATUSCD, status, living, and the per-tree change fields such as
dbh_before, dbh_growth, mort, reg) are carried over from the
remeasurement tree list produced by makeObsData(); at year 0 most of
them are NA by construction.
An object of class data.table (inherits from data.frame) with 6083 rows and 28 columns.
Subsampled by dev/make_extdata.R (set.seed(42)) from the Oregon
FIA plots prepared in the (private) FINNverse/FINN-fia analysis repo:
tree records via the FIA
database, climate from WorldClim bioclim variables attached per plot. The
full provenance chain is in data-raw/README.md.
Other fia datasets:
fia_env_dt,
fia_obs_dt,
fia_species_dt
A ready-to-fit sample of the US Forest Inventory and Analysis (FIA) network
in Oregon: 200 training sites (fia_*_dt / fia_init_trees) and 200
disjoint holdout sites (fia_*_test) that are never seen during fitting, so
a model fit on the training tables can be scored genuinely out of sample.
The tables are already in FINN's input format – IDs resolved, species
re-indexed 1..K – so they go straight into fit() / simulateForest()
without makeObsData() or resolveSiteIDs(). The D-Fit_to_FIA and
E-Mortality vignettes are the worked examples.
fia_obs_dt fia_obs_testfia_obs_dt fia_obs_test
fia_obs_dt and fia_obs_test: one row per site x year x species
(4400 rows, 13 columns). Year 1 and 2 are the two remeasurements; the
year-0 initial state is in fia_init_trees / fia_init_test.
siteID, year
integer site index (1..200) and census (1, 2).
species, species_name
species index 1..11 and its name;
"other" pools the species outside the 98 % most abundant stems.
baliving basal area, m2 – the mean over the site's patches
(0.06 ha each; makeObsData(aggregate_by_site = TRUE)).
treesliving stems, mean per patch (hence fractional).
dbhmean diameter at breast height of living trees, cm (NA
when there are none).
growth, growth_n
relative dbh growth rate of the survivors (tree-weighted over patches) and the number of trees behind it.
n_at_risk, n_died, mort
trees alive at the start of the interval and how many of them died, summed over patches, and their ratio (the binomial mortality loss consumes the two counts).
regrecruits, trees per hectare (mean over patches).
An object of class data.table (inherits from data.frame) with 4400 rows and 13 columns.
All seven objects are data.tables. Attach data.table to use its [
semantics on them, and data.table::copy() one before modifying it by
reference (:=), as with any package dataset.
Subsampled by dev/make_extdata.R (set.seed(42)) from the Oregon
FIA plots prepared in the (private) FINNverse/FINN-fia analysis repo:
tree records via the FIA
database, climate from WorldClim bioclim variables attached per plot. The
full provenance chain is in data-raw/README.md.
fia_env_dt, fia_init_trees, fia_species_dt
Other fia datasets:
fia_env_dt,
fia_init_trees,
fia_species_dt
library(data.table) fia_obs_dt[year == 1, .(sites = uniqueN(siteID), ba = mean(ba)), by = species_name]library(data.table) fia_obs_dt[year == 1, .(sites = uniqueN(siteID), ba = mean(ba)), by = species_name]
The species index shared by all fia_* tables. Derived from the training
sites, so species is 1..11 in every table.
fia_species_dtfia_species_dt
A data.table with 11 rows and 2 columns, species (integer index)
and species_name (Latin name, or "other" for the pooled rare species).
Subsampled by dev/make_extdata.R (set.seed(42)) from the Oregon
FIA plots prepared in the (private) FINNverse/FINN-fia analysis repo:
tree records via the FIA
database, climate from WorldClim bioclim variables attached per plot. The
full provenance chain is in data-raw/README.md.
Other fia datasets:
fia_env_dt,
fia_init_trees,
fia_obs_dt
Creates a Forest Informed Neural Network (FINN), a differentiable, cohort-based dynamic forest (gap) model in the tradition of JABOWA/ForClim-style models, in which any of the four demographic processes (growth, mortality, regeneration, competition for light) can either be specified mechanistically or replaced by a deep neural network (DNN). Mechanistic and DNN-based processes are calibrated jointly, end-to-end, via gradient descent (see fit).
finn( N_species, mortality_process = NULL, growth_process = NULL, regeneration_process = NULL, competition_process = NULL, recruits_dbh = 1, recruit_obs_weight = 1, growth_period_scale = FALSE, reg_floor = 0.001 )finn( N_species, mortality_process = NULL, growth_process = NULL, regeneration_process = NULL, competition_process = NULL, recruits_dbh = 1, recruit_obs_weight = 1, growth_period_scale = FALSE, reg_floor = 0.001 )
N_species |
( |
mortality_process |
( |
growth_process |
( |
regeneration_process |
( |
competition_process |
( |
recruits_dbh |
( |
recruit_obs_weight |
( |
growth_period_scale |
( |
reg_floor |
( |
FINN represents the forest as cohorts of trees, grouped by site, patch and cohort, each characterized by diameter at breast height (dbh), number of trees, and species identity. Starting from an initial state, FINN simulates the forest forward in discrete annual time steps by sequentially applying the four demographic processes: competition (light availability, based on basal area and species-specific shading), growth (diameter increment as a function of light, size and environment), mortality (binomial death of trees as a function of growth, light, size and environment) and regeneration (recruitment of new cohorts as a function of light and environment, drawn from a negative binomial distribution). Each process additionally depends on a species- and process-specific environmental-response function that maps site-level environmental predictors to a scalar effect on the process.
Every process and its environmental-response function can be configured in one
of two ways: (1) mechanistically, with an explicit functional form and
interpretable parameters (e.g. light-response thresholds, allometric
coefficients), created via createProcess; or (2) as a hybrid process, in
which the environmental-response function or the entire process equation is
replaced by a DNN, created via createHybrid. The remaining mechanistic
processes constrain the DNN to ecologically plausible behaviour, while the DNN
absorbs misalignments and structural simplifications that would otherwise bias
the mechanistic processes; both are estimated jointly rather than calibrated in
isolation and plugged in afterwards. If a process argument is left at its
default NULL, the corresponding default mechanistic process (as described in
Pichler & Käber, 2026, Methods in Ecology and Evolution) is used.
finn() only assembles the model architecture (analogous to instantiating a
torch nn_module); none of the process or environmental-response parameters
are estimated yet. Use fit to calibrate the returned object against
observed data, and predict.finn_class/simulateForest to
simulate forest dynamics from a (fitted) model.
An object of class finn_class (a torch::nn_module): an assembled
but un-fitted FINN model, ready to pass to fit() or simulateForest().
Pichler, M., & Käber, Y. (2026). Inferring processes within dynamic forest models using hybrid modelling. Methods in Ecology and Evolution. doi:10.1111/2041-210x.70347
This function sets the seed for both R's random number generator and Torch's random number generator, ensuring reproducibility across operations that involve both R and Torch.
FINN.seed(seed)FINN.seed(seed)
seed |
An integer value to set as the seed for both R and Torch. This ensures that random operations in both environments produce consistent results. |
The function calls set.seed() to set the seed for R's random number generator and
torch::torch_manual_seed() to set the seed for Torch's random number generator. This is useful
for ensuring reproducibility in scripts that rely on both R and Torch for random operations.
This function does not return a value. It sets the seed internally for both R and Torch.
FINN.seed(123) # Now both R and Torch are seeded with 123, ensuring reproducible resultsFINN.seed(123) # Now both R and Torch are seeded with 123, ensuring reproducible results
Fit (calibrate) a FINN model end-to-end by gradient-descent optimization. All mechanistic process parameters, environmental-response coefficients and, if present, the weights of any hybrid (DNN-based) processes are estimated jointly in a single optimization, rather than calibrating each process in isolation.
fit( model, data = NULL, env, disturbance = NULL, patches = 100L, patch_size = 0.1, init_cohort = NULL, epochs = 20L, lr = 0.01, lr_scheduler = "none", lr_scheduler_params = list(), loss = c(dbh = "mse", ba = "mse", trees = "poisson", growth = "mse", mortality = "binomial", regeneration = "nbinom"), weights = "auto", optimizer = optim_ignite_adam, batchsize = NULL, device = c("cpu", "gpu"), update_step = 1L, start_time = 1L, plot_progress = TRUE, folder = NULL, checkpoints = 100L, shuffle = TRUE, record_gradients = FALSE, env_autoscale = TRUE, clip_norm = 2, ... )fit( model, data = NULL, env, disturbance = NULL, patches = 100L, patch_size = 0.1, init_cohort = NULL, epochs = 20L, lr = 0.01, lr_scheduler = "none", lr_scheduler_params = list(), loss = c(dbh = "mse", ba = "mse", trees = "poisson", growth = "mse", mortality = "binomial", regeneration = "nbinom"), weights = "auto", optimizer = optim_ignite_adam, batchsize = NULL, device = c("cpu", "gpu"), update_step = 1L, start_time = 1L, plot_progress = TRUE, folder = NULL, checkpoints = 100L, shuffle = TRUE, record_gradients = FALSE, env_autoscale = TRUE, clip_norm = 2, ... )
model |
( |
data |
( Optional columns that change how the responses are scored:
|
env |
( |
disturbance |
( |
patches |
( |
patch_size |
( |
init_cohort |
( |
epochs |
( |
lr |
( |
lr_scheduler |
( |
lr_scheduler_params |
( |
loss |
( |
weights |
( Weights must account for the raw scale of each loss, not just its
importance. The six terms are summed, and their raw magnitudes differ by
orders of magnitude: on the bundled FIA data
A useful side effect: the per-response values in Passing a |
optimizer |
( |
batchsize |
( |
device |
( |
update_step |
( |
start_time |
( |
plot_progress |
( |
folder |
( |
checkpoints |
( |
shuffle |
( |
record_gradients |
( |
env_autoscale |
|
clip_norm |
( |
... |
Additional arguments passed to |
Calibration relies on the fact that FINN is implemented in torch for R and is
therefore fully differentiable: at each simulated time step the predicted stand
variables and demographic rates are compared to the observed data through a
joint loss function, and gradients of this loss with respect to every model
parameter are obtained via automatic differentiation (backpropagation) and used
to update the parameters with a torch optimizer (Adam, torch::optim_ignite_adam,
by default).
The joint loss is the sum of per-variable losses (akin to negative
log-likelihoods), one for each of dbh, ba (basal area), trees (number of
trees), growth, mortality and regeneration. Following Pichler & Käber
(2026), reasonable choices are mean squared error ("mse", equivalent to a
Gaussian likelihood) for dbh and ba, Poisson likelihood for trees,
negative binomial ("nbinom") for regeneration, and "mse" for growth as
a continuous rate. mortality is an observed proportion of trees that died
and the model predicts it through a sigmoid, so it defaults to "binomial" —
a Bernoulli/binomial likelihood (binary cross-entropy, which admits fractional
targets). This respects the [0, 1] support and the mean-variance link of a
proportion, both of which "mse" ignores (the model also supports
"gaussian" and "poisson" as alternatives via the loss argument; see
Appendix B of the paper for details). Each loss can be weighted individually via weights, and
missing values in the observed data are masked out of the corresponding loss
term. The model is trained for epochs iterations over the (optionally
batched and shuffled) data using optimizer with learning rate lr.
Backpropagating gradients through a long simulated time series is prone to
vanishing gradients and is computationally expensive. FINN therefore uses
truncated backpropagation through time: the computational graph is detached
every update_step simulated years, gradients are accumulated and the
parameters are updated, before the simulation continues. Smaller update_step
values are faster and avoid vanishing gradients but provide a more short-sighted
learning signal; larger values let the loss integrate over a longer trajectory
at increased computational cost. start_time allows discarding an initial
burn-in period of the simulation from the loss, and checkpoints/folder
allow periodically saving the model state during training.
growth is compared as a relative rate (dbh/dbh_before - 1), which is
the model's native parameter (dbh_new = dbh * (1 + g)). Training against the
absolute diameter increment (dbh * g) instead was tested over three seeds and
was worse on every one — even when scored on the absolute scale — and about
four times more seed-variable, because it couples the growth parameter to
whichever trees happen to be present.
The fitted model, invisibly. fit() trains the model in place, so
the returned object is the one passed in, now carrying the training results
(e.g. $history, $loss_weights, $loss_baseline).
This function calculates growth based on specified parameters.
growth( dbh, species, parGrowth, pred, light, light_steepness = 10, debug = FALSE, trees = NULL )growth( dbh, species, parGrowth, pred, light, light_steepness = 10, debug = FALSE, trees = NULL )
dbh |
torch.Tensor Diameter at breast height. |
species |
torch.Tensor species of tree. |
parGrowth |
torch.Tensor Growth parameters. |
pred |
torch.Tensor Predicted values. |
light |
torch.Tensor Accumulated Light. |
light_steepness |
numeric Steepness of the light-response sigmoid. Defaults to 10. |
debug |
logical If TRUE, return the intermediate components as a list. Defaults to FALSE. |
trees |
torch.Tensor (Optional) Number of trees per cohort. Defaults to NULL. |
torch.Tensor A tensor representing the forest plot growth.
Calculate the height of a tree based on its diameter at breast height and an allometry parameter
height(dbh, parHeight)height(dbh, parHeight)
dbh |
A numeric value representing the diameter at breast height of the tree in cm. |
parHeight |
A numeric value representing the species height allometry. |
This function calculates the height of a tree based on the diameter at breast height (dbh) and a parameter parHeight.
The height is calculated using the formula:
where dbh is the diameter at breast height of the tree in cm and parHeight is an allometric species specific parameter.
All parameters of parHeight from 0 to 1 result in physiologicaly plausible heights. The range from 0.3 to 0.9 results in realistic tree heights. Values of parHeight close to 1 are physiologically almost impossible, below 0.3 is suitable for small tree species and shrubs.
A numeric value representing the calculated height of the tree.
height(30, 0.5) height(c(30), c(0.5,0.3)) height(c(30,20), c(0.5))height(30, 0.5) height(c(30), c(0.5,0.3)) height(c(30,20), c(0.5))
This function prepares obs_df in the exact format expected by
FINN::CohortMat$new() and calls it. The required long-table schema is:
siteID integer index of sites .
patchID integer index of patches within site .
species integer species code .
dbh numeric DBH in cm (either exact or binned midpoints).
trees integer count of trees in the cohort.
makeInitCohorts( init_trees, dbh_binsize = NULL, min_dbh = NULL, Nspecies, treeID_table = FALSE, singleCohortTreeNames = NULL )makeInitCohorts( init_trees, dbh_binsize = NULL, min_dbh = NULL, Nspecies, treeID_table = FALSE, singleCohortTreeNames = NULL )
init_trees |
data.table of initial trees with columns
|
dbh_binsize |
numeric or NULL. Bin width in cm. If NULL, keep exact DBH. |
min_dbh |
numeric or NULL. Lower bound for binning. If NULL, uses min DBH. |
Nspecies |
integer. Number of species levels passed to |
treeID_table |
logical. If TRUE, also return the cohort table used. |
singleCohortTreeNames |
character vector or NULL. Tree names excluded from binning and kept as single-tree cohorts. |
Aggregates initial trees into cohorts by DBH bins or exact DBH and
constructs a FINN::CohortMat object.
Internally calls:
FINN::CohortMat$new( obs_df = <data.frame with columns siteID, patchID, species, dbh, trees>, dbh = NULL, trees = NULL, species= NULL, dims = c(S, P, K), # inferred from obs_df sp = Nspecies, # passed from argument device = "cpu" )
Key fields of the resulting R6 object:
dbh, trees, species: tensors per cohort.
dims: integer vector c(S, P, K) for sites, patches, cohorts.
sp: integer number of species.
device: "cpu" or "cuda".
dbh_r, trees_r, species_r: R arrays.
obsDF2arrays: method converting obs_df into arrays.
If treeID_table = FALSE, a FINN::CohortMat object.
If TRUE, a list with:
initCohort: the CohortMat object.
init_trees: the obs_df passed to CohortMat$new().
Derives stand metrics by site/patch/species/year from tree measurements, filters sites, harmonizes years, optionally aggregates by site.
makeObsData( tree_dt, plotsize, aggregate_by_site = TRUE, minNyears = 2, fix_period_length = NULL, dbh_growth_thresh = c(-10, 50), Npatches = NULL, Nspecies = NULL, NspeciesQuantile = NULL )makeObsData( tree_dt, plotsize, aggregate_by_site = TRUE, minNyears = 2, fix_period_length = NULL, dbh_growth_thresh = c(-10, 50), Npatches = NULL, Nspecies = NULL, NspeciesQuantile = NULL )
tree_dt |
data.table of tree records with siteName, patchName, year,
treeName, species_name, dbh, |
plotsize |
numeric plot area used to scale recruitment. |
aggregate_by_site |
logical. Aggregate patches to site level. Default TRUE. |
minNyears |
integer or NULL. Keep sites with at least this many years and equal counts across patches. Default 2. |
fix_period_length |
integer or NULL. If set, drop sites whose inventory interval differs. Default NULL. |
dbh_growth_thresh |
length-2 numeric or NULL. Drop sites where any tree
|
Npatches |
integer or NULL. If set, keep only sites with exactly this many patches. |
Nspecies |
integer or NULL. Cap species to the top N (others merged to "other"). |
NspeciesQuantile |
numeric in (0,1] or NULL. Choose the smallest N covering
this fraction of individuals; overrides |
A list with:
obs_dt: observations at site or patch level. growth is
the mean relative diameter increment (dbh/dbh_before - 1), and
growth_n is how many trees that mean rests on. Both matter: means
are aggregated across patches TREE-WEIGHTED (by growth_n), because
that is the quantity the model predicts - it simulates
sum(g*trees)/sum(trees) over the whole site, so an unweighted mean
of patch means would be a different number. growth_n also travels
with the response so fit can weight the likelihood by it: 34% of
patch-level growth observations rest on a single tree while others average
30+, and the variance of a mean is . Mortality comes
back as a closed-cohort pair of counts, n_at_risk (trees alive at
the start of the interval) and n_died (how many of them were dead
at the end), plus the derived rate mort = n_died / n_at_risk
(NA where no cohort was at risk). The counts are the binomial
response; pass them to fit with mortality = "binomial".
tree_dt: input trees with added growth fields and species recode.
Mortality
mortality( dbh, species, trees, parMort, pred, light, base_steepness = 5, debug = FALSE, growth = NULL )mortality( dbh, species, trees, parMort, pred, light, base_steepness = 5, debug = FALSE, growth = NULL )
dbh |
dbh |
species |
species |
trees |
trees |
parMort |
parMort |
pred |
predictions |
light |
available light |
base_steepness |
numeric Steepness of the shade-response sigmoid. Defaults to 5. |
debug |
logical If TRUE, return the intermediate components as a list. Defaults to FALSE. |
growth |
torch.Tensor (Optional) Growth entering the mortality response; defaults to the model's current growth. |
A torch tensor of per-cohort mortality probabilities; or, if
debug = TRUE, a list of the intermediate components.
This function generates random numbers from a uniform distribution with specified low and high values and size similar to np.random.uniform in Python.
np_runif(low, high, size)np_runif(low, high, size)
low |
numeric Lower bound of the uniform distribution. |
high |
numeric Upper bound of the uniform distribution. |
size |
numeric Size of the output array. |
array A numeric array of random numbers.
np_runif(0, 1, c(2, 3))np_runif(0, 1, c(2, 3))
Convert observation data frame to arrays
obsDF2arrays(obs_dt, additional_cols = character(0))obsDF2arrays(obs_dt, additional_cols = character(0))
obs_dt |
data.frame The observation data table containing siteID, patchID, cohortID, species, dbh, and trees columns. |
additional_cols |
character vector Optional. Additional columns to be included as arrays. |
A list of arrays for species, dbh, trees, and additional columns.
obs_dt <- data.frame( siteID = c(1, 1, 2), patchID = c(1, 2, 1), cohortID = c(1, 1, 2), species = c(1, 2, 1), dbh = c(10, 20, 30), trees = c(100, 200, 150), height = c(5, 10, 15)) result <- obsDF2arrays(obs_dt, additional_cols = c("height"))obs_dt <- data.frame( siteID = c(1, 1, 2), patchID = c(1, 2, 1), cohortID = c(1, 1, 2), species = c(1, 2, 1), dbh = c(10, 20, 30), trees = c(100, 200, 150), height = c(5, 10, 15)) result <- obsDF2arrays(obs_dt, additional_cols = c("height"))
Plots the accumulated local effect (ALE) curves produced by ALE() as a
grid: one row per demographic process (growth, mortality, regeneration) and
one column per environmental predictor. When several species are present,
their curves are overlaid in the same panel as one coloured line each.
## S3 method for class 'FINNale' plot(x, process = NULL, scale = FALSE, ...)## S3 method for class 'FINNale' plot(x, process = NULL, scale = FALSE, ...)
x |
an object of class |
process |
( |
scale |
( |
... |
currently ignored. |
invisibly, the ggplot object.
This function takes prediction arrays from a model output and converts them into data frames. The data frames can be returned in either 'wide' or 'long' format. The function processes site-level, patch-level, and cohort-level predictions.
pred2DF(pred, format = "wide")pred2DF(pred, format = "wide")
pred |
A list containing prediction arrays for site-level, patch-level,
and cohort-level data. Each element of |
format |
A character string indicating the desired format of the output data frames. Must be either "wide" (default) or "long". |
The pred argument should be a list containing at least a Predictions element,
which itself is a list of arrays. The arrays represent predictions for different
metrics such as dbh/ba, tree counts, AL (aboveground live biomass), growth rates,
mortality rates, and regeneration rates for sites, patches, or cohorts. The
dimensionality of the arrays should correspond to different factors, such as
siteID, year, species, and optionally patch or cohortID.
The function first converts each prediction array into a data frame, properly
naming and converting the relevant dimensions. It then merges these data frames
by common identifiers such as siteID, year, species, patch, and cohortID.
Depending on the format parameter, the data frames are returned in either a
wide format (one row per site/patch/cohort per year with multiple columns for
different metrics) or a long format (one row per site/patch/cohort per year
per metric).
A list of data frames. The list may contain up to three elements:
site, patch, and cohort, corresponding to the processed site-level,
patch-level, and cohort-level predictions, respectively.
# a minimal prediction object of the shape pred2DF() expects # (normally produced by predict()/simulateForest()): metrics <- c("dbh", "ba", "trees", "growth", "mort", "reg", "r_mean_ha") arr <- array(1, dim = c(1, 2, 3), dimnames = list(1, 1:2, 1:3)) # [site, year, species] pred <- list(Predictions = list(Site = stats::setNames( lapply(metrics, function(m) arr), metrics))) result <- pred2DF(pred, format = "long") head(result$site)# a minimal prediction object of the shape pred2DF() expects # (normally produced by predict()/simulateForest()): metrics <- c("dbh", "ba", "trees", "growth", "mort", "reg", "r_mean_ha") arr <- array(1, dim = c(1, 2, 3), dimnames = list(1, 1:2, 1:3)) # [site, year, species] pred <- list(Predictions = list(Site = stats::setNames( lapply(metrics, function(m) arr), metrics))) result <- pred2DF(pred, format = "long") head(result$site)
Predict from a FINN model
## S3 method for class 'finn_class' predict( object, env, disturbance = NULL, patches = 100L, patch_size = 0.1, init_cohort = NULL, device = c("cpu", "gpu"), return_cohorts = FALSE, debug = FALSE, ... )## S3 method for class 'finn_class' predict( object, env, disturbance = NULL, patches = 100L, patch_size = 0.1, init_cohort = NULL, device = c("cpu", "gpu"), return_cohorts = FALSE, debug = FALSE, ... )
object |
( |
env |
( |
disturbance |
( |
patches |
( |
patch_size |
( |
init_cohort |
( |
device |
( |
return_cohorts |
Controls whether the raw per-cohort state is returned in
addition to the aggregated site output. Storing cohorts every timestep is
expensive, so the default is |
debug |
( |
... |
Advanced options forwarded to the internal simulator, chiefly
|
Simulate from a (fitted) FINN model. This is an S3 method for the
stats::predict generic, so it is dispatched as predict(model, ...).
A named list of predictions. $long$site (and $wide$site) give the
site-level results (columns siteID, year, species, variable,
value). When return_cohorts is set, $long$cohort / $wide$cohort add
the per-cohort state (dbh, trees, species, growth g, mortality m,
...) for the requested timesteps.
This function calculates the regeneration of forest patches based on species information, regeneration parameters, prediction values, and available light.
regeneration(species, parReg, pred, light, debug = FALSE)regeneration(species, parReg, pred, light, debug = FALSE)
species |
torch.Tensor species information. |
parReg |
torch.Tensor Regeneration parameters. 0 <= parReg <= 1 This parameter denotes the fraction of light needed for a species to regenerate. In general low values for high regeneration and high values for low regeneration. |
pred |
torch.Tensor Prediction values. |
light |
torch.Tensor Available light variable for calculation. |
debug |
logical If TRUE, return the intermediate components as a list. Defaults to FALSE. |
torch.Tensor Regeneration values for forest patches.
A drop-in alternative to regeneration that caps the otherwise
unbounded mean recruitment with a Beverton-Holt density dependence,
recruits = K * m / (K + m), where m is the mechanistic mean recruitment
(identical to regeneration) and K is a fitted carrying capacity
(stems ha^-1 step^-1). As m grows, recruitment saturates towards K.
regeneration_saturation(species, parReg, pred, light, debug = FALSE)regeneration_saturation(species, parReg, pred, light, debug = FALSE)
species |
torch.Tensor species information. |
parReg |
torch.Tensor Regeneration parameters. 0 <= parReg <= 1 This parameter denotes the fraction of light needed for a species to regenerate. In general low values for high regeneration and high values for low regeneration. |
pred |
torch.Tensor Prediction values. |
light |
torch.Tensor Available light variable for calculation. |
debug |
logical If TRUE, return the intermediate components as a list. Defaults to FALSE. |
Unlike the other process functions, whose species parameters come from the
built-in par_regeneration table, this function needs one extra parameter,
the log carrying capacity reg_logK (K = exp(reg_logK)). Declare it with
createProcess(custom_parameters = list(reg_logK = ...)); FINN then registers
it as a trainable parameter, optimises it in fit() and round-trips it
through torch_save/torch_load. The length of the init decides the
resolution: a length-1 init gives one shared K for all species; a
length-N_species init gives a per-species K. Read the fitted value back
with exp(as.numeric(m$reg_logK)).
torch.Tensor Saturated regeneration values for forest patches.
## Not run: m <- finn( N_species = Nsp, regeneration_process = createProcess( ~ temp + prec, FINN::regeneration_saturation, custom_parameters = list(reg_logK = rep(log(800), Nsp)), # per-species K optimizeSpecies = TRUE, optimizeEnv = TRUE) ) ## End(Not run)## Not run: m <- finn( N_species = Nsp, regeneration_process = createProcess( ~ temp + prec, FINN::regeneration_saturation, custom_parameters = list(reg_logK = rep(log(800), Nsp)), # per-species K optimizeSpecies = TRUE, optimizeEnv = TRUE) ) ## End(Not run)
Joins tree, environment, and observation tables; assigns integer indices for site, patch, period; standardizes species coding; and returns aligned data plus optional initial cohorts.
resolveSiteIDs(tree_dt, env_dt, obs_dt, createInitCohorts = TRUE)resolveSiteIDs(tree_dt, env_dt, obs_dt, createInitCohorts = TRUE)
tree_dt |
data.table with tree-level data including siteName, patchName, year, species_name, dbh, status, living, and optional trees. |
env_dt |
data.table with environment data including siteName and year. |
obs_dt |
data.table with observations including siteName, patchName, year,
species_name, and stand metrics (ba, dbh, trees, growth, mort, n_at_risk,
n_died, reg), as returned by |
createInitCohorts |
logical. If TRUE, build FINN initial cohorts from
trees with |
A list with:
siteID_dt: site/patch/year index map.
tree_dt: tree table with indices and standardized species.
env_dt: environment table with indices.
obs_dt: site-aggregated observations by species.
obs_dt_patches: patch-level observations by species.
species_dt: species lookup (species, species_name).
initCohorts (optional): FINN CohortMat object.
initCohort_dt (optional): trees used to build cohorts.
This function generates cohort data for tree populations using the Weibull distribution based on specified tree counts, diameter at breast height (DBH) shape, and scale parameters.
rweibull_cohorts( trees = NULL, dbh_shape = NULL, dbh_scale = NULL, dbh_class_range = 1, siteID = 1, patchID = 1, species = 1 )rweibull_cohorts( trees = NULL, dbh_shape = NULL, dbh_scale = NULL, dbh_class_range = 1, siteID = 1, patchID = 1, species = 1 )
trees |
Integer vector specifying the number of trees for each cohort. If a single integer is provided, it will be replicated for each draw. |
dbh_shape |
Numeric vector specifying the shape parameters of the Weibull distribution for DBH for each cohort. If a single numeric value is provided, it will be replicated for each draw. |
dbh_scale |
Numeric vector specifying the scale parameters of the Weibull distribution for DBH for each cohort. If a single numeric value is provided, it will be replicated for each draw. |
dbh_class_range |
Numeric value specifying the range of DBH classes. Default is 1. |
siteID |
Integer vector specifying the site ID for each cohort. If a single integer is provided, it will be replicated for each draw. Default is 1. |
patchID |
Integer vector specifying the patch ID for each cohort. If a single integer is provided, it will be replicated for each draw. Default is 1. |
species |
Integer vector specifying the species ID for each cohort. If a single integer is provided, it will be replicated for each draw. Default is 1. |
The function generates cohort data by drawing samples from the Weibull distribution for each cohort based on the specified shape and scale parameters. The resulting DBH values are binned into classes, and the cohort data is generated accordingly.
A data frame containing the cohort data with columns for site ID, patch ID, cohort ID, species, number of trees, and DBH.
obs_df <- rweibull_cohorts( trees = c(300, 10), dbh_shape = c(3, 3), dbh_scale = c(1, 50), dbh_class_range = 0.1, siteID = c(1, 1), patchID = c(1, 1), species = c(3, 4) ) head(obs_df)obs_df <- rweibull_cohorts( trees = c(300, 10), dbh_shape = c(3, 3), dbh_scale = c(1, 50), dbh_class_range = 0.1, siteID = c(1, 1), patchID = c(1, 1), species = c(3, 4) ) head(obs_df)
Simulate
simulateForest( model, env, disturbance = NULL, patches = 100L, patch_size = 0.1, init_cohort = NULL, device = c("cpu", "gpu"), return_cohorts = FALSE, debug = FALSE, ... )simulateForest( model, env, disturbance = NULL, patches = 100L, patch_size = 0.1, init_cohort = NULL, device = c("cpu", "gpu"), return_cohorts = FALSE, debug = FALSE, ... )
model |
( |
env |
( |
disturbance |
( |
patches |
( |
patch_size |
( |
init_cohort |
( |
device |
( |
return_cohorts |
Controls whether the raw per-cohort state is returned in
addition to the aggregated site output. Storing cohorts every timestep is
expensive, so the default is |
debug |
( |
... |
Advanced options forwarded to the internal simulator, chiefly
|
Simulate from a fitted FINN model. This is a thin, backwards-compatible
alias for predict.finn_class; new code should call
predict(model, ...) directly.
A named list of simulation results. The patch-averaged, stand-level
state variables and demographic rates are in $long$site / $wide$site
(long format: siteID, year, species, variable, value). When
return_cohorts is set, $long$cohort / $wide$cohort hold the raw
per-cohort state for the requested timesteps.
Prints, per process and per species, the environmental variable importance
and the average conditional effects. Both are derived from the model's
conditional effects, which are computed once and cached — so if ALE() was
already run (or a previous summary()), the default path needs no further
simulation.
## S3 method for class 'finn_class' summary( object, env = NULL, init_cohort = NULL, importance = c("ale", "permutation"), env_autoscale = TRUE, sim_seed = 42L, nperm = 20L, scale = TRUE, ... )## S3 method for class 'finn_class' summary( object, env = NULL, init_cohort = NULL, importance = c("ale", "permutation"), env_autoscale = TRUE, sim_seed = 42L, nperm = 20L, scale = TRUE, ... )
object |
( |
env, init_cohort
|
( |
importance |
( |
env_autoscale |
( |
sim_seed |
( |
nperm |
( |
scale |
( |
... |
passed through (e.g. to |
invisibly, a list with importance, average_conditional_effects, and method.