Files
EOS/docs/_generated/configpvforecast.md
T
628226d3d9 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>
2026-11-08 07:42:20 +01:00

45 KiB

PV Forecast Configuration

:::{table} pvforecast :widths: 10 20 10 5 5 30 :align: left

Name Environment Variable Type Read-Only Default Description
adaptive EOS_PVFORECAST__ADAPTIVE PVForecastAdaptiveAkkudoktorCommonSettings rw required Adaptive Akkudoktor: configure string measurements and independent reference flags on the planes.
akkudoktor EOS_PVFORECAST__AKKUDOKTOR PVForecastAkkudoktorLocalCommonSettings rw required Akkudoktor forecast backend and local calibration settings
forecastsolar EOS_PVFORECAST__FORECASTSOLAR PVForecastForecastSolarCommonSettings rw required ForecastSolar provider settings
homeassistant EOS_PVFORECAST__HOMEASSISTANT PVForecastHomeAssistantCommonSettings rw required Home Assistant provider settings
max_planes EOS_PVFORECAST__MAX_PLANES Optional[int] rw 0 Maximum number of planes that can be set
planes EOS_PVFORECAST__PLANES Optional[list[akkudoktoreos.prediction.pvforecast.PVForecastPlaneSetting]] rw None Plane configuration.
planes_azimuth List[float] ro N/A Compute a list of the azimuths per active planes.
planes_inverter_paco Any ro N/A Compute a list of the maximum power rating of the inverter per active planes.
planes_peakpower List[float] ro N/A Compute a list of the peak power per active planes.
planes_tilt List[float] ro N/A Compute a list of the tilts per active planes.
planes_userhorizon Any ro N/A Compute a list of the user horizon per active planes.
provider EOS_PVFORECAST__PROVIDER Optional[str] rw None PVForecast provider id of provider to be used.
providers list[str] ro N/A Available PVForecast provider ids.
pvforecastimport EOS_PVFORECAST__PVFORECASTIMPORT PVForecastImportCommonSettings rw required PV forecast import provider settings
pvlib EOS_PVFORECAST__PVLIB PVForecastPVLibCommonSettings rw required PVLib provider settings
pvnode EOS_PVFORECAST__PVNODE PVForecastPVNodeCommonSettings rw required PVNode provider settings
solcast EOS_PVFORECAST__SOLCAST PVForecastSolcastCommonSettings rw required Solcast provider settings
vrm EOS_PVFORECAST__VRM PVForecastVrmCommonSettings rw required Victron Remote Management (VRM) provider settings
:::

