Climatic Analysis of Telescope Peak Bristlecone Pine Mortality

Table of Contents

1. Introduction

This project extends the climate analysis conducted on the PILO mortality cite documented by (Bentz et al. 2022). We attempt to recreate their analysis here, as well as run an additional analysis following David Thoma's simple water balance model which uses daily Daymet timeseries and incorporates additional physical site parameters and therefore may provided a more advanced, and hopefully accurate, analysis of the site.

Our aim is to examine the mortality event occuring at Panamint Range (Telescope Peak), which was attributed to Mountain Pine Beetle (Bentz et al. 2022). Mortality events also occured at Wah Wah Mountains (Pinyon Ips), Silver Peak (unattributed) , and Hot Creek Range/Rawhide (unattributed). While the mortality event was not recorded until 2022, it is believed that it occured around 2017. Points from Snake Range and Mount Moriah are included as a location where PIFL mortality was noted without associated PILO mortality.

Four analyses were performed:

  1. 3 Use historical water balance summary layers from Mike Tercek
  2. Simple Water Balance Model (Excel) (Penman) shareable_Excel_xlsm_water_balance_model_v3.xlsm Simple Water Balance model implemented in Excel provided by David Toma, using Daymet daily time series
  3. Lutz et al. 2010 Water Balance Method Lutz 2010 Water Balance Model implemented in R following methods in (Bentz et al. 2022), which followed (Lutz, van Wagtendonk, and Franklin 2010), using PRISM 30yr normals

1.1. Map

map.png

2. Data

2.1. DEM

SRTM 30m DEM used to generate slope, aspect, elevation in QGIS 3.10. Some elevations were provided in the Bentz data set, existing values were used instead of generated ones when available.

2.2. Soil

SSURGO database

2.3. t50

merged_jennings.tif

3. Historical Water Balance Averages

Using summary layers generated by Mike Tercek http://screenedcleanedsummaries.s3-website-us-west-2.amazonaws.co/.

3.1. AET x CWD

library(tidyverse)
library(here)
library(gghighlight)

mt_list <- c("PANAMINT RANGE",
             "WAH WAH MOUNTAINS",
             "SILVER PEAK RANGE",
             "HOT CREEK RANGE",
             "SNAKE RANGE",
             "SNAKE RANGE(MORIAH)")
             "HOT CREEK RANGE")

# sampled in QGIS
tbl <- read_csv("./sites_aet_cwd_2000_2019_historical_avg.csv")

ggplot(data = tbl) +
  geom_point(mapping = aes(x = CWD1, y = AET1, color = MT_range)) +
  gghighlight(MT_range %in% mt_list,
              use_direct_label = FALSE) +
  ggtitle("AET x CWD Annual gridMET Historical 2000-2019")

4. Simple Water Balance Model (Excel) (Penman)

Data from David Thoma's Simple Water Balance model xlsm, processed using provided batch script. Daymet data obtained using extractdaymetbatch.py script. PET Values calculated with Penman method

4.1. Libraries

library(tidyverse)
library(ggplot2)
library(readxl)
library(here)
library(gghighlight)
library(ggpubr)

4.2. Data Import and Cleanup

setwd("penman/annual")
tbl <-
    list.files(pattern = "*.csv") %>%
    map_df(~read_csv(., n_max = 41, col_types = cols())) %>% # only read 41 rows (For annual reports) because csvs contain nonsynctactic row of averages at bottom, which creates NAs in df
    mutate(year = strtoi(`Row Labels`)) %>%
    group_by(site)

setwd(here())
sites_tbl <- read_excel("sites.xlsx")

mt_list <- c("PANAMINT RANGE",
             "WAH WAH MOUNTAINS",
             "SILVER PEAK RANGE",
             "HOT CREEK RANGE",
             "SNAKE RANGE",
             "SNAKE RANGE(MORIAH)")

4.3. Summary Table

summary_tbl <- tbl %>%
  summarise(
    mean_t = mean(`Average of T`),
    mean_P = mean(`Sum of P`),
    mean_AET= mean(`Sum of AET`),
    mean_D= mean(`Sum of D`),
    mean_GDD= mean(`Sum of GDD`),
    mean_SOIL= mean(`Average of SOIL`),
    ###
    mean_t_pre_2010 = mean(`Average of T`[year <= 2010]),
    mean_P_pre_2010 = mean(`Sum of P`[year <= 2010]),
    mean_AET_pre_2010 = mean(`Sum of AET`[year <= 2010]),
    mean_D_pre_2010 = mean(`Sum of D`[year <= 2010]),
    mean_GDD_pre_2010 = mean(`Sum of GDD`[year <= 2010]),
    mean_SOIL_pre_2010 = mean(`Average of SOIL`[year <= 2010]),
    ###
    mean_t_post_2010 = mean(`Average of T`[year > 2010]),
    mean_P_post_2010 = mean(`Sum of P`[year > 2010]),
    mean_AET_post_2010 = mean(`Sum of AET`[year > 2010]),
    mean_D_post_2010 = mean(`Sum of D`[year > 2010]),
    mean_GDD_post_2010 = mean(`Sum of GDD`[year > 2010]),
    mean_SOIL_post_2010 = mean(`Average of SOIL`[year > 2010]),
    ###    Delta values pre and post 2010
    dt = mean_t_post_2010 - mean_t_pre_2010,
    dP = mean_P_post_2010 - mean_P_pre_2010,
    dAET = mean_AET_post_2010 - mean_AET_pre_2010,
    dD = mean_D_post_2010 - mean_D_pre_2010,
    dGDD = mean_GDD_post_2010 - mean_GDD_pre_2010,
    dSOIL = mean_SOIL_post_2010 - mean_SOIL_pre_2010,
    ) %>%
  left_join(sites_tbl, by = c("site" = "Site")) %>%
  group_by(MT_range)

4.4. Figures

4.4.1. AET x CWD

summary_tbl %>%
ggplot() +
geom_point(mapping = aes(x = mean_D, y = mean_AET, col = MT_range)) +
gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
ggtitle("AET x CWD Penman Daymet Excel")

aed_cwd_penman_excel.png

4.4.2. Pre 2010 Plot

cwd_plt_pre_2010 <- summary_tbl %>%
  ggplot() +
  geom_point(mapping = aes(x = mean_t_pre_2010, y = mean_D_pre_2010, color = MT_range)) +
  gghighlight(MT_range %in% mt_list,
                use_direct_label = FALSE) +
  labs(x = "Mean Annual T (1980-2010)", y = "CWD")

p_plt_pre_2010 <- summary_tbl %>%
  ggplot() +
  geom_point(mapping = aes(x = mean_t_pre_2010, y = mean_P_pre_2010, color = MT_range)) +
  labs(x = "Mean Annual T (1980-2010)", y = "PPT") +
  gghighlight(MT_range %in% mt_list,
              use_direct_label = FALSE)

aet_plt_pre_2010 <- summary_tbl %>%
  ggplot() +
  geom_point(mapping = aes(x = mean_t_pre_2010, y = mean_AET_pre_2010, color = MT_range)) +
  labs(x = "Mean Annual T", y = "AET") +
  gghighlight(MT_range %in% mt_list,
              use_direct_label = FALSE)

ggarrange(cwd_plt_pre_2010, p_plt_pre_2010, aet_plt_pre_2010,
          common.legend = TRUE) %>%
  annotate_figure(top = text_grob("Climate Space 1980-2010 Penman Daymet Excel"))

pre_2010_penman_excel.png

4.4.3. Post 2010 Plot

cwd_plt_post_2010 <- summary_tbl %>%
  ggplot() +
  geom_point(mapping = aes(x = mean_t_post_2010, y = mean_D_post_2010, color = MT_range)) +
  gghighlight(MT_range %in% mt_list,
                use_direct_label = FALSE) +
  labs(x = "Mean Annual T (2011-2020)", y = "CWD")

p_plt_post_2010 <- summary_tbl %>%
  ggplot() +
  geom_point(mapping = aes(x = mean_t_post_2010, y = mean_P_post_2010, color = MT_range)) +
  labs(x = "Mean Annual T (2011-2020)", y = "PPT") +
  gghighlight(MT_range %in% mt_list,
              use_direct_label = FALSE)

aet_plt_post_2010 <- summary_tbl %>%
  ggplot() +
  geom_point(mapping = aes(x = mean_t_post_2010, y = mean_AET_post_2010, color = MT_range)) +
  labs(x = "Mean Annual T (2011-2020)", y = "AET") +
  gghighlight(MT_range %in% mt_list,
              use_direct_label = FALSE)

ggarrange(cwd_plt_post_2010, p_plt_post_2010, aet_plt_post_2010,
                             common.legend = TRUE) %>%
  annotate_figure(top = text_grob("Climate Space 2011-2020 Penman Daymet Excel"))

post_2010_penman_excel.png

4.4.4. Change Plot

