civilpy.water_resources package

Open-channel and pipe hydraulics, bridge scour, and ODOT culvert headwall standards.

Submodules

civilpy.water_resources.hydraulics module

ODOT standard culvert headwall and wing-wall lookup tables.

OhioCulvertDesign encapsulates ODOT standard-drawing dimensions, reinforcement, and concrete quantities for box-culvert headwalls and wing-walls, keyed by headwall type, design height, and span.

class civilpy.water_resources.hydraulics.OhioCulvertDesign(hw_type='A', des_h='6.5', span=14, wall_theta='All')[source]

Bases: object

ODOT standard culvert headwall and wing-wall design lookup table.

Encapsulates the Ohio DOT standard drawings for box culvert headwall and wing-wall dimensions, reinforcement sizes, and concrete quantities. Dimensions are keyed by headwall type, design height, and culvert span.

hw_type

Headwall type code (e.g. "A").

Type:

str

des_h

Design height key (e.g. "6.5").

Type:

str

span

Culvert span (ft).

Type:

int

wall_theta

Wing-wall skew angle ("All" or specific angle).

Type:

str

footing

Footing reinforcement schedule keyed by design index.

Type:

dict

Headwall_Dimensions

Nested dimension and reinforcement lookup keyed by headwall type → design height → property.

Type:

dict

__init__(hw_type='A', des_h='6.5', span=14, wall_theta='All')[source]

Initialise an ODOT culvert design lookup object.

Parameters:
  • hw_type (str) – Headwall type (default "A").

  • des_h (str) – Design height string key matching ODOT tables (default "6.5").

  • span (int) – Box culvert span in feet (default 14).

  • wall_theta (str) – Wing-wall skew angle descriptor (default "All").

civilpy.water_resources.open_channel module

Open-channel flow for rectangular channels: critical/normal depth, Froude number, and the specific-energy curve with alternate depths — the diagram from every hydraulics course.

US units: ft, cfs; Manning with the 1.486 conversion.

Examples

>>> ch = RectangularChannel(width=10, n=0.013, slope=0.002)
>>> round(ch.critical_depth(q=200), 3)
2.316
>>> yn = ch.normal_depth(q=200)
>>> ch.froude(q=200, y=yn) < 1.0   # mild slope -> subcritical
True
class civilpy.water_resources.open_channel.RectangularChannel(width: float, n: float = 0.013, slope: float = 0.001)[source]

Bases: object

alternate_depths(q: float, energy: float, tol: float = 1e-08) tuple[float, float][source]

The sub- and supercritical depths sharing the given specific energy (raises if E is below the critical minimum).

classify_profile(q: float, y: float) str[source]

Gradually-varied-flow profile class (M1, M2, S3, …) for a flow depth y: slope letter from normal vs. critical depth, zone number from where y sits relative to both.

critical_depth(q: float) float[source]

yc = (q_unit^2/g)^(1/3) for discharge q (cfs).

friction_slope(q: float, y: float) float[source]

Sf from Manning’s equation at depth y — the slope that would make y the normal depth for q.

froude(q: float, y: float) float[source]
gvf_profile(q: float, y_control: float, length: float, n_steps: int = 400, upstream: bool | None = None)[source]

Gradually-varied water-surface profile from a control depth, integrating dy/dx = (S0 - Sf)/(1 - Fr^2) with RK4.

y_control is the known depth (e.g. pool elevation behind a dam for M1, the brink for M2); integration marches length ft upstream for subcritical controls and downstream for supercritical (override with upstream). Returns (x, y) arrays with x positive downstream of the control (so an upstream march has negative stations) — integration stops early if the depth reaches critical, where GVF theory breaks down.

manning_q(y: float) float[source]

Discharge at depth y by Manning’s equation (cfs).

normal_depth(q: float, tol: float = 1e-08) float[source]

Solve Manning for the uniform-flow depth by bisection.

plot_gvf_profile(q: float, y_control: float, length: float, ax=None, n_steps: int = 400, upstream: bool | None = None)[source]

Water-surface profile sketch: channel bottom on its slope, the GVF surface, normal and critical depth lines, the energy grade line, and the profile classification (M1, S2, …). Returns the figure.

plot_specific_energy(q: float, ax=None, energy: float | None = None, y_max: float | None = None)[source]

The E-y curve with the critical point marked and, optionally, the alternate depths for a given energy.