Example Input

   {
       "pvforecast": {
           "provider": "PVForecastAkkudoktor",
           "akkudoktor": {
               "backend": "remote",
               "resolution_minutes": 15,
               "forecast_days": null,
               "past_days": null,
               "weather_models": [
                   "best_match"
               ],
               "transposition_model": "perez",
               "albedo": 0.25,
               "inverter_efficiency": 0.96,
               "temperature_coefficient": -0.36,
               "apply_iam": true,
               "shift_to_interval_start": true,
               "calibration_enabled": false,
               "calibration_days": 30,
               "calibration_reference_days": 30,
               "calibration_outage_filter_enabled": true,
               "calibration_outage_threshold": 0.55,
               "calibration_min_healthy_days": 3,
               "calibration_azimuth_bin_degrees": 45,
               "calibration_prior_kwh": 5.0,
               "calibration_min_factor": 0.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",
               "resolution_minutes": 15,
               "forecast_days": null,
               "past_days": null,
               "weather_models": [
                   "best_match",
                   "icon_d2"
               ],
               "transposition_model": "perez",
               "albedo": 0.25,
               "inverter_efficiency": 0.96,
               "temperature_coefficient": -0.36,
               "apply_iam": true,
               "shift_to_interval_start": true,
               "calibration_enabled": false,
               "calibration_days": 30,
               "calibration_reference_days": 30,
               "calibration_outage_filter_enabled": true,
               "calibration_outage_threshold": 0.55,
               "calibration_min_healthy_days": 3,
               "calibration_azimuth_bin_degrees": 45,
               "calibration_prior_kwh": 5.0,
               "calibration_min_factor": 0.5,
               "calibration_max_factor": 1.5,
               "regional_weather_model": "icon_d2",
               "regional_horizon_hours": 6,
               "learning_days": 21,
               "min_training_days": 5,
               "max_ac_power_w": 0,
               "nominal_total_peakpower_kw": null,
               "diagnostics_days": 7,
               "archive_retention_days": 60,
               "nowcast_enabled": true,
               "trend_enabled": true,
               "solar_response_enabled": true,
               "weather_response_enabled": true,
               "correction_mode": "auto",
               "validation_days": 7,
               "min_validation_days": 3,
               "min_validation_samples": 32,
               "min_validation_improvement": 0.02,
               "training_delay_minutes": 15,
               "total_measurement_key": null,
               "total_curtailment_sensitive": false,
               "total_curtailment_key": null,
               "total_learning_guard_key": null,
               "total_learning_guard_max": null,
               "total_learning_guard_max_gap_seconds": 600
           },
           "pvforecastimport": {
               "import_file_path": null,
               "import_json": null
           },
           "vrm": {
               "token": "your-token",
               "site_id": 12345
           },
           "homeassistant": {
               "entity_id": "sensor.pv_forecast",
               "attribute": "forecast",
               "datetime_key": "datetime",
               "value_key": "watts",
               "value_unit": "W",
               "base_url": null,
               "token": null
           },
           "pvlib": {},
           "pvnode": {
               "api_key": "",
               "site_id": null,
               "forecast_days": 2
           },
           "forecastsolar": {
               "api_key": null
           },
           "solcast": {
               "api_key": "",
               "site_id": ""
           },
           "planes": [
               {
                   "name": null,
                   "measurement_key": null,
                   "nowcast_reference": false,
                   "curtailment_sensitive": false,
                   "parameters_estimated": false,
                   "correction_group": null,
                   "curtailment_key": null,
                   "learning_guard_key": null,
                   "learning_guard_max": null,
                   "learning_guard_max_gap_seconds": 600,
                   "surface_tilt": 10.0,
                   "surface_azimuth": 180.0,
                   "userhorizon": [
                       10.0,
                       20.0,
                       30.0
                   ],
                   "peakpower": 5.0,
                   "pvtechchoice": "crystSi",
                   "mountingplace": "free",
                   "loss": 14.0,
                   "trackingtype": 0,
                   "optimal_surface_tilt": false,
                   "optimalangles": false,
                   "albedo": null,
                   "module_model": null,
                   "inverter_model": null,
                   "inverter_paco": 6000,
                   "modules_per_string": 20,
                   "strings_per_inverter": 2
               },
               {
                   "name": null,
                   "measurement_key": null,
                   "nowcast_reference": false,
                   "curtailment_sensitive": false,
                   "parameters_estimated": false,
                   "correction_group": null,
                   "curtailment_key": null,
                   "learning_guard_key": null,
                   "learning_guard_max": null,
                   "learning_guard_max_gap_seconds": 600,
                   "surface_tilt": 20.0,
                   "surface_azimuth": 90.0,
                   "userhorizon": [
                       5.0,
                       15.0,
                       25.0
                   ],
                   "peakpower": 3.5,
                   "pvtechchoice": "crystSi",
                   "mountingplace": "free",
                   "loss": 14.0,
                   "trackingtype": 1,
                   "optimal_surface_tilt": false,
                   "optimalangles": false,
                   "albedo": null,
                   "module_model": null,
                   "inverter_model": null,
                   "inverter_paco": 4000,
                   "modules_per_string": 20,
                   "strings_per_inverter": 2
               }
           ],
           "max_planes": 1
       }
   }

