feat(pvforecast): model snow on the modules in the local backend (#1484)

No PV forecast modelled snow lying on the modules, so after a snowfall the
optimizer planned with PV that never came. The local Open-Meteo/pvlib
backend can now reduce the forecast by the snow coverage (opt-in via
pvforecast.akkudoktor.snow_enabled):

- Request hourly snowfall and snow depth with the weather and run the NREL
  snow coverage model (pvlib.snow) per plane.
- Correct the coverage with measured PV production: production above the
  model clears it at once; low production raises it only while snow is
  plausible, so curtailment and inverter outages are not learned as snow.
  Persisting snow damps the slide rate, fading with
  snow_feedback_memory_hours.
- Rebuild the coverage from snow_lookback_days on every update instead of
  storing state.
- Exclude intervals with possible snow from the calibration fit.
- Warn when PV meter readings are missing or stale; ignore impossible meter
  jumps.

_plane_power keeps its two-value signature (used by the adaptive provider in
#1422); the irradiance comes from the new _plane_power_and_poa.

Tests replay real February 2026 data from one plant (snow on 02-19/20).

Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
SchaafAlexander
2026-11-08 07:42:20 +01:00
committed by GitHub
co-authored by Claude Opus 5.5
parent a06473b6d6
commit 628226d3d9
7 changed files with 1193 additions and 18 deletions
+44
View File
@@ -908,6 +908,50 @@ Hourly meters are fitted hourly; native quarter-hour meters retain their cadence
Probable outage or curtailment days are excluded by default. Without sufficient
usable measurements the uncalibrated physical model is used.
##### Snow on the modules
Set `snow_enabled` to true to model snow lying on the modules. The local backend
then also requests hourly `snowfall` and `snow_depth` from Open-Meteo and runs the
NREL snow coverage model (`pvlib.snow.coverage_nrel`) per plane: heavy snowfall
covers the modules, warmth and sunshine let the snow slide off, and the modules
count as clear once the ground snow depth drops below `snow_threshold_depth_cm`.
The forecast is reduced by the covered fraction. No state is stored between runs;
every update replays the last `snow_lookback_days` of weather to rebuild the
current coverage.
The weather model alone often misjudges how long snow stays on the modules. With
`measurement.pv_production_emr_keys` configured (cumulative meter readings in
kWh), `snow_feedback_enabled` corrects the coverage with the measured production
of the past hours:
- Production above the snow model clears the coverage at once.
- Production far below the clean-module model (less than `snow_detection_ratio`)
raises the coverage, but only while snow is plausible: snow lies on the ground
or the model already holds some coverage. Low production on a snow-free day
(curtailment, an inverter fault) is ignored.
- Persisting snow also slows the modelled slide rate. This fades back with
`snow_feedback_memory_hours`, so a warm night after a covered day still clears
the modules in the forecast.
Intervals with possible snow are excluded from the calibration fit, so snow is
never learned as a permanent model loss. Meter jumps larger than the plant can
produce are ignored.
```json
{
"pvforecast": {
"provider": "PVForecastAkkudoktor",
"akkudoktor": {
"backend": "local",
"snow_enabled": true
}
},
"measurement": {
"pv_production_emr_keys": ["pv_production_emr"]
}
}
```
Transient weather errors are retried. If an update still fails, existing usable
AC forecasts are retained; a cold start or expired-only forecast reports the
error. Retention does not extend the available forecast horizon. Calibration and