Skip to content

Repository files navigation

Community Energy Flex

Community Energy Flex is a decision-support system for scheduling flexible household electricity tasks. It combines half-hourly carbon data, tariff inputs, task constraints, and rule-based or linear-programming optimisers to recommend feasible run windows with explicit baselines, provenance, and caveats.

It is a portfolio demonstration, not a control system or a savings guarantee. The case-study, Power BI, and synthetic stress-test figures use illustrative household data; they are not measured customer outcomes.

Open the web app · API documentation · Read the worked example

Community Energy Flex planner showing a 24-hour carbon and price view with a recommended flexible-load window

CI

What it demonstrates

  • A typed Python domain model for tasks, half-hour slots, tariffs, schedules, and baselines.
  • Rule-based and LP/MILP scheduling under time, energy, and overlap constraints. The MILP earns its place through one thing the rule-based path structurally cannot express: a peak-load limit that couples every task in the same half-hour slot.
  • A FastAPI contract that reports the actual carbon and price source, whether it is live, and any fallback reason.
  • A Next.js interface that keeps fallback/sample status visible instead of presenting it as live data.
  • A robustness indicator that describes sensitivity to current inputs. It is a heuristic, not a calibrated probability.
  • Conditional ex-post scenario analysis. Because task adherence is not observed, this is a synthetic stress test rather than realised-savings evidence.
  • Text, Excel, PDF, dbt, Dagster, and Power BI reporting paths. Snowflake is a bootstrap DDL script (warehouse/snowflake_setup.sql) plus a second dbt profile target; nothing in this repository has been built against a live Snowflake account. Status labels the whole warehouse row built-local / synthetic-demo.

Fresh action reports are blocked when optimisation fails or required provenance is absent. Deterministic fixture mode (CEF_FIXTURE_MODE=1) uses labelled synthetic-household inputs for CI and demonstrations; it requires no paid or live external API call. All dashboard screenshots and KPIs are illustrative, conditional ex-post where applicable, and not a savings guarantee.

Engineering choices

The MILP is a real MILP, not a renamed greedy loop. optimisation/linear_programming.py builds a pulp.LpProblem with one binary variable per (task, feasible start), a must-run-once constraint per task, per-task deadlines, and — the reason to reach for an LP at all — a peak-load constraint that caps the total kW drawn in any half-hour slot across every task at once. The rule-based optimiser schedules each task independently and cannot express that coupling. CBC solves it; any non-Optimal status raises InfeasibleScheduleError instead of returning a partial schedule. When the shared limit pushes a task off its standalone-best slot, the recommendation caps its robustness and says why. Six dedicated tests cover the coupling, the infeasible case, and parity with the rule-based optimiser when no shared limit applies. It is a library path under test, not a product surface: the API, the Streamlit app, and the daily pipeline all call the rule-based optimiser, and Status records the MILP as built-local.

The public claims are a CI gate. tests/test_public_claims.py reads the README, case study, retro, status, and both public web pages, and fails the build if retired claims reappear or if the README stops stating the synthetic-evidence boundary. Marketing copy is the surface most likely to drift ahead of the evidence, so it is tested like anything else.

One enforced data contract. fct_daily_savings sets contract: enforced: true with a declared data_type on all 33 columns, so a renamed column or a changed type fails dbt build rather than quietly reshaping the Power BI star. Four exposures — FastAPI, Next.js, Power BI, and the action report — are declared at maturity: high, so the downstream blast radius of a model change is in the lineage rather than in someone's head.

One fixture reconciles four runtimes. data/fixtures/reporting_contract_v1.json is consumed by the dbt seed generator, the Python tests, the API contract tests, and the Next.js client tests. The same reporting shape is therefore asserted in SQL, Python, and TypeScript from a single committed source.

Orchestration is deliberately thin. Three Dagster assets, one job, one schedule. The assets wrap pipeline/daily.py and hold no business logic, so the pipeline is unit-tested as plain functions and the scheduler stays swappable. See the Dagster pipeline notes.

CI runs the Python suite on 3.11, 3.12, and 3.13, alongside a typecheck and test pass on the Next.js client and a full DuckDB dbt build of the reporting contract.

Try it

The public endpoints keep their original hostnames; those URLs are stable deployment identifiers, not the current product name.

Surface URL Notes
Web app after-midnight-beta.vercel.app Next.js on Vercel
API community-energy-flex-api.fly.dev FastAPI on Fly.io

