
Unsaturated hydraulic conductivity with hydraulic_conductivity()
Source:vignettes/hydraulic-conductivity.Rmd
hydraulic-conductivity.RmdOverview
hydraulic_conductivity() computes unsaturated hydraulic
conductivity K(h) using the Mualem (1976) – Van Genuchten (1980) model.
The effective saturation S_e is first computed from the Van Genuchten
retention parameters:
Then K(h) is:
where m = 1 − 1/n (the Mualem constraint). Note that m
must be supplied explicitly to hydraulic_conductivity() —
it is not inferred from n.
1. Parameters
| Parameter | Symbol | Units | Notes |
|---|---|---|---|
ks |
K_s | cm/day (or any rate) | Saturated hydraulic conductivity |
alpha |
α | 1/cm | Same unit system as h
|
n |
n | – | Must be > 1 |
m |
m | – | Typically 1 - 1/n; must satisfy 0 < m < 1 |
h |
h | cm | Absolute value used internally |
2. Single-soil K(h) curve
# Loam soil
loam <- tibble(h = c(0, 10, 33, 100, 330, 1000, 3300, 10000, 15000))
loam_kh <- hydraulic_conductivity(loam,
ks = 10.4, # cm/day (Carsel & Parrish 1988)
alpha = 0.036,
n = 1.56,
m = 1 - 1 / 1.56,
h = h
)
loam_kh
#> # A tibble: 9 × 2
#> h .K
#> <dbl> <dbl>
#> 1 0 1.04e+ 1
#> 2 10 2.24e+ 0
#> 3 33 3.04e- 1
#> 4 100 1.41e- 2
#> 5 330 2.88e- 4
#> 6 1000 6.81e- 6
#> 7 3300 1.18e- 7
#> 8 10000 2.73e- 9
#> 9 15000 6.87e-10
ggplot(loam_kh, aes(x = h, y = .K)) +
geom_line(linewidth = 1) +
geom_point(size = 2.5) +
scale_x_log10(labels = scales::label_comma()) +
scale_y_log10(labels = scales::label_scientific()) +
labs(
x = "Matric potential |h| (cm)",
y = "K(h) (cm/day)",
title = "Unsaturated hydraulic conductivity — loam"
)
#> Warning in scale_x_log10(labels = scales::label_comma()): log-10 transformation introduced infinite values.
#> log-10 transformation introduced infinite values.
3. Comparing texture classes
Standard Carsel & Parrish (1988) parameters. K_sat values span several orders of magnitude from clay to sand.
textures <- tribble(
~texture, ~ks, ~alpha, ~n,
"sand", 712.8, 0.145, 2.68,
"sandy loam", 106.1, 0.075, 1.89,
"loam", 10.4, 0.036, 1.56,
"clay loam", 2.2, 0.019, 1.31,
"clay", 4.8, 0.015, 1.09
)
heads <- 10^seq(0, 5, by = 0.1)
kh_curves <- textures |>
mutate(m = 1 - 1 / n) |>
tidyr::crossing(h = heads) |>
hydraulic_conductivity(ks = ks, alpha = alpha, n = n, m = m, h = h)
ggplot(kh_curves, aes(x = h, y = .K, colour = texture)) +
geom_line(linewidth = 0.9) +
scale_x_log10(labels = scales::label_comma()) +
scale_y_log10(labels = scales::label_scientific()) +
labs(
x = "Matric potential |h| (cm)",
y = "K(h) (cm/day)",
colour = "Texture",
title = "Unsaturated hydraulic conductivity by texture class"
)
4. K(h) in context: paired SWRC and K(h) plots
K(h) and the SWRC are derived from the same parameter set. Viewing them together reveals the relationship between water storage and flow capacity.
loam_combined <- tibble(h = heads) |>
mutate(m = 1 - 1 / 1.56) |>
swrc_van_genuchten(theta_r = 0.078, theta_s = 0.430,
alpha = 0.036, n = 1.56, h = h) |>
hydraulic_conductivity(ks = 10.4, alpha = 0.036,
n = 1.56, m = m, h = h)
p_theta <- ggplot(loam_combined, aes(x = h, y = .theta)) +
geom_line(linewidth = 1, colour = "steelblue") +
scale_x_log10(labels = scales::label_comma()) +
labs(x = NULL, y = expression(theta~(m^3/m^3)),
title = "Loam: SWRC (top) and K(h) (bottom)")
p_k <- ggplot(loam_combined, aes(x = h, y = .K)) +
geom_line(linewidth = 1, colour = "darkorange") +
scale_x_log10(labels = scales::label_comma()) +
scale_y_log10(labels = scales::label_scientific()) +
labs(x = "|h| (cm)", y = "K(h) (cm/day)")
# Stack with patchwork if available, otherwise print separately
if (requireNamespace("patchwork", quietly = TRUE)) {
library(patchwork)
p_theta / p_k
} else {
print(p_theta)
print(p_k)
}
5. K_sat comparison table
textures |>
mutate(m = 1 - 1 / n) |>
arrange(desc(ks)) |>
select(texture, ks, alpha, n) |>
rename(`K_sat (cm/day)` = ks,
`α (1/cm)` = alpha)
#> # A tibble: 5 × 4
#> texture `K_sat (cm/day)` `α (1/cm)` n
#> <chr> <dbl> <dbl> <dbl>
#> 1 sand 713. 0.145 2.68
#> 2 sandy loam 106. 0.075 1.89
#> 3 loam 10.4 0.036 1.56
#> 4 clay 4.8 0.015 1.09
#> 5 clay loam 2.2 0.019 1.31Note that K_sat for sand is about 150× higher than for loam, and ~650× higher than for clay loam. Even a small change in texture classification can represent a large hydraulic contrast.
6. Tidy evaluation: columns or scalars
Like swrc_van_genuchten(), all parameters accept bare
column names or scalars.
# Vary K_sat and alpha across rows; scalar n and m
tibble(
site = c("ridge", "midslope", "valley"),
ks = c(150, 30, 5),
alpha = c(0.10, 0.04, 0.015)
) |>
tidyr::crossing(h = c(10, 100, 1000)) |>
hydraulic_conductivity(ks = ks, alpha = alpha,
n = 1.6, m = 1 - 1/1.6, h = h) |>
select(site, h, alpha, ks, .K)
#> # A tibble: 9 × 5
#> site h alpha ks .K
#> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 midslope 10 0.04 30 6.27
#> 2 midslope 100 0.04 30 0.0280
#> 3 midslope 1000 0.04 30 0.0000104
#> 4 ridge 10 0.1 150 6.90
#> 5 ridge 100 0.1 150 0.00642
#> 6 ridge 1000 0.1 150 0.00000211
#> 7 valley 10 0.015 5 2.33
#> 8 valley 100 0.015 5 0.0871
#> 9 valley 1000 0.015 5 0.00005277. Performance
df_1m <- tibble(h = rep(c(33, 330, 15000), length.out = 1e6))
elapsed <- system.time(
hydraulic_conductivity(df_1m, ks = 10, alpha = 0.036,
n = 1.56, m = 1 - 1/1.56, h = h)
)["elapsed"]
cat(sprintf("1 M rows in %.0f ms\n", elapsed * 1000))
#> 1 M rows in 102 msSee also
-
vignette("soil-water-retention")— the SWRC function that shares these parameters -
vignette("fitting-diagnostics")— fit parameters from observed (h, θ) data -
vignette("raster-workflow")— map K(h) across a spatial grid