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.*" + ) +}) +