specific_energy(q: float, y: float) float[source]

E = y + v^2/2g (ft).

civilpy.water_resources.pipe_flow module

Pressure-pipe energy and hydraulic grade lines.

A PipeProfile is a chain of pipe segments leaving a reservoir (or any known energy elevation). Each segment carries Darcy-Weisbach friction (h_f = f L/D * V^2/2g) plus a lumped minor-loss coefficient applied at its entrance, so fittings, bends, entrances, and expansions land where they occur. The EGL steps down at each minor loss and slopes down along each run; the HGL hangs one velocity head below it — the classic profile sketch, including the sub-atmospheric warning zone where the HGL dips below the pipe.

US units: ft, cfs; diameters in inches.

Examples

A 12-in line, 500 ft long, f = 0.02, square entrance (K = 0.5), running 4 cfs out of a reservoir at El. 250:

>>> p = PipeProfile([PipeSegment(500.0, 12.0, f=0.02, k_minor=0.5)],
...                 node_elevations_ft=[200.0, 180.0],
...                 source_energy_ft=250.0, q_cfs=4.0)
>>> round(p.velocity_fps(0), 2)
5.09
>>> round(p.total_head_loss(), 2)              # friction + entrance
4.23
>>> round(p.egl_at_end(), 2)
245.77
class civilpy.water_resources.pipe_flow.PipeProfile(segments, node_elevations_ft, source_energy_ft: float, q_cfs: float)[source]

Bases: object

A reservoir-fed pipe chain with its EGL/HGL profile.

node_elevations_ft are the pipe centerline elevations at each segment end (len(segments) + 1 values); source_energy_ft is the starting energy elevation (reservoir water surface).

egl_at_end() float[source]
friction_loss_ft(i: int) float[source]
grade_lines()[source]

Stations and grade-line elevations: (x, egl, hgl, z_pipe) arrays with two points per segment so minor losses show as vertical EGL drops.

low_pressure_stations()[source]

(station, margin) pairs where the HGL falls below the pipe — sub-atmospheric pressure, the cavitation/air-binding warning.

minor_loss_ft(i: int) float[source]
plot_egl_hgl(ax=None)[source]

The profile sketch: pipe on its alignment, EGL solid red, HGL dashed blue, reservoir at the source, and any sub-atmospheric zone shaded. Returns the figure.

total_head_loss() float[source]
velocity_fps(i: int) float[source]
velocity_head_ft(i: int) float[source]
class civilpy.water_resources.pipe_flow.PipeSegment(length_ft: float, diameter_in: float, f: float = 0.02, k_minor: float = 0.0)[source]

Bases: object

One run of pipe: k_minor is the lumped minor-loss coefficient applied at the segment’s upstream end (entrance, bend, valve, …).

property area_ft2: float
diameter_in: float
f: float = 0.02
k_minor: float = 0.0
length_ft: float

civilpy.water_resources.scour module

Bridge scour by the FHWA HEC-18 method.

Implements the local pier-scour (CSU) equation, the contraction-scour equations (live-bed Laursen and clear-water), and the supporting critical- velocity and correction-factor relationships from FHWA HEC-18, Evaluating Scour at Bridges (5th ed., 2012). The bed grain size these equations need – D50 and D95 – is exactly what a particle-size analysis on a boring log provides, so pier_scour_from_boring() reads it straight from a Borehole.

US customary units throughout: lengths in feet, velocities in ft/s, discharge in cubic feet per second, grain sizes in millimetres (converted internally), angles in degrees.

civilpy.water_resources.scour.G_FT_S2 = 32.2

Gravitational acceleration, ft/s^2.

civilpy.water_resources.scour.KU_CLEARWATER = 0.0077

Clear-water contraction-scour coefficient Ku, English (HEC-18 Eq. 6.4).

civilpy.water_resources.scour.KU_CRITICAL_VELOCITY = 11.17

Critical-velocity coefficient Ku, English units (HEC-18 Eq. 6.1).

civilpy.water_resources.scour.MM_PER_FT = 304.8

Millimetres per foot.

civilpy.water_resources.scour.PIER_SHAPE_K1 = {'cylinder': 1.0, 'group': 1.0, 'round': 1.0, 'sharp': 0.9, 'square': 1.1}

Pier nose shape factor K1 (HEC-18 Table 7.1).

