Source code for windpowerlib.wind_turbine

"""
The ``wind_turbine`` module contains the class WindTurbine that implements
a wind turbine in the windpowerlib and functions needed for the modelling of a
wind turbine.

"""

__copyright__ = "Copyright oemof developer group"
__license__ = "GPLv3"

import pandas as pd
import logging
import sys
import requests
import os
import warnings


[docs]class WindTurbine(object): r""" Defines a standard set of wind turbine attributes. Parameters ---------- name : string Name of the wind turbine type. Use :py:func:`~.get_turbine_types` to see a table of all wind turbines for which power (coefficient) curve data is provided. hub_height : float Hub height of the wind turbine in m. rotor_diameter : None or float Diameter of the rotor in m. Default: None. power_coefficient_curve : None, pandas.DataFrame or dictionary Power coefficient curve of the wind turbine. DataFrame/dictionary must have 'wind_speed' and 'value' columns/keys with wind speeds in m/s and the corresponding power coefficients. Default: None. power_curve : None, pandas.DataFrame or dictionary Power curve of the wind turbine. DataFrame/dictionary must have 'wind_speed' and 'value' columns/keys with wind speeds in m/s and the corresponding power curve value in W. Default: None. nominal_power : None or float The nominal output of the wind turbine in W. Default: None. fetch_curve : string Parameter to specify whether a power or power coefficient curve should be retrieved from the provided turbine data. Valid options are 'power_curve' and 'power_coefficient_curve'. Default: None. coordinates : list or None List of coordinates [lat, lon] of location for loading data. Default: None. data_source : string Specifies whether turbine data (f.e. nominal power, power curve, power coefficient curve) is loaded from the OpenEnergy Database ('oedb') or from a csv file ('<path including file name>'). Default: 'oedb'. See `example_power_curves.csv' and `example_power_coefficient_curves.csv` in example/data for the required form of a csv file (more columns can be added). Attributes ---------- name : string Name of the wind turbine type. Use :py:func:`~.get_turbine_types` to see a table of all wind turbines for which power (coefficient) curve data is provided. hub_height : float Hub height of the wind turbine in m. rotor_diameter : None or float Diameter of the rotor in m. Default: None. power_coefficient_curve : None, pandas.DataFrame or dictionary Power coefficient curve of the wind turbine. DataFrame/dictionary must have 'wind_speed' and 'value' columns/keys with wind speeds in m/s and the corresponding power coefficients. Default: None. power_curve : None, pandas.DataFrame or dictionary Power curve of the wind turbine. DataFrame/dictionary must have 'wind_speed' and 'value' columns/keys with wind speeds in m/s and the corresponding power curve value in W. Default: None. nominal_power : None or float The nominal output of the wind turbine in W. Default: None. coordinates : list or None List of coordinates [lat, lon] of location for loading data. Default: None. power_output : pandas.Series The calculated power output of the wind turbine. Default: None. Notes ------ Your wind turbine object should have a power coefficient or power curve. You can set the `fetch_curve` parameter and the `data_source` parameter if you want to automatically fetch a curve from a data set provided in the OpenEnergy Database (oedb) or want to read a csv file that you provide. See `example_power_curves.csv' and `example_power_coefficient_curves.csv` in example/data for the required form of such a csv file (more columns can be added). Examples -------- >>> from windpowerlib import wind_turbine >>> enerconE126 = { ... 'hub_height': 135, ... 'rotor_diameter': 127, ... 'name': 'E-126/4200', ... 'fetch_curve': 'power_curve', ... 'data_source': 'oedb'} >>> e126 = wind_turbine.WindTurbine(**enerconE126) >>> print(e126.nominal_power) 4200000.0 """
[docs] def __init__(self, name, hub_height, rotor_diameter=None, power_coefficient_curve=None, power_curve=None, nominal_power=None, fetch_curve=None, coordinates=None, data_source='oedb', **kwargs): warnings.warn( "Parameters name, data_source and fetch_curve are deprecated. " "The parameter name will be renamed to turbine_type. " "Data source and fetching will be defined by the parameters " "power_coefficient_curve, power_curve and nominal_power in the " "future.", FutureWarning) if coordinates is not None: warnings.warn( "Parameter coordinates is deprecated. In the future the " "parameter can only be set after instantiation of WindTurbine " "object.", FutureWarning) self.name = name self.hub_height = hub_height self.rotor_diameter = rotor_diameter self.power_coefficient_curve = power_coefficient_curve self.power_curve = power_curve self.nominal_power = nominal_power self.coordinates = coordinates self.power_output = None if self.power_coefficient_curve is None and self.power_curve is None: self.fetch_turbine_data(fetch_curve, data_source)
[docs] def fetch_turbine_data(self, fetch_curve, data_source): r""" Fetches data of the requested wind turbine. Method fetches nominal power as well as power coefficient curve or power curve from a data set provided in the OpenEnergy Database (oedb). You can also import your own power (coefficient) curves from a file. For that the wind speeds in m/s have to be in the first row and the corresponding power coefficient curve values or power curve values in W in a row where the first column contains the turbine name. See `example_power_curves.csv' and `example_power_coefficient_curves.csv` in example/data for the required form of a csv file (more columns can be added). See :py:func:`~.get_turbine_data_from_file` for an example reading data from a csv file. Parameters ---------- fetch_curve : string Parameter to specify whether a power or power coefficient curve should be retrieved from the provided turbine data. Valid options are 'power_curve' and 'power_coefficient_curve'. Default: None. data_source : string Specifies whether turbine data (f.e. nominal power, power curve, power coefficient curve) is loaded from the OpenEnergy Database ('oedb') or from a csv file ('<path including file name>'). Default: 'oedb'. Returns ------- self Examples -------- >>> from windpowerlib import wind_turbine >>> enerconE126 = { ... 'hub_height': 135, ... 'rotor_diameter': 127, ... 'name': 'E-126/4200', ... 'fetch_curve': 'power_coefficient_curve', ... 'data_source': 'oedb'} >>> e126 = wind_turbine.WindTurbine(**enerconE126) >>> print(e126.power_coefficient_curve['value'][5]) 0.44 >>> print(e126.nominal_power) 4200000.0 """ if fetch_curve not in ['power_curve', 'power_coefficient_curve']: raise ValueError("'{0}' is an invalid value for ".format( fetch_curve) + "`fetch_curve`. Must be " + "'power_curve' or 'power_coefficient_curve'.") if data_source == 'oedb': curve_df, nominal_power = get_turbine_data_from_oedb( turbine_type=self.name, fetch_curve=fetch_curve) else: curve_df, nominal_power = get_turbine_data_from_file( turbine_type=self.name, file_=data_source) if fetch_curve == 'power_curve': self.power_curve = curve_df elif fetch_curve == 'power_coefficient_curve': self.power_coefficient_curve = curve_df if self.nominal_power is None: self.nominal_power = nominal_power return self
[docs]def get_turbine_data_from_file(turbine_type, file_): r""" Fetches power (coefficient) curve data from a csv file. See `example_power_curves.csv' and `example_power_coefficient_curves.csv` in example/data for the required format of a csv file. The self-provided csv file may contain more columns than the example files. Only columns containing wind speed and the corresponding power or power coefficient as well as the column 'nominal_power' are taken into account. Parameters ---------- turbine_type : str Specifies the turbine type data is fetched for. file_ : str Specifies the source of the turbine data. See the example below for how to use the example data. Returns ------- tuple(pandas.DataFrame, float) Power curve or power coefficient curve (pandas.DataFrame) and nominal power (float). Power (coefficient) curve DataFrame contains power coefficient curve values (dimensionless) or power curve values in W as column names with the corresponding wind speeds in m/s. Examples -------- >>> from windpowerlib import wind_turbine >>> import os >>> source = os.path.join(os.path.dirname(__file__), '../example/data', ... 'example_power_curves.csv') >>> example_turbine = { ... 'hub_height': 100, ... 'rotor_diameter': 70, ... 'name': 'DUMMY 3', ... 'fetch_curve': 'power_curve', ... 'data_source': source} >>> e_t_1 = wind_turbine.WindTurbine(**example_turbine) >>> print(e_t_1.power_curve['value'][7]) 18000.0 >>> print(e_t_1.nominal_power) 150000 """ def isfloat(x): try: float(x) return x except ValueError: return False try: df = pd.read_csv(file_, index_col=0) except FileNotFoundError: raise FileNotFoundError("The file '{}' was not found.".format(file_)) # note: this try except statement will be removed in 0.2.0 and only # the exception will stay. The example power (coefficient) curve files # will then be adapted try: wpp_df = df[df['turbine_id'] == turbine_type] except KeyError: wpp_df = df[df.index == turbine_type] # if turbine not in data file if wpp_df.shape[0] == 0: pd.set_option('display.max_rows', len(df)) logging.info('Possible types: \n{0}'.format(df['turbine_id'])) pd.reset_option('display.max_rows') sys.exit('Cannot find the wind converter type: {0}'.format( turbine_type)) # if turbine in data file select power (coefficient) curve columns and # drop nans cols = [_ for _ in wpp_df.columns if isfloat(_)] curve_data = wpp_df[cols].dropna(axis=1) df = curve_data.transpose().reset_index() df.columns = ['wind_speed', 'value'] df['wind_speed'] = df['wind_speed'].apply(lambda x: float(x)) # note: this try except statement will be removed in 0.2.0 and only # the exception will stay. The example power (coefficient) curve files # will then be adapted try: nominal_power = wpp_df['p_nom'].iloc[0] except KeyError: nominal_power = float(wpp_df['nominal_power'].iloc[0]) return df, nominal_power
[docs]def get_turbine_data_from_oedb(turbine_type, fetch_curve, overwrite=False): r""" Fetches wind turbine data from the OpenEnergy database (oedb). If turbine data exists in local repository it is loaded from this file. The file is created when turbine data is loaded from oedb in :py:func:`~.load_turbine_data_from_oedb`. Use this function with `overwrite=True` to overwrite your file with newly fetched data. Parameters ---------- turbine_type : str Specifies the turbine type data is fetched for. Use :py:func:`~.get_turbine_types` to see a table of all wind turbines in oedb containing information about whether power (coefficient) curve data is provided. fetch_curve : str Parameter to specify whether a power or power coefficient curve should be retrieved from the provided turbine data. Valid options are 'power_curve' and 'power_coefficient_curve'. Default: None. overwrite : bool If True local file is overwritten by newly fetched data from oedb, if False turbine data is fetched from previously saved file. Returns ------- tuple(pandas.DataFrame, float) Power curve or power coefficient curve (pandas.DataFrame) and nominal power (float) of one wind turbine type. Power (coefficient) curve DataFrame contains power coefficient curve values (dimensionless) or power curve values in W with the corresponding wind speeds in m/s. """ filename = os.path.join(os.path.dirname(__file__), 'data', 'oedb_{}s.csv'.format(fetch_curve)) if not os.path.isfile(filename) or overwrite: # Load data from oedb and save to csv file load_turbine_data_from_oedb() else: logging.debug("Turbine data is fetched from {}".format(filename)) df, nominal_power = get_turbine_data_from_file(turbine_type=turbine_type, file_=filename) # nominal power and power curve values in W nominal_power = nominal_power * 1000 if fetch_curve == 'power_curve': # power in W df['value'] = df['value'] * 1000 return df, nominal_power
[docs]def load_turbine_data_from_oedb(): r""" Loads turbine data from the OpenEnergy database (oedb). Turbine data is saved to csv files ('oedb_power_curves.csv' and 'oedb_power_coefficient_curves.csv') for offline usage of windpowerlib. If the files already exist they are overwritten. Returns ------- pd.DataFrame Contains turbine data of different turbines such as 'manufacturer', 'turbine_type', 'nominal_power'. """ # url of OpenEnergy Platform that contains the oedb oep_url = 'http://oep.iks.cs.ovgu.de/' # location of data schema = 'supply' table = 'wind_turbine_library' # load data result = requests.get( oep_url + '/api/v0/schema/{}/tables/{}/rows/?'.format( schema, table), ) if not result.status_code == 200: raise ConnectionError("Database connection not successful. " "Response: [{}]".format(result.status_code)) # extract data to dataframe turbine_data = pd.DataFrame(result.json()) # standard file name for saving data filename = os.path.join(os.path.dirname(__file__), 'data', 'oedb_{}.csv') # get all power (coefficient) curves and save to file # for curve_type in ['power_curve', 'power_coefficient_curve']: for curve_type in ['power_curve', 'power_coefficient_curve']: curves_df = pd.DataFrame(columns=['wind_speed']) for index in turbine_data.index: if (turbine_data['{}_wind_speeds'.format(curve_type)][index] and turbine_data['{}_values'.format(curve_type)][index]): df = pd.DataFrame(data=[ eval(turbine_data['{}_wind_speeds'.format(curve_type)][ index]), eval(turbine_data['{}_values'.format(curve_type)][ index])]).transpose().rename( columns={0: 'wind_speed', 1: turbine_data['turbine_type'][index]}) curves_df = pd.merge(left=curves_df, right=df, how='outer', on='wind_speed') curves_df = curves_df.set_index('wind_speed').sort_index().transpose() curves_df['turbine_type'] = curves_df.index # add nominal power to power (coefficient) data frame curves_df = pd.merge( left=curves_df, right=turbine_data[['turbine_type', 'nominal_power']], on='turbine_type').set_index('turbine_type') curves_df.to_csv(filename.format('{}s'.format(curve_type))) return turbine_data
[docs]def get_turbine_types(print_out=True, filter_=True): r""" Get all wind turbine types provided in the OpenEnergy database (oedb). By default only turbine types for which a power coefficient curve or power curve is provided are returned. Set `filter_=False` to see all turbine types for which any data (f.e. hub height, rotor diameter, ...) is provided. Parameters ---------- print_out : bool Directly prints a tabular containing the turbine types in column 'turbine_type', the manufacturer in column 'manufacturer' and information about whether a power (coefficient) curve exists (True) or not (False) in columns 'has_power_curve' and 'has_cp_curve'. Default: True. filter_ : bool If True only turbine types for which a power coefficient curve or power curve is provided in the OpenEnergy database (oedb) are returned. Default: True. Returns ------- pd.DataFrame Contains turbine types in column 'turbine_type', the manufacturer in column 'manufacturer' and information about whether a power (coefficient) curve exists (True) or not (False) in columns 'has_power_curve' and 'has_cp_curve'. Notes ----- If the power (coefficient) curve of the desired turbine type (or the turbine type itself) is missing you can contact us via github or windpowerlib@rl-institut.de. You can help us by providing data in the format as shown in `the data base <https://openenergy-platform.org/dataedit/view/model_draft/openfred_windpower_powercurve>`_. Examples -------- >>> from windpowerlib import wind_turbine >>> df = wind_turbine.get_turbine_types(print_out=False) >>> print(df[df["turbine_type"].str.contains("E-126")].iloc[0]) manufacturer Enercon turbine_type E-126/4200 has_power_curve True has_cp_curve True Name: 5, dtype: object >>> print(df[df["manufacturer"].str.contains("Enercon")].iloc[0]) manufacturer Enercon turbine_type E-101/3050 has_power_curve True has_cp_curve True Name: 1, dtype: object """ df = load_turbine_data_from_oedb() if filter_: cp_curves_df = df.loc[df['has_cp_curve']][ ['manufacturer', 'turbine_type', 'has_cp_curve']] p_curves_df = df.loc[df['has_power_curve']][ ['manufacturer', 'turbine_type', 'has_power_curve']] curves_df = pd.merge(p_curves_df, cp_curves_df, how='outer', sort=True).fillna(False) else: curves_df = df[['manufacturer', 'turbine_type', 'has_power_curve', 'has_cp_curve']] if print_out: pd.set_option('display.max_rows', len(curves_df)) print(curves_df) pd.reset_option('display.max_rows') return curves_df