County-level accountability for the data-centre electricity cost fight — where every assumption is a toggle you can flip.
Three credible institutions have looked at whether data centres are raising electricity costs for everyone else, and reached what appear to be opposite conclusions. wattprint is a versioned ETL pipeline that reproduces both sides on one dataset, at county level, with the methodology in the open and every contestable choice exposed as a setting rather than buried in code.
Status: v0.1 — the county-year price panel, with full provenance. The two estimators land in v0.2 and v0.3. What is built works end to end and is validated against published figures; what is not built is marked as not built.
| Who | Claim | What it actually measures |
|---|---|---|
| Monitoring Analytics (PJM's Independent Market Monitor) | Data centres drove $6.3B of ~$16.4B in 2028/29 capacity charges — 38%; $29.4B of $63.6B across four auctions | Forward capacity procurement, 2025–29, on a scarce grid |
| E3 (commissioned by the Data Center Coalition, May 2026) | No quantitative evidence of historical cross-subsidy; ~half the PJM price rise is load growth, half is market design and retirements | Literature review + auction analysis |
| EPRI (Watten, Bistline & Blanford, June 2026) | Each doubling of data-centre capacity → ~3.5% decrease in retail rates; rates would be ~6% higher absent the 2019–24 buildout | Realised retail rates, 2015–24, on a surplus grid |
EPRI and the IMM are not contradicting each other. They are measuring different quantities in different regimes.
On a surplus grid, new load spreads fixed costs over more kWh and average rates fall. On a scarce grid, new load climbs a steep supply curve and procurement costs rise. Both can be true, in sequence, on the same system.
That reframing is cheap to assert and hard to demonstrate. wattprint aims to demonstrate it: estimate the rate effect on rolling windows, overlay the forward capacity result on the same axis, and then test it as a coefficient rather than a picture —
log(rate) = β₀·log₂(DC) + β₁·[log₂(DC) × reserve_margin] + county FE + year FE + ε
H₀: β₁ = 0. Rejecting it is the formal statement of "both findings are right, in
different regimes." That is the most defensible contribution this repo can make,
and it arrives in v0.3.
Three of the most-quoted figures in this fight belong to three different PJM delivery years, and coverage routinely conflates them:
| Delivery year | Clearing price ($/MW-day) | Announced |
|---|---|---|
| 2024/2025 | 28.92 | 2023-07 |
| 2025/2026 | 269.92 | 2024-07-30 |
| 2026/2027 | 329.17 (at collar cap) | 2025-07-22 |
| 2027/2028 | 333.44 (at collar cap) | 2025-12-17 |
| 2028/2029 | 325.00 | 2026-07-14 |
- $333.44/MW-day is the 2027/2028 auction, not 2025/2026.
- The ~833% jump that started the fight is $28.92 → $269.92, 2024/25 → 2025/26.
- ~5,250 MW of forecast load growth, ~5,100 MW of it data centres, is the 2027/2028 forecast versus 2026/2027.
- $6.3B is the 2028/2029 auction — a different auction from the $333.44 one — and it is the IMM's figure, not PJM's. The two publicly disagree.
A trap worth knowing: 134,479 MW × $333.44 × 365 and 138,318 MW × $325.00 × 365 both come to roughly $16.4B, so sources citing "$16.4 billion" for different auctions look contradictory but are not.
This series is pinned as a test fixture
(tests/fixtures/pjm_bra_clearing_prices.yaml),
so a wrong attribution fails a test rather than shipping.
County-level retail electricity price does not exist in any dataset.
EIA-861 publishes revenue and sales at utility × state × sector. A separate table lists which counties each utility serves, with no weight attached. Every county rate in this repo — and in anyone else's — is constructed by choosing a weight and splitting the state total.
wattprint's response is not to hide that but to make it the primary control:
allocation.weight(population / customers / area / equal) is the headline toggle.gold_allocation_sensitivitypublishes the min/max/spread across every scheme for every county-year. No county rate is presented without its band.- Within one utility, every county gets an identical rate by construction, so
the effective unit of variation is
utility × state × year. Standard errors cluster at the utility, and any regression reports effective N beside raw N.
How much does the choice actually matter? Population vs land-area weighting, 30,983 county-years:
| Identical | >1% apart | >10% apart | Median diff | p95 | Max |
|---|---|---|---|---|---|
| 15.4% | 47.7% | 2.3% | 0.91% | 7.05% | 148% |
About 1% for a typical county — with a real tail.
Yes, and the gaps are explained rather than smoothed.
Rate construction vs EIA's published 2023 averages, before any county
allocation. EIA reports energy and delivery service types describing the same
electrons (identical MWh to the digit); naively summing all three understates the
rate by ~13%.
| Sector | wattprint | EIA published |
|---|---|---|
| Residential | 16.01 ¢/kWh | 15.98 |
| Commercial | 12.96 | 12.75 |
| Industrial | 8.21 | 8.06 |
| All sectors | 12.92 | 12.72 |
The county panel re-aggregates to 1.4–3.8% above EIA's published national average — the same direction every year. That offset is fully accounted for: the panel places 88.5–91.7% of national residential MWh into counties, and the unplaced remainder is 60.9% Retail Power Marketers and 11.2% Community Choice Aggregators — competitive suppliers who own no wires and therefore have no service territory. Their sales are ~75% energy-only and average 10.4 ¢/kWh against 14.3 ¢/kWh for placed sales.
So the offset is definitional: a wattprint county rate is a bundled,
incumbent-utility rate. In restructured states it is not what a customer who
shops actually pays. gold_panel_coverage publishes this per year.
Full detail: docs/REPLICATION_LOG.md.
Requires Python 3.11+. No Docker, no orchestration tooling, no API keys — every v0.1 source is public and unauthenticated.
git clone https://github.com/Akasi132/wattprint
cd wattprint
python -m venv .venv
.venv\Scripts\activate # Windows; source .venv/bin/activate elsewhere
pip install -e ".[dev]"
wattprint doctor # is every source still where we think it is?
wattprint fetch --all # ~6.6 MB, content-addressed, provenance logged
wattprint build --scenario baseline
wattprint show gold_county_price_panelThen flip the main assumption and watch the answer move:
wattprint build --scenario allocation_area
wattprint diff baseline allocation_areaTrace any number back to the bytes it came from:
wattprint lineage gold_county_price_panel --scenario baselineEach is a small readable diff against baseline via extends:.
| Scenario | What it is for |
|---|---|
baseline |
Population-weighted residential rates, 2015–2024 |
allocation_area |
Land-area weighting — a deliberately poor proxy, to bound sensitivity |
allocation_equal |
Every served county weighted equally — the null hypothesis of allocation |
epri_replication |
EPRI's window and state-level clustering, for like-for-like comparison |
imm_replication |
The IMM's forward-capacity framing |
adversarial_e3 |
Tuned to make this repo's own likely conclusion look weakest |
adversarial_e3 is not decoration. If wattprint looks built to reach a
predetermined answer it is worthless as a referee, so the strongest version of the
case against our result ships in the repo, runs in CI, and is displayed next to the
baseline.
Three mechanisms, all mechanical:
Content-addressed raw + committed ledger. Downloads land at
data/raw/<source>/<sha256[:12]>/. Re-fetching unchanged bytes is a no-op; changed
bytes land beside the old ones. data/raw/manifest.jsonl is committed — the bulk
data is not, the ledger describing it is. This is how you learn PJM quietly
reissued a report.
Deterministic stages. Every stage is a pure function of declared inputs and the scenario — no I/O, no clock, no network inside a stage body. Verified by hashing frame content (not Parquet bytes, which embed writer metadata).
Lineage beside every output. _lineage.json records input hashes, raw
checksums, package versions, git SHA, and code_dirty — a result built from an
uncommitted tree is labelled as such everywhere it is displayed.
The assumption boundary is the load-bearing design decision: bronze and silver contain zero assumptions; only gold depends on the scenario. Silver stores every allocation weight side by side as columns, so switching the primary toggle is a column selection rather than a full ETL rerun — gold rebuilds in under a second, which is what makes precomputing hundreds of scenarios for a static site feasible. A test asserts no non-gold stage reads the scenario, because a silver stage that did would keep serving a stale table after the assumption changed.
| Source | License | Role |
|---|---|---|
| PUDL (Catalyst Cooperative) | CC-BY-4.0 | EIA-861 service territories + retail sales. Already normalises county FIPS and carries population/area weights. |
| US Census Gazetteer | Public domain | Pins the county FIPS vintage |
| Epoch AI (v0.2) | CC-BY | ~75 largest AI data centres with capacity — a sample, not a census |
| PJM RPM (v0.3) | PJM ToS | Base Residual Auction results |
| IM3 Data Center Atlas (v0.3) | ODbL | OSM-derived siting. Share-alike, so opt-in only |
Deliberately excluded, with reasons recorded in config/sources.yaml and enforced
by a test: all FERC data (Form 1's taxonomy moved in March 2026 across ~116
poorly-normalised tables; Form 714 is balancing-authority grain; eLibrary has no
public API), and the commercial data-centre directories whose terms prohibit
scraping (Baxtel, datacenters.com, DataCenterMap, ringmast4r/Global-Data-Center-Map).
Code is MIT. Data is not — see LICENSE-DATA.md. No bulk
data is committed, which is a license boundary rather than a size preference:
IM3 is ODbL share-alike, and committing ODbL-derived tables into an MIT repo would
attempt to relicense them. License class propagates automatically through lineage
(strictest input wins), and tests/test_licenses.py fails the build if data is
ever tracked.
- v0.1 ✅ County-year price panel, provenance, contracts, allocation sensitivity, coverage accounting
- v0.2 Epoch AI siting → treatment; EPRI-style panel regression (Spec A state-level, Spec B county-level); self-contained HTML report
- v0.3 PJM BRA parser + VRR reconstruction + capacity counterfactual swept over supply elasticity; the regime-interaction test; IM3 opt-in; GitHub Pages site with client-side toggles
- v0.4+ Hourly PJM load; ERCOT; shift-share IV; EIA's data-centre survey once it exits pilot (after 2026-09-30)
- docs/KNOWN_BIASES.md — every limitation that could change how you read a number. Test-enforced.
- docs/REPLICATION_LOG.md — where our numbers land against published figures, including the gaps.
- SPEC.md — the methodology, pre-registered before the pipeline was run.
Kasi, A. (2026). wattprint: county-level data center energy accountability. https://github.com/Akasi132/wattprint
Please cite the underlying data too — PUDL (CC-BY-4.0, DOI 10.5281/zenodo.3653158) and the US Census Bureau.