first_commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from math import pi, sin
|
||||
|
||||
from gibil.classes.models import WeatherForecastPoint, WeatherResolvedTruth
|
||||
from gibil.classes.weather_display import WeatherDisplayDataset
|
||||
|
||||
|
||||
class WeatherSampleData:
|
||||
"""Builds temporary display-only weather data for UI tuning."""
|
||||
|
||||
def build(self, hours: int = 72) -> WeatherDisplayDataset:
|
||||
now = datetime.now(timezone.utc).replace(minute=0, second=0, microsecond=0)
|
||||
start = now - timedelta(hours=hours)
|
||||
horizons = [2, 4, 8, 12, 24]
|
||||
|
||||
forecast_points: list[WeatherForecastPoint] = []
|
||||
resolved_truth: list[WeatherResolvedTruth] = []
|
||||
|
||||
for offset in range(hours + 1):
|
||||
target_at = start + timedelta(hours=offset)
|
||||
truth_temperature = self._temperature(target_at, offset)
|
||||
truth_solar = self._solar(target_at, offset)
|
||||
|
||||
resolved_truth.append(
|
||||
WeatherResolvedTruth(
|
||||
resolved_at=target_at,
|
||||
source="sample",
|
||||
temperature_c=truth_temperature,
|
||||
shortwave_radiation_w_m2=truth_solar,
|
||||
)
|
||||
)
|
||||
|
||||
for horizon in horizons:
|
||||
forecast_points.append(
|
||||
WeatherForecastPoint(
|
||||
issued_at=target_at - timedelta(hours=horizon),
|
||||
target_at=target_at,
|
||||
horizon_hours=horizon,
|
||||
temperature_c=truth_temperature
|
||||
+ self._temperature_error(offset, horizon),
|
||||
shortwave_radiation_w_m2=max(
|
||||
0,
|
||||
truth_solar + self._solar_error(offset, horizon),
|
||||
),
|
||||
cloud_cover_pct=max(
|
||||
0,
|
||||
min(100, 45 + 30 * sin((offset + horizon) / 9)),
|
||||
),
|
||||
source="sample",
|
||||
)
|
||||
)
|
||||
|
||||
return WeatherDisplayDataset(
|
||||
forecast_points=forecast_points,
|
||||
resolved_truth=resolved_truth,
|
||||
)
|
||||
|
||||
def _temperature(self, target_at: datetime, offset: int) -> float:
|
||||
daily = sin(((target_at.hour - 7) / 24) * 2 * pi)
|
||||
slow = sin(offset / 18)
|
||||
return round(6.5 + daily * 5.5 + slow * 1.3, 1)
|
||||
|
||||
def _solar(self, target_at: datetime, offset: int) -> float:
|
||||
daylight = max(0, sin(((target_at.hour - 5) / 15) * pi))
|
||||
cloud_effect = 0.75 + 0.25 * sin(offset / 7)
|
||||
return round(780 * daylight * cloud_effect, 1)
|
||||
|
||||
def _temperature_error(self, offset: int, horizon: int) -> float:
|
||||
return round((horizon / 8) * sin((offset + horizon) / 5), 1)
|
||||
|
||||
def _solar_error(self, offset: int, horizon: int) -> float:
|
||||
return round((horizon * 9) * sin((offset + horizon) / 4), 1)
|
||||
Reference in New Issue
Block a user