From ae662830cc6072dc760e4c4fdb7ed35ffeb0d577 Mon Sep 17 00:00:00 2001 From: SabineH Date: Thu, 5 Dec 2019 14:46:12 +0100 Subject: [PATCH 1/7] Replace logging.info with error msg, issue #69 --- windpowerlib/wind_farm.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/windpowerlib/wind_farm.py b/windpowerlib/wind_farm.py index 2030ce48..9229de2e 100644 --- a/windpowerlib/wind_farm.py +++ b/windpowerlib/wind_farm.py @@ -402,8 +402,10 @@ def assign_power_curve(self, wake_losses_model='wind_farm_efficiency', wind_farm_power_curve['value'].values, wind_farm_efficiency=self.efficiency)) else: - logging.info("`wake_losses_model` is {} but wind farm ".format( - wake_losses_model) + "efficiency is NOT taken into " - "account as it is None.") + msg = ( + "If you use `wake_losses_model` '{model}' your WindFarm " + "needs an efficiency but `efficiency` of {farm} is {eff}.") + raise ValueError(msg.format(model=wake_losses_model, farm=self, + eff=self.efficiency)) self.power_curve = wind_farm_power_curve return self From 2862655910b8fa277a846831ca0c21fc69d2f81a Mon Sep 17 00:00:00 2001 From: SabineH Date: Thu, 5 Dec 2019 14:46:23 +0100 Subject: [PATCH 2/7] Test error msg --- tests/test_wind_farm.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_wind_farm.py b/tests/test_wind_farm.py index 6e5c8aed..11b229d7 100644 --- a/tests/test_wind_farm.py +++ b/tests/test_wind_farm.py @@ -163,3 +163,14 @@ def test_aggregation_of_power_curve_with_missing_power_curve(self): msg = 'For an aggregated wind farm power curve each wind' with pytest.raises(ValueError, match=msg): windfarm.assign_power_curve() + + def test_wind_farm_efficiency_with_missing_efficiency(self): + """Test WindFarm.assign_power_curve() with missing efficiency while + `wake_losses_model` is 'wind_farm_efficiency'.""" + wind_turbine_fleet = [ + {'wind_turbine': WindTurbine(**self.test_turbine), + 'number_of_turbines': 3}] + windfarm = WindFarm(wind_turbine_fleet=wind_turbine_fleet) + msg = 'If you use `wake_losses_model`' + with pytest.raises(ValueError, match=msg): + windfarm.assign_power_curve() \ No newline at end of file From c7d8834aa772264fba7e5b9ba4af9bc554487d66 Mon Sep 17 00:00:00 2001 From: SabineH Date: Thu, 5 Dec 2019 14:53:17 +0100 Subject: [PATCH 3/7] Add information to wake_losses_model parameter description --- windpowerlib/wind_farm.py | 5 +++-- windpowerlib/wind_turbine_cluster.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/windpowerlib/wind_farm.py b/windpowerlib/wind_farm.py index 9229de2e..ee692cff 100644 --- a/windpowerlib/wind_farm.py +++ b/windpowerlib/wind_farm.py @@ -293,8 +293,9 @@ def assign_power_curve(self, wake_losses_model='wind_farm_efficiency', ---------- wake_losses_model : str Defines the method for taking wake losses within the farm into - consideration. Options: 'wind_farm_efficiency' or None. - Default: 'wind_farm_efficiency'. + consideration. Options: 'wind_farm_efficiency' or None. If + 'wind_farm_efficiency' is chosen the `efficiency` of the WindFarm + must be set. Default: 'wind_farm_efficiency'. smoothing : bool If True the power curves will be smoothed before or after the aggregation of power curves depending on `smoothing_order`. diff --git a/windpowerlib/wind_turbine_cluster.py b/windpowerlib/wind_turbine_cluster.py index b67b8739..b37b178c 100644 --- a/windpowerlib/wind_turbine_cluster.py +++ b/windpowerlib/wind_turbine_cluster.py @@ -137,8 +137,10 @@ def assign_power_curve(self, wake_losses_model='wind_farm_efficiency', Parameters ---------- wake_losses_model : str - Defines the method for taking wake losses within the farm into - consideration. Options: 'wind_farm_efficiency' or None. + Defines the method for taking wake losses within the wind farms of + the cluster into consideration. Options: 'wind_farm_efficiency' + or None. If 'wind_farm_efficiency' is chosen the `efficiency` + attribute of the WindFarms must be set. Default: 'wind_farm_efficiency'. smoothing : bool If True the power curves will be smoothed before or after the From a68090e1525156598c2e3c9cc26c163c62fe1101 Mon Sep 17 00:00:00 2001 From: SabineH Date: Tue, 17 Dec 2019 11:52:27 +0100 Subject: [PATCH 4/7] better visibility of error message --- windpowerlib/wind_farm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windpowerlib/wind_farm.py b/windpowerlib/wind_farm.py index ee692cff..d7260484 100644 --- a/windpowerlib/wind_farm.py +++ b/windpowerlib/wind_farm.py @@ -405,7 +405,8 @@ def assign_power_curve(self, wake_losses_model='wind_farm_efficiency', else: msg = ( "If you use `wake_losses_model` '{model}' your WindFarm " - "needs an efficiency but `efficiency` of {farm} is {eff}.") + "needs an efficiency but `efficiency` is {eff}. \n\n" + "Failing farm:\n {farm}") raise ValueError(msg.format(model=wake_losses_model, farm=self, eff=self.efficiency)) self.power_curve = wind_farm_power_curve From 28b541d8d4a91390a8dd78381570bc60abc4f231 Mon Sep 17 00:00:00 2001 From: uvchik Date: Wed, 8 Jan 2020 11:19:54 +0100 Subject: [PATCH 5/7] Fix style --- windpowerlib/wind_farm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windpowerlib/wind_farm.py b/windpowerlib/wind_farm.py index d7260484..fa08f79f 100644 --- a/windpowerlib/wind_farm.py +++ b/windpowerlib/wind_farm.py @@ -406,7 +406,8 @@ def assign_power_curve(self, wake_losses_model='wind_farm_efficiency', msg = ( "If you use `wake_losses_model` '{model}' your WindFarm " "needs an efficiency but `efficiency` is {eff}. \n\n" - "Failing farm:\n {farm}") + "Failing farm:\n {farm}" + ) raise ValueError(msg.format(model=wake_losses_model, farm=self, eff=self.efficiency)) self.power_curve = wind_farm_power_curve From d8b82df88206ef6e0f8e6f3a9bc856742bd2a897 Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Wed, 8 Jan 2020 10:27:50 +0000 Subject: [PATCH 6/7] Fixing style errors. --- tests/test_wind_farm.py | 11 +- windpowerlib/wind_farm.py | 269 ++++++++++++++++++++++++-------------- 2 files changed, 177 insertions(+), 103 deletions(-) diff --git a/tests/test_wind_farm.py b/tests/test_wind_farm.py index 60e61dd0..649c2086 100644 --- a/tests/test_wind_farm.py +++ b/tests/test_wind_farm.py @@ -224,9 +224,12 @@ def test_wind_farm_efficiency_with_missing_efficiency(self): """Test WindFarm.assign_power_curve() with missing efficiency while `wake_losses_model` is 'wind_farm_efficiency'.""" wind_turbine_fleet = [ - {'wind_turbine': WindTurbine(**self.test_turbine), - 'number_of_turbines': 3}] + { + "wind_turbine": WindTurbine(**self.test_turbine), + "number_of_turbines": 3, + } + ] windfarm = WindFarm(wind_turbine_fleet=wind_turbine_fleet) - msg = 'If you use `wake_losses_model`' + msg = "If you use `wake_losses_model`" with pytest.raises(ValueError, match=msg): - windfarm.assign_power_curve() \ No newline at end of file + windfarm.assign_power_curve() diff --git a/windpowerlib/wind_farm.py b/windpowerlib/wind_farm.py index 37890f11..085ec812 100644 --- a/windpowerlib/wind_farm.py +++ b/windpowerlib/wind_farm.py @@ -113,7 +113,7 @@ class WindFarm(object): 31200000.0 """ - def __init__(self, wind_turbine_fleet, efficiency=None, name='', **kwargs): + def __init__(self, wind_turbine_fleet, efficiency=None, name="", **kwargs): self.wind_turbine_fleet = wind_turbine_fleet self.efficiency = efficiency @@ -141,76 +141,100 @@ def check_and_complete_wind_turbine_fleet(self): # check wind turbines try: - for turbine in self.wind_turbine_fleet['wind_turbine']: + for turbine in self.wind_turbine_fleet["wind_turbine"]: if not isinstance(turbine, WindTurbine): raise ValueError( - 'Wind turbine must be provided as WindTurbine object ' - 'but was provided as {}.'.format(type(turbine))) + "Wind turbine must be provided as WindTurbine object " + "but was provided as {}.".format(type(turbine)) + ) except KeyError: - raise KeyError('Missing wind_turbine key/column in ' - 'wind_turbine_fleet parameter.') + raise KeyError( + "Missing wind_turbine key/column in " + "wind_turbine_fleet parameter." + ) # add columns for number of turbines and total capacity if they don't # yet exist - if 'number_of_turbines' not in self.wind_turbine_fleet.columns: - self.wind_turbine_fleet['number_of_turbines'] = np.nan - if 'total_capacity' not in self.wind_turbine_fleet.columns: - self.wind_turbine_fleet['total_capacity'] = np.nan + if "number_of_turbines" not in self.wind_turbine_fleet.columns: + self.wind_turbine_fleet["number_of_turbines"] = np.nan + if "total_capacity" not in self.wind_turbine_fleet.columns: + self.wind_turbine_fleet["total_capacity"] = np.nan # calculate number of turbines if necessary number_turbines_not_provided = self.wind_turbine_fleet[ - self.wind_turbine_fleet['number_of_turbines'].isnull()] + self.wind_turbine_fleet["number_of_turbines"].isnull() + ] for ix, row in number_turbines_not_provided.iterrows(): - msg = 'Number of turbines of type {0} can not be deduced ' \ - 'from total capacity. Please either provide ' \ - '`number_of_turbines` in the turbine fleet definition or ' \ - 'set the nominal power of the wind turbine.' + msg = ( + "Number of turbines of type {0} can not be deduced " + "from total capacity. Please either provide " + "`number_of_turbines` in the turbine fleet definition or " + "set the nominal power of the wind turbine." + ) try: - number_of_turbines = row['total_capacity'] / \ - row['wind_turbine'].nominal_power + number_of_turbines = ( + row["total_capacity"] / row["wind_turbine"].nominal_power + ) if np.isnan(number_of_turbines): - raise ValueError(msg.format(row['wind_turbine'])) + raise ValueError(msg.format(row["wind_turbine"])) else: - self.wind_turbine_fleet.loc[ix, 'number_of_turbines'] = \ - number_of_turbines + self.wind_turbine_fleet.loc[ + ix, "number_of_turbines" + ] = number_of_turbines except TypeError: - raise ValueError(msg.format(row['wind_turbine'])) + raise ValueError(msg.format(row["wind_turbine"])) # calculate total capacity if necessary and check that total capacity # and number of turbines is consistent if both are provided for ix, row in self.wind_turbine_fleet.iterrows(): - if np.isnan(row['total_capacity']): + if np.isnan(row["total_capacity"]): try: - self.wind_turbine_fleet.loc[ix, 'total_capacity'] = \ - row['number_of_turbines'] * \ - row['wind_turbine'].nominal_power + self.wind_turbine_fleet.loc[ix, "total_capacity"] = ( + row["number_of_turbines"] + * row["wind_turbine"].nominal_power + ) except TypeError: raise ValueError( - 'Total capacity of turbines of type {turbine} cannot ' - 'be deduced. Please check if the nominal power of the ' - 'wind turbine is set.'.format( - turbine=row['wind_turbine'])) + "Total capacity of turbines of type {turbine} cannot " + "be deduced. Please check if the nominal power of the " + "wind turbine is set.".format( + turbine=row["wind_turbine"] + ) + ) else: - if not abs(row['total_capacity'] - ( - row['number_of_turbines'] * - row['wind_turbine'].nominal_power)) < 1: - self.wind_turbine_fleet.loc[ix, 'total_capacity'] = \ - row['number_of_turbines'] * \ - row['wind_turbine'].nominal_power + if ( + not abs( + row["total_capacity"] + - ( + row["number_of_turbines"] + * row["wind_turbine"].nominal_power + ) + ) + < 1 + ): + self.wind_turbine_fleet.loc[ix, "total_capacity"] = ( + row["number_of_turbines"] + * row["wind_turbine"].nominal_power + ) msg = ( - 'The provided total capacity of WindTurbine {0} has ' - 'been overwritten as it was not consistent with the ' - 'number of turbines provided for this type.') - warnings.warn(msg.format(row['wind_turbine']), - tools.WindpowerlibUserWarning) + "The provided total capacity of WindTurbine {0} has " + "been overwritten as it was not consistent with the " + "number of turbines provided for this type." + ) + warnings.warn( + msg.format(row["wind_turbine"]), + tools.WindpowerlibUserWarning, + ) def __repr__(self): - if self.name is not '': - return 'Wind farm: {name}'.format(name=self.name) + if self.name is not "": + return "Wind farm: {name}".format(name=self.name) else: - return 'Wind farm with turbine fleet: [number, type]\n {}'.format( + return "Wind farm with turbine fleet: [number, type]\n {}".format( self.wind_turbine_fleet.loc[ - :, ['number_of_turbines', 'wind_turbine']].values) + :, ["number_of_turbines", "wind_turbine"] + ].values + ) @property def nominal_power(self): @@ -268,16 +292,24 @@ def mean_hub_height(self): """ self.hub_height = np.exp( - sum(np.log(row['wind_turbine'].hub_height) * row['total_capacity'] - for ix, row in self.wind_turbine_fleet.iterrows()) / - self.nominal_power) + sum( + np.log(row["wind_turbine"].hub_height) * row["total_capacity"] + for ix, row in self.wind_turbine_fleet.iterrows() + ) + / self.nominal_power + ) return self - def assign_power_curve(self, wake_losses_model='wind_farm_efficiency', - smoothing=False, block_width=0.5, - standard_deviation_method='turbulence_intensity', - smoothing_order='wind_farm_power_curves', - turbulence_intensity=None, **kwargs): + def assign_power_curve( + self, + wake_losses_model="wind_farm_efficiency", + smoothing=False, + block_width=0.5, + standard_deviation_method="turbulence_intensity", + smoothing_order="wind_farm_power_curves", + turbulence_intensity=None, + **kwargs + ): r""" Calculates the power curve of a wind farm. @@ -327,87 +359,126 @@ def assign_power_curve(self, wake_losses_model='wind_farm_efficiency', """ # Check if all wind turbines have a power curve as attribute - for turbine in self.wind_turbine_fleet['wind_turbine']: + for turbine in self.wind_turbine_fleet["wind_turbine"]: if turbine.power_curve is None: - raise ValueError("For an aggregated wind farm power curve " + - "each wind turbine needs a power curve " + - "but `power_curve` of '{}' is None.".format( - turbine)) + raise ValueError( + "For an aggregated wind farm power curve " + + "each wind turbine needs a power curve " + + "but `power_curve` of '{}' is None.".format(turbine) + ) # Initialize data frame for power curve values df = pd.DataFrame() for ix, row in self.wind_turbine_fleet.iterrows(): # Check if needed parameters are available and/or assign them if smoothing: - if (standard_deviation_method == 'turbulence_intensity' and - turbulence_intensity is None): - if 'roughness_length' in kwargs and \ - kwargs['roughness_length'] is not None: + if ( + standard_deviation_method == "turbulence_intensity" + and turbulence_intensity is None + ): + if ( + "roughness_length" in kwargs + and kwargs["roughness_length"] is not None + ): # Calculate turbulence intensity and write to kwargs - turbulence_intensity = ( - tools.estimate_turbulence_intensity( - row['wind_turbine'].hub_height, - kwargs['roughness_length'])) - kwargs['turbulence_intensity'] = turbulence_intensity + turbulence_intensity = tools.estimate_turbulence_intensity( + row["wind_turbine"].hub_height, + kwargs["roughness_length"], + ) + kwargs["turbulence_intensity"] = turbulence_intensity else: raise ValueError( - "`roughness_length` must be defined for using " + - "'turbulence_intensity' as " + - "`standard_deviation_method` if " + - "`turbulence_intensity` is not given") + "`roughness_length` must be defined for using " + + "'turbulence_intensity' as " + + "`standard_deviation_method` if " + + "`turbulence_intensity` is not given" + ) # Get original power curve - power_curve = pd.DataFrame(row['wind_turbine'].power_curve) + power_curve = pd.DataFrame(row["wind_turbine"].power_curve) # Editions to the power curves before the summation - if smoothing and smoothing_order == 'turbine_power_curves': + if smoothing and smoothing_order == "turbine_power_curves": power_curve = power_curves.smooth_power_curve( - power_curve['wind_speed'], power_curve['value'], + power_curve["wind_speed"], + power_curve["value"], standard_deviation_method=standard_deviation_method, - block_width=block_width, **kwargs) + block_width=block_width, + **kwargs, + ) else: # Add value zero to start and end of curve as otherwise # problems can occur during the aggregation - if power_curve.iloc[0]['wind_speed'] != 0.0: + if power_curve.iloc[0]["wind_speed"] != 0.0: power_curve = pd.concat( - [pd.DataFrame(data={ - 'value': [0.0], 'wind_speed': [0.0]}), - power_curve], join='inner') - if power_curve.iloc[-1]['value'] != 0.0: + [ + pd.DataFrame( + data={"value": [0.0], "wind_speed": [0.0]} + ), + power_curve, + ], + join="inner", + ) + if power_curve.iloc[-1]["value"] != 0.0: power_curve = pd.concat( - [power_curve, pd.DataFrame(data={ - 'wind_speed': [power_curve['wind_speed'].loc[ - power_curve.index[-1]] + 0.5], - 'value': [0.0]})], join='inner') + [ + power_curve, + pd.DataFrame( + data={ + "wind_speed": [ + power_curve["wind_speed"].loc[ + power_curve.index[-1] + ] + + 0.5 + ], + "value": [0.0], + } + ), + ], + join="inner", + ) # Add power curves of all turbine types to data frame # (multiplied by turbine amount) df = pd.concat( - [df, pd.DataFrame(power_curve.set_index(['wind_speed']) * - row['number_of_turbines'])], axis=1) + [ + df, + pd.DataFrame( + power_curve.set_index(["wind_speed"]) + * row["number_of_turbines"] + ), + ], + axis=1, + ) # Aggregate all power curves wind_farm_power_curve = pd.DataFrame( - df.interpolate(method='index').sum(axis=1)) - wind_farm_power_curve.columns = ['value'] + df.interpolate(method="index").sum(axis=1) + ) + wind_farm_power_curve.columns = ["value"] wind_farm_power_curve.reset_index(inplace=True) # Apply power curve smoothing and consideration of wake losses # after the summation - if smoothing and smoothing_order == 'wind_farm_power_curves': + if smoothing and smoothing_order == "wind_farm_power_curves": wind_farm_power_curve = power_curves.smooth_power_curve( - wind_farm_power_curve['wind_speed'], - wind_farm_power_curve['value'], + wind_farm_power_curve["wind_speed"], + wind_farm_power_curve["value"], standard_deviation_method=standard_deviation_method, - block_width=block_width, **kwargs) - if wake_losses_model == 'wind_farm_efficiency': + block_width=block_width, + **kwargs, + ) + if wake_losses_model == "wind_farm_efficiency": if self.efficiency is not None: - wind_farm_power_curve = ( - power_curves.wake_losses_to_power_curve( - wind_farm_power_curve['wind_speed'].values, - wind_farm_power_curve['value'].values, - wind_farm_efficiency=self.efficiency)) + wind_farm_power_curve = power_curves.wake_losses_to_power_curve( + wind_farm_power_curve["wind_speed"].values, + wind_farm_power_curve["value"].values, + wind_farm_efficiency=self.efficiency, + ) else: msg = ( "If you use `wake_losses_model` '{model}' your WindFarm " "needs an efficiency but `efficiency` is {eff}. \n\n" "Failing farm:\n {farm}" ) - raise ValueError(msg.format(model=wake_losses_model, farm=self, - eff=self.efficiency)) + raise ValueError( + msg.format( + model=wake_losses_model, farm=self, eff=self.efficiency + ) + ) self.power_curve = wind_farm_power_curve return self From 1a8a6018180310100d8507821c1410be0704d10b Mon Sep 17 00:00:00 2001 From: uvchik Date: Wed, 8 Jan 2020 11:32:37 +0100 Subject: [PATCH 7/7] Fix lint errror --- windpowerlib/wind_farm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windpowerlib/wind_farm.py b/windpowerlib/wind_farm.py index 085ec812..e7deb42d 100644 --- a/windpowerlib/wind_farm.py +++ b/windpowerlib/wind_farm.py @@ -227,7 +227,7 @@ def check_and_complete_wind_turbine_fleet(self): ) def __repr__(self): - if self.name is not "": + if self.name != "": return "Wind farm: {name}".format(name=self.name) else: return "Wind farm with turbine fleet: [number, type]\n {}".format(