Conversation
data_preprocessing() cropped only last_timestep to the year end. The delivered data reaches past it (park_end up to 35,166, drive_end up to 35,057, against a last valid index of 35,040), and the SoC-band code builds an np.linspace() whose length derives from drive_end/park_end - introduced LAST_TIMESTEP and TIMESTEP_COLUMNS and now clip every timestep column, so the ramp length and the slice always agree
nesnoj
marked this pull request as ready for review
September 11, 2026 10:25
Member
Author
|
Hey @CarlosEpia , SH was successful and I merged this into |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1460 .
Replaces the eMobility dataset for motorized individual travel with a new one that covers vehicle class M1 (passenger cars, as before) and N1 (light commercial vehicles < 3.5 t), and that is generated together with the charging infrastructure, so events, vehicles and charging points are mutually consistent.
What changes
Input data instead of derivation. The vehicle pool, the events, the vehicle counts per municipality, the charging locations and the allocation of vehicles to municipalities are delivered by the data providers as parquet, published on Zenodo as one zip archive per scenario and downloaded by the pipeline itself. The KBA/RegioStaR7 chain — registration-district level, population-based disaggregation — is no longer used for the new scenarios. Vehicle profiles are still generated with SimBEV; the charging sites are now placed with GeoLIS from the same run.
Two methodologies side by side.
status2024,reGon2037andreGon2045use the new methodology;eGon2035keeps the simBEV trip tarball from the data bundle and the KBA-derived allocation, as a long-term stable scenario. Every task splits the configured scenario list and dispatches onis_legacy_scenario(), so the DAG shape does not depend on--scenarios. Both write the same tables, discriminated byscenario.Nine vehicle types instead of six: the six private M1 types, plus
bev_commercialandphev_commercial(commercially registered passenger cars, still M1) andbev_light_duty_vehicle(the only N1 type; N1 is BEV-only). They are merged into the existing eTraGo components — oneLi_ionbus, oneBEV_chargerlink, onebattery_storagestore and oneland_transport_EVload per MV grid district. The eTraGo model shape is unchanged.Flexible charging is keyed on the charging use case (
depot,home_detached,home_apartment,work) rather than on the(location, use_case)pair.street,retail,urban_fastandhighway_fastare inflexible.New: flexibility diagnostics. The pipeline now writes what it already computed and discarded — see Additional Notes below.
Table renames (breaking for external consumers)
All MIT tables move to the
egon_ev_mit_lgv_prefix, for both methodologies, so there is exactly one naming family:demand.egon_ev_pooldemand.egon_ev_mit_lgv_pooldemand.egon_ev_tripdemand.egon_ev_mit_lgv_tripdemand.egon_ev_count_registration_districtdemand.egon_ev_mit_lgv_count_registration_districtdemand.egon_ev_count_municipalitydemand.egon_ev_mit_lgv_count_municipalitydemand.egon_ev_count_mv_grid_districtdemand.egon_ev_mit_lgv_count_mv_grid_districtdemand.egon_ev_mv_grid_districtdemand.egon_ev_mit_lgv_mv_grid_districtdemand.egon_ev_metadatademand.egon_ev_mit_lgv_metadataThe FK column
egon_ev_pool_ev_idbecomesev_id, and the ORM classes follow the tables (EgonEvPool→EgonEvMitLgvPool, …) so the code stays greppable from the table name.grid.egon_emob_charging_infrastructureis not renamed — it is TracBEV output and stays with the legacy path.Five new tables, all in
demand:egon_ev_mit_lgv_mapping_ev_municipality,egon_ev_mit_lgv_charging_location,egon_ev_mit_lgv_flex_timeseries,egon_ev_mit_lgv_energy_balance,egon_ev_mit_lgv_charging_profile_use_case.egon_ev_mit_lgv_metadatachanges shape entirely: the per-key columns (eta_cp,stepsize,start_date, …) are replaced by two JSONB documents holding the simBEV and GeoLIS run configurations verbatim. The delivered key set is not stable — between the old bundle and delivery v1.4 it lost two keys and gained a dozen — and the old code raisedKeyErroron the current delivery. eDisGo'ssimbev_config_from_oedb()reads this table and has to unpack the JSON instead.Other taxonomy changes that break silently and are worth flagging to downstream users:
egon_ev_mit_lgv_trip.use_casemoves frompublic/home/work/empty to the eight new values, and.locationcarries two parallel vocabularies (English for private vehicles, German for commercial ones) selected by the vehicle group.📋 Pull Request Guidelines
🧑💻 Contributor Checklist
Before requesting a review, make sure you've completed all of the following:
(for more information on local test, check
toxin the Contributing section)(CI tests are automatically executed when creating a PR, you can see the results of the checks below)
(optional if no dataset changes are involved)
Not yet — blocked, see Open before merge below.
MotorizedIndividualTravel0.0.12 → 0.1.0,MITChargingInfrastructure0.0.9 → 0.1.0CHANGELOG.rstabout the changesAUTHORS.rstOptional:
🔍 Reviewer Checklist
During your review, please check the following:
CHANGELOG.rstupdated accordingly?📝 Additional Notes (optional)
Flexibility diagnostics (D23–D26)
The
land_transport_EVload does not always mean the same quantity: it is the grid-side charging energy in dumb and lowflex scenarios but the battery-side driving energy in flexible ones, with no record of which. That was the core methodological defect of the previous dataset, and it is why the reported 37.3 vs. 41.4 TWh looked like an inconsistency when it is in fact the identityΣ driving_load = eta_cp · Σ charging_load_grid.Three exports now make the reference points explicit, for the new methodology only, without changing the eTraGo model shape:
egon_ev_mit_lgv_flex_timeseries—charging_load_grid,charging_load_grid_flexanddriving_loadper grid district and hour, so both sides are recorded for every scenario;egon_ev_mit_lgv_energy_balance— annual balance per grid district, use case and vehicle type, carrying the battery-side and the grid-side charging energy;egon_ev_mit_lgv_charging_profile_use_case— the dumb charging load per grid district, hour and use case.All three are computed inside the existing event loop and the existing hourly resample — no extra pass. ~4.5 GB worst case for three scenarios, against the ~350 GB of a full run.
docs/data/mobility_demand.rstdocuments the reference points and the post-processing recipe forΔ(t)andE_shift.charging_load_grid_flexand theflexibleflag are a potential, not a realised flexibility — they are populated forstatus2024too, which gets no bus, link or store at all. The documentation says so explicitly, in a warning box, because the data invites the misreading.Design decisions worth a second opinion
egon_ev_mit_lgv_charging_locationis created and filled by the charging infrastructure dataset, although it lives indemandnext to the other M1+N1 tables. The plan claimed it for both datasets in different sections; since the two run in parallel andcreate_tables()drops before it creates, exactly one of them may own it.setnested inside asetis unhashable inTaskGraph, and serialising avoids four concurrent bulk loads competing for the same database anyway. The event import dominates the runtime regardless._DownloadLock) guards the shared archive. D3 keeps the two datasets independent, so both fetch the same multi-GB zip; without mutual exclusion one would read a half-written file.bus_id), so an Airflow retry recovers instead of hitting the primary key on rows the failed attempt already wrote.add_metadatais now wired into the MIT DAG. It was imported but never used as a task, so MIT metadata was never actually written. Refactored from ~370 lines of copy-paste into a table-spec loop covering all eleven tables.pyarrowadded topyproject.toml— the parquet import path needs it and it was not a dependency.ev_mapping_event_locationis deliberately not importedIt carries one row per (charging event × drawn vehicle): 499,677,447 rows measured for
status2024, and an estimated ~6.9·10⁹ / ~8.2·10⁹ for reGon2037 / reGon2045 — roughly 370 GB and 440 GB in PostgreSQL before indexes, against a ~350 GB budget for the entire pipeline. Nothing in the database needs it, because charging locations carry their ownuse_casesince delivery v1.4. It stays a parquet file in the extracted scenario directory and the documentation says where to find it.Vehicles in unknown municipalities are dropped, loudly
Delivery v1.4 covers 10,615 municipalities; VG250 has 11,003 with an MV grid district. The split joins on
ags, so a deliveredagsthat VG250 does not know contributes no population share. Behaviour: continue, drop, and log the number of affected municipalities, the vehicles lost per type and in total, and that total as a share of the delivered fleet — plus a pointed hint when it exceeds ~0.5 %. Aborting the pipeline on ordinary reference-data drift (municipalities merge and are renumbered) is a worse outcome than a documented, quantified loss. This decision is provisional and needs revisiting once the number is measured on a real full delivery of each scenario; if it turns out material, the fix is an AGS-vintage translation table, not a tolerance.What was verified, and how
Since the Zenodo records do not exist yet and no database was available, verification was done against the local v1.4 delivery and in isolation:
bus_id, no RNG), and every placed vehicle lands in a grid district that intersects its own municipality.status2024delivery, reproducing the plan's projections exactly: 1,074 municipalities, 92,047 vehicle instances, 23,840 distinct pool EVs (56.9 % of the pool). Note that the event table shrinks far less than the region share suggests, because the pool is national and each profile is instantiated ~65× across Germany. That is inherent, not a defect — plan test-mode runtime and disk accordingly.use_caseNULLs survive as unquoted empty fields (PostgreSQL CSV reads those as NULL, empty strings as""), booleans astrue/false, geometries as valid WKB hex.pytest tests/passes, the pipeline DAG imports, andblack/isort/flake8are clean on everything touched (only pre-existingF401s remain elsewhere).Open before merge
These need the real data and are not done:
grep -rn PLACEHOLDER src/must come back empty. The Zenodo records do not exist yet, so the URLs are deliberate literalPLACEHOLDERs — an unreplaced one fails with an obvious 404 rather than fetching something plausible-looking.ZENODO_ENVIRONMENTfromzenodo_sandboxtozenodoand fill in the production record ids — these are separate deployments, so it is not a host substitution.status2024+reGon2037+reGon2045, and one foreGon2035proving the legacy path is intact.omi-validate the thirteen metadata strings — eleven MIT tables plus TracBEV's and the charging locations (needs a populated database).Not in scope
sanity_checks.pyis not adapted. It is written against the KBA-derived allocation and the old event semantics, is already excluded from the pipeline, and will be superseded by a new validation concept in a separate effort. Only its imports were updated so the module still imports. What this PR provides instead is one hard assertion at import (every pool type hastech_data) plus logged, non-fatal consistency information after each import and after the diagnostics.src/egon/data/datasets/helpers.py(imported nowhere, carries a stale copy ofread_simbev_metadata_file) is left alone —drop_old_methodology.md§6.2 owns its deletion.eGon2035is retired.create_tables()drops and recreates all MIT tables, soeGon2035and the new scenarios must be built in the same run. RunningeGon2035afterwards destroys the new scenarios' rows.