Files
astrape-oracle/scripts/debug/debug_energy_forecast.py
T
rpotter6298 9a4ee8e0cf Flatten to a single package directory
The checkout is now astrape/astrape-oracle, not astrape/astrape-oracle/<package>. The package
contents move to the repository root and pyproject maps the package name to
'.' via package-dir, with the subpackages listed explicitly.

The systemd units move out to /srv/astrape/deploy/systemd, outside every
repository, because they describe this deployment rather than any one
component.

Verified both install modes: editable, which is how the deployment runs, and
a plain wheel install, which is what the container build does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 15:05:17 +02:00

75 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""Debug baseline energy forecast curves."""
from __future__ import annotations
import argparse
from datetime import timezone
from gibil.classes.oracle_builder import EnergyForecastBuilder
from gibil.classes.forecast_config import EnergyForecastConfig
from astrape_ems.classes.env import EnvLoader
from astrape_ems.classes.store import SigenStore
from astrape_oracle.classes.weather.store import WeatherStore
from gibil.classes.models import PowerForecastPoint
def main() -> None:
EnvLoader().load()
args = parse_args()
solar_run, load_run, net_run = EnergyForecastBuilder(
weather_history=WeatherStore.from_env(),
plant_history=SigenStore.from_env(),
config=EnergyForecastConfig.from_env(),
).build()
print(
f"issued_at={net_run.issued_at.astimezone(timezone.utc).isoformat(timespec='seconds')}"
)
print(
f"solar_model={solar_run.model_version} "
f"load_model={load_run.model_version} points={len(net_run.points)}"
)
print(
"target_at solar_p10 solar_p50 solar_p90 "
"load_p10 load_p50 load_p90 net_p10 net_p50 net_p90"
)
solar_by_target = _by_target(solar_run.points)
load_by_target = _by_target(load_run.points)
for point in net_run.points[: args.limit]:
solar_point = solar_by_target[point.target_at]
load_point = load_by_target[point.target_at]
print(
f"{point.target_at.astimezone(timezone.utc).isoformat(timespec='minutes'):25} "
f"{solar_point.p10_power_w:9.0f} "
f"{solar_point.p50_power_w:9.0f} "
f"{solar_point.p90_power_w:9.0f} "
f"{load_point.p10_power_w:8.0f} "
f"{load_point.p50_power_w:8.0f} "
f"{load_point.p90_power_w:8.0f} "
f"{point.p10_net_power_w:7.0f} "
f"{point.p50_net_power_w:7.0f} "
f"{point.p90_net_power_w:7.0f}"
)
def _by_target(points: list[PowerForecastPoint]) -> dict[object, PowerForecastPoint]:
return {point.target_at: point for point in points}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Print baseline solar/load/net forecast curves."
)
parser.add_argument(
"--limit",
type=int,
default=24,
help="Number of forecast points to show. Defaults to 24.",
)
return parser.parse_args()
if __name__ == "__main__":
main()