Hydrology: units, equations and validation#
Conventions and model limits#
Lengths and depths are metres, times seconds and rates m/s; divide a rain
rate in mm/h by 3.6e6 and convert days with
units.convert(value, "day", "s"). The water balance accepts any
common depth rate, such as mm/day, used consistently. Evaporation rates are
kg/(m^2 s), which equals mm/s of liquid water; multiply by 86400 for mm/day.
Vapour pressures are pascals, and saturation_slope is the derivative of
earthsciencekit.atmosphere.saturation_vapor_pressure() with the same
constant latent heat LATENT_HEAT_VAPORIZATION = 2.5e6 J/kg; the von
Karman constant VON_KARMAN = 0.41 sets the neutral aerodynamic resistance.
Both live in earthsciencekit.constants.
Darcy fluxes are positive down the head gradient, and hydraulic_head
adds the elevation to the gauge pressure head. Aquifers are homogeneous and
isotropic: Dupuit’s water table has no recharge and a horizontal base, and
the Theis and Cooper-Jacob drawdowns assume a fully penetrating well of
negligible radius in an infinite confined aquifer. Cooper-Jacob is the
late-time limit, within 1% of Theis for u < 0.01.
In the unsaturated zone heights and fluxes are positive upward and pressure
heads negative under suction. richards_steady_head solves the steady
Richards equation for Gardner’s exponential conductivity above a water table
at height zero; it rejects downward fluxes at or beyond the saturated
conductivity and heights above the limit of capillary supply. Green-Ampt and
Horton rates are infiltration capacities of a uniform soil; Green-Ampt times
start when the surface ponds, and the suction head is a positive magnitude.
Channel formulas are for steady uniform flow in SI units: Chézy’s
coefficient is in m^0.5/s and Manning’s n in s/m^(1/3). The unit
hydrograph is linear and time-invariant: unit_hydrograph_convolution
takes excess rainfall depths per interval and ordinates in m^3/s per metre of
excess at the same interval, and the Nash shape integrates to one per second.
muskingum_coefficients returns [C0, C1, C2] on the last axis;
muskingum_routing starts from a steady state and needs scalar parameters.
Stream orders follow Strahler (1952). TOPMODEL indices use the upslope area
per unit contour width in metres, and nonpositive local deficits mark
saturated ground.
Input contracts#
Algebraic models accept finite real scalars or nonempty arrays. Booleans,
complex inputs, nonnumeric values and NaN/Inf are rejected. Physical bounds
apply element by element; NumPy broadcasting applies between arguments, except
for the vector inputs of unit_hydrograph_convolution and
muskingum_routing. No unit metadata are carried in raw numbers. Bounds
protect mathematical domains and obvious physical contradictions, but do not
establish model applicability.
Equation and output map#
water_balance#
earthsciencekit.hydrology.water_balance()
Output: m/s. Perrault (1674): a closed catchment with no groundwater exchange across its divide; any common depth rate (such as mm/day) may be used throughout.
precipitation - evapotranspiration - runoff
runoff_coefficient#
earthsciencekit.hydrology.runoff_coefficient()
Output: 1. Both depths over the same area and period; long periods make the storage change negligible.
runoff / precipitation
hydraulic_radius#
earthsciencekit.hydrology.hydraulic_radius()
Output: m. Rectangular section; tends to the depth for a wide channel.
width * depth / (width + 2 * depth)
chezy_velocity#
earthsciencekit.hydrology.chezy_velocity()
Output: m/s. Chezy (1775): steady uniform turbulent flow; the coefficient is not a constant but depends on roughness and depth.
chezy_coefficient * np.sqrt(hydraulic_radius * slope)
dalton_evaporation#
earthsciencekit.hydrology.dalton_evaporation()
Output: kg/(m^2 s). Dalton (1802), a bulk mass-transfer law; the surface temperature it needs is rarely measured.
transfer_coefficient * (saturation_vapor_pressure - vapor_pressure)
hydraulic_head#
earthsciencekit.hydrology.hydraulic_head()
Output: m. Slow flow with negligible velocity head and uniform fluid density.
elevation + pressure / (density * gravity)
darcy_flux#
earthsciencekit.hydrology.darcy_flux()
Output: m/s. Darcy (1856): laminar flow with a pore Reynolds number below about 1-10 in a saturated isotropic medium.
-hydraulic_conductivity * head_gradient
seepage_velocity#
earthsciencekit.hydrology.seepage_velocity()
Output: m/s. Average linear velocity; dispersion spreads tracers about it.
darcy_flux / porosity
dupuit_head#
earthsciencekit.hydrology.dupuit_head()
Output: m. Dupuit (1863): no recharge, a horizontal base, and slopes small enough that the head is uniform on each vertical.
np.sqrt(upstream_head**2 - (upstream_head**2 - downstream_head**2) * distance / length)
dupuit_discharge#
earthsciencekit.hydrology.dupuit_discharge()
Output: m^2/s. Dupuit (1863), the Dupuit-Forchheimer discharge formula, exact for the discharge even where the head profile is not.
hydraulic_conductivity * (upstream_head**2 - downstream_head**2) / (2 * length)
manning_velocity#
earthsciencekit.hydrology.manning_velocity()
Output: m/s. Manning (1891), SI form; fully rough turbulent flow, steady and uniform.
hydraulic_radius**(2 / 3) * np.sqrt(slope) / roughness
chezy_from_manning#
earthsciencekit.hydrology.chezy_from_manning()
Output: m^0.5/s. Manning (1891), SI units.
hydraulic_radius**(1 / 6) / roughness
green_ampt_rate#
earthsciencekit.hydrology.green_ampt_rate()
Output: m/s. Green and Ampt (1911): a piston-like front in a uniform soil with a negligible ponding depth; the rate tends to the saturated conductivity.
saturated_conductivity * (1 + suction_head * moisture_deficit / cumulative_infiltration)
green_ampt_time#
earthsciencekit.hydrology.green_ampt_time()
Output: s. Green and Ampt (1911), ponded from time zero; early on the depth grows as the square root of time.
(cumulative_infiltration - suction_head * moisture_deficit * np.log(1 + cumulative_infiltration / (suction_head * moisture_deficit))) / saturated_conductivity
gardner_conductivity#
earthsciencekit.hydrology.gardner_conductivity()
Output: m/s. Gardner (1958), a one-parameter fit without hysteresis; it makes the steady Richards equation solvable in closed form.
saturated_conductivity * np.exp(sorptive_number * pressure_head)
unsaturated_darcy_flux#
earthsciencekit.hydrology.unsaturated_darcy_flux()
Output: m/s. Richards (1931): the total head is pressure_head + z and air stays at atmospheric pressure.
-conductivity * (pressure_head_gradient + 1)
richards_steady_head#
earthsciencekit.hydrology.richards_steady_head()
Output: m. Richards (1931) steady equation flux = -K(psi) (dpsi/dz + 1) with Gardner’s (1958) conductivity; an upward flux can only be sustained below np.log(1 + saturated_conductivity / flux) / sorptive_number.
np.log(((flux + saturated_conductivity) * np.exp(-sorptive_number * height) - flux) / saturated_conductivity) / sorptive_number
nash_unit_hydrograph#
earthsciencekit.hydrology.nash_unit_hydrograph()
Output: 1/s. Nash (1957), a gamma density with mean lag reservoirs * storage_constant; a smooth synthetic shape for Sherman’s unit hydrograph, linear and time-invariant.
time**(reservoirs - 1) * np.exp(-time / storage_constant) / (storage_constant**reservoirs * scipy.special.gamma(reservoirs))
unit_hydrograph_convolution#
earthsciencekit.hydrology.unit_hydrograph_convolution()
Output: m^3/s. Sherman (1932): linear superposition and time invariance; baseflow and losses are removed beforehand.
np.convolve(excess_rainfall, unit_hydrograph)
horton_infiltration_rate#
earthsciencekit.hydrology.horton_infiltration_rate()
Output: m/s. Horton (1933, 1940), an empirical law for rain at or above capacity throughout.
final_rate + (initial_rate - final_rate) * np.exp(-decay_constant * time)
horton_cumulative_infiltration#
earthsciencekit.hydrology.horton_cumulative_infiltration()
Output: m. Horton (1933, 1940), with rain at or above capacity from time zero.
final_rate * time + (initial_rate - final_rate) * (1 - np.exp(-decay_constant * time)) / decay_constant
well_function#
earthsciencekit.hydrology.well_function()
Output: 1. The exponential integral E1, -0.5772 - ln(u) + u - ... for small u.
scipy.special.exp1(u)
theis_drawdown#
earthsciencekit.hydrology.theis_drawdown()
Output: m. Theis (1935): constant rate, a fully penetrating well of negligible radius in an infinite, homogeneous, isotropic confined aquifer with water released instantly from storage.
pumping_rate / (4 * np.pi * transmissivity) * scipy.special.exp1(radius**2 * storativity / (4 * transmissivity * time))
muskingum_coefficients#
earthsciencekit.hydrology.muskingum_coefficients()
Output: 1. McCarthy (1938), storage K (X I + (1 - X) O); all coefficients are nonnegative when 2 K X <= time_step <= 2 K (1 - X).
[time_step - 2 K X, time_step + 2 K X, 2 K (1 - X) - time_step] / (2 K (1 - X) + time_step)
muskingum_routing#
earthsciencekit.hydrology.muskingum_routing()
Output: m^3/s. McCarthy (1938): O[j+1] = C0 I[j+1] + C1 I[j] + C2 O[j]; choose the time step so that no coefficient is negative.
O[j + 1] = C0 * I[j + 1] + C1 * I[j] + C2 * O[j]
stream_number#
earthsciencekit.hydrology.stream_number()
Output: 1. Horton’s law of stream numbers (1945), an empirical average; Strahler’s (1952) ordering is the usual convention.
bifurcation_ratio**(highest_order - order)
stream_length#
earthsciencekit.hydrology.stream_length()
Output: m. Horton’s law of stream lengths (1945), an empirical average over a network.
first_order_length * length_ratio**(order - 1)
cooper_jacob_drawdown#
earthsciencekit.hydrology.cooper_jacob_drawdown()
Output: m. Cooper and Jacob (1946): the first two terms of the Theis series, within 1% for u < 0.01 and meaningless (it goes negative) for u above about 0.56.
pumping_rate / (4 * np.pi * transmissivity) * (-np.euler_gamma - np.log(radius**2 * storativity / (4 * transmissivity * time)))
cooper_jacob_transmissivity#
earthsciencekit.hydrology.cooper_jacob_transmissivity()
Output: m^2/s. Cooper and Jacob (1946), from late-time data where u < 0.01; the storativity follows from the zero-drawdown intercept t0 as 2.25 * transmissivity * t0 / radius**2.
np.log(10) * pumping_rate / (4 * np.pi * drawdown_per_log_cycle)
saturation_slope#
earthsciencekit.hydrology.saturation_slope()
Output: Pa/K. Derivative of earthsciencekit.atmosphere.saturation_vapor_pressure() (constant latent heat 2.5e6 J/kg).
saturation_vapor_pressure(temperature) * LATENT_HEAT_VAPORIZATION / (R_VAPOR * temperature**2)
psychrometric_constant#
earthsciencekit.hydrology.psychrometric_constant()
Output: Pa/K. Dry-air heat capacity and constant latent heat; equal transfer coefficients for heat and vapour.
CP_DRY_AIR * pressure / (EPSILON_WATER * LATENT_HEAT_VAPORIZATION)
penman_evaporation#
earthsciencekit.hydrology.penman_evaporation()
Output: kg/(m^2 s). Penman (1948): the saturation curve is linearized between air and surface temperature, and heat and vapour share one transfer coefficient; a saturated surface (open water or wet grass).
(saturation_slope(temperature) * net_radiation / LATENT_HEAT_VAPORIZATION + psychrometric_constant(pressure) * aerodynamic_evaporation) / (saturation_slope(temperature) + psychrometric_constant(pressure))
aerodynamic_resistance#
earthsciencekit.hydrology.aerodynamic_resistance()
Output: s/m. Neutral stability; the measurement height above the displacement height must exceed both roughness lengths. For FAO grass (0.12 m) at 2 m it is about 208 / wind_speed.
np.log((measurement_height - displacement_height) / momentum_roughness) * np.log((measurement_height - displacement_height) / vapor_roughness) / (VON_KARMAN**2 * wind_speed)
bulk_surface_resistance#
earthsciencekit.hydrology.bulk_surface_resistance()
Output: s/m. Monteith (1965) big-leaf canopy with parallel leaves; FAO grass has about 70 s/m.
stomatal_resistance / active_leaf_area_index
penman_monteith_evaporation#
earthsciencekit.hydrology.penman_monteith_evaporation()
Output: kg/(m^2 s). Monteith (1965): a big-leaf canopy at one temperature, neutral stability; zero surface resistance recovers Penman with aerodynamic_evaporation = air_density * CP_DRY_AIR * vapor_pressure_deficit / (psychrometric_constant * LATENT_HEAT_VAPORIZATION * aerodynamic_resistance).
(Delta * available_energy + air_density * CP_DRY_AIR * vapor_pressure_deficit / aerodynamic_resistance) / (LATENT_HEAT_VAPORIZATION * (Delta + gamma * (1 + surface_resistance / aerodynamic_resistance)))
topographic_index#
earthsciencekit.hydrology.topographic_index()
Output: 1. Beven and Kirkby (1979): steady recharge, a water table parallel to the surface and transmissivity falling exponentially with deficit.
np.log(specific_area / np.tan(slope))
topmodel_deficit#
earthsciencekit.hydrology.topmodel_deficit()
Output: m. Beven and Kirkby (1979) with uniform transmissivity at saturation and quasi-steady drainage.
mean_deficit + scaling_depth * (mean_index - topographic_index)
topmodel_baseflow#
earthsciencekit.hydrology.topmodel_baseflow()
Output: m/s. Beven and Kirkby (1979): exponential transmissivity profile, water table parallel to the surface.
saturated_transmissivity * np.exp(-mean_index) * np.exp(-mean_deficit / scaling_depth)
Infiltration under steady rain#
earthsciencekit.hydrology.simulate_infiltration() integrates the
cumulative infiltration F of a Green-Ampt soil under rain of rate r,
dF/dt = min(r, K (1 + psi dtheta / F)), from F = 0 at the first
sample. The surface ponds when F = K psi dtheta / (r - K), at
t_p = K psi dtheta / (r (r - K)); rain no faster than K never ponds.
It returns the cumulative infiltration, the infiltration rate and the
infiltration-excess runoff rate r - f. Surface storage, redistribution and
a water table are neglected.
Independent validation#
Every algebraic API has a fixed-value check, broadcasting or bound checks and
invalid-input contracts. The hydrology tests also check that the water balance
closes over a cycle, that Manning’s formula is Chézy’s with R**(1/6) / n
and the hydraulic radius tends to the depth, that every section of Dupuit’s
water table carries the Dupuit discharge, that the steady Richards profile
carries a constant flux, is hydrostatic without flow and tends to unit
gradient, that the Green-Ampt time inverts the rate and grows as the square
of the depth early on, that the Nash hydrograph integrates to one and the
storm hydrograph conserves volume and superposes, that Horton’s cumulative
infiltration integrates the rate, that Theis tends to Cooper-Jacob and its
log-cycle slope returns the transmissivity, that Muskingum coefficients sum
to one and routing conserves volume while delaying and lowering the peak,
that Penman-Monteith with zero surface resistance is Penman and
saturation_slope is the derivative of the saturation curve, that TOPMODEL
deficits average to the mean and baseflow falls by e per scaling depth,
and that simulate_infiltration matches the Mein-Larson closed form after
ponding. See Validation policy for the exact evidence and
Numerical methods and sampled results for numerical conventions.