⚡ How VoltCast works
Predicting how much electricity four US grid regions will need over the next 24 hours, the same way you'd forecast weather but for power demand.
← Back to the dashboardThe problem
Every hour, grid operators have to decide how much power to generate. Too little means blackouts. Too much wastes money and fuel. VoltCast looks at the last week of demand and predicts the next 24 hours for California, Texas (ERCOT), the Mid-Atlantic (PJM), and the Midwest (MISO).
The pipeline
Data flows one direction: from the government API to the chart you see. Each step hands off to the next.
- 1Pull the data. The EIA (US Energy Information Administration) publishes real hourly demand per region. We pull a rolling 5-year window into Parquet files.
- 2Clean it. Empty values, impossible numbers, and sudden one-hour glitches are blanked out, then every missing hour is filled: short gaps with a straight line, long gaps with the same hour one week earlier. A Pandera check confirms one clean value per hour before anything reaches the model.
- 3Engineer features. Raw megawatts plus context: sin/cos encodings of hour, day, and month (so hour 23 sits next to hour 0), a weekend flag, and the load 24h and 168h ago. The calendar features use each region's local clock, since people use power by the local time. Then z-score normalize with a scaler fit on the training years only — no peeking at the future. The scaler is saved with the model so forecasts always use the exact scaling it was trained with.
- 4Slide a window. The long time series becomes thousands of training examples: each is 168 hours of input mapped to the 24 hours that follow.
- 5Predict. The champion model takes the latest 168 real hours and outputs all 24 future hours in one shot. No feeding predictions back in.
- 6Publish. Forecasts land in S3 as JSON. This dashboard reads them through a server route that holds the AWS keys, so the bucket stays private and the browser never touches AWS.
The model
A Temporal Transformer, written from scratch in PyTorch. No HuggingFace, no Trainer abstractions. It learns which past hours matter most for each prediction. To forecast 9pm Friday, it can learn to look hard at 9pm last Friday.
- Input projection maps the 10 features into a 64-dim internal space.
- Positional encoding tells the model where each hour sits in the sequence, since attention has no built-in sense of order.
- Two attention layers (4 heads each) weigh the relationships between hours.
- Output head turns the last timestep into 24 predicted megawatt values.
To prove it earns its place, every region also trains an LSTM and is compared with two naive baselines: "copy yesterday" and "copy last week". The better of the two neural models on a held-out validation year becomes the champion for that region. On a final, untouched test year the champions beat the best naive baseline by 16% to 37%, depending on the region.
The serverless loop
Nothing runs 24/7. There is no API server. Compute is ephemeral GitHub Actions runners, storage is S3 and DagsHub, the dashboard is on Vercel. Cost is close to zero.
- Daily: pull the last three weeks of data and regenerate every region's 24-hour forecast.
- Weekly: check whether each champion still forecasts well. We replay its last 60 days of forecasts against what actually happened and compare with "copy yesterday". If it no longer beats that baseline by about as much as it did on the test year, or if it is more than 30 days old, it is retrained. A challenger only replaces the champion if it beats it by 1% on the same held-out validation year.
- Tracked: MLflow on DagsHub logs every run's hyperparameters, metrics, and model file, with a champion/challenger registry deciding what ships.
Reading the numbers
- MAE (mean absolute error) is the headline metric: on average, how many megawatts the forecast is off by. Lower is better.
- WAPE expresses that error as a percentage of total demand, so regions of different sizes compare fairly.
- All times are UTC. The dataset is UTC end to end, so the chart and table avoid any timezone guessing.