diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index e99a8411b4..8b18b65817 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -22,8 +22,6 @@ corresponding procedural code. pvsystem.PVSystem tracking.SingleAxisTracker modelchain.ModelChain - pvsystem.LocalizedPVSystem - tracking.LocalizedSingleAxisTracker Solar Position @@ -209,7 +207,6 @@ wrap the functions listed below. See its documentation for details. :toctree: generated/ pvsystem.PVSystem - pvsystem.LocalizedPVSystem Incident angle modifiers ------------------------ @@ -421,8 +418,6 @@ The :py:class:`~tracking.SingleAxisTracker` inherits from tracking.SingleAxisTracker tracking.SingleAxisTracker.singleaxis tracking.SingleAxisTracker.get_irradiance - tracking.SingleAxisTracker.localize - tracking.LocalizedSingleAxisTracker Functions --------- diff --git a/docs/sphinx/source/comparison_pvlib_matlab.rst b/docs/sphinx/source/comparison_pvlib_matlab.rst index f66ae68b09..5822e261c2 100644 --- a/docs/sphinx/source/comparison_pvlib_matlab.rst +++ b/docs/sphinx/source/comparison_pvlib_matlab.rst @@ -54,7 +54,6 @@ Major differences * pvlib-python implements a handful of class designed to simplify the PV modeling process. These include :py:class:`~pvlib.location.Location`, :py:class:`~pvlib.pvsystem.PVSystem`, - :py:class:`~pvlib.pvsystem.LocalizedPVSystem`, :py:class:`~pvlib.tracking.SingleAxisTracker`, and :py:class:`~pvlib.modelchain.ModelChain`. diff --git a/docs/sphinx/source/introtutorial.rst b/docs/sphinx/source/introtutorial.rst index 31b6cd71a9..d134d1fe07 100644 --- a/docs/sphinx/source/introtutorial.rst +++ b/docs/sphinx/source/introtutorial.rst @@ -178,81 +178,3 @@ by examining the parameters defined for the module. plt.ylabel('Yearly energy yield (W hr)') @suppress plt.close(); - - -Object oriented (LocalizedPVSystem) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The second object oriented paradigm uses a model where a -:py:class:`~pvlib.pvsystem.LocalizedPVSystem` represents a PV system at -a particular place on the planet. This can be a useful paradigm if -you're thinking about a power plant that already exists. - -The :py:class:`~pvlib.pvsystem.LocalizedPVSystem` inherits from both -:py:class:`~pvlib.pvsystem.PVSystem` and -:py:class:`~pvlib.location.Location`, while the -:py:class:`~pvlib.tracking.LocalizedSingleAxisTracker` inherits from -:py:class:`~pvlib.tracking.SingleAxisTracker` (itself a subclass of -:py:class:`~pvlib.pvsystem.PVSystem`) and -:py:class:`~pvlib.location.Location`. The -:py:class:`~pvlib.pvsystem.LocalizedPVSystem` and -:py:class:`~pvlib.tracking.LocalizedSingleAxisTracker` classes may -contain bugs due to the relative difficulty of implementing multiple -inheritance. The :py:class:`~pvlib.pvsystem.LocalizedPVSystem` and -:py:class:`~pvlib.tracking.LocalizedSingleAxisTracker` may be deprecated -in a future release. We recommend that most modeling workflows implement -:py:class:`~pvlib.location.Location`, -:py:class:`~pvlib.pvsystem.PVSystem`, and -:py:class:`~pvlib.modelchain.ModelChain`. - -The following code demonstrates how to use a -:py:class:`~pvlib.pvsystem.LocalizedPVSystem` object to accomplish our -modeling goal: - -.. ipython:: python - - from pvlib.pvsystem import LocalizedPVSystem - - energies = {} - for latitude, longitude, name, altitude, timezone in coordinates: - localized_system = LocalizedPVSystem(module_parameters=module, - inverter_parameters=inverter, - temperature_model_parameters=temperature_model_parameters, - surface_tilt=latitude, - surface_azimuth=180, - latitude=latitude, - longitude=longitude, - name=name, - altitude=altitude, - tz=timezone) - times = naive_times.tz_localize(timezone) - clearsky = localized_system.get_clearsky(times) - solar_position = localized_system.get_solarposition(times) - total_irrad = localized_system.get_irradiance(solar_position['apparent_zenith'], - solar_position['azimuth'], - clearsky['dni'], - clearsky['ghi'], - clearsky['dhi']) - tcell = localized_system.sapm_celltemp(total_irrad['poa_global'], - temp_air, wind_speed) - aoi = localized_system.get_aoi(solar_position['apparent_zenith'], - solar_position['azimuth']) - airmass = localized_system.get_airmass(solar_position=solar_position) - effective_irradiance = localized_system.sapm_effective_irradiance( - total_irrad['poa_direct'], total_irrad['poa_diffuse'], - airmass['airmass_absolute'], aoi) - dc = localized_system.sapm(effective_irradiance, tcell) - ac = localized_system.snlinverter(dc['v_mp'], dc['p_mp']) - annual_energy = ac.sum() - energies[name] = annual_energy - - energies = pd.Series(energies) - - # based on the parameters specified above, these are in W*hrs - print(energies.round(0)) - - energies.plot(kind='bar', rot=0) - @savefig localized-pvsystem-energies.png width=6in - plt.ylabel('Yearly energy yield (W hr)') - @suppress - plt.close(); diff --git a/docs/sphinx/source/whatsnew/v0.8.0.rst b/docs/sphinx/source/whatsnew/v0.8.0.rst index 193bb8b1d5..3df309ea8a 100644 --- a/docs/sphinx/source/whatsnew/v0.8.0.rst +++ b/docs/sphinx/source/whatsnew/v0.8.0.rst @@ -32,6 +32,17 @@ API Changes with Deprecations glass/glass module in open racking and emit a warning. In v0.9, users must provide ``temperature_model_parameters`` or a valid combination of ``module_type`` and ``racking_model``. (:issue:`1030`, :pull:`1033`) +* Deprecated arbitrary keyword arguments for + :py:class:`pvlib.location.Location`, :py:class:`pvlib.pvsystem.PVSystem`, + :py:class:`pvlib.tracking.SingleAxisTracker`, and + :py:class:`pvlib.modelchain.ModelChain`. Supplying arbitrary keyword + to these objects will result in TypeErrors in v0.9. (:issue:`1029`, :pull:`1053`) +* ``pvlib.pvsystem.LocalizedPVSystem`` and ``pvlib.pvsystem.LocalizedSingleAxisTracker`` + are deprecated and will be removed in 0.9. Use + :py:class:`pvlib.location.Location`, :py:class:`pvlib.pvsystem.PVSystem`, + :py:class:`pvlib.tracking.SingleAxisTracker`, and + :py:class:`pvlib.modelchain.ModelChain` instead. + (:issue:`1029`, :pull:`1034`, :pull:`1053`) API Changes ~~~~~~~~~~~ diff --git a/pvlib/location.py b/pvlib/location.py index c3e751b046..d28c46f856 100644 --- a/pvlib/location.py +++ b/pvlib/location.py @@ -5,11 +5,13 @@ # Will Holmgren, University of Arizona, 2014-2016. import datetime +import warnings import pandas as pd import pytz from pvlib import solarposition, clearsky, atmosphere, irradiance +from pvlib._deprecation import pvlibDeprecationWarning class Location: @@ -48,10 +50,6 @@ class Location: name : None or string, default None. Sets the name attribute of the Location object. - **kwargs - Arbitrary keyword arguments. - Included for compatibility, but not used. - See also -------- pvlib.pvsystem.PVSystem @@ -82,6 +80,12 @@ def __init__(self, latitude, longitude, tz='UTC', altitude=0, self.name = name + if kwargs: + warnings.warn( + 'Arbitrary Location kwargs are deprecated and will be ' + 'removed in v0.9', pvlibDeprecationWarning + ) + def __repr__(self): attrs = ['name', 'latitude', 'longitude', 'altitude', 'tz'] return ('Location: \n ' + '\n '.join( diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index e0956804d1..3cef177de4 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -333,10 +333,6 @@ class ModelChain: name: None or str, default None Name of ModelChain instance. - - **kwargs - Arbitrary keyword arguments. Included for compatibility, but not - used. """ def __init__(self, system, location, @@ -372,6 +368,12 @@ def __init__(self, system, location, self.times = None self.solar_position = None + if kwargs: + warnings.warn( + 'Arbitrary ModelChain kwargs are deprecated and will be ' + 'removed in v0.9', pvlibDeprecationWarning + ) + @classmethod def with_pvwatts(cls, system, location, orientation_strategy=None, diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 95bb6fe379..fecc93075e 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -79,9 +79,6 @@ class PVSystem: :py:class:`~pvlib.modelchain.ModelChain` objects. - See the :py:class:`LocalizedPVSystem` class for an object model that - describes an installed PV system. - The class supports basic system topologies consisting of: * `N` total modules arranged in series @@ -164,7 +161,6 @@ class PVSystem: -------- pvlib.location.Location pvlib.tracking.SingleAxisTracker - pvlib.pvsystem.LocalizedPVSystem """ def __init__(self, @@ -220,6 +216,12 @@ def __init__(self, self.name = name + if kwargs: + warnings.warn( + 'Arbitrary PVSystem kwargs are deprecated and will be ' + 'removed in v0.9', pvlibDeprecationWarning + ) + def __repr__(self): attrs = ['name', 'surface_tilt', 'surface_azimuth', 'module', 'inverter', 'albedo', 'racking_model', 'module_type', @@ -819,9 +821,12 @@ def pvwatts_ac(self, pdc): return inverter.pvwatts(pdc, self.inverter_parameters['pdc0'], **kwargs) + @deprecated('0.8', alternative='PVSystem, Location, and ModelChain', + name='PVSystem.localize', removal='0.9') def localize(self, location=None, latitude=None, longitude=None, **kwargs): - """Creates a LocalizedPVSystem object using this object + """ + Creates a LocalizedPVSystem object using this object and location data. Must supply either location object or latitude, longitude, and any location kwargs @@ -843,6 +848,8 @@ def localize(self, location=None, latitude=None, longitude=None, return LocalizedPVSystem(pvsystem=self, location=location) +@deprecated('0.8', alternative='PVSystem, Location, and ModelChain', + name='LocalizedPVSystem', removal='0.9') class LocalizedPVSystem(PVSystem, Location): """ The LocalizedPVSystem class defines a standard set of installed PV diff --git a/pvlib/tests/test_location.py b/pvlib/tests/test_location.py index 82ab4f8a6d..5c517adecf 100644 --- a/pvlib/tests/test_location.py +++ b/pvlib/tests/test_location.py @@ -16,7 +16,8 @@ from pvlib.solarposition import declination_spencer71 from pvlib.solarposition import equation_of_time_spencer71 from test_solarposition import expected_solpos, golden, golden_mst -from conftest import requires_ephem, requires_tables +from pvlib._deprecation import pvlibDeprecationWarning +from conftest import requires_ephem, requires_tables, fail_on_pvlib_version def test_location_required(): @@ -323,3 +324,10 @@ def test_get_sun_rise_set_transit_valueerror(golden): tz='MST') with pytest.raises(ValueError): golden.get_sun_rise_set_transit(times, method='eyeball') + + +@fail_on_pvlib_version('0.9') +def test_deprecated_09(): + match = "Arbitrary Location kwargs" + with pytest.warns(pvlibDeprecationWarning, match=match): + Location(32.2, -111, arbitrary_kwarg='value') diff --git a/pvlib/tests/test_modelchain.py b/pvlib/tests/test_modelchain.py index 1758b0cfe9..4393c5091d 100644 --- a/pvlib/tests/test_modelchain.py +++ b/pvlib/tests/test_modelchain.py @@ -776,6 +776,13 @@ def test_deprecated_09(sapm_dc_snl_ac_system, cec_dc_adr_ac_system, aoi_model='no_loss', spectral_model='no_loss') +@fail_on_pvlib_version('0.9') +def test_ModelChain_kwargs_deprecated_09(sapm_dc_snl_ac_system, location): + match = "Arbitrary ModelChain kwargs" + with pytest.warns(pvlibDeprecationWarning, match=match): + ModelChain(sapm_dc_snl_ac_system, location, arbitrary_kwarg='value') + + def test_basic_chain_required(sam_data, cec_inverter_parameters, sapm_temperature_cs5p_220m): times = pd.date_range(start='20160101 1200-0700', diff --git a/pvlib/tests/test_pvsystem.py b/pvlib/tests/test_pvsystem.py index e7a0aac882..b9edf54fff 100644 --- a/pvlib/tests/test_pvsystem.py +++ b/pvlib/tests/test_pvsystem.py @@ -1064,10 +1064,12 @@ def test_PVSystem_get_irradiance(): assert_frame_equal(irradiance, expected, check_less_precise=2) +@fail_on_pvlib_version('0.9') def test_PVSystem_localize_with_location(): system = pvsystem.PVSystem(module='blah', inverter='blarg') location = Location(latitude=32, longitude=-111) - localized_system = system.localize(location=location) + with pytest.warns(pvlibDeprecationWarning): + localized_system = system.localize(location=location) assert localized_system.module == 'blah' assert localized_system.inverter == 'blarg' @@ -1075,9 +1077,11 @@ def test_PVSystem_localize_with_location(): assert localized_system.longitude == -111 +@fail_on_pvlib_version('0.9') def test_PVSystem_localize_with_latlon(): system = pvsystem.PVSystem(module='blah', inverter='blarg') - localized_system = system.localize(latitude=32, longitude=-111) + with pytest.warns(pvlibDeprecationWarning): + localized_system = system.localize(latitude=32, longitude=-111) assert localized_system.module == 'blah' assert localized_system.inverter == 'blarg' @@ -1103,11 +1107,13 @@ def test_PVSystem___repr__(): assert system.__repr__() == expected +@fail_on_pvlib_version('0.9') def test_PVSystem_localize___repr__(): system = pvsystem.PVSystem( module='blah', inverter='blarg', name='pv ftw', temperature_model_parameters={'a': -3.56}) - localized_system = system.localize(latitude=32, longitude=-111) + with pytest.warns(pvlibDeprecationWarning): + localized_system = system.localize(latitude=32, longitude=-111) # apparently name is not preserved when creating a system using localize expected = """LocalizedPVSystem: name: None @@ -1131,12 +1137,13 @@ def test_PVSystem_localize___repr__(): # when they are attached to LocalizedPVSystem, but # that's probably not necessary at this point. - +@fail_on_pvlib_version('0.9') def test_LocalizedPVSystem_creation(): - localized_system = pvsystem.LocalizedPVSystem(latitude=32, - longitude=-111, - module='blah', - inverter='blarg') + with pytest.warns(pvlibDeprecationWarning): + localized_system = pvsystem.LocalizedPVSystem(latitude=32, + longitude=-111, + module='blah', + inverter='blarg') assert localized_system.module == 'blah' assert localized_system.inverter == 'blarg' @@ -1144,10 +1151,12 @@ def test_LocalizedPVSystem_creation(): assert localized_system.longitude == -111 +@fail_on_pvlib_version('0.9') def test_LocalizedPVSystem___repr__(): - localized_system = pvsystem.LocalizedPVSystem( - latitude=32, longitude=-111, module='blah', inverter='blarg', - name='my name', temperature_model_parameters={'a': -3.56}) + with pytest.warns(pvlibDeprecationWarning): + localized_system = pvsystem.LocalizedPVSystem( + latitude=32, longitude=-111, module='blah', inverter='blarg', + name='my name', temperature_model_parameters={'a': -3.56}) expected = """LocalizedPVSystem: name: my name @@ -1311,3 +1320,6 @@ def test_deprecated_09(cec_inverter_parameters, adr_inverter_parameters): system = pvsystem.PVSystem() with pytest.warns(pvlibDeprecationWarning, match=match): system.sapm_celltemp(1, 2, 3) + match = "Arbitrary PVSystem kwargs" + with pytest.warns(pvlibDeprecationWarning, match=match): + system = pvsystem.PVSystem(arbitrary_kwarg='value') diff --git a/pvlib/tracking.py b/pvlib/tracking.py index 14e2f73c3b..5397055935 100644 --- a/pvlib/tracking.py +++ b/pvlib/tracking.py @@ -6,6 +6,7 @@ from pvlib.pvsystem import PVSystem from pvlib.location import Location from pvlib import irradiance, atmosphere +from pvlib._deprecation import deprecated class SingleAxisTracker(PVSystem): @@ -55,6 +56,9 @@ class SingleAxisTracker(PVSystem): :func:`~pvlib.tracking.calc_cross_axis_tilt` to calculate `cross_axis_tilt`. [degrees] + **kwargs + Passed to :py:class:`~pvlib.pvsystem.PVSystem`. + See also -------- pvlib.tracking.singleaxis @@ -112,6 +116,9 @@ def singleaxis(self, apparent_zenith, apparent_azimuth): return tracking_data + @deprecated('0.8', + alternative='SingleAxisTracker, Location, and ModelChain', + name='SingleAxisTracker.localize', removal='0.9') def localize(self, location=None, latitude=None, longitude=None, **kwargs): """ @@ -232,6 +239,8 @@ def get_irradiance(self, surface_tilt, surface_azimuth, **kwargs) +@deprecated('0.8', alternative='SingleAxisTracker, Location, and ModelChain', + name='LocalizedSingleAxisTracker', removal='0.9') class LocalizedSingleAxisTracker(SingleAxisTracker, Location): """ The :py:class:`~pvlib.tracking.LocalizedSingleAxisTracker` class defines a