# -*- coding: utf-8 -*-
"""
@author: oemof development group
"""
from abc import ABC, abstractmethod
import os
import sys
import numpy as np
import pandas as pd
import pvlib
import logging
import requests
[docs]class Base(ABC):
r""" The base class of feedinlib models.
Parameters
----------
required : list of strings, optional
Containing the names of the required parameters to use the model.
"""
def __init__(self, **kwargs):
self._required = kwargs.get("required")
@property
@abstractmethod
def required(self):
""" The (names of the) parameters this model requires in order to
calculate the feedin.
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 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 and argument on construction. If you want to keep this
functionality, simply delegate all calls to the superclass.
"""
return self._required
@required.setter
def required(self, names):
self._required = names
# Returning None rarely makes sense, IMHO.
# Returning self at least allows for method chaining.
return self
[docs]class PvlibBased(Base):
r"""Model to determine the output of a photovoltaik module
The calculation is based on the library pvlib. [1]_
Parameters
----------
PvlibBased.required (list of strings, optional)
List of required parameters of the model
Notes
-----
For more information about the photovoltaic model check the documentation
of the pvlib library.
https://readthedocs.org/projects/pvlib-python/
References
----------
.. [1] `pvlib on github <https://github.com/pvlib/pvlib-python>`_
Examples
--------
>>> from feedinlib import models
>>> pv_model = models.PvlibBased()
See Also
--------
Base
SimpleWindTurbine
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.area = None
self.peak = None
@property
def required(self):
r""" The parameters this model requires to calculate a feedin.
In this feedin model the required parameters are:
:modul_name: (string) -
name of a pv module from the sam.nrel database [12]_
:tilt: (float) -
tilt angle of the pv module (horizontal=0°)
:azimuth: (float) -
azimuth angle of the pv module (south=180°)
:albedo: (float) -
albedo factor arround the module
"""
if super().required is not None:
return super().required
return ["azimuth", "tilt", "module_name", "albedo"]
[docs] def feedin(self, **kwargs):
r"""
Feedin time series for the given pv module.
In contrast to :py:func:`turbine_power_output
<feedinlib.models.PvlibBased.get_pv_power_output>` it returns just
the feedin series instead of the whole DataFrame.
Parameters
----------
see :
:py:func:`turbine_power_output
<feedinlib.models.PvlibBased.get_pv_power_output>`
Returns
-------
pandas.Series
A time series of the power output for the given pv module.
"""
return self.get_pv_power_output(**kwargs).p_mp
[docs] def solarposition_hourly_mean(self, location, data, **kwargs):
r"""
Determine the position of the sun as an hourly mean of all angles
above the horizon.
Parameters
----------
location : pvlib.location.Location
A pvlib location object containing the longitude, latitude and the
timezone of the location
data : pandas.DataFrame
Containing the time index of the location.
method : string, optional
Method to calulate the position of the sun according to the
methods provided by the pvlib function (default: 'ephemeris')
'pvlib.solarposition.get_solarposition'. [2]_
freq : string, optional
The time interval to create the hourly mean (default: '5Min').
pandas.DataFrame
The DataFrame contains the following new columns: azimuth, zenith,
elevation
Notes
-----
Combining hourly values for irradiation with discrete values for the
position of the sun can lead to unrealistic results. Using hourly
values for the position minimizes these errors.
References
----------
.. [2] `pvlib solarposition <http://pvlib-python.readthedocs.org/en/
latest/pvlib.html#pvlib.solarposition.get_solarposition>`_.
See Also
--------
solarposition : calculates the position of the sun at a given time
"""
data_5min = pd.DataFrame(
index=pd.date_range(data.index[0],
periods=data.shape[0]*12, freq='5Min',
tz=kwargs['weather'].timezone))
data_5min = pvlib.solarposition.get_solarposition(
time=data_5min.index, latitude=location.latitude,
longitude=location.longitude, method='ephemeris')
return pd.concat(
[data, data_5min.clip_lower(0).resample('H').mean()],
axis=1, join='inner')
[docs] def solarposition(self, location, data, **kwargs):
r"""
Determine the position of the sun unsing the time of the time index.
Parameters
----------
location : pvlib.location.Location
A pvlib location object containing the longitude, latitude and the
timezone of the location
data : pandas.DataFrame
Containing the timeseries of the weather data and the time index of
the location.
method : string, optional
Method to calulate the position of the sun according to the
methods provided by the pvlib function (default: 'ephemeris')
'pvlib.solarposition.get_solarposition'. [2]_
Returns
-------
pandas.DataFrame
The DataFrame contains the following new columns: azimuth, zenith,
elevation
Notes
-----
This method is not used in favour to solarposition_hourly_mean.
Examples
--------
>>> import pvlib
>>> import pandas as pd
>>> from feedinlib import models
>>> loc = pvlib.location.Location(52, 13, 'Europe/Berlin')
>>> pvmodel = models.PvlibBased()
>>> data = pd.DataFrame(index=pd.date_range(pd.datetime(2010, 1, 1, 0),
... periods=8760, freq='H', tz=loc.tz))
>>> elevation = pvmodel.solarposition(loc, data).elevation
>>> print(round(elevation[12], 3))
14.968
See Also
--------
solarposition_hourly_mean : calculates the position of the sun as an
hourly mean.
"""
return pd.concat(
[data, pvlib.solarposition.get_solarposition(
time=data.index, latitude=location.latitude,
longitude=location.longitude,
method=kwargs.get('method', 'ephemeris'))],
axis=1, join='inner')
[docs] def angle_of_incidence(self, data, **kwargs):
r"""
Determine the angle of incidence using the pvlib aoi funktion. [4]_
Parameters
----------
data : pandas.DataFrame
Containing the timeseries of the azimuth and zenith angle
tilt : float
Tilt angle of the pv module (horizontal=0°).
azimuth : float
Azimuth angle of the pv module (south=180°).
Returns
-------
pandas.Series
Angle of incidence in degrees.
See Also
--------
solarposition_hourly_mean, solarposition
References
----------
.. [4] `pvlib angle of incidence <http://pvlib-python.readthedocs.org/
en/latest/pvlib.html#pvlib.irradiance.aoi>`_.
"""
return pvlib.irradiance.aoi(
solar_azimuth=data['azimuth'], solar_zenith=data['zenith'],
surface_tilt=self.powerplant.tilt,
surface_azimuth=self.powerplant.azimuth)
[docs] def global_in_plane_irradiation(self, data, **kwargs):
r"""
Determine the global irradiaton on the tilted surface.
This method determines the global irradiation in plane knowing
the direct and diffuse irradiation, the incident angle and the
orientation of the surface. The method uses the
pvlib.irradiance.globalinplane function [5]_ and some other functions
of the pvlib.atmosphere [6]_ and the pvlib.solarposition [2]_ module to
provide the input parameters for the globalinplane function.
Parameters
----------
data : pandas.DataFrame
Containing the time index of the location and columns with the
following timeseries: (dirhi, dhi, zenith, azimuth, aoi)
tilt : float
Tilt angle of the pv module (horizontal=0°).
azimuth : float
Azimuth angle of the pv module (south=180°).
albedo : float
Albedo factor arround the module
Returns
-------
pandas.DataFrame
The DataFrame contains the following new columns: poa_global,
poa_diffuse, poa_direct
References
----------
.. [5] `pvlib globalinplane <http://pvlib-python.readthedocs.org/en/
latest/pvlib.html#pvlib.irradiance.globalinplane>`_.
.. [6] `pvlib atmosphere <http://pvlib-python.readthedocs.org/en/
latest/pvlib.html#module-pvlib.atmosphere>`_.
See Also
--------
solarposition_hourly_mean, solarposition, angle_of_incidenc
"""
# Determine the extraterrestrial radiation
data['dni_extra'] = pvlib.irradiance.extraradiation(
datetime_or_doy=data.index.dayofyear)
# Determine the relative air mass
data['airmass'] = pvlib.atmosphere.relativeairmass(data['zenith'])
# Determine direct normal irradiation
data['dni'] = (data['dirhi']) / np.sin(np.radians(90 - data['zenith']))
# what for??
data['dni'][data['zenith'] > 88] = data['dirhi']
# Determine the sky diffuse irradiation in plane
# with model of Perez (modell switch would be good)
data['poa_sky_diffuse'] = pvlib.irradiance.perez(
surface_tilt=self.powerplant.tilt,
surface_azimuth=self.powerplant.azimuth,
dhi=data['dhi'],
dni=data['dni'],
dni_extra=data['dni_extra'],
solar_zenith=data['zenith'],
solar_azimuth=data['azimuth'],
airmass=data['airmass'])
# Set NaN values to zero
data['poa_sky_diffuse'][
pd.isnull(data['poa_sky_diffuse'])] = 0
# Determine the diffuse irradiation from ground reflection in plane
data['poa_ground_diffuse'] = pvlib.irradiance.grounddiffuse(
ghi=data['dirhi'] + data['dhi'],
albedo=self.powerplant.albedo,
surface_tilt=self.powerplant.tilt)
# Determine total in-plane irradiance
data = pd.concat(
[data, pvlib.irradiance.globalinplane(
aoi=data['aoi'],
dni=data['dni'],
poa_sky_diffuse=data['poa_sky_diffuse'],
poa_ground_diffuse=data['poa_ground_diffuse'])],
axis=1, join='inner')
return data
[docs] def fetch_module_data(self, lib='sandia-modules', **kwargs):
r"""
Fetch the module data from the Sandia Module library
The file is saved in the ~/.oemof folder and loaded from there to save
time and to make it possible to work if the server is down.
Parameters
----------
module_name : string
Name of a pv module from the sam.nrel database [9]_.
Returns
-------
dictionary
The necessary module data for the selected module to use the
pvlib sapm pv model. [8]_
Examples
--------
>>> from feedinlib import models
>>> pvmodel = models.PvlibBased()
>>> name = 'Yingli_YL210__2008__E__'
>>> print(pvmodel.fetch_module_data(module_name=name).Area)
1.7
See Also
--------
pv_module_output
"""
if kwargs.get('module_name') is None:
kwargs['module_name'] = self.powerplant.module_name
basic_path = os.path.join(os.path.expanduser("~"), '.oemof')
url = 'https://sam.nrel.gov/sites/default/files/'
filename = os.path.join(basic_path, 'sam-library-sandia-modules.csv')
if not os.path.exists(basic_path):
os.makedirs(basic_path)
if not os.path.isfile(filename):
url_file = 'sam-library-sandia-modules-2015-6-30.csv'
req = requests.get(url + url_file)
with open(filename, 'wb') as fout:
fout.write(req.content)
if kwargs.get('module_name') == 'all':
module_data = pvlib.pvsystem.retrieve_sam(path=filename)
else:
module_data = (pvlib.pvsystem.retrieve_sam(path=filename)
[kwargs['module_name']])
self.area = module_data.Area
self.peak = module_data.Impo * module_data.Vmpo
return module_data
[docs] def pv_module_output(self, data, **kwargs):
r"""
Determine the output of pv-system.
Using the pvlib.pvsystem.sapm function of the pvlib [8]_.
Parameters
----------
module_name : string
Name of a pv module from the sam.nrel database [9]_.
data : pandas.DataFrame
Containing the time index of the location and columns with the
following timeseries: (temp_air [K], v_wind, poa_global,
poa_diffuse, poa_direct, airmass, aoi)
method : string, optional
Method to calulate the position of the sun according to the
methods provided by the pvlib function (default: 'ephemeris')
'pvlib.solarposition.get_solarposition'. [10]_
Returns
-------
pandas.DataFrame
The DataFrame contains the following new columns: p_pv_norm,
p_pv_norm_area
References
----------
.. [8] `pvlib pv-system <http://pvlib-python.readthedocs.org/en/
latest/pvlib.html#pvlib.pvsystem.sapm>`_.
.. [9] `module library <https://sam.nrel.gov/sites/default/files/
sam-library-sandia-modules-2015-6-30.csv>`_.
.. [10] `pvlib get_solarposition <http://pvlib-python.readthedocs.org
/en/latest/pvlib.html#pvlib.solarposition.get_solarposition>`_.
See Also
--------
global_in_plane_irradiation
"""
# Determine module and cell temperature
data['temp_air_celsius'] = data['temp_air'] - 273.15
data = pd.concat([data, pvlib.pvsystem.sapm_celltemp(
poa_global=data['poa_global'],
wind_speed=data['v_wind'],
temp_air=data['temp_air_celsius'],
model='Open_rack_cell_polymerback')], axis=1, join='inner')
# Retrieve the module data object
module_data = self.fetch_module_data(**kwargs)
data['effective_irradiance'] = pvlib.pvsystem.sapm_effective_irradiance(
poa_direct=data['poa_direct'], poa_diffuse=data['poa_diffuse'],
airmass_absolute=data['airmass'], aoi=data['aoi'],
module=module_data)
# Apply the Sandia PV Array Performance Model (SAPM) to get a
data = pd.concat([data, pvlib.pvsystem.sapm(
effective_irradiance=data['effective_irradiance'],
temp_cell=data['temp_cell'],
module=module_data)], axis=1, join='inner')
# Set NaN values to zero
data['p_mp'][
pd.isnull(data['p_mp'])] = 0
return data
[docs] def get_pv_power_output(self, **kwargs):
r"""
Output of the given pv module. For the theoretical background see the
pvlib documentation [11]_.
Parameters
----------
weather : feedinlib.weather.FeedinWeather object
Instance of the feedinlib weather object (see class
:py:class:`FeedinWeather<feedinlib.weather.FeedinWeather>` for more
details)
Notes
-----
See :py:func:`method required <feedinlib.models.PvlibBased.required>`
for all required parameters of this model.
Returns
-------
pandas.DataFrame
The DataFrame contains the following new columns: p_pv_norm,
p_pv_norm_area and all timeseries calculated before.
References
----------
.. [11] `pvlib documentation <https://readthedocs.org/projects/
pvlib-python/>`_.
.. [12] `module library <https://sam.nrel.gov/sites/default/files/
sam-library-sandia-modules-2015-6-30.csv>`_.
See Also
--------
pv_module_output, feedin
"""
data = kwargs['weather'].data
# Create a location object
location = pvlib.location.Location(kwargs['weather'].latitude,
kwargs['weather'].longitude,
kwargs['weather'].timezone)
# Determine the position of the sun
data = self.solarposition_hourly_mean(location, data, **kwargs)
# A zenith angle greater than 90° means, that the sun is down.
data['zenith'][data['zenith'] > 90] = 90
# Determine the angle of incidence
data['aoi'] = self.angle_of_incidence(data, **kwargs)
# Determine the irradiation in plane
data = self.global_in_plane_irradiation(data, **kwargs)
# Determine the output of the pv module
data = self.pv_module_output(data, **kwargs)
return data
[docs]class SimpleWindTurbine(Base):
r"""Model to determine the output of a wind turbine
Parameters
----------
required : list of strings
Containing the names of the required parameters to use the model.
Examples
--------
>>> from feedinlib import models
>>> required_ls = ['h_hub', 'd_rotor', 'wind_conv_type', 'data_height']
>>> wind_model = models.SimpleWindTurbine(required=required_ls)
See Also
--------
Base
PvlibBased
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.nominal_power_wind_turbine = None
@property
def required(self):
r""" The parameters this model requires to calculate a feedin.
In this feedin model the required parameters are:
:h_hub: (float) -
Height of the hub of the wind turbine
:d_rotor: (float) -
'Diameter of the rotor [m]',
:wind_conv_type: (string) -
Name of the wind converter type. Use self.get_wind_pp_types() to
see a list of all possible wind converters.
"""
if super().required is not None:
return super().required
return ["h_hub", "d_rotor", "wind_conv_type"]
[docs] def feedin(self, **kwargs):
r"""
Alias for :py:func:`turbine_power_output
<feedinlib.models.SimpleWindTurbine.turbine_power_output>`.
"""
return self.turbine_power_output(**kwargs)
[docs] def get_wind_pp_types(self, print_out=True):
r"""
Get the names of all possible wind converter types.
Parameters
----------
print_out : boolean (default: True)
Directly prints the list of types if set to True.
Examples
--------
>>> from feedinlib import models
>>> w_model = models.SimpleWindTurbine()
>>> valid_types_df = w_model.get_wind_pp_types(print_out=False)
>>> valid_types_df.shape
(91, 2)
"""
res_df, df = self.fetch_cp_values_from_file(wind_conv_type='')
if print_out:
pd.set_option('display.max_rows', len(df))
print(df[['rli_anlagen_id', 'p_nenn']])
pd.reset_option('display.max_rows')
return df[['rli_anlagen_id', 'p_nenn']]
[docs] def rho_hub(self, weather):
r"""
Calculates the density of air in kg/m³ at hub height.
(temperature in K, height in m, pressure in Pa)
Parameters
----------
data : DataFrame or Dictionary
Containing columns or keys with the timeseries for Temperature
(temp_air) and pressure (pressure).
data_height : DataFrame or Dictionary
Containing columns or keys with the height of the measurement or
model data for temperature (temp_air) and pressure (pressure).
h_hub : float
Height of the hub of the wind turbine
Returns
-------
float
density of air in kg/m³ at hub height
Notes
-----
Assumptions:
* Temperature gradient of -6.5 K/km
* Pressure gradient of -1/8 hPa/m
The following equations are used [22]_:
.. math:: T_{hub}=T_{air, data}-0.0065\cdot\left(h_{hub}-h_{T,data}
\right)
.. math:: p_{hub}=\left(p_{data}/100-\left(h_{hub}-h_{p,data}\right)
*\frac{1}{8}\right)/\left(2.8706\cdot T_{hub}\right)
with T: temperature [K], h: height [m], p: pressure [Pa]
ToDo: Check the equation and add references.
References
----------
.. [22] ICAO-Standardatmosphäre (ISA).
http://www.deutscher-wetterdienst.de/lexikon/download.php?file=Standardatmosphaere.pdf
See Also
--------
v_wind_hub
"""
h_temperature_data = weather.data_height['temp_air']
h_pressure_data = weather.data_height['pressure']
T_hub = weather.data.temp_air - 0.0065 * (
self.powerplant.h_hub - h_temperature_data)
return (
weather.data.pressure / 100 -
(self.powerplant.h_hub - h_pressure_data) * 1 / 8
) / (2.8706 * T_hub)
[docs] def v_wind_hub(self, weather):
r"""
Calculates the wind speed in m/s at hub height.
Parameters
----------
data : DataFrame or Dictionary
Containing columns or keys with the timeseries for wind speed
(v_wind) and roughness length (z0).
data_height : DataFrame or Dictionary
Containing columns or keys with the height of the measurement or
model data for temperature (temp_air) and pressure (pressure).
h_hub : float
Height of the hub of the wind turbine
Returns
-------
float
wind speed [m/s] at hub height
Notes
-----
The following equation is used for the logarithmic wind profile [20]_:
.. math:: v_{wind,hub}=v_{wind,data}\cdot\frac{\ln\left(\frac{h_{hub}}
{z_{0}}\right)}{\ln\left(\frac{h_{data}}{z_{0}}\right)}
with:
v: wind speed [m/s], h: height [m], z0: roughnes length [m]
:math:`h_{data}` is the hight in which the wind velocity is measured.
(height in m, velocity in m/s)
ToDo: Check the equation and add references.
References
----------
.. [20] Gasch R., Twele J.: "Windkraftanlagen". 6. Auflage, Wiesbaden,
Vieweg + Teubner, 2010, page 129
See Also
--------
rho_hub
"""
return (weather.data.v_wind * np.log(self.powerplant.h_hub /
weather.data.z0) /
np.log(weather.data_height['v_wind'] /
weather.data.z0))
[docs] def fetch_cp_values_from_file(self, **kwargs):
r"""
Fetch cp values from a file or download it from a server.
The files are located in the ~/.oemof folder.
Parameters
----------
wind_conv_type : string
Name of the wind converter type. Use self.get_wind_pp_types() to
see a list of all possible wind converters.
cp_path : string, optional
Path where the cp file is stored
filename : string, optional
Filename of the cp file without suffix. The suffix should be csv or
hf5.
url : string, optional
URL from where the cp file is loaded if not present
Returns
-------
pandas.DataFrame
cp values, wind converter type, installed capacity or the full
table if the given wind converter cannot be found in the table.
Notes
-----
The files can be downloaded from
http://vernetzen.uni-flensburg.de/~git/
See Also
--------
fetch_cp_values_from_db
"""
wpp_type = kwargs.get('wind_conv_type')
if wpp_type is None:
wpp_type = self.powerplant.wind_conv_type
cp_path = kwargs.get(
'basic_path', os.path.join(os.path.expanduser("~"), '.oemof'))
filename = kwargs.get('filename', 'cp_values')
filepath = os.path.join(cp_path, filename)
url = kwargs.get(
'url', 'http://vernetzen.uni-flensburg.de/~git/cp_values')
suffix = '.hf5'
if not os.path.exists(cp_path):
os.makedirs(cp_path)
if not os.path.isfile(filepath + suffix):
req = requests.get(url + suffix)
with open(filepath + suffix, 'wb') as fout:
fout.write(req.content)
logging.info('Copying cp_values from {0} to {1}'.format(
url, filepath + suffix))
logging.debug('Retrieving cp values from {0}'.format(
filename + suffix))
try:
df = pd.read_hdf(filepath + suffix, 'cp')
except:
suffix = '.csv'
logging.info('Failed loading cp values from hdf file, trying csv.')
logging.debug('Retrieving cp values from {0}'.format(
filename + suffix))
if not os.path.isfile(filename + suffix):
req = requests.get(url + suffix)
with open(filepath + suffix, 'wb') as fout:
fout.write(req.content)
logging.info('Copying cp_values from {0} to {1}'.format(
url, filename + suffix))
df = pd.read_csv(filename + suffix, index_col=0)
res_df = df[df.rli_anlagen_id == wpp_type].reset_index(drop=True)
return res_df, df
[docs] def fetch_cp_values(self, **kwargs):
r"""
Fetch cp values from database, file or http source.
If no set of cp values is given, tries to fetch the values from the
database. If no valid database connection is present, tries to read the
values from a local file. If the need files are not present, loads the
files from a server. First tries to read the hdf5 file and then the csv
file.
Parameters
----------
wind_conv_type : string
Name of the wind converter type. Use self.get_wind_pp_types() to
see a list of all possible wind converters.
Returns
-------
pandas.DataFrame
cp values, wind converter type, installed capacity
Examples
--------
>>> from feedinlib import models
>>> w_model = models.SimpleWindTurbine()
>>> cp = w_model.fetch_cp_values(wind_conv_type='ENERCON E 126 7500')
>>> index=cp.loc[0, :][2:55].index=='8'
>>> print(cp.loc[0, :][2:55].values[index][0])
0.478
See Also
--------
fetch_cp_values_from_db, fetch_cp_values_from_file
"""
if kwargs.get('cp_values', None) is not None:
res_df = kwargs.get['cp_values']
else:
res_df, df = self.fetch_cp_values_from_file(**kwargs)
if res_df.shape[0] == 0:
pd.set_option('display.max_rows', len(df))
logging.info('Possible types: \n{0}'.format(df.rli_anlagen_id))
pd.reset_option('display.max_rows')
res_df = df
sys.exit('Cannot find the wind converter typ: {0}'.format(
kwargs.get('wind_conv_type', None)))
return res_df
[docs] def cp_values(self, v_wind, **kwargs):
r"""
Interpolates the cp value as a function of the wind velocity between
data obtained from the power curve of the specified wind turbine type.
Parameters
----------
wind_conv_type : string
Name of the wind converter type. Use self.get_wind_pp_types() to
see a list of all possible wind converters.
v_wind : pandas.Series or numpy.array
Wind speed at hub height [m/s]
Returns
-------
numpy.array
cp values, wind converter type, installed capacity
>>> from feedinlib import models
>>> import numpy
>>> v_wi = numpy.array([1,2,3,4,5,6,7,8])
>>> w = models.SimpleWindTurbine(required=['wind_conv_type', 'v_wind'])
>>> cp = w.cp_values(wind_conv_type='ENERCON E 126 7500', v_wind=v_wi)
>>> print(cp)
[ 0. 0. 0.191 0.352 0.423 0.453 0.47 0.478]
See Also
--------
fetch_cp_values_from_db, fetch_cp_values_from_file
"""
ncols = ['rli_anlagen_id', 'p_nenn', 'source', 'modificationtimestamp']
res_df = self.fetch_cp_values(**kwargs)
cp_data = np.array([0, 0])
for col in res_df.keys():
if col not in ncols:
if res_df[col][0] is not None and not np.isnan(
float(res_df[col][0])):
cp_data = np.vstack((cp_data, np.array(
[float(col), float(res_df[col])])))
cp_data = np.delete(cp_data, 0, 0)
self.nominal_power_wind_turbine = res_df['p_nenn'][0] * 1000
v_wind[v_wind > np.max(cp_data[:, 0])] = np.max(cp_data[:, 0])
return np.interp(v_wind, cp_data[:, 0], cp_data[:, 1])
[docs] def turbine_power_output(self, **kwargs):
r"""
Calculates the power output in W of one wind turbine.
Parameters
----------
weather : feedinlib.weather.FeedinWeather object
Instance of the feedinlib weather object (see class
:py:class:`FeedinWeather<feedinlib.weather.FeedinWeather>` for more
details)
\**kwargs :
Keyword arguments for underlaying methods like filename to name the
file of the cp_values.
# TODO Move the following parameters to a better place :-)
Returns
-------
pandas.Series
Electrical power of the wind turbine
Notes
-----
The following equation is used for the power output :math:`P_{wpp}`
[21]_:
.. math:: P_{wpp}=\frac{1}{8}\cdot\rho_{air,hub}\cdot d_{rotor}^{2}
\cdot\pi\cdot v_{wind}^{3}\cdot cp\left(v_{wind}\right)
with:
v: wind speed [m/s], d: diameter [m], :math:`\rho`: density [kg/m³]
ToDo: Check the equation and add references.
References
----------
.. [21] Gasch R., Twele J.: "Windkraftanlagen". 6. Auflage, Wiesbaden,
Vieweg + Teubner, 2010, pages 35ff, 208
See Also
--------
get_normalized_wind_pp_time_series
"""
weather = kwargs['weather']
p_wpp = (
(self.rho_hub(weather) / 2) *
(((self.powerplant.d_rotor / 2) ** 2) * np.pi) *
np.power(self.v_wind_hub(weather), 3) *
self.cp_values(self.v_wind_hub(weather), **kwargs))
p_wpp_series = pd.Series(data=p_wpp,
index=kwargs['weather'].data.index,
name='feedin_wind_pp')
return p_wpp_series.clip(
upper=(float(self.nominal_power_wind_turbine)))
if __name__ == "__main__":
import doctest
doctest.testmod()