Skip to content

Repository files navigation

Edge-Driven Load Control for EV Charging

This workspace contains a rapid research prototype for local forecasting and edge orchestration of EV charging demand against on-site photovoltaic generation. It is the code release for a short paper submitted to the NextGCom 2026 EVOLVE special session.

What is included

  • A small package, evloadcontrol/, that:
    • discovers the EV charger columns present in the dataset and loads only the columns it needs (data.py),
    • resamples raw telemetry into 15-minute intervals and engineers the lag/rolling/cyclical features (features.py),
    • trains the XGBoost forecasters and the persistence/ridge baselines (models.py),
    • implements the deterministic edge load-control decision, its persistence-forecast counterfactual, and the grid-limit sensitivity sweep (orchestrator.py),
    • runs a moving block bootstrap to check whether XGBoost's edge over persistence survives temporal autocorrelation (significance.py),
    • computes the temporal-error and per-charger-utilization breakdowns (analysis.py),
    • plots the forecast traces and sensitivity curves (plotting.py),
    • and optionally trains a PyTorch MLP/LSTM/1-D CNN comparison against the same held-out split (torch_models.py), plus a second LSTM given XGBoost's own engineered features so the raw-sequence models face no input asymmetry, retraining each configuration across 5 seeds and reporting mean ± std. This last step is a strictly optional add-on, never imported by default.
  • Four notebooks in notebooks/, the recommended way to explore this project (see below).
  • A thin CLI entry point in pipeline.py (evloadcontrol/cli.py underneath) that runs the full pipeline end to end and writes metrics, plots, and model artifacts to outputs/. It is useful for automation or a real edge deployment, and not required for exploring the results.
  • Dependencies managed as a uv project (pyproject.toml + uv.lock).

