lugasraka/combustiontwin
CombustionTwin
A prototype digital twin and prescriptive setpoint optimizer for waste-to-energy reciprocating-grate combustion. It simulates plant telemetry, trains a PyTorch surrogate of the combustion process, and uses that surrogate to recommend primary air, secondary air, and grate speed setpoints.
Live demo
The studio runs on Hugging Face Spaces: https://huggingface.co/spaces/lugasraka/combustiontwin
It runs on ZeroGPU hardware, but all inference is CPU-class, so visitors consume no GPU quota.
How it works
- Synthetic SCADA simulator (
src/data/physics_simulator.py) generates 1-minute telemetry for a 4-zone grate line, including latent waste LHV and moisture, actuator settings, and flue-gas composition. - PyTorch models (
src/models/) define a temporal-attention virtual calorimeter and a physics-informed combustion surrogate. - Prescriptive optimizer (
src/optimization/prescriptive_engine.py) solves for setpoints with SciPy SLSQP. Each candidate setpoint is evaluated by rolling the surrogate forward over a 15-step horizon, tracking the steam target while the furnace stays above the legal temperature limit. - Gradio studio (
src/app/gradio_app.py) presents all of it in four tabs: live digital twin, model diagnostics, prescriptive sandbox, and MLOps drift monitoring.
flowchart LR
SCADA["GrateSimulator<br/>1-minute synthetic SCADA"]
FRAME["SCADA DataFrame<br/>telemetry + actuators + latent labels"]
SCADA --> FRAME
subgraph TRAIN["Optional training pipeline"]
CAL_DATA["CalorimeterDataset<br/>15-minute window x 12 features"]
CAL["VirtualCalorimeterLSTM<br/>LHV + moisture estimate"]
SUR_DATA["SurrogateDataset<br/>state 8 + control 6"]
SUR["CombustionDynamicsSurrogate<br/>settled response: 5 outputs"]
ART["artifacts/<br/>model weights + scalers"]
FRAME --> CAL_DATA --> CAL
FRAME --> SUR_DATA --> SUR --> ART
end
subgraph RUN["Runtime: Gradio studio"]
INPUT["Scenario inputs<br/>feedstock, feed rate, steam target"]
OPT["PrescriptiveOptimizer<br/>SciPy SLSQP, 15-step rollout"]
ENGINE{"Surrogate artifacts<br/>available?"}
NN["PyTorch surrogate"]
FALLBACK["Analytical surrogate<br/>fallback"]
RESULT["Dispatch recommendation<br/>primary air, secondary air, grate speed"]
INPUT --> OPT --> ENGINE
ENGINE -->|yes| NN
ENGINE -->|no| FALLBACK
NN --> RESULT
FALLBACK --> RESULT
end
ART -. loaded at startup .-> ENGINE
FRAME -. live telemetry .-> RUN
CAL -. optional diagnostic only .-> RESULTTrained weights ship in artifacts/ (deterministic, seed 42). If they are missing, the app falls back to an analytical surrogate so it always launches.
Project layout
configs/ # plant thresholds and model hyperparameters
data/raw, data/processed/ # generated synthetic data
src/data/ # simulator and dataset loaders
src/models/ # virtual calorimeter, combustion surrogate, training
src/optimization/ # prescriptive optimizer
src/app/ # Gradio studio and plotting helpers
artifacts/ # trained weights and scalers
tests/ # unit tests
run_demo.py # app entrypointQuickstart
Requires Python 3.11 or newer (built and tested on 3.13).
pip install -r requirements.txt "gradio==6.25.0"
python run_demo.py # open http://localhost:7860
pytest # 45 tests
python -m src.models.train # optional: regenerate artifacts/Gradio is absent from requirements.txt on purpose: Hugging Face Spaces install it according to the Space's sdk_version, and listing it again breaks their dependency resolution. Pin your local version to match the Space runtime.
Design notes and limitations
- The synthetic data favors a readable demo over plant fidelity: actuators were calibrated so the optimizer shows visible control authority.
- The 850 °C furnace minimum is a legal floor, not a comfort margin. It was softened from an 875 °C design value so low-LHV feedstock (which only reaches about 872 °C) stays feasible. The optimizer enforces it as a hard constraint.
- The surrogate runs in float32, so its outputs carry roughly 1e-3 of numerical noise. The optimizer sets its finite-difference step above that noise floor.
- The virtual calorimeter is trained and, when artifacts are present, wired into the Prescriptive Sandbox via Auto-estimate LHV (checkbox) — inferred LHV/moisture from the live 15-min window is shown against the true latent value; falls back to manual selection when artifacts are missing.
- The sandbox now shows a before→after delta table, 15-step horizon convergence, and a €/h projected margin (steam, fan, NOx) using
plant.economicsfromconfigs/config.yaml; the diagnostics tab shows control-authority sensitivities and a p_air × grate 2D contour (Steam/Temp/O₂) sampling the surrogate grid 31×21, and the MLOps drift monitor uses true synthetic residuals when the NN twin is active. - Field-level data contracts (window, state, control, response shapes), the equilibrium equations used for training, and the full optimizer objective are documented in `methodology.md`. The original design narrative lives in
CombustionTwin_PyTorch_Gradio_Implementation_Plan.md.
