# Berkeley Forests Analytics

## Introduction

In this vignette, we demonstrate the use of the `BerkeleyForestsAnalytics` package. We provide an example of how you might use the BFA package to compile pre- and post-burn field data. Through this example, we highlight some key components of the BFA package:

1. Handling of missing data
2. Handling of 0-values
3. Warning and error messages
   - Note: we do not demonstrate the full suite of warning and error messages. We demonstrate a subset of warnings and errors to give you an idea of what kinds of messages to expect, and how you might address them.

The vignette is not a replacement for the README file, which covers the inputs and outputs of each function in detail. Additionally, the README gives detailed background information and references for the methods used in the package. We recommend that you review the README prior to or in conjunction with the vignette (Find README here).

To begin, we’ll load the required packages:

    library(BerkeleyForestsAnalytics)
    library(dplyr)
    library(tidyr)

## Description of data

This vignette uses data from the Fire and Fire Surrogate (FFS) Study. In brief, FFS is an experimental study that was designed to evaluate the impacts of fire-only (prescribed fire), mechanical-only (mechanical thinning from below followed by mastication), and mechanical + fire (mechanical thinning from below followed by mastication followed by prescribed fire) treatments on forest structure, ecological function, and future fire behavior.

The data used in this vignette are from the fire-only (i.e., prescribed fire) stands. We used data from two time periods: before treatment (2001) and one year after treatment (2003). Note that the data were slightly modified for demonstrations purposes. Therefore, the outputs should not be taken to be actual findings from the FFS Study.

The tree data have the following columns:

1. `id` time:site combined
2. `time` pre (pre-burn) or post (post-burn)
3. `site` compartment (60, 340, or 400)
4. `plot` plot in which the individual tree was measured
5. `exp_factor` stems per hectare
6. `status` live (1) or dead (0)
7. `decay` decay class. 1-5 for standing dead trees. 0 for live trees.
8. `species` species of the individual tree, using four-letter species codes
9. `dbh` diameter at breast height in centimeters
10. `ht` total tree height in meters

The surface and ground fuels data have the following columns:

1. `time` pre (pre-burn) or post (post-burn)
2. `site` compartment (60, 340, or 400)
3. `plot` plot in which the individual transect was measured
4. `transect` azimuth of transect on which the fuel data were collected
5. `count_1h` count of 1-hour fuels
6. `count_10h` count of 10-hour fuels
7. `count_100h` count of 100-hour fuels
8. `length_1h` length of the sampling transect for 1-hour fuels in meters
9. `length_10h` length of the sampling transect for 10-hour fuels in meters
10. `length_100h` length of the sampling transect for 100-hour fuels in meters
11. `length_1000h` length of the sampling transect for 1000-hour fuels in meters
12. `ssd_S` sum-of-squared-diameters for sound 1000-hour fuels
13. `ssd_R` sum-of-squared-diameters for rotten 1000-hour fuels
14. `litter_depth` litter depth in centimeters
15. `duff_depth` duff depth in centimeters
16. `slope` slope along the transect in percent

## Tree biomass

First, we’ll use the `SummaryBiomass()` function to get above-ground stem, bark, and branch tree biomass at the plot level.

Let’s investigate the input dataframe:

    # Note that the example data used in this vignette is included with the package
    # which is why we do not have to read in the data

    head(vign_trees_1)

    ##         id time site plot exp_factor status decay species  dbh   ht
    ## 1 post_340 post  340  103      24.69      1     0    QUKE 85.3 35.2
    ## 2 post_340 post  340  103      24.69      1     0    QUKE 71.4 31.1
    ## 3 post_340 post  340  103      24.69      1     0    ABCO 34.3 22.5
    ## 4 post_340 post  340  103      24.69      1     0    ABCO 18.3 15.7
    ## 5 post_340 post  340  103      24.69      1     0    ABCO 52.8 33.0
    ## 6 post_340 post  340  103      24.69      1     0    ABCO 19.8 17.5

Attempt 1: Now, let’s try using `SummaryBiomass()`. We’ll keep the defaults for sp_codes (= “4letter”), units (= “metric”), and results (= “by_plot):

    tree_bio <- SummaryBiomass(data = vign_trees_1,
                               site = "id",
                               plot = "plot",
                               exp_factor = "exp_factor",
                               status = "status",
                               decay_class = "decay",
                               species = "species",
                               dbh = "dbh",
                               ht = "ht")

    ## Error in `ValidateSumData()`:
    ## ! There are plots with a recorded expansion factor of 0, but with more than one row.
    ## Plots with no trees should be represented by a single row with site and plot filled in as appropriate and an exp_factor of 0.

And we get an error message. It looks like there is an improper use of a 0 expansion factor. An expansion factor of 0 should only be used to represent a plot with no trees. Let’s look at where 0 expansion factors show up in the data:

    vign_trees_1 %>%
      filter(exp_factor == 0)

    ##        id time site plot exp_factor status decay species  dbh   ht
    ## 1 post_60 post   60  112          0      1     0    CADE 25.4 14.4
    ## 2 post_60 post   60  113          0   <NA>  <NA>    <NA>   NA   NA

    vign_trees_1 %>%
      filter(time == "post", site == "60", plot == "112")

    ##         id time site plot exp_factor status decay species  dbh   ht
    ## 1  post_60 post   60  112      24.69      0     3    QUKE 51.1 22.2
    ## 2  post_60 post   60  112      24.69      1     0    ABCO 69.9 30.9
    ## 3  post_60 post   60  112      24.69      1     0    ABCO 58.2 30.1
    ## 4  post_60 post   60  112      24.69      1     0    ABCO 23.6 17.6
    ## 5  post_60 post   60  112      24.69      1     0    ABCO 42.7 24.0
    ## 6  post_60 post   60  112      24.69      1     0    PSME 51.8 29.4
    ## 7  post_60 post   60  112      24.69      1     0    CADE 25.1 11.5
    ## 8  post_60 post   60  112      24.69      1     0    CADE 53.8 25.0
    ## 9  post_60 post   60  112      24.69      1     0    CADE 42.7 23.1
    ## 10 post_60 post   60  112       0.00      1     0    CADE 25.4 14.4

It looks like a 0 expansion factor is properly used for post-60-113, but improperly used for post-60-112. We know what plot radius was used for larger trees, so we can confidently fill in the correct exp_factor here (24.69). The expansion factor will differ among studies. If a nested plot design was used, the expansion factor will differ among trees within the same plot.

Attempt 2: After correcting the expansion factor in the input data, let’s try again:

    tree_bio <- SummaryBiomass(data = vign_trees_2,
                               site = "id",
                               plot = "plot",
                               exp_factor = "exp_factor",
                               status = "status",
                               decay_class = "decay",
                               species = "species",
                               dbh = "dbh",
                               ht = "ht",
                               results = "by_plot")

    ## Warning in ValidateSumData(data_val = data, site_val = site, plot_val = plot, : There are dead trees with NA and/or zero decay class codes.
    ## The biomass of these dead trees will NOT be adjusted.
    ## Consider investigating these trees with mismatched status/decay class.
    ##

    ## Error in `ValidateSumData()`:
    ## ! Not all species codes were recognized!
    ## Unrecognized codes: ABCCO SME

And we get another error message. It looks like there are some typos/transcription errors in the species codes. Looking at the list of unrecognized codes, we can tell that “ABCCO” should be “ABCO” (Abies concolor, commonly known as white fir) and “SME” should probably be “PSME” (Pseudotsuga menziesii, commonly known as Douglas-fir). Depending on the severity of the typo, you may want to go back to double check the species recorded on the original datasheet. In this case, the typos are fairly obvious. Let’s figure out where these typos occur in the data:

    vign_trees_2 %>%
      filter(species == "ABCCO" | species == "SME")

    ##         id time site plot exp_factor status decay species  dbh   ht
    ## 1 post_340 post  340  108      24.69      1     0   ABCCO 12.7 11.3
    ## 2 post_340 post  340  109      24.69      1     0     SME 19.6 13.4

Attempt 3: After correcting the species codes in the input data, let’s try again:

    tree_bio <- SummaryBiomass(data = vign_trees_3,
                               site = "id",
                               plot = "plot",
                               exp_factor = "exp_factor",
                               status = "status",
                               decay_class = "decay",
                               species = "species",
                               dbh = "dbh",
                               ht = "ht",
                               results = "by_plot")

    ## Warning in ValidateSumData(data_val = data, site_val = site, plot_val = plot, : There are dead trees with NA and/or zero decay class codes.
    ## The biomass of these dead trees will NOT be adjusted.
    ## Consider investigating these trees with mismatched status/decay class.
    ##

    ## Warning in ValidateSumData(data_val = data, site_val = site, plot_val = plot, : There are missing DBH values in the provided dataframe.
    ## Trees with NA DBH will have NA biomass estimates.
    ##

    ## Warning in ValidateSumData(data_val = data, site_val = site, plot_val = plot, : The allometric equations are for live trees with DBH >= 2.54cm and dead trees with DBH >= 12.7cm.
    ## You inputted live trees with DBH < 2.54cm. These trees will have NA biomass estimates.
    ##

    head(tree_bio)

    ##       site plot live_Mg_ha dead_Mg_ha
    ## 1 post_340  103     470.15      10.08
    ## 2 post_340  104     241.31       0.00
    ## 3 post_340  108     156.67       0.00
    ## 4 post_340  109     169.09       0.00
    ## 5 post_340  111     189.39       4.31
    ## 6 post_340  112     823.50      14.57

This time the function runs. However, we get some warning messages. Let’s look at the first warning, which tells us that there are trees with mismatched status/decay class. Recall that dead trees should have a decay class of 1-5 and live trees should have a decay class of NA or 0 (in this dataset we use 0 for live trees). Let’s figure out where mismatches occur in the data:

    vign_trees_3 %>%
      filter(status == 0, decay == 0 | is.na(decay))

    ##       id time site plot exp_factor status decay species  dbh   ht
    ## 1 pre_60  pre   60   27      24.69      0     0    CADE 42.2 21.6

This CADE (Calocedrus decurrens, commonly known as incense cedar) was documented as dead (status = 0) but was assigned a decay class of 0 (which is reserved for live trees). We look back at the original datasheet (not shown here) and see that the tree was recorded as dead with a decay class of 0 in the field (which tells us this was not a transcription error). However, we also notice that a height to live crown base was recorded for the tree, indicating that the dead status was likely a recording error. Given that two pieces of information recorded for the tree point to a live status (i.e., a decay class of 0 and a height to live crown base), we can fairly confidently change the CADE’s status to live.

Attempt 4: After correcting for the mismatch in status/decay class, let’s try again:

    tree_bio <- SummaryBiomass(data = vign_trees_4,
                               site = "id",
                               plot = "plot",
                               exp_factor = "exp_factor",
                               status = "status",
                               decay_class = "decay",
                               species = "species",
                               dbh = "dbh",
                               ht = "ht",
                               results = "by_plot")

    ## Warning in ValidateSumData(data_val = data, site_val = site, plot_val = plot, : There are missing DBH values in the provided dataframe.
    ## Trees with NA DBH will have NA biomass estimates.
    ##

    ## Warning in ValidateSumData(data_val = data, site_val = site, plot_val = plot, : The allometric equations are for live trees with DBH >= 2.54cm and dead trees with DBH >= 12.7cm.
    ## You inputted live trees with DBH < 2.54cm. These trees will have NA biomass estimates.
    ##

    head(tree_bio)

    ##       site plot live_Mg_ha dead_Mg_ha
    ## 1 post_340  103     470.15      10.08
    ## 2 post_340  104     241.31       0.00
    ## 3 post_340  108     156.67       0.00
    ## 4 post_340  109     169.09       0.00
    ## 5 post_340  111     189.39       4.31
    ## 6 post_340  112     823.50      14.57

The first warning message is gone (good!), but we still have two other warning messages. Let’s look at the next warning, which tells us there are missing DBH values in the dataframe. Let’s look at where NA DBH values show up in the data:

    vign_trees_4 %>%
      filter(exp_factor > 0, is.na(dbh))

    ##        id time site plot exp_factor status decay species dbh   ht
    ## 1 pre_340  pre  340  115      24.69      1     0    PILA  NA 24.6

We look back at the original datasheet (not shown here) and see that DBH was recorded for the tree in the field. This was just a simple transcription error. However, in your own dataset you may have DBH values (or height values) that are truly missing. In the case of truly missing values, you may want to build a model that will allow you to predict DBH from total height (or total height from DBH). Such models may already exist for your study area.

Attempt 5: After filling in the missing DBH value, let’s try again:

    tree_bio <- SummaryBiomass(data = vign_trees_5,
                               site = "id",
                               plot = "plot",
                               exp_factor = "exp_factor",
                               status = "status",
                               decay_class = "decay",
                               species = "species",
                               dbh = "dbh",
                               ht = "ht",
                               results = "by_plot")

    ## Warning in ValidateSumData(data_val = data, site_val = site, plot_val = plot, : The allometric equations are for live trees with DBH >= 2.54cm and dead trees with DBH >= 12.7cm.
    ## You inputted live trees with DBH < 2.54cm. These trees will have NA biomass estimates.
    ##

    head(tree_bio)

    ##       site plot live_Mg_ha dead_Mg_ha
    ## 1 post_340  103     470.15      10.08
    ## 2 post_340  104     241.31       0.00
    ## 3 post_340  108     156.67       0.00
    ## 4 post_340  109     169.09       0.00
    ## 5 post_340  111     189.39       4.31
    ## 6 post_340  112     823.50      14.57

There’s only one warning message left. The Forest Inventory and Analysis (FIA) Regional Biomass Equations are for live trees with a DBH >= 2.54 cm (1 in) and dead trees with a DBH >= 12.7 cm (5 in). It is not appropriate to use these equations to estimate biomass for trees with DBH below the specified cutoffs. We could filter these trees out of the dataset to avoid the warning message; however, that isn’t necessary. In this example, we’ll leave the small trees in and make note of the warning. If you need to calculate biomass for the smaller trees in your dataset, you will need to explore other options.

## Forest composition and structure

Next, we’ll use the `ForestComp()` and `ForestStr()` functions to get forest composition and structure at the plot level.

### ForestComp()

Let’s try using `ForestComp()` first:

    for_comp <- ForestComp(data = vign_trees_5,
                           site = "id",
                           plot = "plot",
                           exp_factor = "exp_factor",
                           status = "status",
                           species = "species",
                           dbh = "dbh",
                           relative = "ba",
                           units = "metric")

    ## Error in `ValidateCompData()`:
    ## ! The "relative" parameter must be set to either "BA" or "density".

We get an error message. It looks like we set the “relative” parameter to “ba” instead of “BA”. That’s easy to fix:

    for_comp <- ForestComp(data = vign_trees_5,
                           site = "id",
                           plot = "plot",
                           exp_factor = "exp_factor",
                           status = "status",
                           species = "species",
                           dbh = "dbh",
                           relative = "BA",
                           units = "metric")

    ## The following species were present: ABCO CADE CONU NODE PILA PIPO PSME QUKE

    head(for_comp, n = 16)

    ##        site plot species dominance
    ## 1  post_340  103    QUKE      34.7
    ## 2  post_340  103    ABCO      39.2
    ## 3  post_340  103    CADE      26.1
    ## 4  post_340  103    PSME       0.0
    ## 5  post_340  103    NODE       0.0
    ## 6  post_340  103    PIPO       0.0
    ## 7  post_340  103    PILA       0.0
    ## 8  post_340  103    CONU       0.0
    ## 9  post_340  104    QUKE      16.3
    ## 10 post_340  104    ABCO      24.3
    ## 11 post_340  104    CADE       5.8
    ## 12 post_340  104    PSME      53.7
    ## 13 post_340  104    NODE       0.0
    ## 14 post_340  104    PIPO       0.0
    ## 15 post_340  104    PILA       0.0
    ## 16 post_340  104    CONU       0.0

This time the function runs without any warning or error messages. However, there is a note with a list of all the species present in the input data. Notice in the output (we just show two plots here) that each species present is accounted for in each plot, even if the dominance for the specific species is 0. When you compile the data beyond the plot level (e.g. to the compartment level), it’s important that the 0 dominance values are captured.

Before moving on, let’s quickly look at the output for the plot with no trees:

    for_comp %>%
      filter(site == "post_60", plot == "113")

    ##      site plot species dominance
    ## 1 post_60  113    QUKE        NA
    ## 2 post_60  113    ABCO        NA
    ## 3 post_60  113    CADE        NA
    ## 4 post_60  113    PSME        NA
    ## 5 post_60  113    NODE        NA
    ## 6 post_60  113    PIPO        NA
    ## 7 post_60  113    PILA        NA
    ## 8 post_60  113    CONU        NA

Notice that the dominance is NA for all species. If there are no trees present, then % dominance is not applicable (i.e., NA). But why wouldn’t it make sense to say that there is 0% CADE? There’s no CADE on the plot, right? However, think about what % dominance actually is:

\(\frac{BA_{sp,p}}{BA_{total,p}}\)

where

- \(BA_{sp,p}\) is the basal area occupied by species sp in plot p
- \(BA_{total,p}\) is the total basal area for plot p

If there are no trees present on plot p, then \(BA_{total,p}\) will be 0. And anything divided by 0 is not defined. From both a mathematical and logical perspective, % dominance for a plot without trees is not applicable.

### ForestStr()

Now let’s try using `ForestStr()`. We’ll keep the default for units (= “metric”):

    for_str <- ForestStr(data = vign_trees_5,
                         site = "id",
                         plot = "plot",
                         exp_factor = "Exp_factor",
                         dbh = "dbh",
                         ht = "ht")

    ## Error in `ValidateStrData()`:
    ## ! There is no column named "Exp_factor" in the provided dataframe.

We get an error message. Right, we have a column named “exp_factor” but not “Exp_factor” (column names are case sensitive)! That’s easy to fix:

    for_str <- ForestStr(data = vign_trees_5,
                         site = "id",
                         plot = "plot",
                         exp_factor = "exp_factor",
                         dbh = "dbh",
                         ht = "ht")

    head(for_str)

    ##       site plot sph ba_m2_ha qmd_cm dbh_cm ht_m
    ## 1 post_340  103 691    74.04   36.9   32.1 19.1
    ## 2 post_340  104 296    41.38   42.2   36.2 17.5
    ## 3 post_340  108 963    41.17   23.3   21.4 11.5
    ## 4 post_340  109 938    35.22   21.9   16.2  9.3
    ## 5 post_340  111 296    40.52   41.7   38.2 18.9
    ## 6 post_340  112 667   103.76   44.5   38.5 23.7

This time the function runs without any warning or error messages. But before moving on, let’s once again look at the output for the plot with no trees:

    for_str %>%
      filter(site == "post_60", plot == "113")

    ##      site plot sph ba_m2_ha qmd_cm dbh_cm ht_m
    ## 1 post_60  113   0        0     NA     NA   NA

