From ed87cb76d527fcf65e7be2db672870012204c07e Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 23 Aug 2019 15:40:51 -0700 Subject: [PATCH 01/10] modelchain deprecations, remove times --- pvlib/modelchain.py | 124 ++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 69 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 1455a57ad3..694f0bb884 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -8,7 +8,6 @@ from functools import partial import warnings -import pandas as pd from pvlib import (solarposition, pvsystem, clearsky, atmosphere, tools) from pvlib.tracking import SingleAxisTracker @@ -385,13 +384,6 @@ def dc_model(self, model): self._dc_model = self.pvsyst elif model == 'pvwatts': self._dc_model = self.pvwatts_dc - elif model == 'singlediode': - warnings.warn('DC model keyword singlediode used for ' - 'ModelChain object. singlediode is ' - 'ambiguous, use desoto instead. singlediode ' - 'keyword will be removed in v0.7.0 and ' - 'later', pvlibDeprecationWarning) - self._dc_model = self.desoto else: raise ValueError(model + ' is not a valid DC power model') else: @@ -722,7 +714,7 @@ def effective_irradiance_model(self): fd*self.total_irrad['poa_diffuse']) return self - def complete_irradiance(self, times=None, weather=None): + def complete_irradiance(self, weather, times=None): """ Determine the missing irradiation columns. Only two of the following data columns (dni, ghi, dhi) are needed to calculate @@ -734,19 +726,19 @@ def complete_irradiance(self, times=None, weather=None): Parameters ---------- - times : None or DatetimeIndex, default None - Times at which to evaluate the model. Can be None if - attribute `times` is already set. - weather : None or pandas.DataFrame, default None - Table with at least two columns containing one of the - following data sets: dni, dhi, ghi. Can be None if attribute - `weather` is already set. + weather : DataFrame + Column names must be ``'dni'``, ``'ghi'``, ``'dhi'``, + ``'wind_speed'``, ``'temp_air'``. All irradiance components + are required. Air temperature of 20 C and wind speed + of 0 m/s will be added to the DataFrame if not provided. + times : None, deprecated + Deprecated argument included for backwards compatibility. Returns ------- self - Assigns attributes: times, weather + Assigns attributes: weather Examples -------- @@ -758,19 +750,23 @@ def complete_irradiance(self, times=None, weather=None): >>> # my_weather containing 'dhi' and 'ghi'. >>> mc = ModelChain(my_system, my_location) # doctest: +SKIP - >>> mc.complete_irradiance(my_datetime, my_weather) # doctest: +SKIP - >>> mc.run_model() # doctest: +SKIP + >>> mc.complete_irradiance(my_weather) # doctest: +SKIP + >>> mc.run_model(mc.weather) # doctest: +SKIP >>> # my_weather containing 'dhi', 'ghi' and 'dni'. >>> mc = ModelChain(my_system, my_location) # doctest: +SKIP - >>> mc.run_model(my_datetime, my_weather) # doctest: +SKIP + >>> mc.run_model(my_weather) # doctest: +SKIP """ - if weather is not None: - self.weather = weather + self.weather = weather + if times is not None: - self.times = times + warnings.warn('times keyword argument is deprecated and will be ' + 'removed in 0.8. Set the index of the weather ' + 'argument instead.', pvlibDeprecationWarning) + self.solar_position = self.location.get_solarposition( - self.times, method=self.solar_position_method) + self.weather.index, method=self.solar_position_method) + icolumns = set(self.weather.columns) wrn_txt = ("This function is not safe at the moment.\n" + "Results can be too high or negative.\n" + @@ -798,62 +794,50 @@ def complete_irradiance(self, times=None, weather=None): return self - def prepare_inputs(self, times=None, weather=None): + def prepare_inputs(self, weather, times=None): """ Prepare the solar position, irradiance, and weather inputs to the model. Parameters ---------- - times : None or DatetimeIndex, default None - Times at which to evaluate the model. Can be None if - attribute `times` is already set. - weather : None or DataFrame, default None - If ``None``, the weather attribute is used. Column names - must be ``'dni'``, ``'ghi'``, ``'dhi'``, ``'wind_speed'``, - ``'temp_air'``. All irradiance components are required. - Assumes air temperature is 20 C and wind speed is 0 m/s if - not provided. + weather : DataFrame + Column names must be ``'dni'``, ``'ghi'``, ``'dhi'``, + ``'wind_speed'``, ``'temp_air'``. All irradiance components + are required. Air temperature of 20 C and wind speed + of 0 m/s will be added to the DataFrame if not provided. + times : None, deprecated + Deprecated argument included for backwards compatibility. Notes ----- - Assigns attributes: ``times``, ``solar_position``, ``airmass``, + Assigns attributes: ``solar_position``, ``airmass``, ``total_irrad``, `aoi` See also -------- ModelChain.complete_irradiance """ - if weather is not None: - self.weather = weather - if self.weather is None: - self.weather = pd.DataFrame(index=times) + + if not {'ghi', 'dni', 'dhi'} <= set(weather.columns): + raise ValueError( + "Uncompleted irradiance data set. Please check your input " + "data.\nData set needs to have 'dni', 'dhi' and 'ghi'.\n" + "Detected data: {0}".format(list(weather.columns))) + + self.weather = weather if times is not None: - self.times = times + warnings.warn('times keyword argument is deprecated and will be ' + 'removed in 0.8. Set the index of the weather ' + 'argument instead.', pvlibDeprecationWarning) self.solar_position = self.location.get_solarposition( - self.times, method=self.solar_position_method) + self.weather.index, method=self.solar_position_method) self.airmass = self.location.get_airmass( solar_position=self.solar_position, model=self.airmass_model) - if not any([x in ['ghi', 'dni', 'dhi'] for x in self.weather.columns]): - warnings.warn('Clear sky assumption for no input irradiance is ' - 'deprecated and will be removed in v0.7.0. Use ' - 'location.get_clearsky instead', - pvlibDeprecationWarning) - self.weather[['ghi', 'dni', 'dhi']] = self.location.get_clearsky( - self.solar_position.index, self.clearsky_model, - solar_position=self.solar_position, - airmass_absolute=self.airmass['airmass_absolute']) - - if not {'ghi', 'dni', 'dhi'} <= set(self.weather.columns): - raise ValueError( - "Uncompleted irradiance data set. Please check your input " - "data.\nData set needs to have 'dni', 'dhi' and 'ghi'.\n" - "Detected data: {0}".format(list(self.weather.columns))) - # PVSystem.get_irradiance and SingleAxisTracker.get_irradiance # and PVSystem.get_aoi and SingleAxisTracker.get_aoi # have different method signatures. Use partial to handle @@ -897,32 +881,34 @@ def prepare_inputs(self, times=None, weather=None): self.weather['temp_air'] = 20 return self - def run_model(self, times=None, weather=None): + def run_model(self, weather, times=None): """ Run the model. Parameters ---------- - times : None or DatetimeIndex, default None - Times at which to evaluate the model. Can be None if - attribute `times` is already set. - weather : None or DataFrame, default None - If ``None``, the weather attribute is used. Column names - must be ``'dni'``, ``'ghi'``, ``'dhi'``, ``'wind_speed'``, - ``'temp_air'``. All irradiance components are required. - Assumes air temperature is 20 C and wind speed is 0 m/s if - not provided. + weather : DataFrame + Column names must be ``'dni'``, ``'ghi'``, ``'dhi'``, + ``'wind_speed'``, ``'temp_air'``. All irradiance components + are required. Air temperature of 20 C and wind speed + of 0 m/s will be added to the DataFrame if not provided. + times : None, deprecated + Deprecated argument included for backwards compatibility. Returns ------- self - Assigns attributes: times, solar_position, airmass, irradiance, + Assigns attributes: solar_position, airmass, irradiance, total_irrad, effective_irradiance, weather, temps, aoi, aoi_modifier, spectral_modifier, dc, ac, losses. """ + if times is not None: + warnings.warn('times keyword argument is deprecated and will be ' + 'removed in 0.8. Set the index of the weather ' + 'argument instead.', pvlibDeprecationWarning) - self.prepare_inputs(times, weather) + self.prepare_inputs(weather) self.aoi_model() self.spectral_model() self.effective_irradiance_model() From 6d0f1957dcce8414a8704f8c65bd3552d8f33397 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 23 Aug 2019 16:11:56 -0700 Subject: [PATCH 02/10] tests --- pvlib/modelchain.py | 2 +- pvlib/test/test_modelchain.py | 108 ++++++++++++++-------------------- 2 files changed, 46 insertions(+), 64 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 694f0bb884..1804f74762 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -775,7 +775,7 @@ def complete_irradiance(self, weather, times=None): if {'ghi', 'dhi'} <= icolumns and 'dni' not in icolumns: clearsky = self.location.get_clearsky( - times, solar_position=self.solar_position) + self.weather.index, solar_position=self.solar_position) self.weather.loc[:, 'dni'] = pvlib.irradiance.dni( self.weather.loc[:, 'ghi'], self.weather.loc[:, 'dhi'], self.solar_position.zenith, diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 7fac62542c..b9bac34a0b 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -144,29 +144,42 @@ def test_orientation_strategy(strategy, expected, system, location): assert system.surface_azimuth == expected[1] -@requires_scipy -def test_run_model(system, location): +def test_run_model_with_irradiance(system, location): mc = ModelChain(system, location) times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') + irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150}, + index=times) + ac = mc.run_model(irradiance).ac - with pytest.warns(pvlibDeprecationWarning): - ac = mc.run_model(times).ac - - expected = pd.Series(np.array([ 183.522449305, -2.00000000e-02]), + expected = pd.Series(np.array([ 1.90054749e+02, -2.00000000e-02]), index=times) - assert_series_equal(ac, expected, check_less_precise=1) + assert_series_equal(ac, expected) -def test_run_model_with_irradiance(system, location): +def test_run_model_times(system, location): mc = ModelChain(system, location) times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150}, index=times) - ac = mc.run_model(times, weather=irradiance).ac + with pytest.warns(pvlibDeprecationWarning): + mc.run_model(irradiance, times=times) - expected = pd.Series(np.array([ 1.90054749e+02, -2.00000000e-02]), - index=times) - assert_series_equal(ac, expected) + +def test_prepare_inputs_times(system, location): + mc = ModelChain(system, location) + times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') + irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150}, + index=times) + with pytest.warns(pvlibDeprecationWarning): + mc.prepare_inputs(irradiance, times=times) + + +def test_complete_irradiance_times(system, location): + mc = ModelChain(system, location) + times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') + irradiance = pd.DataFrame({'ghi': 600., 'dhi': 150.}, index=times) + with pytest.warns(pvlibDeprecationWarning): + mc.complete_irradiance(irradiance, times=times) def test_run_model_perez(system, location): @@ -174,7 +187,7 @@ def test_run_model_perez(system, location): times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150}, index=times) - ac = mc.run_model(times, weather=irradiance).ac + ac = mc.run_model(irradiance).ac expected = pd.Series(np.array([ 190.194545796, -2.00000000e-02]), index=times) @@ -187,7 +200,7 @@ def test_run_model_gueymard_perez(system, location): times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150}, index=times) - ac = mc.run_model(times, weather=irradiance).ac + ac = mc.run_model(irradiance).ac expected = pd.Series(np.array([ 190.194760203, -2.00000000e-02]), index=times) @@ -199,7 +212,7 @@ def test_run_model_with_weather(system, location, weather, mocker): m = mocker.spy(system, 'sapm_celltemp') weather['wind_speed'] = 5 weather['temp_air'] = 10 - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 1 # assert_called_once_with cannot be used with series, so need to use # assert_series_equal on call_args @@ -213,7 +226,7 @@ def test_run_model_tracker(system, location, weather, mocker): inverter_parameters=system.inverter_parameters) mocker.spy(system, 'singleaxis') mc = ModelChain(system, location) - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert system.singleaxis.call_count == 1 assert (mc.tracking.columns == ['tracker_theta', 'aoi', 'surface_azimuth', 'surface_tilt']).all() @@ -255,7 +268,7 @@ def test_infer_dc_model(system, cec_dc_snl_ac_system, pvsyst_dc_snl_ac_system, m = mocker.spy(system, dc_model_function[dc_model]) mc = ModelChain(system, location, aoi_model='no_loss', spectral_model='no_loss') - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 1 assert isinstance(mc.dc, (pd.Series, pd.DataFrame)) @@ -280,7 +293,7 @@ def test_dc_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather, m = mocker.spy(sys.modules[__name__], 'poadc') mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, dc_model=poadc, aoi_model='no_loss', spectral_model='no_loss') - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 1 assert isinstance(mc.ac, (pd.Series, pd.DataFrame)) assert not mc.ac.empty @@ -304,7 +317,7 @@ def test_ac_models(system, cec_dc_adr_ac_system, pvwatts_dc_pvwatts_ac_system, if ac_model == 'pvwatts': ac_model += '_ac' m = mocker.spy(system, ac_model) - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 1 assert isinstance(mc.ac, pd.Series) assert not mc.ac.empty @@ -316,7 +329,7 @@ def test_ac_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather, m = mocker.spy(sys.modules[__name__], 'acdc') mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, ac_model=acdc, aoi_model='no_loss', spectral_model='no_loss') - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 1 assert_series_equal(mc.ac, mc.dc) assert not mc.ac.empty @@ -333,7 +346,7 @@ def test_aoi_models(system, location, aoi_model, method, weather, mocker): mc = ModelChain(system, location, dc_model='sapm', aoi_model=aoi_model, spectral_model='no_loss') m = mocker.spy(system, method) - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 1 assert isinstance(mc.ac, pd.Series) assert not mc.ac.empty @@ -344,7 +357,7 @@ def test_aoi_models(system, location, aoi_model, method, weather, mocker): def test_aoi_model_no_loss(system, location, weather): mc = ModelChain(system, location, dc_model='sapm', aoi_model='no_loss', spectral_model='no_loss') - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert mc.aoi_modifier == 1.0 assert not mc.ac.empty assert mc.ac[0] > 150 and mc.ac[0] < 200 @@ -355,7 +368,7 @@ def test_aoi_model_user_func(system, location, weather, mocker): m = mocker.spy(sys.modules[__name__], 'constant_aoi_loss') mc = ModelChain(system, location, dc_model='sapm', aoi_model=constant_aoi_loss, spectral_model='no_loss') - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 1 assert mc.aoi_modifier == 0.9 assert not mc.ac.empty @@ -376,8 +389,7 @@ def test_spectral_models(system, location, spectral_model, weather): weather['precipitable_water'] = [0.3, 0.5] mc = ModelChain(system, location, dc_model='sapm', aoi_model='no_loss', spectral_model=spectral_model) - spectral_modifier = mc.run_model(times=weather.index, - weather=weather).spectral_modifier + spectral_modifier = mc.run_model(weather).spectral_modifier assert isinstance(spectral_modifier, (pd.Series, float, int)) @@ -394,7 +406,7 @@ def test_losses_models_pvwatts(pvwatts_dc_pvwatts_ac_system, location, weather, mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, dc_model='pvwatts', aoi_model='no_loss', spectral_model='no_loss', losses_model='pvwatts') - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 1 m.assert_called_with(age=age) assert isinstance(mc.ac, (pd.Series, pd.DataFrame)) @@ -405,7 +417,7 @@ def test_losses_models_pvwatts(pvwatts_dc_pvwatts_ac_system, location, weather, mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, dc_model='pvwatts', aoi_model='no_loss', spectral_model='no_loss', losses_model='no_loss') - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert not np.allclose(mc.dc, dc_with_loss, equal_nan=True) @@ -415,7 +427,7 @@ def test_losses_models_ext_def(pvwatts_dc_pvwatts_ac_system, location, weather, mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, dc_model='pvwatts', aoi_model='no_loss', spectral_model='no_loss', losses_model=constant_losses) - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 1 assert isinstance(mc.ac, (pd.Series, pd.DataFrame)) assert mc.losses == 0.9 @@ -429,7 +441,7 @@ def test_losses_models_no_loss(pvwatts_dc_pvwatts_ac_system, location, weather, aoi_model='no_loss', spectral_model='no_loss', losses_model='no_loss') assert mc.losses_model == mc.no_extra_losses - mc.run_model(weather.index, weather=weather) + mc.run_model(weather) assert m.call_count == 0 assert mc.losses == 1 @@ -473,36 +485,6 @@ def test_bad_get_orientation(): modelchain.get_orientation('bad value') -@fail_on_pvlib_version('0.7') -def test_deprecated_07(): - # explicit system creation call because fail_on_pvlib_version - # does not support decorators. - # does not matter what the parameters are, just fake it until we make it - module_parameters = {'R_sh_ref': 1, 'a_ref': 1, 'I_o_ref': 1, - 'alpha_sc': 1, 'I_L_ref': 1, 'R_s': 1} - system = PVSystem(module_parameters=module_parameters) - with pytest.warns(pvlibDeprecationWarning): - ModelChain(system, location, - dc_model='singlediode', # this should fail after 0.7 - aoi_model='no_loss', spectral_model='no_loss', - ac_model='snlinverter') - - -@requires_tables -@fail_on_pvlib_version('0.7') -def test_deprecated_clearsky_07(): - # explicit system creation call because fail_on_pvlib_version - # does not support decorators. - system = PVSystem(module_parameters={'pdc0': 1, 'gamma_pdc': -0.003}) - location = Location(32.2, -110.9) - mc = ModelChain(system, location, dc_model='pvwatts', ac_model='pvwatts', - aoi_model='no_loss', spectral_model='no_loss') - times = pd.date_range(start='20160101 1200-0700', - end='20160101 1800-0700', freq='6H') - with pytest.warns(pvlibDeprecationWarning): - mc.prepare_inputs(times=times) - - @requires_scipy def test_basic_chain_required(sam_data): times = pd.date_range(start='20160101 1200-0700', @@ -638,7 +620,7 @@ def test_complete_irradiance_clean_run(system, location): i = pd.DataFrame( {'dni': [2, 3], 'dhi': [4, 6], 'ghi': [9, 5]}, index=times) - mc.complete_irradiance(times, weather=i) + mc.complete_irradiance(i) assert_series_equal(mc.weather['dni'], pd.Series([2, 3], index=times, name='dni')) @@ -658,18 +640,18 @@ def test_complete_irradiance(system, location): 'dhi': [356.543700, 465.44400]}, index=times) with pytest.warns(UserWarning): - mc.complete_irradiance(times, weather=i[['ghi', 'dni']]) + mc.complete_irradiance(i[['ghi', 'dni']]) assert_series_equal(mc.weather['dhi'], pd.Series([356.543700, 465.44400], index=times, name='dhi')) with pytest.warns(UserWarning): - mc.complete_irradiance(times, weather=i[['dhi', 'dni']]) + mc.complete_irradiance(i[['dhi', 'dni']]) assert_series_equal(mc.weather['ghi'], pd.Series([372.103976116, 497.087579068], index=times, name='ghi')) - mc.complete_irradiance(times, weather=i[['dhi', 'ghi']]) + mc.complete_irradiance(i[['dhi', 'ghi']]) assert_series_equal(mc.weather['dni'], pd.Series([49.756966, 62.153947], index=times, name='dni')) From fc42dd28a9d67d60e4d7fa445ec2982bd7d38c22 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 23 Aug 2019 16:27:58 -0700 Subject: [PATCH 03/10] add requires_tables decorator --- pvlib/test/test_modelchain.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index b9bac34a0b..c1c526aad6 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -14,7 +14,7 @@ import pytest from test_pvsystem import sam_data, pvsyst_module_params -from conftest import fail_on_pvlib_version, requires_scipy, requires_tables +from conftest import requires_scipy, requires_tables @pytest.fixture @@ -174,6 +174,7 @@ def test_prepare_inputs_times(system, location): mc.prepare_inputs(irradiance, times=times) +@requires_tables def test_complete_irradiance_times(system, location): mc = ModelChain(system, location) times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') From c51bce422f5232a971cd06c8ff6442e4f18195f6 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Tue, 10 Sep 2019 20:34:07 -0700 Subject: [PATCH 04/10] flake8 --- pvlib/test/test_modelchain.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 20f23f5297..b9ee053e26 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -127,7 +127,7 @@ def weather(): def test_ModelChain_creation(system, location): - mc = ModelChain(system, location) + ModelChain(system, location) @pytest.mark.parametrize('strategy, expected', [ @@ -151,7 +151,7 @@ def test_run_model_with_irradiance(system, location): index=times) ac = mc.run_model(irradiance).ac - expected = pd.Series(np.array([ 1.90054749e+02, -2.00000000e-02]), + expected = pd.Series(np.array([1.90054749e+02, -2.00000000e-02]), index=times) assert_series_equal(ac, expected) @@ -613,7 +613,7 @@ def test_basic_chain_alt_az(sam_data, cec_inverter_parameters, surface_tilt=surface_tilt, surface_azimuth=surface_azimuth) - expected = pd.Series(np.array([ 115.40352679, -2.00000000e-02]), + expected = pd.Series(np.array([115.40352679, -2.00000000e-02]), index=times) assert_series_equal(ac, expected, check_less_precise=1) @@ -634,7 +634,7 @@ def test_basic_chain_strategy(sam_data, cec_inverter_parameters, cec_inverter_parameters, orientation_strategy='south_at_latitude_tilt', altitude=altitude) - expected = pd.Series(np.array([ 183.522449305, -2.00000000e-02]), + expected = pd.Series(np.array([183.522449305, -2.00000000e-02]), index=times) assert_series_equal(ac, expected, check_less_precise=1) @@ -659,7 +659,7 @@ def test_basic_chain_altitude_pressure(sam_data, cec_inverter_parameters, surface_azimuth=surface_azimuth, pressure=93194) - expected = pd.Series(np.array([ 116.595664887, -2.00000000e-02]), + expected = pd.Series(np.array([116.595664887, -2.00000000e-02]), index=times) assert_series_equal(ac, expected, check_less_precise=1) @@ -670,7 +670,7 @@ def test_basic_chain_altitude_pressure(sam_data, cec_inverter_parameters, surface_azimuth=surface_azimuth, altitude=altitude) - expected = pd.Series(np.array([ 116.595664887, -2.00000000e-02]), + expected = pd.Series(np.array([116.595664887, -2.00000000e-02]), index=times) assert_series_equal(ac, expected, check_less_precise=1) From 20d5f377bc50d7ace3b421ff8224b12f7d8086c1 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Tue, 10 Sep 2019 20:34:21 -0700 Subject: [PATCH 05/10] remove ModelChain.singlediode --- docs/sphinx/source/whatsnew/v0.7.0.rst | 5 +++-- pvlib/modelchain.py | 18 ------------------ 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index d9045a5cfb..63a8db0fac 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -21,7 +21,7 @@ API Changes old `pvsystem.sapm_celltemp` returned a `DataFrame` with both cell and module temperatures. - Created `temperature.sapm_module` to return module temperature using the SAPM temperature model. - Changed the order of arguments for`pvsystem.sapm_celltemp`, - `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` to be consistent + `pvsystem.pvsyst_celltemp` and `PVSystem.sapm_celltemp` to be consistent among cell temperature model functions. - Removed `model` as a kwarg from `temperature.sapm_cell` and `temperature.pvsyst_cell`. These functions now require model-specific parameters. @@ -35,7 +35,7 @@ API Changes 'open_rack_polymer_thinfilm_steel' and '22x_concentrator_tracker' are considered obsolete and have been removed. * Changes to `PVSystem` class - - Changed the `model` kwarg in `PVSystem.sapm_celltemp` and + - Changed the `model` kwarg in `PVSystem.sapm_celltemp` and `PVSystem.pvsyst_celltemp` to `parameter_set`. `parameter_set` expects a str which is a valid key for `temperature.TEMPERATURE_MODEL_PARAMETERS` for the corresponding temperature model. @@ -114,6 +114,7 @@ Removal of prior version deprecations * Removed `atmosphere.relativeairmass`. * Removed `solarposition.get_sun_rise_set_transit`. * Removed `tmy` module. +* Removed `ModelChain.singlediode` method. Contributors ~~~~~~~~~~~~ diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index a68a6722cb..09017e33ea 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -472,24 +472,6 @@ def cec(self): def pvsyst(self): return self._singlediode(self.system.calcparams_pvsyst) - def singlediode(self): - """Deprecated""" - (photocurrent, saturation_current, resistance_series, - resistance_shunt, nNsVth) = ( - self.system.calcparams_desoto(self.effective_irradiance, - self.cell_temperature)) - - self.desoto = (photocurrent, saturation_current, resistance_series, - resistance_shunt, nNsVth) - - self.dc = self.system.singlediode( - photocurrent, saturation_current, resistance_series, - resistance_shunt, nNsVth) - - self.dc = self.system.scale_voltage_current_power(self.dc).fillna(0) - - return self - def pvwatts_dc(self): self.dc = self.system.pvwatts_dc(self.effective_irradiance, self.cell_temperature) From aa731e2690aeb130221c59d4c4a0f241e4caaba2 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Tue, 10 Sep 2019 20:43:03 -0700 Subject: [PATCH 06/10] fix expected value --- pvlib/test/test_modelchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index b9ee053e26..4f187a2114 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -151,7 +151,7 @@ def test_run_model_with_irradiance(system, location): index=times) ac = mc.run_model(irradiance).ac - expected = pd.Series(np.array([1.90054749e+02, -2.00000000e-02]), + expected = pd.Series(np.array([187.80746494643176, -0.02]), index=times) assert_series_equal(ac, expected) From 8fbffad0d822c2a785288d108eafcf70b7b7b8fc Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Tue, 10 Sep 2019 20:43:14 -0700 Subject: [PATCH 07/10] update whatsnew --- docs/sphinx/source/whatsnew/v0.7.0.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 63a8db0fac..25537dac75 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -71,6 +71,11 @@ API Changes * Calling :py:func:`pvlib.pvsystem.retrieve_sam` with no parameters will raise an exception instead of displaying a dialog. +* The `times` keyword argument has been deprecated in the + :py:meth:`pvlib.modelchain.ModelChain.run_model`, + :py:meth:`pvlib.modelchain.ModelChain.prepare_inputs`, and + :py:meth:`pvlib.modelchain.ModelChain.complete_irradiance` methods. + Model times are now determined by the input weather DataFrame index. Enhancements ~~~~~~~~~~~~ From f418206f086a386a8709b7b5af5eabc866e63a8d Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Tue, 10 Sep 2019 20:49:21 -0700 Subject: [PATCH 08/10] uncovered line, whatsnew --- docs/sphinx/source/whatsnew/v0.7.0.rst | 2 ++ pvlib/test/test_modelchain.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 25537dac75..c5b60d6f3a 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -120,6 +120,8 @@ Removal of prior version deprecations * Removed `solarposition.get_sun_rise_set_transit`. * Removed `tmy` module. * Removed `ModelChain.singlediode` method. +* Removed `ModelChain.prepare_inputs` clearsky assumption when no irradiance + data was provided. Contributors ~~~~~~~~~~~~ diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 4f187a2114..4623d9fe45 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -174,6 +174,13 @@ def test_prepare_inputs_times(system, location): mc.prepare_inputs(irradiance, times=times) +def test_prepare_inputs_no_irradiance(system, location): + mc = ModelChain(system, location) + weather = pd.DataFrame() + with pytest.raises(ValueError): + mc.prepare_inputs(weather) + + @requires_tables def test_complete_irradiance_times(system, location): mc = ModelChain(system, location) From e6e79f9d7f413de9af9ea9b9026b70273a688d7f Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 13 Sep 2019 16:54:14 -0700 Subject: [PATCH 09/10] feedback --- docs/sphinx/source/whatsnew/v0.7.0.rst | 6 +++++- pvlib/modelchain.py | 14 ++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index c5b60d6f3a..b5295434d8 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -75,7 +75,11 @@ API Changes :py:meth:`pvlib.modelchain.ModelChain.run_model`, :py:meth:`pvlib.modelchain.ModelChain.prepare_inputs`, and :py:meth:`pvlib.modelchain.ModelChain.complete_irradiance` methods. - Model times are now determined by the input weather DataFrame index. + Model times are now determined by the input `weather`. `DataFrame`. + Therefore, the `weather` DataFrame must have a `DatetimeIndex`. + The `weather` argument of the above methods is now the first, required + positional argument and the `times` argument is kept as the second keyword + argument for capability during the deprecation period. Enhancements ~~~~~~~~~~~~ diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 09017e33ea..5df50bf6c6 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -767,8 +767,8 @@ def complete_irradiance(self, weather, times=None): if times is not None: warnings.warn('times keyword argument is deprecated and will be ' - 'removed in 0.8. Set the index of the weather ' - 'argument instead.', pvlibDeprecationWarning) + 'removed in 0.8. The index of the weather DataFrame ' + 'is used for times.', pvlibDeprecationWarning) self.solar_position = self.location.get_solarposition( self.weather.index, method=self.solar_position_method) @@ -835,8 +835,10 @@ def prepare_inputs(self, weather, times=None): if times is not None: warnings.warn('times keyword argument is deprecated and will be ' - 'removed in 0.8. Set the index of the weather ' - 'argument instead.', pvlibDeprecationWarning) + 'removed in 0.8. The index of the weather DataFrame ' + 'is used for times.', pvlibDeprecationWarning) + + self.times = self.weather.index self.solar_position = self.location.get_solarposition( self.weather.index, method=self.solar_position_method) @@ -911,8 +913,8 @@ def run_model(self, weather, times=None): """ if times is not None: warnings.warn('times keyword argument is deprecated and will be ' - 'removed in 0.8. Set the index of the weather ' - 'argument instead.', pvlibDeprecationWarning) + 'removed in 0.8. The index of the weather DataFrame ' + 'is used for times.', pvlibDeprecationWarning) self.prepare_inputs(weather) self.aoi_model() From e02312d7e6fc7dc9dccd152cc0bc3f06476b5a92 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Sun, 15 Sep 2019 08:36:51 -0700 Subject: [PATCH 10/10] more explicit docstrings --- pvlib/modelchain.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 5df50bf6c6..6aad00c1dd 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -738,7 +738,9 @@ def complete_irradiance(self, weather, times=None): are required. Air temperature of 20 C and wind speed of 0 m/s will be added to the DataFrame if not provided. times : None, deprecated - Deprecated argument included for backwards compatibility. + Deprecated argument included for API compatibility, but not + used internally. The index of the weather DataFrame is used + for times. Returns ------- @@ -813,7 +815,9 @@ def prepare_inputs(self, weather, times=None): are required. Air temperature of 20 C and wind speed of 0 m/s will be added to the DataFrame if not provided. times : None, deprecated - Deprecated argument included for backwards compatibility. + Deprecated argument included for API compatibility, but not + used internally. The index of the weather DataFrame is used + for times. Notes ----- @@ -901,7 +905,9 @@ def run_model(self, weather, times=None): are required. Air temperature of 20 C and wind speed of 0 m/s will be added to the DataFrame if not provided. times : None, deprecated - Deprecated argument included for backwards compatibility. + Deprecated argument included for API compatibility, but not + used internally. The index of the weather DataFrame is used + for times. Returns -------