Skip to content

feat(weather): per-zone weather for the ENTSO-E four-zone pipeline#376

Merged
bartzbeielstein merged 1 commit into
developfrom
feat/per-zone-weather-entsoe
Jun 13, 2026
Merged

feat(weather): per-zone weather for the ENTSO-E four-zone pipeline#376
bartzbeielstein merged 1 commit into
developfrom
feat/per-zone-weather-entsoe

Conversation

@bartzbeielstein

Copy link
Copy Markdown
Collaborator

Summary

When ConfigMulti.per_zone_weather=True, each of the four German TSO control zones (load_50hertz/load_amprion/load_tennet/load_transnetbw) is driven by weather from its own region instead of one shared national index. Calendar, holiday and event-window features stay shared (national); only weather becomes regional, under the same un-prefixed column names so nothing downstream changes. Opt-in, default OFF → byte-identical to the prior shared-weather baseline.

This closes the gap the four-zone bottom-up KB entry left open ("reconciliation/stacking deferred").

Mechanism (ADR-approved)

The existing single global weather build stays as the shared schema/baseline; build_exogenous_features additionally fetches one weather frame per zone (reusing the multi-city get_weather_features path with each zone's city list) into self.zone_weather_aligned. At the single per-target seam (get_target_data, new keyword-only zone_weather) the zone's weather columns overwrite the shared values in-place — column order/shape preserved. The per-zone frame keeps its native [start, cov_end] index so it spans the forecast horizon (regression-tested). On all-zones-success the shared schema is seeded from the first zone, so per-zone weather survives a global fetch that failed under on_weather_failure="skip".

Changes

  • weather/locations.py: registry 13→15 (Mannheim, Karlsruhe for TransnetBW); frozen GERMAN_TSO_ZONE_CITIES partition (provenance: SMARD control-area map + TSO wiki; Hamburg∈50Hertz, Bremen∈TenneT); locations_for_zone resolver (fail-safe ValueError on unknown zone). Pre-existing Sphinx roles swept.
  • configurator/: per_zone_weather + zone_weather_locations fields; validate_config guards reject combination with use_population_weighted_weather (mutual exclusivity), use_exogenous_features=False, and poly_features_degree>=2.
  • Fail-safe: under "skip" any zone fetch failure degrades the whole pipeline to no-weather (never substitutes the global index); under "raise" aborts naming the zone.
  • tasks/task_safe_zone_load_demo.py: --per_zone_weather flag (default off, offline-safe).
  • 36 network-free tests incl. mapping/partition, config guards, the seam overwrite, forecast-horizon coverage, skip-degradation, and non-zone-target fail-safe. quartodoc reference + freeze regenerated.

Verification (all green locally)

  • uv run pytest tests/ -q2635 passed, 1 skipped
  • ruff / black / isort / reuse lint → clean
  • quartodoc build + interlinks → API reference in sync
  • quarto render --no-cache → all 191 pages, exit 0

Authoring: tbb-skills4agents agents (software-architect → code-writer → code-reviewer) with orchestrator review that caught & regression-tested a forecast-horizon truncation bug.

🤖 Generated with Claude Code

When `ConfigMulti.per_zone_weather=True`, each of the four German TSO control
zones (load_50hertz/load_amprion/load_tennet/load_transnetbw) is driven by
weather from its own region instead of one shared national index. Calendar,
holiday and event-window features stay shared (they are national); only weather
becomes regional, under the same un-prefixed column names so no estimator,
factory or column matcher downstream changes. Opt-in, default OFF -> byte
-identical to the prior shared-weather baseline.

Mechanism: the existing single global weather build stays as the shared schema/
baseline; build_exogenous_features additionally fetches one weather frame per
zone (reusing the multi-city get_weather_features path with each zone's city
list) into self.zone_weather_aligned. At the single per-target seam
(get_target_data, new keyword-only `zone_weather`) the zone's weather columns
overwrite the shared values in-place; column order and shape are preserved. The
per-zone frame keeps its native [start, cov_end] index so it spans the forecast
horizon (regression-tested). On all-zones-success the shared schema is seeded
from the first zone, so per-zone weather survives a global fetch that failed
under on_weather_failure="skip".

- weather/locations.py: registry 13->15 (Mannheim, Karlsruhe for TransnetBW);
  frozen GERMAN_TSO_ZONE_CITIES partition (provenance: SMARD control-area map +
  TSO wiki; Hamburg in 50Hertz, Bremen in TenneT); locations_for_zone resolver
  (fail-safe ValueError on unknown zone). Pre-existing Sphinx roles swept.
- configurator: per_zone_weather + zone_weather_locations fields; validate_config
  guards reject combination with use_population_weighted_weather (mutual
  exclusivity), use_exogenous_features=False, and poly_features_degree>=2.
- Fail-safe: under "skip" any zone fetch failure degrades the whole pipeline to
  no-weather (never substitutes the global index); under "raise" aborts naming
  the zone.
- tasks/task_safe_zone_load_demo.py: --per_zone_weather flag (default off,
  offline-safe via on_weather_failure="skip").
- 36 network-free tests incl. mapping/partition, config guards, the seam
  overwrite, forecast-horizon coverage, skip-degradation, and non-zone-target
  fail-safe. quartodoc reference + freeze regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bartzbeielstein bartzbeielstein merged commit 65d04d2 into develop Jun 13, 2026
10 checks passed
@bartzbeielstein bartzbeielstein deleted the feat/per-zone-weather-entsoe branch June 13, 2026 19:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant