Files
EOS/docs/_generated/configpvforecast.md
T
Andreas 6dc58c33e2 feat(pvforecast): keep outages out of the local provider's calibration
A battery or inverter failure limits PV to local demand for days. The
calibration read that as the plant's true output and learned it as a permanent
model loss, so one outage degraded the forecast long after the hardware was
fixed.

Calibration now estimates the healthy plant ratio over
`calibration_reference_days`, excludes days below `calibration_outage_threshold`
of it, and falls back to the most recent `calibration_min_healthy_days` when the
normal window is contaminated. `calibration_outage_filter_enabled` turns this
off for plants where measured curtailment, not available potential, is the
prediction target.

The fit also uses native 15-minute meter readings when every configured PV meter
supplies them - never interpolating hourly counters into an invented
quarter-hour profile - interpolates azimuth factors smoothly between bin centres
instead of stepping the EMS input curve, and normalizes the shape per forecast
day so it redistributes energy without changing that day's kWh correction. The
default azimuth bin widens from 15 to 45 degrees, which is what a typical
window actually supports.

Fixes the calibration window itself: it was derived from the measurement store
as a whole rather than from the configured PV production meters. A load meter
reaching further than the PV meter placed the window where no PV reading exists,
so calibration silently fell back to hourly fitting or skipped itself.

Also fixes `Measurement.load()`, which discarded every stored record. It
validated the file into a "temporary" Measurement, but Measurement is a
singleton, so that instance was the live one and the parsed records were
dropped.
2026-09-09 07:56:55 +02:00

22 KiB

PV Forecast Configuration

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

Name Environment Variable Type Read-Only Default Description
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.
provider_settings EOS_PVFORECAST__PROVIDER_SETTINGS PVForecastCommonProviderSettings rw required Provider settings
providers list[str] ro N/A Available PVForecast provider ids.
:::

Example Input

   {
       "pvforecast": {
           "provider": "PVForecastAkkudoktor",
           "provider_settings": {
               "PVForecastImport": null,
               "PVForecastVrm": null,
               "PVForecastPVNode": null,
               "PVForecastForecastSolar": null,
               "PVForecastSolcast": null,
               "PVForecastAkkudoktorLocal": null
           },
           "planes": [
               {
                   "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
               },
               {
                   "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",
           "provider_settings": {
               "PVForecastImport": null,
               "PVForecastVrm": null,
               "PVForecastPVNode": null,
               "PVForecastForecastSolar": null,
               "PVForecastSolcast": null,
               "PVForecastAkkudoktorLocal": null
           },
           "planes": [
               {
                   "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
               },
               {
                   "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",
               "PVForecastVrm",
               "PVForecastPVNode",
               "PVForecastForecastSolar",
               "PVForecastSolcast",
               "PVForecastImport",
               "PVForecastAkkudoktorLocal"
           ],
           "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 the local (pvlib) PV forecast provider

:::{table} pvforecast::provider_settings::PVForecastAkkudoktorLocal :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.
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.
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": {
           "provider_settings": {
               "PVForecastAkkudoktorLocal": {
                   "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
               }
           }
       }
   }

Common settings for the Solcast PV forecast provider

:::{table} pvforecast::provider_settings::PVForecastSolcast :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": {
           "provider_settings": {
               "PVForecastSolcast": {
                   "api_key": "your-solcast-key",
                   "site_id": "abcd-1234-efgh-5678"
               }
           }
       }
   }

Common settings for the Forecast.Solar PV forecast provider

:::{table} pvforecast::provider_settings::PVForecastForecastSolar :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": {
           "provider_settings": {
               "PVForecastForecastSolar": {
                   "api_key": null
               }
           }
       }
   }

Common settings for the pvnode.com PV forecast provider

:::{table} pvforecast::provider_settings::PVForecastPVNode :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": {
           "provider_settings": {
               "PVForecastPVNode": {
                   "api_key": "pvn_live_xxxxxxxxxxxxxxxx",
                   "site_id": "abcd-1234",
                   "forecast_days": 2
               }
           }
       }
   }

Common settings for PV forecast VRM API

:::{table} pvforecast::provider_settings::PVForecastVrm :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
pvforecast_vrm_idsite int rw 12345 VRM-Installation-ID
pvforecast_vrm_token str rw your-token Token for Connecting VRM API
:::

Example Input/Output

   {
       "pvforecast": {
           "provider_settings": {
               "PVForecastVrm": {
                   "pvforecast_vrm_token": "your-token",
                   "pvforecast_vrm_idsite": 12345
               }
           }
       }
   }

Common settings for pvforecast data import from file or JSON string

:::{table} pvforecast::provider_settings::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": {
           "provider_settings": {
               "PVForecastImport": {
                   "import_file_path": null,
                   "import_json": "{\"pvforecast_ac_power\": [0, 8.05, 352.91]}"
               }
           }
       }
   }

PV Forecast Provider Configuration

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

Name Type Read-Only Default Description
PVForecastAkkudoktorLocal Optional[akkudoktoreos.prediction.pvforecastakkudoktorlocal.PVForecastAkkudoktorLocalCommonSettings] rw None PVForecastAkkudoktorLocal settings
PVForecastForecastSolar Optional[akkudoktoreos.prediction.pvforecastforecastsolar.PVForecastForecastSolarCommonSettings] rw None PVForecastForecastSolar settings
PVForecastImport Optional[akkudoktoreos.prediction.pvforecastimport.PVForecastImportCommonSettings] rw None PVForecastImport settings
PVForecastPVNode Optional[akkudoktoreos.prediction.pvforecastpvnode.PVForecastPVNodeCommonSettings] rw None PVForecastPVNode settings
PVForecastSolcast Optional[akkudoktoreos.prediction.pvforecastsolcast.PVForecastSolcastCommonSettings] rw None PVForecastSolcast settings
PVForecastVrm Optional[akkudoktoreos.prediction.pvforecastvrm.PVForecastVrmCommonSettings] rw None PVForecastVrm settings
:::

Example Input/Output

   {
       "pvforecast": {
           "provider_settings": {
               "PVForecastImport": null,
               "PVForecastVrm": null,
               "PVForecastPVNode": null,
               "PVForecastForecastSolar": null,
               "PVForecastSolcast": null,
               "PVForecastAkkudoktorLocal": null
           }
       }
   }

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 None Proportion of the light hitting the ground that it reflects back.
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].
loss Optional[float] rw 14.0 Sum of PV system losses in percent
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 free Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.
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.
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": [
               {
                   "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
               }
           ]
       }
   }