Competitive Powertrain Benchmarking
Benchmark competitor powertrains — on free, recurring data
Give R&D and the Benchmarking Team an evidence base on engine specs, driveline configuration and certified whole-vehicle CO₂ — without paid market-intelligence subscriptions.
- Frame — turn "how do we compare?" into measurable questions across OEM × segment × year.
- Mine & validate — recurring ingest from an authoritative EU source, reproducible and auditable.
- Analyse & model — honest statistics; a CO₂ predictor that says what it does and does not know.
- Deliver — an interactive dashboard R&D can drive, plus a static, offline record.
EEA HDV CO₂ monitoring — queried live over HTTP
Regulation (EU) 2018/956: Member States and manufacturers report every certified truck to the European Environment Agency. Exposed through Discodata — a SQL Server reachable by plain HTTP, no authentication.
Five stages, offline-first, one shared core package
powerbench/ holds paths, the Discodata client, the schema, data-IO, features and evaluation — imported by every script and by the Streamlit app.
Work around the endpoint, don't fight it
- Pagination is broken — Discodata's p parameter repeats page one. Instead, bound every request with a WHERE on year × manufacturer and pull each chunk whole.
- Two source tables, one schema — CO2_HeavyDutyVehicles (2019–20, full VECTO detail) and HDV_2023_viewer (2023, + registration country); a mapper reconciles them.
- Every pull is a record — one JSON snapshot per source with a SHA-256 sidecar; retry with backoff; unpublished years skipped with one clear message.
Reject the row's mistakes, not the row
- Physical bounds — displacement ≤ 40 L, power ≤ 1500 kW, GVW ≤ 120 t.
- Out-of-range → null — a bad optional measurement drops to None; the vehicle is kept. Saved ~3,600 rows.
- OEM names are dirty — "Daimler AG" and "Daimler Truck AG" become one brand; address junk → "Unknown".
- Derived, not guessed — powertrain class from the EEA flag columns + fuel type.
# powerbench/schema.py class HDVRow(BaseModel): Engine_RatedPower_kw: float | None GrossVehicleMass_t: float | None # value outside (0, hi] -> None, # never a rejected record
The manifest is the authority
Clean rows land in DuckDB (powerbench.duckdb) and a Parquet mirror. manifest.json records every source snapshot, its SHA-256 and row count — so the active dataset is always known and reproducible.
Three different "CO₂", three different jobs
g·kWh⁻¹Engine on a test bench — fuel efficiency of the engine alone. Independent of truck weight, aero, mission.
g·km⁻¹Whole truck, simulated by VECTO — engine + gearbox + axles + aero + tyres. The headline number. Our benchmark and ML target.
g·t⁻¹km⁻¹Freight efficiency — CO₂ per tonne of goods moved one km. The real logistics figure; only ~3% populated.
Who is lowest, and who is improving fastest
The dashboard renders this as OEM box plots plus a 2019→2023 change ranking — narrowed to comparable vehicle groups.
Engine-cycle CO₂ is not a league table
Correlation analysis shows engine-bench CO₂ (g/kWh) falls as engine displacement rises — r ≈ −0.5. Bigger diesels run closer to their efficient point.
- The consequence — ranking OEMs on raw g/kWh flatters whoever sells the heaviest long-haul tractors.
- The rule — benchmark on CO₂v (whole vehicle); use g/kWh as a model feature, never as a scoreboard.
Predict whole-vehicle CO₂ from the spec sheet
Target: CO₂v (g/km), restricted to the VECTO classes with a comparable figure. Two feature sets, so coverage and ceiling are both explicit:
- base — mass, vehicle class, powertrain, fuel, flags. Works on every reporting year.
- rich — base + engine ratings + axle configuration. 2019–2020 only.
The evaluation method is the result
- Out-of-fold headline — shuffled 5-fold cross-validation; every row scored by a model that never saw it.
- Baseline in view — "predict the median" MAE shown next to every model. A useful model must beat it.
- Optimism gap shown — the naive train-on-test R² is printed beside the CV number, not instead of it.
- Leakage guard — assert_no_leakage() bans every CO₂ / fuel-consumption column; g/kWh feeds the VECTO calc that makes the target.
Spec sheet explains about half; engine specs push it to ~60%
Curb mass and vehicle group carry most of it; engine displacement and power add the rich set's lift. The naive↔CV gap is small — not overfit.
A number that admits what it can't see
- Inputs — sliders for GVW, curb mass, vehicle group, powertrain, and (rich) engine power / displacement / speed.
- Output — predicted CO₂v ± the model's cross-validated MAE.
- Scope flag — any input outside the training min–max raises an extrapolation warning.
- Labelled illustrative — the model sees only these fields; real CO₂v also depends on aero, tyres, gearbox, auxiliaries.
An eight-tab Streamlit app R&D can drive
- Pipeline — mine → validate → load → train, live from the browser, streamed progress. Nothing pre-downloaded.
- Overview · Distributions · Correlations · Benchmark — the analysis, filterable by year / OEM / powertrain / segment.
- ML — both feature sets side by side, out-of-fold scatter, feature importance, what-if.
- Metrics · Documentation · Provenance — definitions, this write-up, and the SHA-256 lineage. Light / dark.
Reproducible, offline, auditable — and easy to extend
- 2024 data — one button in the Pipeline tab the moment the EEA publishes it (~9–12 month lag).
- 2021–2022 — wire the bulk CSV for a contiguous 2019–2023 trend.
- Market weighting — add KBA / ACEA registration volumes for share-weighted benchmarks.
github.com/mohamed-soubhi/Competitive-Powertrain-Benchmarking