Example Output

   {
       "pvforecast": {
           "provider": "PVForecastAkkudoktor",
           "akkudoktor": {
               "backend": "remote",
               "resolution_minutes": 15,
               "forecast_days": null,
               "past_days": null,
               "weather_models": [
                   "best_match"
               ],
               "transposition_model": "perez",
               "albedo": 0.25,
               "inverter_efficiency": 0.96,
               "temperature_coefficient": -0.36,
               "apply_iam": true,
               "shift_to_interval_start": true,
               "calibration_enabled": false,
               "calibration_days": 30,
               "calibration_reference_days": 30,
               "calibration_outage_filter_enabled": true,
               "calibration_outage_threshold": 0.55,
               "calibration_min_healthy_days": 3,
               "calibration_azimuth_bin_degrees": 45,
               "calibration_prior_kwh": 5.0,
               "calibration_min_factor": 0.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",
               "resolution_minutes": 15,
               "forecast_days": null,
               "past_days": null,
               "weather_models": [
                   "best_match",
                   "icon_d2"
               ],
               "transposition_model": "perez",
               "albedo": 0.25,
               "inverter_efficiency": 0.96,
               "temperature_coefficient": -0.36,
               "apply_iam": true,
               "shift_to_interval_start": true,
               "calibration_enabled": false,
               "calibration_days": 30,
               "calibration_reference_days": 30,
               "calibration_outage_filter_enabled": true,
               "calibration_outage_threshold": 0.55,
               "calibration_min_healthy_days": 3,
               "calibration_azimuth_bin_degrees": 45,
               "calibration_prior_kwh": 5.0,
               "calibration_min_factor": 0.5,
               "calibration_max_factor": 1.5,
               "regional_weather_model": "icon_d2",
               "regional_horizon_hours": 6,
               "learning_days": 21,
               "min_training_days": 5,
               "max_ac_power_w": 0,
               "nominal_total_peakpower_kw": null,
               "diagnostics_days": 7,
               "archive_retention_days": 60,
               "nowcast_enabled": true,
               "trend_enabled": true,
               "solar_response_enabled": true,
               "weather_response_enabled": true,
               "correction_mode": "auto",
               "validation_days": 7,
               "min_validation_days": 3,
               "min_validation_samples": 32,
               "min_validation_improvement": 0.02,
               "training_delay_minutes": 15,
               "total_measurement_key": null,
               "total_curtailment_sensitive": false,
               "total_curtailment_key": null,
               "total_learning_guard_key": null,
               "total_learning_guard_max": null,
               "total_learning_guard_max_gap_seconds": 600
           },
           "pvforecastimport": {
               "import_file_path": null,
               "import_json": null
           },
           "vrm": {
               "token": "your-token",
               "site_id": 12345
           },
           "homeassistant": {
               "entity_id": "sensor.pv_forecast",
               "attribute": "forecast",
               "datetime_key": "datetime",
               "value_key": "watts",
               "value_unit": "W",
               "base_url": null,
               "token": null
           },
           "pvlib": {},
           "pvnode": {
               "api_key": "",
               "site_id": null,
               "forecast_days": 2
           },
           "forecastsolar": {
               "api_key": null
           },
           "solcast": {
               "api_key": "",
               "site_id": ""
           },
           "planes": [
               {
                   "name": null,
                   "measurement_key": null,
                   "nowcast_reference": false,
                   "curtailment_sensitive": false,
                   "parameters_estimated": false,
                   "correction_group": null,
                   "curtailment_key": null,
                   "learning_guard_key": null,
                   "learning_guard_max": null,
                   "learning_guard_max_gap_seconds": 600,
                   "surface_tilt": 10.0,
                   "surface_azimuth": 180.0,
                   "userhorizon": [
                       10.0,
                       20.0,
                       30.0
                   ],
                   "peakpower": 5.0,
                   "pvtechchoice": "crystSi",
                   "mountingplace": "free",
                   "loss": 14.0,
                   "trackingtype": 0,
                   "optimal_surface_tilt": false,
                   "optimalangles": false,
                   "albedo": null,
                   "module_model": null,
                   "inverter_model": null,
                   "inverter_paco": 6000,
                   "modules_per_string": 20,
                   "strings_per_inverter": 2
               },
               {
                   "name": null,
                   "measurement_key": null,
                   "nowcast_reference": false,
                   "curtailment_sensitive": false,
                   "parameters_estimated": false,
                   "correction_group": null,
                   "curtailment_key": null,
                   "learning_guard_key": null,
                   "learning_guard_max": null,
                   "learning_guard_max_gap_seconds": 600,
                   "surface_tilt": 20.0,
                   "surface_azimuth": 90.0,
                   "userhorizon": [
                       5.0,
                       15.0,
                       25.0
                   ],
                   "peakpower": 3.5,
                   "pvtechchoice": "crystSi",
                   "mountingplace": "free",
                   "loss": 14.0,
                   "trackingtype": 1,
                   "optimal_surface_tilt": false,
                   "optimalangles": false,
                   "albedo": null,
                   "module_model": null,
                   "inverter_model": null,
                   "inverter_paco": 4000,
                   "modules_per_string": 20,
                   "strings_per_inverter": 2
               }
           ],
           "max_planes": 1,
           "providers": [
               "PVForecastAkkudoktor",
               "PVForecastAdaptiveAkkudoktor",
               "PVForecastForecastSolar",
               "PVForecastHomeAssistant",
               "PVForecastImport",
               "PVForecastPVLib",
               "PVForecastPVNode",
               "PVForecastSolcast",
               "PVForecastVrm"
           ],
           "planes_peakpower": [
               5.0,
               3.5
           ],
           "planes_azimuth": [
               180.0,
               90.0
           ],
           "planes_tilt": [
               10.0,
               20.0
           ],
           "planes_userhorizon": [
               [
                   10.0,
                   20.0,
                   30.0
               ],
               [
                   5.0,
                   15.0,
                   25.0
               ]
           ],
           "planes_inverter_paco": [
               6000.0,
               4000.0
           ]
       }
   }

