The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
okBATHTUB is an R implementation of steady-state
empirical reservoir eutrophication modelling. It supports three
retention model families:
The package predicts in-lake total phosphorus, total nitrogen, chlorophyll-a, and Secchi depth, and computes Carlson (1977) Trophic State Indices. It is designed to complement watershed loading models such as SWAT or HAWQS in a two-model nutrient management workflow.
The standard single-segment workflow chains five functions with the base pipe operator:
ok_load() |> ok_hydraulics() |> ok_retention() |> ok_inlake() |> ok_tsi()
ok_load() accepts tributary inflow volume and
flow-weighted nutrient concentrations:
result <- ok_load(
inflow_m3yr = 45e6, # tributary inflow (m^3/yr)
tp_inflow_ugl = 120, # flow-weighted mean TP (ug/L)
tn_inflow_ugl = 1800 # flow-weighted mean TN (ug/L)
)
print(result)
#> -- okBATHTUB Result --
#> Pipeline step : load
#> Segment : main
#> Coefficients : walker
#>
#> inflow_m3yr : 4.5e+07
#> tp_inflow_ugl : 120
#> tp_load_kgyr : 5400
#> tn_inflow_ugl : 1800
#> tn_load_kgyr : 8.1e+04ok_hydraulics() adds reservoir morphometry and computes
residence time and areal water load:
result <- result |>
ok_hydraulics(
surface_area_ha = 890,
mean_depth_m = 4.2
)
cat("Hydraulic residence time:", round(result$data$hydraulic_residence_time_yr, 2), "yr\n")
#> Hydraulic residence time: 0.83 yr
cat("Areal water load (qs): ", round(result$data$areal_water_load_myr, 2), "m/yr\n")
#> Areal water load (qs): 5.06 m/yrok_retention() estimates the fraction of incoming TP and
TN retained in the reservoir. The default uses Walker BATHTUB Model 1
(second-order available-P sedimentation):
\[P = \frac{-1 + \sqrt{1 + 4 K A_1 P_i \tau}}{2 K A_1 \tau}\]
where \(A_1 = 0.17 \cdot Q_s/(Q_s + 13.3)\) and \(Q_s = \max(Z/\tau, 4)\) m/yr. The retention coefficient stored on the object is back-calculated from this solution so that the downstream mass balance \(C_{lake} = C_{in}(1 - R)\) exactly reproduces the Model 1 result.
result <- result |> ok_retention()
cat("TP retention coefficient:", round(result$data$tp_retention_coeff, 3),
"(", result$data$tp_retention_form, ")\n")
#> TP retention coefficient: 0.632 ( walker_model1 )
cat("TN retention coefficient:", round(result$data$tn_retention_coeff, 3), "\n")
#> TN retention coefficient: 0.553To use the simpler Vollenweider / Larsen-Mercier form instead, pass
coefficients = "vollenweider" to
ok_load().
ok_inlake() applies the mass balance and the
chlorophyll-a / Secchi regressions:
result <- result |> ok_inlake()
cat("In-lake TP: ", round(result$data$tp_inlake_ugl, 1), "ug/L\n")
#> In-lake TP: 44.2 ug/L
cat("Chlorophyll-a: ", round(result$data$chla_ugl, 2), "ug/L\n")
#> Chlorophyll-a: 17.68 ug/L
cat("Secchi depth: ", round(result$data$secchi_m, 2), "m\n")
#> Secchi depth: 1.06 mok_tsi() computes Carlson (1977) TSI from all predicted
variables:
result <- result |> ok_tsi()
summary(result)
#> ========================================
#> okBATHTUB Water Quality Summary
#> ========================================
#>
#> Segment : main
#> Coefficients : walker
#> Pipeline : tsi
#>
#> -- Hydraulics --
#> Inflow : 4.500e+07 m3/yr
#> Surface area : 890.0 ha
#> Mean depth : 4.20 m
#> Residence time : 0.831 yr
#> Areal water load : 5.06 m/yr
#>
#> -- Nutrient Retention --
#> TP retention : 0.632 (walker_model1)
#> TN retention : 0.553 (walker_model1)
#>
#> -- In-Lake Predictions --
#> TP : 44.2 ug/L
#> TN : 803.8 ug/L
#> Chlorophyll-a : 17.68 ug/L
#> Secchi depth : 1.06 m
#>
#> -- Carlson Trophic State Index --
#> TSI(TP) : 58.8
#> TSI(Chl-a) : 58.8
#> TSI(Secchi) : 59.1
#> TSI(mean) : 58.9 (n = 3 components)
#> Trophic state : Eutrophic
#>
#> ========================================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)
#> ========================================
#> okBATHTUB Water Quality Summary
#> ========================================
#>
#> Segment : main
#> Coefficients : walker
#> Pipeline : tsi
#>
#> -- Hydraulics --
#> Inflow : 4.500e+07 m3/yr
#> Surface area : 890.0 ha
#> Mean depth : 4.20 m
#> Residence time : 0.831 yr
#> Areal water load : 5.06 m/yr
#>
#> -- Nutrient Retention --
#> TP retention : 0.632 (walker_model1)
#> TN retention : 0.553 (walker_model1)
#>
#> -- In-Lake Predictions --
#> TP : 44.2 ug/L
#> TN : 803.8 ug/L
#> Chlorophyll-a : 17.68 ug/L
#> Secchi depth : 1.06 m
#>
#> -- Carlson Trophic State Index --
#> TSI(TP) : 58.8
#> TSI(Chl-a) : 58.8
#> TSI(Secchi) : 59.1
#> TSI(mean) : 58.9 (n = 3 components)
#> Trophic state : Eutrophic
#>
#> ========================================The ok_reservoirs dataset contains morphometry for major
Oklahoma reservoirs compiled from publicly available USACE, BOR, NID,
and OWRB sources. Use ok_reservoir() for a quick
lookup:
res <- ok_reservoir("Arcadia Lake", exact = TRUE)
res[, c("lake_name", "surface_area_ha", "mean_depth_m", "eco_l3_name",
"data_quality")]
#> lake_name surface_area_ha mean_depth_m eco_l3_name data_quality
#> 1 Arcadia Lake 716 4.6 Cross Timbers Aresult <- ok_load(
inflow_m3yr = 45e6,
tp_inflow_ugl = 120,
tn_inflow_ugl = 1800
) |>
ok_hydraulics(
surface_area_ha = res$surface_area_ha[1],
mean_depth_m = res$mean_depth_m[1]
) |>
ok_retention() |>
ok_inlake() |>
ok_tsi()
cat("Trophic state:", result$data$trophic_state, "\n")
#> Trophic state: Eutrophic
cat("Mean TSI: ", round(result$data$tsi_mean, 1), "\n")
#> Mean TSI: 58.8For a dataset overview by ecoregion:
ok_reservoir_summary()
#> ========================================
#> okBATHTUB - ok_reservoirs Coverage
#> ========================================
#>
#> Total lakes: 40
#> Quality A (authoritative source): 39
#> Quality B (depth estimated): 1
#>
#> eco_l3_name n_lakes n_quality_a n_quality_b area_min_ha
#> Cross Timbers 14 14 0 440
#> Ozark Highlands 7 7 0 440
#> Arkansas Valley 6 5 1 212
#> Central Oklahoma/Texas Plains 6 6 0 720
#> Ouachita Mountains 6 6 0 1620
#> Southwestern Tablelands 1 1 0 2090
#> area_max_ha depth_min_m depth_max_m
#> 10570 2.4 11.5
#> 18700 3.8 11.3
#> 41280 3.4 7.0
#> 35840 4.3 11.6
#> 5710 4.4 15.5
#> 2090 3.5 3.5When more than one tributary contributes to the reservoir, use
ok_load_multi() to compute flow-weighted mean
concentrations automatically:
tributaries <- data.frame(
inflow_m3yr = c(30e6, 15e6),
tp_inflow_ugl = c(100, 160),
tn_inflow_ugl = c(1500, 2400)
)
result_multi <- ok_load_multi(tributaries) |>
ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |>
ok_retention() |>
ok_inlake() |>
ok_tsi()
cat("FW mean TP inflow:", round(result_multi$data$tp_inflow_ugl, 1), "ug/L\n")
#> FW mean TP inflow: 120 ug/Lok_tsi_observed() computes Carlson TSI directly from
monitoring data without running the full pipeline — useful for comparing
observed trophic state against modelled predictions:
obs <- ok_tsi_observed(
tp_ugl = 85,
chla_ugl = 22,
secchi_m = 0.8
)
cat("TSI(TP): ", round(obs$tsi_tp, 1), "\n")
#> TSI(TP): 68.2
cat("TSI(Chl-a): ", round(obs$tsi_chla, 1), "\n")
#> TSI(Chl-a): 60.9
cat("TSI(Secchi):", round(obs$tsi_secchi, 1), "\n")
#> TSI(Secchi): 63.2
cat("Mean TSI: ", round(obs$tsi_mean, 1), "\n")
#> Mean TSI: 64.1
cat("Class: ", obs$trophic_state, "\n")
#> Class: EutrophicA useful sanity check is to run the same reservoir under different coefficient assumptions and see how predictions vary:
run_pipeline <- function(coef, eco = NULL) {
ok_load(
inflow_m3yr = 45e6,
tp_inflow_ugl = 120,
tn_inflow_ugl = 1800,
coefficients = coef,
ecoregion = eco
) |>
ok_hydraulics(surface_area_ha = 890, mean_depth_m = 4.2) |>
ok_retention() |>
ok_inlake() |>
ok_tsi()
}
r_walker <- run_pipeline("walker")
r_voll <- run_pipeline("vollenweider")
r_okla_ct <- run_pipeline("oklahoma", "Cross Timbers")
comparison <- data.frame(
scenario = c("Walker Model 1",
"Vollenweider/Larsen-Mercier",
"Oklahoma (Cross Timbers)"),
tp_inlake = round(c(r_walker$data$tp_inlake_ugl,
r_voll$data$tp_inlake_ugl,
r_okla_ct$data$tp_inlake_ugl), 1),
chla = round(c(r_walker$data$chla_ugl,
r_voll$data$chla_ugl,
r_okla_ct$data$chla_ugl), 2),
tsi_mean = round(c(r_walker$data$tsi_mean,
r_voll$data$tsi_mean,
r_okla_ct$data$tsi_mean), 1)
)
print(comparison, row.names = FALSE)
#> scenario tp_inlake chla tsi_mean
#> Walker Model 1 44.2 17.68 58.9
#> Vollenweider/Larsen-Mercier 62.8 29.45 63.4
#> Oklahoma (Cross Timbers) 44.2 19.83 62.3A few observations to expect:
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.