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
+10 -1
View File
@@ -336,7 +336,16 @@
"calibration_azimuth_bin_degrees": 45,
"calibration_prior_kwh": 5.0,
"calibration_min_factor": 0.5,
"calibration_max_factor": 1.5
"calibration_max_factor": 1.5,
"snow_enabled": false,
"snow_lookback_days": 7,
"snow_threshold_snowfall_cm_per_hour": 0.5,
"snow_threshold_depth_cm": 1.0,
"snow_feedback_enabled": true,
"snow_feedback_gain": 0.8,
"snow_feedback_min_fraction": 0.05,
"snow_feedback_memory_hours": 12.0,
"snow_detection_ratio": 0.5
},
"adaptive": {
"backend": "local",