Notice that basal area (ba_m2_ha) and stems per hectare (sph) are both 0. This is correct for a plot with no trees. Then, average quadratic mean diameter (qmd_cm), average diameter at breast height (dbh_cm), and average height (ht_m) are all NA. These tree-level variables are not applicable for a plot without trees. For example, there is no “average diameter at breast height” if there are no diameters at breast height to take an average of. The average diameter at breast height is not 0 in this case! When you compile the data beyond the plot level (e.g., to the compartment level), it’s important that the 0s and NAs are accurately captured. A 0 qmd_cm here (at the plot level) would incorrectly pull down the compartment qmd_cm.

## Surface and ground fuel loads

Finally, we’ll use the `FineFuels()`, `CoarseFuels()`, and `LitterDuff()` functions to get surface and ground fuel loads at the plot level.

Let’s investigate the input dataframe:

    head(vign_fuels_1)

    ##   time site plot transect count_1h count_10h count_100h length_1h length_10h
    ## 1 post  340  103       17        4         1          1      1.83       1.83
    ## 2 post  340  103       89       11         2          1      1.83       1.83
    ## 3 post  340  104       13        4         0          1      1.83       1.83
    ## 4 post  340  104       60        3         0          0      1.83       1.83
    ## 5 post  340  108        7       11         1          3      1.83       1.83
    ## 6 post  340  108       80       14         4          3      1.83       1.83
    ##   length_100h length_1000h ssd_S ssd_R litter_depth duff_depth slope
    ## 1        3.05        11.34     0    64         0.00        0.0     7
    ## 2        3.05        11.34     0     0         0.00        0.0    15
    ## 3        3.05        11.34     0     0         0.00        0.0     6
    ## 4        3.05        11.34     0   313         0.50        0.5     8
    ## 5        3.05        11.34     0   289         0.75        0.5     7
    ## 6        3.05        11.34     0     0         0.00        0.0     2

### FineFuels()

Attempt 1: Now, let’s try using `FineFuels()`. We’ll keep the defaults for sp_codes (= “4letter”) and units (= “metric”):

    FWD <- FineFuels(tree_data = vign_trees_5,
                     fuel_data = vign_fuels_1)

    ## Error in `ValidateFWD()`:
    ## ! For fuel_data, there are repeat time:site:plot:transect observations.
    ## There should only be one observation/row for an individual transect at a specific time:site:plot.
    ## Investigate the following time:site:plot:transect combinations: post-400-9-159

And we get an error message. It looks like there is a duplicate time:site:plot:transect observation for post-400-9-159. Let’s take a closer look:

    vign_fuels_1 %>%
      filter(time == "post", site == "400", plot == "9")

    ##   time site plot transect count_1h count_10h count_100h length_1h length_10h
    ## 1 post  400    9      159       19         2          0      1.83       1.83
    ## 2 post  400    9      159       28         5          0      1.83       1.83
    ##   length_100h length_1000h ssd_S ssd_R litter_depth duff_depth slope
    ## 1        3.05        11.34     0     0          1.5        1.0    11
    ## 2        3.05        11.34     0     0          1.0        0.5    11

    vign_fuels_1 %>%
      filter(time == "pre", site == "400", plot == "9")

    ##   time site plot transect count_1h count_10h count_100h length_1h length_10h
    ## 1  pre  400    9      159       32         8          0      1.83       1.83
    ## 2  pre  400    9      239       42         5          0      1.83       1.83
    ##   length_100h length_1000h ssd_S ssd_R litter_depth duff_depth slope
    ## 1        3.05        11.34     0     0            1        4.5    11
    ## 2        3.05        11.34   225     0            1        0.5    11

Based on the field protocol, we know that there should be two transects per time:site:plot and that the two azimuths should be the same pre- and post-treatment. For the post-treatment observations, we can see that the counts, litter depth, and duff depth are not the same. Looking at the pre-treatment observations, we can see that the transect azimuths for site 400, plot 9 should be 159 and 239. It’s likely that one of the two post-400-9 transects should be changed to 239. We look back at the original datasheet (not shown here) and see that this is indeed the case.

Attempt 2: After correcting the transect azimuth in the input fuel data, let’s try again:

    FWD <- FineFuels(tree_data = vign_trees_5,
                     fuel_data = vign_fuels_2)

    ## Error in `ValidateFWD()`:
    ## ! For fuel_data, count_100h must be a positive, whole number.

We get another error message. It looks like there is an issue with one (or more) of the 100-hour counts. Let’s figure out where the issue occurs in the data:

    vign_fuels_2 %>%
      mutate(count_100h_check = abs(round(count_100h))) %>%
      filter(count_100h != count_100h_check)

    ##   time site plot transect count_1h count_10h count_100h length_1h length_10h
    ## 1  pre  340   13      215       35        13        1.1      1.83       1.83
    ##   length_100h length_1000h ssd_S ssd_R litter_depth duff_depth slope
    ## 1        3.05        11.34    81   100          3.5          3     7
    ##   count_100h_check
    ## 1                1

We can see that the count_100h value for pre-340-13-215 is 1.1, which is not a whole number. We look back at the original datasheet (not shown here) and see that this count should be 1. This was a transcription error.

