Estimating Time of Concentration with the NRCS Velocity Method
Time of concentration, Tc, is the time it takes a drop of water to travel from the hydraulically most distant point in a watershed to the outlet. It sets the timing of the runoff response, and in the NRCS framework it is computed not from a single regression formula but by summing the travel time of water through each flow regime it passes on the way down. This guide implements that segment-by-segment velocity method in Python, walking a flow path through sheet flow, shallow concentrated flow, and open channel, and then deriving the basin lag. It is a sibling guide to computing composite curve numbers from land-use rasters in Python under the SCS curve number runoff estimation topic, within the Rainfall-Runoff Modeling & Hydrologic Simulation domain. Together the curve number and the time of concentration supply the two parameters a curve-number hydrograph model needs: how much runs off, and when it arrives.
The velocity method is more work than a lumped empirical formula, but it is defensible because every term is traceable to a physical reach with a measured length, slope, and surface type. Where a regression equation hides its assumptions, the velocity method exposes them one segment at a time.
The Three Flow Regimes
Water leaving the divide starts as a thin sheet, concentrates into rills and swales, and finally enters a defined channel. Each regime moves water at a different speed, and the NRCS method computes a travel time Tt for each, then sums them:
Tc = Tt(sheet) + Tt(shallow concentrated) + Tt(channel)
The convention below uses United States customary units because the sheet-flow equation is defined in them: lengths in feet, slopes in feet per foot, velocities in feet per second, the two-year twenty-four-hour rainfall in inches, and travel times in hours.
Sheet flow
Sheet flow is shallow overland flow over a plane surface, the slowest regime. The TR-55 kinematic-wave solution gives its travel time:
Tt = 0.007 * (n * L)^0.8 / (P2^0.5 * s^0.4)
where n is the sheet-flow Manning roughness (a special overland-flow value, not a channel value), L is the reach length in feet, P2 is the two-year twenty-four-hour rainfall depth in inches, and s is the land slope in feet per foot. Tt comes out in hours. Sheet flow persists only until the flow gathers into rills — current NRCS guidance limits the reach to roughly 30 metres (about 100 feet), beyond which the reach must be reclassified as shallow concentrated flow.
Shallow concentrated flow
Once flow concentrates into rills and swales, hydraulic radius becomes impractical to define, so the method estimates velocity directly from slope:
V = k * sqrt(s)
with V in feet per second and s the slope in feet per foot. The coefficient k is 16.1345 for unpaved surfaces and 20.3282 for paved surfaces; these values already fold in a representative roughness and flow depth. The travel time is then Tt = L / (3600 * V) in hours, dividing by 3600 to convert seconds to hours.
Channel flow
In a defined channel with a measurable cross section, Manning’s equation gives the velocity:
V = (1.49 / n) * R^(2/3) * sqrt(s)
where R is the hydraulic radius (flow area divided by wetted perimeter) in feet, n is the channel Manning roughness, s is the channel slope, and the 1.49 factor carries the customary-unit conversion. As before, Tt = L / (3600 * V).
Parameter Reference
The velocity method’s accuracy rests almost entirely on choosing defensible roughness and coefficient values for each reach.
| Segment | Governing input | Typical values |
|---|---|---|
| Sheet flow | Overland Manning n |
Smooth asphalt 0.011; fallow soil 0.05; cultivated, > 20% residue 0.17; short-grass prairie 0.15; dense grass 0.24; woods, light underbrush 0.40 |
| Shallow concentrated | Coefficient k |
Unpaved 16.1345; paved 20.3282 (ft/s per √(ft/ft)) |
| Channel | Manning n |
Concrete-lined 0.013; clean natural stream 0.030; winding stream with pools 0.040; heavy brush channel 0.075 |
The sheet-flow n is a special overland-flow roughness and is much larger than the channel n for the same nominal cover, because it represents raindrop impact and micro-topography over a plane, not established open-channel resistance. Mixing the two tables is the single most common parameter error in the method.
Python Implementation
The function below accepts a list of flow-path segments, each a dictionary describing its type, length, slope, and roughness, and returns the total time of concentration and the derived lag. Each regime is handled by its own helper so the equations stay readable and independently testable.
import logging
import math
from dataclasses import dataclass
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logger = logging.getLogger(__name__)
# Shallow concentrated flow velocity coefficients (ft/s per sqrt(ft/ft))
K_UNPAVED = 16.1345
K_PAVED = 20.3282
SHEET_FLOW_MAX_FT = 100.0 # ~30 m; beyond this, reclassify as shallow concentrated
@dataclass
class Segment:
"""One reach of the hydraulic flow path."""
kind: str # 'sheet', 'shallow', or 'channel'
length_ft: float # reach length along the flow path
slope: float # slope in ft/ft (rise over run)
n: float = 0.0 # Manning roughness (sheet or channel)
paved: bool = False # shallow concentrated only
hydraulic_radius_ft: float = 0.0 # channel only
p2_in: float = 0.0 # sheet only: 2-yr 24-hr rainfall, inches
def sheet_flow_time(seg: Segment) -> float:
"""TR-55 kinematic sheet-flow travel time in hours."""
if seg.length_ft > SHEET_FLOW_MAX_FT:
logger.warning(
"Sheet-flow reach %.0f ft exceeds the %.0f ft limit; "
"reclassify the excess as shallow concentrated flow.",
seg.length_ft, SHEET_FLOW_MAX_FT,
)
tt = 0.007 * (seg.n * seg.length_ft) ** 0.8 / (
seg.p2_in ** 0.5 * seg.slope ** 0.4
)
logger.info("Sheet flow: L=%.0f ft, n=%.3f -> Tt=%.3f h", seg.length_ft, seg.n, tt)
return tt
def shallow_flow_time(seg: Segment) -> float:
"""Shallow concentrated flow travel time in hours from V = k*sqrt(s)."""
k = K_PAVED if seg.paved else K_UNPAVED
velocity = k * math.sqrt(seg.slope) # ft/s
tt = seg.length_ft / (3600.0 * velocity) # seconds -> hours
logger.info("Shallow flow: V=%.2f ft/s, L=%.0f ft -> Tt=%.3f h",
velocity, seg.length_ft, tt)
return tt
def channel_flow_time(seg: Segment) -> float:
"""Open-channel travel time in hours from Manning's equation."""
velocity = (1.49 / seg.n) * seg.hydraulic_radius_ft ** (2.0 / 3.0) * math.sqrt(seg.slope)
tt = seg.length_ft / (3600.0 * velocity)
logger.info("Channel flow: V=%.2f ft/s, R=%.2f ft, L=%.0f ft -> Tt=%.3f h",
velocity, seg.hydraulic_radius_ft, seg.length_ft, tt)
return tt
def time_of_concentration(segments: list[Segment]) -> dict:
"""Sum segment travel times into Tc (hours) and derive SCS lag = 0.6*Tc."""
dispatch = {
"sheet": sheet_flow_time,
"shallow": shallow_flow_time,
"channel": channel_flow_time,
}
total = 0.0
for seg in segments:
if seg.kind not in dispatch:
raise ValueError(f"Unknown segment kind {seg.kind!r}")
if seg.slope <= 0:
raise ValueError(f"Segment slope must be positive; got {seg.slope} for {seg.kind}")
total += dispatch[seg.kind](seg)
lag = 0.6 * total
logger.info("Time of concentration Tc = %.2f h; basin lag = %.2f h", total, lag)
return {"tc_hours": total, "lag_hours": lag}
if __name__ == "__main__":
flow_path = [
Segment(kind="sheet", length_ft=100.0, slope=0.010, n=0.24, p2_in=3.5),
Segment(kind="shallow", length_ft=1400.0, slope=0.020, paved=False),
Segment(kind="channel", length_ft=7500.0, slope=0.005, n=0.040,
hydraulic_radius_ft=2.2),
]
result = time_of_concentration(flow_path)
logger.info("Result: %s", result)
The time_of_concentration function validates that every slope is positive before applying an equation — a zero or negative slope produces a division by zero in the sheet-flow term and an imaginary velocity everywhere else. The dispatch table keeps the summation loop agnostic to the number and order of segments, so a flow path with two channel reaches or no sheet-flow reach works without special cases.
Worked Example: Reading the Segment Contributions
Running the demo flow path makes the method’s structure concrete. The three reaches produce very different travel times despite the channel being by far the longest:
| Reach | Length | Slope | Velocity or basis | Travel time |
|---|---|---|---|---|
| Sheet flow | 100 ft | 0.010 | TR-55 kinematic, n = 0.24 | 0.30 h |
| Shallow concentrated | 1400 ft | 0.020 | V = 2.28 ft/s (unpaved) | 0.17 h |
| Channel | 7500 ft | 0.005 | V = 4.46 ft/s (Manning) | 0.47 h |
The 100 ft sheet-flow reach contributes 0.30 hours — nearly a third of the total Tc of 0.94 hours — despite being less than 2 percent of the flow path length. That is the signature of the velocity method: the slow overland regime dominates the timing far out of proportion to its distance, which is exactly why the sheet-flow length cap matters so much. Double the sheet reach to 200 ft (in violation of the cap) and its travel time would jump to roughly 0.52 hours, inflating the whole Tc by more than 20 percent and pushing the derived lag from 0.56 to nearly 0.70 hours.
The channel reach, though 75 times longer than the sheet reach, adds only 0.47 hours because water moves an order of magnitude faster once it is confined. This pattern — a short, decisive overland head and a long, fast channel tail — holds for most natural watersheds and is worth checking as a sanity test: if the channel reach dominates the travel time in a basin with any appreciable overland length, the sheet or shallow roughness values are probably too low.
Deriving the Flow Path from a DEM
In an automated pipeline the segments are rarely typed in by hand; they are extracted from terrain. The hydraulically longest flow path is traced by following the flow-direction grid upstream from the outlet to the cell with the greatest travel-weighted distance, a computation that builds directly on the flow-routing products described in the broader hydrology data preparation work. Once the polyline path exists, it is split into reaches by regime: the uppermost portion, above the point where contributing area first exceeds a small threshold, is sheet flow; the mid-path, below that threshold but above channel initiation, is shallow concentrated flow; and everything below the mapped channel head is channel flow.
Slope for each reach comes from sampling DEM elevations at the reach endpoints and dividing the drop by the along-path length rather than the straight-line distance, which keeps the grade consistent with how the travel-time equations define it. Roughness and, for channels, hydraulic radius are attached from land-cover and channel-geometry lookups keyed to each reach. The result is a list of Segment objects identical in structure to the hand-built demo, which the same time_of_concentration function then reduces to Tc and lag. Automating the split points is where most of the engineering judgment lives, because the transition from sheet to shallow flow and the location of channel initiation both depend on thresholds that should be documented and, ideally, calibrated against surveyed flow paths.
Gotchas and Edge Cases
-
Sheet-flow length cap. Sheet flow is the slowest regime, so an over-long sheet-flow reach inflates
Tcdramatically. Enforce the 30 m (100 ft) limit and push the remainder into shallow concentrated flow. The implementation logs a warning rather than silently accepting a physically implausible reach. -
Roughness table confusion. The overland-flow Manning
nused in the sheet-flow equation is far larger than the channelnused in Manning’s equation. Using a channel roughness of 0.03 in the sheet-flow term, or a sheet value of 0.24 in the channel term, produces travel times off by an order of magnitude. -
Flat or reversed slopes. DEM-derived slopes can come out as zero or slightly negative in flat reaches and depressions. Guard against non-positive slopes explicitly and substitute a defensible minimum grade rather than letting the arithmetic blow up.
-
Not tracing the hydraulically longest path.
Tcmust follow the flow path with the greatest travel time, which is not always the longest straight-line distance. A short, steep channel can carry water faster than a long, flat overland reach; select the path by travel time, not geometry alone. -
Unit slips. The customary-unit equations assume feet, feet per second, and inches. Feeding metres into the sheet-flow length or the channel hydraulic radius, or millimetres into
P2, yields a plausible-looking but wrongTc. Convert all inputs to the expected units before calling the functions, and keep the conversion at the boundary of the code. -
Ignoring the lag propagation. Because basin lag is fixed at 0.6 times
Tc, a 20 percent error inTcbecomes a 20 percent error in the modeled hydrograph peak timing. Sensitivity-test the segments that dominate the sum before trusting a singleTcvalue.
Frequently Asked Questions
How long can the sheet-flow segment be in the NRCS velocity method?
The TR-55 sheet-flow equation is only valid for short overland distances before flow concentrates into rills. Current NRCS guidance caps sheet flow at roughly 30 metres (about 100 feet). Beyond that length, reclassify the reach as shallow concentrated flow, or the equation will greatly overestimate travel time because sheet flow is the slowest regime and its travel time grows nonlinearly with length.
What is the relationship between time of concentration and lag?
In the NRCS method, basin lag is 0.6 times the time of concentration. Lag is the time from the centroid of excess rainfall to the peak of the runoff hydrograph, and it is the parameter that positions the SCS dimensionless unit hydrograph in time. Because lag is a fixed fraction of Tc, any error in Tc propagates directly into the modeled peak timing.
Why does shallow concentrated flow use a coefficient instead of Manning’s equation?
Shallow concentrated flow occurs in poorly defined rills and swales where the hydraulic radius is impractical to measure. The NRCS collapses Manning’s equation into a simple velocity-slope relationship, V = k * sqrt(s), with separate paved and unpaved coefficients that already embed a representative roughness and flow depth. This avoids forcing the analyst to estimate a cross section for a flow regime that has none.
Related Topics
- SCS Curve Number Runoff Estimation — the parent guide whose runoff volume this timing parameter complements to build a hydrograph
- Unit Hydrograph Methods — where the time of concentration and lag position the response hydrograph in time
- Computing Composite Curve Numbers from Land-Use Rasters in Python — the sibling guide that derives the runoff-volume parameter paired with this timing estimate
- Rainfall-Runoff Modeling & Hydrologic Simulation — the domain overview connecting loss methods, timing, and transforms