Surface Runoff Model
Overview
This module implements a surface runoff model based on the Saint-Venant equation system for surface water movement, coupled with a Heaviside step function boundary condition for surface-subsurface water flow interaction.
The Saint-Venant equations describe the mass conservation and momentum conservation of surface water flow along a soil surface slope. The Heaviside boundary condition enables automatic switching between infiltration and evaporation conditions at the soil surface based on the soil water pressure head.
The model includes three key processes:
- Surface water flow along the soil surface (Saint-Venant equations)
- Friction effects via Manning's roughness equation
- Automatic surface-subsurface boundary condition switching (Heaviside function)
Reference: Wang, Z., Timlin, D., Kouznetsov, M., Fleisher, D., Li, S., Tully, K., & Reddy, V. (2020). Coupled model of surface runoff and surface-subsurface water movement. Advances in Water Resources, 137, 103499. https://doi.org/10.1016/j.advwatres.2019.103499
EnvironmentalTransport.SurfaceRunoff — Function
SurfaceRunoff(; name) -> ModelingToolkitBase.System
Create a surface runoff model based on the Saint-Venant equation system for surface water movement, following the approach of Wang et al. (2020).
The model describes mass conservation and momentum conservation of surface water flow along a soil surface slope. The Saint-Venant equations are:
\[\frac{\partial \tilde{h}}{\partial t} = -\frac{\partial q}{\partial l} + (P - I)\]
\[\frac{\partial q}{\partial t} = -\frac{\partial}{\partial l}\left(\frac{q^2}{\tilde{h}} + \frac{g \tilde{h}^2}{2}\right) + g \tilde{h} (S_0 - S_f)\]
where $\tilde{h}$ is the flow depth (ponded water height), $q$ is the surface runoff flux per unit width, $P$ is precipitation flux, $I$ is infiltration flux, $S_0$ is the surface slope, and $S_f$ is the friction slope computed from Manning's equation.
This component represents the equations at a single surface node. The spatial derivative terms ($\partial q / \partial l$ and the momentum flux divergence) are represented as input parameters that should be provided by a spatial discretization scheme or coupled model.
Reference: Wang, Z., Timlin, D., Kouznetsov, M., Fleisher, D., Li, S., Tully, K., & Reddy, V. (2020). Coupled model of surface runoff and surface-subsurface water movement. Advances in Water Resources, 137, 103499. https://doi.org/10.1016/j.advwatres.2019.103499
EnvironmentalTransport.HeavisideBoundaryCondition — Function
HeavisideBoundaryCondition(
;
name
) -> ModelingToolkitBase.System
Create a smoothed Heaviside boundary condition component for coupling surface and subsurface water flow, following Wang et al. (2020).
The Heaviside step function determines whether the soil surface is in a ponding state (h ≥ 0) or unsaturated state (h < 0), enabling automatic switching between infiltration and evaporation boundary conditions:
\[\eta_\omega(h) = \frac{1}{\pi} \arctan\left(\frac{h}{\omega}\right) + \frac{1}{2}\]
\[\delta_\omega(h) = \frac{1}{\pi} \frac{\omega}{\omega^2 + h^2}\]
These smooth approximations converge to the Heaviside step function and Dirac delta function respectively as $\omega \to 0^+$.
The boundary condition equation is (Eq. 3):
\[\frac{\partial(\eta(h) \cdot h)}{\partial t} - (P - I) = 0\]
Reference: Wang, Z., Timlin, D., Kouznetsov, M., Fleisher, D., Li, S., Tully, K., & Reddy, V. (2020). Coupled model of surface runoff and surface-subsurface water movement. Advances in Water Resources, 137, 103499. https://doi.org/10.1016/j.advwatres.2019.103499
EnvironmentalTransport.SaintVenantPDE — Function
SaintVenantPDE(
L_domain,
T_end;
P_val,
I_val,
S_0_val,
n_manning_val,
g_val,
h_min_val,
h_init_val,
q_init_val,
name
) -> ModelingToolkitBase.PDESystem
Create a PDESystem for the Saint-Venant equations (Eq. 1 from Wang et al., 2020) suitable for spatial discretization with MethodOfLines.jl.
The system implements the full 1D Saint-Venant equations with proper SI units:
- Mass conservation (Eq. 1a): $\partial \tilde{h}/\partial t = -\partial q/\partial l + (P - I)$
- Momentum conservation (Eq. 1b): $\partial q/\partial t = -\partial F/\partial l + g \tilde{h}(S_0 - S_f)$
- Momentum flux (auxiliary): $F = q^2/\tilde{h} + g \tilde{h}^2/2$
where $S_f$ is Manning's friction slope computed with non-dimensionalized fractional exponents: $S_f = ((n/n_{ref})(q/q_{ref}))^2 / (\tilde{h}/h_{ref})^{10/3}$.
Arguments
L_domain: Length of the spatial domain (m)T_end: Duration of the simulation (s)
Keyword Arguments
P_val: Precipitation rate (m/s), default 70 mm/hrI_val: Infiltration rate (m/s), default 0.0S_0_val: Surface slope (dimensionless), default 0.01n_manning_val: Manning roughness coefficient (m^(-1/3)·s), default 0.03g_val: Gravitational acceleration (m/s²), default 9.81h_min_val: Minimum flow depth to prevent singularity (m), default 1e-5h_init_val: Initial and boundary flow depth (m), default 1e-3q_init_val: Initial and boundary flux (m²/s), default 0.0name: System name, default:SaintVenantPDE
Reference: Wang, Z., Timlin, D., Kouznetsov, M., Fleisher, D., Li, S., Tully, K., & Reddy, V. (2020). Coupled model of surface runoff and surface-subsurface water movement. Advances in Water Resources, 137, 103499. https://doi.org/10.1016/j.advwatres.2019.103499
Soil Properties (Table 1)
The following table lists the soil physics properties and parameters for soil hydraulic models used in the numerical examples of Wang et al. (2020). Values are shown in the original paper units with SI equivalents.
| Parameter | Soil A | Soil B | SI Unit |
|---|---|---|---|
| Residual Water Content ($\theta_r$) | 0.02 | 0.02 | m$^3$ m$^{-3}$ |
| Saturated Water Content ($\theta_s$) | 0.33 | 0.52 | m$^3$ m$^{-3}$ |
| van Genuchten Parameter ($\alpha$) | 0.04 (cm$^{-1}$) = 4.0 (m$^{-1}$) | 0.03 (cm$^{-1}$) = 3.0 (m$^{-1}$) | m$^{-1}$ |
| van Genuchten Parameter ($n$) | 2.0 | 1.1 | dimensionless |
| Saturated Hydraulic Conductivity ($k_s$) | 62.4 cm day$^{-1}$ = 7.22$\times 10^{-6}$ m s$^{-1}$ | 1.0 cm day$^{-1}$ = 1.16$\times 10^{-7}$ m s$^{-1}$ | m s$^{-1}$ |
| Soil Bulk Density ($\rho_b$) | 1.51 g cm$^{-3}$ = 1510 kg m$^{-3}$ | 1.2 g cm$^{-3}$ = 1200 kg m$^{-3}$ | kg m$^{-3}$ |
| Mass Fraction of Soil Organic Matter | 0.04 | 0.04 | kg kg$^{-1}$ |
| Mass Fraction of Sand | 0.95 | 0.25 | kg kg$^{-1}$ |
| Mass Fraction of Silt | 0.04 | 0.15 | kg kg$^{-1}$ |
Implementation
SurfaceRunoff Component
The SurfaceRunoff component implements the Saint-Venant equation system (Eq. 1 from Wang et al., 2020) at a single surface computing node.
State Variables
using DataFrames, ModelingToolkit, Symbolics, DynamicQuantities
using EnvironmentalTransport
sys = SurfaceRunoff()
vars = unknowns(sys)
DataFrame(
:Name => [string(Symbolics.tosymbol(v, escape = false)) for v in vars],
:Units => [string(dimension(ModelingToolkit.get_unit(v))) for v in vars],
:Description => [ModelingToolkit.getdescription(v) for v in vars]
)| Row | Name | Units | Description |
|---|---|---|---|
| String | String | String | |
| 1 | h̃ | m | Flow depth / ponded water height (Eq. 1) |
| 2 | q | m² s⁻¹ | Surface runoff flux per unit width (Eq. 1) |
| 3 | S_f | Friction slope from Manning's equation (Eq. 1) |
Parameters
params = parameters(sys)
DataFrame(
:Name => [string(Symbolics.tosymbol(p, escape = false)) for p in params],
:Units => [try string(dimension(ModelingToolkit.get_unit(p))) catch; "dimensionless" end for p in params],
:Description => [ModelingToolkit.getdescription(p) for p in params]
)| Row | Name | Units | Description |
|---|---|---|---|
| String | String | String | |
| 1 | dqdl | m s⁻¹ | Spatial derivative of runoff flux ∂q/∂l (Eq. 1) |
| 2 | I_infil | m s⁻¹ | Infiltration flux density (Eq. 1) |
| 3 | P | m s⁻¹ | Precipitation/irrigation flux density (Eq. 1) |
| 4 | dFdl | m² s⁻² | Spatial derivative of momentum flux ∂/∂l(q²/h̃ + g·h̃²/2) (Eq. 1) |
| 5 | S_0 | Surface slope (dimensionless) | |
| 6 | h̃_0 | m | Minimum flow depth to prevent singularity (Eq. 1) |
| 7 | g | m s⁻² | Gravitational acceleration |
| 8 | n_mann | m⁻¹ᐟ³ s | Manning roughness coefficient |
| 9 | q_ref | m² s⁻¹ | Reference flux for non-dimensionalization |
| 10 | n_ref | m⁻¹ᐟ³ s | Reference Manning coefficient for non-dimensionalization |
| 11 | h_ref | m | Reference flow depth for non-dimensionalization |
Equations
eqs = equations(sys)\[ \begin{align} \frac{\mathrm{d} ~ \mathtt{\tilde{h}}\left( t \right)}{\mathrm{d}t} &= - \mathtt{I\_infil} + P - \mathtt{dqdl} \\ \frac{\mathrm{d} ~ q\left( t \right)}{\mathrm{d}t} &= - \mathtt{dFdl} + \left( \mathtt{S\_0} - \mathtt{S\_f}\left( t \right) \right) ~ g ~ max\left( \mathtt{\tilde{h}}\left( t \right), \mathtt{\tilde{h}\_0} \right) \\ \mathtt{S\_f}\left( t \right) &= \frac{\left( \frac{\mathtt{n\_mann} ~ q\left( t \right)}{\mathtt{n\_ref} ~ \mathtt{q\_ref}} \right)^{2}}{\left( \frac{max\left( \mathtt{\tilde{h}}\left( t \right), \mathtt{\tilde{h}\_0} \right)}{\mathtt{h\_ref}} \right)^{3.3333}} \end{align} \]
HeavisideBoundaryCondition Component
The HeavisideBoundaryCondition component implements the smoothed Heaviside step function (Eq. 5) and the surface boundary condition (Eq. 3) for coupling surface and subsurface water flow.
State Variables
hbc = HeavisideBoundaryCondition()
vars_hbc = unknowns(hbc)
DataFrame(
:Name => [string(Symbolics.tosymbol(v, escape = false)) for v in vars_hbc],
:Units => [try string(dimension(ModelingToolkit.get_unit(v))) catch; "dimensionless" end for v in vars_hbc],
:Description => [ModelingToolkit.getdescription(v) for v in vars_hbc]
)| Row | Name | Units | Description |
|---|---|---|---|
| String | String | String | |
| 1 | h | m | Soil water pressure head at surface (Eq. 3) |
| 2 | η_ω | Smoothed Heaviside step function (Eq. 5) (dimensionless) | |
| 3 | δ_ω | m⁻¹ | Smoothed Dirac delta function (Eq. 5) |
Parameters
params_hbc = parameters(hbc)
DataFrame(
:Name => [string(Symbolics.tosymbol(p, escape = false)) for p in params_hbc],
:Units => [try string(dimension(ModelingToolkit.get_unit(p))) catch; "dimensionless" end for p in params_hbc],
:Description => [ModelingToolkit.getdescription(p) for p in params_hbc]
)| Row | Name | Units | Description |
|---|---|---|---|
| String | String | String | |
| 1 | one_over_pi | 1/π constant (dimensionless) | |
| 2 | ω | m | Smoothing parameter for Heaviside approximation (Eq. 5) |
| 3 | one_half | 1/2 constant (dimensionless) | |
| 4 | I_infil | m s⁻¹ | Infiltration flux density (Eq. 3) |
| 5 | P | m s⁻¹ | Precipitation/irrigation flux density (Eq. 3) |
Equations
eqs_hbc = equations(hbc)\[ \begin{align} \frac{\mathrm{d} ~ h\left( t \right)}{\mathrm{d}t} &= \frac{ - \mathtt{I\_infil} + P}{\mathtt{\eta\_\omega}\left( t \right) + h\left( t \right) ~ \mathtt{\delta\_\omega}\left( t \right)} \\ \mathtt{\eta\_\omega}\left( t \right) &= \mathtt{one\_half} + \mathtt{one\_over\_pi} ~ \arctan\left( \frac{h\left( t \right)}{\omega} \right) \\ \mathtt{\delta\_\omega}\left( t \right) &= \frac{\mathtt{one\_over\_pi} ~ \omega}{\left( h\left( t \right) \right)^{2} + \omega^{2}} \end{align} \]
Analysis
Smoothed Heaviside Step Function (Eq. 5)
The smoothed Heaviside function $\eta_\omega(h)$ transitions from 0 to 1 around $h = 0$, with the sharpness controlled by the parameter $\omega$. Smaller values of $\omega$ produce a sharper transition, approaching the true Heaviside step function.
using OrdinaryDiffEqDefault
using Plots
# Evaluate η_ω for different ω values
h_range = range(-0.01, 0.01, length=500)
p = plot(xlabel="Soil water pressure head h (m)",
ylabel="η_ω(h)",
title="Smoothed Heaviside Step Function (Eq. 5)")
for ω_val in [1e-2, 1e-3, 1e-4]
η_vals = [(1/π) * atan(h / ω_val) + 0.5 for h in h_range]
plot!(p, h_range, η_vals, label="ω = $ω_val m", linewidth=2)
end
pSmoothed Dirac Delta Function (Eq. 5)
The smoothed Dirac delta function $\delta_\omega(h)$ is the derivative of the smoothed Heaviside function. It peaks at $h = 0$ with amplitude $1/(\pi\omega)$.
p2 = plot(xlabel="Soil water pressure head h (m)",
ylabel="δ_ω(h) (1/m)",
title="Smoothed Dirac Delta Function (Eq. 5)")
for ω_val in [1e-2, 1e-3, 1e-4]
δ_vals = [(1/π) * ω_val / (ω_val^2 + h^2) for h in h_range]
plot!(p2, h_range, δ_vals, label="ω = $ω_val m", linewidth=2)
end
p2Boundary Condition Transition: Unsaturated to Ponding
This example demonstrates the automatic switching behavior of the Heaviside boundary condition (Eq. 3). Starting from an unsaturated state ($h < 0$), precipitation drives the soil water pressure head toward positive values, transitioning from unsaturated to ponded conditions.
hbc = HeavisideBoundaryCondition()
chbc = mtkcompile(hbc)
# Scenario: Start unsaturated, apply precipitation with partial infiltration
prob = ODEProblem(chbc,
merge(
Dict(chbc.h => -0.05),
Dict(chbc.P => 1e-5, chbc.I_infil => 0.0, chbc.ω => 1e-3)
),
(0.0, 20000.0))
sol = solve(prob)
p3 = plot(sol.t, sol[chbc.h] .* 100,
xlabel="Time (s)", ylabel="Pressure head h (cm)",
title="Boundary Condition Transition (Eq. 3)",
label="h(t)", linewidth=2, legend=:bottomright)
hline!(p3, [0.0], linestyle=:dash, color=:red, label="h = 0 (ponding threshold)")
p3Manning's Friction Effect on Surface Runoff
This example shows how Manning's roughness coefficient affects the friction slope and thus the momentum balance in the Saint-Venant equations (Eq. 1). Higher roughness produces greater friction, reducing flow velocity. Note that this single-node model demonstrates the local momentum response; in a full spatial simulation, the spatial flux terms would transport momentum downstream.
sys_sr = SurfaceRunoff()
csys = mtkcompile(sys_sr)
# Compare different Manning coefficients with steady precipitation
# Using a short time window to observe the initial momentum response
p4 = plot(xlabel="Time (s)", ylabel="Runoff flux q (m²/s)",
title="Effect of Manning's Roughness on Runoff (Eq. 1)",
legend=:topleft)
for (n_val, label) in [(0.01, "n=0.01 (smooth)"),
(0.03, "n=0.03 (bare soil)"),
(0.10, "n=0.10 (dense grass)")]
prob_sr = ODEProblem(csys,
merge(
Dict(csys.h̃ => 1e-3, csys.q => 0.0),
Dict(
csys.P => 2e-5,
csys.I_infil => 1e-5,
csys.S_0 => 0.01,
csys.n_mann => n_val,
csys.h̃_0 => 1e-5,
csys.dqdl => 0.0,
csys.dFdl => 0.0,
)
),
(0.0, 5.0))
sol_sr = solve(prob_sr)
plot!(p4, sol_sr.t, sol_sr[csys.q], label=label, linewidth=2)
end
p4Water Depth Accumulation Under Different Precipitation-Infiltration Regimes
This example demonstrates the mass conservation equation (Eq. 1a) under different net water input scenarios, showing how the balance between precipitation and infiltration controls ponded water depth.
p5 = plot(xlabel="Time (s)", ylabel="Flow depth h̃ (m)",
title="Water Depth vs. Net Water Input (Eq. 1a)",
legend=:topleft)
for (P_val, I_val, label) in [
(3e-5, 1e-5, "P=3e-5, I=1e-5 (net gain)"),
(2e-5, 2e-5, "P=2e-5, I=2e-5 (balanced)"),
(1e-5, 2e-5, "P=1e-5, I=2e-5 (net loss)")]
prob_acc = ODEProblem(csys,
merge(
Dict(csys.h̃ => 0.005, csys.q => 0.0),
Dict(
csys.P => P_val,
csys.I_infil => I_val,
csys.S_0 => 0.0,
csys.n_mann => 0.03,
csys.h̃_0 => 1e-5,
csys.dqdl => 0.0,
csys.dFdl => 0.0,
)
),
(0.0, 200.0))
sol_acc = solve(prob_acc)
plot!(p5, sol_acc.t, sol_acc[csys.h̃], label=label, linewidth=2)
end
p5PDE Spatial Solution with MethodOfLines.jl
The full Saint-Venant equations (Eq. 1 from Wang et al., 2020) can be solved as a PDE system using MethodOfLines.jl for spatial discretization via the method of lines (finite differences).
The SaintVenantPDE function creates a PDESystem with proper SI units that can be directly discretized with MethodOfLines.jl. All variables and parameters carry SI unit annotations, and unit consistency is verified during system construction.
using ModelingToolkit: t
using DomainSets
using MethodOfLines
# Create the Saint-Venant PDE system with 70 mm/hr rainfall on a 0.5 m domain
pde = SaintVenantPDE(0.5, 60.0;
P_val = 70.0 / 1000 / 3600, # 70 mm/hr in m/s
S_0_val = 0.01,
n_manning_val = 0.03,
h_init_val = 1e-3,
q_init_val = 0.0)
# Discretize using method of lines (finite differences)
l = pde.ivs[2]
dl = 0.1
discretization = MOLFiniteDifference([l => dl], t, approx_order = 2)
prob = discretize(pde, discretization; checks = false)
# Solve the discretized ODE system
sol = solve(prob)
h_tilde = pde.dvs[1]
q_flux = pde.dvs[2]
disc_l = sol[l]
h_vals = sol[h_tilde]
# Plot spatial profiles of water depth at different times
p6 = plot(xlabel="Distance along slope l (m)",
ylabel="Flow depth h̃ (m)",
title="Saint-Venant PDE: Water Depth Evolution (Eq. 1)",
legend=:topleft)
for (i, ti) in enumerate(sol.t)
label_str = "t = $(round(ti, digits=1)) s"
plot!(p6, disc_l, h_vals[i, :], label=label_str, linewidth=2)
end
p6println("ODEProblem created with $(length(prob.u0)) unknowns")
println("Time span: $(prob.tspan)")
println("Solution time steps: $(length(sol.t))")ODEProblem created with 8 unknowns
Time span: (0.0, 60.0)
Solution time steps: 28Limitations
The current implementation provides:
- Single-node ODE components (
SurfaceRunoff,HeavisideBoundaryCondition) suitable for coupling with the EarthSciML framework - PDE spatial solution (
SaintVenantPDE) via MethodOfLines.jl discretization of the Saint-Venant equations (Eq. 1) with full SI unit support
The following features from Wang et al. (2020) are not yet implemented:
- Richards equation for subsurface flow (Eq. 2) — requires a separate subsurface flow component
- Coupled surface-subsurface simulations (Figs. 3-6) — requires both surface and subsurface models
- Ridge-furrow water harvesting application (Figs. 7-8) — requires the full coupled model with complex topography