| Title: | Empirical Reservoir Eutrophication Modelling with Oklahoma Calibration |
|---|---|
| Description: | Empirical reservoir water quality modelling using Walker's 'BATHTUB' Model 1 (second-order available-phosphorus sedimentation) from Walker (1985) <https://hdl.handle.net/11681/13884> and Walker (1996) <https://hdl.handle.net/11681/4353> as the default retention model. The Vollenweider (1976) hydraulic- residence form and the equivalent formulation of Larsen and Mercier (1976) are available as alternatives. Predicts in-lake total phosphorus, total nitrogen, chlorophyll-a, and Secchi depth from tributary nutrient and hydraulic loading inputs, and computes Carlson (1977) <doi:10.4319/lo.1977.22.2.0361> Trophic State Indices. Optional Oklahoma-specific chlorophyll and Secchi regression coefficients are provided, calibrated from publicly available state lake monitoring data. Supports single-segment and multi-segment reservoir configurations and load-reduction scenario analysis. Designed to complement watershed loading models such as the Soil and Water Assessment Tool ('SWAT'; <https://swat.tamu.edu>) and the U.S. EPA Hydrologic and Water Quality System ('HAWQS'; <https://hawqs.tamu.edu>) in a two-model nutrient management workflow. |
| Authors: | Jordon Henderson [aut, cre, cph] |
| Maintainer: | Jordon Henderson <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.10 |
| Built: | 2026-05-29 13:00:00 UTC |
| Source: | https://github.com/cran/okBATHTUB |
ok_hydraulics() takes the inflow volume from ok_load() and the
reservoir's morphometric parameters to compute two quantities that
drive nutrient retention in all subsequent steps:
Hydraulic residence time (tau, yr): reservoir volume
divided by annual outflow, where volume is approximated as
mean_depth * surface_area.
Areal water load (q_s, m/yr): inflow volume divided by
surface area. The primary driver of settling-velocity based
retention.
ok_hydraulics(x, surface_area_ha, mean_depth_m, outflow_m3yr = NULL)ok_hydraulics(x, surface_area_ha, mean_depth_m, outflow_m3yr = NULL)
x |
An |
surface_area_ha |
Numeric. Reservoir surface area at normal pool (ha). Must be positive. |
mean_depth_m |
Numeric. Mean reservoir depth at normal pool (m). Must be positive. For Oklahoma reservoirs this is typically 2-10 m. |
outflow_m3yr |
Numeric. Annual outflow volume (m^3/yr). If
|
An okBATHTUB object at pipeline step "hydraulics".
Reservoir volume is computed as mean_depth * surface_area, treating
the reservoir as a right rectangular prism with flat bottom. This is a
simplification: real reservoirs have varying bathymetry, and the
relationship V = Z * A is exact only when Z is the
volume-weighted mean depth, which is what bathymetric surveys
typically report. If you only have maximum depth or a depth-area
regression estimate, expect roughly a factor-of-1.5 uncertainty in
tau and proportional downstream uncertainty in predicted in-lake
concentrations.
result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800 ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) print(result)result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800 ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) print(result)
ok_inlake() applies nutrient retention coefficients from
ok_retention() to predict in-lake total phosphorus (TP) and total
nitrogen (TN) concentrations via the mass balance
then uses empirical log-log regression to predict chlorophyll-a from in-lake TP and Secchi depth from chlorophyll-a.
Note on retention identity: when coefficients = "walker" (Walker
BATHTUB Model 1), the retention coefficient stored by ok_retention()
is back-calculated from Walker's quadratic mass balance solution so
that C_lake = C_in * (1 - R) exactly reproduces the Model 1 result.
ok_inlake(x, predict_chla = TRUE, predict_secchi = TRUE)ok_inlake(x, predict_chla = TRUE, predict_secchi = TRUE)
x |
An |
predict_chla |
Logical. Whether to predict chlorophyll-a from
in-lake TP. Default |
predict_secchi |
Logical. Whether to predict Secchi depth from
chlorophyll-a. Requires |
An okBATHTUB object at pipeline step "inlake".
Log-log linear regression:
Default coefficients are Walker's nationally-derived values
(, ); Oklahoma ecoregion-specific
values are applied when coefficients = "oklahoma".
Default Walker national: , .
In high-turbidity Oklahoma reservoirs, Secchi depth is often controlled more by inorganic suspended sediment than by algal biomass. This is partly captured by the Oklahoma ecoregion-specific Secchi regressions, but for reservoirs with very high non-algal turbidity (e.g. central and western Oklahoma), Secchi predictions should be interpreted with caution.
result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800 ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() |> ok_inlake() print(result)result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800 ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() |> ok_inlake() print(result)
Convenience wrapper around ok_lake_ecoregions for retrieving the
ecoregion assignment for one or more lakes. Always returns a data
frame so that the return type is stable across single-match and
multi-match results.
To get a bare ecoregion name string for use in ok_load(), use:
eco <- ok_lake_ecoregion("Arcadia Lake", exact = TRUE)$eco_l3_name
ok_lake_ecoregion(lake_name, exact = FALSE, simplify = NULL)ok_lake_ecoregion(lake_name, exact = FALSE, simplify = NULL)
lake_name |
Character. One or more lake names to look up.
Partial matching is supported (case-insensitive) unless |
exact |
Logical. If |
simplify |
Deprecated. Retained for backward compatibility with pre-0.1.3 callers; ignored with a deprecation warning. The function always returns a data frame. |
A data frame of zero or more rows from ok_lake_ecoregions
with all columns. An unmatched lookup returns an empty data frame
(with the correct column structure) plus a warning.
ok_lake_ecoregions, ok_reservoir()
# Standard usage: exact match, extract ecoregion name eco <- ok_lake_ecoregion("Arcadia Lake", exact = TRUE)$eco_l3_name eco # Partial match: returns a data frame of all matches ok_lake_ecoregion("Tenkiller") # Use in pipeline eco <- ok_lake_ecoregion("Arcadia Lake", exact = TRUE)$eco_l3_name if (length(eco) == 1L && !is.na(eco)) { ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = eco) }# Standard usage: exact match, extract ecoregion name eco <- ok_lake_ecoregion("Arcadia Lake", exact = TRUE)$eco_l3_name eco # Partial match: returns a data frame of all matches ok_lake_ecoregion("Tenkiller") # Use in pipeline eco <- ok_lake_ecoregion("Arcadia Lake", exact = TRUE)$eco_l3_name if (length(eco) == 1L && !is.na(eco)) { ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = eco) }
A data frame mapping 214 Oklahoma (and several border) lakes to their
EPA Level III ecoregion, with monitoring coverage statistics from a
2000-2024 snapshot of publicly available state lake monitoring
records. Useful for quickly resolving an ecoregion for use with
coefficients = "oklahoma", or for understanding which lakes have
sufficient monitoring history to support empirical analysis.
ok_lake_ecoregionsok_lake_ecoregions
A data frame with one row per lake and the following columns:
Character. Lake name as it appeared in source monitoring records.
Character. EPA Level III ecoregion numeric code
(as string), e.g. "28" for Cross Timbers. NA for lakes
outside Oklahoma's ecoregion boundaries (border / out-of-state
lakes that appeared in monitoring records).
Character. EPA Level III ecoregion name.
NA for unmapped lakes (see above).
Integer. Total number of monitoring stations on this lake in the source dataset.
Integer. Number of stations that met the
primary-station criteria used in the calibration workflow
(1+ years of usable data). Always <= n_sites_total.
Numeric. Approximate lake centroid latitude (WGS84 decimal degrees), computed as the mean of monitoring station coordinates.
Numeric. Approximate lake centroid longitude.
Integer. Maximum number of calendar years (in the 2000-2024 window) with any reported total phosphorus data at any station on the lake.
Integer. Same, for chlorophyll-a.
Integer. Same, for Secchi depth.
Integer. Same, for total nitrogen.
The n_sites_* and max_yrs_* columns reflect a 2000-2024 snapshot
taken when the calibration was performed. They are not updated
automatically with new monitoring data. Treat them as a useful
starting point for assessing data availability, not as a current
inventory. For up-to-date monitoring coverage, query the source
monitoring system directly.
Ecoregion assignment uses EPA Level III boundaries (Griffith et al.
2004) applied to lake centroid coordinates. A handful of lakes
(5 in the current dataset) have eco_l3_name = NA either because
they sit outside Oklahoma's ecoregion shapefile coverage (border
lakes in TX/KS that appeared in monitoring records) or because the
centroid fell in an ambiguous boundary zone. For these lakes,
modelling with coefficients = "oklahoma" will fall back to the
statewide pooled regressions.
Compiled from publicly available Oklahoma lake monitoring records
(2000-2024). Ecoregion polygons from Griffith, G.E. et al. (2004),
Ecoregions of Oklahoma, U.S. Geological Survey, Reston, Virginia.
See data-raw/ok_ecoregion_assignment.R in the package source for
the assignment script.
ok_lake_ecoregion(), ok_reservoirs
# All lakes in a specific ecoregion head(ok_lake_ecoregions[ok_lake_ecoregions$eco_l3_name == "Cross Timbers", ]) # Lakes with the longest monitoring history top_monitored <- ok_lake_ecoregions[ order(-ok_lake_ecoregions$max_yrs_tp), ][1:10, ] top_monitored[, c("lake_name", "eco_l3_name", "max_yrs_tp")]# All lakes in a specific ecoregion head(ok_lake_ecoregions[ok_lake_ecoregions$eco_l3_name == "Cross Timbers", ]) # Lakes with the longest monitoring history top_monitored <- ok_lake_ecoregions[ order(-ok_lake_ecoregions$max_yrs_tp), ][1:10, ] top_monitored[, c("lake_name", "eco_l3_name", "max_yrs_tp")]
ok_load() is the entry point for the okBATHTUB pipeline. It accepts
tributary hydraulic and nutrient loading data, validates inputs,
resolves the coefficient set, and returns an okBATHTUB object ready
to pass into ok_hydraulics().
All concentration inputs are volume-flow-weighted means representing
the period of analysis (typically an annual or seasonal average). If
multiple tributaries contribute to the reservoir, either aggregate
them manually before calling ok_load(), or use ok_load_multi() to
supply tributary data as a data frame.
ok_load( inflow_m3yr, tp_inflow_ugl, tn_inflow_ugl = NULL, tss_inflow_mgl = NULL, segment_label = "main", coefficients = "walker", ecoregion = NULL )ok_load( inflow_m3yr, tp_inflow_ugl, tn_inflow_ugl = NULL, tss_inflow_mgl = NULL, segment_label = "main", coefficients = "walker", ecoregion = NULL )
inflow_m3yr |
Numeric. Total annual tributary inflow volume (m^3/yr). Must be positive. |
tp_inflow_ugl |
Numeric. Flow-weighted mean total phosphorus concentration of tributary inflow (ug/L). Must be non-negative. |
tn_inflow_ugl |
Numeric. Flow-weighted mean total nitrogen
concentration of tributary inflow (ug/L). Optional. Default |
tss_inflow_mgl |
Numeric. Flow-weighted mean total suspended
solids concentration (mg/L). Optional. Default |
segment_label |
Character. Optional label for this reservoir
segment (e.g. |
coefficients |
One of |
ecoregion |
Character. EPA Level III ecoregion name
(e.g. |
An okBATHTUB object at pipeline step "load".
ok_load_multi(), ok_hydraulics()
# Minimum required inputs (TP only) result <- ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120) print(result) # Full inputs with TN and TSS result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800, tss_inflow_mgl = 35, segment_label = "lacustrine" ) # Oklahoma ecoregion-specific coefficients result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = "Cross Timbers" )# Minimum required inputs (TP only) result <- ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120) print(result) # Full inputs with TN and TSS result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800, tss_inflow_mgl = 35, segment_label = "lacustrine" ) # Oklahoma ecoregion-specific coefficients result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = "Cross Timbers" )
A convenience wrapper around ok_load() for reservoirs with more than
one tributary. Accepts a data frame of tributary data, computes
flow-weighted mean concentrations, sums inflow volumes, and calls
ok_load() with the aggregated values.
ok_load_multi( tributaries, segment_label = "main", coefficients = "walker", ecoregion = NULL )ok_load_multi( tributaries, segment_label = "main", coefficients = "walker", ecoregion = NULL )
tributaries |
A data frame with one row per tributary and these columns:
|
segment_label |
Character. Segment label passed to |
coefficients |
Coefficient set. Default |
ecoregion |
EPA Level III ecoregion name. Default |
An okBATHTUB object at pipeline step "load".
tribs <- data.frame( inflow_m3yr = c(30e6, 15e6), tp_inflow_ugl = c(110, 145), tn_inflow_ugl = c(1600, 2100) ) result <- ok_load_multi(tribs)tribs <- data.frame( inflow_m3yr = c(30e6, 15e6), tp_inflow_ugl = c(110, 145), tn_inflow_ugl = c(1600, 2100) ) result <- ok_load_multi(tribs)
ok_plot_response() generates a load-response curve showing how
predicted in-lake chlorophyll-a, Secchi depth, or mean TSI responds to
a range of inflow total phosphorus concentrations. This is the core
planning tool for answering "what inflow TP concentration achieves
a given trophic state target?"
The curve is generated by running the full okBATHTUB pipeline across a sequence of TP concentrations while holding all other parameters constant at the baseline values.
ok_plot_response( baseline, response = c("chla", "secchi", "tsi", "tp_inlake"), tp_range = c(10, 300), n_points = 100L, target_tsi = NULL, target_class = NULL, show_trophic_bands = NULL, current_tp = NULL, lake_name = NULL )ok_plot_response( baseline, response = c("chla", "secchi", "tsi", "tp_inlake"), tp_range = c(10, 300), n_points = 100L, target_tsi = NULL, target_class = NULL, show_trophic_bands = NULL, current_tp = NULL, lake_name = NULL )
baseline |
An |
response |
One of |
tp_range |
Numeric vector of length 2. Range of inflow TP
concentrations to plot (ug/L). Default |
n_points |
Integer. Number of points along the curve. Default 100. |
target_tsi |
Numeric. Optional horizontal reference line showing a
TSI target. Only shown when |
target_class |
Character. Trophic class target. Sets
|
show_trophic_bands |
Logical. Show coloured trophic state background
bands on TSI plots. Default |
current_tp |
Numeric. Optional vertical reference line showing the current inflow TP concentration. |
lake_name |
Character. Lake name for plot title. Default |
A ggplot2 object.
baseline <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = "Cross Timbers" ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) ok_plot_response(baseline, response = "tsi", target_class = "mesotrophic", current_tp = 120, lake_name = "Arcadia Lake")baseline <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = "Cross Timbers" ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) ok_plot_response(baseline, response = "tsi", target_class = "mesotrophic", current_tp = 120, lake_name = "Arcadia Lake")
ok_plot_scenario() visualises the output of ok_scenario() or
ok_scenario_sweep() as a faceted dot-and-line chart, showing how
key water quality metrics change across loading scenarios.
ok_plot_scenario( scenario_result, metrics = c("tp_inlake_ugl", "chla_ugl", "secchi_m", "tsi_mean"), highlight_target = TRUE, lake_name = NULL )ok_plot_scenario( scenario_result, metrics = c("tp_inlake_ugl", "chla_ugl", "secchi_m", "tsi_mean"), highlight_target = TRUE, lake_name = NULL )
scenario_result |
A data frame returned by |
metrics |
Character vector. Which metrics to plot. Any subset of
|
highlight_target |
Logical. If the scenario result contains a
|
lake_name |
Character. Lake name for the plot title. |
A ggplot2 object.
ok_scenario, ok_scenario_sweep
baseline <- ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = "Cross Timbers") |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) sweep <- ok_scenario_sweep(baseline, target_class = "mesotrophic") ok_plot_scenario(sweep, lake_name = "Arcadia Lake")baseline <- ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = "Cross Timbers") |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) sweep <- ok_scenario_sweep(baseline, target_class = "mesotrophic") ok_plot_scenario(sweep, lake_name = "Arcadia Lake")
ok_plot_segments() visualises the spatial gradient in water quality
from riverine to lacustrine zones of a multi-segment reservoir, using the
output of ok_segment_summary() or ok_segment_chain().
ok_plot_segments( segment_data, metrics = c("tp_inlake_ugl", "chla_ugl", "secchi_m", "tsi_mean"), lake_name = NULL )ok_plot_segments( segment_data, metrics = c("tp_inlake_ugl", "chla_ugl", "secchi_m", "tsi_mean"), lake_name = NULL )
segment_data |
Either a named list of |
metrics |
Character vector. Which metrics to plot. Any subset of
|
lake_name |
Character. Lake name for the plot title. |
A ggplot2 object.
ok_segment_chain, ok_segment_summary
segs <- list( list(label = "Riverine", surface_area_ha = 280, mean_depth_m = 3.1), list(label = "Transitional", surface_area_ha = 410, mean_depth_m = 4.5), list(label = "Lacustrine", surface_area_ha = 610, mean_depth_m = 5.8) ) chain <- ok_segment_chain(45e6, 150, tn_inflow_ugl = 2200, segments = segs) ok_plot_segments(chain, lake_name = "Example Reservoir")segs <- list( list(label = "Riverine", surface_area_ha = 280, mean_depth_m = 3.1), list(label = "Transitional", surface_area_ha = 410, mean_depth_m = 4.5), list(label = "Lacustrine", surface_area_ha = 610, mean_depth_m = 5.8) ) chain <- ok_segment_chain(45e6, 150, tn_inflow_ugl = 2200, segments = segs) ok_plot_segments(chain, lake_name = "Example Reservoir")
ok_plot_tsi() produces a Carlson (1977) TSI deviation diagram,
plotting TSI(TP) on the x-axis against TSI(Chl-a) and TSI(Secchi) on
the y-axis with trophic state background bands. Deviations from the
1:1 line indicate light limitation (Chl-a below TP line) or non-algal
turbidity (Secchi below Chl-a line).
Can accept either a single okBATHTUB result, or a data frame
containing the required TSI columns (e.g. from ok_scenario()
or a user-supplied observed-data summary).
ok_plot_tsi(x, color_by = NULL, lake_name = NULL, show_diagonal = TRUE)ok_plot_tsi(x, color_by = NULL, lake_name = NULL, show_diagonal = TRUE)
x |
An |
color_by |
Character. Column name in a data frame to color points by.
Common choices: |
lake_name |
Character. Lake name for the plot title when |
show_diagonal |
Logical. Show the 1:1 reference line. Default
|
A ggplot2 object.
Carlson, R.E. (1977). A trophic state index for lakes. Limnology and Oceanography, 22(2), 361-369.
# From a single pipeline result result <- ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = "Cross Timbers") |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() |> ok_inlake() |> ok_tsi() ok_plot_tsi(result, lake_name = "Arcadia Lake")# From a single pipeline result result <- ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "oklahoma", ecoregion = "Cross Timbers") |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() |> ok_inlake() |> ok_tsi() ok_plot_tsi(result, lake_name = "Arcadia Lake")
ok_reservoir() retrieves morphometric parameters for one or more
Oklahoma reservoirs from the bundled ok_reservoirs dataset.
Returns a data frame that can be used directly with ok_hydraulics().
ok_reservoir( lake_name = NULL, exact = FALSE, ecoregion = NULL, data_quality = c("A", "B") )ok_reservoir( lake_name = NULL, exact = FALSE, ecoregion = NULL, data_quality = c("A", "B") )
lake_name |
Character. One or more lake names to look up. Partial
matching is supported - |
exact |
Logical. If |
ecoregion |
Character. Filter results to a specific EPA Level III
ecoregion name. Default |
data_quality |
Character vector. Filter to specific data quality
codes. Default |
A data frame with one row per matched reservoir containing all
columns from ok_reservoirs.
ok_reservoirs, ok_hydraulics()
# Look up a single lake (partial match) ok_reservoir("Arcadia") # Exact match ok_reservoir("Arcadia Lake", exact = TRUE) # Use in pipeline res <- ok_reservoir("Arcadia Lake") if (nrow(res) > 0) { ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120) |> ok_hydraulics( surface_area_ha = res$surface_area_ha[1], mean_depth_m = res$mean_depth_m[1] ) } # All Cross Timbers lakes with quality A data ok_reservoir(ecoregion = "Cross Timbers", data_quality = "A")# Look up a single lake (partial match) ok_reservoir("Arcadia") # Exact match ok_reservoir("Arcadia Lake", exact = TRUE) # Use in pipeline res <- ok_reservoir("Arcadia Lake") if (nrow(res) > 0) { ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120) |> ok_hydraulics( surface_area_ha = res$surface_area_ha[1], mean_depth_m = res$mean_depth_m[1] ) } # All Cross Timbers lakes with quality A data ok_reservoir(ecoregion = "Cross Timbers", data_quality = "A")
Prints a summary of the ok_reservoirs dataset by ecoregion, showing
the number of lakes, surface area range, and data quality breakdown.
Useful for understanding dataset coverage before modelling.
ok_reservoir_summary()ok_reservoir_summary()
A data frame (invisibly) summarising coverage by ecoregion.
A data frame containing morphometric and geographic characteristics for major reservoirs in Oklahoma, compiled from publicly available sources to enable rapid setup of okBATHTUB pipelines without manual morphometric lookup.
ok_reservoirsok_reservoirs
A data frame with one row per reservoir and the following columns:
Character. Canonical lake name.
Character. Common alternate name, if any.
Character. Primary Oklahoma county.
Character. Primary managing agency.
Character. Primary designated use.
Numeric. Normal pool surface area (hectares).
Numeric. Mean depth at normal pool (metres).
Numeric. Maximum depth at normal pool (metres).
Numeric. Total storage at normal pool (m^3).
Numeric. Contributing watershed area (km^2).
Character. EPA Level III ecoregion code.
Character. EPA Level III ecoregion name.
Numeric. Approximate dam latitude (WGS84 decimal degrees).
Numeric. Approximate dam longitude (WGS84 decimal degrees).
Integer. Year dam was completed.
Character. Data quality code:
"A" = direct from authoritative source (USACE design memoranda,
published bathymetric surveys, or National Inventory of Dams);
"B" = mean depth estimated from Oklahoma regional regression.
Character. Data source or caveat.
Mean depth is the most critical morphometric parameter for okBATHTUB
modelling (it drives hydraulic residence time). For reservoirs coded
"B", mean depth was estimated using an Oklahoma regional log-log
regression fitted to reservoirs with known bathymetry:
This regression has substantial residual scatter; the resulting depth
carries roughly a factor-of-1.5 prediction interval. Users with access
to authoritative bathymetric data for specific reservoirs are
encouraged to supply those values directly to ok_hydraulics() rather
than relying on the estimated values here.
Compiled from publicly available sources including U.S. Army Corps of Engineers (USACE) Tulsa District design memoranda, the National Inventory of Dams (NID), U.S. Bureau of Reclamation (BOR) design data, and published Oklahoma Water Resources Board reports. This dataset is provided as a convenience starting point and should be verified against the most current authoritative source for any decision-relevant application.
# View all reservoirs head(ok_reservoirs) # Filter to a specific ecoregion ok_reservoirs[ok_reservoirs$eco_l3_name == "Cross Timbers", ]# View all reservoirs head(ok_reservoirs) # Filter to a specific ecoregion ok_reservoirs[ok_reservoirs$eco_l3_name == "Cross Timbers", ]
ok_retention() computes the fraction of incoming total phosphorus
(TP) and total nitrogen (TN) that is retained within the reservoir.
The retention coefficient is defined as
and is applied in ok_inlake() to predict in-lake nutrient
concentrations.
Three retention model families are supported, selected via the
coefficients argument to ok_load():
"walker_model1" (Walker BATHTUB Model 1, default)Second-order available-phosphorus sedimentation (Walker 1985, 1996). The mass balance solution is
where for TP and
for TN, with
.
"vollenweider" (Vollenweider 1976 / Larsen-Mercier 1976)First-order hydraulic-residence model:
Mathematically equivalent to Walker BATHTUB Model 5 (Northern Lakes). Single parameter (residence time), no ortho-P data required. Walker (1996) notes this form is not calibrated to Corps of Engineers reservoir data.
"settling_velocity" (used as TN companion to vollenweider)
where is an apparent settling velocity (m/yr).
ok_retention(x, tp_retention_override = NULL, tn_retention_override = NULL)ok_retention(x, tp_retention_override = NULL, tn_retention_override = NULL)
x |
An |
tp_retention_override |
Numeric between 0 and 1. If supplied,
bypasses the equation and uses this value directly. Useful when
observed retention is available from paired inflow/outflow monitoring.
Default |
tn_retention_override |
Numeric between 0 and 1. Same as above for
TN. Default |
An okBATHTUB object at pipeline step "retention" with the
following fields added to $data:
tp_retention_coeffTP retention coefficient (0-1).
tn_retention_coeffTN retention coefficient (0-1), or
NULL if TN was not supplied.
tp_retention_form, tn_retention_form
Character. Which retention equation was applied.
result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800 ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() print(result) # Vollenweider / Larsen-Mercier retention result_v <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "vollenweider" ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() # Observed retention coefficient override result_obs <- ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention(tp_retention_override = 0.42)result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800 ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() print(result) # Vollenweider / Larsen-Mercier retention result_v <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, coefficients = "vollenweider" ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() # Observed retention coefficient override result_obs <- ok_load(inflow_m3yr = 45e6, tp_inflow_ugl = 120) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention(tp_retention_override = 0.42)
ok_scenario() takes a baseline ok_load() result and runs
the full pipeline under one or more alternative loading scenarios, returning
a tidy comparison table of predicted water quality across all scenarios.
This is the primary tool for nutrient management planning - answering
"how much does TP need to be reduced to move this lake from eutrophic to
mesotrophic?"
ok_scenario( baseline, scenarios, include_baseline = TRUE, target_tsi = NULL, target_class = NULL )ok_scenario( baseline, scenarios, include_baseline = TRUE, target_tsi = NULL, target_class = NULL )
baseline |
An |
scenarios |
A list of named lists, one per scenario. Each list must
have a |
include_baseline |
Logical. Whether to include the baseline run as
the first row in the output. Default |
target_tsi |
Numeric. Optional TSI target. If supplied, a
|
target_class |
Character. One of |
A data frame with one row per scenario (plus baseline if requested).
Columns include: scenario (label), tp_inflow_ugl (inflow TP
in ug/L), tp_reduction_pct (reduction from baseline in percent),
tp_inlake_ugl (predicted in-lake TP), chla_ugl (predicted
chlorophyll-a), secchi_m (predicted Secchi depth), tsi_tp,
tsi_chla, tsi_mean (Carlson TSI values), trophic_state
(classification), and optionally meets_target (logical, present when
target_tsi is supplied).
Each scenario modifies one or more inflow parameters relative to the
baseline. Reductions are expressed as fractions (0-1), where
tp_reduction = 0.30 means a 30% reduction in inflow TP load.
Scenarios can also specify absolute concentrations directly via
tp_inflow_ugl, which overrides the reduction fraction.
The morphometric parameters (surface area, mean depth) from the baseline
ok_hydraulics() call are applied to every scenario.
# Baseline: Arcadia Lake with estimated inflow loading baseline <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800, segment_label = "lacustrine", coefficients = "oklahoma", ecoregion = "Cross Timbers" ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) # Scenario analysis: what TP reduction gets us to mesotrophic? scenarios <- list( list(label = "10% TP reduction", tp_reduction = 0.10), list(label = "20% TP reduction", tp_reduction = 0.20), list(label = "30% TP reduction", tp_reduction = 0.30), list(label = "40% TP reduction", tp_reduction = 0.40), list(label = "50% TP reduction", tp_reduction = 0.50) ) results <- ok_scenario( baseline = baseline, scenarios = scenarios, target_class = "mesotrophic" ) print(results)# Baseline: Arcadia Lake with estimated inflow loading baseline <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800, segment_label = "lacustrine", coefficients = "oklahoma", ecoregion = "Cross Timbers" ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) # Scenario analysis: what TP reduction gets us to mesotrophic? scenarios <- list( list(label = "10% TP reduction", tp_reduction = 0.10), list(label = "20% TP reduction", tp_reduction = 0.20), list(label = "30% TP reduction", tp_reduction = 0.30), list(label = "40% TP reduction", tp_reduction = 0.40), list(label = "50% TP reduction", tp_reduction = 0.50) ) results <- ok_scenario( baseline = baseline, scenarios = scenarios, target_class = "mesotrophic" ) print(results)
A convenience wrapper around ok_scenario() that automatically
generates a sequence of TP reduction scenarios from 0 to max_reduction_pct percent in steps of step_pct percent. Useful for
generating load-response curves and finding the minimum reduction needed
to achieve a trophic state target.
ok_scenario_sweep( baseline, max_reduction_pct = 70, step_pct = 5, target_tsi = NULL, target_class = NULL )ok_scenario_sweep( baseline, max_reduction_pct = 70, step_pct = 5, target_tsi = NULL, target_class = NULL )
baseline |
An |
max_reduction_pct |
Numeric. Maximum TP reduction to evaluate (percent). Default 70. |
step_pct |
Numeric. Step size between scenarios (percent). Default 5. |
target_tsi |
Numeric. Optional TSI target passed to
|
target_class |
Character. Optional trophic class target
( |
A data frame as returned by ok_scenario(), with one row
per reduction step plus the baseline.
baseline <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, ecoregion = "Cross Timbers", coefficients = "oklahoma" ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) sweep <- ok_scenario_sweep(baseline, target_class = "mesotrophic") print(sweep[, c("scenario", "tp_inflow_ugl", "tsi_mean", "trophic_state", "meets_target")])baseline <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, ecoregion = "Cross Timbers", coefficients = "oklahoma" ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) sweep <- ok_scenario_sweep(baseline, target_class = "mesotrophic") print(sweep[, c("scenario", "tp_inflow_ugl", "tsi_mean", "trophic_state", "meets_target")])
ok_segment() links two reservoir segments in series, passing the
outflow water quality of an upstream segment as the inflow to the next
downstream segment. This reflects the longitudinal zonation common in
Oklahoma reservoirs - riverine, transitional, and lacustrine segments each
behave differently and should be modelled separately when data support it.
The function takes a completed upstream segment result (through at least
ok_inlake()) and returns a new okBATHTUB object at the
"load" step, pre-populated with the upstream outflow concentrations
as the downstream inflow inputs. The downstream segment can then be run
through the full pipeline normally.
ok_segment( upstream, segment_label = "downstream", coefficients = NULL, ecoregion = NULL )ok_segment( upstream, segment_label = "downstream", coefficients = NULL, ecoregion = NULL )
upstream |
An |
segment_label |
Character. Label for the downstream segment.
Default |
coefficients |
Coefficient set for the downstream segment. Defaults to the same coefficient set used in the upstream segment. Can be overridden to apply different ecoregion coefficients to different segments. |
ecoregion |
Character. EPA Level III ecoregion for the downstream
segment. If |
An okBATHTUB object at pipeline step "load",
ready to pipe into ok_hydraulics() for the downstream segment.
The outflow TP concentration from the upstream segment becomes the inflow TP concentration for the downstream segment:
Inflow volume is passed through unchanged under the steady-state
assumption. If the downstream segment has a different surface area or
morphometry, supply those via ok_hydraulics() after this call.
# Two-segment reservoir: riverine -> lacustrine riverine <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 150, tn_inflow_ugl = 2200, segment_label = "riverine" ) |> ok_hydraulics(surface_area_ha = 280, mean_depth_m = 3.1) |> ok_retention() |> ok_inlake() lacustrine <- ok_segment(riverine, segment_label = "lacustrine") |> ok_hydraulics(surface_area_ha = 610, mean_depth_m = 5.8) |> ok_retention() |> ok_inlake() |> ok_tsi() summary(lacustrine)# Two-segment reservoir: riverine -> lacustrine riverine <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 150, tn_inflow_ugl = 2200, segment_label = "riverine" ) |> ok_hydraulics(surface_area_ha = 280, mean_depth_m = 3.1) |> ok_retention() |> ok_inlake() lacustrine <- ok_segment(riverine, segment_label = "lacustrine") |> ok_hydraulics(surface_area_ha = 610, mean_depth_m = 5.8) |> ok_retention() |> ok_inlake() |> ok_tsi() summary(lacustrine)
A convenience wrapper around ok_segment() for reservoirs with more
than two segments. Accepts a list of segment morphometry specifications and
runs them sequentially, passing each segment's outflow into the next.
ok_segment_chain( inflow_m3yr, tp_inflow_ugl, tn_inflow_ugl = NULL, segments, coefficients = "walker", ecoregion = NULL )ok_segment_chain( inflow_m3yr, tp_inflow_ugl, tn_inflow_ugl = NULL, segments, coefficients = "walker", ecoregion = NULL )
inflow_m3yr |
Numeric. Total annual inflow (m |
tp_inflow_ugl |
Numeric. Inflow TP (ug/L). |
tn_inflow_ugl |
Numeric. Inflow TN (ug/L). Optional. |
segments |
A list of named lists, one per segment, each containing:
|
coefficients |
Coefficient set applied to all segments.
Default |
ecoregion |
EPA Level III ecoregion. Applied to all segments. |
A named list of okBATHTUB objects at step "tsi",
one per segment, in downstream order. Names match the label
field of each segment specification.
segments <- list( list(label = "riverine", surface_area_ha = 280, mean_depth_m = 3.1), list(label = "transitional", surface_area_ha = 410, mean_depth_m = 4.5), list(label = "lacustrine", surface_area_ha = 610, mean_depth_m = 5.8) ) results <- ok_segment_chain( inflow_m3yr = 45e6, tp_inflow_ugl = 150, tn_inflow_ugl = 2200, segments = segments ) # View trophic state of each segment lapply(results, function(r) r$data$trophic_state)segments <- list( list(label = "riverine", surface_area_ha = 280, mean_depth_m = 3.1), list(label = "transitional", surface_area_ha = 410, mean_depth_m = 4.5), list(label = "lacustrine", surface_area_ha = 610, mean_depth_m = 5.8) ) results <- ok_segment_chain( inflow_m3yr = 45e6, tp_inflow_ugl = 150, tn_inflow_ugl = 2200, segments = segments ) # View trophic state of each segment lapply(results, function(r) r$data$trophic_state)
Converts the list output of ok_segment_chain() into a tidy data
frame with one row per segment, suitable for plotting or reporting.
ok_segment_summary(chain_result)ok_segment_summary(chain_result)
chain_result |
Named list of |
A data frame with one row per segment containing key water quality predictions and TSI values.
A clean, minimal ggplot2 theme used consistently across all okBATHTUB
visualization functions. Based on theme_minimal() with clean
typography and grid settings.
ok_theme(base_size = 11, legend_position = "bottom")ok_theme(base_size = 11, legend_position = "bottom")
base_size |
Numeric. Base font size. Default 11. |
legend_position |
Character. Legend position. Default |
A ggplot2::theme object.
Named color vector for trophic state classifications used consistently across all okBATHTUB plots.
ok_trophic_colors()ok_trophic_colors()
Named character vector of hex colors.
ok_tsi() computes Carlson (1977) Trophic State Index (TSI) values
from in-lake water quality predictions and assigns an overall trophic
state classification.
ok_tsi( x, observed_tp_ugl = NULL, observed_chla_ugl = NULL, observed_secchi_m = NULL )ok_tsi( x, observed_tp_ugl = NULL, observed_chla_ugl = NULL, observed_secchi_m = NULL )
x |
An |
observed_tp_ugl |
Numeric. If supplied, computes TSI(TP) from
this observed value instead of the model-predicted in-lake TP.
Default |
observed_chla_ugl |
Numeric. Observed chlorophyll-a (ug/L) to use
instead of predicted. Default |
observed_secchi_m |
Numeric. Observed Secchi depth (m) to use
instead of predicted. Default |
An okBATHTUB object at pipeline step "tsi".
where TP is in ug/L, chlorophyll-a is in ug/L, and Secchi depth is in metres. The Chl-a coefficient 9.81 matches the original Carlson (1977) paper; Walker's BATHTUB documentation uses 9.84 (likely a rounding artifact). The package uses 9.81, consistent with the primary limnological literature.
Based on the mean TSI across available indices:
TSI < 40 -> Oligotrophic
40 <= TSI < 50 -> Mesotrophic
50 <= TSI < 70 -> Eutrophic
TSI >= 70 -> Hypereutrophic
When only one or two of the three TSI components are available
(e.g. because Secchi depth could not be predicted), tsi_mean is the
arithmetic mean of the available components and tsi_n reports how
many were used. Carlson's deviation analysis assumes all three are
available; interpret tsi_mean with caution when tsi_n < 3.
Carlson, R.E. (1977). A trophic state index for lakes. Limnology and Oceanography, 22(2), 361-369.
ok_inlake(), ok_tsi_observed()
result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800 ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() |> ok_inlake() |> ok_tsi() summary(result)result <- ok_load( inflow_m3yr = 45e6, tp_inflow_ugl = 120, tn_inflow_ugl = 1800 ) |> ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |> ok_retention() |> ok_inlake() |> ok_tsi() summary(result)
A standalone helper for computing Carlson TSI directly from observed water quality measurements, without running the full prediction pipeline. Useful for computing observed trophic state from grab sample data for comparison against modelled predictions.
ok_tsi_observed(tp_ugl = NULL, chla_ugl = NULL, secchi_m = NULL)ok_tsi_observed(tp_ugl = NULL, chla_ugl = NULL, secchi_m = NULL)
tp_ugl |
Numeric. Observed in-lake total phosphorus (ug/L). Optional. |
chla_ugl |
Numeric. Observed chlorophyll-a (ug/L). Optional. |
secchi_m |
Numeric. Observed Secchi depth (m). Optional. |
A named list with elements tsi_tp, tsi_chla, tsi_secchi,
tsi_n, tsi_mean, and trophic_state.
Carlson, R.E. (1977). A trophic state index for lakes. Limnology and Oceanography, 22(2), 361-369.
ok_tsi_observed(tp_ugl = 85, chla_ugl = 22, secchi_m = 0.8)ok_tsi_observed(tp_ugl = 85, chla_ugl = 22, secchi_m = 0.8)
Prints a compact summary of an okBATHTUB pipeline result to the
console, including the pipeline step, segment label (if any), the
coefficient set in use, and a list of single-value numeric or
character fields stored in the result.
## S3 method for class 'okBATHTUB' print(x, ...)## S3 method for class 'okBATHTUB' print(x, ...)
x |
An |
... |
Ignored. |
The okBATHTUB object x, returned invisibly. Called
primarily for the side effect of printing a formatted summary to
the console via cat().
Prints a formatted summary of all water quality predictions accumulated
through the pipeline. Most informative after ok_tsi() has been run.
## S3 method for class 'okBATHTUB' summary(object, ...)## S3 method for class 'okBATHTUB' summary(object, ...)
object |
An |
... |
Ignored. |
The okBATHTUB object object, returned invisibly. Called
primarily for the side effect of printing a multi-line formatted
water quality summary to the console. The summary block includes
segment metadata, hydraulic and load fields, in-lake water quality
predictions, and Carlson Trophic State Indices, depending on which
pipeline steps have been completed on the input object.