Common settings for PV forecast VRM API

:::{table} pvforecast::vrm :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
site_id int rw 12345 VRM-Installation-ID
token str rw your-token Access token for connecting to the Victron Remote Management (VRM) API
:::

Example Input/Output

   {
       "pvforecast": {
           "vrm": {
               "token": "your-token",
               "site_id": 12345
           }
       }
   }

Common settings for the Solcast PV forecast provider

:::{table} pvforecast::solcast :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
api_key str rw `` Solcast API key (Bearer auth). Required.
site_id str rw `` Solcast rooftop site (resource) id. Required.
:::

Example Input/Output

   {
       "pvforecast": {
           "solcast": {
               "api_key": "your-solcast-key",
               "site_id": "abcd-1234-efgh-5678"
           }
       }
   }

Common settings for the pvnode.com PV forecast provider

:::{table} pvforecast::pvnode :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
api_key str rw `` pvnode.com API key (Bearer auth). Required.
forecast_days int rw 2 Forecast horizon in days (1-7, capped by the pvnode plan).
site_id Optional[str] rw None pvnode.com site id of the saved plant ('Anlagen-ID'). When set, the saved (possibly calibrated) site is used. Leave empty to send the configured pvforecast.planes inline instead.
:::

Example Input/Output

   {
       "pvforecast": {
           "pvnode": {
               "api_key": "pvn_live_xxxxxxxxxxxxxxxx",
               "site_id": "abcd-1234",
               "forecast_days": 2
           }
       }
   }

Common settings for pvforecast data calculation with PVLib

:::{table} pvforecast::pvlib :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
:::

Example Input/Output

   {
       "pvforecast": {
           "pvlib": {}
       }
   }

Common settings for pvforecast data import from file or JSON string

:::{table} pvforecast::pvforecastimport :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
import_file_path Union[str, pathlib.Path, NoneType] rw None Path to the file to import PV forecast data from.
import_json Optional[str] rw None JSON string, dictionary of PV forecast value lists.
:::

Example Input/Output

   {
       "pvforecast": {
           "pvforecastimport": {
               "import_file_path": null,
               "import_json": "{\"pvforecast_ac_power\": [0, 8.05, 352.91]}"
           }
       }
   }

PV Forecast Plane Configuration

