#' Creates a specific energy diagram for a trapezoidal channel
#'
#' This function plots a specific energy diagram of a trapezoidal
#' (including rectangular and triangular) channel, with annotation of
#' critical depth and minimum specific energy.
#'
#' @param Q flow rate [\eqn{m^3 s^{-1}}{m^3/s} or \eqn{ft^3 s^{-1}}{ft^3/s}]
#' @param b bottom width [\eqn{m}{m} or \eqn{ft}{ft}]
#' @param m side slope (H:1) [unitless]
#' @param y depth(s) of flow (a numeric vector of length <= 2) [\eqn{m}{m} or \eqn{ft}{ft}] (optional)
#' @param scale multiplier (of yc) for axis scales (default is 3)
#' @param units character vector that contains the system of units [options are
#' \code{SI} for International System of Units and \code{Eng} for English (US customary)
#' units.
#'
#' @return a specific energy diagram
#'
#' @author Ed Maurer
#'
#' @details Specific Energy, E, is the energy, expressed as a head (i.e., the mechanical energy
#' per unit weight of the water, with units of length) relative to the channel bottom.
#' It is calculated as:
#' \deqn{E = y+\alpha\frac{Q^{2}}{2g\,A^{2}} = y+\alpha\frac{V^{2}}{2g}}
#' where \eqn{y} is flow depth, \eqn{A} is the cross-sectional flow area, \eqn{{V}=\frac{Q}{A}}, and
#' and \eqn{\alpha} is a kinetic energy correction factor to account for non-uniform velocities across
#' the cross-section; \eqn{\alpha=1.0} in this function (as is commonly assumed).
#'
#' @examples
#'
#' # Draw a specific energy diagram for a cross-section with flow 1, width 2, side slope 3:1 (H:V)
#' spec_energy_trap(Q = 1.0, b = 2.0, m = 3.0, scale = 4, units = "SI")
#'
#' # Draw the same specific energy diagram adding lines for depths, y = 0.5 and 0.8 m
#' spec_energy_trap(Q = 1.0, b = 2.0, m = 3.0, scale = 4, y = c(0.5, 0.8), units = "SI")
#'
#' @import ggplot2
#' @import grid
#'
#' @export
spec_energy_trap <- function(Q = NULL, b = NULL, m = NULL, y = NULL, scale = 3,
units = c("SI", "Eng")) {
# Check for packages needed to create plot
if(!requireNamespace("ggplot2", quietly = TRUE)) {
stop("xc_trap diagram plot requires ggplot2 to be installed.",
call. = FALSE)
}
if(!requireNamespace("grid", quietly = TRUE)) {
stop("xc_trap diagram plot requires grid to be installed.",
call. = FALSE)
}
if (length(c(Q, b, m)) != 3) {
stop("One of required inputs is missing: Q, b or m may be zero")
}
if (any(c(Q, b, m) < 0)) {
stop("Either Q, b, or m is < 0. All of these variables must be non-negative")
}
if ( ( b == 0 ) & ( m == 0 ) ) {
stop("m (side slope) and b (bottom width) are zero. Channel has no area")
}
if( inherits(Q, "units")) Q <- units::drop_units(Q)
if( inherits(b, "units")) b <- units::drop_units(b)
if( inherits(m, "units")) m <- units::drop_units(m)
scalefact <- scale
units <- units
if (length(units) != 1) stop("Incorrect unit system. Specify either SI or Eng.")
if (units == "SI") {
g <- 9.80665 # m / s^2
txtx <- sprintf("Specific Energy, E (m)")
txty <- sprintf("Depth, y (m)")
} else if (units == "Eng") {
g <- 32.2 #ft / s^2
txtx <- sprintf("Specific Energy, E (ft)")
txty <- sprintf("Depth, y (ft)")
} else if (all(c("SI", "Eng") %in% units == FALSE) == FALSE) {
stop("Incorrect unit system. Must be SI or Eng")
}
ycfun <- function(yc) { (Q^2 / g) - ((b * yc + m * yc^2)^3)/(b + 2 * m * yc)}
yc <- uniroot(ycfun, interval = c(0.0000001, 200), extendInt = "yes")$root
Ac <- yc * (b + m * yc)
Emin <- yc + ((Q ^ 2) / (2 * g * Ac ^ 2))
ylim <- yc*scalefact
yalt <- E1 <- yalt2 <- E2 <- NULL
#if y is given, find alternate depth
if (length(y) > 2) {
stop("y cannot be greater than 2")
}
if ( length(y) >= 1 ) {
E1 <- y[1] + (Q ^ 2) / (2 * g * (y[1] * (b + m * y[1]))^2)
if ( y[1] > yc ) interv <- c(0.0000001, yc)
if ( y[1] < yc ) interv <- c(yc, 200)
yaltfun <- function(ya) { E1 - (ya + (Q ^ 2) / (2 * g * (ya * (b + m * ya))^2)) }
yalt <- uniroot(yaltfun, interval = interv, extendInt = "yes")$root
ylim <- max(ylim, y[1], yalt)
if ( length(y) == 2 ) {
E2 <- y[2] + (Q ^ 2) / (2 * g * (y[2] * (b + m * y[2]))^2)
if ( y[2] > yc ) interv <- c(0.0000001, yc)
if ( y[2] < yc ) interv <- c(yc, 200)
yaltfun2 <- function(ya) { E2 - (ya + (Q ^ 2) / (2 * g * (ya * (b + m * ya))^2)) }
yalt2 <- uniroot(yaltfun2, interval = interv, extendInt = "yes")$root
ylim <- max(ylim, y[1], y[2], yalt)
}
}
#round up ylim a little
ordr <- floor(log10(ylim))
ymax <- round(ylim + 0.5*10^(ordr-1), -(ordr-1))
ys <- seq(0.1*yc,ymax,length=1000)
As <- ys * (b + m * ys)
Es <- ys + ((Q ^ 2) / (2 * g * As ^ 2))
xx <- yy <- NULL
eycurve <- data.frame( xx = Es , yy = ys )
eycurve <- subset(eycurve, xx <= ymax,select=c(xx, yy))
seg1 <- data.frame(xx = c(Emin, Emin),yy = c(0, yc))
seg2 <- data.frame(xx = c(0, Emin),yy = c(yc, yc))
txt1 <- sprintf("Emin=%.3f",Emin)
txt2 <- sprintf("yc=%.3f",yc)
offst <- ymax*0.0275
p <- ggplot2::ggplot() +
ggplot2::geom_path(data=eycurve,ggplot2::aes(x=xx, y=yy),color="black", linewidth=1.5) +
ggplot2::scale_x_continuous(txtx, limits = c(0, ymax), expand = c(0,0)) +
ggplot2::scale_y_continuous(txty, limits = c(0, ymax), expand = c(0,0)) +
ggplot2::geom_abline(slope = 1, intercept = 0 ,color="black",linetype = "dashed") +
ggplot2::geom_segment(ggplot2::aes(x=Emin, xend=Emin, y=0, yend=yc)) +
ggplot2::geom_segment(ggplot2::aes(x=0, xend=Emin, y=yc, yend=yc)) +
ggplot2::annotate(geom="text", x=Emin-offst, y=yc/2, label=txt1, angle = 90, size = 3) +
ggplot2::annotate(geom="text", x=Emin/2, y=yc+offst, label=txt2, angle = 0, size = 3) +
ggplot2::coord_fixed(ratio = 1) +
ggplot2::theme_bw()
if ( ! is.null(yalt) ) {
txt3 <- sprintf("y=%.3f",y[1])
txt4 <- sprintf("y=%.3f",yalt)
txt5 <- sprintf("E=%.3f",E1)
p <- p + ggplot2::geom_segment(ggplot2::aes(x=E1, xend=E1, y=0, yend=max(y[1],yalt)),linetype=3) +
ggplot2::geom_segment(ggplot2::aes(x=0, xend=E1, y=yalt, yend=yalt), linetype=3) +
ggplot2::geom_segment(ggplot2::aes(x=0, xend=E1, y=y[1], yend=y[1]), linetype=3) +
ggplot2::annotate(geom="text", x=min(Emin/2,E1/2), y=y[1]+offst, label=txt3, angle = 0, size = 3,hjust = "left") +
ggplot2::annotate(geom="text", x=min(Emin/2,E1/2), y=yalt+offst, label=txt4, angle = 0, size = 3,hjust = "left") +
ggplot2::annotate(geom="text", x=E1+offst, y=(y[1]+yalt)/2, label=txt5, angle = 90, size = 3)
}
if ( ! is.null(yalt2) ) {
txt3 <- sprintf("y=%.3f",y[2])
txt4 <- sprintf("y=%.3f",yalt2)
txt5 <- sprintf("E=%.3f",E2)
p <- p + ggplot2::geom_segment(ggplot2::aes(x=E2, xend=E2, y=0, yend=max(y[2],yalt2)),linetype=3) +
ggplot2::geom_segment(ggplot2::aes(x=0, xend=E2, y=yalt2, yend=yalt2), linetype=3) +
ggplot2::geom_segment(ggplot2::aes(x=0, xend=E2, y=y[2], yend=y[2]), linetype=3) +
ggplot2::annotate(geom="text", x=min(Emin/2,E2/2), y=y[2]+offst, label=txt3, angle = 0, size = 3,hjust = "left") +
ggplot2::annotate(geom="text", x=min(Emin/2,E2/2), y=yalt2+offst, label=txt4, angle = 0, size = 3,hjust = "left") +
ggplot2::annotate(geom="text", x=E2+offst, y=(y[2]+yalt2)/2, label=txt5, angle = 90, size = 3)
}
return(p)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.