diff --git a/.stickler.yml b/.stickler.yml new file mode 100644 index 00000000..49b2bdd8 --- /dev/null +++ b/.stickler.yml @@ -0,0 +1,12 @@ +linters: + flake8: + python: 3 + max-line-length: 79 + select: C,E,F,W,B,B950 + ignore: E203, E501, W503 + black: + config: ./pyproject.toml + fixer: true + +fixers: + enable: true diff --git a/.travis.yml b/.travis.yml index 9fb5c8f3..84ad58b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,7 @@ matrix: - python: 3.5 - python: 3.6 - python: 3.7 - dist: xenial - sudo: true + - python: 3.8 # command to install dependencies #before_install: diff --git a/example/modelchain_example.py b/example/modelchain_example.py index 6aec70f2..c4686e61 100644 --- a/example/modelchain_example.py +++ b/example/modelchain_example.py @@ -64,19 +64,24 @@ def get_weather_data(filename='weather.csv', **kwargs): """ - if 'datapath' not in kwargs: - kwargs['datapath'] = os.path.join(os.path.split( - os.path.dirname(__file__))[0], 'example') - file = os.path.join(kwargs['datapath'], filename) + if "datapath" not in kwargs: + kwargs["datapath"] = os.path.join( + os.path.split(os.path.dirname(__file__))[0], "example" + ) + file = os.path.join(kwargs["datapath"], filename) # read csv file weather_df = pd.read_csv( - file, index_col=0, header=[0, 1], - date_parser=lambda idx: pd.to_datetime(idx, utc=True)) + file, + index_col=0, + header=[0, 1], + date_parser=lambda idx: pd.to_datetime(idx, utc=True), + ) # change type of index to datetime and set time zone weather_df.index = pd.to_datetime(weather_df.index).tz_convert( - 'Europe/Berlin') + "Europe/Berlin" + ) # change type of height from str to int by resetting columns l0 = [_[0] for _ in weather_df.columns] @@ -112,8 +117,8 @@ def initialize_wind_turbines(): # specification of wind turbine where data is provided in the oedb # turbine library enercon_e126 = { - 'turbine_type': 'E-126/4200', # turbine type as in register - 'hub_height': 135 # in m + "turbine_type": "E-126/4200", # turbine type as in register + "hub_height": 135, # in m } # initialize WindTurbine object e126 = WindTurbine(**enercon_e126) @@ -121,24 +126,29 @@ def initialize_wind_turbines(): # specification of own wind turbine (Note: power values and nominal power # have to be in Watt) my_turbine = { - 'nominal_power': 3e6, # in W - 'hub_height': 105, # in m - 'power_curve': pd.DataFrame( - data={'value': [p * 1000 for p in [ - 0.0, 26.0, 180.0, 1500.0, 3000.0, 3000.0]], # in W - 'wind_speed': [0.0, 3.0, 5.0, 10.0, 15.0, 25.0]}) # in m/s + "nominal_power": 3e6, # in W + "hub_height": 105, # in m + "power_curve": pd.DataFrame( + data={ + "value": [ + p * 1000 + for p in [0.0, 26.0, 180.0, 1500.0, 3000.0, 3000.0] + ], # in W + "wind_speed": [0.0, 3.0, 5.0, 10.0, 15.0, 25.0], + } + ), # in m/s } # initialize WindTurbine object my_turbine = WindTurbine(**my_turbine) # specification of wind turbine where power coefficient curve and nominal # power is provided in an own csv file - csv_path = os.path.join(os.path.dirname(__file__), 'data') + csv_path = os.path.join(os.path.dirname(__file__), "data") dummy_turbine = { - 'turbine_type': "DUMMY 1", - 'hub_height': 100, # in m - 'rotor_diameter': 70, # in m - 'path': csv_path + "turbine_type": "DUMMY 1", + "hub_height": 100, # in m + "rotor_diameter": 70, # in m + "path": csv_path, } # initialize WindTurbine object dummy_turbine = WindTurbine(**dummy_turbine) @@ -182,18 +192,19 @@ def calculate_power_output(weather, my_turbine, e126, dummy_turbine): # power output calculation for e126 # own specifications for ModelChain setup modelchain_data = { - 'wind_speed_model': 'logarithmic', # 'logarithmic' (default), - # 'hellman' or - # 'interpolation_extrapolation' - 'density_model': 'ideal_gas', # 'barometric' (default), 'ideal_gas' or - # 'interpolation_extrapolation' - 'temperature_model': 'linear_gradient', # 'linear_gradient' (def.) or - # 'interpolation_extrapolation' - 'power_output_model': 'power_curve', # 'power_curve' (default) or - # 'power_coefficient_curve' - 'density_correction': True, # False (default) or True - 'obstacle_height': 0, # default: 0 - 'hellman_exp': None} # None (default) or None + "wind_speed_model": "logarithmic", # 'logarithmic' (default), + # 'hellman' or + # 'interpolation_extrapolation' + "density_model": "ideal_gas", # 'barometric' (default), 'ideal_gas' or + # 'interpolation_extrapolation' + "temperature_model": "linear_gradient", # 'linear_gradient' (def.) or + # 'interpolation_extrapolation' + "power_output_model": "power_curve", # 'power_curve' (default) or + # 'power_coefficient_curve' + "density_correction": True, # False (default) or True + "obstacle_height": 0, # default: 0 + "hellman_exp": None, + } # None (default) or None # initialize ModelChain with own specifications and use run_model method # to calculate power output mc_e126 = ModelChain(e126, **modelchain_data).run_model(weather) @@ -203,8 +214,8 @@ def calculate_power_output(weather, my_turbine, e126, dummy_turbine): # power output calculation for example_turbine # own specification for 'power_output_model' mc_example_turbine = ModelChain( - dummy_turbine, - power_output_model='power_coefficient_curve').run_model(weather) + dummy_turbine, power_output_model="power_coefficient_curve" + ).run_model(weather) dummy_turbine.power_output = mc_example_turbine.power_output return @@ -272,7 +283,7 @@ def run_example(): Runs the basic example. """ - weather = get_weather_data('weather.csv') + weather = get_weather_data("weather.csv") my_turbine, e126, dummy_turbine = initialize_wind_turbines() calculate_power_output(weather, my_turbine, e126, dummy_turbine) plot_or_print(my_turbine, e126, dummy_turbine) diff --git a/example/test_examples.py b/example/test_examples.py index 8b18e8db..afa612f4 100644 --- a/example/test_examples.py +++ b/example/test_examples.py @@ -15,31 +15,45 @@ class TestExamples: - def test_modelchain_example_flh(self): # tests full load hours - weather = mc_e.get_weather_data('weather.csv') + weather = mc_e.get_weather_data("weather.csv") my_turbine, e126, dummy_turbine = mc_e.initialize_wind_turbines() mc_e.calculate_power_output(weather, my_turbine, e126, dummy_turbine) - assert_allclose(2764.194772, (e126.power_output.sum() / - e126.nominal_power), 0.01) - assert_allclose(1882.7567, (my_turbine.power_output.sum() / - my_turbine.nominal_power), 0.01) + assert_allclose( + 2764.194772, (e126.power_output.sum() / e126.nominal_power), 0.01 + ) + assert_allclose( + 1882.7567, + (my_turbine.power_output.sum() / my_turbine.nominal_power), + 0.01, + ) def test_turbine_cluster_modelchain_example_flh(self): # tests full load hours - weather = mc_e.get_weather_data('weather.csv') + weather = mc_e.get_weather_data("weather.csv") my_turbine, e126, dummy_turbine = mc_e.initialize_wind_turbines() example_farm, example_farm_2 = tc_mc_e.initialize_wind_farms( - my_turbine, e126) + my_turbine, e126 + ) example_cluster = tc_mc_e.initialize_wind_turbine_cluster( - example_farm, example_farm_2) + example_farm, example_farm_2 + ) tc_mc_e.calculate_power_output(weather, example_farm, example_cluster) - assert_allclose(1956.164053, (example_farm.power_output.sum() / - example_farm.nominal_power), 0.01) - assert_allclose(2156.794154, (example_cluster.power_output.sum() / - example_cluster.nominal_power), 0.01) + assert_allclose( + 1956.164053, + (example_farm.power_output.sum() / example_farm.nominal_power), + 0.01, + ) + assert_allclose( + 2156.794154, + ( + example_cluster.power_output.sum() + / example_cluster.nominal_power + ), + 0.01, + ) def _notebook_run(self, path): """ @@ -49,17 +63,29 @@ def _notebook_run(self, path): dirname, __ = os.path.split(path) os.chdir(dirname) with tempfile.NamedTemporaryFile(suffix=".ipynb") as fout: - args = ["jupyter", "nbconvert", "--to", "notebook", "--execute", - "--ExecutePreprocessor.timeout=60", - "--output", fout.name, path] + args = [ + "jupyter", + "nbconvert", + "--to", + "notebook", + "--execute", + "--ExecutePreprocessor.timeout=60", + "--output", + fout.name, + path, + ] subprocess.check_call(args) fout.seek(0) nb = nbformat.read(fout, nbformat.current_nbformat) - errors = [output for cell in nb.cells if "outputs" in cell - for output in cell["outputs"] - if output.output_type == "error"] + errors = [ + output + for cell in nb.cells + if "outputs" in cell + for output in cell["outputs"] + if output.output_type == "error" + ] return nb, errors @@ -67,12 +93,14 @@ def _notebook_run(self, path): def test_modelchain_example_ipynb(self): dir_path = os.path.dirname(os.path.realpath(__file__)) nb, errors = self._notebook_run( - os.path.join(dir_path, 'modelchain_example.ipynb')) + os.path.join(dir_path, "modelchain_example.ipynb") + ) assert errors == [] @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6") def test_turbine_cluster_modelchain_example_ipynb(self): dir_path = os.path.dirname(os.path.realpath(__file__)) nb, errors = self._notebook_run( - os.path.join(dir_path, 'turbine_cluster_modelchain_example.ipynb')) + os.path.join(dir_path, "turbine_cluster_modelchain_example.ipynb") + ) assert errors == [] diff --git a/example/turbine_cluster_modelchain_example.py b/example/turbine_cluster_modelchain_example.py index c308bc02..ea0584bd 100644 --- a/example/turbine_cluster_modelchain_example.py +++ b/example/turbine_cluster_modelchain_example.py @@ -59,21 +59,27 @@ def initialize_wind_farms(my_turbine, e126): # that type in the wind farm (float values are possible as well) or the # total installed capacity of that turbine type in W wind_turbine_fleet = pd.DataFrame( - {'wind_turbine': [my_turbine, e126], # as windpowerlib.WindTurbine - 'number_of_turbines': [6, None], - 'total_capacity': [None, 12.6e6]} + { + "wind_turbine": [my_turbine, e126], # as windpowerlib.WindTurbine + "number_of_turbines": [6, None], + "total_capacity": [None, 12.6e6], + } ) # initialize WindFarm object - example_farm = WindFarm(name='example_farm', - wind_turbine_fleet=wind_turbine_fleet) + example_farm = WindFarm( + name="example_farm", wind_turbine_fleet=wind_turbine_fleet + ) # specification of wind farm data (2) containing a wind farm efficiency # wind turbine fleet is provided using the to_group function example_farm_2_data = { - 'name': 'example_farm_2', - 'wind_turbine_fleet': [my_turbine.to_group(6), - e126.to_group(total_capacity=12.6e6)], - 'efficiency': 0.9} + "name": "example_farm_2", + "wind_turbine_fleet": [ + my_turbine.to_group(6), + e126.to_group(total_capacity=12.6e6), + ], + "efficiency": 0.9, + } # initialize WindFarm object example_farm_2 = WindFarm(**example_farm_2_data) @@ -104,8 +110,9 @@ def initialize_wind_turbine_cluster(example_farm, example_farm_2): # specification of cluster data example_cluster_data = { - 'name': 'example_cluster', - 'wind_farms': [example_farm, example_farm_2]} + "name": "example_cluster", + "wind_farms": [example_farm, example_farm_2], + } # initialize WindTurbineCluster object example_cluster = WindTurbineCluster(**example_cluster_data) @@ -144,35 +151,36 @@ class that provides all necessary steps to calculate the power output of a # power output calculation for turbine_cluster # own specifications for TurbineClusterModelChain setup modelchain_data = { - 'wake_losses_model': - 'wind_farm_efficiency', # 'dena_mean' (default), None, - # 'wind_farm_efficiency' or name - # of another wind efficiency curve - # see :py:func:`~.wake_losses.get_wind_efficiency_curve` - 'smoothing': True, # False (default) or True - 'block_width': 0.5, # default: 0.5 - 'standard_deviation_method': 'Staffell_Pfenninger', # - # 'turbulence_intensity' (default) - # or 'Staffell_Pfenninger' - 'smoothing_order': 'wind_farm_power_curves', # - # 'wind_farm_power_curves' (default) or - # 'turbine_power_curves' - 'wind_speed_model': 'logarithmic', # 'logarithmic' (default), - # 'hellman' or - # 'interpolation_extrapolation' - 'density_model': 'ideal_gas', # 'barometric' (default), 'ideal_gas' or - # 'interpolation_extrapolation' - 'temperature_model': 'linear_gradient', # 'linear_gradient' (def.) or - # 'interpolation_extrapolation' - 'power_output_model': 'power_curve', # 'power_curve' (default) or - # 'power_coefficient_curve' - 'density_correction': True, # False (default) or True - 'obstacle_height': 0, # default: 0 - 'hellman_exp': None} # None (default) or None + "wake_losses_model": "wind_farm_efficiency", # 'dena_mean' (default), None, + # 'wind_farm_efficiency' or name + # of another wind efficiency curve + # see :py:func:`~.wake_losses.get_wind_efficiency_curve` + "smoothing": True, # False (default) or True + "block_width": 0.5, # default: 0.5 + "standard_deviation_method": "Staffell_Pfenninger", # + # 'turbulence_intensity' (default) + # or 'Staffell_Pfenninger' + "smoothing_order": "wind_farm_power_curves", # + # 'wind_farm_power_curves' (default) or + # 'turbine_power_curves' + "wind_speed_model": "logarithmic", # 'logarithmic' (default), + # 'hellman' or + # 'interpolation_extrapolation' + "density_model": "ideal_gas", # 'barometric' (default), 'ideal_gas' or + # 'interpolation_extrapolation' + "temperature_model": "linear_gradient", # 'linear_gradient' (def.) or + # 'interpolation_extrapolation' + "power_output_model": "power_curve", # 'power_curve' (default) or + # 'power_coefficient_curve' + "density_correction": True, # False (default) or True + "obstacle_height": 0, # default: 0 + "hellman_exp": None, + } # None (default) or None # initialize TurbineClusterModelChain with own specifications and use # run_model method to calculate power output mc_example_cluster = TurbineClusterModelChain( - example_cluster, **modelchain_data).run_model(weather) + example_cluster, **modelchain_data + ).run_model(weather) # write power output time series to WindTurbineCluster object example_cluster.power_output = mc_example_cluster.power_output @@ -209,11 +217,12 @@ def run_example(): Runs the example. """ - weather = mc_e.get_weather_data('weather.csv') + weather = mc_e.get_weather_data("weather.csv") my_turbine, e126, dummy_turbine = mc_e.initialize_wind_turbines() example_farm, example_farm_2 = initialize_wind_farms(my_turbine, e126) - example_cluster = initialize_wind_turbine_cluster(example_farm, - example_farm_2) + example_cluster = initialize_wind_turbine_cluster( + example_farm, example_farm_2 + ) calculate_power_output(weather, example_farm, example_cluster) plot_or_print(example_farm, example_cluster) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..27913c1d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[tool.black] +line-length = 79 +target-version = ['py35', 'py36', 'py37', 'py38'] +include = '\.pyi?$' +exclude = ''' +/( + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' diff --git a/tests/test_density.py b/tests/test_density.py index 7dd681af..68556f4d 100644 --- a/tests/test_density.py +++ b/tests/test_density.py @@ -12,57 +12,66 @@ class TestDensity: - def test_barometric(self): - parameters = {'pressure': pd.Series(data=[101125, 101000]), - 'pressure_height': 0, - 'hub_height': 100, - 'temperature_hub_height': pd.Series(data=[267, 268])} + parameters = { + "pressure": pd.Series(data=[101125, 101000]), + "pressure_height": 0, + "hub_height": 100, + "temperature_hub_height": pd.Series(data=[267, 268]), + } # Test pressure as pd.Series and temperature_hub_height as pd.Series # and np.array rho_exp = pd.Series(data=[1.30305336, 1.29656645]) assert_series_equal(barometric(**parameters), rho_exp) - parameters['temperature_hub_height'] = np.array( - parameters['temperature_hub_height']) + parameters["temperature_hub_height"] = np.array( + parameters["temperature_hub_height"] + ) assert_series_equal(barometric(**parameters), rho_exp) # Test pressure as np.array and temperature_hub_height as pd.Series - parameters['pressure'] = np.array(parameters['pressure']) - parameters['temperature_hub_height'] = pd.Series( - data=parameters['temperature_hub_height']) + parameters["pressure"] = np.array(parameters["pressure"]) + parameters["temperature_hub_height"] = pd.Series( + data=parameters["temperature_hub_height"] + ) assert_series_equal(barometric(**parameters), rho_exp) # Test pressure as np.array and temperature_hub_height as np.array rho_exp = np.array([1.30305336, 1.29656645]) - parameters['temperature_hub_height'] = np.array( - parameters['temperature_hub_height']) + parameters["temperature_hub_height"] = np.array( + parameters["temperature_hub_height"] + ) assert_allclose(barometric(**parameters), rho_exp) assert isinstance(barometric(**parameters), np.ndarray) def test_ideal_gas(self): - parameters = {'pressure': pd.Series(data=[101125, 101000]), - 'pressure_height': 0, - 'hub_height': 100, - 'temperature_hub_height': pd.Series(data=[267, 268])} + parameters = { + "pressure": pd.Series(data=[101125, 101000]), + "pressure_height": 0, + "hub_height": 100, + "temperature_hub_height": pd.Series(data=[267, 268]), + } # Test pressure as pd.Series and temperature_hub_height as pd.Series # and np.array rho_exp = pd.Series(data=[1.30309439, 1.29660728]) assert_series_equal(ideal_gas(**parameters), rho_exp) - parameters['temperature_hub_height'] = np.array( - parameters['temperature_hub_height']) + parameters["temperature_hub_height"] = np.array( + parameters["temperature_hub_height"] + ) assert_series_equal(ideal_gas(**parameters), rho_exp) # Test pressure as np.array and temperature_hub_height as pd.Series - parameters['pressure'] = np.array(parameters['pressure']) - parameters['temperature_hub_height'] = pd.Series( - data=parameters['temperature_hub_height']) + parameters["pressure"] = np.array(parameters["pressure"]) + parameters["temperature_hub_height"] = pd.Series( + data=parameters["temperature_hub_height"] + ) assert_allclose(ideal_gas(**parameters), rho_exp) # Test pressure as np.array and temperature_hub_height as np.array rho_exp = np.array([1.30309439, 1.29660728]) - parameters['temperature_hub_height'] = np.array( - parameters['temperature_hub_height']) + parameters["temperature_hub_height"] = np.array( + parameters["temperature_hub_height"] + ) assert_allclose(ideal_gas(**parameters), rho_exp) assert isinstance(ideal_gas(**parameters), np.ndarray) diff --git a/tests/test_modelchain.py b/tests/test_modelchain.py index fbc1b5d4..8bdac075 100644 --- a/tests/test_modelchain.py +++ b/tests/test_modelchain.py @@ -14,15 +14,16 @@ class TestModelChain: - @classmethod def setup_class(self): """Setup default values""" - self.test_turbine = {'hub_height': 100, - 'turbine_type': 'E-126/4200', - 'power_curve': pd.DataFrame( - data={'value': [0.0, 4200 * 1000], - 'wind_speed': [0.0, 25.0]})} + self.test_turbine = { + "hub_height": 100, + "turbine_type": "E-126/4200", + "power_curve": pd.DataFrame( + data={"value": [0.0, 4200 * 1000], "wind_speed": [0.0, 25.0]} + ), + } temperature_2m = np.array([[267], [268]]) temperature_10m = np.array([[267], [266]]) @@ -31,13 +32,31 @@ def setup_class(self): wind_speed_10m = np.array([[5.0], [6.5]]) roughness_length = np.array([[0.15], [0.15]]) self.weather_df = pd.DataFrame( - np.hstack((temperature_2m, temperature_10m, pressure_0m, - wind_speed_8m, wind_speed_10m, roughness_length)), + np.hstack( + ( + temperature_2m, + temperature_10m, + pressure_0m, + wind_speed_8m, + wind_speed_10m, + roughness_length, + ) + ), index=[0, 1], - columns=[np.array(['temperature', 'temperature', 'pressure', - 'wind_speed', 'wind_speed', - 'roughness_length']), - np.array([2, 10, 0, 8, 10, 0])]) + columns=[ + np.array( + [ + "temperature", + "temperature", + "pressure", + "wind_speed", + "wind_speed", + "roughness_length", + ] + ), + np.array([2, 10, 0, 8, 10, 0]), + ], + ) def test_temperature_hub(self): # Test modelchain with temperature_model='linear_gradient' @@ -45,17 +64,20 @@ def test_temperature_hub(self): # Test modelchain with temperature_model='interpolation_extrapolation' test_mc_2 = mc.ModelChain( wt.WindTurbine(**self.test_turbine), - temperature_model='interpolation_extrapolation') + temperature_model="interpolation_extrapolation", + ) # Parameters for tests temperature_2m = np.array([[267], [268]]) temperature_10m = np.array([[267], [266]]) - weather_df = pd.DataFrame(np.hstack((temperature_2m, - temperature_10m)), - index=[0, 1], - columns=[np.array(['temperature', - 'temperature']), - np.array([2, 10])]) + weather_df = pd.DataFrame( + np.hstack((temperature_2m, temperature_10m)), + index=[0, 1], + columns=[ + np.array(["temperature", "temperature"]), + np.array([2, 10]), + ], + ) # temperature_10m is closer to hub height than temperature_2m temp_exp = pd.Series(data=[266.415, 265.415], name=10) @@ -64,16 +86,20 @@ def test_temperature_hub(self): assert_series_equal(test_mc_2.temperature_hub(weather_df), temp_exp) # change heights of temperatures so that old temperature_2m is now used - weather_df.columns = [np.array(['temperature', 'temperature']), - np.array([10, 200])] + weather_df.columns = [ + np.array(["temperature", "temperature"]), + np.array([10, 200]), + ] temp_exp = pd.Series(data=[266.415, 267.415], name=10) assert_series_equal(test_mc.temperature_hub(weather_df), temp_exp) temp_exp = pd.Series(data=[267.0, 267.052632]) assert_series_equal(test_mc_2.temperature_hub(weather_df), temp_exp) # temperature at hub height - weather_df.columns = [np.array(['temperature', 'temperature']), - np.array([100, 10])] + weather_df.columns = [ + np.array(["temperature", "temperature"]), + np.array([100, 10]), + ] temp_exp = pd.Series(data=[267, 268], name=100) assert_series_equal(test_mc.temperature_hub(weather_df), temp_exp) @@ -81,24 +107,27 @@ def test_density_hub(self): # Test modelchain with density_model='barometric' test_mc = mc.ModelChain(wt.WindTurbine(**self.test_turbine)) # Test modelchain with density_model='ideal_gas' - test_mc_2 = mc.ModelChain(wt.WindTurbine(**self.test_turbine), - density_model='ideal_gas') + test_mc_2 = mc.ModelChain( + wt.WindTurbine(**self.test_turbine), density_model="ideal_gas" + ) # Test modelchain with density_model='interpolation_extrapolation' - test_mc_3 = mc.ModelChain(wt.WindTurbine(**self.test_turbine), - density_model='interpolation_extrapolation') + test_mc_3 = mc.ModelChain( + wt.WindTurbine(**self.test_turbine), + density_model="interpolation_extrapolation", + ) # Parameters for tests temperature_2m = np.array([[267], [268]]) temperature_10m = np.array([[267], [266]]) pressure_0m = np.array([[101125], [101000]]) - weather_df = pd.DataFrame(np.hstack((temperature_2m, - temperature_10m, - pressure_0m)), - index=[0, 1], - columns=[np.array(['temperature', - 'temperature', - 'pressure']), - np.array([2, 10, 0])]) + weather_df = pd.DataFrame( + np.hstack((temperature_2m, temperature_10m, pressure_0m)), + index=[0, 1], + columns=[ + np.array(["temperature", "temperature", "pressure"]), + np.array([2, 10, 0]), + ], + ) # temperature_10m is closer to hub height than temperature_2m rho_exp = pd.Series(data=[1.30591, 1.30919]) @@ -107,30 +136,31 @@ def test_density_hub(self): assert_series_equal(test_mc_2.density_hub(weather_df), rho_exp) # change heights of temperatures so that old temperature_2m is now used - weather_df.columns = [np.array(['temperature', 'temperature', - 'pressure']), - np.array([10, 200, 0])] + weather_df.columns = [ + np.array(["temperature", "temperature", "pressure"]), + np.array([10, 200, 0]), + ] rho_exp = pd.Series(data=[1.30591, 1.29940]) assert_series_equal(test_mc.density_hub(weather_df), rho_exp) rho_exp = pd.Series(data=[1.30595575725, 1.29944375221]) assert_series_equal(test_mc_2.density_hub(weather_df), rho_exp) # temperature at hub height - weather_df.columns = [np.array(['temperature', 'temperature', - 'pressure']), - np.array([100, 10, 0])] + weather_df.columns = [ + np.array(["temperature", "temperature", "pressure"]), + np.array([100, 10, 0]), + ] rho_exp = pd.Series(data=[1.30305, 1.29657]) assert_series_equal(test_mc.density_hub(weather_df), rho_exp) # density interpolation density_10m = np.array([[1.30591], [1.29940]]) density_150m = np.array([[1.30305], [1.29657]]) - weather_df = pd.DataFrame(np.hstack((density_10m, - density_150m)), - index=[0, 1], - columns=[np.array(['density', - 'density']), - np.array([10, 150])]) + weather_df = pd.DataFrame( + np.hstack((density_10m, density_150m)), + index=[0, 1], + columns=[np.array(["density", "density"]), np.array([10, 150])], + ) rho_exp = pd.Series(data=[1.304071, 1.297581]) assert_series_equal(test_mc_3.density_hub(weather_df), rho_exp) @@ -138,30 +168,33 @@ def test_wind_speed_hub(self): # Test modelchain with wind_speed_model='logarithmic' test_mc = mc.ModelChain(wt.WindTurbine(**self.test_turbine)) # Test modelchain with wind_speed_model='hellman' - test_mc_2 = mc.ModelChain(wt.WindTurbine(**self.test_turbine), - wind_speed_model='hellman') + test_mc_2 = mc.ModelChain( + wt.WindTurbine(**self.test_turbine), wind_speed_model="hellman" + ) # Test modelchain with wind_speed_model='interpolation_extrapolation' test_mc_3 = mc.ModelChain( wt.WindTurbine(**self.test_turbine), - wind_speed_model='interpolation_extrapolation') + wind_speed_model="interpolation_extrapolation", + ) # Test modelchain with # wind_speed_model='log_interpolation_extrapolation' test_mc_4 = mc.ModelChain( wt.WindTurbine(**self.test_turbine), - wind_speed_model='log_interpolation_extrapolation') + wind_speed_model="log_interpolation_extrapolation", + ) # Parameters for tests wind_speed_8m = np.array([[4.0], [5.0]]) wind_speed_10m = np.array([[5.0], [6.5]]) roughness_length = np.array([[0.15], [0.15]]) - weather_df = pd.DataFrame(np.hstack((wind_speed_8m, - wind_speed_10m, - roughness_length)), - index=[0, 1], - columns=[np.array(['wind_speed', - 'wind_speed', - 'roughness_length']), - np.array([8, 10, 0])]) + weather_df = pd.DataFrame( + np.hstack((wind_speed_8m, wind_speed_10m, roughness_length)), + index=[0, 1], + columns=[ + np.array(["wind_speed", "wind_speed", "roughness_length"]), + np.array([8, 10, 0]), + ], + ) # wind_speed_10m is closer to hub height than wind_speed_8m v_wind_exp = pd.Series(data=[7.74137, 10.06377]) @@ -174,9 +207,10 @@ def test_wind_speed_hub(self): assert_series_equal(test_mc_4.wind_speed_hub(weather_df), v_wind_exp) # wind_speed is given at hub height - weather_df.columns = [np.array(['wind_speed', 'wind_speed', - 'roughness_length']), - np.array([10, 100, 0])] + weather_df.columns = [ + np.array(["wind_speed", "wind_speed", "roughness_length"]), + np.array([10, 100, 0]), + ] v_wind_exp = pd.Series(data=[5.0, 6.5], name=100) assert_series_equal(test_mc.wind_speed_hub(weather_df), v_wind_exp) @@ -184,160 +218,216 @@ def test_wind_speed_hub(self): def test_with_default_parameter(self): """Test with default parameters of modelchain (power curve)""" - test_turbine = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'E-126/4200'} - power_output_exp = pd.Series(data=[1637405.4840444783, - 3154438.3894902095], - name='feedin_power_plant') + test_turbine = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "E-126/4200", + } + power_output_exp = pd.Series( + data=[1637405.4840444783, 3154438.3894902095], + name="feedin_power_plant", + ) test_mc = mc.ModelChain(wt.WindTurbine(**test_turbine)) test_mc.run_model(self.weather_df) assert_series_equal(test_mc.power_output, power_output_exp) def test_with_density_corrected_power_curve_and_hellman(self): """Test with density corrected power curve and hellman""" - test_turbine = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'E-126/4200'} - test_modelchain = {'wind_speed_model': 'hellman', - 'power_output_model': 'power_curve', - 'density_correction': True} - power_output_exp = pd.Series(data=[1366958.544547462, - 2823402.837201821], - name='feedin_power_plant') - test_mc = mc.ModelChain(wt.WindTurbine(**test_turbine), - **test_modelchain) + test_turbine = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "E-126/4200", + } + test_modelchain = { + "wind_speed_model": "hellman", + "power_output_model": "power_curve", + "density_correction": True, + } + power_output_exp = pd.Series( + data=[1366958.544547462, 2823402.837201821], + name="feedin_power_plant", + ) + test_mc = mc.ModelChain( + wt.WindTurbine(**test_turbine), **test_modelchain + ) test_mc.run_model(self.weather_df) assert_series_equal(test_mc.power_output, power_output_exp) def test_with_power_coefficient_curve_and_hellman(self): """Test with power coefficient curve and hellman""" - test_turbine = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'E-126/4200'} - power_output_exp = pd.Series(data=[534137.5112701517, - 1103611.1736067757], - name='feedin_power_plant') - test_modelchain = {'wind_speed_model': 'hellman', - 'power_output_model': 'power_coefficient_curve', - 'density_correction': False} - test_mc = mc.ModelChain(wt.WindTurbine(**test_turbine), - **test_modelchain) + test_turbine = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "E-126/4200", + } + power_output_exp = pd.Series( + data=[534137.5112701517, 1103611.1736067757], + name="feedin_power_plant", + ) + test_modelchain = { + "wind_speed_model": "hellman", + "power_output_model": "power_coefficient_curve", + "density_correction": False, + } + test_mc = mc.ModelChain( + wt.WindTurbine(**test_turbine), **test_modelchain + ) test_mc.run_model(self.weather_df) assert_series_equal(test_mc.power_output, power_output_exp) def test_wrong_spelling_power_output_model(self): """Raise ValueErrors due to wrong spelling of power_output_model""" with pytest.raises(ValueError): - test_modelchain = {'wind_speed_model': 'hellman', - 'power_output_model': 'wrong_spelling', - 'density_correction': False} - test_mc = mc.ModelChain(wt.WindTurbine(**self.test_turbine), - **test_modelchain) + test_modelchain = { + "wind_speed_model": "hellman", + "power_output_model": "wrong_spelling", + "density_correction": False, + } + test_mc = mc.ModelChain( + wt.WindTurbine(**self.test_turbine), **test_modelchain + ) test_mc.run_model(self.weather_df) def test_wrong_spelling_density_model(self): """Raise ValueErrors due to wrong spelling of density_model""" with pytest.raises(ValueError): - test_modelchain = {'wind_speed_model': 'hellman', - 'power_output_model': 'power_coefficient_curve', - 'density_correction': False, - 'density_model': 'wrong_spelling'} - test_mc = mc.ModelChain(wt.WindTurbine(**self.test_turbine), - **test_modelchain) + test_modelchain = { + "wind_speed_model": "hellman", + "power_output_model": "power_coefficient_curve", + "density_correction": False, + "density_model": "wrong_spelling", + } + test_mc = mc.ModelChain( + wt.WindTurbine(**self.test_turbine), **test_modelchain + ) test_mc.run_model(self.weather_df) def test_wrong_spelling_temperature_model(self): """Raise ValueErrors due to wrong spelling of temperature_model""" with pytest.raises(ValueError): - test_modelchain = {'wind_speed_model': 'hellman', - 'power_output_model': 'power_coefficient_curve', - 'density_correction': False, - 'temperature_model': 'wrong_spelling'} - test_mc = mc.ModelChain(wt.WindTurbine(**self.test_turbine), - **test_modelchain) + test_modelchain = { + "wind_speed_model": "hellman", + "power_output_model": "power_coefficient_curve", + "density_correction": False, + "temperature_model": "wrong_spelling", + } + test_mc = mc.ModelChain( + wt.WindTurbine(**self.test_turbine), **test_modelchain + ) test_mc.run_model(self.weather_df) def test_wrong_spelling_wind_speed_model(self): """Raise ValueErrors due to wrong spelling of wind_speed_model""" with pytest.raises(ValueError): - test_modelchain = {'wind_speed_model': 'wrong_spelling', - 'power_output_model': 'power_coefficient_curve', - 'density_correction': False} - test_mc = mc.ModelChain(wt.WindTurbine(**self.test_turbine), - **test_modelchain) + test_modelchain = { + "wind_speed_model": "wrong_spelling", + "power_output_model": "power_coefficient_curve", + "density_correction": False, + } + test_mc = mc.ModelChain( + wt.WindTurbine(**self.test_turbine), **test_modelchain + ) test_mc.run_model(self.weather_df) def test_wrong_density_correction_type(self): """Raise TypeErrors due to wrong type of `density_correction`""" with pytest.raises(TypeError): - test_modelchain = {'power_output_model': 'power_curve', - 'density_correction': 'wrong_type'} - test_mc = mc.ModelChain(wt.WindTurbine(**self.test_turbine), - **test_modelchain) + test_modelchain = { + "power_output_model": "power_curve", + "density_correction": "wrong_type", + } + test_mc = mc.ModelChain( + wt.WindTurbine(**self.test_turbine), **test_modelchain + ) test_mc.run_model(self.weather_df) @pytest.mark.filterwarnings("ignore:The WindTurbine") def test_missing_cp_values(self): """Raise TypeErrors due to missing cp-values""" - test_turbine = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'E-126/4201'} + test_turbine = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "E-126/4201", + } msg = "Power coefficient curve values of" with pytest.raises(TypeError, match=msg): - test_modelchain = {'power_output_model': 'power_coefficient_curve', - 'density_correction': True} - test_mc = mc.ModelChain(wt.WindTurbine(**test_turbine), - **test_modelchain) + test_modelchain = { + "power_output_model": "power_coefficient_curve", + "density_correction": True, + } + test_mc = mc.ModelChain( + wt.WindTurbine(**test_turbine), **test_modelchain + ) test_mc.run_model(self.weather_df) @pytest.mark.filterwarnings("ignore:The WindTurbine") def test_missing_p_values(self): """Raise TypeErrors due to missing p-values""" - test_turbine = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'E-126/4205'} + test_turbine = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "E-126/4205", + } msg = "Power curve values of" with pytest.raises(TypeError, match=msg): - test_modelchain = {'power_output_model': 'power_curve', - 'density_corr': True} - test_mc = mc.ModelChain(wt.WindTurbine(**test_turbine), - **test_modelchain) + test_modelchain = { + "power_output_model": "power_curve", + "density_corr": True, + } + test_mc = mc.ModelChain( + wt.WindTurbine(**test_turbine), **test_modelchain + ) test_mc.run_model(self.weather_df) def test_modelchain_with_power_curve_as_dict(self): """Test power curves as dict""" - my_turbine = {'nominal_power': 3e6, 'hub_height': 105, - 'rotor_diameter': 70, - 'power_curve': { - 'value': [p * 1000 for p in [ - 0.0, 26.0, 180.0, 1500.0, 3000.0, 3000.0]], - 'wind_speed': [0.0, 3.0, 5.0, 10.0, 15.0, 25.0]}, - 'power_coefficient_curve': { - 'value': [0.0, 0.43, 0.45, 0.35, 0.12, 0.03], - 'wind_speed': [0.0, 3.0, 5.0, 10.0, 15.0, 25.0]}} - power_output_exp = pd.Series(data=[919055.54840, - 1541786.60559], - name='feedin_power_plant') + my_turbine = { + "nominal_power": 3e6, + "hub_height": 105, + "rotor_diameter": 70, + "power_curve": { + "value": [ + p * 1000 + for p in [0.0, 26.0, 180.0, 1500.0, 3000.0, 3000.0] + ], + "wind_speed": [0.0, 3.0, 5.0, 10.0, 15.0, 25.0], + }, + "power_coefficient_curve": { + "value": [0.0, 0.43, 0.45, 0.35, 0.12, 0.03], + "wind_speed": [0.0, 3.0, 5.0, 10.0, 15.0, 25.0], + }, + } + power_output_exp = pd.Series( + data=[919055.54840, 1541786.60559], name="feedin_power_plant" + ) test_mc = mc.ModelChain(wt.WindTurbine(**my_turbine)) test_mc.run_model(self.weather_df) assert_series_equal(test_mc.power_output, power_output_exp) def test_modelchain_with_power_coefficient_curve_as_dict(self): """Test power coefficient curves as dict""" - my_turbine = {'nominal_power': 3e6, 'hub_height': 105, - 'rotor_diameter': 70, - 'power_curve': { - 'value': [p * 1000 for p in [ - 0.0, 26.0, 180.0, 1500.0, 3000.0, 3000.0]], - 'wind_speed': [0.0, 3.0, 5.0, 10.0, 15.0, 25.0]}, - 'power_coefficient_curve': { - 'value': [0.0, 0.43, 0.45, 0.35, 0.12, 0.03], - 'wind_speed': [0.0, 3.0, 5.0, 10.0, 15.0, 25.0]}} - power_output_exp = pd.Series(data=[469518.35104, - 901794.28532], - name='feedin_power_plant') - test_mc = mc.ModelChain(wt.WindTurbine(**my_turbine), - power_output_model='power_coefficient_curve') + my_turbine = { + "nominal_power": 3e6, + "hub_height": 105, + "rotor_diameter": 70, + "power_curve": { + "value": [ + p * 1000 + for p in [0.0, 26.0, 180.0, 1500.0, 3000.0, 3000.0] + ], + "wind_speed": [0.0, 3.0, 5.0, 10.0, 15.0, 25.0], + }, + "power_coefficient_curve": { + "value": [0.0, 0.43, 0.45, 0.35, 0.12, 0.03], + "wind_speed": [0.0, 3.0, 5.0, 10.0, 15.0, 25.0], + }, + } + power_output_exp = pd.Series( + data=[469518.35104, 901794.28532], name="feedin_power_plant" + ) + test_mc = mc.ModelChain( + wt.WindTurbine(**my_turbine), + power_output_model="power_coefficient_curve", + ) test_mc.run_model(self.weather_df) assert_series_equal(test_mc.power_output, power_output_exp) diff --git a/tests/test_power_curves.py b/tests/test_power_curves.py index 6e530e7e..ccf296ab 100644 --- a/tests/test_power_curves.py +++ b/tests/test_power_curves.py @@ -8,90 +8,148 @@ import pytest from pandas.util.testing import assert_frame_equal -from windpowerlib.power_curves import (smooth_power_curve, - wake_losses_to_power_curve) +from windpowerlib.power_curves import ( + smooth_power_curve, + wake_losses_to_power_curve, +) import windpowerlib.wind_turbine as wt class TestPowerCurves: - @classmethod def setup_class(self): - self.test_turbine = {'hub_height': 100, - 'turbine_type': 'E-126/4200'} + self.test_turbine = {"hub_height": 100, "turbine_type": "E-126/4200"} def test_smooth_power_curve(self): test_curve = wt.WindTurbine(**self.test_turbine).power_curve - parameters = {'power_curve_wind_speeds': test_curve['wind_speed'], - 'power_curve_values': test_curve['value'], - 'standard_deviation_method': 'turbulence_intensity'} + parameters = { + "power_curve_wind_speeds": test_curve["wind_speed"], + "power_curve_values": test_curve["value"], + "standard_deviation_method": "turbulence_intensity", + } # Raise ValueError - `turbulence_intensity` missing with pytest.raises(ValueError): - parameters['standard_deviation_method'] = 'turbulence_intensity' + parameters["standard_deviation_method"] = "turbulence_intensity" smooth_power_curve(**parameters) # Test turbulence_intensity method - parameters['turbulence_intensity'] = 0.5 - wind_speed_values_exp = pd.Series([6.0, 7.0, 8.0, 9.0, 10.0], - name='wind_speed') - power_values_exp = pd.Series([ - 1141906.9806766496, 1577536.8085282773, 1975480.993355767, - 2314059.4022704284, 2590216.6802602503], name='value') - smoothed_curve_exp = pd.DataFrame(data=pd.concat([ - wind_speed_values_exp, power_values_exp], axis=1)) + parameters["turbulence_intensity"] = 0.5 + wind_speed_values_exp = pd.Series( + [6.0, 7.0, 8.0, 9.0, 10.0], name="wind_speed" + ) + power_values_exp = pd.Series( + [ + 1141906.9806766496, + 1577536.8085282773, + 1975480.993355767, + 2314059.4022704284, + 2590216.6802602503, + ], + name="value", + ) + smoothed_curve_exp = pd.DataFrame( + data=pd.concat([wind_speed_values_exp, power_values_exp], axis=1) + ) smoothed_curve_exp.index = np.arange(5, 10, 1) - assert_frame_equal(smooth_power_curve(**parameters)[5:10], - smoothed_curve_exp) + assert_frame_equal( + smooth_power_curve(**parameters)[5:10], smoothed_curve_exp + ) # Test Staffel_Pfenninger method - parameters['standard_deviation_method'] = 'Staffell_Pfenninger' - power_values_exp = pd.Series([ - 929405.1348918702, 1395532.5468724659, 1904826.6851982325, - 2402659.118305521, 2844527.1732449625], name='value') + parameters["standard_deviation_method"] = "Staffell_Pfenninger" + power_values_exp = pd.Series( + [ + 929405.1348918702, + 1395532.5468724659, + 1904826.6851982325, + 2402659.118305521, + 2844527.1732449625, + ], + name="value", + ) smoothed_curve_exp = pd.DataFrame( - data=pd.concat([wind_speed_values_exp, power_values_exp], axis=1)) + data=pd.concat([wind_speed_values_exp, power_values_exp], axis=1) + ) smoothed_curve_exp.index = np.arange(5, 10, 1) - assert_frame_equal(smooth_power_curve(**parameters)[5:10], - smoothed_curve_exp) + assert_frame_equal( + smooth_power_curve(**parameters)[5:10], smoothed_curve_exp + ) # Raise ValueError - misspelling with pytest.raises(ValueError): - parameters['standard_deviation_method'] = 'misspelled' + parameters["standard_deviation_method"] = "misspelled" smooth_power_curve(**parameters) def test_wake_losses_to_power_curve(self): test_curve = wt.WindTurbine(**self.test_turbine).power_curve - parameters = {'power_curve_wind_speeds': test_curve['wind_speed'], - 'power_curve_values': test_curve['value'], - 'wind_farm_efficiency': 0.9} + parameters = { + "power_curve_wind_speeds": test_curve["wind_speed"], + "power_curve_values": test_curve["value"], + "wind_farm_efficiency": 0.9, + } # Test constant efficiency power_curve_exp = test_curve.copy(deep=True) - power_curve_exp['value'] = power_curve_exp['value'].values * 0.9 - assert_frame_equal(wake_losses_to_power_curve(**parameters), - power_curve_exp) + power_curve_exp["value"] = power_curve_exp["value"].values * 0.9 + assert_frame_equal( + wake_losses_to_power_curve(**parameters), power_curve_exp + ) # Test efficiency curve - parameters['wind_farm_efficiency'] = pd.DataFrame( - pd.concat([pd.Series(np.arange(1, 26, 1)), - pd.Series([ - 1.0, 1.0, 1.0, 0.84, 0.85, 0.86, 0.85, 0.85, 0.85, - 0.86, 0.87, 0.89, 0.92, 0.95, 0.95, 0.96, 0.99, - 0.95, 0.98, 0.97, 0.99, 1.0, 1.0, 1.0, 1.0])], - axis=1)) - parameters['wind_farm_efficiency'].columns = ['wind_speed', - 'efficiency'] + parameters["wind_farm_efficiency"] = pd.DataFrame( + pd.concat( + [ + pd.Series(np.arange(1, 26, 1)), + pd.Series( + [ + 1.0, + 1.0, + 1.0, + 0.84, + 0.85, + 0.86, + 0.85, + 0.85, + 0.85, + 0.86, + 0.87, + 0.89, + 0.92, + 0.95, + 0.95, + 0.96, + 0.99, + 0.95, + 0.98, + 0.97, + 0.99, + 1.0, + 1.0, + 1.0, + 1.0, + ] + ), + ], + axis=1, + ) + ) + parameters["wind_farm_efficiency"].columns = [ + "wind_speed", + "efficiency", + ] power_curve_exp = test_curve.copy(deep=True) - power_curve_exp['value'] = ( - power_curve_exp['value'].values * parameters[ - 'wind_farm_efficiency']['efficiency']) - assert_frame_equal(wake_losses_to_power_curve(**parameters), - power_curve_exp) + power_curve_exp["value"] = ( + power_curve_exp["value"].values + * parameters["wind_farm_efficiency"]["efficiency"] + ) + assert_frame_equal( + wake_losses_to_power_curve(**parameters), power_curve_exp + ) # Raise TypeError if wind farm efficiency is of wrong type with pytest.raises(TypeError): - parameters['wind_farm_efficiency'] = 1 + parameters["wind_farm_efficiency"] = 1 wake_losses_to_power_curve(**parameters) diff --git a/tests/test_power_output.py b/tests/test_power_output.py index 4476da9c..d29d6e35 100644 --- a/tests/test_power_output.py +++ b/tests/test_power_output.py @@ -9,88 +9,103 @@ from numpy.testing import assert_allclose from pandas.util.testing import assert_series_equal -from windpowerlib.power_output import (power_coefficient_curve, - power_curve, - power_curve_density_correction) +from windpowerlib.power_output import ( + power_coefficient_curve, + power_curve, + power_curve_density_correction, +) class TestPowerOutput: - def test_power_coefficient_curve(self): - parameters = {'wind_speed': pd.Series(data=[2.0, 5.5, 7.0]), - 'density': pd.Series(data=[1.3, 1.3, 1.3]), - 'rotor_diameter': 80, - 'power_coefficient_curve_wind_speeds': - pd.Series([4.0, 5.0, 6.0]), - 'power_coefficient_curve_values': - pd.Series([0.3, 0.4, 0.5])} + parameters = { + "wind_speed": pd.Series(data=[2.0, 5.5, 7.0]), + "density": pd.Series(data=[1.3, 1.3, 1.3]), + "rotor_diameter": 80, + "power_coefficient_curve_wind_speeds": pd.Series([4.0, 5.0, 6.0]), + "power_coefficient_curve_values": pd.Series([0.3, 0.4, 0.5]), + } # Test wind_speed as pd.Series with density and power_coefficient_curve # as pd.Series and np.array - power_output_exp = pd.Series(data=[0.0, 244615.399, 0.0], - name='feedin_power_plant') - assert_series_equal(power_coefficient_curve(**parameters), - power_output_exp) - parameters['density'] = np.array(parameters['density']) - assert_series_equal(power_coefficient_curve(**parameters), - power_output_exp) - parameters['power_coefficient_curve_values'] = np.array( - parameters['power_coefficient_curve_values']) - parameters['power_coefficient_curve_wind_speeds'] = np.array( - parameters['power_coefficient_curve_wind_speeds']) - assert_series_equal(power_coefficient_curve(**parameters), - power_output_exp) + power_output_exp = pd.Series( + data=[0.0, 244615.399, 0.0], name="feedin_power_plant" + ) + assert_series_equal( + power_coefficient_curve(**parameters), power_output_exp + ) + parameters["density"] = np.array(parameters["density"]) + assert_series_equal( + power_coefficient_curve(**parameters), power_output_exp + ) + parameters["power_coefficient_curve_values"] = np.array( + parameters["power_coefficient_curve_values"] + ) + parameters["power_coefficient_curve_wind_speeds"] = np.array( + parameters["power_coefficient_curve_wind_speeds"] + ) + assert_series_equal( + power_coefficient_curve(**parameters), power_output_exp + ) # Test wind_speed as np.array with density and power_coefficient_curve # as np.array and pd.Series power_output_exp = np.array([0.0, 244615.399, 0.0]) - parameters['wind_speed'] = np.array(parameters['wind_speed']) - assert_allclose(power_coefficient_curve(**parameters), - power_output_exp) + parameters["wind_speed"] = np.array(parameters["wind_speed"]) + assert_allclose( + power_coefficient_curve(**parameters), power_output_exp + ) assert isinstance(power_coefficient_curve(**parameters), np.ndarray) - parameters['density'] = pd.Series(data=parameters['density']) - assert_allclose(power_coefficient_curve(**parameters), - power_output_exp) + parameters["density"] = pd.Series(data=parameters["density"]) + assert_allclose( + power_coefficient_curve(**parameters), power_output_exp + ) assert isinstance(power_coefficient_curve(**parameters), np.ndarray) - parameters['power_coefficient_curve_wind_speeds'] = pd.Series( - data=parameters['power_coefficient_curve_wind_speeds']) - parameters['power_coefficient_curve_values'] = pd.Series( - data=parameters['power_coefficient_curve_values']) - assert_allclose(power_coefficient_curve(**parameters), - power_output_exp) + parameters["power_coefficient_curve_wind_speeds"] = pd.Series( + data=parameters["power_coefficient_curve_wind_speeds"] + ) + parameters["power_coefficient_curve_values"] = pd.Series( + data=parameters["power_coefficient_curve_values"] + ) + assert_allclose( + power_coefficient_curve(**parameters), power_output_exp + ) assert isinstance(power_coefficient_curve(**parameters), np.ndarray) def test_power_curve(self): - parameters = {'wind_speed': pd.Series(data=[2.0, 5.5, 7.0]), - 'density': pd.Series(data=[1.3, 1.3, 1.3]), - 'density_correction': False, - 'power_curve_wind_speeds': - pd.Series([4.0, 5.0, 6.0]), - 'power_curve_values': - pd.Series([300, 400, 500]) - } + parameters = { + "wind_speed": pd.Series(data=[2.0, 5.5, 7.0]), + "density": pd.Series(data=[1.3, 1.3, 1.3]), + "density_correction": False, + "power_curve_wind_speeds": pd.Series([4.0, 5.0, 6.0]), + "power_curve_values": pd.Series([300, 400, 500]), + } # Tests without density correction: # Test wind_speed as pd.Series and power_curve as pd.Series and # np.array - power_output_exp = pd.Series(data=[0.0, 450.0, 0.0], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[0.0, 450.0, 0.0], name="feedin_power_plant" + ) + assert_series_equal(power_curve(**parameters), power_output_exp) + parameters["power_curve_values"] = np.array( + parameters["power_curve_values"] + ) + parameters["power_curve_wind_speeds"] = np.array( + parameters["power_curve_wind_speeds"] + ) assert_series_equal(power_curve(**parameters), power_output_exp) - parameters['power_curve_values'] = np.array( - parameters['power_curve_values']) - parameters['power_curve_wind_speeds'] = np.array( - parameters['power_curve_wind_speeds']) - assert_series_equal(power_curve(**parameters), - power_output_exp) # Test wind_speed as np.array and power_curve as pd.Series and np.array power_output_exp = np.array([0.0, 450.0, 0.0]) - parameters['wind_speed'] = np.array(parameters['wind_speed']) + parameters["wind_speed"] = np.array(parameters["wind_speed"]) assert_allclose(power_curve(**parameters), power_output_exp) assert isinstance(power_curve(**parameters), np.ndarray) - parameters['power_curve_wind_speeds'] = pd.Series( - data=parameters['power_curve_wind_speeds']) - parameters['power_curve_values'] = pd.Series( - data=parameters['power_curve_values']) + parameters["power_curve_wind_speeds"] = pd.Series( + data=parameters["power_curve_wind_speeds"] + ) + parameters["power_curve_values"] = pd.Series( + data=parameters["power_curve_values"] + ) assert_allclose(power_curve(**parameters), power_output_exp) assert isinstance(power_curve(**parameters), np.ndarray) @@ -98,98 +113,110 @@ def test_power_curve(self): # Test wind_speed as np.array with density and power_curve as pd.Series # and np.array power_output_exp = np.array([0.0, 461.00290572, 0.0]) - parameters['density_correction'] = True + parameters["density_correction"] = True assert_allclose(power_curve(**parameters), power_output_exp) assert isinstance(power_curve(**parameters), np.ndarray) - parameters['density'] = np.array(parameters['density']) + parameters["density"] = np.array(parameters["density"]) assert_allclose(power_curve(**parameters), power_output_exp) assert isinstance(power_curve(**parameters), np.ndarray) - parameters['power_curve_values'] = np.array( - parameters['power_curve_values']) - parameters['power_curve_wind_speeds'] = np.array( - parameters['power_curve_wind_speeds']) + parameters["power_curve_values"] = np.array( + parameters["power_curve_values"] + ) + parameters["power_curve_wind_speeds"] = np.array( + parameters["power_curve_wind_speeds"] + ) assert_allclose(power_curve(**parameters), power_output_exp) assert isinstance(power_curve(**parameters), np.ndarray) # Test wind_speed as pd.Series with density and power_curve as # np. array and pd.Series - power_output_exp = pd.Series(data=[0.0, 461.00290572, 0.0], - name='feedin_power_plant') - parameters['wind_speed'] = pd.Series(data=parameters['wind_speed']) + power_output_exp = pd.Series( + data=[0.0, 461.00290572, 0.0], name="feedin_power_plant" + ) + parameters["wind_speed"] = pd.Series(data=parameters["wind_speed"]) + assert_series_equal(power_curve(**parameters), power_output_exp) + parameters["density"] = pd.Series(data=parameters["density"]) + assert_series_equal(power_curve(**parameters), power_output_exp) + parameters["power_curve_wind_speeds"] = pd.Series( + data=parameters["power_curve_wind_speeds"] + ) + parameters["power_curve_values"] = pd.Series( + data=parameters["power_curve_values"] + ) assert_series_equal(power_curve(**parameters), power_output_exp) - parameters['density'] = pd.Series(data=parameters['density']) - assert_series_equal(power_curve(**parameters), - power_output_exp) - parameters['power_curve_wind_speeds'] = pd.Series( - data=parameters['power_curve_wind_speeds']) - parameters['power_curve_values'] = pd.Series( - data=parameters['power_curve_values']) - assert_series_equal(power_curve(**parameters), - power_output_exp) # Raise TypeErrors due to wrong type of `density_correction` with pytest.raises(TypeError): - parameters['density'] = 'wrong_type' + parameters["density"] = "wrong_type" power_curve(**parameters) def test_power_curve_density_correction(self): - parameters = {'wind_speed': pd.Series(data=[2.0, 5.5, 7.0]), - 'density': pd.Series(data=[1.3, 1.3, 1.3]), - 'power_curve_wind_speeds': - pd.Series([4.0, 5.0, 6.0]), - 'power_curve_values': - pd.Series([300, 400, 500]) - } + parameters = { + "wind_speed": pd.Series(data=[2.0, 5.5, 7.0]), + "density": pd.Series(data=[1.3, 1.3, 1.3]), + "power_curve_wind_speeds": pd.Series([4.0, 5.0, 6.0]), + "power_curve_values": pd.Series([300, 400, 500]), + } # Test wind_speed as pd.Series with density and power_curve as # pd.Series and np.array - power_output_exp = pd.Series(data=[0.0, 461.00290572, 0.0], - name='feedin_power_plant') - assert_series_equal(power_curve_density_correction(**parameters), - power_output_exp) - parameters['density'] = np.array(parameters['density']) - assert_series_equal(power_curve_density_correction(**parameters), - power_output_exp) - parameters['power_curve_values'] = np.array( - parameters['power_curve_values']) - parameters['power_curve_wind_speeds'] = np.array( - parameters['power_curve_wind_speeds']) - assert_series_equal(power_curve_density_correction(**parameters), - power_output_exp) + power_output_exp = pd.Series( + data=[0.0, 461.00290572, 0.0], name="feedin_power_plant" + ) + assert_series_equal( + power_curve_density_correction(**parameters), power_output_exp + ) + parameters["density"] = np.array(parameters["density"]) + assert_series_equal( + power_curve_density_correction(**parameters), power_output_exp + ) + parameters["power_curve_values"] = np.array( + parameters["power_curve_values"] + ) + parameters["power_curve_wind_speeds"] = np.array( + parameters["power_curve_wind_speeds"] + ) + assert_series_equal( + power_curve_density_correction(**parameters), power_output_exp + ) # Test wind_speed as np.array with density and power_curve as np.array # and pd.Series - parameters['wind_speed'] = np.array(parameters['wind_speed']) + parameters["wind_speed"] = np.array(parameters["wind_speed"]) power_output_exp = np.array([0.0, 461.00290572, 0.0]) - assert_allclose(power_curve_density_correction(**parameters), - power_output_exp) + assert_allclose( + power_curve_density_correction(**parameters), power_output_exp + ) assert isinstance(power_curve(**parameters), np.ndarray) - parameters['density'] = pd.Series(data=parameters['density']) - assert_allclose(power_curve_density_correction(**parameters), - power_output_exp) + parameters["density"] = pd.Series(data=parameters["density"]) + assert_allclose( + power_curve_density_correction(**parameters), power_output_exp + ) assert isinstance(power_curve(**parameters), np.ndarray) - parameters['power_curve_wind_speeds'] = pd.Series( - data=parameters['power_curve_wind_speeds']) - parameters['power_curve_values'] = pd.Series( - data=parameters['power_curve_values']) - assert_allclose(power_curve_density_correction(**parameters), - power_output_exp) + parameters["power_curve_wind_speeds"] = pd.Series( + data=parameters["power_curve_wind_speeds"] + ) + parameters["power_curve_values"] = pd.Series( + data=parameters["power_curve_values"] + ) + assert_allclose( + power_curve_density_correction(**parameters), power_output_exp + ) assert isinstance(power_curve(**parameters), np.ndarray) # Raise TypeError due to density is None with pytest.raises(TypeError): - parameters['density'] = None + parameters["density"] = None power_curve_density_correction(**parameters) def test_wrong_spelling_density_correction(self): - parameters = {'wind_speed': pd.Series(data=[2.0, 5.5, 7.0]), - 'density': pd.Series(data=[1.3, 1.3, 1.3]), - 'power_curve_wind_speeds': - pd.Series([4.0, 5.0, 6.0]), - 'power_curve_values': - pd.Series([300, 400, 500]) - } + parameters = { + "wind_speed": pd.Series(data=[2.0, 5.5, 7.0]), + "density": pd.Series(data=[1.3, 1.3, 1.3]), + "power_curve_wind_speeds": pd.Series([4.0, 5.0, 6.0]), + "power_curve_values": pd.Series([300, 400, 500]), + } msg = "is an invalid type. `density_correction` must be Boolean" with pytest.raises(TypeError, match=msg): - parameters['density_correction'] = None + parameters["density_correction"] = None power_curve(**parameters) diff --git a/tests/test_temperature.py b/tests/test_temperature.py index 11427df2..d0e2ba4b 100644 --- a/tests/test_temperature.py +++ b/tests/test_temperature.py @@ -12,20 +12,23 @@ class TestTemperature: - def test_linear_gradient(self): """Test temperature as pd.Series""" - parameters = {'temperature': pd.Series(data=[267, 268]), - 'temperature_height': 2, - 'hub_height': 100} + parameters = { + "temperature": pd.Series(data=[267, 268]), + "temperature_height": 2, + "hub_height": 100, + } temp_hub_exp = pd.Series(data=[266.363, 267.36300]) assert_series_equal(linear_gradient(**parameters), temp_hub_exp) def test_temperature_as_np_array(self): """Test temperature as np.array""" - parameters = {'temperature': np.array([267, 268]), - 'temperature_height': 2, - 'hub_height': 100} + parameters = { + "temperature": np.array([267, 268]), + "temperature_height": 2, + "hub_height": 100, + } temp_hub_exp = np.array([266.363, 267.36300]) assert_array_equal(linear_gradient(**parameters), temp_hub_exp) assert isinstance(linear_gradient(**parameters), np.ndarray) diff --git a/tests/test_tools.py b/tests/test_tools.py index e51584c9..2392d89d 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -6,19 +6,24 @@ import pandas as pd from pandas.util.testing import assert_series_equal -from windpowerlib.tools import (linear_interpolation_extrapolation, - logarithmic_interpolation_extrapolation) +from windpowerlib.tools import ( + linear_interpolation_extrapolation, + logarithmic_interpolation_extrapolation, +) class TestTools: - @classmethod def setup_class(cls): - cls.parameters = {'target_height': 80} - cls.df = pd.DataFrame(data={10: [2.0, 2.0, 3.0], - 80: [4.0, 5.0, 6.0], - 200: [5.0, 8.0, 10.0]}, - index=[0, 1, 2]) + cls.parameters = {"target_height": 80} + cls.df = pd.DataFrame( + data={ + 10: [2.0, 2.0, 3.0], + 80: [4.0, 5.0, 6.0], + 200: [5.0, 8.0, 10.0], + }, + index=[0, 1, 2], + ) def test_linear_target_height_is_equal_to_given_height(self): """ @@ -26,8 +31,10 @@ def test_linear_target_height_is_equal_to_given_height(self): to height given in a column of the DataFrame. """ exp_output = pd.Series(data=[4.0, 5.0, 6.0]) - assert_series_equal(linear_interpolation_extrapolation( - self.df, **self.parameters), exp_output) + assert_series_equal( + linear_interpolation_extrapolation(self.df, **self.parameters), + exp_output, + ) def test_linear_target_height_is_between_given_heights(self): """ @@ -35,14 +42,18 @@ def test_linear_target_height_is_between_given_heights(self): heights given in the columns of the DataFrame """ exp_output = pd.Series(data=[4.5, 6.5, 8.0]) - self.parameters['target_height'] = 140 - assert_series_equal(linear_interpolation_extrapolation( - self.df, **self.parameters), exp_output) + self.parameters["target_height"] = 140 + assert_series_equal( + linear_interpolation_extrapolation(self.df, **self.parameters), + exp_output, + ) exp_output = pd.Series(data=[4.285714, 5.428571, 6.428571]) - self.parameters['target_height'] = 90 - assert_series_equal(linear_interpolation_extrapolation( - self.df, **self.parameters), exp_output) + self.parameters["target_height"] = 90 + assert_series_equal( + linear_interpolation_extrapolation(self.df, **self.parameters), + exp_output, + ) def test_linear_target_height_is_greater_than_the_given_heights(self): """ @@ -50,9 +61,11 @@ def test_linear_target_height_is_greater_than_the_given_heights(self): than the heights given in the columns of the DataFrame """ exp_output = pd.Series(data=[5.333333, 9.0, 11.333333]) - self.parameters['target_height'] = 240 - assert_series_equal(linear_interpolation_extrapolation( - self.df, **self.parameters), exp_output) + self.parameters["target_height"] = 240 + assert_series_equal( + linear_interpolation_extrapolation(self.df, **self.parameters), + exp_output, + ) def test_linear_target_height_is_smaller_than_the_given_heights(self): """ @@ -60,42 +73,61 @@ def test_linear_target_height_is_smaller_than_the_given_heights(self): than the heights given in the columns of the DataFrame """ exp_output = pd.Series(data=[1.857143, 1.785714, 2.785714]) - self.parameters['target_height'] = 5 - assert_series_equal(linear_interpolation_extrapolation( - self.df, **self.parameters), exp_output) + self.parameters["target_height"] = 5 + assert_series_equal( + linear_interpolation_extrapolation(self.df, **self.parameters), + exp_output, + ) def test_logarithmic_interpolation_extrapolation(self): - parameters = {'target_height': 80} - df = pd.DataFrame(data={10: [2.0, 2.0, 3.0], - 80: [4.0, 5.0, 6.0], - 200: [5.0, 8.0, 10.0]}, - index=[0, 1, 2]) + parameters = {"target_height": 80} + df = pd.DataFrame( + data={ + 10: [2.0, 2.0, 3.0], + 80: [4.0, 5.0, 6.0], + 200: [5.0, 8.0, 10.0], + }, + index=[0, 1, 2], + ) # target_height is equal to height given in a column of the DataFrame exp_output = pd.Series(data=[4.0, 5.0, 6.0]) - assert_series_equal(logarithmic_interpolation_extrapolation( - df, **parameters), exp_output) + assert_series_equal( + logarithmic_interpolation_extrapolation(df, **parameters), + exp_output, + ) # target_height is between heights given in the columns of the # DataFrame exp_output = pd.Series( - data=[4.61074042165, 6.83222126494, 8.44296168659]) - parameters['target_height'] = 140 - assert_series_equal(logarithmic_interpolation_extrapolation( - df, **parameters), exp_output) + data=[4.61074042165, 6.83222126494, 8.44296168659] + ) + parameters["target_height"] = 140 + assert_series_equal( + logarithmic_interpolation_extrapolation(df, **parameters), + exp_output, + ) exp_output = pd.Series( - data=[4.11328333429, 5.16992500144, 6.16992500144]) - parameters['target_height'] = 90 - assert_series_equal(logarithmic_interpolation_extrapolation( - df, **parameters), exp_output) + data=[4.11328333429, 5.16992500144, 6.16992500144] + ) + parameters["target_height"] = 90 + assert_series_equal( + logarithmic_interpolation_extrapolation(df, **parameters), + exp_output, + ) # target_height is greater than the heights given in the columns of the # DataFrame exp_output = pd.Series( - data=[5.19897784672, 8.59693354015, 10.7959113869]) - parameters['target_height'] = 240 - assert_series_equal(logarithmic_interpolation_extrapolation( - df, **parameters), exp_output) + data=[5.19897784672, 8.59693354015, 10.7959113869] + ) + parameters["target_height"] = 240 + assert_series_equal( + logarithmic_interpolation_extrapolation(df, **parameters), + exp_output, + ) # target_height is smaller than the heights given in the columns of the # DataFrame exp_output = pd.Series(data=[1.33333333333, 1.0, 2.0]) - parameters['target_height'] = 5 - assert_series_equal(logarithmic_interpolation_extrapolation( - df, **parameters), exp_output) + parameters["target_height"] = 5 + assert_series_equal( + logarithmic_interpolation_extrapolation(df, **parameters), + exp_output, + ) diff --git a/tests/test_turbine_cluster_modelchain.py b/tests/test_turbine_cluster_modelchain.py index 6e9356be..2b20e151 100644 --- a/tests/test_turbine_cluster_modelchain.py +++ b/tests/test_turbine_cluster_modelchain.py @@ -15,7 +15,6 @@ class TestTurbineClusterModelChain: - @classmethod def setup_class(self): temperature_2m = np.array([[267], [268]]) @@ -25,144 +24,192 @@ def setup_class(self): wind_speed_10m = np.array([[5.0], [6.5]]) roughness_length = np.array([[0.15], [0.15]]) self.weather_df = pd.DataFrame( - np.hstack((temperature_2m, - temperature_10m, - pressure_0m, - wind_speed_8m, - wind_speed_10m, - roughness_length)), - index=[0, 1], - columns=[np.array(['temperature', - 'temperature', - 'pressure', - 'wind_speed', - 'wind_speed', - 'roughness_length']), - np.array([2, 10, 0, 8, 10, 0])]) - self.test_turbine = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'E-126/4200'} - self.test_turbine_2 = {'hub_height': 90, - 'rotor_diameter': 60, - 'turbine_type': 'V90/2000', - 'nominal_power': 2000000.0} - self.test_farm = {'wind_turbine_fleet': [ - {'wind_turbine': - wt.WindTurbine(**self.test_turbine), - 'number_of_turbines': 3}]} - self.test_farm_2 = {'name': 'test farm', - 'wind_turbine_fleet': - [{'wind_turbine': - wt.WindTurbine(**self.test_turbine), - 'number_of_turbines': 3}, - {'wind_turbine': - wt.WindTurbine(**self.test_turbine_2), - 'number_of_turbines': 3}]} - self.test_cluster = {'name': 'example_cluster', - 'wind_farms': [wf.WindFarm(**self.test_farm), - wf.WindFarm(**self.test_farm_2)]} + np.hstack( + ( + temperature_2m, + temperature_10m, + pressure_0m, + wind_speed_8m, + wind_speed_10m, + roughness_length, + ) + ), + index=[0, 1], + columns=[ + np.array( + [ + "temperature", + "temperature", + "pressure", + "wind_speed", + "wind_speed", + "roughness_length", + ] + ), + np.array([2, 10, 0, 8, 10, 0]), + ], + ) + self.test_turbine = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "E-126/4200", + } + self.test_turbine_2 = { + "hub_height": 90, + "rotor_diameter": 60, + "turbine_type": "V90/2000", + "nominal_power": 2000000.0, + } + self.test_farm = { + "wind_turbine_fleet": [ + { + "wind_turbine": wt.WindTurbine(**self.test_turbine), + "number_of_turbines": 3, + } + ] + } + self.test_farm_2 = { + "name": "test farm", + "wind_turbine_fleet": [ + { + "wind_turbine": wt.WindTurbine(**self.test_turbine), + "number_of_turbines": 3, + }, + { + "wind_turbine": wt.WindTurbine(**self.test_turbine_2), + "number_of_turbines": 3, + }, + ], + } + self.test_cluster = { + "name": "example_cluster", + "wind_farms": [ + wf.WindFarm(**self.test_farm), + wf.WindFarm(**self.test_farm_2), + ], + } def test_run_model(self): - parameters = {'wake_losses_model': 'dena_mean', - 'smoothing': False, - 'standard_deviation_method': 'turbulence_intensity', - 'smoothing_order': 'wind_farm_power_curves'} + parameters = { + "wake_losses_model": "dena_mean", + "smoothing": False, + "standard_deviation_method": "turbulence_intensity", + "smoothing_order": "wind_farm_power_curves", + } # Test modelchain with default values - power_output_exp = pd.Series(data=[4198361.4830405945, - 8697966.121234536], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[4198361.4830405945, 8697966.121234536], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=wf.WindFarm(**self.test_farm), **parameters) + power_plant=wf.WindFarm(**self.test_farm), **parameters + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) # Test constant efficiency - parameters['wake_losses_model'] = 'wind_farm_efficiency' + parameters["wake_losses_model"] = "wind_farm_efficiency" test_wind_farm = wf.WindFarm(**self.test_farm) test_wind_farm.efficiency = 0.9 - power_output_exp = pd.Series(data=[4420994.806920091, - 8516983.651623568], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[4420994.806920091, 8516983.651623568], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=test_wind_farm, **parameters) + power_plant=test_wind_farm, **parameters + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) # Test smoothing - parameters['smoothing'] = 'True' + parameters["smoothing"] = "True" test_wind_farm = wf.WindFarm(**self.test_farm) test_wind_farm.efficiency = 0.9 - power_output_exp = pd.Series(data=[4581109.03847444, - 8145581.914240712], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[4581109.03847444, 8145581.914240712], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=test_wind_farm, **parameters) + power_plant=test_wind_farm, **parameters + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) # Test wind farm with different turbine types (smoothing) test_wind_farm = wf.WindFarm(**self.test_farm_2) test_wind_farm.efficiency = 0.9 - power_output_exp = pd.Series(data=[6777087.9658657005, - 12180374.036660176], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[6777087.9658657005, 12180374.036660176], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=test_wind_farm, **parameters) + power_plant=test_wind_farm, **parameters + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) # Test other smoothing order - parameters['smoothing_order'] = 'turbine_power_curves' + parameters["smoothing_order"] = "turbine_power_curves" test_wind_farm = wf.WindFarm(**self.test_farm_2) test_wind_farm.efficiency = 0.9 - power_output_exp = pd.Series(data=[6790706.001026006, - 12179417.461328149], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[6790706.001026006, 12179417.461328149], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=test_wind_farm, **parameters) + power_plant=test_wind_farm, **parameters + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) def test_run_model_turbine_cluster(self): - parameters = {'wake_losses_model': 'dena_mean', - 'smoothing': False, - 'standard_deviation_method': 'turbulence_intensity', - 'smoothing_order': 'wind_farm_power_curves'} + parameters = { + "wake_losses_model": "dena_mean", + "smoothing": False, + "standard_deviation_method": "turbulence_intensity", + "smoothing_order": "wind_farm_power_curves", + } # Test modelchain with default values - power_output_exp = pd.Series(data=[10363047.755401008, - 21694496.68221325], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[10363047.755401008, 21694496.68221325], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( power_plant=wtc.WindTurbineCluster(**self.test_cluster), - **parameters) + **parameters, + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) # Test constant efficiency - parameters['wake_losses_model'] = 'wind_farm_efficiency' + parameters["wake_losses_model"] = "wind_farm_efficiency" test_cluster = wtc.WindTurbineCluster(**self.test_cluster) for farm in test_cluster.wind_farms: farm.efficiency = 0.9 - power_output_exp = pd.Series(data=[10920128.570572512, - 21273144.336885825], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[10920128.570572512, 21273144.336885825], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=test_cluster, **parameters) + power_plant=test_cluster, **parameters + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) # Test smoothing - parameters['smoothing'] = 'True' + parameters["smoothing"] = "True" test_cluster = wtc.WindTurbineCluster(**self.test_cluster) for farm in test_cluster.wind_farms: farm.efficiency = 0.9 - power_output_exp = pd.Series(data=[11360309.77979467, - 20328652.64490018], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[11360309.77979467, 20328652.64490018], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=test_cluster, **parameters) + power_plant=test_cluster, **parameters + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) @@ -170,24 +217,28 @@ def test_run_model_turbine_cluster(self): test_cluster = wtc.WindTurbineCluster(**self.test_cluster) for farm in test_cluster.wind_farms: farm.efficiency = 0.9 - power_output_exp = pd.Series(data=[11360309.77979467, - 20328652.64490018], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[11360309.77979467, 20328652.64490018], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=test_cluster, **parameters) + power_plant=test_cluster, **parameters + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) # Test other smoothing order - parameters['smoothing_order'] = 'turbine_power_curves' + parameters["smoothing_order"] = "turbine_power_curves" test_cluster = wtc.WindTurbineCluster(**self.test_cluster) for farm in test_cluster.wind_farms: farm.efficiency = 0.9 - power_output_exp = pd.Series(data=[11373183.797085874, - 20325877.105744187], - name='feedin_power_plant') + power_output_exp = pd.Series( + data=[11373183.797085874, 20325877.105744187], + name="feedin_power_plant", + ) test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=test_cluster, **parameters) + power_plant=test_cluster, **parameters + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) @@ -196,79 +247,114 @@ def test_error_raising(self): # Raise ValueError when aggregated wind farm power curve needs to be # calculated but turbine does not have a power curve test_turbine_data = { - 'hub_height': 100, - 'rotor_diameter': 98, - 'turbine_type': 'V90/2000'} + "hub_height": 100, + "rotor_diameter": 98, + "turbine_type": "V90/2000", + } test_turbine = wt.WindTurbine(**test_turbine_data) test_turbine.power_curve = True - test_farm = {'wind_turbine_fleet': - [{'wind_turbine': - wt.WindTurbine(**self.test_turbine), - 'number_of_turbines': 3}, - {'wind_turbine': - test_turbine, - 'number_of_turbines': 3}]} + test_farm = { + "wind_turbine_fleet": [ + { + "wind_turbine": wt.WindTurbine(**self.test_turbine), + "number_of_turbines": 3, + }, + {"wind_turbine": test_turbine, "number_of_turbines": 3}, + ] + } test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=wf.WindFarm(**test_farm)) + power_plant=wf.WindFarm(**test_farm) + ) with pytest.raises(ValueError): test_tc_mc.run_model(self.weather_df) # Raise ValueError when neither turbulence intensity nor roughness # length are provided to apply power curve smoothing with standard # deviation method 'turbulence_intensity' - parameters = {'smoothing': True, - 'standard_deviation_method': 'turbulence_intensity'} + parameters = { + "smoothing": True, + "standard_deviation_method": "turbulence_intensity", + } test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=wf.WindFarm(**self.test_farm), **parameters) + power_plant=wf.WindFarm(**self.test_farm), **parameters + ) weather_df = self.weather_df.copy() - weather_df.pop('roughness_length') + weather_df.pop("roughness_length") with pytest.raises(ValueError): test_tc_mc.run_model(weather_df) def test_ignore_wake_losses(self): """Run model without wake losses.""" - parameters = {'wake_losses_model': None, - 'smoothing': False, - 'standard_deviation_method': 'turbulence_intensity', - 'smoothing_order': 'wind_farm_power_curves'} + parameters = { + "wake_losses_model": None, + "smoothing": False, + "standard_deviation_method": "turbulence_intensity", + "smoothing_order": "wind_farm_power_curves", + } # Test modelchain with default values test_tc_mc = tc_mc.TurbineClusterModelChain( power_plant=wtc.WindTurbineCluster(**self.test_cluster), - **parameters) + **parameters, + ) test_tc_mc.run_model(self.weather_df) def test_wind_turbine_cluster_repr_with_name(self): """Test string representation of WindTurbineCluster with a name.""" - assert 'Wind turbine cluster:' in repr( - wtc.WindTurbineCluster(**self.test_cluster)) + assert "Wind turbine cluster:" in repr( + wtc.WindTurbineCluster(**self.test_cluster) + ) def test_wind_turbine_cluster_repr_without_name(self): """Test string representation of WindTurbineCluster without a name.""" - test_cluster = {'wind_farms': [wf.WindFarm(**self.test_farm), - wf.WindFarm(**self.test_farm_2)]} - assert 'Wind turbine cluster with:' in repr( - wtc.WindTurbineCluster(**test_cluster)) + test_cluster = { + "wind_farms": [ + wf.WindFarm(**self.test_farm), + wf.WindFarm(**self.test_farm_2), + ] + } + assert "Wind turbine cluster with:" in repr( + wtc.WindTurbineCluster(**test_cluster) + ) def test_tc_modelchain_with_power_curve_as_dict(self): """Test power curves as dict in TurbineClusterModelChain.run_model()""" - my_turbine = {'nominal_power': 3e6, 'hub_height': 105, - 'power_curve': { - 'value': [p * 1000 for p in [ - 0.0, 26.0, 180.0, 1500.0, 3000.0, 3000.0]], - 'wind_speed': [0.0, 3.0, 5.0, 10.0, 15.0, 25.0]}} - my_farm = {'wind_turbine_fleet': - [{'wind_turbine': wt.WindTurbine(**my_turbine), - 'number_of_turbines': 3}, - {'wind_turbine': wt.WindTurbine(**self.test_turbine), - 'number_of_turbines': 3}]} - my_cluster = {'wind_farms': [wf.WindFarm(**my_farm), - wf.WindFarm(**self.test_farm)]} - power_output_exp = pd.Series(data=[10853277.966972714, - 21731814.593688786], - name='feedin_power_plant') + my_turbine = { + "nominal_power": 3e6, + "hub_height": 105, + "power_curve": { + "value": [ + p * 1000 + for p in [0.0, 26.0, 180.0, 1500.0, 3000.0, 3000.0] + ], + "wind_speed": [0.0, 3.0, 5.0, 10.0, 15.0, 25.0], + }, + } + my_farm = { + "wind_turbine_fleet": [ + { + "wind_turbine": wt.WindTurbine(**my_turbine), + "number_of_turbines": 3, + }, + { + "wind_turbine": wt.WindTurbine(**self.test_turbine), + "number_of_turbines": 3, + }, + ] + } + my_cluster = { + "wind_farms": [ + wf.WindFarm(**my_farm), + wf.WindFarm(**self.test_farm), + ] + } + power_output_exp = pd.Series( + data=[10853277.966972714, 21731814.593688786], + name="feedin_power_plant", + ) # run model with my_cluster test_tc_mc = tc_mc.TurbineClusterModelChain( - power_plant=wtc.WindTurbineCluster(**my_cluster)) + power_plant=wtc.WindTurbineCluster(**my_cluster) + ) test_tc_mc.run_model(self.weather_df) assert_series_equal(test_tc_mc.power_output, power_output_exp) diff --git a/tests/test_wake_losses.py b/tests/test_wake_losses.py index 7ac66ff7..d27fd4a1 100644 --- a/tests/test_wake_losses.py +++ b/tests/test_wake_losses.py @@ -8,49 +8,77 @@ import pytest from pandas.util.testing import assert_series_equal -from windpowerlib.wake_losses import (reduce_wind_speed, - get_wind_efficiency_curve) +from windpowerlib.wake_losses import ( + reduce_wind_speed, + get_wind_efficiency_curve, +) class TestWakeLosses: - def test_reduce_wind_speed(self): - parameters = {'wind_speed': pd.Series(np.arange(0, 26, 1.0)), - 'wind_efficiency_curve_name': 'dena_mean'} - wind_speed_exp = pd.Series([ - 0.0, 0.9949534234119396, 1.9897327884892086, 2.9843374545454546, - 3.807636264984227, 4.714931284760845, 5.642507531914893, - 6.607021108049704, 7.592423167192429, 8.59498170212766, - 9.606135658475111, 10.619828799086758, 11.641291957894737, - 12.674012890137966, 13.709490666666666, 14.742508260567297, - 15.773293013157893, 16.794615009724474, 17.817683032858028, - 18.85294996704484, 19.86509539493748, 20.858807854510186, - 21.854369681134507, 22.850700350710902, 23.85962037735849, - 24.958125]) + parameters = { + "wind_speed": pd.Series(np.arange(0, 26, 1.0)), + "wind_efficiency_curve_name": "dena_mean", + } + wind_speed_exp = pd.Series( + [ + 0.0, + 0.9949534234119396, + 1.9897327884892086, + 2.9843374545454546, + 3.807636264984227, + 4.714931284760845, + 5.642507531914893, + 6.607021108049704, + 7.592423167192429, + 8.59498170212766, + 9.606135658475111, + 10.619828799086758, + 11.641291957894737, + 12.674012890137966, + 13.709490666666666, + 14.742508260567297, + 15.773293013157893, + 16.794615009724474, + 17.817683032858028, + 18.85294996704484, + 19.86509539493748, + 20.858807854510186, + 21.854369681134507, + 22.850700350710902, + 23.85962037735849, + 24.958125, + ] + ) assert_series_equal(reduce_wind_speed(**parameters), wind_speed_exp) # Raise ValueError - misspelling with pytest.raises(ValueError): - parameters['wind_efficiency_curve_name'] = 'misspelled' + parameters["wind_efficiency_curve_name"] = "misspelled" reduce_wind_speed(**parameters) with pytest.raises(ValueError): - parameters['wind_efficiency_curve_name'] = 'dena_misspelled' + parameters["wind_efficiency_curve_name"] = "dena_misspelled" reduce_wind_speed(**parameters) def test_get_wind_efficiency_curve_one(self): """Test get_wind_efficiency_curve() for one curve.""" - wec = get_wind_efficiency_curve('dena_mean').sum() - wec_exp = pd.Series({'efficiency': 162.45047, - 'wind_speed': 1915.23620}) + wec = get_wind_efficiency_curve("dena_mean").sum() + wec_exp = pd.Series( + {"efficiency": 162.45047, "wind_speed": 1915.23620} + ) assert_series_equal(wec.sort_index(), wec_exp.sort_index()) def test_get_wind_efficiency_curve_all(self): """Test get_wind_efficiency_curve() for all curves.""" - wec_all_sum = int(get_wind_efficiency_curve('all').sum().round().sum()) + wec_all_sum = int(get_wind_efficiency_curve("all").sum().round().sum()) assert wec_all_sum == 12145 def test_get_wind_efficiency_curve_list(self): """Test get_wind_efficiency_curve() for all curves.""" - wec_all_sum = int(get_wind_efficiency_curve( - ['dena_mean', 'knorr_mean']).sum().round().sum()) + wec_all_sum = int( + get_wind_efficiency_curve(["dena_mean", "knorr_mean"]) + .sum() + .round() + .sum() + ) assert wec_all_sum == 3568 diff --git a/tests/test_wind_farm.py b/tests/test_wind_farm.py index 6e5c8aed..b031abcf 100644 --- a/tests/test_wind_farm.py +++ b/tests/test_wind_farm.py @@ -11,23 +11,28 @@ class TestWindFarm: - @classmethod def setup_class(self): """Setup default values""" - self.test_turbine = {'hub_height': 100, - 'turbine_type': 'E-126/4200'} - self.test_turbine_2 = {'hub_height': 90, - 'turbine_type': 'V90/2000', - 'nominal_power': 2e6} + self.test_turbine = {"hub_height": 100, "turbine_type": "E-126/4200"} + self.test_turbine_2 = { + "hub_height": 90, + "turbine_type": "V90/2000", + "nominal_power": 2e6, + } def test_initialization_list(self): """test simple initialization with wind turbine fleet list""" wind_turbine_fleet = [ - {'wind_turbine': WindTurbine(**self.test_turbine), - 'number_of_turbines': 3}, - {'wind_turbine': WindTurbine(**self.test_turbine_2), - 'number_of_turbines': 2}] + { + "wind_turbine": WindTurbine(**self.test_turbine), + "number_of_turbines": 3, + }, + { + "wind_turbine": WindTurbine(**self.test_turbine_2), + "number_of_turbines": 2, + }, + ] windfarm = WindFarm(wind_turbine_fleet=wind_turbine_fleet) assert 3 * 4.2e6 + 2 * 2e6 == windfarm.nominal_power @@ -35,37 +40,53 @@ def test_initialization_list_2(self): """test simple initialization with wind turbine fleet list where once number of turbines and once total capacity is provided""" wind_turbine_fleet = [ - {'wind_turbine': WindTurbine(**self.test_turbine), - 'number_of_turbines': 3}, - {'wind_turbine': WindTurbine(**self.test_turbine_2), - 'total_capacity': 2 * 2e6}] + { + "wind_turbine": WindTurbine(**self.test_turbine), + "number_of_turbines": 3, + }, + { + "wind_turbine": WindTurbine(**self.test_turbine_2), + "total_capacity": 2 * 2e6, + }, + ] windfarm = WindFarm(wind_turbine_fleet=wind_turbine_fleet) assert 3 * 4.2e6 + 2 * 2e6 == windfarm.nominal_power def test_initialization_dataframe(self): """test simple initialization with wind turbine fleet dataframe""" wind_turbine_fleet = pd.DataFrame( - data={'wind_turbine': [WindTurbine(**self.test_turbine), - WindTurbine(**self.test_turbine_2)], - 'number_of_turbines': [3, 2]}) + data={ + "wind_turbine": [ + WindTurbine(**self.test_turbine), + WindTurbine(**self.test_turbine_2), + ], + "number_of_turbines": [3, 2], + } + ) windfarm = WindFarm(wind_turbine_fleet=wind_turbine_fleet) assert 3 * 4.2e6 + 2 * 2e6 == windfarm.nominal_power def test_initialization_1(self): """test catching error when wind_turbine_fleet not provided as list""" - msg = 'Wind turbine must be provided as WindTurbine object' + msg = "Wind turbine must be provided as WindTurbine object" with pytest.raises(ValueError, match=msg): - WindFarm(wind_turbine_fleet={'wind_turbine': 'turbine', - 'number_of_turbines': 2}, - name='dummy') + WindFarm( + wind_turbine_fleet={ + "wind_turbine": "turbine", + "number_of_turbines": 2, + }, + name="dummy", + ) def test_initialization_2(self): """test catching error when WindTurbine in wind_turbine_fleet not initialized""" - test_farm = {'wind_turbine_fleet': [ - {'wind_turbine': None, - 'number_of_turbines': 3}]} - msg = 'Wind turbine must be provided as WindTurbine object' + test_farm = { + "wind_turbine_fleet": [ + {"wind_turbine": None, "number_of_turbines": 3} + ] + } + msg = "Wind turbine must be provided as WindTurbine object" with pytest.raises(ValueError, match=msg): WindFarm(**test_farm) @@ -73,10 +94,15 @@ def test_initialization_3(self): """test catching error when wind_turbine not specified in wind_turbine_fleet""" wind_turbine_fleet = pd.DataFrame( - data={'wind_turbines': [WindTurbine(**self.test_turbine), - WindTurbine(**self.test_turbine_2)], - 'number_of_turbines': [3, 2]}) - msg = 'Missing wind_turbine key/column in wind_turbine_fleet' + data={ + "wind_turbines": [ + WindTurbine(**self.test_turbine), + WindTurbine(**self.test_turbine_2), + ], + "number_of_turbines": [3, 2], + } + ) + msg = "Missing wind_turbine key/column in wind_turbine_fleet" with pytest.raises(KeyError, match=msg): WindFarm(wind_turbine_fleet=wind_turbine_fleet) @@ -86,39 +112,53 @@ def test_initialization_4(self, recwarn): wt1 = WindTurbine(**self.test_turbine) wt2 = WindTurbine(**self.test_turbine_2) wind_turbine_fleet = pd.DataFrame( - data={'wind_turbine': [wt1, wt2], - 'number_of_turbines': [3, 2], - 'total_capacity': [3, np.nan]}, - index=[0, 1]) + data={ + "wind_turbine": [wt1, wt2], + "number_of_turbines": [3, 2], + "total_capacity": [3, np.nan], + }, + index=[0, 1], + ) windfarm = WindFarm(wind_turbine_fleet=wind_turbine_fleet) - total_cap_wt1_expected = \ - wt1.nominal_power * wind_turbine_fleet.loc[0, 'number_of_turbines'] - assert windfarm.wind_turbine_fleet.loc[0, 'total_capacity'] == \ - total_cap_wt1_expected - total_cap_wt2_expected = \ - wt2.nominal_power * wind_turbine_fleet.loc[1, 'number_of_turbines'] - assert windfarm.wind_turbine_fleet.loc[1, 'total_capacity'] == \ - total_cap_wt2_expected + total_cap_wt1_expected = ( + wt1.nominal_power * wind_turbine_fleet.loc[0, "number_of_turbines"] + ) + assert ( + windfarm.wind_turbine_fleet.loc[0, "total_capacity"] + == total_cap_wt1_expected + ) + total_cap_wt2_expected = ( + wt2.nominal_power * wind_turbine_fleet.loc[1, "number_of_turbines"] + ) + assert ( + windfarm.wind_turbine_fleet.loc[1, "total_capacity"] + == total_cap_wt2_expected + ) assert recwarn.pop(WindpowerlibUserWarning) def test_initialization_5(self): """test catching error when number of turbines cannot be deduced""" wt = WindTurbine(**self.test_turbine) wt.nominal_power = None - test_farm = {'wind_turbine_fleet': [ - {'wind_turbine': wt, - 'total_capacity': 3e6}]} - msg = 'Number of turbines of type' + test_farm = { + "wind_turbine_fleet": [{"wind_turbine": wt, "total_capacity": 3e6}] + } + msg = "Number of turbines of type" with pytest.raises(ValueError, match=msg): WindFarm(**test_farm) def test_initialization_6(self): """test catching error when neither number_of_turbines nor total_capacity is provided""" - test_farm = {'wind_turbine_fleet': [ - {'wind_turbine': WindTurbine(**self.test_turbine), - 'number_of_turbine': 3e6}]} - msg = 'Number of turbines of type ' + test_farm = { + "wind_turbine_fleet": [ + { + "wind_turbine": WindTurbine(**self.test_turbine), + "number_of_turbine": 3e6, + } + ] + } + msg = "Number of turbines of type " with pytest.raises(ValueError, match=msg): WindFarm(**test_farm) @@ -126,40 +166,56 @@ def test_initialization_7(self): """test catching error when total capacity cannot be deduced""" wt = WindTurbine(**self.test_turbine) wt.nominal_power = None - test_farm = {'wind_turbine_fleet': [ - {'wind_turbine': wt, - 'number_of_turbines': 3}]} - msg = 'Total capacity of turbines of type' + test_farm = { + "wind_turbine_fleet": [ + {"wind_turbine": wt, "number_of_turbines": 3} + ] + } + msg = "Total capacity of turbines of type" with pytest.raises(ValueError, match=msg): WindFarm(**test_farm) def test_mean_hub_height(self): """tests mean_hub_height method""" - test_farm = {'wind_turbine_fleet': [ - {'wind_turbine': WindTurbine(**self.test_turbine), - 'number_of_turbines': 2}, - {'wind_turbine': WindTurbine(**self.test_turbine_2), - 'total_capacity': 3e6}]} + test_farm = { + "wind_turbine_fleet": [ + { + "wind_turbine": WindTurbine(**self.test_turbine), + "number_of_turbines": 2, + }, + { + "wind_turbine": WindTurbine(**self.test_turbine_2), + "total_capacity": 3e6, + }, + ] + } windfarm = WindFarm(**test_farm) assert 97.265 == pytest.approx( - windfarm.mean_hub_height().hub_height, 1e-3) + windfarm.mean_hub_height().hub_height, 1e-3 + ) def test_repr(self): """Test string representation of WindFarm""" - test_fleet = [{'wind_turbine': WindTurbine(**self.test_turbine), - 'number_of_turbines': 2}] - assert 'E-126/4200' in repr(WindFarm(wind_turbine_fleet=test_fleet)) + test_fleet = [ + { + "wind_turbine": WindTurbine(**self.test_turbine), + "number_of_turbines": 2, + } + ] + assert "E-126/4200" in repr(WindFarm(wind_turbine_fleet=test_fleet)) def test_aggregation_of_power_curve_with_missing_power_curve(self): """Test WindFarm.assign_power_curve() with missing power_curve.""" wt1 = WindTurbine(**self.test_turbine) wt1.power_curve = None wind_turbine_fleet = [ - {'wind_turbine': wt1, - 'number_of_turbines': 3}, - {'wind_turbine': WindTurbine(**self.test_turbine_2), - 'number_of_turbines': 2}] + {"wind_turbine": wt1, "number_of_turbines": 3}, + { + "wind_turbine": WindTurbine(**self.test_turbine_2), + "number_of_turbines": 2, + }, + ] windfarm = WindFarm(wind_turbine_fleet=wind_turbine_fleet) - msg = 'For an aggregated wind farm power curve each wind' + msg = "For an aggregated wind farm power curve each wind" with pytest.raises(ValueError, match=msg): windfarm.assign_power_curve() diff --git a/tests/test_wind_speed.py b/tests/test_wind_speed.py index 162084c7..f4af4299 100644 --- a/tests/test_wind_speed.py +++ b/tests/test_wind_speed.py @@ -13,94 +13,104 @@ class TestWindSpeed: - def test_logarithmic_profile(self): - parameters = {'wind_speed': pd.Series(data=[5.0, 6.5]), - 'wind_speed_height': 10, - 'hub_height': 100, - 'roughness_length': pd.Series(data=[0.15, 0.15]), - 'obstacle_height': 0} + parameters = { + "wind_speed": pd.Series(data=[5.0, 6.5]), + "wind_speed_height": 10, + "hub_height": 100, + "roughness_length": pd.Series(data=[0.15, 0.15]), + "obstacle_height": 0, + } # Test wind_speed as pd.Series with roughness_length as pd.Series, # np.array and float v_wind_hub_exp = pd.Series(data=[7.74136523, 10.0637748]) - assert_series_equal(logarithmic_profile(**parameters), - v_wind_hub_exp) - parameters['roughness_length'] = np.array( - parameters['roughness_length']) - assert_series_equal(logarithmic_profile(**parameters), - v_wind_hub_exp) - parameters['roughness_length'] = parameters['roughness_length'][0] - assert_series_equal(logarithmic_profile(**parameters), - v_wind_hub_exp) + assert_series_equal(logarithmic_profile(**parameters), v_wind_hub_exp) + parameters["roughness_length"] = np.array( + parameters["roughness_length"] + ) + assert_series_equal(logarithmic_profile(**parameters), v_wind_hub_exp) + parameters["roughness_length"] = parameters["roughness_length"][0] + assert_series_equal(logarithmic_profile(**parameters), v_wind_hub_exp) # Test wind_speed as np.array with roughness_length as float, pd.Series # and np.array v_wind_hub_exp = np.array([7.74136523, 10.0637748]) - parameters['wind_speed'] = np.array(parameters['wind_speed']) + parameters["wind_speed"] = np.array(parameters["wind_speed"]) assert_allclose(logarithmic_profile(**parameters), v_wind_hub_exp) assert isinstance(logarithmic_profile(**parameters), np.ndarray) - parameters['roughness_length'] = pd.Series( - data=[parameters['roughness_length'], - parameters['roughness_length']]) + parameters["roughness_length"] = pd.Series( + data=[ + parameters["roughness_length"], + parameters["roughness_length"], + ] + ) assert_allclose(logarithmic_profile(**parameters), v_wind_hub_exp) assert isinstance(logarithmic_profile(**parameters), np.ndarray) - parameters['roughness_length'] = np.array( - parameters['roughness_length']) + parameters["roughness_length"] = np.array( + parameters["roughness_length"] + ) assert_allclose(logarithmic_profile(**parameters), v_wind_hub_exp) assert isinstance(logarithmic_profile(**parameters), np.ndarray) # Test obstacle_height is not zero v_wind_hub_exp = np.array([13.54925281, 17.61402865]) - parameters['obstacle_height'] = 12 + parameters["obstacle_height"] = 12 assert_allclose(logarithmic_profile(**parameters), v_wind_hub_exp) # Raise ValueError due to 0.7 * `obstacle_height` > `wind_speed_height` with pytest.raises(ValueError): - parameters['obstacle_height'] = 20 + parameters["obstacle_height"] = 20 logarithmic_profile(**parameters) def test_hellman(self): - parameters = {'wind_speed': pd.Series(data=[5.0, 6.5]), - 'wind_speed_height': 10, - 'hub_height': 100, - 'roughness_length': pd.Series(data=[0.15, 0.15]), - 'hellman_exponent': None} + parameters = { + "wind_speed": pd.Series(data=[5.0, 6.5]), + "wind_speed_height": 10, + "hub_height": 100, + "roughness_length": pd.Series(data=[0.15, 0.15]), + "hellman_exponent": None, + } # Test wind_speed is pd.Series with roughness_length is pd.Series, # np.array and float v_wind_hub_exp = pd.Series(data=[7.12462437, 9.26201168]) assert_series_equal(hellman(**parameters), v_wind_hub_exp) - parameters['roughness_length'] = np.array( - parameters['roughness_length']) + parameters["roughness_length"] = np.array( + parameters["roughness_length"] + ) assert_series_equal(hellman(**parameters), v_wind_hub_exp) - parameters['roughness_length'] = parameters['roughness_length'][0] + parameters["roughness_length"] = parameters["roughness_length"][0] assert_series_equal(hellman(**parameters), v_wind_hub_exp) # Test wind_speed as np.array with roughness_length is float, pd.Series # and np.array v_wind_hub_exp = np.array([7.12462437, 9.26201168]) - parameters['wind_speed'] = np.array(parameters['wind_speed']) + parameters["wind_speed"] = np.array(parameters["wind_speed"]) assert_allclose(hellman(**parameters), v_wind_hub_exp) assert isinstance(hellman(**parameters), np.ndarray) - parameters['roughness_length'] = pd.Series( - data=(parameters['roughness_length'], - parameters['roughness_length'])) + parameters["roughness_length"] = pd.Series( + data=( + parameters["roughness_length"], + parameters["roughness_length"], + ) + ) assert_allclose(hellman(**parameters), v_wind_hub_exp) assert isinstance(hellman(**parameters), np.ndarray) - parameters['roughness_length'] = np.array( - parameters['roughness_length']) + parameters["roughness_length"] = np.array( + parameters["roughness_length"] + ) assert_allclose(hellman(**parameters), v_wind_hub_exp) assert isinstance(hellman(**parameters), np.ndarray) # Test roughness_length is None and hellman_exponent is None v_wind_hub_exp = pd.Series(data=[6.9474774, 9.03172]) - parameters['wind_speed'] = pd.Series(data=parameters['wind_speed']) - parameters['roughness_length'] = None + parameters["wind_speed"] = pd.Series(data=parameters["wind_speed"]) + parameters["roughness_length"] = None assert_series_equal(hellman(**parameters), v_wind_hub_exp) # Test hellman_exponent is not None v_wind_hub_exp = pd.Series(data=[7.92446596, 10.30180575]) - parameters['roughness_length'] = 0.15 - parameters['hellman_exponent'] = 0.2 + parameters["roughness_length"] = 0.15 + parameters["hellman_exponent"] = 0.2 assert_series_equal(hellman(**parameters), v_wind_hub_exp) diff --git a/tests/test_wind_turbine.py b/tests/test_wind_turbine.py index 5ba627a2..1f3acfac 100644 --- a/tests/test_wind_turbine.py +++ b/tests/test_wind_turbine.py @@ -9,47 +9,52 @@ import os from windpowerlib.tools import WindpowerlibUserWarning -from windpowerlib.wind_turbine import (get_turbine_data_from_file, WindTurbine, - get_turbine_types, WindTurbineGroup, - load_turbine_data_from_oedb) +from windpowerlib.wind_turbine import ( + get_turbine_data_from_file, + WindTurbine, + get_turbine_types, + WindTurbineGroup, + load_turbine_data_from_oedb, +) class TestWindTurbine: - @classmethod def setup_class(cls): """Setup default values""" - cls.source = os.path.join(os.path.dirname(__file__), '../example/data') + cls.source = os.path.join(os.path.dirname(__file__), "../example/data") def test_warning(self, recwarn): - test_turbine_data = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'turbine_not_in_file', - 'path': self.source} - assert(WindTurbine(**test_turbine_data).power_curve is None) + test_turbine_data = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "turbine_not_in_file", + "path": self.source, + } + assert WindTurbine(**test_turbine_data).power_curve is None assert recwarn.pop(WindpowerlibUserWarning) def test_get_turbine_data_from_file(self): # Raise FileNotFoundError due to missing with pytest.raises(FileNotFoundError): - get_turbine_data_from_file(turbine_type='...', - path='not_existent') + get_turbine_data_from_file(turbine_type="...", path="not_existent") def test_get_turbine_types(self, capsys): get_turbine_types() captured = capsys.readouterr() - assert 'Enercon' in captured.out - get_turbine_types('oedb', print_out=False, filter_=False) + assert "Enercon" in captured.out + get_turbine_types("oedb", print_out=False, filter_=False) msg = "`turbine_library` is 'wrong' but must be 'local' or 'oedb'." with pytest.raises(ValueError, match=msg): - get_turbine_types('wrong') + get_turbine_types("wrong") def test_wrong_url_load_turbine_data(self): """Load turbine data from oedb.""" - with pytest.raises(ConnectionError, - match="Database connection not successful"): - load_turbine_data_from_oedb('wrong_schema') + with pytest.raises( + ConnectionError, match="Database connection not successful" + ): + load_turbine_data_from_oedb("wrong_schema") @pytest.mark.filterwarnings("ignore:The WindTurbine") def test_string_representation_of_wind_turbine(self): @@ -57,43 +62,51 @@ def test_string_representation_of_wind_turbine(self): def test_power_curve_is_of_wrong_type(self): """Error raising due to wrong type of WindTurbine.power_curve.""" - test_turbine_data = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'test_type', - 'power_curve': 'string'} + test_turbine_data = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "test_type", + "power_curve": "string", + } with pytest.raises(TypeError): WindTurbine(**test_turbine_data) def test_power_coefficient_curve_is_of_wrong_type(self): """Error raising due to wrong type of WindTurbine.power_coefficient_curve.""" - test_turbine_data = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'test_type', - 'power_coefficient_curve': 'string'} + test_turbine_data = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "test_type", + "power_coefficient_curve": "string", + } with pytest.raises(TypeError): WindTurbine(**test_turbine_data) def test_to_group_method(self): example_turbine = { - 'hub_height': 100, - 'rotor_diameter': 70, - 'turbine_type': 'DUMMY 3', - 'path': self.source} + "hub_height": 100, + "rotor_diameter": 70, + "turbine_type": "DUMMY 3", + "path": self.source, + } e_t_1 = WindTurbine(**example_turbine) - assert(isinstance(e_t_1.to_group(), WindTurbineGroup)) - assert(e_t_1.to_group(5).number_of_turbines == 5) - assert(e_t_1.to_group(number_turbines=5).number_of_turbines == 5) - assert(e_t_1.to_group(total_capacity=3e6).number_of_turbines == 2.0) + assert isinstance(e_t_1.to_group(), WindTurbineGroup) + assert e_t_1.to_group(5).number_of_turbines == 5 + assert e_t_1.to_group(number_turbines=5).number_of_turbines == 5 + assert e_t_1.to_group(total_capacity=3e6).number_of_turbines == 2.0 def test_wrongly_defined_to_group_method(self): example_turbine = { - 'hub_height': 100, - 'rotor_diameter': 70, - 'turbine_type': 'DUMMY 3', - 'path': self.source} + "hub_height": 100, + "rotor_diameter": 70, + "turbine_type": "DUMMY 3", + "path": self.source, + } e_t_1 = WindTurbine(**example_turbine) - with pytest.raises(ValueError, - match="The 'number' and the 'total_capacity' " - "parameter are mutually exclusive."): + with pytest.raises( + ValueError, + match="The 'number' and the 'total_capacity' " + "parameter are mutually exclusive.", + ): e_t_1.to_group(5, 3000) diff --git a/windpowerlib/__init__.py b/windpowerlib/__init__.py index 23ebcb2b..a206ff5a 100644 --- a/windpowerlib/__init__.py +++ b/windpowerlib/__init__.py @@ -1,6 +1,6 @@ __copyright__ = "Copyright oemof developer group" __license__ = "MIT" -__version__ = '0.2.1dev' +__version__ = "0.2.1dev" from windpowerlib.wind_turbine import WindTurbine from windpowerlib.wind_farm import WindFarm diff --git a/windpowerlib/modelchain.py b/windpowerlib/modelchain.py index 0a4cfc4e..7553006e 100644 --- a/windpowerlib/modelchain.py +++ b/windpowerlib/modelchain.py @@ -122,26 +122,30 @@ class ModelChain(object): -------- >>> from windpowerlib import modelchain >>> from windpowerlib import wind_turbine - >>> enerconE126 = { + >>> enerconE126={ ... 'hub_height': 135, ... 'rotor_diameter': 127, ... 'turbine_type': 'E-126/4200'} - >>> e126 = wind_turbine.WindTurbine(**enerconE126) - >>> modelchain_data = {'density_model': 'ideal_gas'} - >>> e126_mc = modelchain.ModelChain(e126, **modelchain_data) + >>> e126=wind_turbine.WindTurbine(**enerconE126) + >>> modelchain_data={'density_model': 'ideal_gas'} + >>> e126_mc=modelchain.ModelChain(e126, **modelchain_data) >>> print(e126_mc.density_model) ideal_gas """ - def __init__(self, power_plant, - wind_speed_model='logarithmic', - temperature_model='linear_gradient', - density_model='barometric', - power_output_model='power_curve', - density_correction=False, - obstacle_height=0, - hellman_exp=None, **kwargs): + def __init__( + self, + power_plant, + wind_speed_model="logarithmic", + temperature_model="linear_gradient", + density_model="barometric", + power_output_model="power_curve", + density_correction=False, + obstacle_height=0, + hellman_exp=None, + **kwargs + ): self.power_plant = power_plant self.obstacle_height = obstacle_height @@ -182,28 +186,42 @@ def temperature_hub(self, weather_df): temperature(s) closest to the hub height are used. """ - if self.power_plant.hub_height in weather_df['temperature']: - temperature_hub = weather_df['temperature'][ - self.power_plant.hub_height] - elif self.temperature_model == 'linear_gradient': - logging.debug('Calculating temperature using temperature ' - 'gradient.') - closest_height = weather_df['temperature'].columns[ - min(range(len(weather_df['temperature'].columns)), - key=lambda i: abs(weather_df['temperature'].columns[i] - - self.power_plant.hub_height))] + if self.power_plant.hub_height in weather_df["temperature"]: + temperature_hub = weather_df["temperature"][ + self.power_plant.hub_height + ] + elif self.temperature_model == "linear_gradient": + logging.debug( + "Calculating temperature using temperature " "gradient." + ) + closest_height = weather_df["temperature"].columns[ + min( + range(len(weather_df["temperature"].columns)), + key=lambda i: abs( + weather_df["temperature"].columns[i] + - self.power_plant.hub_height + ), + ) + ] temperature_hub = temperature.linear_gradient( - weather_df['temperature'][closest_height], closest_height, - self.power_plant.hub_height) - elif self.temperature_model == 'interpolation_extrapolation': - logging.debug('Calculating temperature using linear inter- or ' - 'extrapolation.') + weather_df["temperature"][closest_height], + closest_height, + self.power_plant.hub_height, + ) + elif self.temperature_model == "interpolation_extrapolation": + logging.debug( + "Calculating temperature using linear inter- or " + "extrapolation." + ) temperature_hub = tools.linear_interpolation_extrapolation( - weather_df['temperature'], self.power_plant.hub_height) + weather_df["temperature"], self.power_plant.hub_height + ) else: - raise ValueError("'{0}' is an invalid value. ".format( - self.temperature_model) + "`temperature_model` must be " - "'linear_gradient' or 'interpolation_extrapolation'.") + raise ValueError( + "'{0}' is an invalid value. ".format(self.temperature_model) + + "`temperature_model` must be " + "'linear_gradient' or 'interpolation_extrapolation'." + ) return temperature_hub def density_hub(self, weather_df): @@ -241,39 +259,60 @@ def density_hub(self, weather_df): hub height, the `weather_df` must contain at least two time series for density. """ - if self.density_model != 'interpolation_extrapolation': + if self.density_model != "interpolation_extrapolation": temperature_hub = self.temperature_hub(weather_df) # Calculation of density in kg/m³ at hub height - if self.density_model == 'barometric': - logging.debug('Calculating density using barometric height ' - 'equation.') - closest_height = weather_df['pressure'].columns[ - min(range(len(weather_df['pressure'].columns)), - key=lambda i: abs(weather_df['pressure'].columns[i] - - self.power_plant.hub_height))] + if self.density_model == "barometric": + logging.debug( + "Calculating density using barometric height " "equation." + ) + closest_height = weather_df["pressure"].columns[ + min( + range(len(weather_df["pressure"].columns)), + key=lambda i: abs( + weather_df["pressure"].columns[i] + - self.power_plant.hub_height + ), + ) + ] density_hub = density.barometric( - weather_df['pressure'][closest_height], closest_height, - self.power_plant.hub_height, temperature_hub) - elif self.density_model == 'ideal_gas': - logging.debug('Calculating density using ideal gas equation.') - closest_height = weather_df['pressure'].columns[ - min(range(len(weather_df['pressure'].columns)), - key=lambda i: abs(weather_df['pressure'].columns[i] - - self.power_plant.hub_height))] + weather_df["pressure"][closest_height], + closest_height, + self.power_plant.hub_height, + temperature_hub, + ) + elif self.density_model == "ideal_gas": + logging.debug("Calculating density using ideal gas equation.") + closest_height = weather_df["pressure"].columns[ + min( + range(len(weather_df["pressure"].columns)), + key=lambda i: abs( + weather_df["pressure"].columns[i] + - self.power_plant.hub_height + ), + ) + ] density_hub = density.ideal_gas( - weather_df['pressure'][closest_height], closest_height, - self.power_plant.hub_height, temperature_hub) - elif self.density_model == 'interpolation_extrapolation': - logging.debug('Calculating density using linear inter- or ' - 'extrapolation.') + weather_df["pressure"][closest_height], + closest_height, + self.power_plant.hub_height, + temperature_hub, + ) + elif self.density_model == "interpolation_extrapolation": + logging.debug( + "Calculating density using linear inter- or " "extrapolation." + ) density_hub = tools.linear_interpolation_extrapolation( - weather_df['density'], self.power_plant.hub_height) + weather_df["density"], self.power_plant.hub_height + ) else: - raise ValueError("'{0}' is an invalid value. ".format( - self.density_model) + "`density_model` " + - "must be 'barometric', 'ideal_gas' or " + - "'interpolation_extrapolation'.") + raise ValueError( + "'{0}' is an invalid value. ".format(self.density_model) + + "`density_model` " + + "must be 'barometric', 'ideal_gas' or " + + "'interpolation_extrapolation'." + ) return density_hub def wind_speed_hub(self, weather_df): @@ -305,47 +344,71 @@ def wind_speed_hub(self, weather_df): wind speed(s) closest to the hub height are used. """ - if self.power_plant.hub_height in weather_df['wind_speed']: - wind_speed_hub = weather_df['wind_speed'][ - self.power_plant.hub_height] - elif self.wind_speed_model == 'logarithmic': - logging.debug('Calculating wind speed using logarithmic wind ' - 'profile.') - closest_height = weather_df['wind_speed'].columns[ - min(range(len(weather_df['wind_speed'].columns)), - key=lambda i: abs(weather_df['wind_speed'].columns[i] - - self.power_plant.hub_height))] + if self.power_plant.hub_height in weather_df["wind_speed"]: + wind_speed_hub = weather_df["wind_speed"][ + self.power_plant.hub_height + ] + elif self.wind_speed_model == "logarithmic": + logging.debug( + "Calculating wind speed using logarithmic wind " "profile." + ) + closest_height = weather_df["wind_speed"].columns[ + min( + range(len(weather_df["wind_speed"].columns)), + key=lambda i: abs( + weather_df["wind_speed"].columns[i] + - self.power_plant.hub_height + ), + ) + ] wind_speed_hub = wind_speed.logarithmic_profile( - weather_df['wind_speed'][closest_height], closest_height, + weather_df["wind_speed"][closest_height], + closest_height, self.power_plant.hub_height, - weather_df['roughness_length'].iloc[:, 0], - self.obstacle_height) - elif self.wind_speed_model == 'hellman': - logging.debug('Calculating wind speed using hellman equation.') - closest_height = weather_df['wind_speed'].columns[ - min(range(len(weather_df['wind_speed'].columns)), - key=lambda i: abs(weather_df['wind_speed'].columns[i] - - self.power_plant.hub_height))] + weather_df["roughness_length"].iloc[:, 0], + self.obstacle_height, + ) + elif self.wind_speed_model == "hellman": + logging.debug("Calculating wind speed using hellman equation.") + closest_height = weather_df["wind_speed"].columns[ + min( + range(len(weather_df["wind_speed"].columns)), + key=lambda i: abs( + weather_df["wind_speed"].columns[i] + - self.power_plant.hub_height + ), + ) + ] wind_speed_hub = wind_speed.hellman( - weather_df['wind_speed'][closest_height], closest_height, + weather_df["wind_speed"][closest_height], + closest_height, self.power_plant.hub_height, - weather_df['roughness_length'].iloc[:, 0], - self.hellman_exp) - elif self.wind_speed_model == 'interpolation_extrapolation': - logging.debug('Calculating wind speed using linear inter- or ' - 'extrapolation.') + weather_df["roughness_length"].iloc[:, 0], + self.hellman_exp, + ) + elif self.wind_speed_model == "interpolation_extrapolation": + logging.debug( + "Calculating wind speed using linear inter- or " + "extrapolation." + ) wind_speed_hub = tools.linear_interpolation_extrapolation( - weather_df['wind_speed'], self.power_plant.hub_height) - elif self.wind_speed_model == 'log_interpolation_extrapolation': - logging.debug('Calculating wind speed using logarithmic inter- or ' - 'extrapolation.') + weather_df["wind_speed"], self.power_plant.hub_height + ) + elif self.wind_speed_model == "log_interpolation_extrapolation": + logging.debug( + "Calculating wind speed using logarithmic inter- or " + "extrapolation." + ) wind_speed_hub = tools.logarithmic_interpolation_extrapolation( - weather_df['wind_speed'], self.power_plant.hub_height) + weather_df["wind_speed"], self.power_plant.hub_height + ) else: - raise ValueError("'{0}' is an invalid value. ".format( - self.wind_speed_model) + "`wind_speed_model` must be " - "'logarithmic', 'hellman', 'interpolation_extrapolation' " + - "or 'log_interpolation_extrapolation'.") + raise ValueError( + "'{0}' is an invalid value. ".format(self.wind_speed_model) + + "`wind_speed_model` must be " + "'logarithmic', 'hellman', 'interpolation_extrapolation' " + + "or 'log_interpolation_extrapolation'." + ) return wind_speed_hub def calculate_power_output(self, wind_speed_hub, density_hub): @@ -367,34 +430,43 @@ def calculate_power_output(self, wind_speed_hub, density_hub): Electrical power output of the wind turbine in W. """ - if self.power_output_model == 'power_curve': + if self.power_output_model == "power_curve": if self.power_plant.power_curve is None: - raise TypeError("Power curve values of {} are missing.".format( - self.power_plant)) - logging.debug('Calculating power output using power curve.') - return (power_output.power_curve( - wind_speed_hub, - self.power_plant.power_curve['wind_speed'], - self.power_plant.power_curve['value'], - density_hub, self.density_correction)) - elif self.power_output_model == 'power_coefficient_curve': + raise TypeError( + "Power curve values of {} are missing.".format( + self.power_plant + ) + ) + logging.debug("Calculating power output using power curve.") + return power_output.power_curve( + wind_speed_hub, + self.power_plant.power_curve["wind_speed"], + self.power_plant.power_curve["value"], + density_hub, + self.density_correction, + ) + elif self.power_output_model == "power_coefficient_curve": if self.power_plant.power_coefficient_curve is None: - raise TypeError("Power coefficient curve values of {} are " - "missing.".format(self.power_plant)) - logging.debug('Calculating power output using power coefficient ' - 'curve.') - return (power_output.power_coefficient_curve( - wind_speed_hub, - self.power_plant.power_coefficient_curve[ - 'wind_speed'], - self.power_plant.power_coefficient_curve[ - 'value'], - self.power_plant.rotor_diameter, density_hub)) + raise TypeError( + "Power coefficient curve values of {} are " + "missing.".format(self.power_plant) + ) + logging.debug( + "Calculating power output using power coefficient " "curve." + ) + return power_output.power_coefficient_curve( + wind_speed_hub, + self.power_plant.power_coefficient_curve["wind_speed"], + self.power_plant.power_coefficient_curve["value"], + self.power_plant.rotor_diameter, + density_hub, + ) else: - raise ValueError("'{0}' is an invalid value. ".format( - self.power_output_model) + - "`power_output_model` must be " + - "'power_curve' or 'power_coefficient_curve'.") + raise ValueError( + "'{0}' is an invalid value. ".format(self.power_output_model) + + "`power_output_model` must be " + + "'power_curve' or 'power_coefficient_curve'." + ) def run_model(self, weather_df): r""" @@ -422,7 +494,7 @@ def run_model(self, weather_df): --------- >>> import numpy as np >>> import pandas as pd - >>> weather_df = pd.DataFrame(np.random.rand(2,6), + >>> weather_df=pd.DataFrame(np.random.rand(2,6), ... index=pd.date_range('1/1/2012', ... periods=2, ... freq='H'), @@ -439,9 +511,15 @@ def run_model(self, weather_df): """ wind_speed_hub = self.wind_speed_hub(weather_df) - density_hub = (None if (self.power_output_model == 'power_curve' and - self.density_correction is False) - else self.density_hub(weather_df)) - self.power_output = self.calculate_power_output(wind_speed_hub, - density_hub) + density_hub = ( + None + if ( + self.power_output_model == "power_curve" + and self.density_correction is False + ) + else self.density_hub(weather_df) + ) + self.power_output = self.calculate_power_output( + wind_speed_hub, density_hub + ) return self diff --git a/windpowerlib/power_curves.py b/windpowerlib/power_curves.py index 96829474..ef9eb138 100644 --- a/windpowerlib/power_curves.py +++ b/windpowerlib/power_curves.py @@ -60,7 +60,7 @@ def smooth_power_curve(power_curve_wind_speeds, power_curve_values, 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) + .. 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 @@ -83,7 +83,7 @@ def smooth_power_curve(power_curve_wind_speeds, power_curve_values, 'turbulence_intensity' [2]_: - .. math:: \sigma = v_\text{std} \cdot \sigma_\text{n} = v_\text{std} + .. math:: \sigma=v_\text{std} \cdot \sigma_\text{n}=v_\text{std} \cdot TI with: @@ -91,7 +91,7 @@ def smooth_power_curve(power_curve_wind_speeds, power_curve_values, 'Staffell_Pfenninger' [4]_: - .. math:: \sigma = 0.6 \cdot 0.2 \cdot v_\text{std} + .. math:: \sigma=0.6 \cdot 0.2 \cdot v_\text{std} References ---------- @@ -110,43 +110,58 @@ def smooth_power_curve(power_curve_wind_speeds, power_curve_values, """ # 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'] + 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': + 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)) + 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.iloc[-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] + - (power_curve_wind_speeds.iloc[5] - - power_curve_wind_speeds.iloc[4]), - index=[power_curve_wind_speeds.index[-1] + 1])) + pd.Series( + power_curve_wind_speeds.iloc[-1] + + ( + power_curve_wind_speeds.iloc[5] + - power_curve_wind_speeds.iloc[4] + ), + 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])) + 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) + 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) + 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 @@ -154,26 +169,39 @@ def smooth_power_curve(power_curve_wind_speeds, power_curve_values, 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( + 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) + 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() + 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'] + smoothed_power_curve_df.columns = ["wind_speed", "value"] return smoothed_power_curve_df -def wake_losses_to_power_curve(power_curve_wind_speeds, power_curve_values, - wind_farm_efficiency): +def wake_losses_to_power_curve( + power_curve_wind_speeds, power_curve_values, wind_farm_efficiency +): r""" Reduces the power values of a power curve by an efficiency (curve). @@ -201,26 +229,35 @@ def wake_losses_to_power_curve(power_curve_wind_speeds, power_curve_values, """ # Create power curve DataFrame power_curve_df = pd.DataFrame( - data=[list(power_curve_wind_speeds), - list(power_curve_values)]).transpose() + data=[list(power_curve_wind_speeds), list(power_curve_values)] + ).transpose() # Rename columns of DataFrame - power_curve_df.columns = ['wind_speed', 'value'] + power_curve_df.columns = ["wind_speed", "value"] if isinstance(wind_farm_efficiency, float): - power_curve_df['value'] = power_curve_values * wind_farm_efficiency - elif (isinstance(wind_farm_efficiency, dict) or - isinstance(wind_farm_efficiency, pd.DataFrame)): - df = pd.concat([power_curve_df.set_index('wind_speed'), - wind_farm_efficiency.set_index('wind_speed')], axis=1) + power_curve_df["value"] = power_curve_values * wind_farm_efficiency + elif isinstance(wind_farm_efficiency, dict) or isinstance( + wind_farm_efficiency, pd.DataFrame + ): + 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'] + 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 TypeError( "'wind_farm_efficiency' must be float, dict or pd.DataFrame " - "but is {}".format(type(wind_farm_efficiency))) + "but is {}".format(type(wind_farm_efficiency)) + ) return power_curve_df diff --git a/windpowerlib/power_output.py b/windpowerlib/power_output.py index ff9c658f..4db874b9 100644 --- a/windpowerlib/power_output.py +++ b/windpowerlib/power_output.py @@ -64,22 +64,40 @@ def power_coefficient_curve(wind_speed, power_coefficient_curve_wind_speeds, """ power_coefficient_time_series = np.interp( - wind_speed, power_coefficient_curve_wind_speeds, - power_coefficient_curve_values, left=0, right=0) - power_output = (1 / 8 * density * rotor_diameter ** 2 * np.pi * - np.power(wind_speed, 3) * - power_coefficient_time_series) + wind_speed, + power_coefficient_curve_wind_speeds, + power_coefficient_curve_values, + left=0, + right=0, + ) + power_output = ( + 1 + / 8 + * density + * rotor_diameter ** 2 + * np.pi + * np.power(wind_speed, 3) + * power_coefficient_time_series + ) # Power_output as pd.Series if wind_speed is pd.Series (else: np.array) if isinstance(wind_speed, pd.Series): - power_output = pd.Series(data=power_output, index=wind_speed.index, - name='feedin_power_plant') + power_output = pd.Series( + data=power_output, + index=wind_speed.index, + name="feedin_power_plant", + ) else: power_output = np.array(power_output) return power_output -def power_curve(wind_speed, power_curve_wind_speeds, power_curve_values, - density=None, density_correction=False): +def power_curve( + wind_speed, + power_curve_wind_speeds, + power_curve_values, + density=None, + density_correction=False, +): r""" Calculates the turbine power output using a power curve. @@ -120,26 +138,38 @@ def power_curve(wind_speed, power_curve_wind_speeds, power_curve_values, """ if density_correction is False: - power_output = np.interp(wind_speed, power_curve_wind_speeds, - power_curve_values, left=0, right=0) + power_output = np.interp( + wind_speed, + power_curve_wind_speeds, + power_curve_values, + left=0, + right=0, + ) # Power_output as pd.Series if wind_speed is pd.Series (else: np.array) if isinstance(wind_speed, pd.Series): - power_output = pd.Series(data=power_output, index=wind_speed.index, - name='feedin_power_plant') + power_output = pd.Series( + data=power_output, + index=wind_speed.index, + name="feedin_power_plant", + ) else: power_output = np.array(power_output) elif density_correction is True: power_output = power_curve_density_correction( - wind_speed, power_curve_wind_speeds, power_curve_values, density) + wind_speed, power_curve_wind_speeds, power_curve_values, density + ) else: - raise TypeError("'{0}' is an invalid type. ".format(type( - density_correction)) + "`density_correction` must " + - "be Boolean (True or False).") + raise TypeError( + "'{0}' is an invalid type. ".format(type(density_correction)) + + "`density_correction` must " + + "be Boolean (True or False)." + ) return power_output -def power_curve_density_correction(wind_speed, power_curve_wind_speeds, - power_curve_values, density): +def power_curve_density_correction( + wind_speed, power_curve_wind_speeds, power_curve_values, density +): r""" Calculates the turbine power output using a density corrected power curve. @@ -207,18 +237,37 @@ def power_curve_density_correction(wind_speed, power_curve_wind_speeds, """ if density is None: - raise TypeError("`density` is None. For the calculation with a " + - "density corrected power curve density at hub " + - "height is needed.") - power_output = [(np.interp( - wind_speed[i], power_curve_wind_speeds * (1.225 / density[i]) ** ( - np.interp(power_curve_wind_speeds, [7.5, 12.5], [1/3, 2/3])), - power_curve_values, left=0, right=0)) for i in range(len(wind_speed))] + raise TypeError( + "`density` is None. For the calculation with a " + + "density corrected power curve density at hub " + + "height is needed." + ) + power_output = [ + ( + np.interp( + wind_speed[i], + power_curve_wind_speeds + * (1.225 / density[i]) + ** ( + np.interp( + power_curve_wind_speeds, [7.5, 12.5], [1 / 3, 2 / 3] + ) + ), + power_curve_values, + left=0, + right=0, + ) + ) + for i in range(len(wind_speed)) + ] # Power_output as pd.Series if wind_speed is pd.Series (else: np.array) if isinstance(wind_speed, pd.Series): - power_output = pd.Series(data=power_output, index=wind_speed.index, - name='feedin_power_plant') + power_output = pd.Series( + data=power_output, + index=wind_speed.index, + name="feedin_power_plant", + ) else: power_output = np.array(power_output) return power_output diff --git a/windpowerlib/tools.py b/windpowerlib/tools.py index 25f6b99c..faab0202 100644 --- a/windpowerlib/tools.py +++ b/windpowerlib/tools.py @@ -56,16 +56,16 @@ def linear_interpolation_extrapolation(df, target_height): For the inter- and extrapolation the following equation is used: - .. math:: f(x) = \frac{(f(x_2) - f(x_1))}{(x_2 - x_1)} \cdot + .. math:: f(x)=\frac{(f(x_2) - f(x_1))}{(x_2 - x_1)} \cdot (x - x_1) + f(x_1) Examples --------- >>> import numpy as np >>> import pandas as pd - >>> wind_speed_10m = np.array([[3], [4]]) - >>> wind_speed_80m = np.array([[6], [6]]) - >>> weather_df = pd.DataFrame(np.hstack((wind_speed_10m, + >>> wind_speed_10m=np.array([[3], [4]]) + >>> wind_speed_80m=np.array([[6], [6]]) + >>> weather_df=pd.DataFrame(np.hstack((wind_speed_10m, ... wind_speed_80m)), ... index=pd.date_range('1/1/2012', ... periods=2, @@ -73,17 +73,20 @@ def linear_interpolation_extrapolation(df, target_height): ... columns=[np.array(['wind_speed', ... 'wind_speed']), ... np.array([10, 80])]) - >>> value = linear_interpolation_extrapolation( + >>> value=linear_interpolation_extrapolation( ... weather_df['wind_speed'], 100)[0] """ # find closest heights heights_sorted = df.columns[ - sorted(range(len(df.columns)), - key=lambda i: abs(df.columns[i] - target_height))] - return ((df[heights_sorted[1]] - df[heights_sorted[0]]) / - (heights_sorted[1] - heights_sorted[0]) * - (target_height - heights_sorted[0]) + df[heights_sorted[0]]) + sorted( + range(len(df.columns)), + key=lambda i: abs(df.columns[i] - target_height), + ) + ] + return (df[heights_sorted[1]] - df[heights_sorted[0]]) / ( + heights_sorted[1] - heights_sorted[0] + ) * (target_height - heights_sorted[0]) + df[heights_sorted[0]] def logarithmic_interpolation_extrapolation(df, target_height): @@ -120,7 +123,7 @@ def logarithmic_interpolation_extrapolation(df, target_height): For the logarithmic inter- and extrapolation the following equation is used [1]_: - .. math:: f(x) = \frac{\ln(x) \cdot (f(x_2) - f(x_1)) - f(x_2) \cdot + .. math:: f(x)=\frac{\ln(x) \cdot (f(x_2) - f(x_1)) - f(x_2) \cdot \ln(x_1) + f(x_1) \cdot \ln(x_2)}{\ln(x_2) - \ln(x_1)} References @@ -133,13 +136,16 @@ def logarithmic_interpolation_extrapolation(df, target_height): """ # find closest heights heights_sorted = df.columns[ - sorted(range(len(df.columns)), - key=lambda i: abs(df.columns[i] - target_height))] - return ((np.log(target_height) * - (df[heights_sorted[1]] - df[heights_sorted[0]]) - - df[heights_sorted[1]] * np.log(heights_sorted[0]) + - df[heights_sorted[0]] * np.log(heights_sorted[1])) / - (np.log(heights_sorted[1]) - np.log(heights_sorted[0]))) + sorted( + range(len(df.columns)), + key=lambda i: abs(df.columns[i] - target_height), + ) + ] + return ( + np.log(target_height) * (df[heights_sorted[1]] - df[heights_sorted[0]]) + - df[heights_sorted[1]] * np.log(heights_sorted[0]) + + df[heights_sorted[0]] * np.log(heights_sorted[1]) + ) / (np.log(heights_sorted[1]) - np.log(heights_sorted[0])) def gauss_distribution(function_variable, standard_deviation, mean=0): @@ -168,7 +174,7 @@ def gauss_distribution(function_variable, standard_deviation, mean=0): ----- The following equation is used [1]_: - .. math:: f(x) = \frac{1}{\sigma \sqrt{2 \pi}} \exp + .. math:: f(x)=\frac{1}{\sigma \sqrt{2 \pi}} \exp \left[-\frac{(x-\mu)^2}{2 \sigma^2}\right] with: @@ -201,7 +207,7 @@ def estimate_turbulence_intensity(height, roughness_length): ----- The following equation is used [1]_: - .. math:: TI = \frac{1}{\ln\left(\frac{h}{z_\text{0}}\right)} + .. math:: TI=\frac{1}{\ln\left(\frac{h}{z_\text{0}}\right)} with: TI: turbulence intensity, h: height, :math:`z_{0}`: roughness length diff --git a/windpowerlib/turbine_cluster_modelchain.py b/windpowerlib/turbine_cluster_modelchain.py index 39c87480..5e0eeba1 100644 --- a/windpowerlib/turbine_cluster_modelchain.py +++ b/windpowerlib/turbine_cluster_modelchain.py @@ -142,10 +142,17 @@ class TurbineClusterModelChain(ModelChain): Used to set `hellman_exponent` in :func:`~.wind_speed.hellman`. """ - def __init__(self, power_plant, wake_losses_model='dena_mean', - smoothing=False, block_width=0.5, - standard_deviation_method='turbulence_intensity', - smoothing_order='wind_farm_power_curves', **kwargs): + + def __init__( + self, + power_plant, + wake_losses_model="dena_mean", + smoothing=False, + block_width=0.5, + standard_deviation_method="turbulence_intensity", + smoothing_order="wind_farm_power_curves", + **kwargs + ): super(TurbineClusterModelChain, self).__init__(power_plant, **kwargs) self.power_plant = power_plant @@ -187,39 +194,54 @@ def assign_power_curve(self, weather_df): """ # Get turbulence intensity from weather if existent turbulence_intensity = ( - weather_df['turbulence_intensity'].values.mean() if - 'turbulence_intensity' in - weather_df.columns.get_level_values(0) else None) + weather_df["turbulence_intensity"].values.mean() + if "turbulence_intensity" in weather_df.columns.get_level_values(0) + else None + ) roughness_length = ( - weather_df['roughness_length'].values.mean() if - 'roughness_length' in weather_df.columns.get_level_values(0) else - None) + weather_df["roughness_length"].values.mean() + if "roughness_length" in weather_df.columns.get_level_values(0) + else None + ) # Assign power curve - if (self.wake_losses_model == 'wind_farm_efficiency' or - self.wake_losses_model is None): + if ( + self.wake_losses_model == "wind_farm_efficiency" + or self.wake_losses_model is None + ): wake_losses_model_to_power_curve = self.wake_losses_model if self.wake_losses_model is None: - logging.debug('Wake losses in wind farms are not considered.') + logging.debug("Wake losses in wind farms are not considered.") else: - logging.debug('Wake losses considered with {}.'.format( - self.wake_losses_model)) + logging.debug( + "Wake losses considered with {}.".format( + self.wake_losses_model + ) + ) else: - logging.debug('Wake losses considered by {} wind '.format( - self.wake_losses_model) + 'efficiency curve.') + logging.debug( + "Wake losses considered by {} wind ".format( + self.wake_losses_model + ) + + "efficiency curve." + ) wake_losses_model_to_power_curve = None self.power_plant.assign_power_curve( wake_losses_model=wake_losses_model_to_power_curve, - smoothing=self.smoothing, block_width=self.block_width, + smoothing=self.smoothing, + block_width=self.block_width, standard_deviation_method=self.standard_deviation_method, smoothing_order=self.smoothing_order, roughness_length=roughness_length, - turbulence_intensity=turbulence_intensity) + turbulence_intensity=turbulence_intensity, + ) # Further logging messages if self.smoothing is False: - logging.debug('Aggregated power curve not smoothed.') + logging.debug("Aggregated power curve not smoothed.") else: - logging.debug('Aggregated power curve smoothed by method: ' + - self.standard_deviation_method) + logging.debug( + "Aggregated power curve smoothed by method: " + + self.standard_deviation_method + ) return self @@ -250,7 +272,7 @@ def run_model(self, weather_df): --------- >>> import numpy as np >>> import pandas as pd - >>> weather_df = pd.DataFrame(np.random.rand(2,6), + >>> weather_df=pd.DataFrame(np.random.rand(2,6), ... index=pd.date_range('1/1/2012', ... periods=2, ... freq='H'), @@ -270,15 +292,24 @@ def run_model(self, weather_df): self.assign_power_curve(weather_df) self.power_plant.mean_hub_height() wind_speed_hub = self.wind_speed_hub(weather_df) - density_hub = (None if (self.power_output_model == 'power_curve' and - self.density_correction is False) - else self.density_hub(weather_df)) - if (self.wake_losses_model != 'wind_farm_efficiency' and - self.wake_losses_model is not None): + density_hub = ( + None + if ( + self.power_output_model == "power_curve" + and self.density_correction is False + ) + else self.density_hub(weather_df) + ) + if ( + self.wake_losses_model != "wind_farm_efficiency" + and self.wake_losses_model is not None + ): # Reduce wind speed with wind efficiency curve wind_speed_hub = wake_losses.reduce_wind_speed( wind_speed_hub, - wind_efficiency_curve_name=self.wake_losses_model) - self.power_output = self.calculate_power_output(wind_speed_hub, - density_hub) + wind_efficiency_curve_name=self.wake_losses_model, + ) + self.power_output = self.calculate_power_output( + wind_speed_hub, density_hub + ) return self diff --git a/windpowerlib/wake_losses.py b/windpowerlib/wake_losses.py index 7bc83549..77e46834 100644 --- a/windpowerlib/wake_losses.py +++ b/windpowerlib/wake_losses.py @@ -46,15 +46,18 @@ def reduce_wind_speed(wind_speed, wind_efficiency_curve_name='dena_mean'): """ # Get wind efficiency curve wind_efficiency_curve = get_wind_efficiency_curve( - curve_name=wind_efficiency_curve_name) + curve_name=wind_efficiency_curve_name + ) # Reduce wind speed by wind efficiency reduced_wind_speed = wind_speed * np.interp( - wind_speed, wind_efficiency_curve['wind_speed'], - wind_efficiency_curve['efficiency']) + wind_speed, + wind_efficiency_curve["wind_speed"], + wind_efficiency_curve["efficiency"], + ) return reduced_wind_speed -def get_wind_efficiency_curve(curve_name='all'): +def get_wind_efficiency_curve(curve_name="all"): r""" Reads wind efficiency curve(s) specified in `curve_name`. @@ -98,20 +101,26 @@ def get_wind_efficiency_curve(curve_name='all'): -------- .. parsed-literal:: # Example to plot all curves - fig, ax = plt.subplots() /n - df = get_wind_efficiency_curve(curve_name='all') + fig, ax=plt.subplots() /n + df=get_wind_efficiency_curve(curve_name='all') for t in df.columns.get_level_values(0).unique(): - p = df[t].set_index('wind_speed')['efficiency'] - p.name = t - ax = p.plot(ax=ax, legend=True) + p=df[t].set_index('wind_speed')['efficiency'] + p.name=t + ax=p.plot(ax=ax, legend=True) plt.show() """ - possible_curve_names = ['dena_mean', 'knorr_mean', 'dena_extreme1', - 'dena_extreme2', 'knorr_extreme1', - 'knorr_extreme2', 'knorr_extreme3'] - - if curve_name == 'all': + possible_curve_names = [ + "dena_mean", + "knorr_mean", + "dena_extreme1", + "dena_extreme2", + "knorr_extreme1", + "knorr_extreme2", + "knorr_extreme3", + ] + + if curve_name == "all": curve_names = possible_curve_names elif isinstance(curve_name, str): curve_names = [curve_name] @@ -121,31 +130,39 @@ def get_wind_efficiency_curve(curve_name='all'): efficiency_curve = pd.DataFrame() for curve_name in curve_names: - if curve_name.split('_')[0] not in ['dena', 'knorr']: - raise ValueError("`curve_name` must be one of the following: " + - "{} but is {}".format(possible_curve_names, - curve_name)) - path = os.path.join(os.path.dirname(__file__), 'data', - 'wind_efficiency_curves_{}.csv'.format( - curve_name.split('_')[0])) + if curve_name.split("_")[0] not in ["dena", "knorr"]: + raise ValueError( + "`curve_name` must be one of the following: " + + "{} but is {}".format(possible_curve_names, curve_name) + ) + path = os.path.join( + os.path.dirname(__file__), + "data", + "wind_efficiency_curves_{}.csv".format(curve_name.split("_")[0]), + ) # Read wind efficiency curves from file wind_efficiency_curves = pd.read_csv(path) # Raise error if wind efficiency curve specified in 'curve_name' does # not exist if curve_name not in list(wind_efficiency_curves): - msg = ("Efficiency curve <{0}> does not exist. Must be one of the " - "following: {1}.") + msg = ( + "Efficiency curve <{0}> does not exist. Must be one of the " + "following: {1}." + ) raise ValueError(msg.format(curve_name, *possible_curve_names)) # Get wind efficiency curve and rename column containing efficiency - wec = wind_efficiency_curves[['wind_speed', curve_name]] + wec = wind_efficiency_curves[["wind_speed", curve_name]] if efficiency_curve.empty: efficiency_curve = pd.DataFrame( - {(curve_name, 'wind_speed'): wec['wind_speed'], - (curve_name, 'efficiency'): wec[curve_name]}) + { + (curve_name, "wind_speed"): wec["wind_speed"], + (curve_name, "efficiency"): wec[curve_name], + } + ) else: - efficiency_curve[(curve_name, 'wind_speed')] = wec['wind_speed'] - efficiency_curve[(curve_name, 'efficiency')] = wec[curve_name] + efficiency_curve[(curve_name, "wind_speed")] = wec["wind_speed"] + efficiency_curve[(curve_name, "efficiency")] = wec[curve_name] if len(curve_names) == 1: return efficiency_curve[curve_names[0]] else: diff --git a/windpowerlib/wind_farm.py b/windpowerlib/wind_farm.py index 2030ce48..3bb387a1 100644 --- a/windpowerlib/wind_farm.py +++ b/windpowerlib/wind_farm.py @@ -76,44 +76,44 @@ class WindFarm(object): >>> from windpowerlib import wind_farm >>> from windpowerlib import WindTurbine >>> import pandas as pd - >>> enerconE126 = { + >>> enerconE126={ ... 'hub_height': 135, ... 'rotor_diameter': 127, ... 'turbine_type': 'E-126/4200'} - >>> e126 = WindTurbine(**enerconE126) - >>> vestasV90 = { + >>> e126=WindTurbine(**enerconE126) + >>> vestasV90={ ... 'hub_height': 90, ... 'turbine_type': 'V90/2000', ... 'nominal_power': 2e6} - >>> v90 = WindTurbine(**vestasV90) + >>> v90=WindTurbine(**vestasV90) >>> # turbine fleet as DataFrame - >>> wind_turbine_fleet = pd.DataFrame( + >>> wind_turbine_fleet=pd.DataFrame( ... {'wind_turbine': [e126, v90], ... 'number_of_turbines': [6, None], ... 'total_capacity': [None, 3 * 2e6]}) - >>> example_farm = wind_farm.WindFarm(wind_turbine_fleet, name='my_farm') + >>> example_farm=wind_farm.WindFarm(wind_turbine_fleet, name='my_farm') >>> print(example_farm.nominal_power) 31200000.0 >>> # turbine fleet as a list of WindTurbineGroup objects using the >>> # 'to_group' method. - >>> wind_turbine_fleet = [e126.to_group(6), + >>> wind_turbine_fleet=[e126.to_group(6), ... v90.to_group(total_capacity=3 * 2e6)] - >>> example_farm = wind_farm.WindFarm(wind_turbine_fleet, name='my_farm') + >>> example_farm=wind_farm.WindFarm(wind_turbine_fleet, name='my_farm') >>> print(example_farm.nominal_power) 31200000.0 >>> # turbine fleet as list of dictionaries (not recommended) - >>> example_farm_data = { + >>> example_farm_data={ ... 'name': 'my_farm', ... 'wind_turbine_fleet': [{'wind_turbine': e126, ... 'number_of_turbines': 6}, ... {'wind_turbine': v90, ... 'total_capacity': 3 * 2e6}]} - >>> example_farm = wind_farm.WindFarm(**example_farm_data) + >>> example_farm=wind_farm.WindFarm(**example_farm_data) >>> print(example_farm.nominal_power) 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 != "": + 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): @@ -251,7 +275,7 @@ def mean_hub_height(self): ----- The following equation is used [1]_: - .. math:: h_{WF} = e^{\sum\limits_{k}{ln(h_{WT,k})} + .. math:: h_{WF}=e^{\sum\limits_{k}{ln(h_{WT,k})} \frac{P_{N,k}}{\sum\limits_{k}{P_{N,k}}}} with: @@ -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,83 +359,123 @@ 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: - logging.info("`wake_losses_model` is {} but wind farm ".format( - wake_losses_model) + "efficiency is NOT taken into " - "account as it is None.") + logging.info( + "`wake_losses_model` is {} but wind farm ".format( + wake_losses_model + ) + + "efficiency is NOT taken into " + "account as it is None." + ) self.power_curve = wind_farm_power_curve return self diff --git a/windpowerlib/wind_speed.py b/windpowerlib/wind_speed.py index 9c8c0033..c620dd43 100644 --- a/windpowerlib/wind_speed.py +++ b/windpowerlib/wind_speed.py @@ -48,9 +48,9 @@ def logarithmic_profile(wind_speed, wind_speed_height, hub_height, with: v: wind speed, h: height, :math:`z_{0}`: roughness length, - d: boundary layer offset (estimated by d = 0.7 * `obstacle_height`) + d: boundary layer offset (estimated by d=0.7 * `obstacle_height`) - For d = 0 it results in the following equation [2]_ [3]_: + For d=0 it results in the following equation [2]_ [3]_: .. 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)} @@ -73,22 +73,33 @@ def logarithmic_profile(wind_speed, wind_speed_height, hub_height, """ if 0.7 * obstacle_height > wind_speed_height: - raise ValueError("To take an obstacle height of {0} m ".format( - obstacle_height) + "into consideration, wind " + - "speed data of a greater height is needed.") + raise ValueError( + "To take an obstacle height of {0} m ".format(obstacle_height) + + "into consideration, wind " + + "speed data of a greater height is needed." + ) # Return np.array if wind_speed is np.array - if (isinstance(wind_speed, np.ndarray) and - isinstance(roughness_length, pd.Series)): + if isinstance(wind_speed, np.ndarray) and isinstance( + roughness_length, pd.Series + ): roughness_length = np.array(roughness_length) - return (wind_speed * np.log((hub_height - 0.7 * obstacle_height) / - roughness_length) / - np.log((wind_speed_height - 0.7 * obstacle_height) / - roughness_length)) - - -def hellman(wind_speed, wind_speed_height, hub_height, - roughness_length=None, hellman_exponent=None): + return ( + wind_speed + * np.log((hub_height - 0.7 * obstacle_height) / roughness_length) + / np.log( + (wind_speed_height - 0.7 * obstacle_height) / roughness_length + ) + ) + + +def hellman( + wind_speed, + wind_speed_height, + hub_height, + roughness_length=None, + hellman_exponent=None, +): r""" Calculates the wind speed at hub height using the hellman equation. @@ -106,14 +117,14 @@ def hellman(wind_speed, wind_speed_height, hub_height, Hub height of wind turbine. roughness_length : :pandas:`pandas.Series` or numpy.array or float Roughness length. If given and `hellman_exponent` is None: - `hellman_exponent` = 1 / ln(hub_height/roughness_length), - otherwise `hellman_exponent` = 1/7. Default: None. + `hellman_exponent`=1 / ln(hub_height/roughness_length), + otherwise `hellman_exponent`=1/7. Default: None. hellman_exponent : None or float The Hellman exponent, which combines the increase in wind speed due to stability of atmospheric conditions and surface roughness into one constant. If None and roughness length is given - `hellman_exponent` = 1 / ln(hub_height/roughness_length), - otherwise `hellman_exponent` = 1/7. Default: None. + `hellman_exponent`=1 / ln(hub_height/roughness_length), + otherwise `hellman_exponent`=1/7. Default: None. Returns ------- @@ -138,7 +149,7 @@ def hellman(wind_speed, wind_speed_height, hub_height, onshore and a value of 1/9 for offshore. The Hellman exponent can also be calulated by the following equation [2]_ [3]_: - .. math:: \alpha = \frac{1}{\ln\left(\frac{h_{hub}}{z_0} \right)} + .. math:: \alpha=\frac{1}{\ln\left(\frac{h_{hub}}{z_0} \right)} with: :math:`z_{0}`: roughness length @@ -160,10 +171,11 @@ def hellman(wind_speed, wind_speed_height, hub_height, if hellman_exponent is None: if roughness_length is not None: # Return np.array if wind_speed is np.array - if (isinstance(wind_speed, np.ndarray) and - isinstance(roughness_length, pd.Series)): + if isinstance(wind_speed, np.ndarray) and isinstance( + roughness_length, pd.Series + ): roughness_length = np.array(roughness_length) hellman_exponent = 1 / np.log(hub_height / roughness_length) else: - hellman_exponent = 1/7 + hellman_exponent = 1 / 7 return wind_speed * (hub_height / wind_speed_height) ** hellman_exponent diff --git a/windpowerlib/wind_turbine.py b/windpowerlib/wind_turbine.py index a4210895..cb73f698 100644 --- a/windpowerlib/wind_turbine.py +++ b/windpowerlib/wind_turbine.py @@ -94,29 +94,37 @@ class WindTurbine(object): -------- >>> import os >>> from windpowerlib import WindTurbine - >>> enerconE126 = { + >>> enerconE126={ ... 'hub_height': 135, ... 'turbine_type': 'E-126/4200'} - >>> e126 = WindTurbine(**enerconE126) + >>> e126=WindTurbine(**enerconE126) >>> print(e126.nominal_power) 4200000.0 >>> # Example with own path - >>> path = os.path.join(os.path.dirname(__file__), '../example/data') - >>> example_turbine = { + >>> path=os.path.join(os.path.dirname(__file__), '../example/data') + >>> example_turbine={ ... 'hub_height': 100, ... 'rotor_diameter': 70, ... 'turbine_type': 'DUMMY 3', ... 'path' : path} - >>> e_t_1 = WindTurbine(**example_turbine) + >>> e_t_1=WindTurbine(**example_turbine) >>> print(e_t_1.power_curve['value'][7]) 18000.0 >>> print(e_t_1.nominal_power) 1500000.0 """ - def __init__(self, hub_height, nominal_power=None, path='oedb', - power_curve=None, power_coefficient_curve=None, - rotor_diameter=None, turbine_type=None, **kwargs): + def __init__( + self, + hub_height, + nominal_power=None, + path="oedb", + power_curve=None, + power_coefficient_curve=None, + rotor_diameter=None, + turbine_type=None, + **kwargs + ): self.hub_height = hub_height self.turbine_type = turbine_type @@ -125,50 +133,56 @@ def __init__(self, hub_height, nominal_power=None, path='oedb', self.power_curve = power_curve self.power_coefficient_curve = power_coefficient_curve - if path == 'oedb': - path = os.path.join(os.path.dirname(__file__), 'oedb') + if path == "oedb": + path = os.path.join(os.path.dirname(__file__), "oedb") if turbine_type is not None and path is not None: if power_curve is None: try: - fn = os.path.join(path, 'power_curves.csv') + fn = os.path.join(path, "power_curves.csv") self.power_curve = get_turbine_data_from_file( - self.turbine_type, fn) + self.turbine_type, fn + ) except KeyError: msg = "No power curve found for {0}" logging.debug(msg.format(self.turbine_type)) if power_coefficient_curve is None: try: - fn = os.path.join(path, 'power_coefficient_curves.csv') + fn = os.path.join(path, "power_coefficient_curves.csv") self.power_coefficient_curve = get_turbine_data_from_file( - self.turbine_type, fn) + self.turbine_type, fn + ) except KeyError: msg = "No power coefficient curve found for {0}" logging.debug(msg.format(self.turbine_type)) if nominal_power is None or ( - rotor_diameter is None and - self.power_coefficient_curve is not None): + rotor_diameter is None + and self.power_coefficient_curve is not None + ): turbine_data = None try: - fn = os.path.join(path, 'turbine_data.csv') + fn = os.path.join(path, "turbine_data.csv") turbine_data = get_turbine_data_from_file( - self.turbine_type, fn) + self.turbine_type, fn + ) except KeyError: msg = "No turbine data found for {0}" logging.debug(msg.format(self.turbine_type)) if self.nominal_power is None and turbine_data is not None: - self.nominal_power = float(turbine_data['nominal_power']) + self.nominal_power = float(turbine_data["nominal_power"]) if self.rotor_diameter is None and turbine_data is not None: - self.rotor_diameter = float(turbine_data['rotor_diameter']) + self.rotor_diameter = float(turbine_data["rotor_diameter"]) if self.power_curve is None and self.power_coefficient_curve is None: - msg = ("The WindTurbine has been initialised without a power curve" - " and without a power coefficient curve.\nYou will not be" - " able to calculate the power output.\n" - " Check if the turbine type {0} is in your database file" - " or if you passed a valid curve.") + msg = ( + "The WindTurbine has been initialised without a power curve" + " and without a power coefficient curve.\nYou will not be" + " able to calculate the power output.\n" + " Check if the turbine type {0} is in your database file" + " or if you passed a valid curve." + ) warnings.warn(msg.format(turbine_type), WindpowerlibUserWarning) else: # power (coefficient) curve to pd.DataFrame in case of being dict @@ -176,45 +190,55 @@ def __init__(self, hub_height, nominal_power=None, path='oedb', self.power_curve = pd.DataFrame(self.power_curve) if isinstance(self.power_coefficient_curve, dict): self.power_coefficient_curve = pd.DataFrame( - self.power_coefficient_curve) + self.power_coefficient_curve + ) # sort power (coefficient) curve by wind speed if isinstance(self.power_curve, pd.DataFrame): - self.power_curve.sort_values(by='wind_speed') + self.power_curve.sort_values(by="wind_speed") elif self.power_curve is not None: - msg = "Type of power curve of {} is {} but should be " \ - "pd.DataFrame or dict." - raise TypeError(msg.format(self.__repr__(), - type(self.power_curve))) + msg = ( + "Type of power curve of {} is {} but should be " + "pd.DataFrame or dict." + ) + raise TypeError( + msg.format(self.__repr__(), type(self.power_curve)) + ) if isinstance(self.power_coefficient_curve, pd.DataFrame): - self.power_coefficient_curve.sort_values(by='wind_speed') + self.power_coefficient_curve.sort_values(by="wind_speed") elif self.power_coefficient_curve is not None: - msg = "Type of power coefficient curve of {} is {} but " \ - "should be pd.DataFrame or dict." - raise TypeError(msg.format(self.__repr__(), - type(self.power_coefficient_curve))) + msg = ( + "Type of power coefficient curve of {} is {} but " + "should be pd.DataFrame or dict." + ) + raise TypeError( + msg.format( + self.__repr__(), type(self.power_coefficient_curve) + ) + ) def __repr__(self): info = [] if self.nominal_power is not None: - info.append('nominal power={} W'.format(self.nominal_power)) + info.append("nominal power={} W".format(self.nominal_power)) if self.hub_height is not None: - info.append('hub height={} m'.format(self.hub_height)) + info.append("hub height={} m".format(self.hub_height)) if self.rotor_diameter is not None: - info.append('rotor diameter={} m'.format(self.rotor_diameter)) + info.append("rotor diameter={} m".format(self.rotor_diameter)) if self.power_coefficient_curve is not None: - info.append('power_coefficient_curve={}'.format('True')) + info.append("power_coefficient_curve={}".format("True")) else: - info.append('power_coefficient_curve={}'.format('False')) + info.append("power_coefficient_curve={}".format("False")) if self.power_curve is not None: - info.append('power_curve={}'.format('True')) + info.append("power_curve={}".format("True")) else: - info.append('power_curve={}'.format('False')) + info.append("power_curve={}".format("False")) if self.turbine_type is not None: - turbine_repr = 'Wind turbine: {name} {info}'.format( - name=self.turbine_type, info=info) + turbine_repr = "Wind turbine: {name} {info}".format( + name=self.turbine_type, info=info + ) else: - turbine_repr = 'Wind turbine: {info}'.format(info=info) + turbine_repr = "Wind turbine: {info}".format(info=info) return turbine_repr @@ -245,10 +269,10 @@ def to_group(self, number_turbines=None, total_capacity=None): Examples -------- >>> from windpowerlib import WindTurbine - >>> enerconE126 = { + >>> enerconE126={ ... 'hub_height': 135, ... 'turbine_type': 'E-126/4200'} - >>> e126 = WindTurbine(**enerconE126) + >>> e126=WindTurbine(**enerconE126) >>> e126.to_group(5).number_of_turbines 5 >>> e126.to_group().number_of_turbines @@ -271,22 +295,29 @@ def to_group(self, number_turbines=None, total_capacity=None): """ if number_turbines is not None and total_capacity is not None: - raise ValueError("The 'number' and the 'total_capacity' parameter " - "are mutually exclusive. Use just one of them.") + raise ValueError( + "The 'number' and the 'total_capacity' parameter " + "are mutually exclusive. Use just one of them." + ) elif total_capacity is not None: number_turbines = total_capacity / self.nominal_power elif number_turbines is None: number_turbines = 1 return WindTurbineGroup( - wind_turbine=self, number_of_turbines=number_turbines) + wind_turbine=self, number_of_turbines=number_turbines + ) # This is working for Python >= 3.5. # There a cleaner solutions for Python >= 3.6, once the support of 3.5 is # dropped: https://stackoverflow.com/a/50038614 -class WindTurbineGroup(NamedTuple('WindTurbineGroup', [ - ('wind_turbine', WindTurbine), ('number_of_turbines', float)])): +class WindTurbineGroup( + NamedTuple( + "WindTurbineGroup", + [("wind_turbine", WindTurbine), ("number_of_turbines", float)], + ) +): """ A simple data container to define more than one turbine of the same type. Use the :func:`~windpowerlib.wind_turbine.WindTurbine.to_group` method to @@ -300,13 +331,16 @@ class WindTurbineGroup(NamedTuple('WindTurbineGroup', [ 'number_of_turbines' : float The number of turbines. The number is not restricted to integer values. """ + __slots__ = () WindTurbineGroup.wind_turbine.__doc__ = ( - 'A :class:`~windpowerlib.wind_farm.WindTurbine` object.') + "A :class:`~windpowerlib.wind_farm.WindTurbine` object." +) WindTurbineGroup.number_of_turbines.__doc__ = ( - 'Number of turbines of type WindTurbine') + "Number of turbines of type WindTurbine" +) def get_turbine_data_from_file(turbine_type, path): @@ -339,9 +373,9 @@ def get_turbine_data_from_file(turbine_type, path): -------- >>> from windpowerlib import wind_turbine >>> import os - >>> path = os.path.join(os.path.dirname(__file__), '../example/data', + >>> path=os.path.join(os.path.dirname(__file__), '../example/data', ... 'power_curves.csv') - >>> d3 = get_turbine_data_from_file('DUMMY 3', path) + >>> d3=get_turbine_data_from_file('DUMMY 3', path) >>> print(d3['value'][7]) 18000.0 >>> print(d3['value'].max()) @@ -359,18 +393,18 @@ def get_turbine_data_from_file(turbine_type, path): raise KeyError(msg.format(turbine_type, list(df.index))) # if turbine in data file # get nominal power or power (coefficient) curve - if 'turbine_data' in path: + if "turbine_data" in path: return wpp_df else: wpp_df.dropna(axis=1, inplace=True) wpp_df = wpp_df.transpose().reset_index() - wpp_df.columns = ['wind_speed', 'value'] + wpp_df.columns = ["wind_speed", "value"] # transform wind speeds to floats - wpp_df['wind_speed'] = wpp_df['wind_speed'].apply(lambda x: float(x)) + wpp_df["wind_speed"] = wpp_df["wind_speed"].apply(lambda x: float(x)) return wpp_df -def load_turbine_data_from_oedb(schema='supply', table='wind_turbine_library'): +def load_turbine_data_from_oedb(schema="supply", table="wind_turbine_library"): r""" Loads turbine library from the OpenEnergy database (oedb). @@ -393,58 +427,82 @@ def load_turbine_data_from_oedb(schema='supply', table='wind_turbine_library'): """ # url of OpenEnergy Platform that contains the oedb - oep_url = 'http://oep.iks.cs.ovgu.de/' + oep_url = "http://oep.iks.cs.ovgu.de/" # load data result = requests.get( - oep_url + '/api/v0/schema/{}/tables/{}/rows/?'.format( - schema, table), ) + 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)) + 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__), 'oedb', - '{}.csv') + filename = os.path.join(os.path.dirname(__file__), "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 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() + 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() # power curve values in W - if curve_type == 'power_curve': + if curve_type == "power_curve": curves_df *= 1000 - curves_df.index.name = 'turbine_type' - curves_df.to_csv(filename.format('{}s'.format(curve_type))) + curves_df.index.name = "turbine_type" + curves_df.to_csv(filename.format("{}s".format(curve_type))) # get turbine data and save to file (excl. curves) turbine_data_df = turbine_data.drop( - ['power_curve_wind_speeds', 'power_curve_values', - 'power_coefficient_curve_wind_speeds', - 'power_coefficient_curve_values', - 'thrust_coefficient_curve_wind_speeds', - 'thrust_coefficient_curve_values'], axis=1).set_index('turbine_type') + [ + "power_curve_wind_speeds", + "power_curve_values", + "power_coefficient_curve_wind_speeds", + "power_coefficient_curve_values", + "thrust_coefficient_curve_wind_speeds", + "thrust_coefficient_curve_values", + ], + axis=1, + ).set_index("turbine_type") # nominal power in W - turbine_data_df['nominal_power'] = turbine_data_df[ - 'nominal_power'] * 1000 - turbine_data_df.to_csv(filename.format('turbine_data')) + turbine_data_df["nominal_power"] = turbine_data_df["nominal_power"] * 1000 + turbine_data_df.to_csv(filename.format("turbine_data")) return turbine_data -def get_turbine_types(turbine_library='local', print_out=True, filter_=True): +def get_turbine_types(turbine_library="local", print_out=True, filter_=True): r""" Get all provided wind turbine types provided. @@ -491,7 +549,7 @@ def get_turbine_types(turbine_library='local', print_out=True, filter_=True): Examples -------- >>> from windpowerlib import wind_turbine - >>> df = wind_turbine.get_turbine_types(print_out=False) + >>> 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 @@ -506,27 +564,34 @@ def get_turbine_types(turbine_library='local', print_out=True, filter_=True): Name: 1, dtype: object """ - if turbine_library == 'local': - filename = os.path.join(os.path.dirname(__file__), 'oedb', - 'turbine_data.csv') + if turbine_library == "local": + filename = os.path.join( + os.path.dirname(__file__), "oedb", "turbine_data.csv" + ) df = pd.read_csv(filename, index_col=0).reset_index() - elif turbine_library == 'oedb': + elif turbine_library == "oedb": df = load_turbine_data_from_oedb() else: - raise ValueError("`turbine_library` is '{}' ".format(turbine_library) + - "but must be 'local' or 'oedb'.") + raise ValueError( + "`turbine_library` is '{}' ".format(turbine_library) + + "but must be 'local' or '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) + 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']] + curves_df = df[ + ["manufacturer", "turbine_type", "has_power_curve", "has_cp_curve"] + ] if print_out: - pd.set_option('display.max_rows', len(curves_df)) + pd.set_option("display.max_rows", len(curves_df)) print(curves_df) - pd.reset_option('display.max_rows') + pd.reset_option("display.max_rows") return curves_df diff --git a/windpowerlib/wind_turbine_cluster.py b/windpowerlib/wind_turbine_cluster.py index b67b8739..0f2f7f8c 100644 --- a/windpowerlib/wind_turbine_cluster.py +++ b/windpowerlib/wind_turbine_cluster.py @@ -37,7 +37,8 @@ class WindTurbineCluster(object): :py:func:`assign_power_curve` for more information. """ - def __init__(self, wind_farms, name='', **kwargs): + + def __init__(self, wind_farms, name="", **kwargs): self.wind_farms = wind_farms self.name = name @@ -47,13 +48,13 @@ def __init__(self, wind_farms, name='', **kwargs): self.power_curve = None def __repr__(self): - if self.name is not '': - wf_repr = 'Wind turbine cluster: {name}'.format(name=self.name) + if self.name != "": + wf_repr = "Wind turbine cluster: {name}".format(name=self.name) else: info = [] for wind_farm in self.wind_farms: info.append(wind_farm) - wf_repr = r'Wind turbine cluster with: {info}'.format(info=info) + wf_repr = r"Wind turbine cluster with: {info}".format(info=info) return wf_repr @property @@ -69,8 +70,9 @@ def nominal_power(self): """ if not self._nominal_power: - self.nominal_power = sum(wind_farm.nominal_power - for wind_farm in self.wind_farms) + self.nominal_power = sum( + wind_farm.nominal_power for wind_farm in self.wind_farms + ) return self._nominal_power @nominal_power.setter @@ -97,7 +99,7 @@ def mean_hub_height(self): ----- The following equation is used [1]_: - .. math:: h_{WTC} = e^{\sum\limits_{k}{ln(h_{WF,k})} + .. math:: h_{WTC}=e^{\sum\limits_{k}{ln(h_{WF,k})} \frac{P_{N,k}}{\sum\limits_{k}{P_{N,k}}}} with: @@ -113,16 +115,25 @@ def mean_hub_height(self): p. 35 """ - self.hub_height = np.exp(sum( - np.log(wind_farm.hub_height) * wind_farm.nominal_power for - wind_farm in self.wind_farms) / self.nominal_power) + self.hub_height = np.exp( + sum( + np.log(wind_farm.hub_height) * wind_farm.nominal_power + for wind_farm in self.wind_farms + ) + / 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 turbine cluster. @@ -180,19 +191,30 @@ def assign_power_curve(self, wake_losses_model='wind_farm_efficiency', # Assign wind farm power curve farm.assign_power_curve( wake_losses_model=wake_losses_model, - smoothing=smoothing, block_width=block_width, + smoothing=smoothing, + block_width=block_width, standard_deviation_method=standard_deviation_method, smoothing_order=smoothing_order, - turbulence_intensity=turbulence_intensity, **kwargs) + turbulence_intensity=turbulence_intensity, + **kwargs, + ) # Create data frame from power curves of all wind farms - df = pd.concat([farm.power_curve.set_index(['wind_speed']).rename( - columns={'value': i}) for - farm, i in zip(self.wind_farms, - list(range(len(self.wind_farms))))], axis=1) + df = pd.concat( + [ + farm.power_curve.set_index(["wind_speed"]).rename( + columns={"value": i} + ) + for farm, i in zip( + self.wind_farms, list(range(len(self.wind_farms))) + ) + ], + axis=1, + ) # Sum up power curves cluster_power_curve = pd.DataFrame( - df.interpolate(method='index').sum(axis=1)) - cluster_power_curve.columns = ['value'] + df.interpolate(method="index").sum(axis=1) + ) + cluster_power_curve.columns = ["value"] # Return wind speed (index) to a column of the data frame cluster_power_curve.reset_index(inplace=True) self.power_curve = cluster_power_curve