Reproducibility: every stochastic step (XGBoost's row/column subsampling, the PyTorch initializations, the block bootstrap) uses the fixed seeds in evloadcontrol/config.py (SEEDS = (42, 43, 44, 45, 46)), so the CLI and notebooks reproduce the paper's tables and figures exactly, not just approximately.

Seeds and what the paper reports: every XGBoost number the paper prints is a mean over all five seeds, including the accuracy metrics, the feature ablation, the hourly error breakdown, the gain importances, and the Fig. 1 window errors. A few artifacts have no meaningful average and come instead from REFERENCE_SEED (42): the saved model files, the traces Fig. 1 plots, the paired predictions the block bootstrap resamples, and the controller event log, whose throttle counts are integers the paper reasons about individually. For those, summary.json also records the across-seed spread (throttle_seed_spread) so the reference run can be told apart from a seed artifact.

Dataset note

This repository does not include the raw telemetry CSV. The UMA Adabyron dataset is private and available on request from its maintainers, not redistributed here. To run the pipeline, place your own export (or a compatible CSV with the same column naming convention) at data/20260514_uma_adabyron_data.csv, or pass a different path via --data-path. The pipeline discovers whichever EV charger columns are present in the header and only loads the columns it needs, so it adapts automatically to datasets with a different charger count.

Setup

Install uv (brew install uv on macOS), then from this directory:

uv sync --extra notebooks --extra torch

That resolves and installs the core pipeline plus the notebook and PyTorch extras into a local .venv, without needing to create or activate one by hand. Leave off --extra torch if you don't need the deep-learning comparison (it is never required for the default pipeline).

Notebooks (recommended)

notebooks/ is the easiest way to run this project: no CLI flags, each step's output is visible inline, and every notebook is pinned to the same fixed seeds as the paper, so re-running one reproduces the paper's numbers exactly.

uv run --extra notebooks jupyter lab notebooks/
  1. 01_data_exploration.ipynb loads the raw telemetry, resamples it, and looks at the PV/EV signals and their correlations before any modeling.
  2. 02_forecasting_and_baselines.ipynb trains the XGBoost forecasters, compares them against persistence/ridge and the deep-learning architectures (Table I), runs the block bootstrap significance test (Section IV-C), and reproduces the ablation study (Table IV) and Fig. 1.
  3. 03_orchestrator_and_sensitivity.ipynb runs the deterministic orchestrator, logs individual throttle events with genuine-risk flags, reruns the orchestrator on persistence forecasts to quantify what the forecaster buys the control loop (Section IV-F), sweeps the safe grid-limit parameter, and breaks down forecast error by time of day and by charger.
  4. 04_deep_learning_comparison.ipynb trains the PyTorch MLP/LSTM/CNN comparison and compares it against XGBoost's 5-seed mean on the same basis (requires --extra torch).

Each notebook is self-contained (it reloads and rebuilds whatever it needs), so they can be run independently and in any order. They import directly from the evloadcontrol package rather than duplicating logic.

CLI (for automation or edge deployment)

The same pipeline as a single script, useful for a real deployment or a CI job rather than interactive exploration:

uv run python pipeline.py --data-path data/20260514_uma_adabyron_data.csv

For faster iteration, limit the rows during development:

uv run python pipeline.py --data-path data/20260514_uma_adabyron_data.csv --max-rows 50000

To also run the PyTorch MLP/LSTM/CNN comparison against the same held-out split (requires the torch extra installed via uv sync --extra torch):

uv run python pipeline.py --data-path data/20260514_uma_adabyron_data.csv --include-torch

This is never required for the default pipeline. evloadcontrol.torch_models is only imported when --include-torch is passed, so a standard edge deployment does not need PyTorch installed.

Live demo

For presentations, live_demo.py (evloadcontrol/live_demo.py underneath) loads the already-trained models from a prior pipeline.py run and replays a short window of the held-out test split one interval at a time, single-sample inference (not a batch predict), timed and printed live with a short pause between intervals so it reads as a real-time stream on camera. By default it auto-selects a window centered on the largest genuine throttle event, the same data-driven-window principle plot_forecast_zoom uses, so it doesn't need a hand-picked date:

uv run python pipeline.py --data-path data/20260514_uma_adabyron_data.csv  # once, to produce outputs/model_*.json
uv run python live_demo.py

Useful flags: --grid-limit (default 1000 W, the stress case with more events to show), --window-intervals (how many 15-minute steps to replay), --delay (seconds between intervals, for pacing on camera), --start (an explicit index into the test split instead of auto-selection).

Outputs

The pipeline writes the following files to outputs/:

  • model_pv.json, model_ev.json are the trained XGBoost models.
  • feature_importance_pv.csv, feature_importance_ev.csv hold the per-feature gain importances as a mean and std across the 5 seeds. Gain shifts between near-duplicate features (current PV and its 15-minute lag) from seed to seed, so the per-feature std matters here as much as the mean.
  • test_predictions.csv holds actual vs. predicted values for both targets on the held-out split.
  • forecast_plot.png is the forecast trace over the full test horizon (each day is a sliver at this scale, so it works as a coverage sanity check but not for reading forecast quality).
  • forecast_plot_zoom.png shows the same traces over a two-week window chosen for typical (not spike) EV demand, readable at daily resolution. This is the paper's Fig. 1.
  • baseline_comparison.csv compares XGBoost, persistence, and ridge regression by MAE/RMSE per target. XGBoost's row is a mean/std over 5 seeds (its subsample/colsample_bytree < 1 make random_state a real variance source), while persistence and ridge are deterministic solvers with no seed dependence.
  • bootstrap_significance.csv holds the moving block bootstrap (24h blocks, 10,000 resamples) 95% CI on MAE(XGBoost) - MAE(persistence) per target, i.e. whether XGBoost's point-estimate edge over persistence survives temporal autocorrelation in the 15-minute series (Section IV-C).
  • baseline_comparison_with_torch.csv adds the PyTorch MLP/LSTM/CNN comparison and the engineered-feature LSTM variant to the above, each as mean/std MAE and RMSE over the same 5 seeds (only written with --include-torch).
  • ablation_study.csv compares the base (current-timestep only) feature set against the full engineered one, both sides averaged over the same 5 seeds so the ablation delta does not mix a mean with a single run.
  • grid_sensitivity.csv, grid_sensitivity_plot.png give throttle rate and mean per-EV reduction swept across safe grid limits.
  • hourly_error_breakdown.csv buckets forecast MAE by time of day, seed-averaged, with the across-seed std per bucket.
  • charger_utilization.csv reports per-charger total energy delivered and active-charging share over the full telemetry span.
  • orchestrator_events_<grid_limit>w.csv, orchestrator_events_1000w.csv hold one row per triggered throttle event at the run's --grid-limit and at a fixed 1 kW stress case, including a genuine_risk flag computed from actual (not forecasted) PV/EV values. The default grid limit triggers too rarely to assess control quality from the aggregate rate alone, so these logs let each event be checked individually instead of only counted.
  • summary.json is the consolidated run summary including all of the above, plus the persistence-forecast orchestrator counterfactual (throttle rate, raw decision-agreement rate, Cohen's kappa, and the agreement restricted to intervals where either controller acts, Section IV-F).

Research framing

The workflow is designed for a short paper on edge-deployed load control, with a focus on low-latency local forecasting, deterministic orchestration, and battery-less microgrid constraints.

License

Released under the MIT License.

About

Edge-deployed forecasting and deterministic load shaping for EV charging under battery-less microgrid constraints. Code release for the NextGCom 2026 EVOLVE Special Session paper.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages