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.

Bars by state of high-hazard dams rated, with the poor or unsatisfactory part highlighted: North Carolina 246 of 1,484, Texas 217 of 1,167, Georgia 192 of 505, New Mexico 145 of 230. Bars by state of high-hazard dams rated, with the poor or unsatisfactory part highlighted: North Carolina 246 of 1,484, Texas 217 of 1,167, Georgia 192 of 505, New Mexico 145 of 230.
Poor or unsatisfactory high-hazard dams by state.

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.

Map of the lower 48 with high-hazard dams in grey and those in poor or unsatisfactory condition in red, clustered in the Northeast, the Carolinas and Georgia, and parts of the South. Map of the lower 48 with high-hazard dams in grey and those in poor or unsatisfactory condition in red, clustered in the Northeast, the Carolinas and Georgia, and parts of the South.
High-hazard dams in poor or unsatisfactory 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.

Horizontal bars of operational status for poor or unsatisfactory high-hazard dams: 1,938 blank, 404 enforcement pending or ongoing, 272 in investigation or design, 135 normal operations, 39 under remediation. Horizontal bars of operational status for poor or unsatisfactory high-hazard dams: 1,938 blank, 404 enforcement pending or ongoing, 272 in investigation or design, 135 normal operations, 39 under remediation.
What the inventory says is being done.