FullCAM is the Australian Government model for estimating carbon sequestration around Australia. It runs as it’s own software program (which must be downloaded and installed on a computer), and could be described as clunky at best.
This code and methodology takes the FullCAM output and transforms it into something useful for estimating carbon sequestration of a given land area.
#Load up a bunch of packages
library(plyr)
library(dplyr)
library(vctrs)
library(tidyr)
library(ggplot2)
library(rgdal)
library(tmap)
library(ggmap)
library(dplyr)
library(sf)
library(ggspatial)
library(rlang)
library(broom)
library(tidyverse)
library(readxl)
library(purrr)
library(zoo)
library(RcppRoll)
library(rmarkdown)
First we need to import data from the FullCAM model. Luckily, the table that is produced when a scenario is run can be exported as a csv. Let’s load this in.
#Read in the exported FullCAM data
<- read.csv(file="C:/06 R_code/Test_Data_Environmental_Plantings_Method.csv")
FullCAM_output
::kable(head(FullCAM_output),digits=2, caption ="Raw FullCAM output","simple") knitr
Year | Month | Day | Dec..Year | C.mass.of.trees…tC.ha. | C.mass.of.forest.debris…tC.ha. | CH4.emitted.due.to.fire..tCH4.ha. | N2O.emitted.due.to.fire..tN2O.ha. |
---|---|---|---|---|---|---|---|
2021 | 6 | 30 | 2021.50 | 0.00 | 0.00 | NA | NA |
2021 | 7 | 31 | 2021.58 | 0.29 | 0.01 | 0 | 0 |
2021 | 8 | 31 | 2021.67 | 0.29 | 0.01 | 0 | 0 |
2021 | 9 | 30 | 2021.75 | 0.29 | 0.01 | 0 | 0 |
2021 | 10 | 31 | 2021.83 | 0.29 | 0.01 | 0 | 0 |
2021 | 11 | 30 | 2021.91 | 0.29 | 0.01 | 0 | 0 |
The first thing we realise from this table is that FullCAM outputs this data in a terrible format. Let’s do some cleaning, fix up headings, and make the units more meaningful.
#Let's start by removing the 4th column with the clunky date
= subset(FullCAM_output, select = -c(4))
FullCAM_output_clean
#Rename the remaining columns to something more useful
<- FullCAM_output_clean %>%
FullCAM_output_clean rename(
C_mass_tree_tC_ha = 4,
C_mass_forest_debris_tC_ha = 5,
CH4_emmited_fire_tCH4_ha = 6,
N2O_emmited_fire_tN2O_ha = 7)
#Now let's convert these measures of carbon into measures of CO2
<- FullCAM_output_clean %>%
FullCAM_output_transformed mutate(
Date = as.Date(with(FullCAM_output_clean, paste(Year, Month, Day,sep="-")), "%Y-%m-%d"),
#Convert tonnes of C into tonnes of CO2 by multiplying by the weight of a carbon dioxide molecule
CO2_stored_in_trees = C_mass_tree_tC_ha*(44/12),
CO2_stored_in_forest_debris = C_mass_forest_debris_tC_ha*(44/12),
#Add data sets together to get the total amount sequestered
Total_CO2_sequestered = CO2_stored_in_trees+CO2_stored_in_forest_debris,
#Find the marginal change in CO2 sequestration each month
CO2_sequestered_month = lead(Total_CO2_sequestered,n=1) - (Total_CO2_sequestered))
<- na.omit(FullCAM_output_transformed)
FullCAM_output_transformed
#Now we want to find the marginal change per year, updated on a rolling monthly basis
<- FullCAM_output_transformed %>%
FullCAM_output_transformed mutate(
#Note the allign='right' bit is important here to make sure it starts the rollsum after the first year.
CO2_sequestered_year = RcppRoll::roll_sum(CO2_sequestered_month,12,fill=NA,align='right'))
Now that we have clean data, let’s see what the sequestration rate of CO2 (rather than carbon) looks like over the crediting period.
<- ggplot(data=FullCAM_output_transformed)+
plot_FullCAM_annual geom_line(aes(x = Date, y = CO2_sequestered_year), col = "#3C33FE",size=1.5) +
#geom_area(aes(x = Date, y = CO2_sequestered_year), fill = "lightgrey",size=1) +
labs(title="Annual sequestration rate using the environmental plantings method",
subtitle = "Analysis from the Hunter Valley, New South Wales",
caption = "Data: FullCAM Model 2021",
x="",
y="Annual CO2 sequestration (tCO2/ha)")+
scale_x_date(date_breaks = "5 years",date_labels="%Y")+
scale_y_continuous(expand = c(0,0)) +
theme(legend.position="none")+
theme_minimal()+
theme(axis.line.x = element_line(color="black", size = 0.3),axis.line.y = element_line(color="black", size = 0.3))+
theme(axis.text=element_text(size=12))+
theme(panel.grid.minor = element_blank())+
theme(plot.title=element_text(face="bold",size=12))+
theme(plot.subtitle=element_text(size=11,margin=margin(0,0,15,0)))+
theme(plot.caption=element_text(size=8))
plot_FullCAM_annual
ggsave("C:/06 R_code/FullCAM_sequestration_plot.png",units="cm",dpi=200, device="png")
We see that gum trees grow fasster and faster until around 10 years old, then the rate of growth slows.
Now we visualize the total carbon sequestration over time.
<- ggplot(data=FullCAM_output_transformed)+
plot_FullCAM_cumulative geom_line(aes(x = Date, y = Total_CO2_sequestered), col = "darkblue",size=1.3,fill="darkblue") +
geom_area(aes(x = Date, y = Total_CO2_sequestered), col = "darkblue",size=1.3,fill="#1589FF") +
geom_area(aes(x = Date, y = CO2_stored_in_forest_debris), col = "darkblue", fill="darkblue",size=1.3) +
labs(title="Cumulative CO2 sequestration using the environmental plantings method",
subtitle = "Analysis from the Hunter Valley, New South Wales",
caption = "Data: FullCAM Model 2021",
x="",
y="Total CO2 sequestration (tCO2/ha)")+
scale_x_date(date_breaks = "5 years",date_labels="%Y")+
scale_y_continuous(expand = c(0,0)) +
theme_minimal()+
theme(axis.line.x = element_line(color="black", size = 0.3),axis.line.y = element_line(color="black", size = 0.3))+
theme(axis.text=element_text(size=12))+
theme(panel.grid.minor = element_blank())+
theme(plot.title=element_text(face="bold",size=12))+
theme(plot.subtitle=element_text(size=11,margin=margin(0,0,15,0)))+
theme(plot.caption=element_text(size=8))
plot_FullCAM_cumulative
ggsave("C:/06 R_code/FullCAM_cumulative_plot.png",units="cm",dpi=200, device="png")
These charts are great… but how many tonnes of CO2 could we expect these planting to sequester per hectare per year?
Let’s make a simple table.
# Calculate the input table annually over the crediting period
<- FullCAM_output_transformed %>%
FullCAM_output_annual group_by(Year) %>%
summarise(yearly_total=sum(CO2_sequestered_month))
::kable(FullCAM_output_annual,digits=2,caption = "Annual sequestration per hectare", "simple") knitr
Year | yearly_total |
---|---|
2021 | 0.04 |
2022 | 1.61 |
2023 | 7.78 |
2024 | 14.10 |
2025 | 17.75 |
2026 | 19.29 |
2027 | 19.53 |
2028 | 19.11 |
2029 | 18.23 |
2030 | 17.24 |
2031 | 16.19 |
2032 | 15.19 |
2033 | 14.14 |
2034 | 13.19 |
2035 | 12.29 |
2036 | 11.49 |
2037 | 10.69 |
2038 | 9.98 |
2039 | 9.33 |
2040 | 8.75 |
2041 | 8.17 |
2042 | 7.66 |
2043 | 7.19 |
2044 | 6.77 |
2045 | 6.36 |
2046 | 2.83 |
Better. And useful as an input for ACCU models.
Boiling this all down to a simple number - how many tonnes of CO2 can we expect to be sequestered per hectare per year?
#Calculate the mean sequestration per hectare per year
<- FullCAM_output_annual %>% summarise(mean_ha_yr=mean(yearly_total))
average_per_ha_per_year
::kable(average_per_ha_per_year,digits=2, caption ="Average sequestration accross all hectares","simple") knitr
mean_ha_yr |
---|
11.34 |
Ace. 11 tCO2/ha is slightly better than the ~10 tCO2/ha that CSIRO forecasts for the average across Australia.
Let’s use this data to develop some more general equations for carbon sequestration. How many x terms do we need to fit a good polynomial? Unsure. Let’s find out by running a bunch of estimates.
#Create a polynomial to estimate the growth rates
<- ggplot(FullCAM_output_transformed, aes(Date, CO2_sequestered_year))+
plot_FullCAM_annual_model geom_smooth(method = "lm", formula = y ~ poly(x, 1), aes(color = "1"), se = F) +
geom_smooth(method = "lm", formula = y ~ poly(x, 2), aes(color = "2"), se = F) +
geom_smooth(method = "lm", formula = y ~ poly(x, 3), aes(color = "3"), se = F) +
geom_smooth(method = "lm", formula = y ~ poly(x, 4), aes(color = "4"), se = F) +
geom_smooth(method = "lm", formula = y ~ poly(x, 5), aes(color = "5"), se = F) +
geom_smooth(method = "lm", formula = y ~ poly(x, 6), aes(color = "6"), se = F) +
geom_smooth(method = "lm", formula = y ~ poly(x, 7), aes(color = "7"), se = F) +
labs(title="Annual sequestration rate using the environmental plantings method",
subtitle = "Fitting adequate polynomials to the series",
caption = "Data: FullCAM Model 2021",
x="",
y="Annual CO2 sequestration (tCO2/ha)",
color= "Polynomial")+
scale_x_date(date_breaks = "5 years",date_labels="%Y")+
scale_y_continuous(expand = c(0,0)) +
theme(legend.position="none")+
theme_minimal()+
theme(axis.line.x = element_line(color="black", size = 0.3),axis.line.y = element_line(color="black", size = 0.3))+
theme(axis.text=element_text(size=12))+
theme(panel.grid.minor = element_blank())+
theme(plot.title=element_text(face="bold",size=12))+
theme(plot.subtitle=element_text(size=11,margin=margin(0,0,15,0)))+
theme(plot.caption=element_text(size=8))
plot_FullCAM_annual_model
The polynomial of order 4 seems to be there best here in estimating the function of CO2 sequestration. It’s about the right shape, and has less x terms than the higher order models. Let’s see what it looks like compared to the original series.
<- ggplot(FullCAM_output_transformed, aes(Date, CO2_sequestered_year))+
plot_FullCAM_annual_model_2 geom_line(aes(x = Date, y = CO2_sequestered_year), col = "#3C33FE",size=1) +
geom_smooth(method = "lm", formula = y ~ poly(x, 4), col = "darkgreen", se = F, linetype="dashed") +
labs(title="Annual sequestration rate using the environmental plantings method",
subtitle = "Polynomial fit compared to actual data set",
caption = "Data: FullCAM Model 2021",
x="",
y="Annual CO2 sequestration (tCO2/ha)",
color= "Polynomial")+
scale_x_date(date_breaks = "5 years",date_labels="%Y")+
scale_y_continuous(expand = c(0,0)) +
theme(legend.position="none")+
theme_minimal()+
theme(axis.line.x = element_line(color="black", size = 0.3),axis.line.y = element_line(color="black", size = 0.3))+
theme(axis.text=element_text(size=12))+
theme(panel.grid.minor = element_blank())+
theme(plot.title=element_text(face="bold",size=12))+
theme(plot.subtitle=element_text(size=11,margin=margin(0,0,15,0)))+
theme(plot.caption=element_text(size=8))
plot_FullCAM_annual_model_2
There’s a bunch of requirements that must be satisfied to be eligible under the ERF method:
The plantings must include tree or mallee species native to the local area.
Mallee species must only be planted where annual rainfall is under 600 mm.
The project area must have been clear of forest cover for at least five years prior to project commencement.
The plantings must have the potential to reach forest cover (20 per cent crown cover consisting of trees that are at least two metres tall).
So how do we get this information? Let’s start with rainfall. We can download climate data from the BOM here: http://www.bom.gov.au/jsp/ncc/climate_averages/rainfall/index.jsp
#It's possible to read this in as a text file
#BOM_rainfall <- read.delim("C:/06 R_code/rainan.txt")
#However, a raster file is far more useful
#BOM_rainfall <- as.data.frame(raster("C:/06 R_code/rainan.txt"))
#head(BOM_rainfall)
#plot(BOM_rainfall)
#sf_join <- sf::st_join(BOM_rainfall, absmapsdata::sa12016, join = st_intersects)
#Let's cut this raster down to the Australia shapefile using the absmapsdata package
#BOM_rainfall_sa12016 <- crop(BOM_rainfall,extent(absmapsdata::sa12016))
#plot(BOM_rainfall_sa12016)
#mask(BOM_rainfall_sa12016,absmapsdata::sa12016)