Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔌 GreenGrid UK — When is Britain's electricity greenest?

Live app: https://greengrid-uk-4hou3jvgi9rebmisrvspmh.streamlit.app/

The same kilowatt-hour can cost 5× more CO₂ depending on when you draw it from the GB grid: a windy night might run at ~50 gCO₂/kWh while a calm winter evening peak runs at 300+. GreenGrid predicts the grid's carbon intensity up to 48 hours ahead and tells you the cleanest time to charge an EV, run a washing machine, or cook dinner — turning the UK's net-zero transition into a decision anyone can act on tonight.

This matters at scale: shifting one EV charge into the cleanest window saves kilograms of CO₂ per charge, the behaviour smart tariffs (Octopus Agile, Intelligent Go) already pay for — so the green slot is usually the cheap slot too.

The data

NESO Historic GB Generation Mix — the National Energy System Operator's own published record of the grid:

  • 305,711 half-hourly rows, January 2009 → present (refreshed continuously)
  • generation by fuel (gas, coal, nuclear, wind, solar, …), absolute and % share
  • computed carbon intensity in gCO₂/kWh — our prediction target

The live app additionally pulls recent actuals from the official Carbon Intensity API to build its lag features at prediction time.

Cleaning decisions (full audit in notebook 01)

Issue checked Found Decision
Nulls / duplicate timestamps 0 / 0 nothing to impute
Out-of-order rows 2 (a Dec-2025 append glitch) sort by DATETIME
Gaps in the half-hourly series 0 after sorting no imputation needed
Implausible target values 0 (range 19–644 is physically real) no clipping — trimming "outliers" would erase the real coal era
Regime change coal fell 45% → 0% of generation train on 2017+ only — a model that learns coal-era physics predicts a grid that no longer exists

These checks are encoded as assertions in src/data.py, so a future NESO refresh that breaks any guarantee fails loudly instead of silently corrupting the model.

What the data told us (notebook 01)

  • The grid decarbonised 3.6× since 2009 (445 → ~125 gCO₂/kWh yearly average) — wind, solar and biomass replaced coal entirely and half of gas.
  • The daily cycle is the product: overnight trough (~02:00–05:00) and midday solar dip vs a dirty evening peak (17:00–20:00). Median within-day spread in 2024–26 is ~90 gCO₂/kWh — worth ~4.6 kg CO₂ per 50 kWh EV charge.
  • Weekends and summer run cleaner (lower industrial demand; solar) — calendar features carry real signal.
  • Wind is the #1 lever: wind share correlates −0.75 with carbon intensity, and wind regimes persist across days — which is why lagged wind share helps at a 48h horizon.

The models (notebook 02)

Leakage rule: the app forecasts up to 48h ahead, so every feature must be knowable 48h before the target — all lags look back ≥48h (3h-smoothed; see below for why).

Engineered features: cyclical hour/month encodings, weekend/UK-bank-holiday flags, 3h-smoothed carbon-intensity lags (48h, 1 week), a 24h rolling "grid regime" mean, and lagged wind & solar shares. Business rationale for each is in src/features.py.

Split by year (never shuffled): train 2017–2024, validation 2025, test 2026.

Model (test 2026) MAE ↓ RMSE ↓ green-window hit rate ↑ green-window regret ↓
persistence (48h ago) 55.3 69.0 0.38 26.1
climatology (seasonal clock) 56.9 70.6 0.61 17.3
Ridge 48.5 59.1 0.46 22.2
LightGBM 49.2 61.0 0.43 23.4
GreenBlend (shipped) 50.5 62.6 0.54 19.4

Why these metrics — and the finding worth presenting

MAE/RMSE (in gCO₂/kWh, the units users see) measure curve accuracy. But the product's actual job is picking the right hours, so we also measure:

  • green-window hit rate — does the predicted greenest 3h window of each day overlap the actual one?
  • green-window regret — how many extra gCO₂/kWh you pay following our window instead of the hindsight-perfect one (the true product cost).

The accuracy metric and the business metric disagreed about which model to ship. The lag-driven models win on MAE but inject 48h-old shape noise into the daily curve, while the humble seasonal average nails the shape (when the trough falls) and misses the level. Shipping on MAE alone would have shipped the worse product. The resolution is GreenBlend = 0.6·climatology + 0.4·LightGBM, with the weight chosen on the 2025 validation year against regret — never on test data. It beats both baselines on MAE and recovers most of climatology's window-picking skill; pure climatology still edges the window metrics, which we report openly — the fix is weather-forecast features (see Production notes).

The app

Streamlit app (app.py): pick an appliance, how long it runs, and how long you can wait — the model forecasts the next 48h, highlights your cleanest slot, and quantifies the CO₂ saved vs starting now, alongside the live grid mix. If the live API is down, the app degrades to seasonal-average forecasts with a warning instead of crashing.

Repository structure

├── app.py                       # Streamlit app
├── requirements.txt
├── data/                        # raw data not committed — see data/README.md
├── models/                      # trained artifacts + metrics.json (small, committed)
├── notebooks/
│   ├── 01_eda_cleaning.ipynb    # data audit, cleaning decisions, EDA insights
│   └── 02_features_modeling.ipynb  # features, models, metric story, error analysis
└── src/
    ├── data.py                  # download + clean (with assertions)
    ├── features.py              # feature builder shared by training AND the app
    ├── live.py                  # Carbon Intensity API client
    └── train.py                 # full training pipeline -> models/ + metrics.json

How to run

git clone https://github.com/jadstrike/greengrid-uk && cd greengrid-uk
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

python src/train.py      # downloads the NESO data (~60MB) and reproduces models/ + metrics
streamlit run app.py     # local app

Deployment

Deployed free on Streamlit Community Cloud: https://greengrid-uk-4hou3jvgi9rebmisrvspmh.streamlit.app/ (connect the repo at share.streamlit.io, main file app.py). The committed models/ artifacts mean the cloud app needs no training step.

What would break in production

  • Schema/regime drift: NESO column changes are caught by src/data.py assertions, but a grid regime change (new interconnectors, mass storage) would silently age the model — needs scheduled retraining (the pipeline is one command) and monitoring of live MAE.
  • Single upstream dependency: the Carbon Intensity API is unauthenticated with no SLA; the app already degrades to climatology, but a product would cache recent actuals server-side.
  • The known gap: without wind/solar forecasts the model cannot beat the seasonal clock at ranking hours within a day. NESO publishes day-ahead wind forecasts — the single highest-value next feature.
  • Honest-metric risk: optimising MAE alone would regress the product (see above) — any retraining must gate on regret, not just accuracy.

Data attribution

Contains data from the National Energy System Operator (NESO) and the GB Carbon Intensity API (NESO/Oxford), used under their open data licences. Not affiliated with NESO.

About

Predict GB grid carbon intensity 48h ahead and find the greenest time to charge your EV — NESO open data, LightGBM + climatology blend, live Streamlit app

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages