diff --git a/NAMESPACE b/NAMESPACE
index 242b860..b8d2412 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -21,6 +21,8 @@ export(silv_dominant_height)
export(silv_lorey_height)
export(silv_ntrees_ha)
export(silv_predict_biomass)
+export(silv_predict_biomass_auto)
+export(silv_predict_biomass_components)
export(silv_predict_height)
export(silv_sample_size)
export(silv_sample_size_simple)
diff --git a/R/predict-biomass.R b/R/predict-biomass.R
index 26f9313..b14b2fe 100644
--- a/R/predict-biomass.R
+++ b/R/predict-biomass.R
@@ -22,12 +22,19 @@ ModelBiomass <- S7::new_class(
#' Computes the biomass of a tree species using species-specific allometric
#' equations (in kg). Currently, only equations for Spain are available.
#'
-#' @param diameter A numeric vector of tree diameters (in cm).
+#' @param diameter A numeric vector of tree diameters at breast height (in cm).
#' @param height A numeric vector of tree heights (in m).
#' @param model A function. A function with the structure \code{eq_biomass_*()} with
#' additional arguments depending on the model used.
#' @param ntrees An optional numeric value indicating the number of trees in
#' this diameter-height class. Defaults to 1 if \code{NULL}.
+#' @param rcd An optional numeric vector of root collar diameters (in cm). Required
+#' for \code{\link{eq_biomass_menendez_2022}}, which uses root collar diameter
+#' instead of diameter at breast height. Defaults to \code{diameter} if \code{NULL}.
+#' @param bp An optional numeric vector of biomass packing values (in m\ifelse{html}{\out{3}}{$^3$}). Required
+#' for a subset of species in \code{\link{eq_biomass_menendez_2022}} (e.g.
+#' \emph{Pinus halepensis}, \emph{Pinus nigra}, \emph{Quercus suber},
+#' \emph{Evergreen broadleaves}).
#' @param quiet A logical value. If \code{TRUE}, suppresses any informational messages.
#'
#' @return A numeric vector
@@ -40,6 +47,14 @@ ModelBiomass <- S7::new_class(
#'
#' - **[eq_biomass_ruiz_peinado_2011()]**: Developed for softwood species in Spain.
#' - **[eq_biomass_ruiz_peinado_2012()]**: Developed for hardwood species in Spain.
+#' - **[eq_biomass_montero_2005()]**: Developed for 35 Spanish species.
+#' - **[eq_biomass_dieguez_aranda_2009()]**: Developed for 7 Galician species.
+#' - **[eq_biomass_manrique_2017()]**: Developed for *Quercus petraea* and *Quercus pyrenaica*.
+#' - **[eq_biomass_menendez_2022()]**: Developed for young plantations (< 30 years) of 18
+#' Spanish species. Uses `rcd` (root collar diameter) instead of `diameter`; some species
+#' require `bp` (biomass packing) instead of `rcd`.
+#' - **[eq_biomass_cudjoe_2024()]**: Developed for *Pinus sylvestris* and *Quercus petraea*
+#' in Castille and León, Spain.
#'
#' Users can check the list of supported species and their corresponding components
#' in [biomass_models].
@@ -62,52 +77,69 @@ silv_predict_biomass <- function(
height = NULL,
model,
ntrees = NULL,
+ rcd = NULL,
+ bp = NULL,
quiet = FALSE) {
# 0. Handle errors and setup
- ## 0.2. Ensure species has same length as the rest, and ntrees = 1 when ntrees = NULL
+ ## 0.1. Default rcd to diameter if not provided
+ if (is.null(rcd)) rcd <- diameter
+ ## 0.2. Ensure ntrees = 1 when ntrees = NULL
if (is.null(ntrees)) ntrees <- rep(1, length(diameter))
- # species <- rep_len(species, length(diameter))
+ ## 0.3. Default height to NA_real_ if NULL
+ if (is.null(height)) height <- rep(NA_real_, length(diameter))
# 1. Define a helper function to calculate biomass for a single tree
- if (model@equation %in% c(
- "ruiz-peinado-2011",
- "ruiz-peinado-2012")
- ) {
-
- ## function to calculate biomass
- calc_biomass <- function(d, h, n, sp) {
- ## select expression based on species
- selected_expr <- model@expression[model@expression$species == sp, ]$expression
- ## if there are multiple expressions (AGB, BGB)
- selected_expr <- paste0(
- "(",
- paste(selected_expr, collapse = " + "),
- ")"
- )
- ##
- if (grepl("h", selected_expr)) {
- f1 <- function(d, h) eval(parse(text = selected_expr))
- biomass <- f1(d, h) * n
- } else {
- f2 <- function(d) eval(parse(text = selected_expr))
- biomass <- f2(d) * n
- }
-
- ## create a table with the outputs
- biomass_tbl <- data.frame(
- biomass = ifelse(model@params$return_rmse, model@params$rmse, biomass),
- citation = model@url,
- obs = model@obs
+ calc_biomass <- function(d, h, n, sp, rcd_val, bp_val) {
+ ## select expression based on species
+ selected_rows <- model@expression$species == sp
+ selected_expr <- model@expression[selected_rows, ]$expression
+ selected_expr <- selected_expr[!is.na(selected_expr)]
+ if (length(selected_expr) == 0) {
+ cli::cli_abort(
+ "The selected model contains no valid expression for species {.val {sp}}."
)
+ }
+ ## if there are multiple expressions (AGB, BGB)
+ selected_expr <- paste0(
+ "(",
+ paste(selected_expr, collapse = " + "),
+ ")"
+ )
- return(biomass_tbl)
+ ## evaluate in a named environment that exposes all possible variable names
+ eval_env <- list(d = d, h = h, rcd = rcd_val, bp = bp_val)
+ biomass <- eval(parse(text = selected_expr), envir = eval_env) * n
+
+ metric <- biomass
+ if (isTRUE(model@params$return_rmse)) {
+ metric <- model@params$rmse[selected_rows]
+ metric <- metric[!is.na(metric)]
+ if (length(metric) != 1) {
+ cli::cli_abort(
+ "RMSE is only available when the selected species-component combination resolves to a single equation."
+ )
+ }
+ } else if (isTRUE(model@params$return_r2)) {
+ metric <- model@params$r2[selected_rows]
+ metric <- metric[!is.na(metric)]
+ if (length(metric) != 1) {
+ cli::cli_abort(
+ "R2 is only available when the selected species-component combination resolves to a single equation."
+ )
+ }
}
- }
+ data.frame(
+ biomass = metric,
+ citation = model@url,
+ obs = model@obs
+ )
+ }
# 2. Vectorize the function to handle multiple inputs
- biomass_mat <- mapply(calc_biomass, diameter, height, ntrees, model@species)
+ bp_vec <- if (is.null(bp)) rep(NA_real_, length(diameter)) else bp
+ biomass_mat <- mapply(calc_biomass, diameter, height, ntrees, model@species, rcd, bp_vec)
biomass_df <- biomass_mat |>
t() |>
@@ -152,15 +184,44 @@ silv_predict_biomass <- function(
#' @export
#'
#' @details
-#' Users can check the list of supported species and their corresponding components
-#' in [biomass_models].
+#'
+#' **Supported species (10):**
+#'
+#' *Abies alba*, *Abies pinsapo*, *Juniperus thurifera*, *Pinus canariensis*,
+#' *Pinus halepensis*, *Pinus nigra*, *Pinus pinaster*, *Pinus pinea*,
+#' *Pinus sylvestris*, *Pinus uncinata*
+#'
+#' **Available components:**
+#'
+#' Aboveground / belowground groups (summed automatically):
+#'
+#' * `"AGB"` — total aboveground biomass
+#' * `"BGB"` — total belowground biomass (roots)
+#' * `"tree"` — total tree biomass (AGB + BGB)
+#'
+#' Tree structural groups:
+#'
+#' * `"stem"` — stem wood
+#' * `"branches"` — all branch fractions combined
+#' * `"roots"` — roots (equivalent to BGB)
+#'
+#' Individual tree components (species availability varies):
+#'
+#' * `"thick branches"` — branches > 7 cm
+#' * `"thick and medium branches"` — branches > 2 cm
+#' * `"medium branches"` — branches 2–7 cm
+#' * `"small branches and leaves"` — branches < 2 cm including leaves/needles
+#'
+#' Note that *Abies pinsapo* does not have a separate BGB equation (requesting `"BGB"` or `"roots"` will fail, though `"tree"` still works via its pre-summed formula).
+#'
+#' Users can check all available species and components in the [biomass_models] dataset provided by the library.
#'
#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()], [eq_biomass_dieguez_aranda_2009()],
#' [eq_biomass_ruiz_peinado_2012()], [eq_biomass_manrique_2017()], [eq_biomass_menendez_2022()], [eq_biomass_cudjoe_2024()]
#'
#' @examples
-#' ## get model parameters for silv_predict_biomass
-#' eq_biomass_ruiz_peinado_2011("Pinus pinaster")
+#' ## Aboveground biomass for Pinus pinaster
+#' eq_biomass_ruiz_peinado_2011("Pinus pinaster", "AGB")
eq_biomass_ruiz_peinado_2011 <- function(species, component = "stem", return_rmse = FALSE) {
# 0. Handle errors
@@ -184,10 +245,15 @@ eq_biomass_ruiz_peinado_2011 <- function(species, component = "stem", return_rms
if (nrow(sel_component) == 0) sel_component <- sel_species[sel_species$tree_component %in% component, ]
}
## 1.4. Check if there's a matching model
- if (nrow(sel_component) == 0) cli::cli_abort(
- "The combination of species-component-model doesn't match any available option.
- Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
- )
+ if (nrow(sel_component) == 0) {
+ if (component %in% c("BGB", "roots") && "Abies pinsapo" %in% species) {
+ cli::cli_abort("Model {.val ruiz-peinado-2011} does not include BGB equations for {.val Abies pinsapo}.")
+ }
+ cli::cli_abort(
+ "The combination of species-component-model doesn't match any available option.
+ Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
+ )
+ }
# 2. Return
ModelBiomass(
@@ -230,16 +296,49 @@ eq_biomass_ruiz_peinado_2011 <- function(species, component = "stem", return_rms
#' @export
#'
#' @details
-#' Users can check the list of supported species and their corresponding components
-#' in [biomass_models].
#'
-#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()], [eq_biomass_dieguez_aranda_2009()]
-#' [eq_biomass_ruiz_peinado_2011()], [eq_biomass_manrique_2017()], [eq_biomass_menendez_2022()],
+#' **Supported species (13):**
+#'
+#' *Alnus glutinosa*, *Castanea sativa*, *Ceratonia siliqua*, *Eucalyptus globulus*,
+#' *Fagus sylvatica*, *Fraxinus angustifolia*, *Olea europaea*, *Populus x euramericana*,
+#' *Quercus canariensis*, *Quercus faginea*, *Quercus ilex*, *Quercus pyrenaica*,
+#' *Quercus suber*
+#'
+#' **Available components:**
+#'
+#' Aboveground / belowground groups (summed automatically):
+#'
+#' * `"AGB"` — total aboveground biomass
+#' * `"BGB"` — total belowground biomass (roots)
+#' * `"tree"` — total tree biomass (AGB + BGB)
+#'
+#' Tree structural groups:
+#'
+#' * `"stem"` — stem wood
+#' * `"branches"` — all branch fractions combined
+#' * `"roots"` — roots (equivalent to BGB)
+#'
+#' Individual tree components (species availability varies):
+#'
+#' * `"stem and thick branches"` — stem together with branches > 7 cm
+#' * `"thick branches"` — branches > 7 cm
+#' * `"thick and medium branches"` — branches > 2 cm
+#' * `"medium branches"` — branches 2–7 cm
+#' * `"small branches"` — branches 0.5–2 cm
+#' * `"small branches and leaves"` — branches < 2 cm including leaves
+#' * `"medium branches, small branches and leaves"` — branches < 7 cm including leaves
+#'
+#' Note that *Eucalyptus globulus* does not have a separate BGB equation (requesting `"BGB"` or `"roots"` will fail, though `"tree"` still works via its pre-summed formula).
+#'
+#' Users can check all available species and components in the [biomass_models] dataset provided by the library.
+#'
+#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()], [eq_biomass_dieguez_aranda_2009()],
+#' [eq_biomass_ruiz_peinado_2011()], [eq_biomass_manrique_2017()], [eq_biomass_menendez_2022()],
#' [eq_biomass_cudjoe_2024()]
#'
#' @examples
-#' ## get model parameters for silv_predict_biomass
-#' eq_biomass_ruiz_peinado_2012("Quercus suber")
+#' ## Aboveground biomass for Quercus suber
+#' eq_biomass_ruiz_peinado_2012("Quercus suber", "AGB")
eq_biomass_ruiz_peinado_2012 <- function(species, component = "stem", return_rmse = FALSE) {
# 0. Handle errors
@@ -263,10 +362,15 @@ eq_biomass_ruiz_peinado_2012 <- function(species, component = "stem", return_rms
if (nrow(sel_component) == 0) sel_component <- sel_species[sel_species$tree_component %in% component, ]
}
## 1.4. Check if there's a matching model
- if (nrow(sel_component) == 0) cli::cli_abort(
- "The combination of species-component-model doesn't match any available option.
- Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
- )
+ if (nrow(sel_component) == 0) {
+ if (component %in% c("BGB", "roots") && "Eucalyptus globulus" %in% species) {
+ cli::cli_abort("Model {.val ruiz-peinado-2012} does not include BGB equations for {.val Eucalyptus globulus}.")
+ }
+ cli::cli_abort(
+ "The combination of species-component-model doesn't match any available option.
+ Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
+ )
+ }
# 2. Return
ModelBiomass(
@@ -313,36 +417,47 @@ eq_biomass_ruiz_peinado_2012 <- function(species, component = "stem", return_rms
#'
#' @details
#'
-#' There are seven species included in this model: *Pinus pinaster, Pinaster radiata, Pinus*
-#' *sylvestris, Eucalyptus globulus, Eucalyptus nitens, Quercus robur*, and *Betula alba*
+#' **Supported species (7):**
#'
-#' The tree components are divided into groups, and any of them can be introduced in the
-#' component argument:
+#' *Betula alba*, *Eucalyptus globulus*, *Eucalyptus nitens*, *Pinus pinaster*,
+#' *Pinus radiata*, *Pinus sylvestris*, *Quercus robur*
#'
-#' * **AGB**: all aboveground biomass components
-#' * **BGB**: all belowground biomass compoponents
-#' * **tree**: total tree biomass includying AGB and BGB
+#' **Available components:**
#'
-#' Then we have the second group of components, which are related to tree groups:
+#' Aboveground / belowground groups (summed automatically):
#'
-#' * **stem**: includes the stem and bark
-#' * **branches**: includes all branches
-#' * **roots**: includes the roots (same as BGB)
+#' * `"AGB"` — total aboveground biomass
+#' * `"BGB"` — total belowground biomass (roots)
#'
-#' Finally, we have the last level, which includes tree components (not all of them
-#' are available for all species): stem, bark, thick branches (>7cm), medium branches (2-7cm),
-#' thin branches (0.5-2cm), twigs (<0.5cm), dry branches, leaves, roots. In some species,
-#' there's "stem and thick branches", instead of two groups.
+#' Tree structural groups:
#'
-#' Users can check the list of supported species and their corresponding components
-#' in [biomass_models].
+#' * `"stem"` — stem fraction(s)
+#' * `"branches"` — all branch fractions combined
+#' * `"roots"` — roots (equivalent to BGB)
+#'
+#' Individual tree components (species availability varies):
+#'
+#' * `"stem and thick branches"` — stem together with thickest branches
+#' * `"thick branches"` — branches > 7 cm
+#' * `"medium branches"` — branches 2–7 cm
+#' * `"small branches"` — branches 0.5–2 cm
+#' * `"twigs"` — branches < 0.5 cm
+#' * `"dry branches"` — dead attached branches
+#' * `"leaves"` — foliage (including needles)
+#' * `"roots"` — coarse roots
+#'
+#'
+#' Note that total-tree equations (`"tree"` / `"all"`) are not available for this model.
+#' Also, *Eucalyptus globulus*, *Eucalyptus nitens*, and *Pinus pinaster* lack BGB equations (requesting `"BGB"` or `"roots"` will fail).
+#'
+#' Users can check all available species and components in the [biomass_models] dataset provided by the library.
#'
#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()],
#' [eq_biomass_ruiz_peinado_2011()], [eq_biomass_ruiz_peinado_2012()], [eq_biomass_manrique_2017()],
#' [eq_biomass_menendez_2022()], [eq_biomass_cudjoe_2024()]
#'
#' @examples
-#' ## get model parameters for silv_predict_biomass
+#' ## Aboveground biomass for Pinus pinaster
#' eq_biomass_dieguez_aranda_2009("Pinus pinaster", "AGB")
eq_biomass_dieguez_aranda_2009 <- function(species, component = "stem", return_r2 = FALSE, return_rmse = FALSE) {
@@ -368,10 +483,18 @@ eq_biomass_dieguez_aranda_2009 <- function(species, component = "stem", return_r
if (nrow(sel_component) == 0) sel_component <- sel_species[sel_species$tree_component %in% component, ]
}
## 1.4. Check if there's a matching model
- if (nrow(sel_component) == 0) cli::cli_abort(
- "The combination of species-component-model doesn't match any available option.
- Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
- )
+ if (nrow(sel_component) == 0) {
+ if (component %in% c("tree", "all")) {
+ cli::cli_abort("Model {.val dieguez-aranda-2009} does not include total-tree ('tree' / 'all') equations.")
+ }
+ if (component %in% c("BGB", "roots") && species %in% c("Eucalyptus globulus", "Eucalyptus nitens", "Pinus pinaster")) {
+ cli::cli_abort("Model {.val dieguez-aranda-2009} does not include BGB equations for {.val {species}}.")
+ }
+ cli::cli_abort(
+ "The combination of species-component-model doesn't match any available option.
+ Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
+ )
+ }
# 2. Return
ModelBiomass(
@@ -417,35 +540,55 @@ eq_biomass_dieguez_aranda_2009 <- function(species, component = "stem", return_r
#'
#' @details
#'
-#' There are 35 species included in the model.
+#' **Supported species (35):**
#'
-#' The tree components are divided into groups, and any of them can be introduced in the
-#' component argument:
+#' *Abies alba*, *Abies pinsapo*, *Alnus glutinosa*, *Betula* spp., *Castanea sativa*,
+#' *Ceratonia siliqua*, *Erica arborea*, *Eucalyptus* spp., *Fagus sylvatica*,
+#' *Fraxinus* spp., *Ilex canariensis*, *Juniperus oxycedrus*, *Juniperus phoenicea*,
+#' *Juniperus thurifera*, *Laurus azorica*, *Myrica faya*, *Olea europaea* var. *sylvestris*,
+#' *Other broadleaves*, *Other conifers*, *Other laurel species*, *Pinus canariensis*,
+#' *Pinus halepensis*, *Pinus nigra*, *Pinus pinaster*, *Pinus pinea*, *Pinus radiata*,
+#' *Pinus sylvestris*, *Pinus uncinata*, *Populus x euramericana*, *Quercus canariensis*,
+#' *Quercus faginea*, *Quercus ilex*, *Quercus pyrenaica*, *Quercus robur*, *Quercus suber*
#'
-#' * **AGB**: all aboveground biomass components
-#' * **BGB**: all belowground biomass compoponents
-#' * **tree** or **all**: total tree biomass includying AGB and BGB
+#' **Available components:**
#'
-#' Then we have the second group of components, which are related to tree groups:
+#' Aboveground / belowground groups (summed automatically):
#'
-#' * **stem**: includes the stem and bark
-#' * **branches**: includes all branches
-#' * **roots**: includes the roots (same as BGB)
+#' * `"AGB"` — total aboveground biomass
+#' * `"BGB"` — total belowground biomass (roots)
+#' * `"all"` or `"tree"` — total tree biomass (AGB + BGB)
#'
-#' Finally, we have the last level, which includes tree components (not all of them
-#' are available for all species): stem, bark, thick branches (>7cm), medium branches (2-7cm),
-#' thin branches (0.5-2cm), leaves (include needles), roots. In some species,
-#' there's "stem and thick branches", instead of two groups.
+#' Tree structural groups:
#'
-#' Users can check the list of supported species and their corresponding components
-#' in [biomass_models].
+#' * `"stem"` — stem fraction(s)
+#' * `"branches"` — all branch fractions combined
+#' * `"roots"` — roots (equivalent to BGB)
+#'
+#' Individual tree components (species availability varies):
+#'
+#' * `"stem"` — stem wood
+#' * `"stem and thick branches"` — stem together with branches > 7 cm
+#' * `"thick branches"` — branches > 7 cm
+#' * `"medium branches"` — branches 2–7 cm
+#' * `"small branches"` — branches < 2 cm
+#' * `"leaves"` — foliage (including needles)
+#' * `"roots"` — coarse roots
#'
-#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_dieguez_aranda_2009()]
+#'
+#' Note that for this model, `"tree"` (or `"all"`) is an independent regression equation fitted to total-tree data. It was **not** derived by summing the AGB and BGB equations.
+#' Consequently, there is a numerical discrepancy between the direct `"tree"` estimation and the sum of separate `"AGB"` and `"BGB"` estimations (e.g. for *Pinus sylvestris* at diameter = 20 cm and height = 10 m, the direct total is 89.1 kg, while AGB + BGB is 115.9 kg, a 24% difference).
+#'
+#' Also, the following 6 species have no BGB/roots equations in this model: *Abies pinsapo*, *Erica arborea*, *Eucalyptus* spp., *Ilex canariensis*, *Laurus azorica*, *Myrica faya* (requesting `"BGB"` or `"roots"` will fail).
+#'
+#' Users can check all available species and components in the [biomass_models] dataset provided by the library.
+#'
+#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_dieguez_aranda_2009()],
#' [eq_biomass_ruiz_peinado_2011()], [eq_biomass_ruiz_peinado_2012()], [eq_biomass_manrique_2017()],
#' [eq_biomass_menendez_2022()], [eq_biomass_cudjoe_2024()]
#'
#' @examples
-#' ## get model parameters for silv_predict_biomass
+#' ## Aboveground biomass for Pinus pinaster
#' eq_biomass_montero_2005("Pinus pinaster", "AGB")
eq_biomass_montero_2005 <- function(species, component = "stem", return_r2 = FALSE) {
@@ -470,10 +613,19 @@ eq_biomass_montero_2005 <- function(species, component = "stem", return_r2 = FAL
if (nrow(sel_component) == 0) sel_component <- sel_species[sel_species$tree_component %in% component, ]
}
## 1.4. Check if there's a matching model
- if (nrow(sel_component) == 0) cli::cli_abort(
- "The combination of species-component-model doesn't match any available option.
- Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
- )
+ if (nrow(sel_component) == 0 || all(is.na(sel_component$expression))) {
+ if (component %in% c("BGB", "roots") && species %in% c("Abies pinsapo", "Erica arborea", "Eucalyptus spp.", "Ilex canariensis", "Laurus azorica", "Myrica faya")) {
+ cli::cli_abort("Model {.val montero-2005} does not include BGB equations for {.val {species}}.")
+ }
+ if (nrow(sel_component) == 0) {
+ cli::cli_abort(
+ "The combination of species-component-model doesn't match any available option.
+ Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
+ )
+ } else {
+ cli::cli_abort("Model {.val montero-2005} does not include valid equations for component {.val {component}} and species {.val {species}}.")
+ }
+ }
# 2. Return
ModelBiomass(
@@ -521,21 +673,34 @@ eq_biomass_montero_2005 <- function(species, component = "stem", return_r2 = FAL
#'
#' @details
#'
-#' There are two species in this model: *Quercus petraea* and *Quercus pyrenaica*
+#' **Supported species (2):**
+#'
+#' * *Quercus petraea*
+#' * *Quercus pyrenaica*
+#'
+#' **Available components:**
+#'
+#' Aboveground group (summed automatically):
+#'
+#' * `"AGB"` — total aboveground biomass (sum of all components below)
#'
-#' The tree components include:
+#' Individual tree components:
#'
-#' * **stem**: includes stem and the thickest branches
-#' * **medium branches**
-#' * **thin branches**
-#' * **AGB**: total biomass, results of summing the previous three components
+#' * `"stem and thick branches"` — stem together with branches > 7 cm
+#' * `"medium branches"` — branches 2–7 cm
+#' * `"small branches"` — branches < 2 cm
#'
-#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()], [eq_biomass_dieguez_aranda_2009()]
+#'
+#' Note that no belowground biomass (BGB / roots) or total-tree equations are available in the source paper.
+#'
+#' Users can check all available species and components in the [biomass_models] dataset provided by the library.
+#'
+#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()], [eq_biomass_dieguez_aranda_2009()],
#' [eq_biomass_ruiz_peinado_2011()], [eq_biomass_ruiz_peinado_2012()], [eq_biomass_menendez_2022()],
#' [eq_biomass_cudjoe_2024()]
#'
#' @examples
-#' ## get model parameters for silv_predict_biomass
+#' ## Aboveground biomass for Quercus petraea
#' eq_biomass_manrique_2017("Quercus petraea", "AGB")
eq_biomass_manrique_2017 <- function(species, component = "AGB", return_r2 = FALSE, return_rmse = FALSE) {
@@ -561,10 +726,15 @@ eq_biomass_manrique_2017 <- function(species, component = "AGB", return_r2 = FAL
if (nrow(sel_component) == 0) sel_component <- sel_species[sel_species$tree_component %in% component, ]
}
## 1.4. Check if there's a matching model
- if (nrow(sel_component) == 0) cli::cli_abort(
- "The combination of species-component-model doesn't match any available option.
- Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
- )
+ if (nrow(sel_component) == 0) {
+ if (component %in% c("BGB", "roots", "tree", "all")) {
+ cli::cli_abort("Model {.val manrique-2017} does not include BGB / total-tree equations.")
+ }
+ cli::cli_abort(
+ "The combination of species-component-model doesn't match any available option.
+ Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
+ )
+ }
# 2. Return
ModelBiomass(
@@ -611,17 +781,46 @@ eq_biomass_manrique_2017 <- function(species, component = "AGB", return_r2 = FAL
#'
#' @details
#'
-#' There are 15 species in this model, including generic equations for *Conifers*,
-#' *Deciduous broadleaves*, and *Evergreen broadleaves*.
+#' **Supported species (18):**
+#'
+#' *Betula* sp., *Fagus sylvatica*, *Juniperus thurifera*, *Pinus halepensis*,
+#' *Pinus nigra*, *Pinus pinaster*, *Pinus pinea*, *Pinus radiata*, *Pinus sylvestris*,
+#' *Quercus faginea*, *Quercus ilex*, *Quercus petraea*, *Quercus pyrenaica*,
+#' *Quercus robur*, *Quercus suber*
+#'
+#' Generic equations are also available for functional groups:
+#'
+#' * `"Conifers"` — generic equation for conifer species
+#' * `"Deciduous broadleaves"` — generic equation for deciduous broadleaf species
+#' * `"Evergreen broadleaves"` — generic equation for evergreen broadleaf species
+#'
+#' **Available components:**
+#'
+#' * `"AGB"` — aboveground biomass (only component available; no `component` argument needed)
#'
-#' All the models measure only aboveground biomass.
+#' **Important — non-standard input variables:**
#'
-#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()], [eq_biomass_dieguez_aranda_2009()]
+#' Unlike other biomass models, these equations were fitted on **young plantations
+#' (< 30 years)** and use different predictor variables:
+#'
+#' * Most species use `rcd` = root collar diameter (cm), **not** diameter at breast
+#' height. Pass it via the `rcd` argument of [silv_predict_biomass()].
+#' * Some species (*Pinus halepensis*, *Pinus nigra*, *Quercus suber*,
+#' *Evergreen broadleaves*) use `bp` = biomass packing (m\ifelse{html}{\out{3}}{$^3$}). Pass it via the
+#' `bp` argument of [silv_predict_biomass()].
+#' * *Betula* sp. uses only `h` (total height).
+#'
+#'
+#' Note that no belowground biomass (BGB / roots) or total-tree equations are available in the source paper for this model.
+#'
+#' Users can check all available species and components in the [biomass_models] dataset provided by the library.
+#'
+#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()], [eq_biomass_dieguez_aranda_2009()],
#' [eq_biomass_ruiz_peinado_2011()], [eq_biomass_ruiz_peinado_2012()], [eq_biomass_manrique_2017()],
#' [eq_biomass_cudjoe_2024()]
#'
#' @examples
-#' ## get model parameters for silv_predict_biomass
+#' ## AGB for Fagus sylvatica using root collar diameter
#' eq_biomass_menendez_2022("Fagus sylvatica")
eq_biomass_menendez_2022 <- function(species, return_r2 = FALSE, return_rmse = FALSE) {
@@ -680,28 +879,36 @@ eq_biomass_menendez_2022 <- function(species, return_r2 = FALSE, return_rmse = F
#'
#' @details
#'
-#' There are three species options in this model:
+#' **Supported species (3):**
+#'
+#' * *Pinus sylvestris*
+#' * *Quercus petraea*
+#' * `"mixed"` — mixed stand of *Pinus sylvestris* × *Quercus petraea*
#'
-#' * ***Quercus petraea***
+#' **Available components:**
#'
-#' * ***Pinus sylvestris***
+#' Aboveground group (summed automatically):
#'
-#' * **Mixed**: stands with *Quercus petraea* and *Pinus sylvestris*
+#' * `"AGB"` — total aboveground biomass (sum of all components below)
#'
-#' The tree components include some AGB components:
+#' Individual tree components (species availability varies):
#'
-#' * **leaves**: only for *P. sylvestris*
-#' * **stem**: for all species
-#' * **medium branches and small brances**: for all species
-#' * **thick branches**: for all species
-#' * **AGB**: total biomass, results of summing the previous components
+#' * `"stem"` — stem wood (all species)
+#' * `"thick branches"` — branches > 7 cm (all species)
+#' * `"medium branches and small branches"` — branches < 7 cm (all species)
+#' * `"leaves"` — foliage/needles (*Pinus sylvestris* only)
#'
-#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()], [eq_biomass_dieguez_aranda_2009()]
+#'
+#' Note that no belowground biomass (BGB / roots) or total-tree equations are available in the source paper for this model.
+#'
+#' Users can check all available species and components in the [biomass_models] dataset provided by the library.
+#'
+#' @seealso [silv_predict_biomass()], [biomass_models], [eq_biomass_montero_2005()], [eq_biomass_dieguez_aranda_2009()],
#' [eq_biomass_ruiz_peinado_2011()], [eq_biomass_ruiz_peinado_2012()], [eq_biomass_manrique_2017()],
#' [eq_biomass_menendez_2022()]
#'
#' @examples
-#' ## get model parameters for silv_predict_biomass
+#' ## Aboveground biomass for a mixed stand
#' eq_biomass_cudjoe_2024("mixed", "AGB")
eq_biomass_cudjoe_2024 <- function(species, component = "AGB", return_rmse = FALSE) {
@@ -726,14 +933,19 @@ eq_biomass_cudjoe_2024 <- function(species, component = "AGB", return_rmse = FAL
if (nrow(sel_component) == 0) sel_component <- sel_species[sel_species$tree_component %in% component, ]
}
## 1.4. Check if there's a matching model
- if (nrow(sel_component) == 0) cli::cli_abort(
- "The combination of species-component-model doesn't match any available option.
- Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
- )
+ if (nrow(sel_component) == 0) {
+ if (component %in% c("BGB", "roots", "tree", "all")) {
+ cli::cli_abort("Model {.val cudjoe-2024} does not include BGB / total-tree equations.")
+ }
+ cli::cli_abort(
+ "The combination of species-component-model doesn't match any available option.
+ Check {.url https://cidree.github.io/silviculture/reference/biomass_models.html} for available models."
+ )
+ }
# 2. Return
ModelBiomass(
- equation = "cudjoe-2017",
+ equation = "cudjoe-2024",
species = species,
component = component,
expression = data.frame(
@@ -751,3 +963,413 @@ eq_biomass_cudjoe_2024 <- function(species, component = "AGB", return_rmse = FAL
)
}
+
+
+
+
+#' Automatically Predict Biomass Using the Best Available Model
+#'
+#' Evaluates vectors of tree species, diameters, and heights, and automatically
+#' selects the best available allometric model based on a specified priority.
+#' If tree height is not provided, is NA, or is 0, the function automatically
+#' falls back to the \code{\link{eq_biomass_montero_2005}} model.
+#'
+#' @param species A character vector specifying the scientific names of the tree species.
+#' @param diameter A numeric vector of tree diameters at breast height (in cm).
+#' @param height An optional numeric vector of tree heights (in m). Defaults to \code{NULL}.
+#' @param component A character string specifying the tree component for biomass
+#' calculation (e.g., "tree", "stem", "branches"). Defaults to \code{"tree"}.
+#' @param ntrees An optional numeric vector indicating the number of trees in
+#' each class. Defaults to \code{NULL} (equivalent to 1 tree per entry).
+#' @param rcd An optional numeric vector of root collar diameters (in cm). Required
+#' for young plantation equations (\code{\link{eq_biomass_menendez_2022}}).
+#' @param bp An optional numeric vector of biomass packing values (in m\ifelse{html}{\out{3}}{$^3$}). Required
+#' for some species in young plantation equations.
+#' @param priority A character vector specifying the priority order for model selection.
+#' Defaults to \code{c("ruiz-peinado-2011", "ruiz-peinado-2012", "montero-2005", "dieguez-aranda-2009", "manrique-2017", "menendez-2022", "cudjoe-2024")}.
+#' @param quiet A logical value. If \code{TRUE}, suppresses any informational messages and warnings.
+#'
+#' @return A \code{data.frame} containing two columns:
+#' \itemize{
+#' \item \code{biomass}: Numeric vector of predicted biomass values (in kg).
+#' \item \code{biomass_model}: Character vector specifying the model ID used.
+#' }
+#'
+#' @export
+#'
+#' @examples
+#' # Predict biomass using default priorities
+#' species_vec <- c("Pinus pinaster", "Quercus petraea")
+#' d_vec <- c(20, 25)
+#' h_vec <- c(12, 14)
+#' silv_predict_biomass_auto(species_vec, d_vec, h_vec)
+#'
+#' # Fallback to Montero 2005 when height is missing
+#' silv_predict_biomass_auto(species_vec, d_vec, height = NULL)
+silv_predict_biomass_auto <- function(
+ species,
+ diameter,
+ height = NULL,
+ component = "tree",
+ ntrees = NULL,
+ rcd = NULL,
+ bp = NULL,
+ priority = c("ruiz-peinado-2011", "ruiz-peinado-2012", "montero-2005", "dieguez-aranda-2009", "manrique-2017", "menendez-2022", "cudjoe-2024"),
+ quiet = FALSE) {
+
+ # 1. Sanity Checks & Length Validations
+ n_trees <- length(species)
+ if (length(diameter) != n_trees) {
+ cli::cli_abort("{.arg species} and {.arg diameter} must have the same length.")
+ }
+ if (!is.null(height) && length(height) != n_trees) {
+ cli::cli_abort("{.arg species} and {.arg height} must have the same length.")
+ }
+
+ # Expand scalars/defaults to vectors of length n_trees
+ if (is.null(ntrees)) {
+ ntrees <- rep(1, n_trees)
+ } else if (length(ntrees) == 1) {
+ ntrees <- rep(ntrees, n_trees)
+ } else if (length(ntrees) != n_trees) {
+ cli::cli_abort("{.arg ntrees} must be of length 1 or the same length as {.arg species}.")
+ }
+
+ if (is.null(rcd)) {
+ rcd <- diameter
+ } else if (length(rcd) == 1) {
+ rcd <- rep(rcd, n_trees)
+ } else if (length(rcd) != n_trees) {
+ cli::cli_abort("{.arg rcd} must be of length 1 or the same length as {.arg species}.")
+ }
+
+ if (is.null(bp)) {
+ bp <- rep(NA_real_, n_trees)
+ } else if (length(bp) == 1) {
+ bp <- rep(bp, n_trees)
+ } else if (length(bp) != n_trees) {
+ cli::cli_abort("{.arg bp} must be of length 1 or the same length as {.arg species}.")
+ }
+
+ # Determine height availability per tree
+ if (is.null(height)) {
+ has_height <- rep(FALSE, n_trees)
+ height_vec <- rep(NA_real_, n_trees)
+ } else {
+ has_height <- !is.na(height) & height > 0
+ height_vec <- height
+ }
+
+ # 2. Find Best Model for each unique combination of (species, has_height)
+ unique_df <- unique(data.frame(
+ species = species,
+ has_height = has_height,
+ stringsAsFactors = FALSE
+ ))
+
+ unique_df$model <- mapply(
+ .find_best_model,
+ unique_df$species,
+ MoreArgs = list(component = component, priority = priority),
+ unique_df$has_height,
+ USE.NAMES = FALSE
+ )
+
+ # Match back to the full tree lists
+ matched_models <- unique_df$model[match(
+ paste(species, has_height, sep = "_"),
+ paste(unique_df$species, unique_df$has_height, sep = "_")
+ )]
+
+ # 3. Warn about unmatched entries
+ na_indices <- which(is.na(matched_models))
+ if (length(na_indices) > 0 && !quiet) {
+ unsupported <- unique(species[na_indices])
+ cli::cli_warn(
+ "No compatible model was found in priority for species: {.val {unsupported}} with component {.val {component}}."
+ )
+ }
+
+ # 4. Predict Biomass grouping by (species, model)
+ biomass_vec <- rep(NA_real_, n_trees)
+
+ unique_groups <- unique(data.frame(
+ species = species,
+ model = matched_models,
+ stringsAsFactors = FALSE
+ ))
+ unique_groups <- unique_groups[!is.na(unique_groups$model), ]
+
+ for (row_idx in seq_len(nrow(unique_groups))) {
+ sp <- unique_groups$species[row_idx]
+ m_id <- unique_groups$model[row_idx]
+
+ idx <- which(species == sp & matched_models == m_id)
+ if (length(idx) == 0) next
+
+ fn_name <- paste0("eq_biomass_", gsub("-", "_", m_id))
+ fn <- get(fn_name, envir = asNamespace("silviculture"), mode = "function")
+ model_obj <- fn(species = sp, component = component)
+
+ group_biomass <- silv_predict_biomass(
+ diameter = diameter[idx],
+ height = height_vec[idx],
+ model = model_obj,
+ ntrees = ntrees[idx],
+ rcd = rcd[idx],
+ bp = bp[idx],
+ quiet = TRUE
+ )
+
+ biomass_vec[idx] <- group_biomass
+ }
+
+ # 5. Citations / feedback
+ if (!quiet) {
+ used_models <- unique(matched_models[!is.na(matched_models)])
+ if (length(used_models) > 0) {
+ sel_models_info <- biomass_models[biomass_models$article_id %in% used_models, ]
+ urls <- unique(sel_models_info$doi_url)
+ obss <- unique(sel_models_info$obs)
+ if (length(urls) == 1) {
+ cli::cli_alert_warning("Cite this model using {.url {urls}}")
+ cli::cli_alert_info(obss)
+ } else {
+ cli::cli_alert_warning("Cite these models using <{paste0(urls, collapse = ', ')}>")
+ cli::cli_alert_info("{paste0(obss, collapse = ', ')}")
+ }
+ }
+ }
+
+ data.frame(
+ biomass = biomass_vec,
+ biomass_model = matched_models,
+ stringsAsFactors = FALSE
+ )
+}
+
+
+
+
+#' Predict All Individual Biomass Components for Trees
+#'
+#' Predicts all available individual biomass components (e.g., stem, bark, branches,
+#' roots) for the given trees in a single call, returning a wide data frame.
+#'
+#' @param species A character vector specifying the scientific names of the tree species.
+#' @param diameter A numeric vector of tree diameters at breast height (in cm).
+#' @param height An optional numeric vector of tree heights (in m). Defaults to \code{NULL}.
+#' @param model_fn A function or character string. The constructer function of the model
+#' (e.g., \code{eq_biomass_ruiz_peinado_2011}) or the model ID string (e.g., \code{"ruiz-peinado-2011"}).
+#' @param ntrees An optional numeric vector indicating the number of trees in
+#' each class. Defaults to \code{NULL}.
+#' @param rcd An optional numeric vector of root collar diameters (in cm).
+#' @param bp An optional numeric vector of biomass packing values (in m\ifelse{html}{\out{3}}{$^3$}).
+#' @param quiet A logical value. If \code{TRUE}, suppresses any informational messages.
+#'
+#' @return A \code{data.frame} with the columns \code{species}, \code{diameter},
+#' \code{height} (if provided), and one additional column for each individual biomass
+#' component available for the selected species and model.
+#'
+#' @export
+#'
+#' @examples
+#' # Predict all components using Ruiz-Peinado 2011
+#' silv_predict_biomass_components(
+#' species = "Pinus pinaster",
+#' diameter = 25,
+#' height = 15,
+#' model_fn = eq_biomass_ruiz_peinado_2011
+#' )
+silv_predict_biomass_components <- function(
+ species,
+ diameter,
+ height = NULL,
+ model_fn,
+ ntrees = NULL,
+ rcd = NULL,
+ bp = NULL,
+ quiet = TRUE) {
+
+ # 1. Resolve model_fn
+ if (is.character(model_fn)) {
+ fn_name <- if (startsWith(model_fn, "eq_biomass_")) {
+ model_fn
+ } else {
+ paste0("eq_biomass_", gsub("-", "_", model_fn))
+ }
+ model_fn <- get(fn_name, envir = asNamespace("silviculture"), mode = "function")
+ }
+
+ fn_name <- .get_function_name(model_fn)
+ if (is.null(fn_name)) {
+ cli::cli_abort("The provided function is not a recognized model function in silviculture.")
+ }
+
+ article_id <- gsub("^eq_biomass_", "", fn_name)
+ article_id <- gsub("_", "-", article_id)
+
+ # 2. Sanity Checks & Length Validations
+ n_trees <- length(species)
+ if (length(diameter) != n_trees) {
+ cli::cli_abort("{.arg species} and {.arg diameter} must have the same length.")
+ }
+ if (!is.null(height) && length(height) != n_trees) {
+ cli::cli_abort("{.arg species} and {.arg height} must have the same length.")
+ }
+
+ # Expand vectors
+ if (is.null(ntrees)) {
+ ntrees <- rep(1, n_trees)
+ } else if (length(ntrees) == 1) {
+ ntrees <- rep(ntrees, n_trees)
+ } else if (length(ntrees) != n_trees) {
+ cli::cli_abort("{.arg ntrees} must be of length 1 or the same length as {.arg species}.")
+ }
+
+ if (is.null(rcd)) {
+ rcd <- diameter
+ } else if (length(rcd) == 1) {
+ rcd <- rep(rcd, n_trees)
+ } else if (length(rcd) != n_trees) {
+ cli::cli_abort("{.arg rcd} must be of length 1 or the same length as {.arg species}.")
+ }
+
+ if (is.null(bp)) {
+ bp <- rep(NA_real_, n_trees)
+ } else if (length(bp) == 1) {
+ bp <- rep(bp, n_trees)
+ } else if (length(bp) != n_trees) {
+ cli::cli_abort("{.arg bp} must be of length 1 or the same length as {.arg species}.")
+ }
+
+ # 3. Find unique components across the species in the input
+ unique_species <- unique(species)
+ sel_model <- biomass_models[biomass_models$article_id == article_id, ]
+
+ # Check if species are supported by the model
+ unsupported_species <- setdiff(unique_species, sel_model$species)
+ if (length(unsupported_species) > 0) {
+ cli::cli_abort("Species {.val {unsupported_species}} {?is/are} not supported by model {.val {article_id}}.")
+ }
+
+ sel_species <- sel_model[sel_model$species %in% unique_species, ]
+
+ components <- unique(sel_species$tree_component)
+ non_totals <- components[!components %in% c("tree", "agb")]
+ if (length(non_totals) > 0) {
+ components <- non_totals
+ }
+
+ # 4. Construct output dataframe
+ res_df <- data.frame(
+ species = species,
+ diameter = diameter,
+ stringsAsFactors = FALSE
+ )
+ if (!is.null(height)) {
+ res_df$height <- height
+ }
+
+ # 5. Predict each component
+ for (comp in components) {
+ comp_values <- rep(NA_real_, n_trees)
+
+ for (sp in unique_species) {
+ idx <- which(species == sp)
+ if (length(idx) == 0) next
+
+ # Check support
+ has_comp <- any(sel_species$species == sp & sel_species$tree_component == comp)
+ if (!has_comp) next
+
+ args <- names(formals(model_fn))
+ if ("component" %in% args) {
+ model_obj <- model_fn(species = sp, component = comp)
+ } else {
+ model_obj <- model_fn(species = sp)
+ }
+
+ comp_values[idx] <- silv_predict_biomass(
+ diameter = diameter[idx],
+ height = if (!is.null(height)) height[idx] else NULL,
+ model = model_obj,
+ ntrees = ntrees[idx],
+ rcd = rcd[idx],
+ bp = bp[idx],
+ quiet = TRUE
+ )
+ }
+
+ # Capitalize acronyms for column names
+ col_name <- comp
+ if (col_name == "agb") col_name <- "AGB"
+ if (col_name == "bgb") col_name <- "BGB"
+
+ res_df[[col_name]] <- comp_values
+ }
+
+ # 6. Citations / feedback
+ if (!quiet && nrow(sel_species) > 0) {
+ urls <- unique(sel_species$doi_url)
+ obss <- unique(sel_species$obs)
+ if (length(urls) == 1) {
+ cli::cli_alert_warning("Cite this model using {.url {urls}}")
+ cli::cli_alert_info(obss)
+ } else {
+ cli::cli_alert_warning("Cite these models using <{paste0(urls, collapse = ', ')}>")
+ cli::cli_alert_info("{paste0(obss, collapse = ', ')}")
+ }
+ }
+
+ return(res_df)
+}
+
+
+
+
+# --- Internal Helpers ---
+
+.find_best_model <- function(species, component, priority, has_height) {
+ available_priorities <- if (has_height) priority else "montero-2005"
+
+ for (model_id in available_priorities) {
+ fn_name <- paste0("eq_biomass_", gsub("-", "_", model_id))
+ if (!exists(fn_name, envir = asNamespace("silviculture"), mode = "function")) {
+ next
+ }
+ fn <- get(fn_name, envir = asNamespace("silviculture"), mode = "function")
+
+ args <- names(formals(fn))
+
+ res <- tryCatch({
+ if ("component" %in% args) {
+ fn(species = species, component = component)
+ } else {
+ if (component == "AGB") {
+ fn(species = species)
+ } else {
+ cli::cli_abort("Component not supported")
+ }
+ }
+ TRUE
+ }, error = function(e) {
+ FALSE
+ })
+
+ if (res) {
+ return(model_id)
+ }
+ }
+ return(NA_character_)
+}
+
+.get_function_name <- function(fun) {
+ ns <- asNamespace("silviculture")
+ for (name in names(ns)) {
+ if (is.function(ns[[name]]) && identical(ns[[name]], fun)) {
+ return(name)
+ }
+ }
+ return(NULL)
+}
diff --git a/README.md b/README.md
index 2f37ad0..8e37e28 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,13 @@ common silvicultural analysis. It aims to support forest management and
research by providing a flexible toolkit for data manipulation and
summary.
+### Suggested companion reading
+
+If you want a visual overview before diving into the API, use the
+supporting poster:
+
+- [Poster on R silviculture](https://doi.org/10.13140/RG.2.2.28098.75205)
+
## Installation
You can install the development version of `silviculture` from
diff --git a/_pkgdown.yml b/_pkgdown.yml
index 048d33a..4b64fe5 100644
--- a/_pkgdown.yml
+++ b/_pkgdown.yml
@@ -23,6 +23,8 @@ reference:
desc: Functions for estimating forest variables from models or equations
contents:
- silv_predict_biomass
+ - silv_predict_biomass_auto
+ - silv_predict_biomass_components
- silv_predict_height
- silv_biomass
@@ -93,6 +95,7 @@ reference:
desc: Datasets included in the package
contents:
- biomass_models
+ - carbon_models
- inventory_samples
- title: Objects
diff --git a/inst/references.bib b/inst/references.bib
index 18f35ce..92318f8 100644
--- a/inst/references.bib
+++ b/inst/references.bib
@@ -105,7 +105,6 @@ @article{vazquez-veloso_one_2025
pages = {122981},
}
-<<<<<<< HEAD
@article{menendez-miguelez_species-specific_2022,
title = {Species-specific and generalized biomass models for estimating carbon stocks of young reforestations},
@@ -138,17 +137,12 @@ @article{cudjoe_allometry_2024
year = {2024},
keywords = {Pinus sylvestris, Tree allometry, Species mixture, Quercus petraea, Biomass equations, Dirichlet regression, Pine-oak mixed stand},
pages = {176061},
-=======
-@article{palahi_individual-tree_2003,
- title = {Individual-tree growth and mortality models for {Scots} pine (\textit{{Pinus} sylvestris} {L}.) in north-east {Spain}},
- volume = {60},
- url = {https://www.afs-journal.org/articles/forest/pdf/2003/01/F3101.pdf},
- number = {1},
- journal = {Annals of Forest Science},
- author = {Palahí, Marc and Pukkala, Timo and Miina, Jari and Montero, Gregorio},
- year = {2003},
- note = {Publisher: EDP Sciences},
- keywords = {Pinus sylvestris},
- pages = {1--10},
->>>>>>> ed07048169f72c5136e72eea26506076f320a24a
+}
+
+@misc{vazquez-veloso_poster_2025,
+ author = {Cidre-González, Adrián and Vázquez-Veloso, Aitor},
+ title = {silviculture: {An} {R} Package for {Forest} {Inventory} and {Silvicultural} {Workflows}},
+ year = {2025},
+ doi = {10.13140/RG.2.2.28098.75205},
+ howpublished = {Poster, ResearchGate},
}
diff --git a/man/eq_biomass_cudjoe_2024.Rd b/man/eq_biomass_cudjoe_2024.Rd
index 6060313..d85c8c3 100644
--- a/man/eq_biomass_cudjoe_2024.Rd
+++ b/man/eq_biomass_cudjoe_2024.Rd
@@ -25,28 +25,38 @@ Allometric equations adjusted for \emph{Quercus petraea}, and \emph{Pinus sylves
in Castille and León (Spain)
}
\details{
-There are three species options in this model:
+\strong{Supported species (3):}
\itemize{
-\item \emph{\strong{Quercus petraea}}
-\item \emph{\strong{Pinus sylvestris}}
-\item \strong{Mixed}: stands with \emph{Quercus petraea} and \emph{Pinus sylvestris}
+\item \emph{Pinus sylvestris}
+\item \emph{Quercus petraea}
+\item \code{"mixed"} — mixed stand of \emph{Pinus sylvestris} × \emph{Quercus petraea}
}
-The tree components include some AGB components:
+\strong{Available components:}
+
+Aboveground group (summed automatically):
+\itemize{
+\item \code{"AGB"} — total aboveground biomass (sum of all components below)
+}
+
+Individual tree components (species availability varies):
\itemize{
-\item \strong{leaves}: only for \emph{P. sylvestris}
-\item \strong{stem}: for all species
-\item \strong{medium branches and small brances}: for all species
-\item \strong{thick branches}: for all species
-\item \strong{AGB}: total biomass, results of summing the previous components
+\item \code{"stem"} — stem wood (all species)
+\item \code{"thick branches"} — branches > 7 cm (all species)
+\item \code{"medium branches and small branches"} — branches < 7 cm (all species)
+\item \code{"leaves"} — foliage/needles (\emph{Pinus sylvestris} only)
}
+
+Note that no belowground biomass (BGB / roots) or total-tree equations are available in the source paper for this model.
+
+Users can check all available species and components in the \link{biomass_models} dataset provided by the library.
}
\examples{
-## get model parameters for silv_predict_biomass
+## Aboveground biomass for a mixed stand
eq_biomass_cudjoe_2024("mixed", "AGB")
}
\seealso{
-\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}}
+\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}},
\code{\link[=eq_biomass_ruiz_peinado_2011]{eq_biomass_ruiz_peinado_2011()}}, \code{\link[=eq_biomass_ruiz_peinado_2012]{eq_biomass_ruiz_peinado_2012()}}, \code{\link[=eq_biomass_manrique_2017]{eq_biomass_manrique_2017()}},
\code{\link[=eq_biomass_menendez_2022]{eq_biomass_menendez_2022()}}
}
diff --git a/man/eq_biomass_dieguez_aranda_2009.Rd b/man/eq_biomass_dieguez_aranda_2009.Rd
index 60bf676..1768b66 100644
--- a/man/eq_biomass_dieguez_aranda_2009.Rd
+++ b/man/eq_biomass_dieguez_aranda_2009.Rd
@@ -32,34 +32,45 @@ A S7 list of parameters
Allometric equations adjusted for Galician (Spain) species
}
\details{
-There are seven species included in this model: \emph{Pinus pinaster, Pinaster radiata, Pinus}
-\emph{sylvestris, Eucalyptus globulus, Eucalyptus nitens, Quercus robur}, and \emph{Betula alba}
+\strong{Supported species (7):}
-The tree components are divided into groups, and any of them can be introduced in the
-component argument:
+\emph{Betula alba}, \emph{Eucalyptus globulus}, \emph{Eucalyptus nitens}, \emph{Pinus pinaster},
+\emph{Pinus radiata}, \emph{Pinus sylvestris}, \emph{Quercus robur}
+
+\strong{Available components:}
+
+Aboveground / belowground groups (summed automatically):
+\itemize{
+\item \code{"AGB"} — total aboveground biomass
+\item \code{"BGB"} — total belowground biomass (roots)
+}
+
+Tree structural groups:
\itemize{
-\item \strong{AGB}: all aboveground biomass components
-\item \strong{BGB}: all belowground biomass compoponents
-\item \strong{tree}: total tree biomass includying AGB and BGB
+\item \code{"stem"} — stem fraction(s)
+\item \code{"branches"} — all branch fractions combined
+\item \code{"roots"} — roots (equivalent to BGB)
}
-Then we have the second group of components, which are related to tree groups:
+Individual tree components (species availability varies):
\itemize{
-\item \strong{stem}: includes the stem and bark
-\item \strong{branches}: includes all branches
-\item \strong{roots}: includes the roots (same as BGB)
+\item \code{"stem and thick branches"} — stem together with thickest branches
+\item \code{"thick branches"} — branches > 7 cm
+\item \code{"medium branches"} — branches 2–7 cm
+\item \code{"small branches"} — branches 0.5–2 cm
+\item \code{"twigs"} — branches < 0.5 cm
+\item \code{"dry branches"} — dead attached branches
+\item \code{"leaves"} — foliage (including needles)
+\item \code{"roots"} — coarse roots
}
-Finally, we have the last level, which includes tree components (not all of them
-are available for all species): stem, bark, thick branches (>7cm), medium branches (2-7cm),
-thin branches (0.5-2cm), twigs (<0.5cm), dry branches, leaves, roots. In some species,
-there's "stem and thick branches", instead of two groups.
+Note that total-tree equations (\code{"tree"} / \code{"all"}) are not available for this model.
+Also, \emph{Eucalyptus globulus}, \emph{Eucalyptus nitens}, and \emph{Pinus pinaster} lack BGB equations (requesting \code{"BGB"} or \code{"roots"} will fail).
-Users can check the list of supported species and their corresponding components
-in \link{biomass_models}.
+Users can check all available species and components in the \link{biomass_models} dataset provided by the library.
}
\examples{
-## get model parameters for silv_predict_biomass
+## Aboveground biomass for Pinus pinaster
eq_biomass_dieguez_aranda_2009("Pinus pinaster", "AGB")
}
\seealso{
diff --git a/man/eq_biomass_manrique_2017.Rd b/man/eq_biomass_manrique_2017.Rd
index b0a15e7..a254960 100644
--- a/man/eq_biomass_manrique_2017.Rd
+++ b/man/eq_biomass_manrique_2017.Rd
@@ -33,22 +33,36 @@ Allometric equations adjusted for \emph{Quercus petraea} and \emph{Quercus pyren
in Palencia, Spain
}
\details{
-There are two species in this model: \emph{Quercus petraea} and \emph{Quercus pyrenaica}
+\strong{Supported species (2):}
+\itemize{
+\item \emph{Quercus petraea}
+\item \emph{Quercus pyrenaica}
+}
+
+\strong{Available components:}
+
+Aboveground group (summed automatically):
+\itemize{
+\item \code{"AGB"} — total aboveground biomass (sum of all components below)
+}
-The tree components include:
+Individual tree components:
\itemize{
-\item \strong{stem}: includes stem and the thickest branches
-\item \strong{medium branches}
-\item \strong{thin branches}
-\item \strong{AGB}: total biomass, results of summing the previous three components
+\item \code{"stem and thick branches"} — stem together with branches > 7 cm
+\item \code{"medium branches"} — branches 2–7 cm
+\item \code{"small branches"} — branches < 2 cm
}
+
+Note that no belowground biomass (BGB / roots) or total-tree equations are available in the source paper.
+
+Users can check all available species and components in the \link{biomass_models} dataset provided by the library.
}
\examples{
-## get model parameters for silv_predict_biomass
+## Aboveground biomass for Quercus petraea
eq_biomass_manrique_2017("Quercus petraea", "AGB")
}
\seealso{
-\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}}
+\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}},
\code{\link[=eq_biomass_ruiz_peinado_2011]{eq_biomass_ruiz_peinado_2011()}}, \code{\link[=eq_biomass_ruiz_peinado_2012]{eq_biomass_ruiz_peinado_2012()}}, \code{\link[=eq_biomass_menendez_2022]{eq_biomass_menendez_2022()}},
\code{\link[=eq_biomass_cudjoe_2024]{eq_biomass_cudjoe_2024()}}
}
diff --git a/man/eq_biomass_menendez_2022.Rd b/man/eq_biomass_menendez_2022.Rd
index dd0855f..3f5592a 100644
--- a/man/eq_biomass_menendez_2022.Rd
+++ b/man/eq_biomass_menendez_2022.Rd
@@ -25,17 +25,48 @@ Allometric equations for young (<30) plantations of 18 Spanish species including
broadleaf and conifer species. Only aboveground biomass.
}
\details{
-There are 15 species in this model, including generic equations for \emph{Conifers},
-\emph{Deciduous broadleaves}, and \emph{Evergreen broadleaves}.
+\strong{Supported species (18):}
-All the models measure only aboveground biomass.
+\emph{Betula} sp., \emph{Fagus sylvatica}, \emph{Juniperus thurifera}, \emph{Pinus halepensis},
+\emph{Pinus nigra}, \emph{Pinus pinaster}, \emph{Pinus pinea}, \emph{Pinus radiata}, \emph{Pinus sylvestris},
+\emph{Quercus faginea}, \emph{Quercus ilex}, \emph{Quercus petraea}, \emph{Quercus pyrenaica},
+\emph{Quercus robur}, \emph{Quercus suber}
+
+Generic equations are also available for functional groups:
+\itemize{
+\item \code{"Conifers"} — generic equation for conifer species
+\item \code{"Deciduous broadleaves"} — generic equation for deciduous broadleaf species
+\item \code{"Evergreen broadleaves"} — generic equation for evergreen broadleaf species
+}
+
+\strong{Available components:}
+\itemize{
+\item \code{"AGB"} — aboveground biomass (only component available; no \code{component} argument needed)
+}
+
+\strong{Important — non-standard input variables:}
+
+Unlike other biomass models, these equations were fitted on \strong{young plantations
+(< 30 years)} and use different predictor variables:
+\itemize{
+\item Most species use \code{rcd} = root collar diameter (cm), \strong{not} diameter at breast
+height. Pass it via the \code{rcd} argument of \code{\link[=silv_predict_biomass]{silv_predict_biomass()}}.
+\item Some species (\emph{Pinus halepensis}, \emph{Pinus nigra}, \emph{Quercus suber},
+\emph{Evergreen broadleaves}) use \code{bp} = biomass packing (m\ifelse{html}{\out{3}}{$^3$}). Pass it via the
+\code{bp} argument of \code{\link[=silv_predict_biomass]{silv_predict_biomass()}}.
+\item \emph{Betula} sp. uses only \code{h} (total height).
+}
+
+Note that no belowground biomass (BGB / roots) or total-tree equations are available in the source paper for this model.
+
+Users can check all available species and components in the \link{biomass_models} dataset provided by the library.
}
\examples{
-## get model parameters for silv_predict_biomass
+## AGB for Fagus sylvatica using root collar diameter
eq_biomass_menendez_2022("Fagus sylvatica")
}
\seealso{
-\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}}
+\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}},
\code{\link[=eq_biomass_ruiz_peinado_2011]{eq_biomass_ruiz_peinado_2011()}}, \code{\link[=eq_biomass_ruiz_peinado_2012]{eq_biomass_ruiz_peinado_2012()}}, \code{\link[=eq_biomass_manrique_2017]{eq_biomass_manrique_2017()}},
\code{\link[=eq_biomass_cudjoe_2024]{eq_biomass_cudjoe_2024()}}
}
diff --git a/man/eq_biomass_montero_2005.Rd b/man/eq_biomass_montero_2005.Rd
index 78ec4a2..8e455b8 100644
--- a/man/eq_biomass_montero_2005.Rd
+++ b/man/eq_biomass_montero_2005.Rd
@@ -24,37 +24,57 @@ A S7 list of parameters
Allometric equations adjusted for Spanish species
}
\details{
-There are 35 species included in the model.
+\strong{Supported species (35):}
-The tree components are divided into groups, and any of them can be introduced in the
-component argument:
+\emph{Abies alba}, \emph{Abies pinsapo}, \emph{Alnus glutinosa}, \emph{Betula} spp., \emph{Castanea sativa},
+\emph{Ceratonia siliqua}, \emph{Erica arborea}, \emph{Eucalyptus} spp., \emph{Fagus sylvatica},
+\emph{Fraxinus} spp., \emph{Ilex canariensis}, \emph{Juniperus oxycedrus}, \emph{Juniperus phoenicea},
+\emph{Juniperus thurifera}, \emph{Laurus azorica}, \emph{Myrica faya}, \emph{Olea europaea} var. \emph{sylvestris},
+\emph{Other broadleaves}, \emph{Other conifers}, \emph{Other laurel species}, \emph{Pinus canariensis},
+\emph{Pinus halepensis}, \emph{Pinus nigra}, \emph{Pinus pinaster}, \emph{Pinus pinea}, \emph{Pinus radiata},
+\emph{Pinus sylvestris}, \emph{Pinus uncinata}, \emph{Populus x euramericana}, \emph{Quercus canariensis},
+\emph{Quercus faginea}, \emph{Quercus ilex}, \emph{Quercus pyrenaica}, \emph{Quercus robur}, \emph{Quercus suber}
+
+\strong{Available components:}
+
+Aboveground / belowground groups (summed automatically):
+\itemize{
+\item \code{"AGB"} — total aboveground biomass
+\item \code{"BGB"} — total belowground biomass (roots)
+\item \code{"all"} or \code{"tree"} — total tree biomass (AGB + BGB)
+}
+
+Tree structural groups:
\itemize{
-\item \strong{AGB}: all aboveground biomass components
-\item \strong{BGB}: all belowground biomass compoponents
-\item \strong{tree} or \strong{all}: total tree biomass includying AGB and BGB
+\item \code{"stem"} — stem fraction(s)
+\item \code{"branches"} — all branch fractions combined
+\item \code{"roots"} — roots (equivalent to BGB)
}
-Then we have the second group of components, which are related to tree groups:
+Individual tree components (species availability varies):
\itemize{
-\item \strong{stem}: includes the stem and bark
-\item \strong{branches}: includes all branches
-\item \strong{roots}: includes the roots (same as BGB)
+\item \code{"stem"} — stem wood
+\item \code{"stem and thick branches"} — stem together with branches > 7 cm
+\item \code{"thick branches"} — branches > 7 cm
+\item \code{"medium branches"} — branches 2–7 cm
+\item \code{"small branches"} — branches < 2 cm
+\item \code{"leaves"} — foliage (including needles)
+\item \code{"roots"} — coarse roots
}
-Finally, we have the last level, which includes tree components (not all of them
-are available for all species): stem, bark, thick branches (>7cm), medium branches (2-7cm),
-thin branches (0.5-2cm), leaves (include needles), roots. In some species,
-there's "stem and thick branches", instead of two groups.
+Note that for this model, \code{"tree"} (or \code{"all"}) is an independent regression equation fitted to total-tree data. It was \strong{not} derived by summing the AGB and BGB equations.
+Consequently, there is a numerical discrepancy between the direct \code{"tree"} estimation and the sum of separate \code{"AGB"} and \code{"BGB"} estimations (e.g. for \emph{Pinus sylvestris} at diameter = 20 cm and height = 10 m, the direct total is 89.1 kg, while AGB + BGB is 115.9 kg, a 24\% difference).
+
+Also, the following 6 species have no BGB/roots equations in this model: \emph{Abies pinsapo}, \emph{Erica arborea}, \emph{Eucalyptus} spp., \emph{Ilex canariensis}, \emph{Laurus azorica}, \emph{Myrica faya} (requesting \code{"BGB"} or \code{"roots"} will fail).
-Users can check the list of supported species and their corresponding components
-in \link{biomass_models}.
+Users can check all available species and components in the \link{biomass_models} dataset provided by the library.
}
\examples{
-## get model parameters for silv_predict_biomass
+## Aboveground biomass for Pinus pinaster
eq_biomass_montero_2005("Pinus pinaster", "AGB")
}
\seealso{
-\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}}
+\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}},
\code{\link[=eq_biomass_ruiz_peinado_2011]{eq_biomass_ruiz_peinado_2011()}}, \code{\link[=eq_biomass_ruiz_peinado_2012]{eq_biomass_ruiz_peinado_2012()}}, \code{\link[=eq_biomass_manrique_2017]{eq_biomass_manrique_2017()}},
\code{\link[=eq_biomass_menendez_2022]{eq_biomass_menendez_2022()}}, \code{\link[=eq_biomass_cudjoe_2024]{eq_biomass_cudjoe_2024()}}
}
diff --git a/man/eq_biomass_ruiz_peinado_2011.Rd b/man/eq_biomass_ruiz_peinado_2011.Rd
index 3876c19..94e0023 100644
--- a/man/eq_biomass_ruiz_peinado_2011.Rd
+++ b/man/eq_biomass_ruiz_peinado_2011.Rd
@@ -24,12 +24,43 @@ A S7 list of parameters
Allometric equations adjusted for Spanish softwood species
}
\details{
-Users can check the list of supported species and their corresponding components
-in \link{biomass_models}.
+\strong{Supported species (10):}
+
+\emph{Abies alba}, \emph{Abies pinsapo}, \emph{Juniperus thurifera}, \emph{Pinus canariensis},
+\emph{Pinus halepensis}, \emph{Pinus nigra}, \emph{Pinus pinaster}, \emph{Pinus pinea},
+\emph{Pinus sylvestris}, \emph{Pinus uncinata}
+
+\strong{Available components:}
+
+Aboveground / belowground groups (summed automatically):
+\itemize{
+\item \code{"AGB"} — total aboveground biomass
+\item \code{"BGB"} — total belowground biomass (roots)
+\item \code{"tree"} — total tree biomass (AGB + BGB)
+}
+
+Tree structural groups:
+\itemize{
+\item \code{"stem"} — stem wood
+\item \code{"branches"} — all branch fractions combined
+\item \code{"roots"} — roots (equivalent to BGB)
+}
+
+Individual tree components (species availability varies):
+\itemize{
+\item \code{"thick branches"} — branches > 7 cm
+\item \code{"thick and medium branches"} — branches > 2 cm
+\item \code{"medium branches"} — branches 2–7 cm
+\item \code{"small branches and leaves"} — branches < 2 cm including leaves/needles
+}
+
+Note that \emph{Abies pinsapo} does not have a separate BGB equation (requesting \code{"BGB"} or \code{"roots"} will fail, though \code{"tree"} still works via its pre-summed formula).
+
+Users can check all available species and components in the \link{biomass_models} dataset provided by the library.
}
\examples{
-## get model parameters for silv_predict_biomass
-eq_biomass_ruiz_peinado_2011("Pinus pinaster")
+## Aboveground biomass for Pinus pinaster
+eq_biomass_ruiz_peinado_2011("Pinus pinaster", "AGB")
}
\seealso{
\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}},
diff --git a/man/eq_biomass_ruiz_peinado_2012.Rd b/man/eq_biomass_ruiz_peinado_2012.Rd
index b71f964..fcf9509 100644
--- a/man/eq_biomass_ruiz_peinado_2012.Rd
+++ b/man/eq_biomass_ruiz_peinado_2012.Rd
@@ -24,15 +24,50 @@ A S7 list of parameters
Allometric equations adjusted for Spanish hardwood species
}
\details{
-Users can check the list of supported species and their corresponding components
-in \link{biomass_models}.
+\strong{Supported species (13):}
+
+\emph{Alnus glutinosa}, \emph{Castanea sativa}, \emph{Ceratonia siliqua}, \emph{Eucalyptus globulus},
+\emph{Fagus sylvatica}, \emph{Fraxinus angustifolia}, \emph{Olea europaea}, \emph{Populus x euramericana},
+\emph{Quercus canariensis}, \emph{Quercus faginea}, \emph{Quercus ilex}, \emph{Quercus pyrenaica},
+\emph{Quercus suber}
+
+\strong{Available components:}
+
+Aboveground / belowground groups (summed automatically):
+\itemize{
+\item \code{"AGB"} — total aboveground biomass
+\item \code{"BGB"} — total belowground biomass (roots)
+\item \code{"tree"} — total tree biomass (AGB + BGB)
+}
+
+Tree structural groups:
+\itemize{
+\item \code{"stem"} — stem wood
+\item \code{"branches"} — all branch fractions combined
+\item \code{"roots"} — roots (equivalent to BGB)
+}
+
+Individual tree components (species availability varies):
+\itemize{
+\item \code{"stem and thick branches"} — stem together with branches > 7 cm
+\item \code{"thick branches"} — branches > 7 cm
+\item \code{"thick and medium branches"} — branches > 2 cm
+\item \code{"medium branches"} — branches 2–7 cm
+\item \code{"small branches"} — branches 0.5–2 cm
+\item \code{"small branches and leaves"} — branches < 2 cm including leaves
+\item \code{"medium branches, small branches and leaves"} — branches < 7 cm including leaves
+}
+
+Note that \emph{Eucalyptus globulus} does not have a separate BGB equation (requesting \code{"BGB"} or \code{"roots"} will fail, though \code{"tree"} still works via its pre-summed formula).
+
+Users can check all available species and components in the \link{biomass_models} dataset provided by the library.
}
\examples{
-## get model parameters for silv_predict_biomass
-eq_biomass_ruiz_peinado_2012("Quercus suber")
+## Aboveground biomass for Quercus suber
+eq_biomass_ruiz_peinado_2012("Quercus suber", "AGB")
}
\seealso{
-\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}}
+\code{\link[=silv_predict_biomass]{silv_predict_biomass()}}, \link{biomass_models}, \code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}, \code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}},
\code{\link[=eq_biomass_ruiz_peinado_2011]{eq_biomass_ruiz_peinado_2011()}}, \code{\link[=eq_biomass_manrique_2017]{eq_biomass_manrique_2017()}}, \code{\link[=eq_biomass_menendez_2022]{eq_biomass_menendez_2022()}},
\code{\link[=eq_biomass_cudjoe_2024]{eq_biomass_cudjoe_2024()}}
}
diff --git a/man/silv_predict_biomass.Rd b/man/silv_predict_biomass.Rd
index b89eadd..090592b 100644
--- a/man/silv_predict_biomass.Rd
+++ b/man/silv_predict_biomass.Rd
@@ -9,11 +9,13 @@ silv_predict_biomass(
height = NULL,
model,
ntrees = NULL,
+ rcd = NULL,
+ bp = NULL,
quiet = FALSE
)
}
\arguments{
-\item{diameter}{A numeric vector of tree diameters (in cm).}
+\item{diameter}{A numeric vector of tree diameters at breast height (in cm).}
\item{height}{A numeric vector of tree heights (in m).}
@@ -23,6 +25,15 @@ additional arguments depending on the model used.}
\item{ntrees}{An optional numeric value indicating the number of trees in
this diameter-height class. Defaults to 1 if \code{NULL}.}
+\item{rcd}{An optional numeric vector of root collar diameters (in cm). Required
+for \code{\link{eq_biomass_menendez_2022}}, which uses root collar diameter
+instead of diameter at breast height. Defaults to \code{diameter} if \code{NULL}.}
+
+\item{bp}{An optional numeric vector of biomass packing values (in m\ifelse{html}{\out{3}}{$^3$}). Required
+for a subset of species in \code{\link{eq_biomass_menendez_2022}} (e.g.
+\emph{Pinus halepensis}, \emph{Pinus nigra}, \emph{Quercus suber},
+\emph{Evergreen broadleaves}).}
+
\item{quiet}{A logical value. If \code{TRUE}, suppresses any informational messages.}
}
\value{
@@ -38,6 +49,14 @@ dataset \link{biomass_models}. The available models include:
\itemize{
\item \strong{\code{\link[=eq_biomass_ruiz_peinado_2011]{eq_biomass_ruiz_peinado_2011()}}}: Developed for softwood species in Spain.
\item \strong{\code{\link[=eq_biomass_ruiz_peinado_2012]{eq_biomass_ruiz_peinado_2012()}}}: Developed for hardwood species in Spain.
+\item \strong{\code{\link[=eq_biomass_montero_2005]{eq_biomass_montero_2005()}}}: Developed for 35 Spanish species.
+\item \strong{\code{\link[=eq_biomass_dieguez_aranda_2009]{eq_biomass_dieguez_aranda_2009()}}}: Developed for 7 Galician species.
+\item \strong{\code{\link[=eq_biomass_manrique_2017]{eq_biomass_manrique_2017()}}}: Developed for \emph{Quercus petraea} and \emph{Quercus pyrenaica}.
+\item \strong{\code{\link[=eq_biomass_menendez_2022]{eq_biomass_menendez_2022()}}}: Developed for young plantations (< 30 years) of 18
+Spanish species. Uses \code{rcd} (root collar diameter) instead of \code{diameter}; some species
+require \code{bp} (biomass packing) instead of \code{rcd}.
+\item \strong{\code{\link[=eq_biomass_cudjoe_2024]{eq_biomass_cudjoe_2024()}}}: Developed for \emph{Pinus sylvestris} and \emph{Quercus petraea}
+in Castille and León, Spain.
}
Users can check the list of supported species and their corresponding components
diff --git a/man/silv_predict_biomass_auto.Rd b/man/silv_predict_biomass_auto.Rd
new file mode 100644
index 0000000..6313500
--- /dev/null
+++ b/man/silv_predict_biomass_auto.Rd
@@ -0,0 +1,66 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/predict-biomass.R
+\name{silv_predict_biomass_auto}
+\alias{silv_predict_biomass_auto}
+\title{Automatically Predict Biomass Using the Best Available Model}
+\usage{
+silv_predict_biomass_auto(
+ species,
+ diameter,
+ height = NULL,
+ component = "tree",
+ ntrees = NULL,
+ rcd = NULL,
+ bp = NULL,
+ priority = c("ruiz-peinado-2011", "ruiz-peinado-2012", "montero-2005",
+ "dieguez-aranda-2009", "manrique-2017", "menendez-2022", "cudjoe-2024"),
+ quiet = FALSE
+)
+}
+\arguments{
+\item{species}{A character vector specifying the scientific names of the tree species.}
+
+\item{diameter}{A numeric vector of tree diameters at breast height (in cm).}
+
+\item{height}{An optional numeric vector of tree heights (in m). Defaults to \code{NULL}.}
+
+\item{component}{A character string specifying the tree component for biomass
+calculation (e.g., "tree", "stem", "branches"). Defaults to \code{"tree"}.}
+
+\item{ntrees}{An optional numeric vector indicating the number of trees in
+each class. Defaults to \code{NULL} (equivalent to 1 tree per entry).}
+
+\item{rcd}{An optional numeric vector of root collar diameters (in cm). Required
+for young plantation equations (\code{\link{eq_biomass_menendez_2022}}).}
+
+\item{bp}{An optional numeric vector of biomass packing values (in m\ifelse{html}{\out{3}}{$^3$}). Required
+for some species in young plantation equations.}
+
+\item{priority}{A character vector specifying the priority order for model selection.
+Defaults to \code{c("ruiz-peinado-2011", "ruiz-peinado-2012", "montero-2005", "dieguez-aranda-2009", "manrique-2017", "menendez-2022", "cudjoe-2024")}.}
+
+\item{quiet}{A logical value. If \code{TRUE}, suppresses any informational messages and warnings.}
+}
+\value{
+A \code{data.frame} containing two columns:
+\itemize{
+\item \code{biomass}: Numeric vector of predicted biomass values (in kg).
+\item \code{biomass_model}: Character vector specifying the model ID used.
+}
+}
+\description{
+Evaluates vectors of tree species, diameters, and heights, and automatically
+selects the best available allometric model based on a specified priority.
+If tree height is not provided, is NA, or is 0, the function automatically
+falls back to the \code{\link{eq_biomass_montero_2005}} model.
+}
+\examples{
+# Predict biomass using default priorities
+species_vec <- c("Pinus pinaster", "Quercus petraea")
+d_vec <- c(20, 25)
+h_vec <- c(12, 14)
+silv_predict_biomass_auto(species_vec, d_vec, h_vec)
+
+# Fallback to Montero 2005 when height is missing
+silv_predict_biomass_auto(species_vec, d_vec, height = NULL)
+}
diff --git a/man/silv_predict_biomass_components.Rd b/man/silv_predict_biomass_components.Rd
new file mode 100644
index 0000000..8b07915
--- /dev/null
+++ b/man/silv_predict_biomass_components.Rd
@@ -0,0 +1,54 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/predict-biomass.R
+\name{silv_predict_biomass_components}
+\alias{silv_predict_biomass_components}
+\title{Predict All Individual Biomass Components for Trees}
+\usage{
+silv_predict_biomass_components(
+ species,
+ diameter,
+ height = NULL,
+ model_fn,
+ ntrees = NULL,
+ rcd = NULL,
+ bp = NULL,
+ quiet = TRUE
+)
+}
+\arguments{
+\item{species}{A character vector specifying the scientific names of the tree species.}
+
+\item{diameter}{A numeric vector of tree diameters at breast height (in cm).}
+
+\item{height}{An optional numeric vector of tree heights (in m). Defaults to \code{NULL}.}
+
+\item{model_fn}{A function or character string. The constructer function of the model
+(e.g., \code{eq_biomass_ruiz_peinado_2011}) or the model ID string (e.g., \code{"ruiz-peinado-2011"}).}
+
+\item{ntrees}{An optional numeric vector indicating the number of trees in
+each class. Defaults to \code{NULL}.}
+
+\item{rcd}{An optional numeric vector of root collar diameters (in cm).}
+
+\item{bp}{An optional numeric vector of biomass packing values (in m\ifelse{html}{\out{3}}{$^3$}).}
+
+\item{quiet}{A logical value. If \code{TRUE}, suppresses any informational messages.}
+}
+\value{
+A \code{data.frame} with the columns \code{species}, \code{diameter},
+\code{height} (if provided), and one additional column for each individual biomass
+component available for the selected species and model.
+}
+\description{
+Predicts all available individual biomass components (e.g., stem, bark, branches,
+roots) for the given trees in a single call, returning a wide data frame.
+}
+\examples{
+# Predict all components using Ruiz-Peinado 2011
+silv_predict_biomass_components(
+ species = "Pinus pinaster",
+ diameter = 25,
+ height = 15,
+ model_fn = eq_biomass_ruiz_peinado_2011
+)
+}
diff --git a/tests/testthat/test-predict-biomass.R b/tests/testthat/test-predict-biomass.R
new file mode 100644
index 0000000..247ad9f
--- /dev/null
+++ b/tests/testthat/test-predict-biomass.R
@@ -0,0 +1,149 @@
+test_that("eq_biomass_* custom error messages for BGB and tree gaps work", {
+ # Ruiz-Peinado 2011 BGB gap
+ expect_error(
+ eq_biomass_ruiz_peinado_2011("Abies pinsapo", "BGB"),
+ class = "rlang_error",
+ regexp = "Model .*ruiz-peinado-2011.* does not include BGB equations for .*Abies pinsapo.*"
+ )
+ expect_error(
+ eq_biomass_ruiz_peinado_2011("Abies pinsapo", "roots"),
+ class = "rlang_error",
+ regexp = "Model .*ruiz-peinado-2011.* does not include BGB equations for .*Abies pinsapo.*"
+ )
+
+ # Ruiz-Peinado 2012 BGB gap
+ expect_error(
+ eq_biomass_ruiz_peinado_2012("Eucalyptus globulus", "BGB"),
+ class = "rlang_error",
+ regexp = "Model .*ruiz-peinado-2012.* does not include BGB equations for .*Eucalyptus globulus.*"
+ )
+
+ # Dieguez-Aranda 2009 tree and BGB gaps
+ expect_error(
+ eq_biomass_dieguez_aranda_2009("Pinus pinaster", "tree"),
+ class = "rlang_error",
+ regexp = "Model .*dieguez-aranda-2009.* does not include total-tree.*equations"
+ )
+ expect_error(
+ eq_biomass_dieguez_aranda_2009("Pinus pinaster", "BGB"),
+ class = "rlang_error",
+ regexp = "Model .*dieguez-aranda-2009.* does not include BGB equations for .*Pinus pinaster.*"
+ )
+
+ # Montero 2005 BGB gaps
+ expect_error(
+ eq_biomass_montero_2005("Abies pinsapo", "BGB"),
+ class = "rlang_error",
+ regexp = "Model .*montero-2005.* does not include BGB equations for .*Abies pinsapo.*"
+ )
+
+ # Manrique 2017 & Cudjoe 2024 gaps
+ expect_error(
+ eq_biomass_manrique_2017("Quercus petraea", "BGB"),
+ class = "rlang_error",
+ regexp = "Model .*manrique-2017.* does not include BGB / total-tree equations"
+ )
+ expect_error(
+ eq_biomass_cudjoe_2024("mixed", "tree"),
+ class = "rlang_error",
+ regexp = "Model .*cudjoe-2024.* does not include BGB / total-tree equations"
+ )
+
+ # Test via silv_predict_biomass() wrapper
+ expect_error(
+ silv_predict_biomass(
+ diameter = 20,
+ height = 10,
+ model = eq_biomass_ruiz_peinado_2011("Abies pinsapo", "BGB")
+ ),
+ class = "rlang_error",
+ regexp = "Model .*ruiz-peinado-2011.* does not include BGB equations for .*Abies pinsapo.*"
+ )
+})
+
+test_that("silv_predict_biomass_auto priority and height fallback work", {
+ # 1. Normal priority selection (Pinus pinaster has height -> ruiz-peinado-2011)
+ res_normal <- silv_predict_biomass_auto(
+ species = c("Pinus pinaster"),
+ diameter = c(20),
+ height = c(12),
+ component = "tree",
+ quiet = TRUE
+ )
+ expect_equal(res_normal$biomass_model, "ruiz-peinado-2011")
+ expect_equal(nrow(res_normal), 1)
+
+ # 2. Height fallback (height is NULL -> montero-2005)
+ res_null_h <- silv_predict_biomass_auto(
+ species = c("Pinus pinaster"),
+ diameter = c(20),
+ height = NULL,
+ component = "tree",
+ quiet = TRUE
+ )
+ expect_equal(res_null_h$biomass_model, "montero-2005")
+
+ # 3. Height fallback (height is NA/0 -> montero-2005)
+ res_na_h <- silv_predict_biomass_auto(
+ species = c("Pinus pinaster", "Pinus pinaster"),
+ diameter = c(20, 20),
+ height = c(NA, 0),
+ component = "tree",
+ quiet = TRUE
+ )
+ expect_equal(res_na_h$biomass_model, c("montero-2005", "montero-2005"))
+
+ # 4. Warnings and NA for unsupported combinations
+ expect_warning(
+ res_unsupported <- silv_predict_biomass_auto(
+ species = c("Quercus petraea"),
+ diameter = c(25),
+ height = c(12),
+ component = "tree",
+ quiet = FALSE
+ ),
+ regexp = "No compatible model was found in priority"
+ )
+ expect_true(is.na(res_unsupported$biomass[1]))
+ expect_true(is.na(res_unsupported$biomass_model[1]))
+})
+
+test_that("silv_predict_biomass_components works in wide format", {
+ # 1. Ruiz-Peinado 2011 (Standard conifer)
+ res_peinado <- silv_predict_biomass_components(
+ species = c("Pinus pinaster", "Pinus pinaster"),
+ diameter = c(20, 25),
+ height = c(12, 15),
+ model_fn = eq_biomass_ruiz_peinado_2011,
+ quiet = TRUE
+ )
+ expect_equal(nrow(res_peinado), 2)
+ expect_true(all(c("species", "diameter", "height", "stem", "thick and medium branches", "small branches and leaves", "roots") %in% colnames(res_peinado)))
+ expect_false("tree" %in% colnames(res_peinado))
+
+ # 2. Menendez 2022 (Young plantation model, uses RCD, no component arg)
+ res_menendez <- silv_predict_biomass_components(
+ species = "Fagus sylvatica",
+ diameter = 2,
+ height = 3,
+ model_fn = eq_biomass_menendez_2022,
+ rcd = 2,
+ quiet = TRUE
+ )
+ expect_equal(nrow(res_menendez), 1)
+ expect_true("AGB" %in% colnames(res_menendez))
+ expect_false("roots" %in% colnames(res_menendez))
+
+ # 3. Unsupported species throws an error
+ expect_error(
+ silv_predict_biomass_components(
+ species = "Quercus petraea",
+ diameter = 25,
+ height = 15,
+ model_fn = eq_biomass_ruiz_peinado_2012
+ ),
+ class = "rlang_error",
+ regexp = "Species .*Quercus petraea.* is not supported by model .*ruiz-peinado-2012.*"
+ )
+})
+