gpt5.5 - odladeni dokumentace dle kodu
Some checks failed
CI and deploy / migration-check (pull_request) Failing after 27s
CI and deploy / deploy (pull_request) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-05-02 19:17:04 +02:00
parent 3595b24f3b
commit 02f0ab66e4
9 changed files with 161 additions and 114 deletions

View File

@@ -5,7 +5,9 @@
- Stahuje meteorologická data (irradiance, teplota) pro každé FVE pole zvlášť
- Vypočítává predikovaný výkon v 15min intervalech
- Ukládá výsledek per `pv_array_id` + `run_id`
- Predikce se spouští denně a před každým plánovacím během
- Predikce se spouští každé 2 hodiny v `:05` a ručně přes API. Plánovač používá
poslední dostupné uložené forecasty; forecast nespouští implicitně před každým
plánovacím během.
---
@@ -13,12 +15,15 @@
| Pole | Výkon | Azimut | Sklon | Střídač | Řízení |
|---|---|---|---|---|---|
| A | 10 kWp | TBD | TBD | Deye 20kW | řídíme |
| B | 10 kWp | TBD | TBD | Ongridový | autonomní, **nepredikujeme odděleně** |
| A | 10 kWp | 184° | 22° | Deye 20kW | řídíme |
| B | 10 kWp | 184° | 35° | Ongridový | autonomní, predikujeme jako samostatné pole |
> **Předpoklad:** Pole B (ongridový) je zapojeno do GEN portu Deye. Jeho výkon se projeví v `pv_power_w` telemetrie jako součást celkového výkonu. Pro plánování modelujeme jen pole A. Pole B bereme jako šum / bonus který se projeví v auditu.
> **Aktuální implementace:** Forecast služba počítá všechna FVE pole lokality,
> která mají vyplněný `azimuth_deg` a `tilt_deg`; plánovač pracuje odděleně s
> `pv_a_forecast_w` i `pv_b_forecast_w`.
> Azimuty a sklony je nutné doplnit při konfiguraci lokality do `asset_pv_array`.
> Azimut je uložen v kompasové / pvlib konvenci: `0=N`, `90=E`, `180=S`,
> `270=W`.
---
@@ -36,10 +41,9 @@
GET https://api.open-meteo.com/v1/forecast
?latitude={lat}
&longitude={lon}
&hourly=shortwave_radiation,temperature_2m
&minutely_15=shortwave_radiation,temperature_2m
&timezone=Europe/Prague
&forecast_days=3
&minutely_15=direct_normal_irradiance,diffuse_radiation,shortwave_radiation,temperature_2m
&timezone=auto
&forecast_days=7
```
**Záložní / budoucí: Solcast**
@@ -51,34 +55,28 @@ GET https://api.open-meteo.com/v1/forecast
## Výpočet výkonu z irradiance
Jednoduchý fyzikální model (dostatečný pro plánování):
Implementace používá `pvlib` a model POA irradiance `haydavies`:
```python
def calculate_pv_power(
irradiance_wm2: float, # GHI ze weather service
temp_c: float,
nominal_power_wp: int,
azimuth_deg: float,
tilt_deg: float,
shading_factor: float = 1.0,
temp_coeff: float = -0.004 # typicky -0.4%/°C pro křemík
) -> int:
# 1. Korekce na teplotu panelu
panel_temp = temp_c + 25 # zjednodušený NOCT model
temp_correction = 1 + temp_coeff * (panel_temp - 25)
poa_global = pvlib.irradiance.get_total_irradiance(
surface_tilt=tilt_deg,
surface_azimuth=azimuth_deg, # 0=N, 90=E, 180=S, 270=W
solar_zenith=solar_pos["apparent_zenith"],
solar_azimuth=solar_pos["azimuth"],
dni=dni,
ghi=ghi,
dhi=dhi,
dni_extra=dni_extra,
model="haydavies",
)["poa_global"].fillna(0).clip(lower=0)
# 2. Korekce na azimut a sklon (zjednodušená, bez přesného GHI→POA)
# Přesnější model: pvlib knihovna (doporučeno pro produkci)
orientation_factor = cos_angle_of_incidence(azimuth_deg, tilt_deg)
# 3. Výsledný výkon
power_w = (irradiance_wm2 / 1000) * nominal_power_wp * temp_correction * orientation_factor * shading_factor
return max(0, int(power_w))
area_m2 = nominal_power_wp / (1000.0 * 0.20)
power_w = (poa_global * area_m2 * 0.20 * shading_factor).clip(
lower=0,
upper=nominal_power_wp * 1.1,
)
```
> **Doporučení pro implementaci:** Použít knihovnu `pvlib` (Python) pro přesný POA irradiance výpočet z GHI + azimut + sklon. Je to standardní nástroj, dobře dokumentovaný.
---
## Kdo spouští predikci
@@ -107,15 +105,15 @@ def calculate_pv_power(
## Logika běhu predikce
```python
def run_forecast(site_id: int, horizon_days: int = 2):
def run_forecast(site_id: int):
site = db.get_site(site_id)
arrays = db.get_pv_arrays(site_id, controllable=True)
arrays = db.get_pv_arrays_with_azimuth_and_tilt(site_id)
for array in arrays:
# 1. Stáhnout meteorologická data
weather = open_meteo_client.fetch(
lat=site.lat, lon=site.lon,
start=today, end=today + horizon_days
forecast_days=clamp(OPEN_METEO_FORECAST_DAYS, 2, 16)
)
# 2. Vytvořit forecast_pv_run
@@ -147,8 +145,7 @@ def run_forecast(site_id: int, horizon_days: int = 2):
temp_c=slot.temperature_2m
))
db.upsert_forecast_intervals(intervals)
db.update_forecast_run_status(run.id, "ok")
db.insert_forecast_intervals(intervals)
```
---
@@ -204,23 +201,20 @@ Hromadně: **`ems.fn_pv_forecast_sync_reference_days(site_id, p_days_local date[
```env
OPEN_METEO_API_URL=https://api.open-meteo.com/v1/forecast
OPEN_METEO_FORECAST_DAYS=7
FORECAST_MAX_AGE_HOURS=2 # plánovač odmítne starší predikci
FORECAST_RETRY_COUNT=3
```
---
## Monitoring
- Alert pokud forecast pro dnešden + zítřek není k dispozici do 15:00
- Endpoint `GET /health/forecast?site_id=1&date=YYYY-MM-DD` → čerstvost a počet intervalů
- Zatím není samostatný `/health/forecast` endpoint.
- Stav se kontroluje přes logy běhu `scheduled_forecast_refresh`, přes forecast API
a přes obecné health endpointy.
- Log každého běhu (délka horizontu, počet intervalů, trvání, zdroj)
---
## Otevřené body
- [ ] Doplnit přesný azimut a sklon obou FVE polí při instalaci
- [ ] Rozhodnout: pvlib pro přesnější POA výpočet vs jednoduchý model doporučujeme pvlib od začátku
- [ ] Pole B (ongridový) zda vůbec modelovat nebo ignorovat v plánu a jen sledovat v auditu
- [ ] Ověřit přesný azimut a sklon obou FVE polí proti skutečné instalaci
- [ ] Solcast jako alternativa v budoucnu `forecast_source` to umožňuje bez DB změn