:::{table} pvforecast::planes::list :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
albedo Optional[float] rw 0.2 Proportion of the light hitting the ground that it reflects back.
correction_group Optional[str] rw None Optional group of planes sharing short-term validation. Unset independent references share one group; other planes are separate.
curtailment_key Optional[str] rw None Optional measured flag: 0 explicitly unrestricted, 1 curtailed. Missing or stale flags never authorize learning a curtailment-sensitive plane.
curtailment_sensitive bool rw False Battery-full curtailment possible; exclude from unrestricted weather learning.
inverter_model Optional[str] rw None Model of the inverter of this plane.
inverter_paco Optional[int] rw None AC power rating of the inverter [W].
learning_guard_key Optional[str] rw None Optional site-specific positive learning guard, e.g. battery SoC, as an alternative to a binary curtailment flag.
learning_guard_max Optional[float] rw None Learn only while every supported guard value is below this bound. This is an explicit site assumption, not automatic curtailment detection.
learning_guard_max_gap_seconds int rw 600 None
loss Optional[float] rw 14.0 Sum of PV system losses in percent
measurement_key Optional[str] rw None Matching EOS power measurement key for adaptive learning.
module_model Optional[str] rw None Model of the PV modules of this plane.
modules_per_string Optional[int] rw None Number of the PV modules of the strings of this plane.
mountingplace Optional[str] rw building Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.
name Optional[str] rw None Readable PV string name.
nowcast_reference bool rw False Use this independent array as a local weather reference.
optimal_surface_tilt Optional[bool] rw False Calculate the optimum tilt angle. Ignored for two-axis tracking.
optimalangles Optional[bool] rw False Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.
parameters_estimated bool rw False Geometry and peak power are effective fitted model estimates, not verified nameplate data.
peakpower Optional[float] rw None Nominal power of PV system in kW.
pvtechchoice Optional[str] rw crystSi PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.
strings_per_inverter Optional[int] rw None Number of the strings of the inverter of this plane.
surface_azimuth Optional[float] rw 180.0 Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).
surface_tilt Optional[float] rw 30.0 Tilt angle from horizontal plane. Ignored for two-axis tracking.
trackingtype Optional[int] rw None Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south.
userhorizon Optional[List[float]] rw None Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.
:::

Example Input/Output

   {
       "pvforecast": {
           "planes": [
               {
                   "name": null,
                   "measurement_key": null,
                   "nowcast_reference": false,
                   "curtailment_sensitive": false,
                   "parameters_estimated": false,
                   "correction_group": null,
                   "curtailment_key": null,
                   "learning_guard_key": null,
                   "learning_guard_max": null,
                   "learning_guard_max_gap_seconds": 600,
                   "surface_tilt": 10.0,
                   "surface_azimuth": 180.0,
                   "userhorizon": [
                       10.0,
                       20.0,
                       30.0
                   ],
                   "peakpower": 5.0,
                   "pvtechchoice": "crystSi",
                   "mountingplace": "building",
                   "loss": 14.0,
                   "trackingtype": 0,
                   "optimal_surface_tilt": false,
                   "optimalangles": false,
                   "albedo": null,
                   "module_model": null,
                   "inverter_model": null,
                   "inverter_paco": 6000,
                   "modules_per_string": 20,
                   "strings_per_inverter": 2
               }
           ]
       }
   }

Common settings for pvforecast data from a Home Assistant entity

:::{table} pvforecast::homeassistant :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
attribute str rw forecast Entity attribute holding the forecast list.
base_url Optional[str] rw None Base URL of the Home Assistant instance. Only required when EOS is not running as a Home Assistant add-on (no SUPERVISOR_TOKEN available).
datetime_key str rw datetime Key for the timestamp in each forecast entry.
entity_id str rw sensor.pv_forecast Home Assistant entity providing the PV forecast.
token Optional[str] rw None Long-lived access token for the Home Assistant instance. Only required when EOS is not running as a Home Assistant add-on.
value_key str rw watts Key for the AC power value in each forecast entry.
value_unit Literal['W', 'kW'] rw W Unit of the forecast value. Converted to W internally.
:::

Example Input/Output

   {
       "pvforecast": {
           "homeassistant": {
               "entity_id": "sensor.pv1_power_now",
               "attribute": "forecast",
               "datetime_key": "datetime",
               "value_key": "watts",
               "value_unit": "W",
               "base_url": "http://homeassistant.local:8123",
               "token": null
           }
       }
   }

