Source code for feedinlib.models

# -*- coding: utf-8 -*-

"""
Feed-in model classes.

This module provides abstract classes as blueprints for classes that implement
feed-in models for weather dependent renewable energy resources. These models
take in power plant and weather data to calculate power plant feed-in.
Furthermore, this module holds implementations of feed-in models. So far models
using the python libraries pvlib and windpowerlib to calculate photovoltaic and
wind power feed-in, respectively, have been implemented.

"""

from abc import ABC, abstractmethod
import warnings
import pandas as pd
from copy import deepcopy

from windpowerlib import ModelChain as WindpowerlibModelChain
from windpowerlib import (
    TurbineClusterModelChain as WindpowerlibClusterModelChain,
)
from windpowerlib import WindTurbine as WindpowerlibWindTurbine
from windpowerlib import WindFarm as WindpowerlibWindFarm
from windpowerlib import WindTurbineCluster as WindpowerlibWindTurbineCluster
from windpowerlib import get_turbine_types

from pvlib.modelchain import ModelChain as PvlibModelChain
from pvlib.pvsystem import PVSystem as PvlibPVSystem
from pvlib.location import Location as PvlibLocation
import pvlib.pvsystem

import feedinlib.powerplants


[docs]class Base(ABC): r""" The base class of feedinlib models. This base class is an abstract class serving as a blueprint for classes that implement feed-in models for weather dependent renewable energy resources. It forces implementors to implement certain properties and methods. """
[docs] def __init__(self, **kwargs): """ """ self._power_plant_requires = kwargs.get("powerplant_requires", None) self._requires = kwargs.get("requires", None)
@property @abstractmethod def power_plant_requires(self): """ The (names of the) power plant parameters this model requires in order to calculate the feed-in. As this is an abstract property you have to override it in a subclass so that the model can be instantiated. This forces implementors to make the required power plant parameters for a model explicit, even if they are empty, and gives them a good place to document them. By default, this property is settable and its value can be specified via an argument upon construction. If you want to keep this functionality, simply delegate all calls to the superclass. Parameters ---------- names : list(str), optional Containing the names of the required power plant parameters. """ return self._power_plant_requires @power_plant_requires.setter def power_plant_requires(self, names): self._power_plant_requires = names def _power_plant_requires_check(self, parameters): """ Function to check if all required power plant parameters are provided. This function only needs to be implemented in a subclass in case required power plant parameters specified in :attr:`power_plant_requires` are not a simple list that can be checked by :func:`~.power_plants.Base.check_models_power_plant_requirements`. """ raise NotImplementedError @property @abstractmethod def requires(self): """ The (names of the) parameters this model requires in order to calculate the feed-in. As this is an abstract property you have to override it in a subclass so that the model can be instantiated. This forces implementors to make the required model parameters explicit, even if they are empty, and gives them a good place to document them. By default, this property is settable and its value can be specified via an argument upon construction. If you want to keep this functionality, simply delegate all calls to the superclass. Parameters ---------- names : list(str), optional Containing the names of the required power plant parameters. """ return self._requires @requires.setter def requires(self, names): self._requires = names
[docs] @abstractmethod def feedin(self, weather, power_plant_parameters, **kwargs): """ Calculates power plant feed-in in Watt. As this is an abstract method you have to override it in a subclass so that the power plant feed-in using the respective model can be calculated. Parameters ---------- weather : Weather data to calculate feed-in. Format and required parameters depend on the model. power_plant_parameters : dict Dictionary with power plant specifications. Keys of the dictionary are the power plant parameter names, values of the dictionary hold the corresponding value. The dictionary must at least contain the power plant parameters required by the respective model and may further contain optional power plant parameters. See `power_plant_requires` property of the respective model for futher information. **kwargs : Keyword arguments for respective model's feed-in calculation. Returns ------- feedin : :pandas:`pandas.Series<series>` Series with power plant feed-in for specified time span in Watt. If respective model does calculate AC and DC feed-in, AC feed-in should be returned by default. `mode` parameter can be used to overwrite this default behavior and return DC power output instead (for an example see :meth:`~.models.Pvlib.feedin`). """ pass
[docs]class PhotovoltaicModelBase(Base): """ Expands model base class :class:`~.models.Base` by PV specific attributes. """ @property @abstractmethod def pv_system_area(self): r""" Area of PV system in :math:`m^2`. As this is an abstract property you have to override it in a subclass so that the model can be instantiated. This forces implementors to provide a way to retrieve the area of the PV system that is e.g. used to scale the feed-in by area. """ @property @abstractmethod def pv_system_peak_power(self): """ Peak power of PV system in Watt. As this is an abstract property you have to override it in a subclass so that the model can be instantiated. This forces implementors to provide a way to retrieve the peak power of the PV system that is e.g. used to scale the feed-in by installed capacity. """
[docs]class WindpowerModelBase(Base): """ Expands model base class :class:`~.models.Base` by wind power specific attributes. """ @property @abstractmethod def nominal_power_wind_power_plant(self): """ Nominal power of wind power plant in Watt. As this is an abstract property you have to override it in a subclass so that the model can be instantiated. This forces implementors to provide a way to retrieve the nominal power of the wind power plant that is e.g. used to scale the feed-in by installed capacity. """
[docs]class Pvlib(PhotovoltaicModelBase): r""" Model to determine the feed-in of a photovoltaic module using the pvlib. The pvlib [1]_ is a python library for simulating the performance of photovoltaic energy systems. For more information about the photovoltaic model check the documentation of the pvlib [2]_. Notes ------ In order to use this model various power plant and model parameters have to be provided. See :attr:`~.power_plant_requires` as well as :attr:`~.requires` for further information. Furthermore, the weather data used to calculate the feed-in has to have a certain format. See :meth:`~.feedin` for further information. References ---------- .. [1] `pvlib on github <https://github.com/pvlib/pvlib-python>`_ .. [2] `pvlib documentation <https://pvlib-python.readthedocs.io>`_ See Also -------- :class:`~.models.Base` :class:`~.models.PhotovoltaicModelBase` """
[docs] def __init__(self, **kwargs): """ """ super().__init__(**kwargs) self.power_plant = None
def __repr__(self): return "pvlib" @property def power_plant_requires(self): r""" The power plant parameters this model requires to calculate a feed-in. The required power plant parameters are: `module_name`, `inverter_name`, `azimuth`, `tilt`, `albedo/surface_type` module_name (str) Name of the PV module as in the Sandia module database. Use :func:`~.get_power_plant_data` with `dataset` = 'sandiamod' to get an overview of all provided modules. See the data set documentation [3]_ for further information on provided parameters. inverter_name (str) Name of the inverter as in the CEC inverter database. Use :func:`~.get_power_plant_data` with `dataset` = 'cecinverter' to get an overview of all provided inverters. See the data set documentation [4]_ for further information on provided parameters. azimuth (float) Azimuth angle of the module surface (South=180). See also :pvlib:`PVSystem.surface_azimuth <pvlib.pvsystem.\ PVSystem.surface_azimuth>` in pvlib documentation. tilt (float) Surface tilt angle in decimal degrees. The tilt angle is defined as degrees from horizontal (e.g. surface facing up = 0, surface facing horizon = 90). See also :pvlib:`PVSystem.surface_tilt <pvlib.pvsystem.PVSystem.\ surface_tilt>` in pvlib documentation. albedo (float) The ground albedo. See also :pvlib:`PVSystem.albedo <pvlib.\ pvsystem.PVSystem.albedo>` in pvlib documentation. surface_type (str) The ground surface type. See `SURFACE_ALBEDOS` in `pvlib.irradiance <https://github.com/pvlib/pvlib-python/blob/master/pvlib/irradiance.py>`_ module for valid values. References ---------- .. [3] `Sandia module database documentation <https://prod-ng.sandia.gov/techlib-noauth/access-control.cgi/2004/043535.pdf>`_ .. [4] `CEC inverter database documentation <https://prod-ng.sandia.gov/techlib-noauth/access-control.cgi/2007/075036.pdf>`_ """ # ToDo Maybe add method to assign suitable inverter if none is # specified required = [ "azimuth", "tilt", "module_name", ["albedo", "surface_type"], "inverter_name", ] # ToDo @Günni: is this necessary? if super().power_plant_requires is not None: required.extend(super().power_plant_requires) return required @property def requires(self): r""" The parameters this model requires to calculate a feed-in. The required model parameters are: `location` location (:obj:`tuple` or :shapely:`Point`) Geo location of the PV system. Can either be provided as a tuple with first entry being the latitude and second entry being the longitude or as a :shapely:`Point`. """ required = ["location"] if super().requires is not None: required.extend(super().requires) return required @property def pv_system_area(self): """ Area of PV system in :math:`m^2`. """ if self.power_plant: return ( self.power_plant.module_parameters.Area * self.power_plant.strings_per_inverter * self.power_plant.modules_per_string ) else: return None @property def pv_system_peak_power(self): """ Peak power of PV system in Watt. The peak power of the PV system can either be limited by the inverter or the PV module(s), wherefore in the case the `mode` parameter, which specifies whether AC or DC feed-in is calculated, is set to 'ac' (which is the default), the minimum of AC inverter power and maximum power of the module(s) is returned. In the case that `mode` is set to 'dc' the inverter power is not considered and the peak power is equal to the maximum power of the module(s). """ if self.power_plant: if self.mode == "ac": return min( self.power_plant.module_parameters.Impo * self.power_plant.module_parameters.Vmpo * self.power_plant.strings_per_inverter * self.power_plant.modules_per_string, self.power_plant.inverter_parameters.Paco, ) elif self.mode == "dc": return ( self.power_plant.module_parameters.Impo * self.power_plant.module_parameters.Vmpo * self.power_plant.strings_per_inverter * self.power_plant.modules_per_string ) else: raise ValueError( "{} is not a valid `mode`. `mode` must " "either be 'ac' or 'dc'.".format(self.mode) ) else: return None def _power_plant_requires_check(self, parameters): """ Function to check if all required power plant parameters are provided. Power plant parameters this model requires are specified in :attr:`~.models.Pvlib.power_plant_requires`. Parameters ----------- parameters : list(str) List of provided power plant parameters. """ for k in self.power_plant_requires: if not isinstance(k, list): if k not in parameters: raise AttributeError( "The specified model '{model}' requires power plant " "parameter '{k}' but it's not provided as an " "argument.".format(k=k, model=self) ) else: # in case one of several parameters can be provided if not list(filter(lambda x: x in parameters, k)): raise AttributeError( "The specified model '{model}' requires one of the " "following power plant parameters '{k}' but neither " "is provided as an argument.".format(k=k, model=self) )
[docs] def instantiate_module(self, **kwargs): """ Instantiates a :pvlib:`pvlib.PVSystem <pvlib.pvsystem.PVSystem>` object. Parameters ----------- **kwargs See `power_plant_parameters` parameter in :meth:`~.feedin` for more information. Returns -------- :pvlib:`pvlib.PVSystem <pvlib.pvsystem.PVSystem>` PV system to calculate feed-in for. """ # match all power plant parameters from power_plant_requires property # to pvlib's PVSystem parameters rename = { "module_parameters": get_power_plant_data("SandiaMod")[ kwargs.pop("module_name") ], "inverter_parameters": get_power_plant_data("CECInverter")[ kwargs.pop("inverter_name") ], "surface_azimuth": kwargs.pop("azimuth"), "surface_tilt": kwargs.pop("tilt"), } # update kwargs with renamed power plant parameters kwargs.update(rename) return PvlibPVSystem(**kwargs)
[docs] def feedin(self, weather, power_plant_parameters, **kwargs): r""" Calculates power plant feed-in in Watt. This function uses the :pvlib:`pvlib.ModelChain <pvlib.modelchain.\ ModelChain>` to calculate the feed-in for the given weather time series and PV system. By default the AC feed-in is returned. Set `mode` parameter to 'dc' to retrieve DC feed-in. Parameters ---------- weather : :pandas:`pandas.DataFrame<dataframe>` Weather time series used to calculate feed-in. See `weather` parameter in pvlib's Modelchain :pvlib:`run_model <pvlib.\ modelchain.ModelChain.run_model>` method for more information on required variables, units, etc. power_plant_parameters : dict Dictionary with power plant specifications. Keys of the dictionary are the power plant parameter names, values of the dictionary hold the corresponding value. The dictionary must at least contain the required power plant parameters (see :attr:`~.power_plant_requires`) and may further contain optional power plant parameters (see :pvlib:`pvlib.PVSystem <pvlib.\ pvsystem.PVSystem>`). location : :obj:`tuple` or :shapely:`Point` Geo location of the PV system. Can either be provided as a tuple with first entry being the latitude and second entry being the longitude or as a :shapely:`Point`. mode : str (optional) Can be used to specify whether AC or DC feed-in is returned. By default `mode` is 'ac'. To retrieve DC feed-in set `mode` to 'dc'. `mode` also influences the peak power of the PV system. See :attr:`~.pv_system_peak_power` for more information. **kwargs : Further keyword arguments can be used to overwrite :pvlib:`pvlib.\ ModelChain <pvlib.modelchain.ModelChain>` parameters. Returns ------- :pandas:`pandas.Series<series>` Power plant feed-in time series in Watt. """ self.mode = kwargs.pop("mode", "ac").lower() # ToDo Allow usage of feedinlib weather object which makes location # parameter obsolete location = kwargs.pop("location") # ToDo Allow location provided as shapely Point location = PvlibLocation( latitude=location[0], longitude=location[1], tz=weather.index.tz ) self.power_plant = self.instantiate_module(**power_plant_parameters) mc = PvlibModelChain(self.power_plant, location, **kwargs) mc.complete_irradiance(times=weather.index, weather=weather) mc.run_model() if self.mode == "ac": return mc.ac elif self.mode == "dc": return mc.dc.p_mp else: raise ValueError( "{} is not a valid `mode`. `mode` must " "either be 'ac' or 'dc'.".format(self.mode) )
[docs]class WindpowerlibTurbine(WindpowerModelBase): r""" Model to determine the feed-in of a wind turbine using the windpowerlib. The windpowerlib [1]_ is a python library for simulating the performance of wind turbines and farms. For more information about the model check the documentation of the windpowerlib [2]_. Notes ------ In order to use this model various power plant and model parameters have to be provided. See :attr:`~.power_plant_requires` as well as :attr:`~.requires` for further information. Furthermore, the weather data used to calculate the feed-in has to have a certain format. See :meth:`~.feedin` for further information. References ---------- .. [1] `windpowerlib on github <https://github.com/wind-python/windpowerlib>`_ .. [2] `windpowerlib documentation <https://windpowerlib.readthedocs.io>`_ See Also -------- :class:`~.models.Base` :class:`~.models.WindpowerModelBase` """
[docs] def __init__(self, **kwargs): """ """ super().__init__(**kwargs) self.power_plant = None
def __repr__(self): return "windpowerlib_single_turbine" @property def power_plant_requires(self): r""" The power plant parameters this model requires to calculate a feed-in. The required power plant parameters are: `hub_height`, `power_curve/power_coefficient_curve/turbine_type` hub_height (float) Hub height in m. See also :wind_turbine:`WindTurbine.hub_height <windpowerlib.\ wind_turbine.WindTurbine.hub_height>` in windpowerlib documentation. power_curve (:pandas:`pandas.DataFrame<frame>` or dict) DataFrame/dictionary with wind speeds in m/s and corresponding power curve value in W. See also :wind_turbine:`WindTurbine.power_curve <windpowerlib.\ wind_turbine.WindTurbine.power_curve>` in windpowerlib documentation. power_coefficient_curve (:pandas:`pandas.DataFrame<frame>` or dict) DataFrame/dictionary with wind speeds in m/s and corresponding power coefficient. See also :wind_turbine:`WindTurbine.power_coefficient_curve \ <windpowerlib.wind_turbine.WindTurbine.power_coefficient_curve>` in windpowerlib documentation. turbine_type (str) Name of the wind turbine type as in the oedb turbine library. Use :func:`~.get_power_plant_data` with `dataset` = 'oedb_turbine_library' to get an overview of all provided turbines. See the data set metadata [3]_ for further information on provided parameters. References ---------- .. [3] `oedb wind turbine library <https://openenergy-platform.org/dataedit/view/supply/wind_turbine_library>`_ """ required = [ "hub_height", ["power_curve", "power_coefficient_curve", "turbine_type"], ] if super().power_plant_requires is not None: required.extend(super().power_plant_requires) return required @property def requires(self): r""" The parameters this model requires to calculate a feed-in. This model does not require any additional model parameters. """ required = [] if super().requires is not None: required.extend(super().requires) return required @property def nominal_power_wind_power_plant(self): """ Nominal power of wind turbine in Watt. See :wind_turbine:`WindTurbine.nominal_power <windpowerlib.\ wind_turbine.WindTurbine.nominal_power>` in windpowerlib for further information. """ if self.power_plant: return self.power_plant.nominal_power else: return None def _power_plant_requires_check(self, parameters): """ Function to check if all required power plant parameters are provided. Power plant parameters this model requires are specified in :attr:`~.power_plant_requires`. Parameters ----------- parameters : list(str) List of provided power plant parameters. """ for k in self.power_plant_requires: if not isinstance(k, list): if k not in parameters: raise AttributeError( "The specified model '{model}' requires power plant " "parameter '{k}' but it's not provided as an " "argument.".format(k=k, model=self) ) else: # in case one of several parameters can be provided if not list(filter(lambda x: x in parameters, k)): raise AttributeError( "The specified model '{model}' requires one of the " "following power plant parameters '{k}' but neither " "is provided as an argument.".format(k=k, model=self) )
[docs] def instantiate_turbine(self, **kwargs): """ Instantiates a :windpowerlib:`windpowerlib.WindTurbine \ <windpowerlib.wind_turbine.WindTurbine>` object. Parameters ----------- **kwargs See `power_plant_parameters` parameter in :meth:`~.feedin` for more information. Returns -------- :windpowerlib:`windpowerlib.WindTurbine \ <windpowerlib.wind_turbine.WindTurbine>` Wind turbine to calculate feed-in for. """ return WindpowerlibWindTurbine(**kwargs)
[docs] def feedin(self, weather, power_plant_parameters, **kwargs): r""" Calculates power plant feed-in in Watt. This function uses the windpowerlib's :windpowerlib:`ModelChain \ <windpowerlib.modelchain.ModelChain>` to calculate the feed-in for the given weather time series and wind turbine. Parameters ---------- weather : :pandas:`pandas.DataFrame<dataframe>` Weather time series used to calculate feed-in. See `weather_df` parameter in windpowerlib's Modelchain :windpowerlib:`run_model \ <windpowerlib.modelchain.ModelChain.run_model>` method for more information on required variables, units, etc. power_plant_parameters : dict Dictionary with power plant specifications. Keys of the dictionary are the power plant parameter names, values of the dictionary hold the corresponding value. The dictionary must at least contain the required power plant parameters (see :attr:`~.power_plant_requires`) and may further contain optional power plant parameters (see :windpowerlib:`windpowerlib.\ WindTurbine <windpowerlib.wind_turbine.WindTurbine>`). **kwargs : Keyword arguments can be used to overwrite the windpowerlib's :windpowerlib:`ModelChain <windpowerlib.modelchain.ModelChain>` parameters. Returns ------- :pandas:`pandas.Series<series>` Power plant feed-in time series in Watt. """ self.power_plant = self.instantiate_turbine(**power_plant_parameters) mc = WindpowerlibModelChain(self.power_plant, **kwargs) return mc.run_model(weather).power_output
[docs]class WindpowerlibTurbineCluster(WindpowerModelBase): """ Model to determine the feed-in of a wind turbine cluster using the windpowerlib. The windpowerlib [1]_ is a python library for simulating the performance of wind turbines and farms. For more information about the model check the documentation of the windpowerlib [2]_. Notes ------ In order to use this model various power plant and model parameters have to be provided. See :attr:`~.power_plant_requires` as well as :attr:`~.requires` for further information. Furthermore, the weather data used to calculate the feed-in has to have a certain format. See :meth:`~.feedin` for further information. See Also -------- :class:`~.models.Base` :class:`~.models.WindpowerModelBase` References ---------- .. [1] `windpowerlib on github <https://github.com/wind-python/windpowerlib>`_ .. [2] `windpowerlib documentation <https://windpowerlib.readthedocs.io>`_ """
[docs] def __init__(self, **kwargs): """ """ super().__init__(**kwargs) self.power_plant = None
def __repr__(self): return "WindpowerlibTurbineCluster" @property def power_plant_requires(self): r""" The power plant parameters this model requires to calculate a feed-in. The required power plant parameters are: `wind_turbine_fleet/wind_farms` The windpowerlib differentiates between wind farms as a group of wind turbines (of the same or different type) in the same location and wind turbine clusters as wind farms and turbines that are assigned the same weather data point to obtain weather data for feed-in calculations and can therefore be clustered to speed up calculations. The `WindpowerlibTurbineCluster` class can be used for both :windpowerlib:`windpowerlib.WindFarm <windpowerlib.wind_farm.\ WindFarm>` and :windpowerlib:`windpowerlib.WindTurbineCluster \ <windpowerlib.wind_turbine_cluster.WindTurbineCluster>` calculations. To set up a :windpowerlib:`windpowerlib.WindFarm <windpowerlib.\ wind_farm.WindFarm>` please provide a `wind_turbine_fleet` and to set up a :windpowerlib:`windpowerlib.WindTurbineCluster \ <windpowerlib.wind_turbine_cluster.WindTurbineCluster>` please provide a list of `wind_farms`. See below for further information. wind_turbine_fleet (:pandas:`pandas.DataFrame<frame>`) The wind turbine fleet specifies the turbine types and their corresponding number or total installed capacity in the wind farm. DataFrame must have columns 'wind_turbine' and either 'number_of_turbines' (number of wind turbines of the same turbine type in the wind farm, can be a float) or 'total_capacity' (installed capacity of wind turbines of the same turbine type in the wind farm in Watt). The wind turbine in column 'wind_turbine' can be provided as a :class:`~.powerplants.WindPowerPlant` object, a dictionary with power plant parameters (see :attr:`~.models.WindpowerlibTurbine.power_plant_requires` for required parameters) or a :windpowerlib:`windpowerlib.\ WindTurbine <windpowerlib.wind_turbine.WindTurbine>`. See also `wind_turbine_fleet` parameter of :windpowerlib:`windpowerlib.WindFarm <windpowerlib.wind_farm.\ WindFarm>`. The wind turbine fleet may also be provided as a list of :windpowerlib:`windpowerlib.WindTurbineGroup <windpowerlib.\ wind_turbine.WindTurbineGroup>` as described there. wind_farms (list(dict) or list(:windpowerlib:`windpowerlib.WindFarm <windpowerlib.wind_farm.WindFarm>`)) List of wind farms in cluster. Wind farms in the list can either be provided as :windpowerlib:`windpowerlib.WindFarm \ <windpowerlib.wind_farm.WindFarm>` or as dictionaries where the keys of the dictionary are the wind farm parameter names and the values of the dictionary hold the corresponding value. The dictionary must at least contain a wind turbine fleet (see 'wind_turbine_fleet' parameter specifications above) and may further contain optional wind farm parameters (see :windpowerlib:`windpowerlib.WindFarm <windpowerlib.wind_farm.\ WindFarm>`). """ required = ["wind_turbine_fleet", "wind_farms"] if super().power_plant_requires is not None: required.extend(super().power_plant_requires) return required @property def requires(self): r""" The parameters this model requires to calculate a feed-in. This model does not require any additional model parameters. """ required = [] if super().requires is not None: required.extend(super().requires) return required @property def nominal_power_wind_power_plant(self): """ Nominal power of wind turbine cluster in Watt. The nominal power is the sum of the nominal power of all turbines. See `nominal_power` of :windpowerlib:`windpowerlib.WindFarm \ <windpowerlib.wind_farm.WindFarm>` or :windpowerlib:`windpowerlib.WindTurbineCluster \ <windpowerlib.wind_turbine_cluster.WindTurbineCluster>` for further information. """ if self.power_plant: return self.power_plant.nominal_power else: return None def _power_plant_requires_check(self, parameters): r""" Function to check if all required power plant parameters are provided. Power plant parameters this model requires are specified in :attr:`~.power_plant_requires`. Parameters ----------- parameters : list(str) List of provided power plant parameters. """ if not any([_ in parameters for _ in self.power_plant_requires]): raise KeyError( "The specified model '{model}' requires one of the following " "power plant parameters: {parameters}".format( model=self, parameters=self.power_plant_requires ) )
[docs] def instantiate_turbine(self, **kwargs): """ Instantiates a :windpowerlib:`windpowerlib.WindTurbine \ <windpowerlib.wind_turbine.WindTurbine>` object. Parameters ----------- **kwargs Dictionary with wind turbine specifications. Keys of the dictionary are the power plant parameter names, values of the dictionary hold the corresponding value. The dictionary must at least contain the required turbine parameters (see :attr:`~.models.WindpowerlibTurbine.power_plant_requires`) and may further contain optional power plant parameters (see :windpowerlib:`windpowerlib.WindTurbine \ <windpowerlib.wind_turbine.WindTurbine>`). Returns -------- :windpowerlib:`windpowerlib.WindTurbine \ <windpowerlib.wind_turbine.WindTurbine>` Wind turbine in wind farm or turbine cluster. """ power_plant = WindpowerlibWindTurbine(**kwargs) return power_plant
[docs] def instantiate_windfarm(self, **kwargs): r""" Instantiates a :windpowerlib:`windpowerlib.WindFarm <windpowerlib.\ wind_farm.WindFarm>` object. Parameters ---------- **kwargs Dictionary with wind farm specifications. Keys of the dictionary are the parameter names, values of the dictionary hold the corresponding value. The dictionary must at least contain a wind turbine fleet (see 'wind_turbine_fleet' specifications in :attr:`~.power_plant_requires`) and may further contain optional wind farm parameters (see :windpowerlib:`windpowerlib.WindFarm \ <windpowerlib.wind_farm.WindFarm>`). Returns -------- :windpowerlib:`windpowerlib.WindFarm <windpowerlib.wind_farm.WindFarm>` """ # deepcopy turbine fleet to not alter original turbine fleet wind_turbine_fleet = deepcopy(kwargs.pop("wind_turbine_fleet")) # if turbine fleet is provided as list, it is assumed that list # contains WindTurbineGroups and WindFarm can be directly instantiated if isinstance(wind_turbine_fleet, list): return WindpowerlibWindFarm(wind_turbine_fleet, **kwargs) # if turbine fleet is provided as DataFrame wind turbines in # 'wind_turbine' column have to be converted to windpowerlib # WindTurbine object elif isinstance(wind_turbine_fleet, pd.DataFrame): for ix, row in wind_turbine_fleet.iterrows(): turbine = row["wind_turbine"] if not isinstance(turbine, WindpowerlibWindTurbine): if isinstance( turbine, feedinlib.powerplants.WindPowerPlant ): turbine_data = turbine.parameters elif isinstance(turbine, dict): turbine_data = turbine else: raise TypeError( "The WindpowerlibTurbineCluster model requires " "that wind turbines must either be provided as " "WindPowerPlant objects, windpowerlib.WindTurbine " "objects or as dictionary containing all turbine " "parameters required by the WindpowerlibTurbine " "model but type of `wind_turbine` " "is {}.".format(type(row["wind_turbine"])) ) # initialize WindpowerlibTurbine instead of directly # initializing windpowerlib.WindTurbine to check required # power plant parameters wind_turbine = WindpowerlibTurbine() wind_turbine._power_plant_requires_check( turbine_data.keys() ) wind_turbine_fleet.loc[ ix, "wind_turbine" ] = wind_turbine.instantiate_turbine(**turbine_data) kwargs["wind_turbine_fleet"] = wind_turbine_fleet return WindpowerlibWindFarm(**kwargs) else: raise TypeError( "The WindpowerlibTurbineCluster model requires that the " "`wind_turbine_fleet` parameter is provided as a list or " "pandas.DataFrame but type of `wind_turbine_fleet` is " "{}.".format(type(wind_turbine_fleet)) )
[docs] def instantiate_turbine_cluster(self, **kwargs): r""" Instantiates a :windpowerlib:`windpowerlib.WindTurbineCluster \ <windpowerlib.wind_turbine_cluster.WindTurbineCluster>` object. Parameters ---------- **kwargs Dictionary with turbine cluster specifications. Keys of the dictionary are the parameter names, values of the dictionary hold the corresponding value. The dictionary must at least contain a list of wind farms (see 'wind_farms' specifications in :attr:`~.power_plant_requires`) and may further contain optional wind turbine cluster parameters (see :windpowerlib:`windpowerlib.WindTurbineCluster \ <windpowerlib.wind_turbine_cluster.WindTurbineCluster>`). Returns -------- :windpowerlib:`windpowerlib.WindTurbineCluster <windpowerlib.wind_turbine_cluster.WindTurbineCluster>` """ wind_farm_list = [] for wind_farm in kwargs.pop("wind_farms"): if not isinstance(wind_farm, WindpowerlibWindFarm): wind_farm_list.append(self.instantiate_windfarm(**wind_farm)) else: wind_farm_list.append(wind_farm) kwargs["wind_farms"] = wind_farm_list return WindpowerlibWindTurbineCluster(**kwargs)
[docs] def feedin(self, weather, power_plant_parameters, **kwargs): r""" Calculates power plant feed-in in Watt. This function uses the windpowerlib's :windpowerlib:`TurbineClusterModelChain <windpowerlib.\ turbine_cluster_modelchain.TurbineClusterModelChain>` to calculate the feed-in for the given weather time series and wind farm or cluster. Parameters ---------- weather : :pandas:`pandas.DataFrame<dataframe>` Weather time series used to calculate feed-in. See `weather_df` parameter in windpowerlib's TurbineClusterModelChain :windpowerlib:`run_model <windpowerlib.turbine_cluster_modelchain.\ TurbineClusterModelChain.run_model>` method for more information on required variables, units, etc. power_plant_parameters : dict Dictionary with either wind farm or wind turbine cluster specifications. For more information on wind farm parameters see `kwargs` in :meth:`~.instantiate_windfarm`. For information on turbine cluster parameters see `kwargs` in :meth:`~.instantiate_turbine_cluster`. **kwargs : Keyword arguments can be used to overwrite the windpowerlib's :windpowerlib:`TurbineClusterModelChain <windpowerlib.\ turbine_cluster_modelchain.TurbineClusterModelChain>` parameters. Returns ------- :pandas:`pandas.Series<series>` Power plant feed-in time series in Watt. """ # wind farm calculation if "wind_turbine_fleet" in power_plant_parameters.keys(): self.power_plant = self.instantiate_windfarm( **power_plant_parameters ) # wind cluster calculation else: self.power_plant = self.instantiate_turbine_cluster( **power_plant_parameters ) mc = WindpowerlibClusterModelChain(self.power_plant, **kwargs) return mc.run_model(weather).power_output
[docs]def get_power_plant_data(dataset, **kwargs): r""" Function to retrieve power plant data sets provided by feed-in models. This function can be used to retrieve power plant data from data sets and to get an overview of which modules, inverters and turbine types are provided and can be used in feed-in calculations. Parameters ---------- dataset : str Specifies data set to retrieve. Possible options are: * pvlib PV module and inverter datasets: 'sandiamod', 'cecinverter' The original data sets are hosted here: https://github.com/NREL/SAM/tree/develop/deploy/libraries See :pvlib:`retrieve_sam <pvlib.pvsystem.retrieve_sam>` for further information. * windpowerlib wind turbine dataset: 'oedb_turbine_library' See :windpowerlib:`get_turbine_types <windpowerlib.wind_turbine.\ get_turbine_types>` for further information. **kwargs See referenced functions for each dataset above for further optional parameters. Example ------- >>> from feedinlib import get_power_plant_data >>> data = get_power_plant_data('sandiamod') >>> # list of all provided PV modules >>> pv_modules = data.columns >>> print(data.loc["Area", data.columns.str.contains('Aleo_S03')]) Aleo_S03_160__2007__E__ 1.28 Aleo_S03_165__2007__E__ 1.28 Name: Area, dtype: object """ dataset = dataset.lower() if dataset in ["sandiamod", "cecinverter"]: return pvlib.pvsystem.retrieve_sam( name=dataset, path=kwargs.get("path", None) ) elif dataset == "oedb_turbine_library": return get_turbine_types( turbine_library=kwargs.get("turbine_library", "local"), print_out=kwargs.get("print_out", False), filter_=kwargs.get("filter_", True), ) else: warnings.warn("Unknown dataset {}.".format(dataset)) return None