Source code for windpowerlib.power_curves

"""
The ``power_curves`` module contains functions for applying alterations like
power curve smoothing or reducing power values by an efficiency to the power
curve of a wind turbine, wind farm or wind turbine cluster.

"""

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

import numpy as np
import pandas as pd
from windpowerlib import tools


[docs]def smooth_power_curve(power_curve_wind_speeds, power_curve_values, block_width=0.5, wind_speed_range=15.0, standard_deviation_method='turbulence_intensity', mean_gauss=0, **kwargs): r""" Smooths the input power curve values by using a Gauss distribution. The smoothing serves for taking the distribution of wind speeds over space into account. Parameters ---------- power_curve_wind_speeds : pandas.Series or numpy.array Wind speeds in m/s for which the power curve values are provided in `power_curve_values`. power_curve_values : pandas.Series or numpy.array Power curve values corresponding to wind speeds in `power_curve_wind_speeds`. block_width : float Width between the wind speeds in the sum of equation :eq:`power`. Default: 0.5. wind_speed_range : float The sum in the equation below is taken for this wind speed range below and above the power curve wind speed. Default: 15.0. standard_deviation_method : string Method for calculating the standard deviation for the Gauss distribution. Options: 'turbulence_intensity', 'Staffell_Pfenninger'. Default: 'turbulence_intensity'. mean_gauss : float Mean of the Gauss distribution in :py:func:`~.tools.gauss_distribution`. Default: 0. Other Parameters ---------------- turbulence intensity : float, optional Turbulence intensity at hub height of the wind turbine, wind farm or wind turbine cluster the power curve is smoothed for. Returns ------- smoothed_power_curve_df : pd.DataFrame Smoothed power curve. DataFrame has 'wind_speed' and 'value' columns with wind speeds in m/s and the corresponding power curve value in W. Notes ----- The following equation is used to calculated the power curves values of the smoothed power curve [1]_: .. math:: P_{smoothed}(v_{std}) = \sum\limits_{v_i} \Delta v_i \cdot P(v_i) \cdot \frac{1}{\sigma \sqrt{2 \pi}} \exp \left[-\frac{(v_{std} - v_i -\mu)^2}{2 \sigma^2} \right] :label: power with: P: power [W], v: wind speed [m/s], :math:`\sigma`: standard deviation (Gauss), :math:`\mu`: mean (Gauss) :math:`P_{smoothed}` is the smoothed power curve value, :math:`v_{std}` is the standard wind speed in the power curve, :math:`\Delta v_i` is the interval length between :math:`v_\text{i}` and :math:`v_\text{i+1}` Power curve smoothing is applied to take account for the spatial distribution of wind speed. This way of smoothing power curves is also used in [2]_ and [3]_. The standard deviation :math:`\sigma` of the above equation can be calculated by the following methods. 'turbulence_intensity' [2]_: .. math:: \sigma = v_\text{std} \cdot \sigma_\text{n} = v_\text{std} \cdot TI with: TI: turbulence intensity 'Staffell_Pfenninger' [4]_: .. math:: \sigma = 0.6 \cdot 0.2 \cdot v_\text{std} References ---------- .. [1] Knorr, K.: "Modellierung von raum-zeitlichen Eigenschaften der Windenergieeinspeisung für wetterdatenbasierte Windleistungssimulationen". Universität Kassel, Diss., 2016, p. 106 .. [2] Nørgaard, P. and Holttinen, H.: "A Multi-Turbine and Power Curve Approach". Nordic Wind Power Conference, 1.–2.3.2004, 2000, p. 5 .. [3] Kohler, S. and Agricola, A.-Cl. and Seidl, H.: "dena-Netzstudie II. Integration erneuerbarer Energien in die deutsche Stromversorgung im Zeitraum 2015 – 2020 mit Ausblick 2025". Technical report, 2010. .. [4] Staffell, I. and Pfenninger, S.: "Using Bias-Corrected Reanalysis to Simulate Current and Future Wind Power Output". 2005, p. 11 """ # Specify normalized standard deviation if standard_deviation_method == 'turbulence_intensity': if ('turbulence_intensity' in kwargs and kwargs['turbulence_intensity'] is not np.nan): normalized_standard_deviation = kwargs['turbulence_intensity'] else: raise ValueError("Turbulence intensity must be defined for " + "using 'turbulence_intensity' as " + "`standard_deviation_method`") elif standard_deviation_method == 'Staffell_Pfenninger': normalized_standard_deviation = 0.2 else: raise ValueError("{} is no valid `standard_deviation_method`. Valid " + "options are 'turbulence_intensity', or " + "'Staffell_Pfenninger'".format( standard_deviation_method)) # Initialize list for power curve values smoothed_power_curve_values = [] # Append wind speeds to `power_curve_wind_speeds` maximum_value = power_curve_wind_speeds.values[-1] + wind_speed_range while power_curve_wind_speeds.values[-1] < maximum_value: power_curve_wind_speeds = power_curve_wind_speeds.append( pd.Series(power_curve_wind_speeds.iloc[-1] + 0.5, index=[power_curve_wind_speeds.index[-1] + 1])) power_curve_values = power_curve_values.append( pd.Series(0.0, index=[power_curve_values.index[-1] + 1])) for power_curve_wind_speed in power_curve_wind_speeds: # Create array of wind speeds for the sum wind_speeds_block = (np.arange( -wind_speed_range, wind_speed_range + block_width, block_width) + power_curve_wind_speed) # Get standard deviation for Gauss function standard_deviation = ( (power_curve_wind_speed * normalized_standard_deviation + 0.6) if standard_deviation_method is 'Staffell_Pfenninger' else power_curve_wind_speed * normalized_standard_deviation) # Get the smoothed value of the power output if standard_deviation == 0.0: # The gaussian distribution is not defined for a standard deviation # of zero. Smoothed power curve value is set to zero. smoothed_value = 0.0 else: smoothed_value = sum( block_width * np.interp(wind_speed, power_curve_wind_speeds, power_curve_values, left=0, right=0) * tools.gauss_distribution( power_curve_wind_speed - wind_speed, standard_deviation, mean_gauss) for wind_speed in wind_speeds_block) # Add value to list - add zero if `smoothed_value` is nan as Gauss # distribution for a standard deviation of zero. smoothed_power_curve_values.append(smoothed_value) # Create smoothed power curve data frame smoothed_power_curve_df = pd.DataFrame( data=[list(power_curve_wind_speeds.values), smoothed_power_curve_values]).transpose() # Rename columns of the data frame smoothed_power_curve_df.columns = ['wind_speed', 'value'] return smoothed_power_curve_df
[docs]def wake_losses_to_power_curve(power_curve_wind_speeds, power_curve_values, wind_farm_efficiency, wake_losses_model='power_efficiency_curve'): r""" Reduces the power values of a power curve by an efficiency (curve). Parameters ---------- power_curve_wind_speeds : pandas.Series or numpy.array Wind speeds in m/s for which the power curve values are provided in `power_curve_values`. power_curve_values : pandas.Series or numpy.array Power curve values corresponding to wind speeds in `power_curve_wind_speeds`. wind_farm_efficiency : float or pd.DataFrame Efficiency of the wind farm. Either constant (float) or efficiency curve (pd.DataFrame) containing 'wind_speed' and 'efficiency' columns with wind speeds in m/s and the corresponding dimensionless wind farm efficiency (reduction of power). Default: None. wake_losses_model : String Defines the method for taking wake losses within the farm into consideration. Options: 'power_efficiency_curve', 'constant_efficiency'. Default: 'power_efficiency_curve'. Returns ------- power_curve_df : pd.DataFrame Power curve with power values reduced by a wind farm efficiency. DataFrame has 'wind_speed' and 'value' columns with wind speeds in m/s and the corresponding power curve value in W. """ # Create power curve DataFrame power_curve_df = pd.DataFrame( data=[list(power_curve_wind_speeds), list(power_curve_values)]).transpose() # Rename columns of DataFrame power_curve_df.columns = ['wind_speed', 'value'] if wake_losses_model == 'constant_efficiency': if not isinstance(wind_farm_efficiency, float): raise TypeError("'wind_farm_efficiency' must be float if " + "`wake_losses_model´ is '{}' but is {}".format( wake_losses_model, wind_farm_efficiency)) power_curve_df['value'] = power_curve_values * wind_farm_efficiency elif wake_losses_model == 'power_efficiency_curve': if (not isinstance(wind_farm_efficiency, dict) and not isinstance(wind_farm_efficiency, pd.DataFrame)): raise TypeError( "'wind_farm_efficiency' must be pd.DataFrame if " + "`wake_losses_model´ is '{}' but is {}".format( wake_losses_model, wind_farm_efficiency)) df = pd.concat([power_curve_df.set_index('wind_speed'), wind_farm_efficiency.set_index('wind_speed')], axis=1) # Add column with reduced power (nan values of efficiency are # interpolated) df['reduced_power'] = df['value'] * df['efficiency'].interpolate( method='index') reduced_power = df['reduced_power'].dropna() power_curve_df = pd.DataFrame([reduced_power.index, reduced_power.values]).transpose() power_curve_df.columns = ['wind_speed', 'value'] else: raise ValueError( "`wake_losses_model` is {} but should be ".format( wake_losses_model) + "'constant_efficiency' or 'power_efficiency_curve'") return power_curve_df