Common settings for the Forecast.Solar PV forecast provider

:::{table} pvforecast::forecastsolar :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
api_key Optional[str] rw None Forecast.Solar API key. Optional — the public endpoint works without a key (lower rate limit).
:::

Example Input/Output

   {
       "pvforecast": {
           "forecastsolar": {
               "api_key": null
           }
       }
   }

Common settings for the local (pvlib) PV forecast provider

:::{table} pvforecast::akkudoktor :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
albedo float rw 0.25 Ground albedo used for planes that do not set their own.
apply_iam bool rw True Apply the ASHRAE incidence-angle modifier to the beam component.
backend Literal['remote', 'local'] rw remote Akkudoktor forecast backend: remote API or local Open-Meteo/pvlib model.
calibration_azimuth_bin_degrees int rw 45 Width of the solar-azimuth bins for the correction. 0 fits a single global factor only.
calibration_days int rw 30 Length of the measurement window used to fit the correction.
calibration_enabled bool rw False Correct systematic model error against measured PV production. Requires measurement.pv_production_emr_keys to be configured and fed. Fits a global scale factor plus per-solar-azimuth factors, which is what catches near-field shading the horizon profile misses.
calibration_max_factor float rw 1.5 Upper clamp on any fitted correction factor.
calibration_min_factor float rw 0.5 Lower clamp on any fitted correction factor.
calibration_min_healthy_days int rw 3 Minimum number of healthy days used for a fit. Older healthy days from the reference window are added when the recent window contains fewer.
calibration_outage_filter_enabled bool rw True Exclude days whose measured production is far below the recent healthy plant level. This prevents inverter, battery and curtailment events from being learned as permanent PV model losses.
calibration_outage_threshold float rw 0.55 A day is treated as unavailable when its measured/modelled energy ratio is below this fraction of the robust healthy reference ratio.
calibration_prior_kwh float rw 5.0 Shrinkage strength: a bin needs this much modelled energy before its own factor outweighs the global one. Higher is more conservative.
calibration_reference_days int rw 30 Lookback used to distinguish healthy production from outages or curtailment. If the calibration window contains too few healthy days, the most recent healthy days from this reference window are used.
forecast_days Optional[int] rw None Forecast horizon in days (1-16). Leave empty to derive it from prediction.hours, which is what keeps the optimizer's tail horizon fed.
inverter_efficiency float rw 0.96 Nominal inverter efficiency (PVWatts eta_inv_nom).
past_days Optional[int] rw None Days of past data to request (0-92). Leave empty to derive it from prediction.historic_hours.
resolution_minutes int rw 15 Forecast resolution in minutes. 15 requests Open-Meteo's minutely_15 block (natively resolved over Central Europe and North America, interpolated from hourly elsewhere); 60 requests the hourly block.
shift_to_interval_start bool rw True Open-Meteo stamps an interval mean with the interval END. EOS labels an interval by its START, so records are shifted back by one interval. Disable only to compare like-for-like against a provider that does not.
snow_detection_ratio float rw 0.5 Measured/clear-model production ratio below which an interval counts as evidence of snow the weather model missed.
snow_enabled bool rw False Model snow lying on the modules. Requests snowfall and snow depth from Open-Meteo and runs the NREL snow coverage model (pvlib.snow) per plane, which covers the modules on heavy snowfall and lets the snow slide off with warmth and sunshine. Without this, the forecast after a snowfall assumes clean modules.
snow_feedback_enabled bool rw True Correct the modelled snow coverage with measured PV production from measurement.pv_production_emr_keys. Production well above the snow model clears the coverage at once; production far below the clear-module model raises it, but only while snow is plausible (snow on the ground or already modelled on the modules). Needs cumulative meter readings.
snow_feedback_gain float rw 0.8 How far one measured interval pulls the coverage towards the observed value (1.0 = adopt the observation).
snow_feedback_memory_hours float rw 12.0 When measured production shows more snow than modelled, the snow is assumed to slide off slower than the NREL model says. This damping fades back to the model's rate with this time constant after the last measurement.
snow_feedback_min_fraction float rw 0.05 A measured interval is only used when the clear-module model expects at least this fraction of the installed AC capacity. Dawn, dusk and dark overcast carry no usable evidence about snow.
snow_lookback_days int rw 7 Days of past weather (and PV measurements) replayed on every update to rebuild the current snow coverage. No state is stored between runs, so this must reach back before the last relevant snowfall.
snow_threshold_depth_cm float rw 1.0 Ground snow depth below which the modules are assumed clear (pvlib threshold_depth).
snow_threshold_snowfall_cm_per_hour float rw 0.5 Snowfall rate that fully covers the modules (pvlib threshold_snowfall). pvlib's default of 1.0 cm/h was tuned on US station data; Open-Meteo's hourly model snowfall is smoother, so a lower rate fits better.
temperature_coefficient float rw -0.36 Module power temperature coefficient in %/degC (negative). Matches the cellCoEff the akkudoktor.net forecast uses.
transposition_model str rw perez pvlib sky-diffuse transposition model: isotropic, klucher, haydavies, reindl, king or perez.
weather_models list[str] rw ['best_match'] Open-Meteo weather models to request. Listing more than one turns the input into a poor-man's ensemble: the members are averaged per variable, which is the cheapest reliable way to cut irradiance forecast error. Costs no extra API calls.
:::

