mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 08:55:15 +00:00
PVForecast: planes as nested config (list)
This commit is contained in:
parent
e0b1ece524
commit
af5e4a753a
3
.gitignore
vendored
3
.gitignore
vendored
@ -260,3 +260,6 @@ tests/testdata/new_optimize_result*
|
|||||||
tests/testdata/openapi-new.json
|
tests/testdata/openapi-new.json
|
||||||
tests/testdata/openapi-new.md
|
tests/testdata/openapi-new.md
|
||||||
tests/testdata/config-new.md
|
tests/testdata/config-new.md
|
||||||
|
|
||||||
|
# FastHTML session key
|
||||||
|
.sesskey
|
||||||
|
@ -510,109 +510,13 @@ Validators:
|
|||||||
| Name | Environment Variable | Type | Read-Only | Default | Description |
|
| Name | Environment Variable | Type | Read-Only | Default | Description |
|
||||||
| ---- | -------------------- | ---- | --------- | ------- | ----------- |
|
| ---- | -------------------- | ---- | --------- | ------- | ----------- |
|
||||||
| provider | `EOS_PVFORECAST__PROVIDER` | `Optional[str]` | `rw` | `None` | PVForecast provider id of provider to be used. |
|
| provider | `EOS_PVFORECAST__PROVIDER` | `Optional[str]` | `rw` | `None` | PVForecast provider id of provider to be used. |
|
||||||
| pvforecast0_surface_tilt | `EOS_PVFORECAST__PVFORECAST0_SURFACE_TILT` | `Optional[float]` | `rw` | `None` | Tilt angle from horizontal plane. Ignored for two-axis tracking. |
|
| planes | `EOS_PVFORECAST__PLANES` | `Optional[list[akkudoktoreos.prediction.pvforecast.PVForecastPlaneSetting]]` | `rw` | `None` | Plane configuration. |
|
||||||
| pvforecast0_surface_azimuth | `EOS_PVFORECAST__PVFORECAST0_SURFACE_AZIMUTH` | `Optional[float]` | `rw` | `None` | Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). |
|
|
||||||
| pvforecast0_userhorizon | `EOS_PVFORECAST__PVFORECAST0_USERHORIZON` | `Optional[List[float]]` | `rw` | `None` | Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. |
|
|
||||||
| pvforecast0_peakpower | `EOS_PVFORECAST__PVFORECAST0_PEAKPOWER` | `Optional[float]` | `rw` | `None` | Nominal power of PV system in kW. |
|
|
||||||
| pvforecast0_pvtechchoice | `EOS_PVFORECAST__PVFORECAST0_PVTECHCHOICE` | `Optional[str]` | `rw` | `crystSi` | PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'. |
|
|
||||||
| pvforecast0_mountingplace | `EOS_PVFORECAST__PVFORECAST0_MOUNTINGPLACE` | `Optional[str]` | `rw` | `free` | Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated. |
|
|
||||||
| pvforecast0_loss | `EOS_PVFORECAST__PVFORECAST0_LOSS` | `Optional[float]` | `rw` | `14.0` | Sum of PV system losses in percent |
|
|
||||||
| pvforecast0_trackingtype | `EOS_PVFORECAST__PVFORECAST0_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. |
|
|
||||||
| pvforecast0_optimal_surface_tilt | `EOS_PVFORECAST__PVFORECAST0_OPTIMAL_SURFACE_TILT` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt angle. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast0_optimalangles | `EOS_PVFORECAST__PVFORECAST0_OPTIMALANGLES` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast0_albedo | `EOS_PVFORECAST__PVFORECAST0_ALBEDO` | `Optional[float]` | `rw` | `None` | Proportion of the light hitting the ground that it reflects back. |
|
|
||||||
| pvforecast0_module_model | `EOS_PVFORECAST__PVFORECAST0_MODULE_MODEL` | `Optional[str]` | `rw` | `None` | Model of the PV modules of this plane. |
|
|
||||||
| pvforecast0_inverter_model | `EOS_PVFORECAST__PVFORECAST0_INVERTER_MODEL` | `Optional[str]` | `rw` | `None` | Model of the inverter of this plane. |
|
|
||||||
| pvforecast0_inverter_paco | `EOS_PVFORECAST__PVFORECAST0_INVERTER_PACO` | `Optional[int]` | `rw` | `None` | AC power rating of the inverter. [W] |
|
|
||||||
| pvforecast0_modules_per_string | `EOS_PVFORECAST__PVFORECAST0_MODULES_PER_STRING` | `Optional[int]` | `rw` | `None` | Number of the PV modules of the strings of this plane. |
|
|
||||||
| pvforecast0_strings_per_inverter | `EOS_PVFORECAST__PVFORECAST0_STRINGS_PER_INVERTER` | `Optional[int]` | `rw` | `None` | Number of the strings of the inverter of this plane. |
|
|
||||||
| pvforecast1_surface_tilt | `EOS_PVFORECAST__PVFORECAST1_SURFACE_TILT` | `Optional[float]` | `rw` | `None` | Tilt angle from horizontal plane. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast1_surface_azimuth | `EOS_PVFORECAST__PVFORECAST1_SURFACE_AZIMUTH` | `Optional[float]` | `rw` | `None` | Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). |
|
|
||||||
| pvforecast1_userhorizon | `EOS_PVFORECAST__PVFORECAST1_USERHORIZON` | `Optional[List[float]]` | `rw` | `None` | Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. |
|
|
||||||
| pvforecast1_peakpower | `EOS_PVFORECAST__PVFORECAST1_PEAKPOWER` | `Optional[float]` | `rw` | `None` | Nominal power of PV system in kW. |
|
|
||||||
| pvforecast1_pvtechchoice | `EOS_PVFORECAST__PVFORECAST1_PVTECHCHOICE` | `Optional[str]` | `rw` | `crystSi` | PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'. |
|
|
||||||
| pvforecast1_mountingplace | `EOS_PVFORECAST__PVFORECAST1_MOUNTINGPLACE` | `Optional[str]` | `rw` | `free` | Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated. |
|
|
||||||
| pvforecast1_loss | `EOS_PVFORECAST__PVFORECAST1_LOSS` | `Optional[float]` | `rw` | `14.0` | Sum of PV system losses in percent |
|
|
||||||
| pvforecast1_trackingtype | `EOS_PVFORECAST__PVFORECAST1_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. |
|
|
||||||
| pvforecast1_optimal_surface_tilt | `EOS_PVFORECAST__PVFORECAST1_OPTIMAL_SURFACE_TILT` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt angle. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast1_optimalangles | `EOS_PVFORECAST__PVFORECAST1_OPTIMALANGLES` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast1_albedo | `EOS_PVFORECAST__PVFORECAST1_ALBEDO` | `Optional[float]` | `rw` | `None` | Proportion of the light hitting the ground that it reflects back. |
|
|
||||||
| pvforecast1_module_model | `EOS_PVFORECAST__PVFORECAST1_MODULE_MODEL` | `Optional[str]` | `rw` | `None` | Model of the PV modules of this plane. |
|
|
||||||
| pvforecast1_inverter_model | `EOS_PVFORECAST__PVFORECAST1_INVERTER_MODEL` | `Optional[str]` | `rw` | `None` | Model of the inverter of this plane. |
|
|
||||||
| pvforecast1_inverter_paco | `EOS_PVFORECAST__PVFORECAST1_INVERTER_PACO` | `Optional[int]` | `rw` | `None` | AC power rating of the inverter. [W] |
|
|
||||||
| pvforecast1_modules_per_string | `EOS_PVFORECAST__PVFORECAST1_MODULES_PER_STRING` | `Optional[int]` | `rw` | `None` | Number of the PV modules of the strings of this plane. |
|
|
||||||
| pvforecast1_strings_per_inverter | `EOS_PVFORECAST__PVFORECAST1_STRINGS_PER_INVERTER` | `Optional[int]` | `rw` | `None` | Number of the strings of the inverter of this plane. |
|
|
||||||
| pvforecast2_surface_tilt | `EOS_PVFORECAST__PVFORECAST2_SURFACE_TILT` | `Optional[float]` | `rw` | `None` | Tilt angle from horizontal plane. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast2_surface_azimuth | `EOS_PVFORECAST__PVFORECAST2_SURFACE_AZIMUTH` | `Optional[float]` | `rw` | `None` | Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). |
|
|
||||||
| pvforecast2_userhorizon | `EOS_PVFORECAST__PVFORECAST2_USERHORIZON` | `Optional[List[float]]` | `rw` | `None` | Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. |
|
|
||||||
| pvforecast2_peakpower | `EOS_PVFORECAST__PVFORECAST2_PEAKPOWER` | `Optional[float]` | `rw` | `None` | Nominal power of PV system in kW. |
|
|
||||||
| pvforecast2_pvtechchoice | `EOS_PVFORECAST__PVFORECAST2_PVTECHCHOICE` | `Optional[str]` | `rw` | `crystSi` | PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'. |
|
|
||||||
| pvforecast2_mountingplace | `EOS_PVFORECAST__PVFORECAST2_MOUNTINGPLACE` | `Optional[str]` | `rw` | `free` | Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated. |
|
|
||||||
| pvforecast2_loss | `EOS_PVFORECAST__PVFORECAST2_LOSS` | `Optional[float]` | `rw` | `14.0` | Sum of PV system losses in percent |
|
|
||||||
| pvforecast2_trackingtype | `EOS_PVFORECAST__PVFORECAST2_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. |
|
|
||||||
| pvforecast2_optimal_surface_tilt | `EOS_PVFORECAST__PVFORECAST2_OPTIMAL_SURFACE_TILT` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt angle. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast2_optimalangles | `EOS_PVFORECAST__PVFORECAST2_OPTIMALANGLES` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast2_albedo | `EOS_PVFORECAST__PVFORECAST2_ALBEDO` | `Optional[float]` | `rw` | `None` | Proportion of the light hitting the ground that it reflects back. |
|
|
||||||
| pvforecast2_module_model | `EOS_PVFORECAST__PVFORECAST2_MODULE_MODEL` | `Optional[str]` | `rw` | `None` | Model of the PV modules of this plane. |
|
|
||||||
| pvforecast2_inverter_model | `EOS_PVFORECAST__PVFORECAST2_INVERTER_MODEL` | `Optional[str]` | `rw` | `None` | Model of the inverter of this plane. |
|
|
||||||
| pvforecast2_inverter_paco | `EOS_PVFORECAST__PVFORECAST2_INVERTER_PACO` | `Optional[int]` | `rw` | `None` | AC power rating of the inverter. [W] |
|
|
||||||
| pvforecast2_modules_per_string | `EOS_PVFORECAST__PVFORECAST2_MODULES_PER_STRING` | `Optional[int]` | `rw` | `None` | Number of the PV modules of the strings of this plane. |
|
|
||||||
| pvforecast2_strings_per_inverter | `EOS_PVFORECAST__PVFORECAST2_STRINGS_PER_INVERTER` | `Optional[int]` | `rw` | `None` | Number of the strings of the inverter of this plane. |
|
|
||||||
| pvforecast3_surface_tilt | `EOS_PVFORECAST__PVFORECAST3_SURFACE_TILT` | `Optional[float]` | `rw` | `None` | Tilt angle from horizontal plane. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast3_surface_azimuth | `EOS_PVFORECAST__PVFORECAST3_SURFACE_AZIMUTH` | `Optional[float]` | `rw` | `None` | Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). |
|
|
||||||
| pvforecast3_userhorizon | `EOS_PVFORECAST__PVFORECAST3_USERHORIZON` | `Optional[List[float]]` | `rw` | `None` | Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. |
|
|
||||||
| pvforecast3_peakpower | `EOS_PVFORECAST__PVFORECAST3_PEAKPOWER` | `Optional[float]` | `rw` | `None` | Nominal power of PV system in kW. |
|
|
||||||
| pvforecast3_pvtechchoice | `EOS_PVFORECAST__PVFORECAST3_PVTECHCHOICE` | `Optional[str]` | `rw` | `crystSi` | PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'. |
|
|
||||||
| pvforecast3_mountingplace | `EOS_PVFORECAST__PVFORECAST3_MOUNTINGPLACE` | `Optional[str]` | `rw` | `free` | Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated. |
|
|
||||||
| pvforecast3_loss | `EOS_PVFORECAST__PVFORECAST3_LOSS` | `Optional[float]` | `rw` | `14.0` | Sum of PV system losses in percent |
|
|
||||||
| pvforecast3_trackingtype | `EOS_PVFORECAST__PVFORECAST3_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. |
|
|
||||||
| pvforecast3_optimal_surface_tilt | `EOS_PVFORECAST__PVFORECAST3_OPTIMAL_SURFACE_TILT` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt angle. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast3_optimalangles | `EOS_PVFORECAST__PVFORECAST3_OPTIMALANGLES` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast3_albedo | `EOS_PVFORECAST__PVFORECAST3_ALBEDO` | `Optional[float]` | `rw` | `None` | Proportion of the light hitting the ground that it reflects back. |
|
|
||||||
| pvforecast3_module_model | `EOS_PVFORECAST__PVFORECAST3_MODULE_MODEL` | `Optional[str]` | `rw` | `None` | Model of the PV modules of this plane. |
|
|
||||||
| pvforecast3_inverter_model | `EOS_PVFORECAST__PVFORECAST3_INVERTER_MODEL` | `Optional[str]` | `rw` | `None` | Model of the inverter of this plane. |
|
|
||||||
| pvforecast3_inverter_paco | `EOS_PVFORECAST__PVFORECAST3_INVERTER_PACO` | `Optional[int]` | `rw` | `None` | AC power rating of the inverter. [W] |
|
|
||||||
| pvforecast3_modules_per_string | `EOS_PVFORECAST__PVFORECAST3_MODULES_PER_STRING` | `Optional[int]` | `rw` | `None` | Number of the PV modules of the strings of this plane. |
|
|
||||||
| pvforecast3_strings_per_inverter | `EOS_PVFORECAST__PVFORECAST3_STRINGS_PER_INVERTER` | `Optional[int]` | `rw` | `None` | Number of the strings of the inverter of this plane. |
|
|
||||||
| pvforecast4_surface_tilt | `EOS_PVFORECAST__PVFORECAST4_SURFACE_TILT` | `Optional[float]` | `rw` | `None` | Tilt angle from horizontal plane. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast4_surface_azimuth | `EOS_PVFORECAST__PVFORECAST4_SURFACE_AZIMUTH` | `Optional[float]` | `rw` | `None` | Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). |
|
|
||||||
| pvforecast4_userhorizon | `EOS_PVFORECAST__PVFORECAST4_USERHORIZON` | `Optional[List[float]]` | `rw` | `None` | Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. |
|
|
||||||
| pvforecast4_peakpower | `EOS_PVFORECAST__PVFORECAST4_PEAKPOWER` | `Optional[float]` | `rw` | `None` | Nominal power of PV system in kW. |
|
|
||||||
| pvforecast4_pvtechchoice | `EOS_PVFORECAST__PVFORECAST4_PVTECHCHOICE` | `Optional[str]` | `rw` | `crystSi` | PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'. |
|
|
||||||
| pvforecast4_mountingplace | `EOS_PVFORECAST__PVFORECAST4_MOUNTINGPLACE` | `Optional[str]` | `rw` | `free` | Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated. |
|
|
||||||
| pvforecast4_loss | `EOS_PVFORECAST__PVFORECAST4_LOSS` | `Optional[float]` | `rw` | `14.0` | Sum of PV system losses in percent |
|
|
||||||
| pvforecast4_trackingtype | `EOS_PVFORECAST__PVFORECAST4_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. |
|
|
||||||
| pvforecast4_optimal_surface_tilt | `EOS_PVFORECAST__PVFORECAST4_OPTIMAL_SURFACE_TILT` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt angle. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast4_optimalangles | `EOS_PVFORECAST__PVFORECAST4_OPTIMALANGLES` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast4_albedo | `EOS_PVFORECAST__PVFORECAST4_ALBEDO` | `Optional[float]` | `rw` | `None` | Proportion of the light hitting the ground that it reflects back. |
|
|
||||||
| pvforecast4_module_model | `EOS_PVFORECAST__PVFORECAST4_MODULE_MODEL` | `Optional[str]` | `rw` | `None` | Model of the PV modules of this plane. |
|
|
||||||
| pvforecast4_inverter_model | `EOS_PVFORECAST__PVFORECAST4_INVERTER_MODEL` | `Optional[str]` | `rw` | `None` | Model of the inverter of this plane. |
|
|
||||||
| pvforecast4_inverter_paco | `EOS_PVFORECAST__PVFORECAST4_INVERTER_PACO` | `Optional[int]` | `rw` | `None` | AC power rating of the inverter. [W] |
|
|
||||||
| pvforecast4_modules_per_string | `EOS_PVFORECAST__PVFORECAST4_MODULES_PER_STRING` | `Optional[int]` | `rw` | `None` | Number of the PV modules of the strings of this plane. |
|
|
||||||
| pvforecast4_strings_per_inverter | `EOS_PVFORECAST__PVFORECAST4_STRINGS_PER_INVERTER` | `Optional[int]` | `rw` | `None` | Number of the strings of the inverter of this plane. |
|
|
||||||
| pvforecast5_surface_tilt | `EOS_PVFORECAST__PVFORECAST5_SURFACE_TILT` | `Optional[float]` | `rw` | `None` | Tilt angle from horizontal plane. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast5_surface_azimuth | `EOS_PVFORECAST__PVFORECAST5_SURFACE_AZIMUTH` | `Optional[float]` | `rw` | `None` | Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). |
|
|
||||||
| pvforecast5_userhorizon | `EOS_PVFORECAST__PVFORECAST5_USERHORIZON` | `Optional[List[float]]` | `rw` | `None` | Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. |
|
|
||||||
| pvforecast5_peakpower | `EOS_PVFORECAST__PVFORECAST5_PEAKPOWER` | `Optional[float]` | `rw` | `None` | Nominal power of PV system in kW. |
|
|
||||||
| pvforecast5_pvtechchoice | `EOS_PVFORECAST__PVFORECAST5_PVTECHCHOICE` | `Optional[str]` | `rw` | `crystSi` | PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'. |
|
|
||||||
| pvforecast5_mountingplace | `EOS_PVFORECAST__PVFORECAST5_MOUNTINGPLACE` | `Optional[str]` | `rw` | `free` | Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated. |
|
|
||||||
| pvforecast5_loss | `EOS_PVFORECAST__PVFORECAST5_LOSS` | `Optional[float]` | `rw` | `14.0` | Sum of PV system losses in percent |
|
|
||||||
| pvforecast5_trackingtype | `EOS_PVFORECAST__PVFORECAST5_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. |
|
|
||||||
| pvforecast5_optimal_surface_tilt | `EOS_PVFORECAST__PVFORECAST5_OPTIMAL_SURFACE_TILT` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt angle. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast5_optimalangles | `EOS_PVFORECAST__PVFORECAST5_OPTIMALANGLES` | `Optional[bool]` | `rw` | `False` | Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking. |
|
|
||||||
| pvforecast5_albedo | `EOS_PVFORECAST__PVFORECAST5_ALBEDO` | `Optional[float]` | `rw` | `None` | Proportion of the light hitting the ground that it reflects back. |
|
|
||||||
| pvforecast5_module_model | `EOS_PVFORECAST__PVFORECAST5_MODULE_MODEL` | `Optional[str]` | `rw` | `None` | Model of the PV modules of this plane. |
|
|
||||||
| pvforecast5_inverter_model | `EOS_PVFORECAST__PVFORECAST5_INVERTER_MODEL` | `Optional[str]` | `rw` | `None` | Model of the inverter of this plane. |
|
|
||||||
| pvforecast5_inverter_paco | `EOS_PVFORECAST__PVFORECAST5_INVERTER_PACO` | `Optional[int]` | `rw` | `None` | AC power rating of the inverter. [W] |
|
|
||||||
| pvforecast5_modules_per_string | `EOS_PVFORECAST__PVFORECAST5_MODULES_PER_STRING` | `Optional[int]` | `rw` | `None` | Number of the PV modules of the strings of this plane. |
|
|
||||||
| pvforecast5_strings_per_inverter | `EOS_PVFORECAST__PVFORECAST5_STRINGS_PER_INVERTER` | `Optional[int]` | `rw` | `None` | Number of the strings of the inverter of this plane. |
|
|
||||||
| provider_settings | `EOS_PVFORECAST__PROVIDER_SETTINGS` | `Optional[akkudoktoreos.prediction.pvforecastimport.PVForecastImportCommonSettings]` | `rw` | `None` | Provider settings |
|
| provider_settings | `EOS_PVFORECAST__PROVIDER_SETTINGS` | `Optional[akkudoktoreos.prediction.pvforecastimport.PVForecastImportCommonSettings]` | `rw` | `None` | Provider settings |
|
||||||
| pvforecast_planes | | `List[str]` | `ro` | `N/A` | Compute a list of active planes. |
|
| planes_peakpower | | `List[float]` | `ro` | `N/A` | Compute a list of the peak power per active planes. |
|
||||||
| pvforecast_planes_peakpower | | `List[float]` | `ro` | `N/A` | Compute a list of the peak power per active planes. |
|
| planes_azimuth | | `List[float]` | `ro` | `N/A` | Compute a list of the azimuths per active planes. |
|
||||||
| pvforecast_planes_azimuth | | `List[float]` | `ro` | `N/A` | Compute a list of the azimuths per active planes. |
|
| planes_tilt | | `List[float]` | `ro` | `N/A` | Compute a list of the tilts per active planes. |
|
||||||
| pvforecast_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. |
|
||||||
| pvforecast_planes_userhorizon | | `Any` | `ro` | `N/A` | Compute a list of the user horizon per active planes. |
|
| planes_inverter_paco | | `Any` | `ro` | `N/A` | Compute a list of the maximum power rating of the inverter per active planes. |
|
||||||
| pvforecast_planes_inverter_paco | | `Any` | `ro` | `N/A` | Compute a list of the maximum power rating of the inverter per active planes. |
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Example Input
|
### Example Input
|
||||||
@ -623,110 +527,52 @@ Validators:
|
|||||||
{
|
{
|
||||||
"pvforecast": {
|
"pvforecast": {
|
||||||
"provider": "PVForecastAkkudoktor",
|
"provider": "PVForecastAkkudoktor",
|
||||||
"pvforecast0_surface_tilt": 10.0,
|
"planes": [
|
||||||
"pvforecast0_surface_azimuth": 10.0,
|
{
|
||||||
"pvforecast0_userhorizon": [
|
"surface_tilt": 10.0,
|
||||||
10.0,
|
"surface_azimuth": 10.0,
|
||||||
20.0,
|
"userhorizon": [
|
||||||
30.0
|
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": 20.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
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"pvforecast0_peakpower": 5.0,
|
|
||||||
"pvforecast0_pvtechchoice": "crystSi",
|
|
||||||
"pvforecast0_mountingplace": "free",
|
|
||||||
"pvforecast0_loss": 14.0,
|
|
||||||
"pvforecast0_trackingtype": 0,
|
|
||||||
"pvforecast0_optimal_surface_tilt": false,
|
|
||||||
"pvforecast0_optimalangles": false,
|
|
||||||
"pvforecast0_albedo": null,
|
|
||||||
"pvforecast0_module_model": null,
|
|
||||||
"pvforecast0_inverter_model": null,
|
|
||||||
"pvforecast0_inverter_paco": 6000,
|
|
||||||
"pvforecast0_modules_per_string": 20,
|
|
||||||
"pvforecast0_strings_per_inverter": 2,
|
|
||||||
"pvforecast1_surface_tilt": 20.0,
|
|
||||||
"pvforecast1_surface_azimuth": 20.0,
|
|
||||||
"pvforecast1_userhorizon": [
|
|
||||||
5.0,
|
|
||||||
15.0,
|
|
||||||
25.0
|
|
||||||
],
|
|
||||||
"pvforecast1_peakpower": 3.5,
|
|
||||||
"pvforecast1_pvtechchoice": "crystSi",
|
|
||||||
"pvforecast1_mountingplace": "free",
|
|
||||||
"pvforecast1_loss": 14.0,
|
|
||||||
"pvforecast1_trackingtype": null,
|
|
||||||
"pvforecast1_optimal_surface_tilt": false,
|
|
||||||
"pvforecast1_optimalangles": false,
|
|
||||||
"pvforecast1_albedo": null,
|
|
||||||
"pvforecast1_module_model": null,
|
|
||||||
"pvforecast1_inverter_model": null,
|
|
||||||
"pvforecast1_inverter_paco": 4000,
|
|
||||||
"pvforecast1_modules_per_string": 20,
|
|
||||||
"pvforecast1_strings_per_inverter": 2,
|
|
||||||
"pvforecast2_surface_tilt": null,
|
|
||||||
"pvforecast2_surface_azimuth": null,
|
|
||||||
"pvforecast2_userhorizon": null,
|
|
||||||
"pvforecast2_peakpower": null,
|
|
||||||
"pvforecast2_pvtechchoice": null,
|
|
||||||
"pvforecast2_mountingplace": null,
|
|
||||||
"pvforecast2_loss": null,
|
|
||||||
"pvforecast2_trackingtype": null,
|
|
||||||
"pvforecast2_optimal_surface_tilt": null,
|
|
||||||
"pvforecast2_optimalangles": null,
|
|
||||||
"pvforecast2_albedo": null,
|
|
||||||
"pvforecast2_module_model": null,
|
|
||||||
"pvforecast2_inverter_model": null,
|
|
||||||
"pvforecast2_inverter_paco": null,
|
|
||||||
"pvforecast2_modules_per_string": null,
|
|
||||||
"pvforecast2_strings_per_inverter": null,
|
|
||||||
"pvforecast3_surface_tilt": null,
|
|
||||||
"pvforecast3_surface_azimuth": null,
|
|
||||||
"pvforecast3_userhorizon": null,
|
|
||||||
"pvforecast3_peakpower": null,
|
|
||||||
"pvforecast3_pvtechchoice": null,
|
|
||||||
"pvforecast3_mountingplace": null,
|
|
||||||
"pvforecast3_loss": null,
|
|
||||||
"pvforecast3_trackingtype": null,
|
|
||||||
"pvforecast3_optimal_surface_tilt": null,
|
|
||||||
"pvforecast3_optimalangles": null,
|
|
||||||
"pvforecast3_albedo": null,
|
|
||||||
"pvforecast3_module_model": null,
|
|
||||||
"pvforecast3_inverter_model": null,
|
|
||||||
"pvforecast3_inverter_paco": null,
|
|
||||||
"pvforecast3_modules_per_string": null,
|
|
||||||
"pvforecast3_strings_per_inverter": null,
|
|
||||||
"pvforecast4_surface_tilt": null,
|
|
||||||
"pvforecast4_surface_azimuth": null,
|
|
||||||
"pvforecast4_userhorizon": null,
|
|
||||||
"pvforecast4_peakpower": null,
|
|
||||||
"pvforecast4_pvtechchoice": null,
|
|
||||||
"pvforecast4_mountingplace": null,
|
|
||||||
"pvforecast4_loss": null,
|
|
||||||
"pvforecast4_trackingtype": null,
|
|
||||||
"pvforecast4_optimal_surface_tilt": null,
|
|
||||||
"pvforecast4_optimalangles": null,
|
|
||||||
"pvforecast4_albedo": null,
|
|
||||||
"pvforecast4_module_model": null,
|
|
||||||
"pvforecast4_inverter_model": null,
|
|
||||||
"pvforecast4_inverter_paco": null,
|
|
||||||
"pvforecast4_modules_per_string": null,
|
|
||||||
"pvforecast4_strings_per_inverter": null,
|
|
||||||
"pvforecast5_surface_tilt": null,
|
|
||||||
"pvforecast5_surface_azimuth": null,
|
|
||||||
"pvforecast5_userhorizon": null,
|
|
||||||
"pvforecast5_peakpower": null,
|
|
||||||
"pvforecast5_pvtechchoice": null,
|
|
||||||
"pvforecast5_mountingplace": null,
|
|
||||||
"pvforecast5_loss": null,
|
|
||||||
"pvforecast5_trackingtype": null,
|
|
||||||
"pvforecast5_optimal_surface_tilt": null,
|
|
||||||
"pvforecast5_optimalangles": null,
|
|
||||||
"pvforecast5_albedo": null,
|
|
||||||
"pvforecast5_module_model": null,
|
|
||||||
"pvforecast5_inverter_model": null,
|
|
||||||
"pvforecast5_inverter_paco": null,
|
|
||||||
"pvforecast5_modules_per_string": null,
|
|
||||||
"pvforecast5_strings_per_inverter": null,
|
|
||||||
"provider_settings": null
|
"provider_settings": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -740,128 +586,66 @@ Validators:
|
|||||||
{
|
{
|
||||||
"pvforecast": {
|
"pvforecast": {
|
||||||
"provider": "PVForecastAkkudoktor",
|
"provider": "PVForecastAkkudoktor",
|
||||||
"pvforecast0_surface_tilt": 10.0,
|
"planes": [
|
||||||
"pvforecast0_surface_azimuth": 10.0,
|
{
|
||||||
"pvforecast0_userhorizon": [
|
"surface_tilt": 10.0,
|
||||||
10.0,
|
"surface_azimuth": 10.0,
|
||||||
20.0,
|
"userhorizon": [
|
||||||
30.0
|
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": 20.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
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"pvforecast0_peakpower": 5.0,
|
|
||||||
"pvforecast0_pvtechchoice": "crystSi",
|
|
||||||
"pvforecast0_mountingplace": "free",
|
|
||||||
"pvforecast0_loss": 14.0,
|
|
||||||
"pvforecast0_trackingtype": 0,
|
|
||||||
"pvforecast0_optimal_surface_tilt": false,
|
|
||||||
"pvforecast0_optimalangles": false,
|
|
||||||
"pvforecast0_albedo": null,
|
|
||||||
"pvforecast0_module_model": null,
|
|
||||||
"pvforecast0_inverter_model": null,
|
|
||||||
"pvforecast0_inverter_paco": 6000,
|
|
||||||
"pvforecast0_modules_per_string": 20,
|
|
||||||
"pvforecast0_strings_per_inverter": 2,
|
|
||||||
"pvforecast1_surface_tilt": 20.0,
|
|
||||||
"pvforecast1_surface_azimuth": 20.0,
|
|
||||||
"pvforecast1_userhorizon": [
|
|
||||||
5.0,
|
|
||||||
15.0,
|
|
||||||
25.0
|
|
||||||
],
|
|
||||||
"pvforecast1_peakpower": 3.5,
|
|
||||||
"pvforecast1_pvtechchoice": "crystSi",
|
|
||||||
"pvforecast1_mountingplace": "free",
|
|
||||||
"pvforecast1_loss": 14.0,
|
|
||||||
"pvforecast1_trackingtype": null,
|
|
||||||
"pvforecast1_optimal_surface_tilt": false,
|
|
||||||
"pvforecast1_optimalangles": false,
|
|
||||||
"pvforecast1_albedo": null,
|
|
||||||
"pvforecast1_module_model": null,
|
|
||||||
"pvforecast1_inverter_model": null,
|
|
||||||
"pvforecast1_inverter_paco": 4000,
|
|
||||||
"pvforecast1_modules_per_string": 20,
|
|
||||||
"pvforecast1_strings_per_inverter": 2,
|
|
||||||
"pvforecast2_surface_tilt": null,
|
|
||||||
"pvforecast2_surface_azimuth": null,
|
|
||||||
"pvforecast2_userhorizon": null,
|
|
||||||
"pvforecast2_peakpower": null,
|
|
||||||
"pvforecast2_pvtechchoice": null,
|
|
||||||
"pvforecast2_mountingplace": null,
|
|
||||||
"pvforecast2_loss": null,
|
|
||||||
"pvforecast2_trackingtype": null,
|
|
||||||
"pvforecast2_optimal_surface_tilt": null,
|
|
||||||
"pvforecast2_optimalangles": null,
|
|
||||||
"pvforecast2_albedo": null,
|
|
||||||
"pvforecast2_module_model": null,
|
|
||||||
"pvforecast2_inverter_model": null,
|
|
||||||
"pvforecast2_inverter_paco": null,
|
|
||||||
"pvforecast2_modules_per_string": null,
|
|
||||||
"pvforecast2_strings_per_inverter": null,
|
|
||||||
"pvforecast3_surface_tilt": null,
|
|
||||||
"pvforecast3_surface_azimuth": null,
|
|
||||||
"pvforecast3_userhorizon": null,
|
|
||||||
"pvforecast3_peakpower": null,
|
|
||||||
"pvforecast3_pvtechchoice": null,
|
|
||||||
"pvforecast3_mountingplace": null,
|
|
||||||
"pvforecast3_loss": null,
|
|
||||||
"pvforecast3_trackingtype": null,
|
|
||||||
"pvforecast3_optimal_surface_tilt": null,
|
|
||||||
"pvforecast3_optimalangles": null,
|
|
||||||
"pvforecast3_albedo": null,
|
|
||||||
"pvforecast3_module_model": null,
|
|
||||||
"pvforecast3_inverter_model": null,
|
|
||||||
"pvforecast3_inverter_paco": null,
|
|
||||||
"pvforecast3_modules_per_string": null,
|
|
||||||
"pvforecast3_strings_per_inverter": null,
|
|
||||||
"pvforecast4_surface_tilt": null,
|
|
||||||
"pvforecast4_surface_azimuth": null,
|
|
||||||
"pvforecast4_userhorizon": null,
|
|
||||||
"pvforecast4_peakpower": null,
|
|
||||||
"pvforecast4_pvtechchoice": null,
|
|
||||||
"pvforecast4_mountingplace": null,
|
|
||||||
"pvforecast4_loss": null,
|
|
||||||
"pvforecast4_trackingtype": null,
|
|
||||||
"pvforecast4_optimal_surface_tilt": null,
|
|
||||||
"pvforecast4_optimalangles": null,
|
|
||||||
"pvforecast4_albedo": null,
|
|
||||||
"pvforecast4_module_model": null,
|
|
||||||
"pvforecast4_inverter_model": null,
|
|
||||||
"pvforecast4_inverter_paco": null,
|
|
||||||
"pvforecast4_modules_per_string": null,
|
|
||||||
"pvforecast4_strings_per_inverter": null,
|
|
||||||
"pvforecast5_surface_tilt": null,
|
|
||||||
"pvforecast5_surface_azimuth": null,
|
|
||||||
"pvforecast5_userhorizon": null,
|
|
||||||
"pvforecast5_peakpower": null,
|
|
||||||
"pvforecast5_pvtechchoice": null,
|
|
||||||
"pvforecast5_mountingplace": null,
|
|
||||||
"pvforecast5_loss": null,
|
|
||||||
"pvforecast5_trackingtype": null,
|
|
||||||
"pvforecast5_optimal_surface_tilt": null,
|
|
||||||
"pvforecast5_optimalangles": null,
|
|
||||||
"pvforecast5_albedo": null,
|
|
||||||
"pvforecast5_module_model": null,
|
|
||||||
"pvforecast5_inverter_model": null,
|
|
||||||
"pvforecast5_inverter_paco": null,
|
|
||||||
"pvforecast5_modules_per_string": null,
|
|
||||||
"pvforecast5_strings_per_inverter": null,
|
|
||||||
"provider_settings": null,
|
"provider_settings": null,
|
||||||
"pvforecast_planes": [
|
"planes_peakpower": [
|
||||||
"pvforecast0",
|
|
||||||
"pvforecast1"
|
|
||||||
],
|
|
||||||
"pvforecast_planes_peakpower": [
|
|
||||||
5.0,
|
5.0,
|
||||||
3.5
|
3.5
|
||||||
],
|
],
|
||||||
"pvforecast_planes_azimuth": [
|
"planes_azimuth": [
|
||||||
10.0,
|
10.0,
|
||||||
20.0
|
20.0
|
||||||
],
|
],
|
||||||
"pvforecast_planes_tilt": [
|
"planes_tilt": [
|
||||||
10.0,
|
10.0,
|
||||||
20.0
|
20.0
|
||||||
],
|
],
|
||||||
"pvforecast_planes_userhorizon": [
|
"planes_userhorizon": [
|
||||||
[
|
[
|
||||||
10.0,
|
10.0,
|
||||||
20.0,
|
20.0,
|
||||||
@ -873,7 +657,7 @@ Validators:
|
|||||||
25.0
|
25.0
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"pvforecast_planes_inverter_paco": [
|
"planes_inverter_paco": [
|
||||||
6000.0,
|
6000.0,
|
||||||
4000.0
|
4000.0
|
||||||
]
|
]
|
||||||
@ -889,8 +673,8 @@ Validators:
|
|||||||
|
|
||||||
| Name | Type | Read-Only | Default | Description |
|
| Name | Type | Read-Only | Default | Description |
|
||||||
| ---- | ---- | --------- | ------- | ----------- |
|
| ---- | ---- | --------- | ------- | ----------- |
|
||||||
| pvforecastimport_file_path | `Union[str, pathlib.Path, NoneType]` | `rw` | `None` | Path to the file to import PV forecast data from. |
|
| import_file_path | `Union[str, pathlib.Path, NoneType]` | `rw` | `None` | Path to the file to import PV forecast data from. |
|
||||||
| pvforecastimport_json | `Optional[str]` | `rw` | `None` | JSON string, dictionary of PV forecast value lists. |
|
| import_json | `Optional[str]` | `rw` | `None` | JSON string, dictionary of PV forecast value lists. |
|
||||||
:::
|
:::
|
||||||
|
|
||||||
#### Example Input/Output
|
#### Example Input/Output
|
||||||
@ -901,13 +685,96 @@ Validators:
|
|||||||
{
|
{
|
||||||
"pvforecast": {
|
"pvforecast": {
|
||||||
"provider_settings": {
|
"provider_settings": {
|
||||||
"pvforecastimport_file_path": null,
|
"import_file_path": null,
|
||||||
"pvforecastimport_json": "{\"pvforecast_ac_power\": [0, 8.05, 352.91]}"
|
"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 |
|
||||||
|
| ---- | ---- | --------- | ------- | ----------- |
|
||||||
|
| surface_tilt | `Optional[float]` | `rw` | `None` | Tilt angle from horizontal plane. Ignored for two-axis tracking. |
|
||||||
|
| surface_azimuth | `Optional[float]` | `rw` | `None` | Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). |
|
||||||
|
| userhorizon | `Optional[List[float]]` | `rw` | `None` | Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. |
|
||||||
|
| 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'. |
|
||||||
|
| mountingplace | `Optional[str]` | `rw` | `free` | Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated. |
|
||||||
|
| loss | `Optional[float]` | `rw` | `14.0` | Sum of PV system losses in percent |
|
||||||
|
| 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. |
|
||||||
|
| 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. |
|
||||||
|
| albedo | `Optional[float]` | `rw` | `None` | Proportion of the light hitting the ground that it reflects back. |
|
||||||
|
| module_model | `Optional[str]` | `rw` | `None` | Model of the PV modules of this plane. |
|
||||||
|
| 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] |
|
||||||
|
| modules_per_string | `Optional[int]` | `rw` | `None` | Number of the PV modules of the strings of this plane. |
|
||||||
|
| strings_per_inverter | `Optional[int]` | `rw` | `None` | Number of the strings of the inverter of this plane. |
|
||||||
|
:::
|
||||||
|
|
||||||
|
#### Example Input/Output
|
||||||
|
|
||||||
|
```{eval-rst}
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"pvforecast": {
|
||||||
|
"planes": [
|
||||||
|
{
|
||||||
|
"surface_tilt": 10.0,
|
||||||
|
"surface_azimuth": 10.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": 20.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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Weather Forecast Configuration
|
## Weather Forecast Configuration
|
||||||
|
|
||||||
:::{table} weather
|
:::{table} weather
|
||||||
|
@ -211,8 +211,8 @@ Configuration options:
|
|||||||
- `pvforecast<0..5>_inverter_paco`: AC power rating of the inverter. [W]
|
- `pvforecast<0..5>_inverter_paco`: AC power rating of the inverter. [W]
|
||||||
- `pvforecast<0..5>_modules_per_string`: Number of the PV modules of the strings of this plane.
|
- `pvforecast<0..5>_modules_per_string`: Number of the PV modules of the strings of this plane.
|
||||||
- `pvforecast<0..5>_strings_per_inverter`: Number of the strings of the inverter of this plane.
|
- `pvforecast<0..5>_strings_per_inverter`: Number of the strings of the inverter of this plane.
|
||||||
- `pvforecastimport_file_path`: Path to the file to import PV forecast data from.
|
- `import_file_path`: Path to the file to import PV forecast data from.
|
||||||
- `pvforecastimport_json`: JSON string, dictionary of PV forecast value lists.
|
- `import_json`: JSON string, dictionary of PV forecast value lists.
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
@ -337,7 +337,7 @@ The prediction keys for the PV forecast data are:
|
|||||||
|
|
||||||
The PV forecast data must be provided in one of the formats described in
|
The PV forecast data must be provided in one of the formats described in
|
||||||
<project:#prediction-import-providers>. The data source must be given in the
|
<project:#prediction-import-providers>. The data source must be given in the
|
||||||
`pvforecastimport_file_path` or `pvforecastimport_json` configuration option.
|
`import_file_path` or `import_json` configuration option.
|
||||||
|
|
||||||
## Weather Prediction
|
## Weather Prediction
|
||||||
|
|
||||||
@ -459,4 +459,4 @@ The prediction keys for the PV forecast data are:
|
|||||||
|
|
||||||
The PV forecast data must be provided in one of the formats described in
|
The PV forecast data must be provided in one of the formats described in
|
||||||
<project:#prediction-import-providers>. The data source must be given in the
|
<project:#prediction-import-providers>. The data source must be given in the
|
||||||
`import_file_path` or `pvforecastimport_json` configuration option.
|
`import_file_path` or `import_json` configuration option.
|
||||||
|
3489
openapi.json
3489
openapi.json
File diff suppressed because it is too large
Load Diff
@ -13,6 +13,7 @@ from pydantic_core import PydanticUndefined
|
|||||||
from akkudoktoreos.config.config import get_config
|
from akkudoktoreos.config.config import get_config
|
||||||
from akkudoktoreos.core.logging import get_logger
|
from akkudoktoreos.core.logging import get_logger
|
||||||
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
||||||
|
from akkudoktoreos.utils.docs import get_model_structure_from_examples
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
@ -51,23 +52,13 @@ def resolve_nested_types(field_type: Any, parent_types: list[str]) -> list[tuple
|
|||||||
return resolved_types
|
return resolved_types
|
||||||
|
|
||||||
|
|
||||||
def get_example_or_default(field_name: str, field_info: FieldInfo) -> dict[str, Any]:
|
def create_model_from_examples(
|
||||||
"""Generate a default value for a field, considering constraints."""
|
model_class: PydanticBaseModel, multiple: bool
|
||||||
if field_info.examples is not None:
|
) -> list[PydanticBaseModel]:
|
||||||
return field_info.examples[0]
|
|
||||||
|
|
||||||
if field_info.default is not None:
|
|
||||||
return field_info.default
|
|
||||||
|
|
||||||
raise NotImplementedError(f"No default or example provided '{field_name}': {field_info}")
|
|
||||||
|
|
||||||
|
|
||||||
def create_model_from_examples(model_class: PydanticBaseModel) -> PydanticBaseModel:
|
|
||||||
"""Create a model instance with default or example values, respecting constraints."""
|
"""Create a model instance with default or example values, respecting constraints."""
|
||||||
example_data = {}
|
return [
|
||||||
for field_name, field_info in model_class.model_fields.items():
|
model_class(**data) for data in get_model_structure_from_examples(model_class, multiple)
|
||||||
example_data[field_name] = get_example_or_default(field_name, field_info)
|
]
|
||||||
return model_class(**example_data)
|
|
||||||
|
|
||||||
|
|
||||||
def build_nested_structure(keys: list[str], value: Any) -> Any:
|
def build_nested_structure(keys: list[str], value: Any) -> Any:
|
||||||
@ -198,21 +189,29 @@ def generate_config_table_md(
|
|||||||
if toplevel:
|
if toplevel:
|
||||||
table += ":::\n\n" # Add an empty line after the table
|
table += ":::\n\n" # Add an empty line after the table
|
||||||
|
|
||||||
ins = create_model_from_examples(config)
|
has_examples_list = toplevel_keys[-1] == "list"
|
||||||
if ins:
|
instance_list = create_model_from_examples(config, has_examples_list)
|
||||||
# Transform to JSON (and manually to dict) to use custom serializers and then merge with parent keys
|
if instance_list:
|
||||||
ins_json = ins.model_dump_json(include_computed_fields=False)
|
ins_dict_list = []
|
||||||
ins_dict = json.loads(ins_json)
|
ins_out_dict_list = []
|
||||||
|
for ins in instance_list:
|
||||||
|
# Transform to JSON (and manually to dict) to use custom serializers and then merge with parent keys
|
||||||
|
ins_json = ins.model_dump_json(include_computed_fields=False)
|
||||||
|
ins_dict_list.append(json.loads(ins_json))
|
||||||
|
|
||||||
ins_out_json = ins.model_dump_json(include_computed_fields=True)
|
ins_out_json = ins.model_dump_json(include_computed_fields=True)
|
||||||
ins_out_dict = json.loads(ins_out_json)
|
ins_out_dict_list.append(json.loads(ins_out_json))
|
||||||
same_output = ins_out_dict == ins_dict
|
|
||||||
|
same_output = ins_out_dict_list == ins_dict_list
|
||||||
same_output_str = "/Output" if same_output else ""
|
same_output_str = "/Output" if same_output else ""
|
||||||
|
|
||||||
table += f"#{heading_level} Example Input{same_output_str}\n\n"
|
table += f"#{heading_level} Example Input{same_output_str}\n\n"
|
||||||
table += "```{eval-rst}\n"
|
table += "```{eval-rst}\n"
|
||||||
table += ".. code-block:: json\n\n"
|
table += ".. code-block:: json\n\n"
|
||||||
input_dict = build_nested_structure(toplevel_keys, ins_dict)
|
if has_examples_list:
|
||||||
|
input_dict = build_nested_structure(toplevel_keys[:-1], ins_dict_list)
|
||||||
|
else:
|
||||||
|
input_dict = build_nested_structure(toplevel_keys, ins_dict_list[0])
|
||||||
table += textwrap.indent(json.dumps(input_dict, indent=4), " ")
|
table += textwrap.indent(json.dumps(input_dict, indent=4), " ")
|
||||||
table += "\n"
|
table += "\n"
|
||||||
table += "```\n\n"
|
table += "```\n\n"
|
||||||
@ -221,7 +220,10 @@ def generate_config_table_md(
|
|||||||
table += f"#{heading_level} Example Output\n\n"
|
table += f"#{heading_level} Example Output\n\n"
|
||||||
table += "```{eval-rst}\n"
|
table += "```{eval-rst}\n"
|
||||||
table += ".. code-block:: json\n\n"
|
table += ".. code-block:: json\n\n"
|
||||||
output_dict = build_nested_structure(toplevel_keys, ins_out_dict)
|
if has_examples_list:
|
||||||
|
output_dict = build_nested_structure(toplevel_keys[:-1], ins_out_dict_list)
|
||||||
|
else:
|
||||||
|
output_dict = build_nested_structure(toplevel_keys, ins_out_dict_list[0])
|
||||||
table += textwrap.indent(json.dumps(output_dict, indent=4), " ")
|
table += textwrap.indent(json.dumps(output_dict, indent=4), " ")
|
||||||
table += "\n"
|
table += "\n"
|
||||||
table += "```\n\n"
|
table += "```\n\n"
|
||||||
|
@ -24,27 +24,36 @@ def config_pvforecast() -> dict:
|
|||||||
},
|
},
|
||||||
"pvforecast": {
|
"pvforecast": {
|
||||||
"provider": "PVForecastAkkudoktor",
|
"provider": "PVForecastAkkudoktor",
|
||||||
"pvforecast0_peakpower": 5.0,
|
"planes": [
|
||||||
"pvforecast0_surface_azimuth": -10,
|
{
|
||||||
"pvforecast0_surface_tilt": 7,
|
"peakpower": 5.0,
|
||||||
"pvforecast0_userhorizon": [20, 27, 22, 20],
|
"surface_azimuth": -10,
|
||||||
"pvforecast0_inverter_paco": 10000,
|
"surface_tilt": 7,
|
||||||
"pvforecast1_peakpower": 4.8,
|
"userhorizon": [20, 27, 22, 20],
|
||||||
"pvforecast1_surface_azimuth": -90,
|
"inverter_paco": 10000,
|
||||||
"pvforecast1_surface_tilt": 7,
|
},
|
||||||
"pvforecast1_userhorizon": [30, 30, 30, 50],
|
{
|
||||||
"pvforecast1_inverter_paco": 10000,
|
"peakpower": 4.8,
|
||||||
"pvforecast2_peakpower": 1.4,
|
"surface_azimuth": -90,
|
||||||
"pvforecast2_surface_azimuth": -40,
|
"surface_tilt": 7,
|
||||||
"pvforecast2_surface_tilt": 60,
|
"userhorizon": [30, 30, 30, 50],
|
||||||
"pvforecast2_userhorizon": [60, 30, 0, 30],
|
"inverter_paco": 10000,
|
||||||
"pvforecast2_inverter_paco": 2000,
|
},
|
||||||
"pvforecast3_peakpower": 1.6,
|
{
|
||||||
"pvforecast3_surface_azimuth": 5,
|
"peakpower": 1.4,
|
||||||
"pvforecast3_surface_tilt": 45,
|
"surface_azimuth": -40,
|
||||||
"pvforecast3_userhorizon": [45, 25, 30, 60],
|
"surface_tilt": 60,
|
||||||
"pvforecast3_inverter_paco": 1400,
|
"userhorizon": [60, 30, 0, 30],
|
||||||
"pvforecast4_peakpower": None,
|
"inverter_paco": 2000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"peakpower": 1.6,
|
||||||
|
"surface_azimuth": 5,
|
||||||
|
"surface_tilt": 45,
|
||||||
|
"userhorizon": [45, 25, 30, 60],
|
||||||
|
"inverter_paco": 1400,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return settings
|
return settings
|
||||||
@ -112,7 +121,7 @@ def run_prediction(provider_id: str, verbose: bool = False) -> str:
|
|||||||
elif provider_id in ("BrightSky", "ClearOutside"):
|
elif provider_id in ("BrightSky", "ClearOutside"):
|
||||||
settings = config_weather()
|
settings = config_weather()
|
||||||
settings["weather"]["provider"] = provider_id
|
settings["weather"]["provider"] = provider_id
|
||||||
elif provider_id in ("Akkudoktor",):
|
elif provider_id in ("ElecPriceAkkudoktor",):
|
||||||
settings = config_elecprice()
|
settings = config_elecprice()
|
||||||
settings["elecprice"]["provider"] = provider_id
|
settings["elecprice"]["provider"] = provider_id
|
||||||
elif provider_id in ("LoadAkkudoktor",):
|
elif provider_id in ("LoadAkkudoktor",):
|
||||||
|
@ -1,16 +1,113 @@
|
|||||||
"""PV forecast module for PV power predictions."""
|
"""PV forecast module for PV power predictions."""
|
||||||
|
|
||||||
from typing import Any, ClassVar, List, Optional
|
from typing import Any, ClassVar, List, Optional, Self
|
||||||
|
|
||||||
from pydantic import Field, computed_field
|
from pydantic import Field, computed_field, field_validator, model_validator
|
||||||
|
|
||||||
from akkudoktoreos.config.configabc import SettingsBaseModel
|
from akkudoktoreos.config.configabc import SettingsBaseModel
|
||||||
from akkudoktoreos.core.logging import get_logger
|
from akkudoktoreos.core.logging import get_logger
|
||||||
from akkudoktoreos.prediction.pvforecastimport import PVForecastImportCommonSettings
|
from akkudoktoreos.prediction.pvforecastimport import PVForecastImportCommonSettings
|
||||||
|
from akkudoktoreos.utils.docs import get_model_structure_from_examples
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PVForecastPlaneSetting(SettingsBaseModel):
|
||||||
|
"""PV Forecast Plane Configuration."""
|
||||||
|
|
||||||
|
# latitude: Optional[float] = Field(default=None, description="Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)")
|
||||||
|
surface_tilt: Optional[float] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
||||||
|
examples=[10.0, 20.0],
|
||||||
|
)
|
||||||
|
surface_azimuth: Optional[float] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
||||||
|
examples=[10.0, 20.0],
|
||||||
|
)
|
||||||
|
userhorizon: Optional[List[float]] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
||||||
|
examples=[[10.0, 20.0, 30.0], [5.0, 15.0, 25.0]],
|
||||||
|
)
|
||||||
|
peakpower: Optional[float] = Field(
|
||||||
|
default=None, description="Nominal power of PV system in kW.", examples=[5.0, 3.5]
|
||||||
|
)
|
||||||
|
pvtechchoice: Optional[str] = Field(
|
||||||
|
default="crystSi", description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'."
|
||||||
|
)
|
||||||
|
mountingplace: Optional[str] = Field(
|
||||||
|
default="free",
|
||||||
|
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
||||||
|
)
|
||||||
|
loss: Optional[float] = Field(default=14.0, description="Sum of PV system losses in percent")
|
||||||
|
trackingtype: Optional[int] = Field(
|
||||||
|
default=None,
|
||||||
|
ge=0,
|
||||||
|
le=5,
|
||||||
|
description="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.",
|
||||||
|
examples=[0, 1, 2, 3, 4, 5],
|
||||||
|
)
|
||||||
|
optimal_surface_tilt: Optional[bool] = Field(
|
||||||
|
default=False,
|
||||||
|
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
||||||
|
examples=[False],
|
||||||
|
)
|
||||||
|
optimalangles: Optional[bool] = Field(
|
||||||
|
default=False,
|
||||||
|
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
||||||
|
examples=[False],
|
||||||
|
)
|
||||||
|
albedo: Optional[float] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Proportion of the light hitting the ground that it reflects back.",
|
||||||
|
examples=[None],
|
||||||
|
)
|
||||||
|
module_model: Optional[str] = Field(
|
||||||
|
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
||||||
|
)
|
||||||
|
inverter_model: Optional[str] = Field(
|
||||||
|
default=None, description="Model of the inverter of this plane.", examples=[None]
|
||||||
|
)
|
||||||
|
inverter_paco: Optional[int] = Field(
|
||||||
|
default=None, description="AC power rating of the inverter. [W]", examples=[6000, 4000]
|
||||||
|
)
|
||||||
|
modules_per_string: Optional[int] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Number of the PV modules of the strings of this plane.",
|
||||||
|
examples=[20],
|
||||||
|
)
|
||||||
|
strings_per_inverter: Optional[int] = Field(
|
||||||
|
default=None,
|
||||||
|
description="Number of the strings of the inverter of this plane.",
|
||||||
|
examples=[2],
|
||||||
|
)
|
||||||
|
|
||||||
|
@model_validator(mode="after")
|
||||||
|
def validate_list_length(self) -> Self:
|
||||||
|
# Check if either attribute is set and add to active planes
|
||||||
|
if self.trackingtype == 2:
|
||||||
|
# Tilt angle from horizontal plane is ignored for two-axis tracking.
|
||||||
|
if self.surface_azimuth is None:
|
||||||
|
raise ValueError("If trackingtype is set, azimuth must be set as well.")
|
||||||
|
elif self.surface_tilt is None or self.surface_azimuth is None:
|
||||||
|
raise ValueError("surface_tilt and surface_azimuth must be set.")
|
||||||
|
return self
|
||||||
|
|
||||||
|
@field_validator("mountingplace")
|
||||||
|
def validate_mountingplace(cls, mountingplace: Optional[str]) -> Optional[str]:
|
||||||
|
if mountingplace is not None and mountingplace not in ["free", "building"]:
|
||||||
|
raise ValueError(f"Invalid mountingplace: {mountingplace}")
|
||||||
|
return mountingplace
|
||||||
|
|
||||||
|
@field_validator("pvtechchoice")
|
||||||
|
def validate_pvtechchoice(cls, pvtechchoice: Optional[str]) -> Optional[str]:
|
||||||
|
if pvtechchoice is not None and pvtechchoice not in ["crystSi", "CIS", "CdTe", "Unknown"]:
|
||||||
|
raise ValueError(f"Invalid pvtechchoice: {pvtechchoice}")
|
||||||
|
return pvtechchoice
|
||||||
|
|
||||||
|
|
||||||
class PVForecastCommonSettings(SettingsBaseModel):
|
class PVForecastCommonSettings(SettingsBaseModel):
|
||||||
"""PV Forecast Configuration."""
|
"""PV Forecast Configuration."""
|
||||||
|
|
||||||
@ -24,539 +121,109 @@ class PVForecastCommonSettings(SettingsBaseModel):
|
|||||||
description="PVForecast provider id of provider to be used.",
|
description="PVForecast provider id of provider to be used.",
|
||||||
examples=["PVForecastAkkudoktor"],
|
examples=["PVForecastAkkudoktor"],
|
||||||
)
|
)
|
||||||
# pvforecast0_latitude: Optional[float] = Field(default=None, description="Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)")
|
|
||||||
# Plane 0
|
planes: Optional[list[PVForecastPlaneSetting]] = Field(
|
||||||
pvforecast0_surface_tilt: Optional[float] = Field(
|
|
||||||
default=None,
|
default=None,
|
||||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
description="Plane configuration.",
|
||||||
examples=[10.0],
|
examples=[get_model_structure_from_examples(PVForecastPlaneSetting, True)],
|
||||||
)
|
|
||||||
pvforecast0_surface_azimuth: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
|
||||||
examples=[10.0],
|
|
||||||
)
|
|
||||||
pvforecast0_userhorizon: Optional[List[float]] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
|
||||||
examples=[[10.0, 20.0, 30.0]],
|
|
||||||
)
|
|
||||||
pvforecast0_peakpower: Optional[float] = Field(
|
|
||||||
default=None, description="Nominal power of PV system in kW.", examples=[5.0]
|
|
||||||
)
|
|
||||||
pvforecast0_pvtechchoice: Optional[str] = Field(
|
|
||||||
default="crystSi", description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'."
|
|
||||||
)
|
|
||||||
pvforecast0_mountingplace: Optional[str] = Field(
|
|
||||||
default="free",
|
|
||||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
|
||||||
)
|
|
||||||
pvforecast0_loss: Optional[float] = Field(
|
|
||||||
default=14.0, description="Sum of PV system losses in percent"
|
|
||||||
)
|
|
||||||
pvforecast0_trackingtype: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="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.",
|
|
||||||
examples=[0, 1, 2, 3, 4, 5],
|
|
||||||
)
|
|
||||||
pvforecast0_optimal_surface_tilt: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
|
||||||
examples=[False],
|
|
||||||
)
|
|
||||||
pvforecast0_optimalangles: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
|
||||||
examples=[False],
|
|
||||||
)
|
|
||||||
pvforecast0_albedo: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Proportion of the light hitting the ground that it reflects back.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast0_module_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast0_inverter_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast0_inverter_paco: Optional[int] = Field(
|
|
||||||
default=None, description="AC power rating of the inverter. [W]", examples=[6000]
|
|
||||||
)
|
|
||||||
pvforecast0_modules_per_string: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the PV modules of the strings of this plane.",
|
|
||||||
examples=[20],
|
|
||||||
)
|
|
||||||
pvforecast0_strings_per_inverter: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the strings of the inverter of this plane.",
|
|
||||||
examples=[2],
|
|
||||||
)
|
|
||||||
# Plane 1
|
|
||||||
pvforecast1_surface_tilt: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
|
||||||
examples=[20.0],
|
|
||||||
)
|
|
||||||
pvforecast1_surface_azimuth: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
|
||||||
examples=[20.0],
|
|
||||||
)
|
|
||||||
pvforecast1_userhorizon: Optional[List[float]] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
|
||||||
examples=[[5.0, 15.0, 25.0]],
|
|
||||||
)
|
|
||||||
pvforecast1_peakpower: Optional[float] = Field(
|
|
||||||
default=None, description="Nominal power of PV system in kW.", examples=[3.5]
|
|
||||||
)
|
|
||||||
pvforecast1_pvtechchoice: Optional[str] = Field(
|
|
||||||
default="crystSi", description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'."
|
|
||||||
)
|
|
||||||
pvforecast1_mountingplace: Optional[str] = Field(
|
|
||||||
default="free",
|
|
||||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
|
||||||
)
|
|
||||||
pvforecast1_loss: Optional[float] = Field(
|
|
||||||
default=14.0, description="Sum of PV system losses in percent"
|
|
||||||
)
|
|
||||||
pvforecast1_trackingtype: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="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.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast1_optimal_surface_tilt: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
|
||||||
examples=[False],
|
|
||||||
)
|
|
||||||
pvforecast1_optimalangles: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
|
||||||
examples=[False],
|
|
||||||
)
|
|
||||||
pvforecast1_albedo: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Proportion of the light hitting the ground that it reflects back.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast1_module_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast1_inverter_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast1_inverter_paco: Optional[int] = Field(
|
|
||||||
default=None, description="AC power rating of the inverter. [W]", examples=[4000]
|
|
||||||
)
|
|
||||||
pvforecast1_modules_per_string: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the PV modules of the strings of this plane.",
|
|
||||||
examples=[20],
|
|
||||||
)
|
|
||||||
pvforecast1_strings_per_inverter: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the strings of the inverter of this plane.",
|
|
||||||
examples=[2],
|
|
||||||
)
|
|
||||||
# Plane 2
|
|
||||||
pvforecast2_surface_tilt: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_surface_azimuth: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_userhorizon: Optional[List[float]] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_peakpower: Optional[float] = Field(
|
|
||||||
default=None, description="Nominal power of PV system in kW.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast2_pvtechchoice: Optional[str] = Field(
|
|
||||||
default="crystSi",
|
|
||||||
description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_mountingplace: Optional[str] = Field(
|
|
||||||
default="free",
|
|
||||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_loss: Optional[float] = Field(
|
|
||||||
default=14.0, description="Sum of PV system losses in percent", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast2_trackingtype: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="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.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_optimal_surface_tilt: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_optimalangles: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_albedo: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Proportion of the light hitting the ground that it reflects back.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_module_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast2_inverter_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast2_inverter_paco: Optional[int] = Field(
|
|
||||||
default=None, description="AC power rating of the inverter. [W]", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast2_modules_per_string: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the PV modules of the strings of this plane.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast2_strings_per_inverter: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the strings of the inverter of this plane.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
# Plane 3
|
|
||||||
pvforecast3_surface_tilt: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_surface_azimuth: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_userhorizon: Optional[List[float]] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_peakpower: Optional[float] = Field(
|
|
||||||
default=None, description="Nominal power of PV system in kW.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast3_pvtechchoice: Optional[str] = Field(
|
|
||||||
default="crystSi",
|
|
||||||
description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_mountingplace: Optional[str] = Field(
|
|
||||||
default="free",
|
|
||||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_loss: Optional[float] = Field(
|
|
||||||
default=14.0, description="Sum of PV system losses in percent", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast3_trackingtype: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="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.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_optimal_surface_tilt: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_optimalangles: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_albedo: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Proportion of the light hitting the ground that it reflects back.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_module_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast3_inverter_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast3_inverter_paco: Optional[int] = Field(
|
|
||||||
default=None, description="AC power rating of the inverter. [W]", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast3_modules_per_string: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the PV modules of the strings of this plane.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast3_strings_per_inverter: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the strings of the inverter of this plane.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
# Plane 4
|
|
||||||
pvforecast4_surface_tilt: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_surface_azimuth: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_userhorizon: Optional[List[float]] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_peakpower: Optional[float] = Field(
|
|
||||||
default=None, description="Nominal power of PV system in kW.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast4_pvtechchoice: Optional[str] = Field(
|
|
||||||
default="crystSi",
|
|
||||||
description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_mountingplace: Optional[str] = Field(
|
|
||||||
default="free",
|
|
||||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_loss: Optional[float] = Field(
|
|
||||||
default=14.0, description="Sum of PV system losses in percent", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast4_trackingtype: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="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.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_optimal_surface_tilt: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_optimalangles: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_albedo: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Proportion of the light hitting the ground that it reflects back.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_module_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast4_inverter_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast4_inverter_paco: Optional[int] = Field(
|
|
||||||
default=None, description="AC power rating of the inverter. [W]", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast4_modules_per_string: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the PV modules of the strings of this plane.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast4_strings_per_inverter: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the strings of the inverter of this plane.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
# Plane 5
|
|
||||||
pvforecast5_surface_tilt: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Tilt angle from horizontal plane. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_surface_azimuth: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_userhorizon: Optional[List[float]] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_peakpower: Optional[float] = Field(
|
|
||||||
default=None, description="Nominal power of PV system in kW.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast5_pvtechchoice: Optional[str] = Field(
|
|
||||||
default="crystSi",
|
|
||||||
description="PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_mountingplace: Optional[str] = Field(
|
|
||||||
default="free",
|
|
||||||
description="Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_loss: Optional[float] = Field(
|
|
||||||
default=14.0, description="Sum of PV system losses in percent", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast5_trackingtype: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="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.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_optimal_surface_tilt: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt angle. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_optimalangles: Optional[bool] = Field(
|
|
||||||
default=False,
|
|
||||||
description="Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_albedo: Optional[float] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Proportion of the light hitting the ground that it reflects back.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_module_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the PV modules of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast5_inverter_model: Optional[str] = Field(
|
|
||||||
default=None, description="Model of the inverter of this plane.", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast5_inverter_paco: Optional[int] = Field(
|
|
||||||
default=None, description="AC power rating of the inverter. [W]", examples=[None]
|
|
||||||
)
|
|
||||||
pvforecast5_modules_per_string: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the PV modules of the strings of this plane.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
|
||||||
pvforecast5_strings_per_inverter: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
description="Number of the strings of the inverter of this plane.",
|
|
||||||
examples=[None],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
pvforecast_max_planes: ClassVar[int] = 6 # Maximum number of planes that can be set
|
max_planes: ClassVar[int] = 6 # Maximum number of planes that can be set
|
||||||
|
|
||||||
|
@field_validator("planes")
|
||||||
|
def validate_planes(
|
||||||
|
cls, planes: Optional[list[PVForecastPlaneSetting]]
|
||||||
|
) -> Optional[list[PVForecastPlaneSetting]]:
|
||||||
|
if planes is not None and len(planes) > cls.max_planes:
|
||||||
|
raise ValueError(f"Maximum number of supported planes: {cls.max_planes}.")
|
||||||
|
return planes
|
||||||
|
|
||||||
provider_settings: Optional[PVForecastImportCommonSettings] = Field(
|
provider_settings: Optional[PVForecastImportCommonSettings] = Field(
|
||||||
default=None, description="Provider settings", examples=[None]
|
default=None, description="Provider settings", examples=[None]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Computed fields
|
## Computed fields
|
||||||
@computed_field # type: ignore[prop-decorator]
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@property
|
||||||
def pvforecast_planes(self) -> List[str]:
|
def planes_peakpower(self) -> List[float]:
|
||||||
"""Compute a list of active planes."""
|
|
||||||
active_planes = []
|
|
||||||
|
|
||||||
# Loop through pvforecast0 to pvforecast4
|
|
||||||
for i in range(self.pvforecast_max_planes):
|
|
||||||
plane = f"pvforecast{i}"
|
|
||||||
tackingtype_attr = f"{plane}_trackingtype"
|
|
||||||
tilt_attr = f"{plane}_surface_tilt"
|
|
||||||
azimuth_attr = f"{plane}_surface_azimuth"
|
|
||||||
|
|
||||||
# Check if either attribute is set and add to active planes
|
|
||||||
if getattr(self, tackingtype_attr, None) == 2:
|
|
||||||
# Tilt angle from horizontal plane is gnored for two-axis tracking.
|
|
||||||
if getattr(self, azimuth_attr, None) is not None:
|
|
||||||
active_planes.append(f"pvforecast{i}")
|
|
||||||
elif getattr(self, tilt_attr, None) and getattr(self, azimuth_attr, None):
|
|
||||||
active_planes.append(f"pvforecast{i}")
|
|
||||||
|
|
||||||
return active_planes
|
|
||||||
|
|
||||||
@computed_field # type: ignore[prop-decorator]
|
|
||||||
@property
|
|
||||||
def pvforecast_planes_peakpower(self) -> List[float]:
|
|
||||||
"""Compute a list of the peak power per active planes."""
|
"""Compute a list of the peak power per active planes."""
|
||||||
planes_peakpower = []
|
planes_peakpower = []
|
||||||
|
|
||||||
for plane in self.pvforecast_planes:
|
if self.planes:
|
||||||
peakpower_attr = f"{plane}_peakpower"
|
for plane in self.planes:
|
||||||
peakpower = getattr(self, peakpower_attr, None)
|
peakpower = plane.peakpower
|
||||||
if peakpower is None:
|
if peakpower is None:
|
||||||
# TODO calculate peak power from modules/strings
|
# TODO calculate peak power from modules/strings
|
||||||
planes_peakpower.append(float(5000))
|
planes_peakpower.append(float(5000))
|
||||||
else:
|
else:
|
||||||
planes_peakpower.append(float(peakpower))
|
planes_peakpower.append(float(peakpower))
|
||||||
|
|
||||||
return planes_peakpower
|
return planes_peakpower
|
||||||
|
|
||||||
@computed_field # type: ignore[prop-decorator]
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@property
|
||||||
def pvforecast_planes_azimuth(self) -> List[float]:
|
def planes_azimuth(self) -> List[float]:
|
||||||
"""Compute a list of the azimuths per active planes."""
|
"""Compute a list of the azimuths per active planes."""
|
||||||
planes_azimuth = []
|
planes_azimuth = []
|
||||||
|
|
||||||
for plane in self.pvforecast_planes:
|
if self.planes:
|
||||||
azimuth_attr = f"{plane}_surface_azimuth"
|
for plane in self.planes:
|
||||||
azimuth = getattr(self, azimuth_attr, None)
|
azimuth = plane.surface_azimuth
|
||||||
if azimuth is None:
|
if azimuth is None:
|
||||||
# TODO Use default
|
# TODO Use default
|
||||||
planes_azimuth.append(float(180))
|
planes_azimuth.append(float(180))
|
||||||
else:
|
else:
|
||||||
planes_azimuth.append(float(azimuth))
|
planes_azimuth.append(float(azimuth))
|
||||||
|
|
||||||
return planes_azimuth
|
return planes_azimuth
|
||||||
|
|
||||||
@computed_field # type: ignore[prop-decorator]
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@property
|
||||||
def pvforecast_planes_tilt(self) -> List[float]:
|
def planes_tilt(self) -> List[float]:
|
||||||
"""Compute a list of the tilts per active planes."""
|
"""Compute a list of the tilts per active planes."""
|
||||||
planes_tilt = []
|
planes_tilt = []
|
||||||
|
|
||||||
for plane in self.pvforecast_planes:
|
if self.planes:
|
||||||
tilt_attr = f"{plane}_surface_tilt"
|
for plane in self.planes:
|
||||||
tilt = getattr(self, tilt_attr, None)
|
tilt = plane.surface_tilt
|
||||||
if tilt is None:
|
if tilt is None:
|
||||||
# TODO Use default
|
# TODO Use default
|
||||||
planes_tilt.append(float(30))
|
planes_tilt.append(float(30))
|
||||||
else:
|
else:
|
||||||
planes_tilt.append(float(tilt))
|
planes_tilt.append(float(tilt))
|
||||||
|
|
||||||
return planes_tilt
|
return planes_tilt
|
||||||
|
|
||||||
@computed_field # type: ignore[prop-decorator]
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@property
|
||||||
def pvforecast_planes_userhorizon(self) -> Any:
|
def planes_userhorizon(self) -> Any:
|
||||||
"""Compute a list of the user horizon per active planes."""
|
"""Compute a list of the user horizon per active planes."""
|
||||||
planes_userhorizon = []
|
planes_userhorizon = []
|
||||||
|
|
||||||
for plane in self.pvforecast_planes:
|
if self.planes:
|
||||||
userhorizon_attr = f"{plane}_userhorizon"
|
for plane in self.planes:
|
||||||
userhorizon = getattr(self, userhorizon_attr, None)
|
userhorizon = plane.userhorizon
|
||||||
if userhorizon is None:
|
if userhorizon is None:
|
||||||
# TODO Use default
|
# TODO Use default
|
||||||
planes_userhorizon.append([float(0), float(0)])
|
planes_userhorizon.append([float(0), float(0)])
|
||||||
else:
|
else:
|
||||||
planes_userhorizon.append(userhorizon)
|
planes_userhorizon.append(userhorizon)
|
||||||
|
|
||||||
return planes_userhorizon
|
return planes_userhorizon
|
||||||
|
|
||||||
@computed_field # type: ignore[prop-decorator]
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@property
|
||||||
def pvforecast_planes_inverter_paco(self) -> Any:
|
def planes_inverter_paco(self) -> Any:
|
||||||
"""Compute a list of the maximum power rating of the inverter per active planes."""
|
"""Compute a list of the maximum power rating of the inverter per active planes."""
|
||||||
planes_inverter_paco = []
|
planes_inverter_paco = []
|
||||||
|
|
||||||
for plane in self.pvforecast_planes:
|
if self.planes:
|
||||||
inverter_paco_attr = f"{plane}_inverter_paco"
|
for plane in self.planes:
|
||||||
inverter_paco = getattr(self, inverter_paco_attr, None)
|
inverter_paco = plane.inverter_paco
|
||||||
if inverter_paco is None:
|
if inverter_paco is None:
|
||||||
# TODO Use default - no clipping
|
# TODO Use default - no clipping
|
||||||
planes_inverter_paco.append(25000.0)
|
planes_inverter_paco.append(25000.0)
|
||||||
else:
|
else:
|
||||||
planes_inverter_paco.append(float(inverter_paco))
|
planes_inverter_paco.append(float(inverter_paco))
|
||||||
|
|
||||||
return planes_inverter_paco
|
return planes_inverter_paco
|
||||||
|
@ -22,16 +22,22 @@ Example:
|
|||||||
},
|
},
|
||||||
"pvforecast": {
|
"pvforecast": {
|
||||||
"provider": "PVForecastAkkudoktor",
|
"provider": "PVForecastAkkudoktor",
|
||||||
"pvforecast0_peakpower": 5.0,
|
"planes": [
|
||||||
"pvforecast0_surface_azimuth": -10,
|
{
|
||||||
"pvforecast0_surface_tilt": 7,
|
"peakpower": 5.0,
|
||||||
"pvforecast0_userhorizon": [20, 27, 22, 20],
|
"surface_azimuth": -10,
|
||||||
"pvforecast0_inverter_paco": 10000,
|
"surface_tilt": 7,
|
||||||
"pvforecast1_peakpower": 4.8,
|
"userhorizon": [20, 27, 22, 20],
|
||||||
"pvforecast1_surface_azimuth": -90,
|
"inverter_paco": 10000,
|
||||||
"pvforecast1_surface_tilt": 7,
|
},
|
||||||
"pvforecast1_userhorizon": [30, 30, 30, 50],
|
{
|
||||||
"pvforecast1_inverter_paco": 10000,
|
"peakpower": 4.8,
|
||||||
|
"surface_azimuth": -90,
|
||||||
|
"surface_tilt": 7,
|
||||||
|
"userhorizon": [30, 30, 30, 50],
|
||||||
|
"inverter_paco": 10000,
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,19 +217,15 @@ class PVForecastAkkudoktor(PVForecastProvider):
|
|||||||
f"lon={self.config.prediction.longitude}",
|
f"lon={self.config.prediction.longitude}",
|
||||||
]
|
]
|
||||||
|
|
||||||
for i in range(len(self.config.pvforecast.pvforecast_planes)):
|
for i in range(len(self.config.pvforecast.planes)):
|
||||||
|
query_params.append(f"power={int(self.config.pvforecast.planes_peakpower[i] * 1000)}")
|
||||||
|
query_params.append(f"azimuth={int(self.config.pvforecast.planes_azimuth[i])}")
|
||||||
|
query_params.append(f"tilt={int(self.config.pvforecast.planes_tilt[i])}")
|
||||||
query_params.append(
|
query_params.append(
|
||||||
f"power={int(self.config.pvforecast.pvforecast_planes_peakpower[i] * 1000)}"
|
f"powerInverter={int(self.config.pvforecast.planes_inverter_paco[i])}"
|
||||||
)
|
|
||||||
query_params.append(
|
|
||||||
f"azimuth={int(self.config.pvforecast.pvforecast_planes_azimuth[i])}"
|
|
||||||
)
|
|
||||||
query_params.append(f"tilt={int(self.config.pvforecast.pvforecast_planes_tilt[i])}")
|
|
||||||
query_params.append(
|
|
||||||
f"powerInverter={int(self.config.pvforecast.pvforecast_planes_inverter_paco[i])}"
|
|
||||||
)
|
)
|
||||||
horizon_values = ",".join(
|
horizon_values = ",".join(
|
||||||
str(int(h)) for h in self.config.pvforecast.pvforecast_planes_userhorizon[i]
|
str(int(h)) for h in self.config.pvforecast.planes_userhorizon[i]
|
||||||
)
|
)
|
||||||
query_params.append(f"horizont={horizon_values}")
|
query_params.append(f"horizont={horizon_values}")
|
||||||
|
|
||||||
@ -273,7 +275,7 @@ class PVForecastAkkudoktor(PVForecastProvider):
|
|||||||
`PVForecastAkkudoktorDataRecord`.
|
`PVForecastAkkudoktorDataRecord`.
|
||||||
"""
|
"""
|
||||||
# Assure we have something to request PV power for.
|
# Assure we have something to request PV power for.
|
||||||
if not self.config.pvforecast.pvforecast_planes:
|
if not self.config.pvforecast.planes:
|
||||||
# No planes for PV
|
# No planes for PV
|
||||||
error_msg = "Requested PV forecast, but no planes configured."
|
error_msg = "Requested PV forecast, but no planes configured."
|
||||||
logger.error(f"Configuration error: {error_msg}")
|
logger.error(f"Configuration error: {error_msg}")
|
||||||
@ -381,26 +383,36 @@ if __name__ == "__main__":
|
|||||||
},
|
},
|
||||||
"pvforecast": {
|
"pvforecast": {
|
||||||
"provider": "PVForecastAkkudoktor",
|
"provider": "PVForecastAkkudoktor",
|
||||||
"pvforecast0_peakpower": 5.0,
|
"planes": [
|
||||||
"pvforecast0_surface_azimuth": -10,
|
{
|
||||||
"pvforecast0_surface_tilt": 7,
|
"peakpower": 5.0,
|
||||||
"pvforecast0_userhorizon": [20, 27, 22, 20],
|
"surface_azimuth": -10,
|
||||||
"pvforecast0_inverter_paco": 10000,
|
"surface_tilt": 7,
|
||||||
"pvforecast1_peakpower": 4.8,
|
"userhorizon": [20, 27, 22, 20],
|
||||||
"pvforecast1_surface_azimuth": -90,
|
"inverter_paco": 10000,
|
||||||
"pvforecast1_surface_tilt": 7,
|
},
|
||||||
"pvforecast1_userhorizon": [30, 30, 30, 50],
|
{
|
||||||
"pvforecast1_inverter_paco": 10000,
|
"peakpower": 4.8,
|
||||||
"pvforecast2_peakpower": 1.4,
|
"surface_azimuth": -90,
|
||||||
"pvforecast2_surface_azimuth": -40,
|
"surface_tilt": 7,
|
||||||
"pvforecast2_surface_tilt": 60,
|
"userhorizon": [30, 30, 30, 50],
|
||||||
"pvforecast2_userhorizon": [60, 30, 0, 30],
|
"inverter_paco": 10000,
|
||||||
"pvforecast2_inverter_paco": 2000,
|
},
|
||||||
"pvforecast3_peakpower": 1.6,
|
{
|
||||||
"pvforecast3_surface_azimuth": 5,
|
"peakpower": 1.4,
|
||||||
"pvforecast3_surface_tilt": 45,
|
"surface_azimuth": -40,
|
||||||
"pvforecast3_userhorizon": [45, 25, 30, 60],
|
"surface_tilt": 60,
|
||||||
"pvforecast3_inverter_paco": 1400,
|
"userhorizon": [60, 30, 0, 30],
|
||||||
|
"inverter_paco": 2000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"peakpower": 1.6,
|
||||||
|
"surface_azimuth": 5,
|
||||||
|
"surface_tilt": 45,
|
||||||
|
"userhorizon": [45, 25, 30, 60],
|
||||||
|
"inverter_paco": 1400,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,24 +22,22 @@ logger = get_logger(__name__)
|
|||||||
class PVForecastImportCommonSettings(SettingsBaseModel):
|
class PVForecastImportCommonSettings(SettingsBaseModel):
|
||||||
"""Common settings for pvforecast data import from file or JSON string."""
|
"""Common settings for pvforecast data import from file or JSON string."""
|
||||||
|
|
||||||
pvforecastimport_file_path: Optional[Union[str, Path]] = Field(
|
import_file_path: Optional[Union[str, Path]] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Path to the file to import PV forecast data from.",
|
description="Path to the file to import PV forecast data from.",
|
||||||
examples=[None, "/path/to/pvforecast.json"],
|
examples=[None, "/path/to/pvforecast.json"],
|
||||||
)
|
)
|
||||||
|
|
||||||
pvforecastimport_json: Optional[str] = Field(
|
import_json: Optional[str] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="JSON string, dictionary of PV forecast value lists.",
|
description="JSON string, dictionary of PV forecast value lists.",
|
||||||
examples=['{"pvforecast_ac_power": [0, 8.05, 352.91]}'],
|
examples=['{"pvforecast_ac_power": [0, 8.05, 352.91]}'],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Validators
|
# Validators
|
||||||
@field_validator("pvforecastimport_file_path", mode="after")
|
@field_validator("import_file_path", mode="after")
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_pvforecastimport_file_path(
|
def validate_import_file_path(cls, value: Optional[Union[str, Path]]) -> Optional[Path]:
|
||||||
cls, value: Optional[Union[str, Path]]
|
|
||||||
) -> Optional[Path]:
|
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
@ -65,13 +63,13 @@ class PVForecastImport(PVForecastProvider, PredictionImportProvider):
|
|||||||
return "PVForecastImport"
|
return "PVForecastImport"
|
||||||
|
|
||||||
def _update_data(self, force_update: Optional[bool] = False) -> None:
|
def _update_data(self, force_update: Optional[bool] = False) -> None:
|
||||||
if self.config.pvforecast.provider_settings.pvforecastimport_file_path is not None:
|
if self.config.pvforecast.provider_settings.import_file_path is not None:
|
||||||
self.import_from_file(
|
self.import_from_file(
|
||||||
self.config.pvforecast.provider_settings.pvforecastimport_file_path,
|
self.config.pvforecast.provider_settings.import_file_path,
|
||||||
key_prefix="pvforecast",
|
key_prefix="pvforecast",
|
||||||
)
|
)
|
||||||
if self.config.pvforecast.provider_settings.pvforecastimport_json is not None:
|
if self.config.pvforecast.provider_settings.import_json is not None:
|
||||||
self.import_from_json(
|
self.import_from_json(
|
||||||
self.config.pvforecast.provider_settings.pvforecastimport_json,
|
self.config.pvforecast.provider_settings.import_json,
|
||||||
key_prefix="pvforecast",
|
key_prefix="pvforecast",
|
||||||
)
|
)
|
||||||
|
42
src/akkudoktoreos/utils/docs.py
Normal file
42
src/akkudoktoreos/utils/docs.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pydantic.fields import FieldInfo
|
||||||
|
|
||||||
|
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
||||||
|
|
||||||
|
|
||||||
|
def get_example_or_default(field_name: str, field_info: FieldInfo, example_ix: int) -> Any:
|
||||||
|
"""Generate a default value for a field, considering constraints."""
|
||||||
|
if field_info.examples is not None:
|
||||||
|
try:
|
||||||
|
return field_info.examples[example_ix]
|
||||||
|
except IndexError:
|
||||||
|
return field_info.examples[-1]
|
||||||
|
|
||||||
|
if field_info.default is not None:
|
||||||
|
return field_info.default
|
||||||
|
|
||||||
|
raise NotImplementedError(f"No default or example provided '{field_name}': {field_info}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_model_structure_from_examples(
|
||||||
|
model_class: type[PydanticBaseModel], multiple: bool
|
||||||
|
) -> list[dict[str, Any]]:
|
||||||
|
"""Create a model instance with default or example values, respecting constraints."""
|
||||||
|
example_max_length = 1
|
||||||
|
|
||||||
|
# Get first field with examples (non-default) to get example_max_length
|
||||||
|
if multiple:
|
||||||
|
for _, field_info in model_class.model_fields.items():
|
||||||
|
if field_info.examples is not None:
|
||||||
|
example_max_length = len(field_info.examples)
|
||||||
|
break
|
||||||
|
|
||||||
|
example_data: list[dict[str, Any]] = [{} for _ in range(example_max_length)]
|
||||||
|
|
||||||
|
for field_name, field_info in model_class.model_fields.items():
|
||||||
|
for example_ix in range(example_max_length):
|
||||||
|
example_data[example_ix][field_name] = get_example_or_default(
|
||||||
|
field_name, field_info, example_ix
|
||||||
|
)
|
||||||
|
return example_data
|
@ -1,82 +1,75 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from akkudoktoreos.prediction.pvforecast import PVForecastCommonSettings
|
from akkudoktoreos.prediction.pvforecast import (
|
||||||
|
PVForecastCommonSettings,
|
||||||
|
PVForecastPlaneSetting,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def settings():
|
def settings():
|
||||||
"""Fixture that creates an empty PVForecastSettings."""
|
"""Fixture that creates an empty PVForecastSettings."""
|
||||||
settings = PVForecastCommonSettings()
|
settings = PVForecastCommonSettings()
|
||||||
|
assert settings.planes is None
|
||||||
# Check default values for plane 0
|
|
||||||
assert settings.pvforecast0_surface_tilt is None
|
|
||||||
assert settings.pvforecast0_surface_azimuth is None
|
|
||||||
assert settings.pvforecast0_pvtechchoice == "crystSi"
|
|
||||||
assert settings.pvforecast0_mountingplace == "free"
|
|
||||||
assert settings.pvforecast0_trackingtype is None
|
|
||||||
assert settings.pvforecast0_optimal_surface_tilt is False
|
|
||||||
assert settings.pvforecast0_optimalangles is False
|
|
||||||
# Check default values for plane 1
|
|
||||||
assert settings.pvforecast1_surface_azimuth is None
|
|
||||||
assert settings.pvforecast1_pvtechchoice == "crystSi"
|
|
||||||
assert settings.pvforecast1_mountingplace == "free"
|
|
||||||
assert settings.pvforecast1_trackingtype is None
|
|
||||||
assert settings.pvforecast1_optimal_surface_tilt is False
|
|
||||||
assert settings.pvforecast1_optimalangles is False
|
|
||||||
|
|
||||||
expected_planes: list[str] = []
|
|
||||||
assert settings.pvforecast_planes == expected_planes
|
|
||||||
|
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
|
|
||||||
def test_active_planes_detection(settings):
|
|
||||||
"""Test that active planes are correctly detected based on tilt and azimuth."""
|
|
||||||
settings.pvforecast1_surface_tilt = 10.0
|
|
||||||
settings.pvforecast1_surface_azimuth = 10.0
|
|
||||||
settings.pvforecast2_surface_tilt = 20.0
|
|
||||||
settings.pvforecast2_surface_azimuth = 20.0
|
|
||||||
|
|
||||||
expected_planes = ["pvforecast1", "pvforecast2"]
|
|
||||||
assert settings.pvforecast_planes == expected_planes
|
|
||||||
|
|
||||||
|
|
||||||
def test_planes_peakpower_computation(settings):
|
def test_planes_peakpower_computation(settings):
|
||||||
"""Test computation of peak power for active planes."""
|
"""Test computation of peak power for active planes."""
|
||||||
settings.pvforecast1_surface_tilt = 10.0
|
settings.planes = [
|
||||||
settings.pvforecast1_surface_azimuth = 10.0
|
PVForecastPlaneSetting(
|
||||||
settings.pvforecast1_peakpower = 5.0
|
surface_tilt=10.0,
|
||||||
settings.pvforecast2_surface_tilt = 20.0
|
surface_azimuth=10.0,
|
||||||
settings.pvforecast2_surface_azimuth = 20.0
|
peakpower=5.0,
|
||||||
settings.pvforecast2_peakpower = 3.5
|
),
|
||||||
settings.pvforecast3_surface_tilt = 30.0
|
PVForecastPlaneSetting(
|
||||||
settings.pvforecast3_surface_azimuth = 30.0
|
surface_tilt=20.0,
|
||||||
settings.pvforecast3_modules_per_string = 20 # Should use default 5000W
|
surface_azimuth=20.0,
|
||||||
|
peakpower=3.5,
|
||||||
|
),
|
||||||
|
PVForecastPlaneSetting(
|
||||||
|
surface_tilt=30.0,
|
||||||
|
surface_azimuth=30.0,
|
||||||
|
modules_per_string=20, # Should use default 5000W
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
expected_peakpower = [5.0, 3.5, 5000.0]
|
expected_peakpower = [5.0, 3.5, 5000.0]
|
||||||
assert settings.pvforecast_planes_peakpower == expected_peakpower
|
assert settings.planes_peakpower == expected_peakpower
|
||||||
|
|
||||||
|
|
||||||
def test_planes_azimuth_computation(settings):
|
def test_planes_azimuth_computation(settings):
|
||||||
"""Test computation of azimuth values for active planes."""
|
"""Test computation of azimuth values for active planes."""
|
||||||
settings.pvforecast1_surface_tilt = 10.0
|
settings.planes = [
|
||||||
settings.pvforecast1_surface_azimuth = 10.0
|
PVForecastPlaneSetting(
|
||||||
settings.pvforecast2_surface_tilt = 20.0
|
surface_tilt=10.0,
|
||||||
settings.pvforecast2_surface_azimuth = 20.0
|
surface_azimuth=10.0,
|
||||||
|
),
|
||||||
|
PVForecastPlaneSetting(
|
||||||
|
surface_tilt=20.0,
|
||||||
|
surface_azimuth=20.0,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
expected_azimuths = [10.0, 20.0]
|
expected_azimuths = [10.0, 20.0]
|
||||||
assert settings.pvforecast_planes_azimuth == expected_azimuths
|
assert settings.planes_azimuth == expected_azimuths
|
||||||
|
|
||||||
|
|
||||||
def test_planes_tilt_computation(settings):
|
def test_planes_tilt_computation(settings):
|
||||||
"""Test computation of tilt values for active planes."""
|
"""Test computation of tilt values for active planes."""
|
||||||
settings.pvforecast1_surface_tilt = 10.0
|
settings.planes = [
|
||||||
settings.pvforecast1_surface_azimuth = 10.0
|
PVForecastPlaneSetting(
|
||||||
settings.pvforecast2_surface_tilt = 20.0
|
surface_tilt=10.0,
|
||||||
settings.pvforecast2_surface_azimuth = 20.0
|
surface_azimuth=10.0,
|
||||||
|
),
|
||||||
|
PVForecastPlaneSetting(
|
||||||
|
surface_tilt=20.0,
|
||||||
|
surface_azimuth=20.0,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
expected_tilts = [10.0, 20.0]
|
expected_tilts = [10.0, 20.0]
|
||||||
assert settings.pvforecast_planes_tilt == expected_tilts
|
assert settings.planes_tilt == expected_tilts
|
||||||
|
|
||||||
|
|
||||||
def test_planes_userhorizon_computation(settings):
|
def test_planes_userhorizon_computation(settings):
|
||||||
@ -84,116 +77,84 @@ def test_planes_userhorizon_computation(settings):
|
|||||||
horizon1 = [10.0, 20.0, 30.0]
|
horizon1 = [10.0, 20.0, 30.0]
|
||||||
horizon2 = [5.0, 15.0, 25.0]
|
horizon2 = [5.0, 15.0, 25.0]
|
||||||
|
|
||||||
settings.pvforecast1_surface_tilt = 10.0
|
settings.planes = [
|
||||||
settings.pvforecast1_surface_azimuth = 10.0
|
PVForecastPlaneSetting(
|
||||||
settings.pvforecast1_userhorizon = horizon1
|
surface_tilt=10.0,
|
||||||
settings.pvforecast2_surface_tilt = 20.0
|
surface_azimuth=10.0,
|
||||||
settings.pvforecast2_surface_azimuth = 20.0
|
userhorizon=horizon1,
|
||||||
settings.pvforecast2_userhorizon = horizon2
|
),
|
||||||
|
PVForecastPlaneSetting(
|
||||||
|
surface_tilt=20.0,
|
||||||
|
surface_azimuth=20.0,
|
||||||
|
userhorizon=horizon2,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
expected_horizons = [horizon1, horizon2]
|
expected_horizons = [horizon1, horizon2]
|
||||||
assert settings.pvforecast_planes_userhorizon == expected_horizons
|
assert settings.planes_userhorizon == expected_horizons
|
||||||
|
|
||||||
|
|
||||||
def test_planes_inverter_paco_computation(settings):
|
def test_planes_inverter_paco_computation(settings):
|
||||||
"""Test computation of inverter power rating for active planes."""
|
"""Test computation of inverter power rating for active planes."""
|
||||||
settings.pvforecast1_surface_tilt = 10.0
|
settings.planes = [
|
||||||
settings.pvforecast1_surface_azimuth = 10.0
|
PVForecastPlaneSetting(
|
||||||
settings.pvforecast1_inverter_paco = 6000
|
surface_tilt=10.0,
|
||||||
settings.pvforecast2_surface_tilt = 20.0
|
surface_azimuth=10.0,
|
||||||
settings.pvforecast2_surface_azimuth = 20.0
|
inverter_paco=6000,
|
||||||
settings.pvforecast2_inverter_paco = 4000
|
),
|
||||||
|
PVForecastPlaneSetting(
|
||||||
|
surface_tilt=20.0,
|
||||||
|
surface_azimuth=20.0,
|
||||||
|
inverter_paco=4000,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
expected_paco = [6000, 4000]
|
expected_paco = [6000, 4000]
|
||||||
assert settings.pvforecast_planes_inverter_paco == expected_paco
|
assert settings.planes_inverter_paco == expected_paco
|
||||||
|
|
||||||
|
|
||||||
def test_non_sequential_plane_numbers(settings):
|
|
||||||
"""Test that non-sequential plane numbers are handled correctly."""
|
|
||||||
settings.pvforecast1_surface_tilt = 10.0
|
|
||||||
settings.pvforecast1_surface_azimuth = 10.0
|
|
||||||
settings.pvforecast1_peakpower = 5.0
|
|
||||||
settings.pvforecast3_surface_tilt = 30.0
|
|
||||||
settings.pvforecast3_surface_azimuth = 30.0
|
|
||||||
settings.pvforecast3_peakpower = 3.5
|
|
||||||
settings.pvforecast5_surface_tilt = 50.0
|
|
||||||
settings.pvforecast5_surface_azimuth = 50.0
|
|
||||||
settings.pvforecast5_peakpower = 2.0
|
|
||||||
|
|
||||||
expected_planes = ["pvforecast1", "pvforecast3", "pvforecast5"]
|
|
||||||
assert settings.pvforecast_planes == expected_planes
|
|
||||||
assert settings.pvforecast_planes_peakpower == [5.0, 3.5, 2.0]
|
|
||||||
|
|
||||||
|
|
||||||
def test_mixed_plane_configuration(settings):
|
def test_mixed_plane_configuration(settings):
|
||||||
"""Test mixed configuration with some planes having peak power and others having modules."""
|
"""Test mixed configuration with some planes having peak power and others having modules."""
|
||||||
settings.pvforecast1_surface_tilt = 10.0
|
settings.planes = [
|
||||||
settings.pvforecast1_surface_azimuth = 10.0
|
PVForecastPlaneSetting(
|
||||||
settings.pvforecast1_peakpower = 5.0
|
surface_tilt=10.0,
|
||||||
settings.pvforecast2_surface_tilt = 20.0
|
surface_azimuth=10.0,
|
||||||
settings.pvforecast2_surface_azimuth = 20.0
|
peakpower=5.0,
|
||||||
settings.pvforecast2_modules_per_string = 20
|
),
|
||||||
settings.pvforecast2_strings_per_inverter = 2
|
PVForecastPlaneSetting(
|
||||||
settings.pvforecast4_surface_tilt = 40.0
|
surface_tilt=20.0,
|
||||||
settings.pvforecast4_surface_azimuth = 40.0
|
surface_azimuth=20.0,
|
||||||
settings.pvforecast4_peakpower = 3.0
|
modules_per_string=20,
|
||||||
|
strings_per_inverter=2,
|
||||||
|
),
|
||||||
|
PVForecastPlaneSetting(
|
||||||
|
surface_tilt=40.0,
|
||||||
|
surface_azimuth=40.0,
|
||||||
|
peakpower=3.0,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
expected_planes = ["pvforecast1", "pvforecast2", "pvforecast4"]
|
|
||||||
assert settings.pvforecast_planes == expected_planes
|
|
||||||
# First plane uses specified peak power, second uses default, third uses specified
|
# First plane uses specified peak power, second uses default, third uses specified
|
||||||
assert settings.pvforecast_planes_peakpower == [5.0, 5000.0, 3.0]
|
assert settings.planes_peakpower == [5.0, 5000.0, 3.0]
|
||||||
|
|
||||||
|
|
||||||
def test_max_planes_limit(settings):
|
def test_max_planes_limit(settings):
|
||||||
"""Test that the maximum number of planes is enforced."""
|
"""Test that the maximum number of planes is enforced."""
|
||||||
assert settings.pvforecast_max_planes == 6
|
assert settings.max_planes == 6
|
||||||
|
|
||||||
# Create settings with more planes than allowed (should only recognize up to max)
|
# Create settings with more planes than allowed (should only recognize up to max)
|
||||||
plane_settings = {}
|
plane_settings = [{"peakpower": 5.0} for _ in range(8)]
|
||||||
for i in range(1, 8): # Try to set up 7 planes, skipping plane 0
|
|
||||||
plane_settings[f"pvforecast{i}_peakpower"] = 5.0
|
|
||||||
|
|
||||||
settings = PVForecastCommonSettings(**plane_settings)
|
with pytest.raises(ValueError):
|
||||||
assert len(settings.pvforecast_planes) <= settings.pvforecast_max_planes
|
PVForecastCommonSettings(planes=plane_settings)
|
||||||
|
|
||||||
|
|
||||||
def test_optional_parameters_non_zero_plane(settings):
|
def test_invalid_plane_settings():
|
||||||
"""Test that optional parameters can be None for non-zero planes."""
|
"""Test that optional parameters can be None for non-zero planes."""
|
||||||
settings.pvforecast1_peakpower = 5.0
|
with pytest.raises(ValueError):
|
||||||
settings.pvforecast1_albedo = None
|
PVForecastPlaneSetting(
|
||||||
settings.pvforecast1_module_model = None
|
peakpower=5.0,
|
||||||
settings.pvforecast1_userhorizon = None
|
albedo=None,
|
||||||
|
module_model=None,
|
||||||
assert settings.pvforecast1_albedo is None
|
userhorizon=None,
|
||||||
assert settings.pvforecast1_module_model is None
|
)
|
||||||
assert settings.pvforecast1_userhorizon is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_tracking_type_values_non_zero_plane(settings):
|
|
||||||
"""Test valid tracking type values for non-zero planes."""
|
|
||||||
valid_types = [0, 1, 2, 3, 4, 5]
|
|
||||||
|
|
||||||
for tracking_type in valid_types:
|
|
||||||
settings.pvforecast1_peakpower = 5.0
|
|
||||||
settings.pvforecast1_trackingtype = tracking_type
|
|
||||||
assert settings.pvforecast1_trackingtype == tracking_type
|
|
||||||
|
|
||||||
|
|
||||||
def test_pv_technology_values_non_zero_plane(settings):
|
|
||||||
"""Test valid PV technology values for non-zero planes."""
|
|
||||||
valid_technologies = ["crystSi", "CIS", "CdTe", "Unknown"]
|
|
||||||
|
|
||||||
for tech in valid_technologies:
|
|
||||||
settings.pvforecast2_peakpower = 5.0
|
|
||||||
settings.pvforecast2_pvtechchoice = tech
|
|
||||||
assert settings.pvforecast2_pvtechchoice == tech
|
|
||||||
|
|
||||||
|
|
||||||
def test_mounting_place_values_non_zero_plane(settings):
|
|
||||||
"""Test valid mounting place values for non-zero planes."""
|
|
||||||
valid_mounting = ["free", "building"]
|
|
||||||
|
|
||||||
for mounting in valid_mounting:
|
|
||||||
settings.pvforecast3_peakpower = 5.0
|
|
||||||
settings.pvforecast3_mountingplace = mounting
|
|
||||||
assert settings.pvforecast3_mountingplace == mounting
|
|
||||||
|
@ -33,27 +33,36 @@ def sample_settings(config_eos):
|
|||||||
},
|
},
|
||||||
"pvforecast": {
|
"pvforecast": {
|
||||||
"provider": "PVForecastAkkudoktor",
|
"provider": "PVForecastAkkudoktor",
|
||||||
"pvforecast0_peakpower": 5.0,
|
"planes": [
|
||||||
"pvforecast0_surface_azimuth": -10,
|
{
|
||||||
"pvforecast0_surface_tilt": 7,
|
"peakpower": 5.0,
|
||||||
"pvforecast0_userhorizon": [20, 27, 22, 20],
|
"surface_azimuth": -10,
|
||||||
"pvforecast0_inverter_paco": 10000,
|
"surface_tilt": 7,
|
||||||
"pvforecast1_peakpower": 4.8,
|
"userhorizon": [20, 27, 22, 20],
|
||||||
"pvforecast1_surface_azimuth": -90,
|
"inverter_paco": 10000,
|
||||||
"pvforecast1_surface_tilt": 7,
|
},
|
||||||
"pvforecast1_userhorizon": [30, 30, 30, 50],
|
{
|
||||||
"pvforecast1_inverter_paco": 10000,
|
"peakpower": 4.8,
|
||||||
"pvforecast2_peakpower": 1.4,
|
"surface_azimuth": -90,
|
||||||
"pvforecast2_surface_azimuth": -40,
|
"surface_tilt": 7,
|
||||||
"pvforecast2_surface_tilt": 60,
|
"userhorizon": [30, 30, 30, 50],
|
||||||
"pvforecast2_userhorizon": [60, 30, 0, 30],
|
"inverter_paco": 10000,
|
||||||
"pvforecast2_inverter_paco": 2000,
|
},
|
||||||
"pvforecast3_peakpower": 1.6,
|
{
|
||||||
"pvforecast3_surface_azimuth": 5,
|
"peakpower": 1.4,
|
||||||
"pvforecast3_surface_tilt": 45,
|
"surface_azimuth": -40,
|
||||||
"pvforecast3_userhorizon": [45, 25, 30, 60],
|
"surface_tilt": 60,
|
||||||
"pvforecast3_inverter_paco": 1400,
|
"userhorizon": [60, 30, 0, 30],
|
||||||
"pvforecast4_peakpower": None,
|
"inverter_paco": 2000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"peakpower": 1.6,
|
||||||
|
"surface_azimuth": 5,
|
||||||
|
"surface_tilt": 45,
|
||||||
|
"userhorizon": [45, 25, 30, 60],
|
||||||
|
"inverter_paco": 1400,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ def provider(sample_import_1_json, config_eos):
|
|||||||
"pvforecast": {
|
"pvforecast": {
|
||||||
"provider": "PVForecastImport",
|
"provider": "PVForecastImport",
|
||||||
"provider_settings": {
|
"provider_settings": {
|
||||||
"pvforecastimport_file_path": str(FILE_TESTDATA_PVFORECASTIMPORT_1_JSON),
|
"import_file_path": str(FILE_TESTDATA_PVFORECASTIMPORT_1_JSON),
|
||||||
"pvforecastimport_json": json.dumps(sample_import_1_json),
|
"import_json": json.dumps(sample_import_1_json),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ def test_invalid_provider(provider, config_eos):
|
|||||||
"pvforecast": {
|
"pvforecast": {
|
||||||
"provider": "<invalid>",
|
"provider": "<invalid>",
|
||||||
"provider_settings": {
|
"provider_settings": {
|
||||||
"pvforecastimport_file_path": str(FILE_TESTDATA_PVFORECASTIMPORT_1_JSON),
|
"import_file_path": str(FILE_TESTDATA_PVFORECASTIMPORT_1_JSON),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,11 +86,11 @@ def test_import(provider, sample_import_1_json, start_datetime, from_file, confi
|
|||||||
ems_eos = get_ems()
|
ems_eos = get_ems()
|
||||||
ems_eos.set_start_datetime(to_datetime(start_datetime, in_timezone="Europe/Berlin"))
|
ems_eos.set_start_datetime(to_datetime(start_datetime, in_timezone="Europe/Berlin"))
|
||||||
if from_file:
|
if from_file:
|
||||||
config_eos.pvforecast.provider_settings.pvforecastimport_json = None
|
config_eos.pvforecast.provider_settings.import_json = None
|
||||||
assert config_eos.pvforecast.provider_settings.pvforecastimport_json is None
|
assert config_eos.pvforecast.provider_settings.import_json is None
|
||||||
else:
|
else:
|
||||||
config_eos.pvforecast.provider_settings.pvforecastimport_file_path = None
|
config_eos.pvforecast.provider_settings.import_file_path = None
|
||||||
assert config_eos.pvforecast.provider_settings.pvforecastimport_file_path is None
|
assert config_eos.pvforecast.provider_settings.import_file_path is None
|
||||||
provider.clear()
|
provider.clear()
|
||||||
|
|
||||||
# Call the method
|
# Call the method
|
||||||
|
Loading…
x
Reference in New Issue
Block a user