summary_tbl %>%
  ggplot(aes(x = mean_D_pre_2010, y = mean_AET_pre_2010, color = MT_range)) +
  geom_arrow(aes(dx = dD, dy = dAET)) +
  gghighlight(MT_range %in% mt_list, use_direct_label = FALSE) +
  labs(title = "Change in mean AET and CWD over periods from 2010-2021 and 1980-2010 Penman Daymet Excel", x = "CWD (mm)", y = "AET (mm)") +
  scale_mag() 

aed_cwd_change_penman_excel.png

4.5. Timeseries

4.5.1. Load Monthly Data

setwd("penman/monthly")
monthly_tbl <-
  list.files(pattern = "*.csv") %>%
  map_df(~read_csv(., col_types = cols())) %>%
  separate(`Row Labels`, c("year", "month")) %>%
  mutate(date = as.Date(paste(year, month, "01", sep = "/"), formate="%Y/%m/%d")) %>%
  left_join(sites_tbl, by = c("site" = "Site")) %>%
  arrange(MT_range, date) %>%
  group_by(MT_range) 

setwd(here())

4.5.2. Summary Table

monthly_summary_tbl <- monthly_tbl %>%
  group_by(MT_range, year, month) %>%
  summarize(AET = mean(`Sum of AET`),
            PET = mean(`Sum of PET`),
            D = mean(`Sum of D`),
            SNOW = mean(`Sum of SNOW`),
            PACK = mean(`Max of PACK`),
            SOIL = mean(`Average of SOIL`),
            GDD = mean(`Sum of GDD`),
            T = mean(`Average of T`),
            P = mean(`Sum of P`),
            RAIN = mean(`Sum of RAIN`),
            date = date) %>%
  distinct()

4.5.3. AET

monthly_summary_tbl %>%
  filter(year >= 2012) %>%
  ggplot(mapping = aes(x = date, y = AET, color = MT_range)) +
  geom_line() +
  gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
  theme(legend.position = "bottom") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y",
               date_minor_breaks = "1 month") +
  ggtitle("AET Monthly Time Series Penman Daymet Excel")


aet_timeseries_penman_excel.png

4.5.4. CWD

monthly_summary_tbl %>%
  filter(year >= 2012) %>%
  ggplot(mapping = aes(x = date, y = D, color = MT_range)) +
  geom_line() +
  gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
  theme(legend.position = "bottom") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y",
               date_minor_breaks = "1 month") +
  ggtitle("CWD Monthly Time Series Penman Daymet Excel")


cwd_timeseries_penman_excel.png

4.5.5. T

monthly_summary_tbl %>%
  filter(year >= 2012) %>%
  ggplot(mapping = aes(x = date, y = T, color = MT_range)) +
  geom_line() +
  gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
  theme(legend.position = "bottom") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y",
               date_minor_breaks = "1 month") +
  ggtitle("Temperature Monthly Time Series Penman Daymet Excel")


t_timeseries_penman_excel.png

4.5.6. PPT

monthly_summary_tbl %>%
  filter(year >= 2012) %>%
  ggplot(mapping = aes(x = date, y = P, color = MT_range)) +
  geom_line() +
  gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
  theme(legend.position = "bottom") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y",
               date_minor_breaks = "1 month") +
  ggtitle("PPT Monthly Time Series Penman Daymet Excel")


p_timeseries_penman_excel.png

4.5.7. RAIN

monthly_summary_tbl %>%
  filter(year >= 2012) %>%
  ggplot(mapping = aes(x = date, y = RAIN, color = MT_range)) +
  geom_line() +
  gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
  theme(legend.position = "bottom") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y",
               date_minor_breaks = "1 month") +
  ggtitle("RAIN Monthly Time Series Penman Daymet Excel")


rain_timeseries_penman_excel.png

4.5.8. SNOW

monthly_summary_tbl %>%
  filter(year >= 2012) %>%
  ggplot(mapping = aes(x = date, y = SNOW, color = MT_range)) +
  geom_line() +
  gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
  theme(legend.position = "bottom") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y",
               date_minor_breaks = "1 month") +
  ggtitle("SNOW Monthly Time Series Penman Daymet Excel")


snow_timeseries_penman_excel.png

4.5.9. PACK

monthly_summary_tbl %>%
  filter(year >= 2008, year <= 2018) %>%
  ggplot(mapping = aes(x = date, y = PACK, color = MT_range)) +
  geom_line() +
  gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
  theme(legend.position = "bottom") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y",
               date_minor_breaks = "1 month") +
  ggtitle("PACK Monthly Time Series Penman Daymet Excel")


pack_timeseries_penman_excel.png

4.5.10. SOIL

monthly_summary_tbl %>%
  filter(year >= 2012) %>%
  ggplot(mapping = aes(x = date, y = SOIL, color = MT_range)) +
  geom_line() +
  gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
  theme(legend.position = "bottom") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y",
               date_minor_breaks = "1 month") +
  ggtitle("SOIL Monthly Time Series Penman Daymet Excel")


soil_timeseries_penman_excel.png

4.5.11. GDD

monthly_summary_tbl %>%
  filter(year >= 2012) %>%
  ggplot(mapping = aes(x = date, y = GDD, color = MT_range)) +
  geom_line() +
  gghighlight(MT_range %in% mt_list,
            use_direct_label = FALSE) +
  theme(legend.position = "bottom") +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y",
               date_minor_breaks = "1 month") +
  ggtitle("GDD Monthly Time Series Penman Daymet Excel")


gdd_timeseries_penman_excel.png

4.6. Animations

library(gganimate)

mortality_tbl <- tbl %>%
  left_join(sites_tbl, by = c("site" = "Site")) %>%
  ungroup() %>%
  mutate(mortality = if_else(MT_range %in% c("PANAMINT RANGE", "WAH WAH MOUNTAINS", "HOT CREEK RANGE", "SILVER PEAK RANGE"),
                             TRUE,
                             FALSE)) 

4.6.1. AET x CWD over time

p <- tbl %>%  
  left_join(sites_tbl, by = c("site" = "Site")) %>%
  ggplot() +
  geom_point(mapping = aes(x = `Sum of D`, y = `Sum of AET`, col = MT_range)) +
  gghighlight(MT_range %in% mt_list,
              use_direct_label = FALSE,
              calculate_per_facet = TRUE) +
  labs(title = 'Year: {frame_time}') +
  transition_time(year) +
  ease_aes('linear')


animate(p, renderer = ffmpeg_renderer())
anim_save("aet_x_cwd.mp4", animation = last_animation(), path = "img")

4.6.2. All Climate variables over time

 p <- mortality_tbl %>%
  group_by(mortality) %>%
  arrange(year) %>%
  pivot_longer(c("ASPECT_QGIS",
         "SLOPE_QGIS",
         "Average of SOIL",
         "Average of T",
         "Elev_m",
         "Max of PACK",
         "Sum of AET",
         "Sum of D",
         "Sum of GDD",
         "Sum of MELT",
         "Sum of P",
         "Sum of PET",
         "Sum of RAIN",
         "Sum of SNOW",
         "Sum of W",
         "Sum of W - PET",
         "Sum of W-ET-/\\SOIL")) %>%
  transform(name = factor(name, levels = c("ASPECT_QGIS",
         "SLOPE_QGIS",
         "Average of SOIL",
         "Average of T",
         "Elev_m",
         "Max of PACK",
         "Sum of AET",
         "Sum of D",
         "Sum of GDD",
         "Sum of MELT",
         "Sum of P",
         "Sum of PET",
         "Sum of RAIN",
         "Sum of SNOW",
         "Sum of W",
         "Sum of W - PET",
         "Sum of W-ET-/\\SOIL"))) %>%
  ggplot() +
  geom_boxplot(mapping = aes(x = mortality, y = value)) +
  facet_wrap(~ name, scales = "free") +
  ## transition_states(
  ##   year,
  ##   transition_length = 2,
  ##   state_length = 1
  ## ) +
  labs(title = 'Year: {frame_time}') +
  transition_time(year) +
  ease_aes('linear')

animate(p, renderer = ffmpeg_renderer())
anim_save("boxplot_time.mp4", animation = last_animation(), path = "img")

4.7. Modelling

## log regression attempt
## for (y in c(2000:2017)) { 
##   mortality_tbl <- tbl %>%
##     ungroup() %>%
##     left_join(sites_tbl, by = c("site" = "Site")) %>%
##     filter(year == y) %>%
##     mutate(mortality = if_else(MT_range == "PANAMINT RANGE",
##                                TRUE,
##                                FALSE)) %>%
##     ungroup() %>%
##     select(ASPECT_QGIS,
##            SLOPE_QGIS,
##            `Average of SOIL`,
##            `Average of T`,
##            Elev_m,
##            `Max of PACK`,
##            `Sum of AET`,
##            `Sum of D`,
##            `Sum of GDD`,
##            `Sum of MELT`,
##            `Sum of P`,
##            `Sum of PET`,
##            `Sum of RAIN`,
##            `Sum of SNOW`,
##            `Sum of W`,
##            `Sum of W - PET`,
##            `Sum of W-ET-/\\SOIL`,
##            mortality)
##   mortality_tbl$mortality <- factor(mortality_tbl$mortality)
##   mylogit <- glm(mortality ~ ., data = mortality_tbl, family = "binomial"(link='logit'))
##   print(summary(mylogit))
## }


## mortality_tbl <- tbl %>%
##   ungroup() %>%
##   left_join(sites_tbl, by = c("site" = "Site")) %>%
##   filter(year == ) %>%
##   mutate(mortality = if_else(MT_range == "PANAMINT RANGE",
##                              TRUE,
##                              FALSE)) %>%
##   ungroup() %>%
##   select(ASPECT_QGIS,
##          SLOPE_QGIS,
##          `Average of SOIL`,
##          `Average of T`,
##          Elev_m,
##          `Max of PACK`,
##          `Sum of AET`,
##          `Sum of D`,
##          `Sum of GDD`,
##          `Sum of MELT`,
##          `Sum of P`,
##          `Sum of PET`,
##          `Sum of RAIN`,
##          `Sum of SNOW`,
##          `Sum of W`,
##          `Sum of W - PET`,
##          `Sum of W-ET-/\\SOIL`,
##          mortality)
## mortality_tbl$mortality <- factor(mortality_tbl$mortality)

## mylogit <- glm(mortality ~ ., data = mortality_tbl, family = "binomial"(link='logit'))
## print(summary(mylogit))

5. Lutz et al. 2010 Water Balance Method

Following (Bentz et al. 2022), Implement water balance model as in (Lutz, van Wagtendonk, and Franklin 2010) and supplemental. Use 800m PRISM 30 yr normals.

5.1. Libraries

library(raster)
library(ggplot2)
library(tidyverse)
library(lubridate)
library(gghighlight)
library(prism)

Be sure to set the download folder using prism_set_dl_dir().

prism_set_dl_dir("~/prismtmp")

5.2. Water Balance Functions

Implement water balance functions from (Lutz, van Wagtendonk, and Franklin 2010) supplemental

get_f <- function (tmean) {
  f <- case_when(
    tmean <= 0 ~ 0,
    tmean > 0 & tmean < 6 ~ 0.167 * tmean,
    tmean >= 6 ~ 1)
  return(f)
}

get_rain <- function (ppt, F) {
  return(F * ppt)
}

get_snow <- function (ppt, F) {
  return( (1 - F) * ppt )
}

get_pack <- function (ppt, F, sp.0=NULL) {
  snowpack <- vector()
  sp.0 <- ifelse(!is.null(sp.0), sp.0, 0)
  for (i in 1:length(ppt)) {
    if (i == 1) {
      snowpack[i] = (1 - F[i])**2 * ppt[i] + (1 - F[i]) * sp.0
    } else {
      snowpack[i] = (1 - F[i])**2 * ppt[i] + (1 - F[i]) * snowpack[i - 1]
    }
  }
  return(snowpack)
}

get_melt <- function (snow, pack, F, sp.0=NULL) {
  sp.0 <- ifelse(!is.null(sp.0), sp.0, 0)
  melt <- vector()
  for (i in 1:length(snow)) {
    if ( i == 1 ) {
      melt[i] = F[i] * (snow[i] + sp.0)
    } else {
      melt[i] = F[i] * (snow[i] + pack[i-1])
    }
  }
  return(melt)
}

get_dl <- function (mon, days, Lat) {
  ## Get Daylength for all days in vector of months
  date <- paste("1980-", mon, "-", days, sep = "")
  yd <- yday(date)
  theta <- 0.2163108+2*atan(0.9671396*tan(0.00860*(yd-186)))
  P <- asin(0.39795 * cos(theta))
  dl <- 24 - (24/pi) * acos((sin((0.8333 * pi)/180) + sin((Lat * pi) / 180) * sin(P))/(cos((Lat*pi)/180)*cos(P)))
  return(dl)
}

get_hl <- function (Lat, slope, aspect_f) {
  ## calculate heat load index multiplier
  Lat.rad <- (pi/180) * Lat
  slope.rad <- (pi/180) * slope
  HL <- 0.339 + 0.808 * (cos(Lat.rad) * cos(slope.rad)) - 0.196 * (sin(Lat.rad) * sin(slope.rad)) - 0.482 * (cos(aspect_f) * sin(slope.rad))
  return(HL)
}

get_soil <- function (soil_max, w, pet, s.0=NULL) {
  s.0 = ifelse(!is.null(s.0), s.0, 0)
  soil <- vector()
  for (i in 1:length(pet)) {
    if ( i == 1 ) {
      soil[i] = pmin(soil_max[i],
                     if (w[i] > pet[i]) {
                       (w[i] - pet[i]) + s.0
                     } else {
                       s.0 * (1 - exp(-(pet[i]-w[i])/soil_max[i]))
                     })
    } else {
      soil[i] = pmin(soil_max[i],
                     if (w[i] > pet[i]) {
                       (w[i] - pet[i]) + soil[i-1]
                     } else {
                       soil[i-1] * (1 - exp(-(pet[i]-w[i])/soil_max[i]))
                     })
    }
  }
  return(soil)
}

get_d_soil <- function (soil, s.0=NULL) {
  s.0 = ifelse(!is.null(s.0), s.0, 0)
  d_soil = soil - lag(soil, default = s.0)
  return(d_soil)
}

get_aet <- function (pet, d_soil, w) {
  aet <- vector()
  for (i in 1:length(pet)) {
    a <- min(pet[i], d_soil[i] + w[i])
    aet[i] = if_else(a > 0,
                     a,
                     0)
  }
  return(aet)
}

5.3. Download PRISM Normals

Only needs to be run once after which data is saved to prism dl dir

get_prism_normals("ppt", "800m", annual = TRUE, keepZip = FALSE)
get_prism_normals("ppt", "800m", mon = 1:12, keepZip = FALSE)
get_prism_normals("tmean", "800m", annual = TRUE, keepZip = FALSE)
get_prism_normals("tmean", "800m", mon = 1:12, keepZip = FALSE)

5.4. Data Import and Cleanup

points <- read_csv("./sites.csv")
points.spdf <- SpatialPointsDataFrame(coords = points[,c('Lon', 'Lat')],
                                      data = points, proj4string = CRS("+proj=longlat +ellps=WGS84 +no_defs"))

prism_data <- data.frame()
for (i in 1:12) {
  ppt_pd <- prism_archive_subset("ppt", "monthly normals", resolution = "800m", mon = i)
  ppt_pd <- pd_to_file(ppt_pd)
  ppt_pd_rast <- raster(ppt_pd)
  ppt <- raster::extract(ppt_pd_rast, points.spdf, fun=mean, na.rm=TRUE, sp=FALSE)
  tmean_pd <- prism_archive_subset("tmean", "monthly normals", resolution = "800m", mon = i)
  tmean_pd <- pd_to_file(tmean_pd)
  tmean_pd_rast <- raster(tmean_pd)
  tmean <- raster::extract(tmean_pd_rast, points.spdf, fun=mean, na.rm=TRUE, sp=FALSE)
  df <- data.frame(Site = points$Site, Lat = points$Lat, Lon = points$Lon, mon = i, ppt = ppt, tmean = tmean)
  prism_data <- rbind(prism_data, df)
}

5.5. Water Balance Calculations

result <- prism_data %>%
  left_join(points, by = c("Site" = "Site")) %>%
  mutate(Lat = Lat.x,
         Lon = Lon.x) %>%
  group_by(Site) %>%
  arrange(mon) %>%    
  # defaults
  mutate(slope = SLOPE_QGIS,
         aspect = ASPECT_QGIS,
         soil_max = 100,
         hock = 4) %>%    
  mutate(F = get_f(tmean),
         RAIN = get_rain(ppt, F),
         SNOW = get_snow(ppt, F),
         PACK = get_pack(ppt, F),
         MELT = get_melt(SNOW, PACK, F), 
         W = RAIN + MELT,
         Days = days_in_month(mon),
         DL = get_dl(mon, Days, Lat),
         A = abs(180 - abs(aspect - 225)), # folded aspect
         HL = get_hl(Lat, slope, A),
         e = 0.611 * exp((17.3 * tmean) / (tmean + 237.3)),
         PET = 29.8 * Days * DL * HL * (e / (tmean + 273.2)),
         #PET = 29.8 * Days * DL * (e / (tmean + 273.2))) %>%
         SOIL = get_soil(soil_max, W, PET),
         dSOIL = get_d_soil(SOIL),
         AET = get_aet(PET, dSOIL, W),
         D = PET - AET)

5.6. Figures

5.6.1. Helper Functions

mt_list <- c("PANAMINT RANGE",
             "WAH WAH MOUNTAINS",
             "SILVER PEAK RANGE",
             "HOT CREEK RANGE",
             "SNAKE RANGE",
             "SNAKE RANGE(MORIAH)")

5.6.2. AET x CWD

result %>%
  group_by(Site) %>%
  filter(Elev_m != TRUE) %>%
  summarise(D = sum(D),
            AET = sum(AET),
            T = mean(tmean),
            ppt = sum(ppt),
            elev = mean(Elev_m)) %>%
  left_join(points, by = "Site") %>%
  ggplot(mapping = aes(x = D, y = AET, color = MT_range)) +
  geom_point() +
  gghighlight(MT_range %in% mt_list,
              use_direct_label = FALSE) +
  ggtitle("AET x CWD Hamon PRISM Lutz")

aet_x_cwd_hamon_prism_lutz.png

5.6.3. AET, CWD, PPT Plot

Re-create figure 4 from (Bentz et al. 2022)

 data_long <- result %>%
   group_by(Site) %>%
   summarise(D = sum(D),
             AET = sum(AET),
             T = mean(tmean),
             ppt = sum(ppt),
             elev = mean(Elev_m),
             MT_range = MT_range) %>%
   distinct() %>%
   pivot_longer(c("D", "AET", "ppt"))


data_long %>%
   transform(name=factor(name,levels=c("D", "ppt", "AET"))) %>%
   ggplot() +
   geom_point(mapping = aes(x = T, y = value, color = MT_range)) +
   gghighlight(MT_range %in% mt_list,
               use_direct_label = FALSE,
               calculate_per_facet = TRUE) +
   facet_wrap(~ name, ncol = 1, scales = "free")

d_ppt_aet_hamon_prism_lutz.png

6. Hypotheses

  1. Low AET values -> carbon starvation -> increased susceptibility to MPB
  2. High treeline temperature growth release. High temps -> accelerated growth rates in T-limited systems -> decreased wood density (?) -> increased susceptibility to MPB
  3. Beetle Hypothesis: Exploding beetle populations, enhanced by climate change, are overwhelming usual food source, PIFL. The beetles are forced to seek out novel food sources (i.e., PILO, which has previously been shown to be resistant to MPB (Bentz et al. 2017)). Beetle feeding could occur at low, undetected levels in PILO and not cause apparent harm, might be now passing threshold that can kill trees.
  4. PIFL promixity theory: This is the theory proposed by (Bentz et al. 2022). It is proposed that the presence of nearby infested PIFL led to the infestation of normally resistant PILO trees.

7. Questions

All of the models are "relative" and don't report absolute magnitudes that reflect conditions on the ground for all of the outputs. Snow might be closest, but soil moisture, evapotranspiration are likely to be different from reality. This is partly because PET regardless of method is for a "reference crop" or short sward of grass like a golf course rough. For this reason, we really need response data to test against your different results to understand which model is giving the most useful results. For example, even tho Penman PET is the gold standard physical basis for ET we found Hamon performs better when we use it in a water balance model to estimate spring flow in the desert and Oudin is better correlated with NDVI than Penman, also in desert settings. It looks like the simple water balance is generating results that are at least consistent with the unusual dryness of the Panamint range. Is that right? You outlined a few hypotheses. Which do you think is most plausible? What more could be done to evaluate each? What additional data would you need to resolve which hypothesis seems most reasonable? After making it this far, what do you think is the best course of action if were able to pick this project up again?

  • The simple water balance results indicate that the Panamint range is exceptionally dry in certain measures, PPT, SOIL, and PACK. However, it is middle of the road among the points in the Bentz data set in CWD (As Bentz et al. found). In fact, all 4 of the ranges with reported PILO mortality had similar results for these measures in the simple water balance results. I'm not totally sure how to interpret these results, as my understanding is that CWD should indicate the dryness that the plants are experiencing, however if a measure like snowpack is so low, I would imagine it would have some physiological consequences.

8. Next Steps

8.1. TODO Validate R implementation of Simple Water Balance Model

Need to get calculations to match up with excel version, calculations are currently slightly off, although patterns are similar. T and P values are slightly off so there may be an issue with my daymet data aggregation code.

8.2. Field Work

  • Gather information on sympatric PIFL trees. What are mortality and beetle infestation rates in PIFL, and how do they compare to PILO? Is there a threshold we can identify where PIFL experiences MPB infestation/mortality but PILO does not?
  • Determine cause of mortality in sites not attributed to MPB - Silver Peak and Hot Creek Range

9. Discussion

9.1. Issues

Penman PET calculation returns extreme values for CWD and AET. This occurs in both analyses above implementing the Simple Water Balance model in R and Excel. The CWD values generated would be expected from a desert rather than montane forest(Tercek et al. 2021). This occurs with both our version as well as the version from WaterBalance. The patterns remain consistent between Hamon and Penman analyses, but absolute values vary. The differences in calculated CWD appear to result from the soil moisture content calculations (SOIL), with Penman calculations showing reduced availability of late season soil moisture compared with Hamon calculations.

Possible Reasons for differences between models:

  • Additional Variables accounted for in Simple Water Balance Model that are missing from Lutz 2010 method
    1. jtemp - Lutz method uses simpler method to determine F
    2. Use of Hamon/Penman calculations
      1. vp - not used in WaterBalance:ET_Hamon_daily
      2. srad - not used in WaterBalance:ET_Hamon_daily
      3. SOIL/PACK - Penman/Hamon calculations produce significantly different results for SOIL/PACK. Within the Simple Water Balance model comparisons, this can account for the difference in magnitudes in CWD calculations, but this does not account for the differences in patterns betwen Hamon SWB and Hamon Lutz calculations.
  • Calculations from daily time series (SWB) instead of 30 yr monthly normals (Lutz)

9.2. Conclusions

(Bentz et al. 2022) determined that the PILO mortality event on Telescope Peak occured at a location that is middle of the road in PILO's climate space. Their climatic analysis used PRISM 30 year normals and the Hamon method to determine PET, following (Lutz, van Wagtendonk, and Franklin 2010). While their code and thus exact method was not provided, we were able to approximately recreate their analysis above, receiving similar results.

Our analysis using the Simple Water Balance model method placed the Telescope Peak sites in a different climatic space than reported by (Bentz et al. 2022). Results varied based on choice of PET calculation method (Hamon vs Penman), with Penman returning much higher CWD and AET values than Hamon, and thus a hotter and drier environment. However, both PET calculations produce the same patterns and relative placement of locations within the Bristlecone climatic space. With both Penman and Hamon PET calculations, the Simple Water Balance model places the Telescope Peak points at the low extreme of AET values for all sites, and around average for CWD.

Additionally, the Simple Water Balance model shows low snow pack (PACK) values in the years preceding 2017 for the Telescope Peak points. Interestingly, (Hankin and Bisbing 2021) found improved PILO regeneration with decreasing spring snowpack, due to release from energy limitations and longer growing seasons. While possibly beneficial to PILO seedlings, this temperature release, also reported by (Salzer et al. 2009), may have deleterious effects on PILO or at least on their ability to resist MPB infestation.

10. Bibliography

Bentz, Barbara J., Sharon M. Hood, E. Matthew Hansen, James C. Vandygriff, and Karen E. Mock. 2017. “Defense Traits in the Long-Lived Great Basin Bristlecone Pine and Resistance to the Native Herbivore Mountain Pine Beetle.” New Phytologist 213 (2): 611–24. https://doi.org/10.1111/nph.14191.
Bentz, Barbara J., Constance I. Millar, James C. Vandygriff, and Earl M. Hansen. 2022. “Great Basin Bristlecone Pine Mortality: Causal Factors and Management Implications.” Forest Ecology and Management 509 (April): 120099. https://doi.org/10.1016/j.foreco.2022.120099.
Hankin, Lacey E., and Sarah M. Bisbing. 2021. “Let It Snow? Spring Snowpack and Microsite Characterize the Regeneration Niche of High-Elevation Pines.” Journal of Biogeography 48 (8): 2068–84. https://doi.org/10.1111/jbi.14136.
Lutz, James A., Jan W. van Wagtendonk, and Jerry F. Franklin. 2010. “Climatic Water Deficit, Tree Species Ranges, and Climate Change in Yosemite National Park.” Journal of Biogeography 37 (5): 936–50. https://doi.org/10.1111/j.1365-2699.2009.02268.x.
Salzer, Matthew W., Malcolm K. Hughes, Andrew G. Bunn, and Kurt F. Kipfmueller. 2009. “Recent Unprecedented Tree-Ring Growth in Bristlecone Pine at the Highest Elevations and Possible Causes.” Proceedings of the National Academy of Sciences 106 (48): 20348–53. https://doi.org/10.1073/pnas.0903029106.
Tercek, Michael T., David Thoma, John E. Gross, Kirk Sherrill, Stefanie Kagone, and Gabriel Senay. 2021. “Historical Changes in Plant Water Use and Need in the Continental United States.” Plos One 16 (9): e0256586. https://doi.org/10.1371/journal.pone.0256586.

Date: 2022-10-18 Tue 00:00

Author: Stephen Huysman

Created: 2022-11-14 Mon 11:25

Validate