Example Input/Output

   {
       "pvforecast": {
           "akkudoktor": {
               "backend": "remote",
               "resolution_minutes": 15,
               "forecast_days": null,
               "past_days": null,
               "weather_models": [
                   "best_match"
               ],
               "transposition_model": "perez",
               "albedo": 0.25,
               "inverter_efficiency": 0.96,
               "temperature_coefficient": -0.36,
               "apply_iam": true,
               "shift_to_interval_start": true,
               "calibration_enabled": true,
               "calibration_days": 30,
               "calibration_reference_days": 30,
               "calibration_outage_filter_enabled": true,
               "calibration_outage_threshold": 0.55,
               "calibration_min_healthy_days": 3,
               "calibration_azimuth_bin_degrees": 45,
               "calibration_prior_kwh": 5.0,
               "calibration_min_factor": 0.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
           }
       }
   }

Automatic defaults; configure measurements and reference flags on each plane

:::{table} pvforecast::adaptive :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
albedo float rw 0.25 Ground albedo used for planes that do not set their own.
apply_iam bool rw True Apply the ASHRAE incidence-angle modifier to the beam component.
archive_retention_days int rw 60 None
backend Literal['remote', 'local'] rw local None
calibration_azimuth_bin_degrees int rw 45 Width of the solar-azimuth bins for the correction. 0 fits a single global factor only.
calibration_days int rw 30 Length of the measurement window used to fit the correction.
calibration_enabled bool rw False None
calibration_max_factor float rw 1.5 Upper clamp on any fitted correction factor.
calibration_min_factor float rw 0.5 Lower clamp on any fitted correction factor.
calibration_min_healthy_days int rw 3 Minimum number of healthy days used for a fit. Older healthy days from the reference window are added when the recent window contains fewer.
calibration_outage_filter_enabled bool rw True Exclude days whose measured production is far below the recent healthy plant level. This prevents inverter, battery and curtailment events from being learned as permanent PV model losses.
calibration_outage_threshold float rw 0.55 A day is treated as unavailable when its measured/modelled energy ratio is below this fraction of the robust healthy reference ratio.
calibration_prior_kwh float rw 5.0 Shrinkage strength: a bin needs this much modelled energy before its own factor outweighs the global one. Higher is more conservative.
calibration_reference_days int rw 30 Lookback used to distinguish healthy production from outages or curtailment. If the calibration window contains too few healthy days, the most recent healthy days from this reference window are used.
correction_mode Literal['auto', 'calibrated', 'local', 'trend'] rw auto Choose validated short-term candidates per group/lead, or explicitly fix a correction stage.
diagnostics_days int rw 7 None
forecast_days Optional[int] rw None Forecast horizon in days (1-16). Leave empty to derive it from prediction.hours, which is what keeps the optimizer's tail horizon fed.
inverter_efficiency float rw 0.96 Nominal inverter efficiency (PVWatts eta_inv_nom).
learning_days int rw 21 None
max_ac_power_w float rw 0 None
min_training_days int rw 5 None
min_validation_days int rw 3 None
min_validation_improvement float rw 0.02 None
min_validation_samples int rw 32 None
nominal_total_peakpower_kw Optional[float] rw None Known installed nameplate total; fitted plane powers may be effective estimates.
nowcast_enabled bool rw True None
past_days Optional[int] rw None Days of past data to request (0-92). Leave empty to derive it from prediction.historic_hours.
regional_horizon_hours float rw 6 None
regional_weather_model Optional[str] rw icon_d2 None
resolution_minutes int rw 15 Forecast resolution in minutes. 15 requests Open-Meteo's minutely_15 block (natively resolved over Central Europe and North America, interpolated from hourly elsewhere); 60 requests the hourly block.
shift_to_interval_start bool rw True Open-Meteo stamps an interval mean with the interval END. EOS labels an interval by its START, so records are shifted back by one interval. Disable only to compare like-for-like against a provider that does not.
solar_response_enabled bool rw True Learn empirical angular response relative to separate, eligible reference arrays; activate only after issued-candidate validation.
temperature_coefficient float rw -0.36 Module power temperature coefficient in %/degC (negative). Matches the cellCoEff the akkudoktor.net forecast uses.
total_curtailment_key Optional[str] rw None None
total_curtailment_sensitive bool rw False None
total_learning_guard_key Optional[str] rw None None
total_learning_guard_max Optional[float] rw None None
total_learning_guard_max_gap_seconds int rw 600 None
total_measurement_key Optional[str] rw None Optional whole-plant power channel when individual plane measurements are unavailable.
training_delay_minutes int rw 15 None
transposition_model str rw perez pvlib sky-diffuse transposition model: isotropic, klucher, haydavies, reindl, king or perez.
trend_enabled bool rw True None
validation_days int rw 7 None
weather_models list[str] rw ['best_match'] Open-Meteo weather models to request. Listing more than one turns the input into a poor-man's ensemble: the members are averaged per variable, which is the cheapest reliable way to cut irradiance forecast error. Costs no extra API calls.
weather_response_enabled bool rw True Archive received model weather and learn horizon/regime corrections; activate only after issued-candidate validation.
:::

