Plating Power Optimizer

Cutting demand charges and energy costs at a mid-size SPP-grid metal finisher — measured against 12 months of real wholesale prices.

$—
Annual savings, 12mo backtest i
Model WMAPE: — i

Tomorrow's schedule i

kW demand by hour, based on the latest 48h price forecast.

Demand charge: before vs after

"Before" = realistic baseline: jobs spread across the scenario's plating lines (1 for Small, 2 for Mid, 3 for Large, 5 for Multi-plant) starting at shift open, with no demand-management thinking. That's what plants actually look like today.

Realistic baseline

Optimized

— kW
Baseline peak i
— kW
Optimized peak i
$—/mo
Demand cost saved i

Savings breakdown i

Two stacked layers make up the total. Each one is real money, computed against the same 12 months of SPP prices.

$—
Job scheduling (greedy)
running plating jobs in cheap hours, peak-flattened
$—
Continuous-load shifting
chiller + DI/RO water + compressed air, 60% shifted off-peak

Continuous loads in this scenario i

The 24/7 background equipment that sets the floor of your demand profile. Sized for the selected scenario.

Shift breakeven calculator

Would extending the day shift to capture cheap overnight hours pay for itself?

$—
Net annual benefit of extending shift i

How it works

Methodology in plain English

What it does

Tells you when to run plating loads during the day to minimize two costs at once: the kWh charge (when wholesale electricity is cheap) and the demand charge (your peak kW during the month).

The data

  • SPP wholesale prices — gridstatus library, 24 months hourly LMP at the SPP North Hub node.
  • Retail rate — LES 2026 industrial tariff: $15.03/kW demand at 115kV.
  • Weather — NOAA NCEI daily temperature at KLNK (feature for the forecast).
  • System demand — EIA RTO hourly demand for SWPP (feature for the forecast).
  • Operator wage — BLS OEWS SOC 51-4193, Lincoln NE MSA median.

The model

XGBoost regressor on hourly LMP with calendar + weather + demand + lag features. 80/20 chronological split. The metric above is WMAPE (weighted absolute percentage error: sum of errors divided by sum of actual prices) — the industry-standard metric for ISO LMP forecasting. WMAPE is used instead of plain MAPE because SPP LMP regularly dips below zero during high-wind hours, which breaks the percentage math in MAPE.

The schedulers

  • Greedy (recommended) — two-pass heuristic. Pass 1 places each job in its cheapest valid window. Pass 2 redistributes jobs out of peak hours when it lowers total cost. Beats the flat baseline on every scenario.
  • LP (V1) — formal optimization with binary decision variables. V1 minimizes energy cost only — it doesn't yet model the demand-charge non-linearity, so it lights up the cheapest hours without flattening peak. On a demand-charge tariff, this actually loses money vs greedy. V2 adds a piecewise constraint that pulls peak kW into the objective.

The backtest

For each of the last 365 days, both schedulers replay against that day's real SPP LMP. The savings number above is the year sum of (flat-baseline cost − optimized cost).

Receipts — every number, every row

Click into any of these to see exactly which rows produced the numbers on this page. Each table is straight from the source-of-truth Supabase database. Use the CSV button to pull a copy into Excel and verify the math yourself.

1 · Daily backtest results

Source: backtest_results table. One row per (date × scenario × scheduler). The hero annual-savings number above is the sum of the savings column for the selected scenario + scheduler across the rolling 12-month window. savings = realistic_baseline_cost − optimized_cost (the database column is still named flat_cost for legacy reasons but stores the realistic-baseline cost).

DateScenarioScheduler OptimizedRealistic baselineSavings EnergyDemandPeak kW

2 · Hourly SPP LMP (price spine)

Source: hourly_prices table. Pulled via the gridstatus library from SPP's day-ahead LMP feed at the SPP North Hub node. The forecast model is trained on this column. By default this shows the most recent 168 hours (one week).

Timestamp (UTC)Node$/MWhSource

3 · Model run history

Source: model_runs table. Every time the XGBoost model is retrained, a row is logged with the train window, the holdout test window, and the holdout WMAPE. The pill at the top of the page reads from the most recent row.

Trained at (UTC)WMAPERMSE Train windowTest windowArtifact

4 · Rate schedule applied

Source: rate_schedule table. Demand charge ($15.03/kW @ 115kV) is from the LES 2026 Industrial Rate Schedules PDF. Energy charges (cents/kWh by season + on/off-peak) are reasonable estimates pending the full PDF parse — the headline savings number depends almost entirely on the demand charge, not the energy charges.

FieldValue

5 · Workload definition (current scenario)

Source: config/workload_scenarios.json (committed to the repo). These are the jobs the scheduler places into the 48-hour planning window. Each scenario is a fixed list of jobs sized to hit the labeled daily MWh total. The scenario shown is whichever one you have selected in the top control bar.

Job IDkWh requiredDuration (h) Rated kWDeadline (h)Lights-out eligible

What's next (V2)

Want a heads-up when V2 ships? DM me on LinkedIn.