library(CSLSscenarios) library(dplyr) library(reshape2) library(NISTunits) library(lubridate) library(ggplot2) library(extrafont) library(DT) save <- TRUE scenarios <- data.frame(scenario = c("no_irr", "cur_irr", "fut_irr"), scenario_name = c("No Irrigated Agriculture", "Current Irrigated Agriculture", "Potential Irrigated Agriculture"), colors = c("steelblue4", "goldenrod1", "darkred")) # Lake Levels ------------------------------------------------------------------ MODFLOW <- CSLSdata::MODFLOW %>% filter(.data$scenario %in% scenarios$scenario, year(.data$date) >= 1986, year(.data$date) <= 2018) %>% mutate(year = year(.data$date), month = month(.data$date), time = .data$year - min(.data$year) + .data$month/12, level = NISTmeterTOft(.data$level_m)) %>% left_join(scenarios, by = "scenario") %>% select(scenario = .data$scenario_name, sim = .data$sim, lake = .data$lake, date = .data$date, time = .data$time, level = .data$level) # Metrics ---------------------------------------------------------------------- MODFLOW_metrics <- CSLSscenarios::MODFLOW_metrics %>% filter(.data$scenario %in% scenarios$scenario) %>% group_by(.data$lake, .data$metric, .data$variable, .data$scenario, .data$series) %>% summarise(base = .data$value[which(.data$sim == 1)], sd = sd(.data$value, na.rm = TRUE), .groups = "drop") %>% left_join(scenarios, by = "scenario") %>% select(scenario = .data$scenario_name, lake = .data$lake, metric = .data$metric, variable = .data$variable, series = .data$series, base = .data$base, sd = .data$sd) MODFLOW_metrics$scenario <- factor(MODFLOW_metrics$scenario, levels = scenarios$scenario_name) exceeds <- MODFLOW_metrics %>% filter(.data$metric == "exceedance_level", .data$scenario == "No Irrigated Agriculture", .data$series == "month") %>% mutate(base = NISTmeterTOft(.data$base))
# Unit Conversions m_metrics <- c("exceedance_level", "mean_depth", "max_depth", "median_level", "median_rise_rate", "median_fall_rate") m2_metrics <- c("area", "centrarchid_substrate") m3_metrics <- c("volume") # Rounding d1_metrics <- c("exceedance_level", "mean_depth", "max_depth", "area", "centrarchid_substrate", "upland_pcnt", "inland_beach_pcnt", "emergent_pcnt", "floating_pcnt", "submergent_pcnt", "submergent_algae_pcnt", "submergent_weed_pcnt", "median_level", "cv_level", "median_rise_rate", "median_fall_rate", "season_higher", "season_lower", "good_spawning") d0_metrics <- c("volume", "num_dur", "num_2yr", "median_dur", "cv_dur", "rise_3ft", "rise_1_5ft", "fall_3ft", "fall_1_5ft", "cv_rise_rate", "cv_fall_rate") # Friendly names med_metrics <- data.frame(metric = c("median_level", "cv_level"), name = c("Median Lake Level (ft)", "CV of Max Depth (%)")) med_var <- data.frame(variable = c("0", "1", "4", "7", "10"), var_name = c("Overall", "Winter", "Spring", "Summer", "Fall")) exceed_metrics <- data.frame(metric = c("exceedance_level", "area", "volume", "mean_depth", "max_depth"), name = c("Elevation (ft)", "Area (acres)", "Volume (acre-ft)", "Mean Depth (ft)", "Max Depth (ft)")) exceed_var <- data.frame(variable = c("10", "25", "50", "75", "90"), var_name = c("Infrequent High", "Frequent High", "Median", "Frequent Low", "Infrequent Low")) plant_metrics <- data.frame(metric = c("upland_pcnt", "inland_beach_pcnt", "emergent_pcnt", "floating_pcnt", "submergent_pcnt", "submergent_algae_pcnt", "submergent_weed_pcnt"), name = c("Upland (% of lake)", "Inland Beach (% of lake)", "Emergents (% of lake)", "Floating-Leaf (% of lake)", "Submergents (% of lake)", "Submergent Macroalgae (% of lake)", "Submergent Pondweeds (% of lake)")) plant_var <- data.frame(variable = c("10", "25", "50", "75", "90"), var_name = c("Infrequent High", "Frequent High", "Median", "Frequent Low", "Infrequent Low")) dur_metrics <- data.frame(metric = c("num_dur", "num_2yr", "median_dur", "cv_dur"), name = c("Times Exceeded for 1+ months", "Times Exceeded for 2+ yrs", "Median Duration (months)", "CV of Duration (%)")) dur_var <- data.frame(variable = c("10", "25", "a50", "b50", "75", "90"), var_name = c("Infrequent High", "Frequent High", "Above Median", "Below Median", "Frequent Low", "Infrequent Low")) rate_metrics <- data.frame(metric = c("median_rise_rate", "cv_rise_rate", "rise_1_5ft", "rise_3ft", "median_fall_rate", "cv_fall_rate", "fall_1_5ft", "fall_3ft"), name = c("Median Rise Rate (ft/time)", "CV of Rise Rate (%)", "Times Rise Rate > 1.5 ft/time", "Times Rise Rate > 3 ft/time", "Median Fall Rate (ft/time)", "CV of Fall Rate (%)", "Times Fall Rate > 1.5 ft/time", "Times Fall Rate > 3 ft/time")) rate_var <- data.frame(variable = c("1", "3", "12"), var_name = c("1 Month", "3 Month", "12 Month")) time_metrics <- data.frame(metric = c("season_higher", "good_spawning"), name = c("% Years > Prior Season", "% Years > Prior Growing Season")) time_var <- data.frame(variable = c("1", "4", "high_spring", "7", "10"), var_name = c("Winter", "Spring", "Spring", "Summer", "Fall"))
format_table <- function(df, metrics, variables, series = "month", m_metrics, m2_metrics, m3_metrics, d1_metrics, d0_metrics) { # Filter to metrics of interest ---------------------------------------------- this_df <- df %>% filter(.data$series == !!series, .data$metric %in% metrics$metric, .data$variable %in% variables$variable) %>% select(.data$lake, .data$scenario, .data$metric, .data$variable, .data$base, .data$sd) %>% left_join(metrics, by = "metric") %>% left_join(variables, by = "variable") this_df$name <- factor(this_df$name, levels = metrics$name) this_df$var_name <- factor(this_df$var_name, levels = unique(variables$var_name)) # Convert Units -------------------------------------------------------------- unit_df <- this_df %>% mutate(base = ifelse(.data$metric %in% m_metrics, NISTmeterTOft(.data$base), ifelse(.data$metric %in% m2_metrics, NISTsqrMeterTOacre(.data$base), ifelse(.data$metric %in% m3_metrics, NISTcubMeterTOacreFt(.data$base), .data$base))), sd = ifelse(.data$metric %in% m_metrics, NISTmeterTOft(.data$sd), ifelse(.data$metric %in% m2_metrics, NISTsqrMeterTOacre(.data$sd), ifelse(.data$metric %in% m3_metrics, NISTcubMeterTOacreFt(.data$sd), .data$sd)))) # Round values and convert to text ------------------------------------------- round_df <- unit_df %>% mutate(base = ifelse(.data$metric %in% d1_metrics, sprintf("%.1f", .data$base), ifelse(.data$metric %in% d0_metrics, sprintf("%.0f", .data$base), sprintf("%f", .data$base))), sd = ifelse(.data$metric %in% d1_metrics, sprintf("%.1f", .data$sd), ifelse(.data$metric %in% d0_metrics, sprintf("%.0f", .data$sd), sprintf("%f", .data$sd))), text = sprintf("%s (%s)", .data$base, .data$sd)) # Format data frame ---------------------------------------------------------- table_df <- round_df %>% select(.data$lake, .data$scenario, .data$name, .data$var_name, .data$text) %>% dcast(lake+var_name+scenario~name, value.var = "text") %>% arrange(.data$lake, .data$var_name, .data$scenario) colnames(table_df) <- c("Lake", "Metric", "Scenario", unique(metrics$name)) return(table_df) }
plot_levels <- function(df, exceeds, scenarios, only_base = FALSE, text_size = 10, lake = NULL) { if (!is.null(lake)) { df <- df %>% filter(.data$lake == !!lake) exceeds <- exceeds %>% filter(.data$lake == !!lake) } if (only_base) { plot_obj <- ggplot(df) } else { plot_obj <- ggplot(df) + geom_line(aes(x = .data$time, y = .data$level, group = interaction(.data$scenario, .data$sim), color = .data$scenario), alpha = 0.03) } solid_line <- exceeds %>% filter(.data$variable == "50") dashed_line <- exceeds %>% filter(.data$variable %in% c("25", "75")) rectangle <- exceeds %>% filter(.data$variable %in% c("10", "90")) %>% select(.data$lake, .data$variable, .data$base) %>% dcast(lake~variable, value.var = "base") plot_obj <- plot_obj + geom_rect(data = rectangle, mapping = aes(xmin = -Inf, xmax = Inf, ymin = .data$`90`, ymax = .data$`10`), fill = "steelblue4", color = NA, alpha = 0.1) + geom_hline(data = solid_line, aes(yintercept = .data$base), linetype = "solid") + geom_hline(data = dashed_line, aes(yintercept = .data$base), linetype = "dashed") + geom_line(data = filter(df, .data$sim == 1), aes(x = .data$time, y = .data$level, group = .data$scenario, color = .data$scenario)) + scale_color_manual(name = "Scenario", breaks = scenarios$scenario_name, values = scenarios$colors, labels = scenarios$scenario_name) + labs(x = "Time (yrs)", y = "Lake Elevation (ft)") + scale_x_continuous(expand = c(0,0), breaks = seq(0,34,5), minor_breaks = seq(0, 34, 1)) + theme_bw() + theme(text = element_text(family = "Segoe UI Semilight", size = text_size), legend.position = "top") # Add facet if plotting all lakes on one if (is.null(lake)) { plot_obj <- plot_obj + facet_wrap(~lake, scales = "free_y", ncol = 1) } return(plot_obj) }
plot_exceedances <- function(df, scenarios, text_size = 10, base_sim = 1, lake = NULL) { if (!is.null(lake)) { df <- df %>% filter(.data$lake == !!lake) } exceeds <- NULL for (scenario in unique(df$scenario)) { this_scenario <- df %>% filter(.data$scenario == !!scenario, .data$sim == base_sim) this_exceeds <- calculate_exceedances(this_scenario, probs = seq(1,99,1)) %>% mutate(probs = as.numeric(as.character(.data$variable)), scenario = scenario) exceeds <- rbind(exceeds, this_exceeds) } plot_obj <- ggplot(exceeds) + geom_line(aes(x = .data$probs, y = .data$value, group = .data$scenario, color = .data$scenario)) + scale_color_manual(name = "Scenario", breaks = scenarios$scenario_name, values = scenarios$colors, labels = scenarios$scenario_name) + scale_x_continuous(expand = c(0,0), breaks = seq(0,100,10)) + labs(x = "Exceedance Probability (%)", y = "Lake Elevation (ft)") + theme_bw() + theme(text = element_text(family = "Segoe UI Semilight", size = text_size), legend.position = "top") # Add facet if plotting all lakes on one if (is.null(lake)) { plot_obj <- plot_obj + facet_wrap(~lake, scales = "free_y", ncol = 1) } return(plot_obj) }
plot_durations <- function(df, scenarios, text_size = 10, base_sim = 1, probs = c(10, 25, 50, 75, 90), max_months = 12*20, variables, only_no_irr = TRUE, lake) { short_names <- c("No Irrigated Ag", "Current Irrigated Ag", "Potential Irrigated Ag") df <- df %>% filter(.data$lake == !!lake) df$scenario <- factor(df$scenario, levels = c("No Irrigated Agriculture", "Current Irrigated Agriculture", "Potential Irrigated Agriculture"), labels = short_names) durations <- NULL for (scenario in unique(df$scenario)) { if (only_no_irr) { exceeds_df <- df %>% filter(.data$scenario == "No Irrigated Ag", .data$sim == base_sim) } else { exceeds_df <- df %>% filter(.data$scenario == !!scenario, .data$sim == base_sim) } exceeds <- calculate_exceedances(exceeds_df, probs = probs) %>% dcast(lake~variable, value.var = "value") this_scenario <- df %>% filter(.data$scenario == !!scenario, .data$sim == base_sim) this_dur <- calculate_durations(this_scenario, probs = probs, exceeds = exceeds) %>% mutate(scenario = scenario) durations <- rbind(durations, this_dur) } durations$variable <- factor(durations$variable, levels = variables$variable, labels = variables$var_name) if (!is.null(max_months)) { durations$value[durations$value > max_months] <- max_months } durations$scenario <- factor(durations$scenario, levels = short_names) plot_obj <- ggplot(data = durations, aes(x = .data$value/12, color = .data$scenario, fill = .data$scenario)) + geom_histogram(binwidth = 1, alpha = 0.3, position = "identity") + scale_fill_manual(name = "Scenario", breaks = short_names, values = scenarios$colors, labels = scenarios$scenario_name) + scale_color_manual(name = "Scenario", breaks = short_names, values = scenarios$colors, labels = scenarios$scenario_name) + scale_y_continuous(expand = c(0,0), limits = c(0, 10), breaks = seq(0,10,5), minor_breaks = seq(0,10,1)) + scale_x_continuous(expand = c(0,0), limits = c(-1, max_months/12+1), breaks = seq(0,max_months/12,5), labels = c(as.character(seq(0,max_months/12-5,5)), sprintf(">%d", max_months/12)), minor_breaks = seq(0,max_months/12,1)) + facet_grid(scenario~variable, switch = "y") + labs(x = "Number of years", y = "Number of times") + theme_bw() + theme(text = element_text(family = "Segoe UI Semilight", size = text_size), legend.position = "top") return(plot_obj) }
plot_rates <- function(df, scenarios, variables, text_size = 10, base_sim = 1, lake) { short_names <- c("No Irrigated Ag", "Current Irrigated Ag", "Potential Irrigated Ag") df <- df %>% filter(.data$lake == !!lake) df$scenario <- factor(df$scenario, levels = c("No Irrigated Agriculture", "Current Irrigated Agriculture", "Potential Irrigated Agriculture"), labels = short_names) rates <- NULL for (scenario in unique(df$scenario)) { this_scenario <- df %>% filter(.data$scenario == !!scenario, .data$sim == base_sim) this_rate <- calculate_rates(this_scenario) %>% mutate(scenario = scenario) rates <- rbind(rates, this_rate) } rates$variable <- factor(rates$variable, levels = variables$variable, labels = variables$var_name) max_rate <- ceiling(4*max(abs(rates$value), na.rm = TRUE))/4 rates$scenario <- factor(rates$scenario, levels = short_names) plot_obj <- ggplot(data = rates, aes(x = .data$value, color = .data$scenario, fill = .data$scenario)) + geom_histogram(binwidth = 0.25, alpha = 0.2, position = "identity") + scale_fill_manual(name = "Scenario", breaks = short_names, values = scenarios$colors, labels = scenarios$scenario_name) + scale_color_manual(name = "Scenario", breaks = short_names, values = scenarios$colors, labels = scenarios$scenario_name) + scale_y_continuous(expand = c(0,0)) + scale_x_continuous(expand = c(0,0), minor_breaks = seq(-max_rate, max_rate, 0.25)) + facet_grid(scenario~variable, switch = "y") + labs(x = "Rate of Change (ft/time period)", y = "Number of Occurrences") + theme_bw() + theme(text = element_text(family = "Segoe UI Semilight", size = text_size), legend.position = "top") return(plot_obj) }
This is the current plan for figures and tables for the Hydrology section in the Significance Determination section. I think with this way of arranging info, we can get away with one table per topic (no need to split by lake), which I've done here to try to minimize the number of tables and figures we have. It's possible that doesn't make as much sense for the plant tables, though, and I'm open to other ideas on this!
These start on "January 1986", but I'm plotting as model years to avoid erroneous comparisons to the past.
plot_levels(MODFLOW, exceeds, scenarios, only_base = TRUE)
p1 <- plot_levels(MODFLOW, exceeds, scenarios, only_base = TRUE, lake = "Pleasant") p2 <- plot_levels(MODFLOW, exceeds, scenarios, only_base = TRUE, lake = "Long") p3 <- plot_levels(MODFLOW, exceeds, scenarios, only_base = TRUE, lake = "Plainfield") if (save) { fig_width <- 6.5 fig_height <- 2.5 ggsave("levels_psnt.png", p1, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("levels_long.png", p2, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("levels_pfl.png", p3, device = "png", width = fig_width, height = fig_height, units = "in") } p1 p2 p3
Kind of interesting - essentially a uniform shift downward at all lakes.
plot_exceedances(MODFLOW, scenarios)
p1 <- plot_exceedances(MODFLOW, scenarios, lake = "Pleasant") p2 <- plot_exceedances(MODFLOW, scenarios, lake = "Long") p3 <- plot_exceedances(MODFLOW, scenarios, lake = "Plainfield") if (save) { fig_width <- 6.5 fig_height <- 2.5 ggsave("exceed_psnt.png", p1, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("exceed_long.png", p2, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("exceed_pfl.png", p3, device = "png", width = fig_width, height = fig_height, units = "in") } p1 p2 p3
Histograms for each scenario are overlayed on top of one another. Levels do not really fluctuate about the median. Instead, they go down fast, stay down, then come back up at the very end. Thus, there are very few occurances of exceedance, and they tend to last a long time. It makes these histograms kind of silly since there are so few occurrences. To make them somewhat more readable, I binned by year. Durations longer than 20 years include:
p1 <- plot_durations(MODFLOW, scenarios, variables = dur_var, lake = "Pleasant") p2 <- plot_durations(MODFLOW, scenarios, variables = dur_var, lake = "Long") p3 <- plot_durations(MODFLOW, scenarios, variables = dur_var, lake = "Plainfield") if (save) { fig_width <- 9 fig_height <- 5 ggsave("dur_psnt.png", p1, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("dur_long.png", p2, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("dur_pfl.png", p3, device = "png", width = fig_width, height = fig_height, units = "in") } p1 p2 p3
This is another way of thinking about the durations, where don't use the no-irrigated-agriculture exceedance levels for both scenarios, but rather use whatever the exceedance level is in the given scenario. With this, you can better see that durations don't really change in and of themselves, they are only different because the entire time series is lower.
p1 <- plot_durations(MODFLOW, scenarios, variables = dur_var, only_no_irr = FALSE, lake = "Pleasant") p2 <- plot_durations(MODFLOW, scenarios, variables = dur_var, only_no_irr = FALSE, lake = "Long") p3 <- plot_durations(MODFLOW, scenarios, variables = dur_var, only_no_irr = FALSE, lake = "Plainfield") if (save) { fig_width <- 9 fig_height <- 5 ggsave("dur2_psnt.png", p1, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("dur2_long.png", p2, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("dur2_pfl.png", p3, device = "png", width = fig_width, height = fig_height, units = "in") } p1 p2 p3
Histograms for each scenario are overlayed on top of one another. They're generally the same. You could maybe argue that rise/fall rates are a little more extreme over 12 months with Current Irrigated Agriculture at Long and Plainfield, but I don't see these as meaningfully different.
p1 <- plot_rates(MODFLOW, scenarios, variables = rate_var, lake = "Pleasant") p2 <- plot_rates(MODFLOW, scenarios, variables = rate_var, lake = "Long") p3 <- plot_rates(MODFLOW, scenarios, variables = rate_var, lake = "Plainfield") if (save) { fig_width <- 6.5 fig_height <- 5 ggsave("rates_psnt.png", p1, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("rates_long.png", p2, device = "png", width = fig_width, height = fig_height, units = "in") ggsave("rates_pfl.png", p3, device = "png", width = fig_width, height = fig_height, units = "in") } p1 p2 p3
datatable(format_table(MODFLOW_metrics, med_metrics, med_var, series = "season", m_metrics, m2_metrics, m3_metrics, d1_metrics, d0_metrics))
datatable(format_table(MODFLOW_metrics, exceed_metrics, exceed_var, series = "month", m_metrics, m2_metrics, m3_metrics, d1_metrics, d0_metrics))
datatable(format_table(MODFLOW_metrics, plant_metrics, plant_var, series = "month", m_metrics, m2_metrics, m3_metrics, d1_metrics, d0_metrics))
Note: these metrics are based on the "no-irrigated-agriculture" exceedance levels. As noted above, nearly all of the differences seen here are due to the lower magnitude, not necessarily a change in the durations or frequency behavior itself.
datatable(format_table(MODFLOW_metrics, dur_metrics, dur_var, series = "month", m_metrics, m2_metrics, m3_metrics, d1_metrics, d0_metrics))
datatable(format_table(MODFLOW_metrics, rate_metrics, rate_var, series = "month", m_metrics, m2_metrics, m3_metrics, d1_metrics, d0_metrics))
datatable(format_table(MODFLOW_metrics, time_metrics, time_var, series = "month", m_metrics, m2_metrics, m3_metrics, d1_metrics, d0_metrics))
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.