How to Count US Dams by Purpose and Owner
On this page
Problem statement
Ask what America's dams are for and the answer depends on how you count. By number, most are small private dams for recreation, farm ponds and fire protection. By storage, a few hundred federal dams for flood control, hydropower and irrigation dominate. The National Inventory of Dams records a primary purpose, an owner type and a list of all purposes for each dam, separated by semicolons, so a dam that serves three purposes is counted once by its primary purpose and three times by its list. This page counts dams by purpose and owner, by number and by storage.
Quick answer
Count the primary purpose by number and by storage, side by side:
one = nid.sort_values("isAssociatedStructureId").drop_duplicates("nidId")
one = one[one.name != "Soo Locks"]
t = one.groupby("primaryPurposeId").agg(dams=("nidId", "size"), storage_maf=("nidStorage", lambda s: s.sum() / 1e6))
t["dam_share"], t["storage_share"] = t.dams / t.dams.sum(), t.storage_maf / t.storage_maf.sum()
print(t.sort_values("dams", ascending=False).round(3).to_string())
dams storage_maf dam_share storage_share
primaryPurposeId
Recreation 30901 37.511 0.359 0.044
Flood Risk Reduction 14968 424.517 0.174 0.497
Fire Protection, Stock, Or Small Fish Pond 11997 1.723 0.139 0.002
Irrigation 7620 38.764 0.089 0.045
Other 7443 17.526 0.086 0.021
Water Supply 5162 53.845 0.060 0.063
Fish and Wildlife Pond 3175 8.967 0.037 0.011
Hydroelectric 1755 224.972 0.020 0.263
Tailings 1160 11.199 0.013 0.013
Grade Stabilization 964 0.150 0.011 0.000
Debris Control 679 0.531 0.008 0.001
Navigation 225 34.205 0.003 0.040
By number, recreation dams are 35.9% of the inventory and stock and fish ponds another 13.9%, but together they hold under 5% of the storage. Flood risk reduction is 17.4% of dams and 49.7% of storage, and hydroelectric dams are 2.0% of dams but 26.3% of storage. The same table gives opposite answers to "what are dams for" depending on the column you read.
Primary purpose and all purposes
1. Splitting the purpose list
p = one.purposeIds.str.split(";").explode().str.strip()
print(f"dams with more than one purpose: {(one.purposeIds.str.count(';') > 0).sum():,}")
print(p.value_counts().head(8).to_string())
dams with more than one purpose: 17,286
purposeIds
Recreation 38371
Flood Risk Reduction 17608
Fire Protection, Stock, Or Small Fish Pond 15712
Irrigation 9416
Other 9064
Water Supply 7154
Fish and Wildlife Pond 5486
Hydroelectric 2102
17,286 dams list more than one purpose. Counted by every purpose they serve, recreation appears on 38,371 dams, compared with 30,901 as a primary purpose, and flood risk reduction on 17,608. A count over the exploded list totals more than the number of dams, so present it as "dams serving each purpose", never as shares that add to 100%.
2. Owners
o = one.groupby("primaryOwnerTypeId").agg(dams=("nidId", "size"), storage_maf=("nidStorage", lambda s: s.sum() / 1e6), high=("publicHazardId", lambda s: (s == "High").sum()))
print(o.sort_values("dams", ascending=False).round(1).to_string())
dams storage_maf high
primaryOwnerTypeId
Private 60172 69.4 7432
Local Government 18620 82.0 5398
State 6705 58.9 1570
Federal 3188 586.5 1071
Not Listed 1617 6.5 199
Public Utility 1510 55.8 804
Tribal Government 17 0.0 0
Private owners hold 60,172 dams but only 69.4 million acre-feet of storage. The federal government owns 3,188 dams with 586.5 million, more than everyone else combined, although that total includes the Soo Locks' Great Lake figure and repeated dike segments. Private owners also hold the most high-hazard dams, 7,432.
3. Who owns what
print(pd.crosstab(one.primaryPurposeId, one.primaryOwnerTypeId, normalize="index").loc[["Recreation", "Flood Risk Reduction", "Hydroelectric", "Irrigation", "Water Supply"], ["Private", "Local Government", "State", "Federal", "Public Utility"]].round(2).to_string())
primaryOwnerTypeId Private Local Government State Federal Public Utility
primaryPurposeId
Recreation 0.78 0.10 0.08 0.02 0.00
Flood Risk Reduction 0.17 0.59 0.18 0.05 0.00
Hydroelectric 0.47 0.12 0.04 0.04 0.25
Irrigation 0.87 0.05 0.02 0.04 0.01
Water Supply 0.39 0.44 0.04 0.03 0.08
Recreation and irrigation dams are overwhelmingly private, flood-control dams mostly local government, and water-supply dams split between towns and private companies. A quarter of hydroelectric dams belong to public utilities and nearly half to private companies.
Dam types and federal agencies
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: what dams are made of
print(one.primaryDamTypeId.value_counts(dropna=False).head(6).to_string())
primaryDamTypeId
Earth 77154
NaN 7774
Concrete 2287
Gravity 2141
Rockfill 792
Other 610
77,154 dams are earthfill, 84% of the dams counted once each: embankments of compacted soil, cheap and easy to build on small streams. Concrete, gravity and rockfill dams are a few thousand between them. 7,774 have no type recorded.
Example 2: federal owners
print(one.fedOwnerIds.str.split(";").explode().str.strip().value_counts().head(6).to_string())
fedOwnerIds
US Army Corps of Engineers 571
Forest Service 444
Bureau of Land Management 417
Bureau of Reclamation 337
US Army 228
Fish and Wildlife Service 219
Among federal owners, the US Army Corps of Engineers owns the most dams, 571, followed by the Forest Service, the Bureau of Land Management and the Bureau of Reclamation.
Internal links
- Ranking reservoirs by storage β the storage side of the counts
- Hazard potential explained β hazard by owner
- Dam age β purposes by decade
- Reading the inventory β the columns