Attempt 3: After correcting the count_100h value in the input fuel data, let’s try again:

    FWD <- FineFuels(tree_data = vign_trees_5,
                     fuel_data = vign_fuels_3)

    ## Error in `ValidateMatches()`:
    ## ! Tree and fuel data did not completely match!
    ## These time:site:plot combinations have tree data but no fuel data: pre-340-111
    ## These time:site:plot combinations have fuel data but no tree data: pre-340-11

And we get yet another error message. Remember that there must be a one-to-one match between time:site:plot identities of tree and fuel data. See background information in the README file for more on why this one-to-one match is important. We known that there isn’t a plot 11 in the dataset, but that there is a plot 111. Pre_340_11 should probably be corrected to pre_340_111.

Attempt 4: After correcting the plot id in the input fuel data, let’s try again:

    FWD <- FineFuels(tree_data = vign_trees_5,
                     fuel_data = vign_fuels_4)

    ## Warning in ValidateFWD(fuel_data_val = fuel_data, units_val = units): For fuel_data, there are missing values in the count_10h column.
    ## For transects with NA 10h counts, 10h fuel load estimates will be NA.
    ##

    ## Warning in ValidateOverstory(tree_data_val = tree_data, sp_codes_val = sp_codes): Not all species codes were recognized! Unrecognized codes were converted to "UNTR" for unknown tree
    ## and will receive generic coefficients. Unrecognized codes: CONU NODE QUKE
    ##

    head(FWD)

    ##   time site plot load_1h_Mg_ha load_10h_Mg_ha load_100h_Mg_ha load_fwd_Mg_ha
    ## 1 post  400  101     0.9029624      1.4033325        0.000000       2.306295
    ## 2  pre  400  101     2.2310300      4.7643799        5.382827      12.378237
    ## 3 post   60  101     0.3571794      0.0000000        4.563755       4.920934
    ## 4  pre   60  101     0.8695333      2.0983663        6.749952       9.717852
    ## 5 post  400  102     0.1562554      0.5359028        4.278554       4.970712
    ## 6  pre  400  102     0.2723569      3.2140721        2.850828       6.337257
    ##   sc_length_1h sc_length_10h sc_length_100h
    ## 1     3.652542      3.652542       6.087570
    ## 2     3.652542      3.652542       6.087570
    ## 3     3.641661      3.641661       6.069435
    ## 4     3.641661      3.641661       6.069435
    ## 5     3.651810      3.651810       6.086350
    ## 6     3.651810      3.651810       6.086350

This time the function runs. However, we get some warning messages. Let’s look at the first warning, which tells us that there are missing 10-hour counts. Let’s look at where NA count_10h values show up in the data:

    vign_fuels_4 %>%
      filter(is.na(count_10h))

    ##   time site plot transect count_1h count_10h count_100h length_1h length_10h
    ## 1 post  400    2      252       12        NA          1      1.83       1.83
    ##   length_100h length_1000h ssd_S ssd_R litter_depth duff_depth slope
    ## 1        3.05        11.34    81    81            0          0     5

We look back the the original datasheet (not shown here) and see that this 10-hour count was not recorded in the field. It truly is missing, and there is little we can do about that at this point. Fortunately, there are two transects per time:site:plot. The other transect will still allow us to get a plot-level estimate of the 10-hour fuel load. The function will appropriately account for the missing 10-hour count.

The final warning message tells us that not all species codes were recognized. We look at the list of unrecognized codes: CONU (Cornus nuttallii, commonly known as pacific dogwood), NODE (Notholithocarpus densiflorus, commonly known as tanoak), and QUKE (Quercus kelloggii, commonly known as black oak). None of these are species code typos (but you should check for typos!). For the surface and ground fuel load calculations, we currently only have the necessary values for 19 Sierra Nevada conifer species (see background information in the README file for more detail on this topic). These three species are hardwoods and are, therefore, not included in the list of 19 Sierra Nevada conifers. This warning should not alarm us. Given the information currently available for the Sierra Nevada, the best we can do is assign generic “all species” values for these hardwoods.

### CoarseFuels()

Attempt 1: Now, let’s try using `CoarseFuels()`. We’ll keep the defaults for sp_codes (= “4letter”) and units (= “metric”):

    CWD <- CoarseFuels(tree_data = vign_trees_5,
                       fuel_data = vign_fuels_4,
                       summed = "yes")

    ## Error in `ValidateCWD()`:
    ## ! For fuel_data, there are missing values in the length_1000h column.

Another error message! It looks like there is a missing transect length. Let’s look at where NA length_1000h values show up in the data:

    vign_fuels_4 %>%
      filter(is.na(length_1000h))

    ##   time site plot transect count_1h count_10h count_100h length_1h length_10h
    ## 1 post   60  102      347       21         8          5      1.83       1.83
    ##   length_100h length_1000h ssd_S ssd_R litter_depth duff_depth slope
    ## 1        3.05           NA     0  1771          0.5       1.25     9

