How to Find High-Hazard Dams in Poor Condition
On this page
Problem statement
A dam whose failure would probably kill people and whose last assessment found it in poor or unsatisfactory condition is the combination dam-safety officials worry about. The National Inventory of Dams holds both fields, so the dams can be listed and counted by state, but the count says as much about each state's reporting as about its dams: some states rate nearly every high-hazard dam, others release almost no ratings. This page finds the high-hazard dams in poor or unsatisfactory condition, counts them against the dams each state has rated, and checks their age and whether they have an emergency action plan.
Quick answer
Filter on both fields, then count by state alongside how many high-hazard dams each state has rated at all:
high = nid[nid.publicHazardId == "High"].sort_values("isAssociatedStructureId").drop_duplicates("nidId")
rated = high.conditionAssessId.isin(["Satisfactory", "Fair", "Poor", "Unsatisfactory"])
bad = high.conditionAssessId.isin(["Poor", "Unsatisfactory"])
st = pd.DataFrame({"high": high.groupby("state").size(), "rated": rated.groupby(high.state).sum(), "poor_or_unsat": bad.groupby(high.state).sum()})
st["share_of_rated"] = st.poor_or_unsat / st.rated
print(st.sort_values("poor_or_unsat", ascending=False).head(6).round(3).to_string())
print(f"high-hazard dams: {len(high):,}; rated: {rated.sum():,}; poor or unsatisfactory: {bad.sum():,}")
high rated poor_or_unsat share_of_rated
state
North Carolina 1664 1484 246 0.166
Texas 1644 1167 217 0.186
Georgia 538 505 192 0.380
South Carolina 652 622 162 0.260
New Mexico 251 230 145 0.630
Pennsylvania 781 740 145 0.196
high-hazard dams: 16,492; rated: 13,042; poor or unsatisfactory: 2,790
Counting each NID id once, 16,492 dams are high hazard, 13,042 of them rated, and 2,790 rated poor or unsatisfactory. North Carolina and Texas have the most, 246 and 217, but they also have the most high-hazard dams. As a share of rated dams, New Mexico stands out: 145 of its 230 rated high-hazard dams, 63.0%, are poor or unsatisfactory, against 16.6% in North Carolina.
Ratings and their gaps
1. States that rate few dams
low = st[st.high >= 100].assign(rated_share=lambda d: d.rated / d.high).sort_values("rated_share")
print(low.head(5)[["high", "rated", "rated_share"]].round(3).to_string())
high rated rated_share
state
Illinois 251 3 0.012
Alabama 216 15 0.069
Missouri 1476 484 0.328
Tennessee 250 148 0.592
Arkansas 190 119 0.626
Illinois has 251 high-hazard dams and has rated 3 of them. Alabama has rated 15 of 216 and Missouri a third of its 1,476. A state with few poor dams on this list may simply be a state that publishes few ratings. Compare states on the share of rated dams, and show how many were rated.
2. How old the assessment is
age = 2026 - high[bad].conditionAssessDate.dt.year
print(f"assessment year known: {age.notna().mean():.1%}; median age {age.median():.0f} years; older than 5 years: {(age > 5).mean():.1%}")
assessment year known: 93.4%; median age 3 years; older than 5 years: 22.8%
93.4% of the poor or unsatisfactory ratings have a date. The median one is 3 years old, but 22.8% are more than 5 years old, and a dam may have been repaired since. A rating is the state of the dam when it was assessed, not today.
3. Age of the dams
y = high.yearCompleted.where(high.yearCompleted > 0)
print(f"median completion year, poor or unsatisfactory: {y[bad].median():.0f}; satisfactory: {y[high.conditionAssessId == 'Satisfactory'].median():.0f}")
median completion year, poor or unsatisfactory: 1955; satisfactory: 1964
Poor and unsatisfactory high-hazard dams are older, with a median completion year of 1955 against 1964 for satisfactory ones. Nine years is not a large gap, so age alone doesn't predict condition.
Plans and remediation
The data used on this page
The US Army Corps of Engineers' National Inventory of Dams (NID), the whole inventory as a GeoPackage from its API, downloaded on 19 October 2026 and saved on this site, because the inventory is updated as states and agencies send revisions. Heights are in feet, storage in acre-feet and areas in acres, as the NID gives them.
from pathlib import Path
import zipfile
import geopandas as gpd
import numpy as np
import pandas as pd
import requests
Path("data").mkdir(exist_ok=True)
# US Army Corps of Engineers, National Inventory of Dams: the whole inventory as a GeoPackage, and the Census
# Bureau's 2024 cartographic boundary counties. Saved 19 October 2026, because the inventory is updated continuously.
# Live: https://nid.sec.usace.army.mil/api/nation/gpkg
ARCHIVE = "usace-nid-2026-10-19.zip"
if not Path(f"data/{ARCHIVE}").exists():
Path(f"data/{ARCHIVE}").write_bytes(requests.get(f"https://data.spatialworkflow.io/{ARCHIVE}", timeout=900).content)
Z = zipfile.ZipFile(f"data/{ARCHIVE}")
if not Path("data/nid.gpkg").exists():
Z.extract("nid.gpkg", "data")
NUMERIC = ["damHeight", "hydraulicHeight", "structuralHeight", "nidHeight", "damLength", "volume", "yearCompleted",
"nidStorage", "maxStorage", "normalStorage", "surfaceArea", "drainageArea", "maxDischarge"]
nid = gpd.read_file("data/nid.gpkg")
nid[NUMERIC] = nid[NUMERIC].apply(pd.to_numeric, errors="coerce") # the GeoPackage stores them as text
print(f"National Inventory of Dams: {len(nid):,} rows, {nid.nidId.nunique():,} NID ids")
National Inventory of Dams: 92,626 rows, 91,840 NID ids
Example 1: emergency action plans
print(pd.crosstab(high.conditionAssessId.fillna("blank"), high.eapId.fillna("blank")).loc[["Satisfactory", "Fair", "Poor", "Unsatisfactory"]].to_string())
eapId No Not Required Yes blank
conditionAssessId
Satisfactory 234 20 4868 1
Fair 843 72 4210 4
Poor 511 106 1672 0
Unsatisfactory 119 17 365 0
Most poor and unsatisfactory high-hazard dams have an emergency action plan, 1,672 and 365. But 511 poor and 119 unsatisfactory dams have none, the combination of a probable loss of life, a dam in poor shape and no written plan for warning people.
Example 2: what is being done
print(high[bad].operationalStatusId.fillna("blank").value_counts().to_string())
operationalStatusId
blank 1938
Enforcement Pending/Ongoing 404
Under Investigation, Planning, Permitting, or Design for Remediation 272
Normal Operations 135
Under Remediation 39
Not Applicable 2
For 1,938 of the 2,790 the operational status is blank. Where it is filled in, 404 have enforcement pending or ongoing, 272 are in investigation, planning or design for repair and 39 are under remediation. Only 135 are recorded as in normal operations.
Internal links
- Hazard potential explained β why hazard and condition are separate
- Dams by county β where the high-hazard dams are
- Dam age β when they were built
- Bridges in poor condition β the same question for bridges