class civilpy.water_resources.scour.PierScourResult(scour_depth_ft: float, froude: float, k1: float, k2: float, k3: float, k4: float, d50_mm: float | None = None, d95_mm: float | None = None)[source]

Bases: object

Local pier scour with the correction factors that produced it.

d50_mm: float | None = None
d95_mm: float | None = None
froude: float
k1: float
k2: float
k3: float
k4: float
scour_depth_ft: float
civilpy.water_resources.scour.angle_of_attack_factor(pier_length_ft: float, pier_width_ft: float, skew_deg: float) float[source]

Flow angle-of-attack correction K2 (HEC-18 Eq. 7.2): K2 = (cos theta + (L/a) sin theta)^0.65.

civilpy.water_resources.scour.armoring_factor_k4(approach_velocity_fps: float, depth_ft: float, pier_width_ft: float, d50_mm: float, d95_mm: float) float[source]

Coarse-bed armoring correction K4 (HEC-18 Eqs. 7.3-7.6).

Applies only when the bed is coarse (D50 >= 2 mm and D95 >= 20 mm); otherwise K4 = 1.0. K4 = 0.4 (VR)^0.15 with a floor of 0.4, where the velocity ratio VR uses the approach velocity for incipient motion at the pier for D50 and D95.

civilpy.water_resources.scour.clear_water_contraction_scour(contracted_discharge_cfs: float, contracted_width_ft: float, d50_mm: float, existing_depth_ft: float) float[source]

Clear-water contraction scour depth ys (ft), Laursen (HEC-18 Eq. 6.4): y2 = [Ku Q^2 / (Dm^(2/3) W^2)]^(3/7), Dm = 1.25 D50, Ku = 0.0077 (English, Dm in feet); ys = y2 - y0.

civilpy.water_resources.scour.critical_velocity(depth_ft: float, d50_mm: float) float[source]

Critical (incipient-motion) velocity Vc (ft/s) for bed material of median size d50_mm at flow depth depth_ft (HEC-18 Eq. 6.1): Vc = Ku y^(1/6) D50^(1/3), Ku = 11.17 (English), D50 in feet.

civilpy.water_resources.scour.froude_number(velocity_fps: float, depth_ft: float) float[source]

Froude number Fr = V / sqrt(g y).

civilpy.water_resources.scour.live_bed_contraction_scour(approach_depth_ft: float, approach_discharge_cfs: float, contracted_discharge_cfs: float, approach_width_ft: float, contracted_width_ft: float, existing_depth_ft: float, k1: float = 0.64) float[source]

Live-bed contraction scour depth ys (ft), Laursen (HEC-18 Eq. 6.2): y2/y1 = (Q2/Q1)^(6/7) (W1/W2)^k1, ys = y2 - y0.

k1 is the bed-transport-mode exponent (0.59-0.69; 0.64 default).

civilpy.water_resources.scour.pier_scour_csu(approach_velocity_fps: float, approach_depth_ft: float, pier_width_ft: float, k1: float = 1.0, k2: float = 1.0, k3: float = 1.1, k4: float = 1.0) float[source]

Local pier scour depth ys (ft) by the CSU equation (HEC-18 Eq. 7.1):

ys = 2.0 K1 K2 K3 K4 y1 (a/y1)^0.65 Fr1^0.43

K1 nose shape, K2 angle of attack, K3 bed condition (1.1 typical plane bed), K4 coarse-bed armoring.

civilpy.water_resources.scour.pier_scour_from_boring(borehole, streambed_depth_ft: float, approach_velocity_fps: float, approach_depth_ft: float, pier_width_ft: float, pier_length_ft: float | None = None, skew_deg: float = 0.0, shape: str = 'round', k3: float = 1.1) PierScourResult[source]

Local pier scour using the bed gradation from a boring log.

Reads D50 (and D95 for armoring) from the particle-size analysis nearest streambed_depth_ft in borehole and applies the CSU equation. K1 comes from shape (HEC-18 Table 7.1), K2 from the pier geometry and skew_deg when pier_length_ft is given, and K4 from coarse-bed armoring when the gradation is coarse enough.

Module contents

Water resources engineering package.

Open-channel hydraulics (open_channel), pressurized pipe flow (pipe_flow), bridge scour (scour), and ODOT culvert headwall standards (hydraulics).