diff --git a/tests/test_modelchain.py b/tests/test_modelchain.py index 04bf19e5..5b2c8b2f 100644 --- a/tests/test_modelchain.py +++ b/tests/test_modelchain.py @@ -11,13 +11,14 @@ import windpowerlib.wind_turbine as wt import windpowerlib.modelchain as mc +from windpowerlib.tools import WindpowerlibUserWarning class TestModelChain: @classmethod - def setup_class(self): + def setup_class(cls): """Setup default values""" - self.test_turbine = { + cls.test_turbine = { "hub_height": 100, "turbine_type": "E-126/4200", "power_curve": pd.DataFrame( @@ -31,7 +32,7 @@ def setup_class(self): 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]]) - self.weather_df = pd.DataFrame( + cls.weather_df = pd.DataFrame( np.hstack( ( temperature_2m, @@ -434,23 +435,54 @@ def test_modelchain_with_power_coefficient_curve_as_dict(self): def test_heigths_as_string(self): """Test run_model if data heights are of type string.""" - test_turbine = {'hub_height': 100, - 'rotor_diameter': 80, - 'turbine_type': 'E-126/4200'} + test_turbine = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "E-126/4200", + } # Convert data heights to str string_weather = self.weather_df.copy() - string_weather.columns = pd.MultiIndex.from_arrays([ - string_weather.columns.get_level_values(0), - string_weather.columns.get_level_values(1).astype(str)]) + string_weather.columns = pd.MultiIndex.from_arrays( + [ + string_weather.columns.get_level_values(0), + string_weather.columns.get_level_values(1).astype(str), + ] + ) # Heights in the original DataFrame are of type np.int64 - assert isinstance(self.weather_df.columns.get_level_values(1)[0], - np.int64) + assert isinstance( + self.weather_df.columns.get_level_values(1)[0], np.int64 + ) assert isinstance(string_weather.columns.get_level_values(1)[0], str) - 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(string_weather) + + def test_weather_with_nan_values(self, recwarn): + """Test warning if weather data contain nan values.""" + test_turbine = { + "hub_height": 100, + "rotor_diameter": 80, + "turbine_type": "E-126/4200", + } + nan_weather = self.weather_df.copy() + nan_weather.loc[1, ("temperature", 10)] = np.nan + test_modelchain = { + "power_output_model": "power_curve", + "density_corr": True, + } + test_mc = mc.ModelChain( + wt.WindTurbine(**test_turbine), **test_modelchain + ) + msg = "'temperature', 10" + with pytest.warns(WindpowerlibUserWarning, match=msg): + test_mc.run_model(nan_weather) + test_mc.run_model(self.weather_df) + assert len(recwarn) == 0 diff --git a/windpowerlib/modelchain.py b/windpowerlib/modelchain.py index 6726541a..57d9dfd4 100644 --- a/windpowerlib/modelchain.py +++ b/windpowerlib/modelchain.py @@ -8,8 +8,7 @@ """ import logging import pandas as pd -from windpowerlib import (wind_speed, density, temperature, power_output, - tools) +from windpowerlib import wind_speed, density, temperature, power_output, tools class ModelChain(object): @@ -511,10 +510,7 @@ def run_model(self, weather_df): 'wind_speed' """ - # Convert data heights to integer. In some case they are strings. - weather_df.columns = pd.MultiIndex.from_arrays([ - weather_df.columns.get_level_values(0), - pd.to_numeric(weather_df.columns.get_level_values(1))]) + weather_df = tools.check_weather_data(weather_df) wind_speed_hub = self.wind_speed_hub(weather_df) density_hub = ( diff --git a/windpowerlib/tools.py b/windpowerlib/tools.py index faab0202..07ec376f 100644 --- a/windpowerlib/tools.py +++ b/windpowerlib/tools.py @@ -7,6 +7,7 @@ """ import numpy as np import warnings +import pandas as pd class WindpowerlibUserWarning(UserWarning): @@ -221,3 +222,30 @@ def estimate_turbulence_intensity(height, roughness_length): """ return 1 / (np.log(height / roughness_length)) + + +def check_weather_data(weather_df): + """ + Check weather Data Frame. + + - Raise warning if there are nan values. + - Convert columns if heights are string and not numeric. + + """ + # Convert data heights to integer. In some case they are strings. + weather_df.columns = pd.MultiIndex.from_arrays( + [ + weather_df.columns.get_level_values(0), + pd.to_numeric(weather_df.columns.get_level_values(1)), + ] + ) + + # check for nan values + if weather_df.isnull().any().any(): + nan_columns = list(weather_df.columns[weather_df.isnull().any()]) + msg = ( + "The following columns of the weather data contain invalid " + "values like 'nan': {0}" + ) + warnings.warn(msg.format(nan_columns), WindpowerlibUserWarning) + return weather_df diff --git a/windpowerlib/turbine_cluster_modelchain.py b/windpowerlib/turbine_cluster_modelchain.py index a72ae52a..b6309ac2 100644 --- a/windpowerlib/turbine_cluster_modelchain.py +++ b/windpowerlib/turbine_cluster_modelchain.py @@ -10,7 +10,7 @@ import logging import pandas as pd from windpowerlib import wake_losses -from windpowerlib.modelchain import ModelChain +from windpowerlib.modelchain import ModelChain, tools class TurbineClusterModelChain(ModelChain): @@ -289,10 +289,7 @@ def run_model(self, weather_df): 'wind_speed' """ - # Convert data heights to integer. In some case they are strings. - weather_df.columns = pd.MultiIndex.from_arrays([ - weather_df.columns.get_level_values(0), - pd.to_numeric(weather_df.columns.get_level_values(1))]) + weather_df = tools.check_weather_data(weather_df) self.assign_power_curve(weather_df) self.power_plant.mean_hub_height()