Example Input/Output

   {
       "pvforecast": {
           "adaptive": {
               "backend": "local",
               "resolution_minutes": 15,
               "forecast_days": null,
               "past_days": null,
               "weather_models": [
                   "best_match",
                   "icon_d2"
               ],
               "transposition_model": "perez",
               "albedo": 0.25,
               "inverter_efficiency": 0.96,
               "temperature_coefficient": -0.36,
               "apply_iam": true,
               "shift_to_interval_start": true,
               "calibration_enabled": false,
               "calibration_days": 30,
               "calibration_reference_days": 30,
               "calibration_outage_filter_enabled": true,
               "calibration_outage_threshold": 0.55,
               "calibration_min_healthy_days": 3,
               "calibration_azimuth_bin_degrees": 45,
               "calibration_prior_kwh": 5.0,
               "calibration_min_factor": 0.5,
               "calibration_max_factor": 1.5,
               "regional_weather_model": "icon_d2",
               "regional_horizon_hours": 6.0,
               "learning_days": 21,
               "min_training_days": 5,
               "max_ac_power_w": 0.0,
               "nominal_total_peakpower_kw": null,
               "diagnostics_days": 7,
               "archive_retention_days": 60,
               "nowcast_enabled": true,
               "trend_enabled": true,
               "solar_response_enabled": true,
               "weather_response_enabled": true,
               "correction_mode": "auto",
               "validation_days": 7,
               "min_validation_days": 3,
               "min_validation_samples": 32,
               "min_validation_improvement": 0.02,
               "training_delay_minutes": 15,
               "total_measurement_key": null,
               "total_curtailment_sensitive": false,
               "total_curtailment_key": null,
               "total_learning_guard_key": null,
               "total_learning_guard_max": null,
               "total_learning_guard_max_gap_seconds": 600
           }
       }
   }