|
| 1 | +""" |
| 2 | +The ``power_curves`` module contains functions apply calculations to the power |
| 3 | +curve of a wind turbine, wind farm or wind turbine cluster. |
| 4 | +
|
| 5 | +""" |
| 6 | + |
| 7 | +__copyright__ = "Copyright oemof developer group" |
| 8 | +__license__ = "GPLv3" |
| 9 | + |
| 10 | +import numpy as np |
| 11 | +import pandas as pd |
| 12 | +from windpowerlib import tools |
| 13 | +from matplotlib import pyplot as plt |
| 14 | +import os |
| 15 | + |
| 16 | + |
| 17 | +def smooth_power_curve(power_curve_wind_speeds, power_curve_values, |
| 18 | + block_width=0.5, |
| 19 | + standard_deviation_method='turbulence_intensity', |
| 20 | + mean_gauss=0, **kwargs): |
| 21 | + # TODO: All functions in this module have to work without pandas |
| 22 | + r""" |
| 23 | + Smoothes the input power curve values by using a gaussian distribution. |
| 24 | +
|
| 25 | + Parameters |
| 26 | + ---------- |
| 27 | + power_curve_wind_speeds : pandas.Series |
| 28 | + Wind speeds in m/s for which the power curve values are provided in |
| 29 | + `power_curve_values`. |
| 30 | + power_curve_values : pandas.Series or numpy.array |
| 31 | + Power curve values corresponding to wind speeds in |
| 32 | + `power_curve_wind_speeds`. |
| 33 | + block_width : Float |
| 34 | + Width of the moving block. Default: 0.5. |
| 35 | + standard_deviation_method : String |
| 36 | + Method for calculating the standard deviation for the gaussian |
| 37 | + distribution. Options: 'turbulence_intensity', 'Norgaard', 'Staffell'. |
| 38 | + Default: 'turbulence_intensity'. |
| 39 | +
|
| 40 | + Other Parameters |
| 41 | + ---------------- |
| 42 | + turbulence intensity : Float, optional |
| 43 | + Turbulence intensity at hub height of the wind turbine the power curve |
| 44 | + is smoothed for. |
| 45 | +
|
| 46 | + Returns |
| 47 | + ------- |
| 48 | + smoothed_power_curve_df : pd.DataFrame |
| 49 | + Smoothed power curve. DataFrame has 'wind_speed' and |
| 50 | + 'power' columns with wind speeds in m/s and the corresponding power |
| 51 | + curve value in W. |
| 52 | +
|
| 53 | + Notes |
| 54 | + ----- |
| 55 | + The following equation is used [1]_: |
| 56 | + # TODO: add equation |
| 57 | +
|
| 58 | + References |
| 59 | + ---------- |
| 60 | + .. [1] Knorr, K.: "Modellierung von raum-zeitlichen Eigenschaften der |
| 61 | + Windenergieeinspeisung für wetterdatenbasierte |
| 62 | + Windleistungssimulationen". Universität Kassel, Diss., 2016, |
| 63 | + p. 106 |
| 64 | +
|
| 65 | + # TODO: add references |
| 66 | + """ |
| 67 | + # Specify normalized standard deviation |
| 68 | + if standard_deviation_method == 'turbulence_intensity': |
| 69 | + if 'turbulence_intensity' in kwargs: |
| 70 | + normalized_standard_deviation = kwargs['turbulence_intensity'] |
| 71 | + else: |
| 72 | + raise ValueError("Turbulence intensity must be defined for " + |
| 73 | + "using 'turbulence_intensity' as " + |
| 74 | + "`standard_deviation_method`") |
| 75 | + elif standard_deviation_method == 'Norgaard': |
| 76 | + pass # TODO add |
| 77 | + elif standard_deviation_method == 'Staffell': |
| 78 | + normalized_standard_deviation = 0.2 |
| 79 | + # Initialize list for power curve values |
| 80 | + smoothed_power_curve_values = [] |
| 81 | + # Step of power curve wind speeds |
| 82 | + step = power_curve_wind_speeds.iloc[-5] - power_curve_wind_speeds.iloc[-6] |
| 83 | + # Append wind speeds to `power_curve_wind_speeds` until 40 m/s |
| 84 | + while (power_curve_wind_speeds.values[-1] < 40.0): |
| 85 | + power_curve_wind_speeds = power_curve_wind_speeds.append( |
| 86 | + pd.Series(power_curve_wind_speeds.iloc[-1] + step, |
| 87 | + index=[power_curve_wind_speeds.index[-1] + 1])) |
| 88 | + power_curve_values = power_curve_values.append( |
| 89 | + pd.Series(0.0, index=[power_curve_values.index[-1] + 1])) |
| 90 | + for power_curve_wind_speed in power_curve_wind_speeds: |
| 91 | + # Create array of wind speeds for the moving block |
| 92 | + wind_speeds_block = ( |
| 93 | + np.arange(-15.0, 15.0 + block_width, block_width) + |
| 94 | + power_curve_wind_speed) |
| 95 | + # Get standard deviation for gaussian filter |
| 96 | + standard_deviation = ( |
| 97 | + (power_curve_wind_speed * normalized_standard_deviation + 0.6) |
| 98 | + if standard_deviation_method is 'Staffell' |
| 99 | + else power_curve_wind_speed * normalized_standard_deviation) |
| 100 | + # Get the smoothed value of the power output |
| 101 | + smoothed_value = sum( |
| 102 | + block_width * np.interp(wind_speed, power_curve_wind_speeds, |
| 103 | + power_curve_values, left=0, right=0) * |
| 104 | + tools.gaussian_distribution( |
| 105 | + power_curve_wind_speed - wind_speed, |
| 106 | + standard_deviation, mean_gauss) |
| 107 | + for wind_speed in wind_speeds_block) |
| 108 | + # Add value to list - add 0 if `smoothed_value` is nan. This occurs |
| 109 | + # because the gaussian distribution is not defined for 0. |
| 110 | + smoothed_power_curve_values.append(0 if np.isnan(smoothed_value) |
| 111 | + else smoothed_value) |
| 112 | + # Create smoothed power curve DataFrame |
| 113 | + smoothed_power_curve_df = pd.DataFrame( |
| 114 | + data=[list(power_curve_wind_speeds.values), |
| 115 | + smoothed_power_curve_values]).transpose() |
| 116 | + # Rename columns of DataFrame |
| 117 | + smoothed_power_curve_df.columns = ['wind_speed', 'power'] |
| 118 | +# # Plot power curves |
| 119 | +# fig = plt.figure() |
| 120 | +# plt.plot(power_curve_wind_speeds.values, power_curve_values.values) |
| 121 | +# plt.plot(power_curve_wind_speeds.values, smoothed_power_curve_values) |
| 122 | +# fig.savefig(os.path.abspath(os.path.join( |
| 123 | +# os.path.dirname(__file__), '../Plots/power_curves', |
| 124 | +# '{0}_{1}_{2}.png'.format(kwargs['object_name'], |
| 125 | +# standard_deviation_method, block_width)))) |
| 126 | +# plt.close() # TODO: delete plot later |
| 127 | + return smoothed_power_curve_df |
| 128 | + |
| 129 | + |
| 130 | +def wake_losses_to_power_curve(power_curve_wind_speeds, power_curve_values, |
| 131 | + wake_losses_method='constant_efficiency', |
| 132 | + wind_farm_efficiency=None): |
| 133 | + r""" |
| 134 | + Applies wake losses depending on the method to a power curve. |
| 135 | +
|
| 136 | + Parameters |
| 137 | + ---------- |
| 138 | + power_curve_wind_speeds : pandas.Series |
| 139 | + Wind speeds in m/s for which the power curve values are provided in |
| 140 | + `power_curve_values`. |
| 141 | + power_curve_values : pandas.Series or numpy.array |
| 142 | + Power curve values corresponding to wind speeds in |
| 143 | + `power_curve_wind_speeds`. |
| 144 | + wake_losses_method : String |
| 145 | + Defines the method for talking wake losses within the farm into |
| 146 | + consideration. Default: 'constant_efficiency'. |
| 147 | + wind_farm_efficiency : Float or pd.DataFrame or Dictionary |
| 148 | + Efficiency of the wind farm. Either constant (float) or wind efficiency |
| 149 | + curve (pd.DataFrame or Dictionary) contianing 'wind_speed' and |
| 150 | + 'efficiency' columns/keys with wind speeds in m/s and the |
| 151 | + corresponding dimensionless wind farm efficiency. Default: None. |
| 152 | +
|
| 153 | + Returns |
| 154 | + ------- |
| 155 | + power_curve_df : pd.DataFrame |
| 156 | + With wind farm efficiency reduced power curve. DataFrame power curve |
| 157 | + values in W with the corresponding wind speeds in m/s. |
| 158 | +
|
| 159 | + Notes |
| 160 | + ----- |
| 161 | + TODO add |
| 162 | +
|
| 163 | + """ |
| 164 | + # Create power curve DataFrame |
| 165 | + power_curve_df = pd.DataFrame( |
| 166 | + data=[list(power_curve_wind_speeds), |
| 167 | + list(power_curve_values)]).transpose() |
| 168 | + # Rename columns of DataFrame |
| 169 | + power_curve_df.columns = ['wind_speed', 'power'] |
| 170 | + if wake_losses_method == 'constant_efficiency': |
| 171 | + if not isinstance(wind_farm_efficiency, float): |
| 172 | + raise TypeError("'wind_farm_efficiency' must be float if " + |
| 173 | + "`wake_losses_method´ is '{0}'") |
| 174 | + power_curve_df['power'] = power_curve_values * wind_farm_efficiency |
| 175 | + elif wake_losses_method == 'wind_efficiency_curve': |
| 176 | + if (not isinstance(wind_farm_efficiency, dict) and |
| 177 | + not isinstance(wind_farm_efficiency, pd.DataFrame)): |
| 178 | + raise TypeError( |
| 179 | + "'wind_farm_efficiency' must be a dictionary or " + |
| 180 | + "pd.DataFrame if `wake_losses_method´ is '{0}'") |
| 181 | + df = pd.concat([power_curve_df.set_index('wind_speed'), |
| 182 | + wind_farm_efficiency.set_index('wind_speed')], axis=1) |
| 183 | + # Add by efficiency reduced power column (nan values of efficiency |
| 184 | + # are interpolated) |
| 185 | + df['reduced_power'] = df['power'] * df['efficiency'].interpolate( |
| 186 | + method='index') |
| 187 | + reduced_power = df['reduced_power'].dropna() |
| 188 | + power_curve_df = pd.DataFrame([reduced_power.index, |
| 189 | + reduced_power.values]).transpose() |
| 190 | + power_curve_df.columns = ['wind_speed', 'power'] |
| 191 | + else: |
| 192 | + raise ValueError( |
| 193 | + "`wake_losses_method` is {0} but should be None, ".format( |
| 194 | + wake_losses_method) + |
| 195 | + "'constant_efficiency' or 'wind_efficiency_curve'") |
| 196 | + return power_curve_df |
0 commit comments