Based on the field protocol, we know that 1000-hour fuels were sampled for 11.34 meters along each transect. This is an easy fix.

Attempt 2: After filling in the missing transect length, let’s try again:

    CWD <- CoarseFuels(tree_data = vign_trees_5,
                       fuel_data = vign_fuels_5,
                       summed = "yes")

    ## Warning in ValidateOverstory(tree_data_val = tree_data, sp_codes_val = sp_codes): Not all species codes were recognized! Unrecognized codes were converted to "UNTR" for unknown tree
    ## and will receive generic coefficients. Unrecognized codes: CONU NODE QUKE
    ##

    head(CWD)

    ##   time site plot load_1000s_Mg_ha load_1000r_Mg_ha load_cwd_Mg_ha
    ## 1 post  400  101          0.00000          0.00000        0.00000
    ## 2  pre  400  101          0.00000          8.02592        8.02592
    ## 3 post   60  101          0.00000          0.00000        0.00000
    ## 4  pre   60  101         18.77363          1.68116       20.45479
    ## 5 post  400  102          0.00000          0.00000        0.00000
    ## 6  pre  400  102         15.56532          0.00000       15.56532
    ##   sc_length_1000s sc_length_1000r
    ## 1        22.63378        22.63378
    ## 2        22.63378        22.63378
    ## 3        22.56636        22.56636
    ## 4        22.56636        22.56636
    ## 5        22.62925        22.62925
    ## 6        22.62925        22.62925

This time the function runs. We are already familiar with this warning message (discussed in the `FineFuels()` function section above).

### LitterDuff()

Lastly, let’s try using `LitterDuff()`. We’ll keep the defaults for sp_codes (= “4letter”), units (= “metric”), and measurement (= “separate”):

    LD <- LitterDuff(tree_data = vign_trees_5,
                     fuel_data = vign_fuels_5)

    ## Warning in ValidateOverstory(tree_data_val = tree_data, sp_codes_val = sp_codes): Not all species codes were recognized! Unrecognized codes were converted to "UNTR" for unknown tree
    ## and will receive generic coefficients. Unrecognized codes: CONU NODE QUKE
    ##

    head(LD)

    ##   time site plot litter_Mg_ha duff_Mg_ha
    ## 1 post  400  101     8.207450   10.78962
    ## 2  pre  400  101    22.940513   82.86332
    ## 3 post   60  101     0.000000    0.00000
    ## 4  pre   60  101    37.114751   86.42521
    ## 5 post  400  102     9.304178    7.08900
    ## 6  pre  400  102    32.336705   31.92495

The function runs. Once again, we are familiar with this particular warning message (discussed in the `FineFuels()` function section above).

## Further data compilation

Now that we have everything summarized at the plot level, let’s try compiling some of the data to the compartment level and then to the entire treatment (here the fire-only treatment) level.

### Tree biomass

Step 1: estimate tree biomass at the plot level

We already went through this process above, but let’s recall what the output dataframe looks like:

    head(tree_bio)

    ##       site plot live_Mg_ha dead_Mg_ha
    ## 1 post_340  103     470.15      10.08
    ## 2 post_340  104     241.31       0.00
    ## 3 post_340  108     156.67       0.00
    ## 4 post_340  109     169.09       0.00
    ## 5 post_340  111     189.39       4.31
    ## 6 post_340  112     823.50      14.57

Step 2: create all necessary columns for input into the `CompilePlots()` function

    tree_bio_2 <- tree_bio %>%
      separate(site, c("time", "site")) %>% # separate id into time and site columns
      mutate(trt_type = "fire") %>% # create a trt_type column
      select(time, trt_type, site, plot, everything()) # organize columns as desired

    head(tree_bio_2)

    ##   time trt_type site plot live_Mg_ha dead_Mg_ha
    ## 1 post     fire  340  103     470.15      10.08
    ## 2 post     fire  340  104     241.31       0.00
    ## 3 post     fire  340  108     156.67       0.00
    ## 4 post     fire  340  109     169.09       0.00
    ## 5 post     fire  340  111     189.39       4.31
    ## 6 post     fire  340  112     823.50      14.57

Step 3: input data into `CompilePlots()`

    # keep the defaults for wt_data (= "not_needed")
    tree_bio_sum <- CompilePlots(data = tree_bio_2,
                                 design = "FFS")

    tree_bio_sum$site # pull out site-level summary

    ##   time trt_type site avg_live_Mg_ha se_live_Mg_ha avg_dead_Mg_ha se_dead_Mg_ha
    ## 1 post     fire  340       315.9475      37.45587       7.713500     3.2631872
    ## 2 post     fire  400       290.6375      22.01452       1.537500     0.5935450
    ## 3 post     fire   60       223.9916      26.24832       5.202632     2.9223176
    ## 4  pre     fire  340       315.1080      35.60767      13.598500     5.2956273
    ## 5  pre     fire  400       291.7030      22.18940       1.288000     0.5949558
    ## 6  pre     fire   60       246.3984      23.62663      10.758421     9.8535120

    tree_bio_sum$trt_type # pull out treatment-level summary

    ##   time trt_type avg_live_Mg_ha se_live_Mg_ha avg_dead_Mg_ha se_dead_Mg_ha
    ## 1 post     fire       276.8589      27.42481       4.817877      1.793207
    ## 2  pre     fire       284.4031      20.16778       8.548307      3.721584

### Fine woody debris

Step 1: estimate fine fuel loads at the plot level

We already went through this process above, but let’s recall what the output dataframe looks like:

    head(FWD)

    ##   time site plot load_1h_Mg_ha load_10h_Mg_ha load_100h_Mg_ha load_fwd_Mg_ha
    ## 1 post  400  101     0.9029624      1.4033325        0.000000       2.306295
    ## 2  pre  400  101     2.2310300      4.7643799        5.382827      12.378237
    ## 3 post   60  101     0.3571794      0.0000000        4.563755       4.920934
    ## 4  pre   60  101     0.8695333      2.0983663        6.749952       9.717852
    ## 5 post  400  102     0.1562554      0.5359028        4.278554       4.970712
    ## 6  pre  400  102     0.2723569      3.2140721        2.850828       6.337257
    ##   sc_length_1h sc_length_10h sc_length_100h
    ## 1     3.652542      3.652542       6.087570
    ## 2     3.652542      3.652542       6.087570
    ## 3     3.641661      3.641661       6.069435
    ## 4     3.641661      3.641661       6.069435
    ## 5     3.651810      3.651810       6.086350
    ## 6     3.651810      3.651810       6.086350

Step 2: create all necessary columns for input into the `CompileSurfaceFuels()` function

    FWD_2 <- FWD %>%
      mutate(trt_type = "fire") %>% # create a trt_type column
      select(time, trt_type, site, plot, everything()) # organize columns as desired

    head(FWD_2)

    ##   time trt_type site plot load_1h_Mg_ha load_10h_Mg_ha load_100h_Mg_ha
    ## 1 post     fire  400  101     0.9029624      1.4033325        0.000000
    ## 2  pre     fire  400  101     2.2310300      4.7643799        5.382827
    ## 3 post     fire   60  101     0.3571794      0.0000000        4.563755
    ## 4  pre     fire   60  101     0.8695333      2.0983663        6.749952
    ## 5 post     fire  400  102     0.1562554      0.5359028        4.278554
    ## 6  pre     fire  400  102     0.2723569      3.2140721        2.850828
    ##   load_fwd_Mg_ha sc_length_1h sc_length_10h sc_length_100h
    ## 1       2.306295     3.652542      3.652542       6.087570
    ## 2      12.378237     3.652542      3.652542       6.087570
    ## 3       4.920934     3.641661      3.641661       6.069435
    ## 4       9.717852     3.641661      3.641661       6.069435
    ## 5       4.970712     3.651810      3.651810       6.086350
    ## 6       6.337257     3.651810      3.651810       6.086350

Step 3: input data into `CompileSurfaceFuels()`

    # keep the defaults for cwd_data (= "none), wt_data (= "not_needed"), and units (= "metric")
    FWD_sum <- CompileSurfaceFuels(fwd_data = FWD_2,
                                   design = "FFS")

    FWD_sum$site # pull out site-level summary

    ##   time trt_type site avg_1h_Mg_ha se_1h_Mg_ha avg_10h_Mg_ha se_10h_Mg_ha
    ## 1 post     fire  400    0.4969988  0.05976717      1.139383    0.2120926
    ## 2  pre     fire  400    0.9968562  0.14076733      2.991177    0.3775630
    ## 3 post     fire   60    0.4725502  0.07563463      1.735057    0.3326378
    ## 4  pre     fire   60    1.2239823  0.20421903      4.355628    0.6468982
    ## 5 post     fire  340    0.3601983  0.04450288      1.051188    0.2369040
    ## 6  pre     fire  340    1.0927353  0.19285796      5.313776    0.9169993
    ##   avg_100h_Mg_ha se_100h_Mg_ha
    ## 1       1.317892     0.3381811
    ## 2       4.438505     0.5900822
    ## 3       4.151810     1.3535638
    ## 4       9.035420     1.3362516
    ## 5       2.351752     0.5969664
    ## 6       6.336984     1.3793372

    FWD_sum$trt_type # pull out treatment-level summary

    ##   time trt_type avg_1h_Mg_ha se_1h_Mg_ha avg_10h_Mg_ha se_10h_Mg_ha
    ## 1 post     fire    0.4432491  0.04212089      1.308543    0.2147717
    ## 2  pre     fire    1.1045246  0.06583011      4.220194    0.6738877
    ##   avg_100h_Mg_ha se_100h_Mg_ha
    ## 1       2.607151     0.8279885
    ## 2       6.603636     1.3336957