The API attempts the GB Carbon Intensity forecast for supported regions. If that source is unavailable, the response names the fallback profile and explains why it was used. Northern Ireland uses a labelled typical profile; it is not advertised as a live forecast.

Run locally

Python 3.11–3.13 is supported.

python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux: source .venv/bin/activate
pip install -e ".[dev,reports,api,app,optim]"
python -m pytest
python -m ruff check .

Run the API and web client in separate terminals:

uvicorn community_energy_api.main:app --app-dir api --reload
cd web
npm install
npm run dev

A Streamlit interface is also included:

streamlit run app/streamlit_app.py

Decision flow

flowchart LR
    C[Carbon curve] --> P[Half-hour planning slots]
    T[Tariff] --> P
    U[Task constraints] --> O[Rule-based or LP/MILP optimiser]
    P --> O
    O --> S[Recommended schedule]
    S --> R[Baseline comparison, robustness, caveat]
    R --> A[Web, API and reports]
Loading

For each task, the engine enumerates feasible starts, estimates cost and carbon for each window, applies the selected objective, and compares the recommendation with an explicit preferred-start baseline. Standing charges are excluded because moving a task does not change them.

The robustness indicator combines decisiveness, forecast horizon, data-source quality, and tariff quality. It is deliberately labelled as an indicator rather than confidence: it has not been calibrated against observed recommendation outcomes.

See Methodology for the equations and limitations.

Provenance and credibility boundary

The public contract exposes structured source metadata:

  • carbon_source, carbon_source_label, is_live_forecast, retrieval/valid timestamps, and fallback_reason;
  • price_source, price_source_label, price_is_live, and price_unavailable_reason;
  • supports_live_forecast as a regional capability flag, separate from whether a particular response actually used live data.

The synthetic retro workflow recomputes the scheduled and baseline windows against altered carbon curves. It does not observe whether a household followed the recommendation, so its outputs are named conditional ex-post savings and schedule_adherence_observed is false.

Project claims and evidence boundaries are tracked in the claim ledger. Current operating state and freeze scope are in status.

Power BI stakeholder dashboard

Community Energy Flex Power BI dashboard showing illustrative daily baseline and optimised cost, a cost-saving trend, and a synthetic-household KPI card

Community Energy Flex Power BI overview showing illustrative household KPIs for cost, carbon, avoided peak slots, scheduled tasks, and average robustness

Both dashboard images use synthetic-household, illustrative planning data. KPI differences are conditional ex-post where applicable and are not a savings guarantee. The model, DAX, theme, and reproducible seed path are in powerbi/; see the dashboard guide.

Repository map

Path Responsibility
src/community_energy_flex/ Domain model, data sources, optimisation, monitoring, reporting
api/community_energy_api/ FastAPI contract and service adapter
web/ Next.js public interface
tests/, api/tests/ Unit, contract, fallback, robustness, and export tests
dbt_energy/, warehouse/ Analytics transformations and Snowflake bootstrap
orchestration/ Dagster assets
powerbi/ Dashboard model, measures, theme, and guidance
docs/evidence/credibility-closeout/ Versioned closeout evidence

Documentation

Document Purpose
Status Current scope, limitations, and feature freeze
Claim ledger Public claims and their evidence status
Data sources Source semantics and fallback rules
Methodology Baseline, objectives, robustness, and conditional ex-post analysis
Architecture Module boundaries and request flow
Metric catalogue Units, signs, owners, and publication conditions
Reporting lineage Core, Dagster, dbt and consumer boundaries
Runbook Local operation and failure modes
Future research Work intentionally deferred beyond v0.2.0
Security Responsible disclosure

Project status

Version 0.2.1 continues the v0.2.0 credibility closeout; it remains a feature-freeze release, with post-release correctness and honesty fixes. Maintenance is limited to correctness, dependency and security updates, and deployment reliability; new research claims require new observed evidence and an explicit scope decision.

Product development stopped on 2026-07-27. An evidence scan found the scheduling function already served — by statute for EV charging, by supplier-dispatched tariffs, and by free tools built on the same two public APIs this project uses. The reasoning and the primary sources are recorded in status. One strand continues: a forecast-vintage archive capturing what the GB carbon-intensity forecast said and when, because that data cannot be recovered afterwards. It is a capture job answering a research question, not a product, and it has produced no result yet.

MIT © 2026 Cheng-Yuan King.

About

Community Energy Flex — decision-support for when to run flexible electricity loads to cut cost and carbon (live API + web).

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages