from dataclasses import dataclass, field
from typing import List, Optional, Union
import pandas as pd
from shapely.geometry import Point
import pytz
from uuid import uuid4
import json
import pvlib
from energydatamodel import EnergyAsset, GeoPolygon, GeoMultiPolygon
[docs]
@dataclass
class FixedMount:
surface_tilt: float = 0.0
surface_azimuth: float = 0.0
[docs]
@dataclass
class SingleAxisTrackerMount:
axis_tilt: float = 0.0
axis_azimuth: float = 0.0
max_angle: Union[float, tuple] = 90.0
backtrack: bool = True
gcr: float = 0.2857142857142857
cross_axis_tilt: float = 0.0
racking_model: Optional[str] = None
module_height: Optional[float] = None
[docs]
@dataclass(repr=False)
class PVArray(EnergyAsset):
capacity: Optional[float] = None
surface_azimuth: Optional[float] = None
surface_tilt: Optional[float] = None
surface_area: Optional[float] = None # Area in square meters
efficiency: Optional[float] = None # Efficiency in percentage
module: Optional[str] = None
module_type: str = "glass_polymer"
module_parameters: Union[dict, pd.Series] = None
temperature_model_parameters: Union[dict, pd.Series] = None
[docs]
def get_timeseries(self):
return self.timeseries_df[self.column_df]
[docs]
@dataclass(repr=False)
class PVSystem(EnergyAsset):
"""
The PVSystem class defines a standard set of PV system attributes
and modeling functions. This class describes the collection and
interactions of PV system components rather than an installed system
on the ground. It is typically used in combination with
:py:class:`~pvlib.location.Location` and
:py:class:`~pvlib.modelchain.ModelChain`
objects.
"""
pv_arrays: List[PVArray] = field(default_factory=list)
capacity: float = None
surface_azimuth: float = None
surface_tilt: float = None
albedo: Optional[float] = None
surface_type: Optional[str] = None
module_parameters: Optional[dict] = None
inverter_parameters: Optional[dict] = None
module_type: str = "glass_polymer"
racking_model: str = "open_rack"
def __post_init__(self):
super().__post_init__()
# If no PVArray list is provided, but capacity, azimuth, and tilt are,
# create a PVArray and add it to the list.
if not self.pv_arrays and all([self.capacity, self.surface_azimuth, self.surface_tilt]):
self.pv_arrays.append(PVArray(capacity=self.capacity, surface_azimuth=self.surface_azimuth, surface_tilt=self.surface_tilt))
"""
The class supports basic system topologies consisting of:
* `N` total modules arranged in series
(`modules_per_string=N`, `strings_per_inverter=1`).
* `M` total modules arranged in parallel
(`modules_per_string=1`, `strings_per_inverter=M`).
* `NxM` total modules arranged in `M` strings of `N` modules each
(`modules_per_string=N`, `strings_per_inverter=M`).
The class is complementary to the module-level functions.
The attributes should generally be things that don't change about
the system, such the type of module and the inverter. The instance
methods accept arguments for things that do change, such as
irradiance and temperature.
Parameters
----------
arrays : Array or iterable of Array, optional
An Array or list of arrays that are part of the system. If not
specified a single array is created from the other parameters (e.g.
`surface_tilt`, `surface_azimuth`). If specified as a list, the list
must contain at least one Array;
if length of arrays is 0 a ValueError is raised. If `arrays` is
specified the following PVSystem parameters are ignored:
- `surface_tilt`
- `surface_azimuth`
- `albedo`
- `surface_type`
- `module`
- `module_type`
- `module_parameters`
- `temperature_model_parameters`
- `modules_per_string`
- `strings_per_inverter`
surface_tilt: float or array-like, default 0
Surface tilt angles in decimal degrees.
The tilt angle is defined as degrees from horizontal
(e.g. surface facing up = 0, surface facing horizon = 90)
surface_azimuth: float or array-like, default 180
Azimuth angle of the module surface.
North=0, East=90, South=180, West=270.
albedo : float, optional
Ground surface albedo. If not supplied, then ``surface_type`` is used
to look up a value in ``irradiance.SURFACE_ALBEDOS``.
If ``surface_type`` is also not supplied then a ground surface albedo
of 0.25 is used.
surface_type : string, optional
The ground surface type. See ``irradiance.SURFACE_ALBEDOS`` for
valid values.
module : string, optional
The model name of the modules.
May be used to look up the module_parameters dictionary
via some other method.
module_type : string, default 'glass_polymer'
Describes the module's construction. Valid strings are 'glass_polymer'
and 'glass_glass'. Used for cell and module temperature calculations.
module_parameters : dict or Series, optional
Module parameters as defined by the SAPM, CEC, or other.
temperature_model_parameters : dict or Series, optional
Temperature model parameters as required by one of the models in
pvlib.temperature (excluding poa_global, temp_air and wind_speed).
modules_per_string: int or float, default 1
See system topology discussion above.
strings_per_inverter: int or float, default 1
See system topology discussion above.
inverter : string, optional
The model name of the inverters.
May be used to look up the inverter_parameters dictionary
via some other method.
inverter_parameters : dict or Series, optional
Inverter parameters as defined by the SAPM, CEC, or other.
racking_model : string, default 'open_rack'
Valid strings are 'open_rack', 'close_mount', and 'insulated_back'.
Used to identify a parameter set for the SAPM cell temperature model.
losses_parameters : dict or Series, optional
Losses parameters as defined by PVWatts or other.
name : string, optional
**kwargs
Arbitrary keyword arguments.
Included for compatibility, but not used.
Raises
------
ValueError
If `arrays` is not None and has length 0.
See also
--------
pvlib.location.Location
"""
[docs]
def to_pvlib(self, **kwargs):
# TODO This one is a bit tricky.
# When creating pvlib object I want to be able to add missing parameters
# But still use the ones that are already set on the edm.PVSystem object.
if self.module_parameters is None:
self.module_parameters = {"pdc0": self.capacity}
# self.module_parameters.update({"pdc0": self.capacity})
if "pdc0" not in self.module_parameters.keys():
self.module_parameters["pdc0"] = self.capacity
if self.inverter_parameters is None:
self.inverter_parameters = {"pdc0": self.capacity}
if "pdc0" not in self.inverter_parameters.keys():
self.inverter_parameters["pdc0"] = self.capacity
return pvlib.pvsystem.PVSystem(name=self.name,
surface_tilt=self.surface_tilt,
surface_azimuth=self.surface_azimuth,
albedo=self.albedo,
surface_type=self.surface_type,
module_parameters=self.module_parameters,
inverter_parameters=self.inverter_parameters,
module_type=self.module_type,
racking_model=self.racking_model,
**kwargs)
[docs]
@dataclass(repr=False)
class SolarPowerArea(EnergyAsset):
capacity: Union[float, pd.DataFrame] = None
geopolygon: Union[GeoPolygon, GeoMultiPolygon] = None
[docs]
def to_geojson(self):
return json.loads(json.dumps(self.geopolygon.multipolygon.__geo_interface__))
@property
def geojson(self):
return self.to_geojson()