Repository: pvlib/pvlib-python
Pull request #1136 (2021-01-19 21:58:24)

remove deprecated items for v0.9
<!-- Thank you for your contribution! The following items must be addressed before the code can be merged. Please don't hesitate to ask for help if you're unsure of how to accomplish any of the items. Feel free to remove checklist items that are not relevant to your change. -->

 -  ~Closes #xxxx~
 - [x] I am familiar with the [contributing guidelines](https://pvlib-python.readthedocs.io/en/latest/contributing.html)
 - [x] Tests added
 - [x] Updates entries to [`docs/sphinx/source/api.rst`](https://github.com/pvlib/pvlib-python/blob/master/docs/sphinx/source/api.rst) for API changes.
 - [x] Adds description and name entries in the appropriate "what's new" file in [`docs/sphinx/source/whatsnew`](https://github.com/pvlib/pvlib-python/tree/master/docs/sphinx/source/whatsnew) for all changes. Includes link to the GitHub Issue with `` :issue:`num` `` or this Pull Request with `` :pull:`num` ``. Includes contributor name and/or GitHub username (link with `` :ghuser:`user` ``).
 -  ~New code is fully documented. Includes [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html) compliant docstrings, examples, and comments where necessary.~
 - [x] Pull request is nearly complete and ready for detailed review.
 - [x] Maintainer: Appropriate GitHub Labels and Milestone are assigned to the Pull Request and linked Issue.

<!-- Brief description of the problem and proposed solution (if not already fully described in the issue linked to above): -->



Patch:
diff --git a/docs/sphinx/source/whatsnew.rst b/docs/sphinx/source/whatsnew.rst
index 271c898..8889cce 100644
--- a/docs/sphinx/source/whatsnew.rst
+++ b/docs/sphinx/source/whatsnew.rst
@@ -6,6 +6,7 @@ What's New
 
 These are new features and improvements of note in each release.
 
+.. include:: whatsnew/v0.9.0.rst
 .. include:: whatsnew/v0.8.1.rst
 .. include:: whatsnew/v0.8.0.rst
 .. include:: whatsnew/v0.7.2.rst
diff --git a/docs/sphinx/source/whatsnew/v0.9.0.rst b/docs/sphinx/source/whatsnew/v0.9.0.rst
index 8b323fd..8c65f43 100644
--- a/docs/sphinx/source/whatsnew/v0.9.0.rst
+++ b/docs/sphinx/source/whatsnew/v0.9.0.rst
@@ -5,13 +5,44 @@ v0.9.0 (MONTH DAY YEAR)
 
 Breaking changes
 ~~~~~~~~~~~~~~~~
+* Moved functions related to inverters from ``pvsystem.py`` to ``inverter.py``.
+  Functions are renamed to follow a more consistent pattern, as follows (:pull:`886`, :pull:`1136`):
+
+    - ``pvlib.pvsystem.snlinverter`` is now :py:func:`pvlib.inverter.sandia`
+    - ``pvlib.pvsystem.pvwatts_ac`` is now :py:func:`pvlib.inverter.pvwatts`
+    - ``pvlib.pvsystem.adrinverter`` is now :py:func:`pvlib.inverter.adr`
+
+* Argument ``ac_model`` for :py:class:`pvlib.modelchain.ModelChain` now accepts
+  ``'sandia'``, ``'pvwatts'`` and ``'adr'`` for the inverter models. (:pull:`886`, :pull:`1136`)
+
+* Calling :py:meth:`pvlib.pvsystem.PVSystem.sapm_celltemp` without setting
+  ``PVSystem.temperature_model_parameters``,
+  or a valid combination of ``PVsystem.module_type`` and ``PVsystem.racking_model``, will
+  now raise an exception. (:issue:`1030`, :pull:`1033`, :pull:`1136`)
+
+* 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 result in TypeErrors in v0.9. (:issue:`1029`, :pull:`1053`, :pull:`1136`)
+
+* ``pvlib.pvsystem.LocalizedPVSystem`` and ``pvlib.pvsystem.LocalizedSingleAxisTracker``
+  have been removed. 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`, :pull:`1136`)
+
+* ``irradiance.liujordan`` and ``ForecastModel.cloud_cover_to_irradiance_liujordan``
+  have been removed. (:pull:`1136`)
 
 
 Deprecations
 ~~~~~~~~~~~~
 * The following ``ModelChain`` attributes are deprecated. They have been moved
   to the :py:class:`~pvlib.modelchain.ModelChainResult` class that is
-  accessible via ``ModelChain.results``
+  accessible via ``ModelChain.results``:
+
   * ``ModelChain.ac``
   * ``ModelChain.airmass``
   * ``ModelChain.aoi``
diff --git a/pvlib/forecast.py b/pvlib/forecast.py
index 4d4500c..01956a5 100644
--- a/pvlib/forecast.py
+++ b/pvlib/forecast.py
@@ -15,7 +15,6 @@ from siphon.catalog import TDSCatalog
 from siphon.ncss import NCSS
 
 import warnings
-from pvlib._deprecation import deprecated
 
 
 warnings.warn(
@@ -562,48 +561,6 @@ class ForecastModel:
 
         return irrads
 
-    @deprecated(
-        '0.8',
-        alternative='Forecast.cloud_cover_to_irradiance_campbell_norman',
-        name='Forecast.cloud_cover_to_irradiance_liujordan',
-        removal='0.9')
-    def cloud_cover_to_irradiance_liujordan(self, cloud_cover, **kwargs):
-        """
-        Deprecated. Use cloud_cover_to_irradiance_campbell_norman instead.
-
-        Estimates irradiance from cloud cover in the following steps:
-
-        1. Determine transmittance using a function of cloud cover e.g.
-           :py:meth:`~ForecastModel.cloud_cover_to_transmittance_linear`
-        2. Calculate GHI, DNI, DHI using the
-           :py:func:`pvlib.irradiance.liujordan` model
-
-        Parameters
-        ----------
-        cloud_cover : Series
-
-        Returns
-        -------
-        irradiance : DataFrame
-            Columns include ghi, dni, dhi
-        """
-        # in principle, get_solarposition could use the forecast
-        # pressure, temp, etc., but the cloud cover forecast is not
-        # accurate enough to justify using these minor corrections
-        solar_position = self.location.get_solarposition(cloud_cover.index)
-        dni_extra = get_extra_radiation(cloud_cover.index)
-        airmass = self.location.get_airmass(cloud_cover.index)
-
-        transmittance = self.cloud_cover_to_transmittance_linear(cloud_cover,
-                                                                 **kwargs)
-
-        irrads = _liujordan(solar_position['apparent_zenith'],
-                            transmittance, airmass['airmass_absolute'],
-                            dni_extra=dni_extra)
-        irrads = irrads.fillna(0)
-
-        return irrads
-
     def cloud_cover_to_irradiance(self, cloud_cover, how='clearsky_scaling',
                                   **kwargs):
         """
@@ -632,9 +589,6 @@ class ForecastModel:
         elif how == 'campbell_norman':
             irrads = self.cloud_cover_to_irradiance_campbell_norman(
                 cloud_cover, **kwargs)
-        elif how == 'liujordan':
-            irrads = self.cloud_cover_to_irradiance_liujordan(
-                cloud_cover, **kwargs)
         else:
             raise ValueError('invalid how argument')
 
diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py
index 67a327a..6580030 100644
--- a/pvlib/irradiance.py
+++ b/pvlib/irradiance.py
@@ -13,8 +13,6 @@ import pandas as pd
 
 from pvlib import atmosphere, solarposition, tools
 
-from pvlib._deprecation import deprecated
-
 
 # see References section of grounddiffuse function
 SURFACE_ALBEDOS = {'urban': 0.18,
@@ -2299,10 +2297,6 @@ def _liujordan(zenith, transmittance, airmass, dni_extra=1367.0):
     return irrads
 
 
-liujordan = deprecated('0.8', alternative='campbellnormam',
-                       name='liujordan', removal='0.9')(_liujordan)
-
-
 def _get_perez_coefficients(perezmodel):
     '''
     Find coefficients for the Perez model
diff --git a/pvlib/location.py b/pvlib/location.py
index d28c46f..93d54fc 100644
--- a/pvlib/location.py
+++ b/pvlib/location.py
@@ -11,8 +11,6 @@ import pandas as pd
 import pytz
 
 from pvlib import solarposition, clearsky, atmosphere, irradiance
-from pvlib._deprecation import pvlibDeprecationWarning
-
 
 class Location:
     """
@@ -55,8 +53,7 @@ class Location:
     pvlib.pvsystem.PVSystem
     """
 
-    def __init__(self, latitude, longitude, tz='UTC', altitude=0,
-                 name=None, **kwargs):
+    def __init__(self, latitude, longitude, tz='UTC', altitude=0, name=None):
 
         self.latitude = latitude
         self.longitude = longitude
@@ -80,12 +77,6 @@ class Location:
 
         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 2e010fa..e4facd9 100644
--- a/pvlib/modelchain.py
+++ b/pvlib/modelchain.py
@@ -377,7 +377,7 @@ class ModelChain:
                  airmass_model='kastenyoung1989',
                  dc_model=None, ac_model=None, aoi_model=None,
                  spectral_model=None, temperature_model=None,
-                 losses_model='no_loss', name=None, **kwargs):
+                 losses_model='no_loss', name=None):
 
         self.name = name
         self.system = system
@@ -403,12 +403,6 @@ class ModelChain:
 
         self.results = ModelChainResult()
 
-        if kwargs:
-            warnings.warn(
-                'Arbitrary ModelChain kwargs are deprecated and will be '
-                'removed in v0.9', pvlibDeprecationWarning
-            )
-
     def __getattr__(self, key):
         if key in ModelChain._deprecated_attrs:
             msg = f'ModelChain.{key} is deprecated and will' \
@@ -750,22 +744,11 @@ class ModelChain:
             self._ac_model = self.infer_ac_model()
         elif isinstance(model, str):
             model = model.lower()
-            # TODO in v0.9: remove 'snlinverter', 'adrinverter'
-            if model in ['sandia', 'snlinverter']:
-                if model == 'snlinverter':
-                    warnings.warn("ac_model = 'snlinverter' is deprecated and"
-                                  " will be removed in v0.9; use"
-                                  " ac_model = 'sandia' instead.",
-                                  pvlibDeprecationWarning)
+            if model == 'sandia':
                 self._ac_model = self.snlinverter
             elif model == 'sandia_multi':
                 self._ac_model = self.sandia_multi_inverter
-            elif model in ['adr', 'adrinverter']:
-                if model == 'adrinverter':
-                    warnings.warn("ac_model = 'adrinverter' is deprecated and"
-                                  " will be removed in v0.9; use"
-                                  " ac_model = 'adr' instead.",
-                                  pvlibDeprecationWarning)
+            elif model in 'adr':
                 self._ac_model = self.adrinverter
             elif model == 'pvwatts':
                 self._ac_model = self.pvwatts_inverter
diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py
index 651c9eb..b2370d4 100644
--- a/pvlib/pvsystem.py
+++ b/pvlib/pvsystem.py
@@ -17,7 +17,6 @@ from pvlib._deprecation import deprecated
 from pvlib import (atmosphere, iam, inverter, irradiance,
                    singlediode as _singlediode, temperature)
 from pvlib.tools import _build_kwargs
-from pvlib.location import Location
 from pvlib._deprecation import pvlibDeprecationWarning
 
 
@@ -46,28 +45,6 @@ _DC_MODEL_PARAMS = {
 }
 
 
-def _combine_localized_attributes(pvsystem=None, location=None, **kwargs):
-    """
-    Get and combine attributes from the pvsystem and/or location
-    with the rest of the kwargs.
-    """
-    if pvsystem is not None:
-        pv_dict = pvsystem.__dict__
-        pv_dict = {**pv_dict, **pv_dict['arrays'][0].__dict__}
-    else:
-        pv_dict = {}
-
-    if location is not None:
-        loc_dict = location.__dict__
-    else:
-        loc_dict = {}
-
-    new_kwargs = dict(
-        list(pv_dict.items()) + list(loc_dict.items()) + list(kwargs.items())
-    )
-    return new_kwargs
-
-
 def _unwrap_single_value(func):
     """Decorator for functions that return iterables.
 
@@ -211,8 +188,7 @@ class PVSystem:
                  temperature_model_parameters=None,
                  modules_per_string=1, strings_per_inverter=1,
                  inverter=None, inverter_parameters=None,
-                 racking_model=None, losses_parameters=None, name=None,
-                 **kwargs):
+                 racking_model=None, losses_parameters=None, name=None):
 
         if arrays is None:
             self.arrays = (Array(
@@ -244,12 +220,6 @@ class PVSystem:
 
         self.name = name
 
-        if kwargs:
-            warnings.warn(
-                'Arbitrary PVSystem kwargs are deprecated and will be '
-                'removed in v0.9', pvlibDeprecationWarning
-            )
-
     def __repr__(self):
         repr = f'PVSystem:\n  name: {self.name}\n  '
         for array in self.arrays:
@@ -587,21 +557,6 @@ class PVSystem:
         poa_global = self._validate_per_array(poa_global)
         temp_air = self._validate_per_array(temp_air, system_wide=True)
         wind_speed = self._validate_per_array(wind_speed, system_wide=True)
-        for array in self.arrays:
-            # warn user about change in default behavior in 0.9.
-            if (array.temperature_model_parameters == {} and array.module_type
-                    is None and array.racking_model is None):
-                warnings.warn(
-                    'temperature_model_parameters, racking_model, and '
-                    'module_type are not specified. Reverting to deprecated '
-                    'default: SAPM cell temperature model parameters for a '
-                    'glass/glass module in open racking. In v0.9, '
-                    'temperature_model_parameters or a valid combination of '
-                    'racking_model and module_type will be required.',
-                    pvlibDeprecationWarning)
-                params = temperature._temperature_model_params(
-                    'sapm', 'open_rack_glass_glass')
-                array.temperature_model_parameters = params
 
         build_kwargs = functools.partial(_build_kwargs, ['a', 'b', 'deltaT'])
         return tuple(
@@ -1015,32 +970,6 @@ class PVSystem:
         return inverter.pvwatts_multi(p_dc, 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
-        and location data. Must supply either location object or
-        latitude, longitude, and any location kwargs
-
-        Parameters
-        ----------
-        location : None or Location, default None
-        latitude : None or float, default None
-        longitude : None or float, default None
-        **kwargs : see Location
-
-        Returns
-        -------
-        localized_system : LocalizedPVSystem
-        """
-
-        if location is None:
-            location = Location(latitude, longitude, **kwargs)
-
-        return LocalizedPVSystem(pvsystem=self, location=location)
-
     @property
     @_unwrap_single_value
     def module_parameters(self):
@@ -1118,39 +1047,6 @@ class PVSystem:
         return len(self.arrays)
 
 
-@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
-    system attributes and modeling functions. This class combines the
-    attributes and methods of the PVSystem and Location classes.
-
-    The LocalizedPVSystem may have bugs due to the difficulty of
-    robustly implementing multiple inheritance. See
-    :py:class:`~pvlib.modelchain.ModelChain` for an alternative paradigm
-    for modeling PV systems at specific locations.
-    """
-    def __init__(self, pvsystem=None, location=None, **kwargs):
-
-        new_kwargs = _combine_localized_attributes(
-            pvsystem=pvsystem,
-            location=location,
-            **kwargs,
-        )
-
-        PVSystem.__init__(self, **new_kwargs)
-        Location.__init__(self, **new_kwargs)
-
-    def __repr__(self):
-        attrs = ['name', 'latitude', 'longitude', 'altitude', 'tz',
-                 'surface_tilt', 'surface_azimuth', 'module', 'inverter',
-                 'albedo', 'racking_model', 'module_type',
-                 'temperature_model_parameters']
-        return ('LocalizedPVSystem:\n  ' + '\n  '.join(
-            f'{attr}: {getattr(self, attr)}' for attr in attrs))
-
-
 class Array:
     """
     An Array is a set of of modules at the same orientation.
@@ -2906,15 +2802,3 @@ def combine_loss_factors(index, *losses, fill_method='ffill'):
         combined_factor *= (1 - loss)
 
     return 1 - combined_factor
-
-
-snlinverter = deprecated('0.8', alternative='inverter.sandia',
-                         name='snlinverter', removal='0.9')(inverter.sandia)
-
-
-adrinverter = deprecated('0.8', alternative='inverter.adr', name='adrinverter',
-                         removal='0.9')(inverter.adr)
-
-
-pvwatts_ac = deprecated('0.8', alternative='inverter.pvwatts',
-                        name='pvwatts_ac', removal='0.9')(inverter.pvwatts)
diff --git a/pvlib/tracking.py b/pvlib/tracking.py
index fa435cc..5c3a160 100644
--- a/pvlib/tracking.py
+++ b/pvlib/tracking.py
@@ -2,11 +2,8 @@ import numpy as np
 import pandas as pd
 
 from pvlib.tools import cosd, sind, tand
-from pvlib.pvsystem import _combine_localized_attributes
 from pvlib.pvsystem import PVSystem
-from pvlib.location import Location
 from pvlib import irradiance, atmosphere
-from pvlib._deprecation import deprecated
 
 
 class SingleAxisTracker(PVSystem):
@@ -140,33 +137,6 @@ class SingleAxisTracker(PVSystem):
 
         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):
-        """
-        Creates a :py:class:`LocalizedSingleAxisTracker` object using
-        this object and location data. Must supply either location
-        object or latitude, longitude, and any location kwargs
-
-        Parameters
-        ----------
-        location : None or Location, default None
-        latitude : None or float, default None
-        longitude : None or float, default None
-        **kwargs : see Location
-
-        Returns
-        -------
-        localized_system : LocalizedSingleAxisTracker
-        """
-
-        if location is None:
-            location = Location(latitude, longitude, **kwargs)
-
-        return LocalizedSingleAxisTracker(pvsystem=self, location=location)
-
     def get_aoi(self, surface_tilt, surface_azimuth, solar_zenith,
                 solar_azimuth):
         """Get the angle of incidence on the system.
@@ -263,42 +233,6 @@ class SingleAxisTracker(PVSystem):
                                                **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
-    standard set of installed PV system attributes and modeling functions. This
-    class combines the attributes and methods of the
-    :py:class:`~pvlib.tracking.SingleAxisTracker` (a subclass of
-    :py:class:`~pvlib.pvsystem.PVSystem`) and
-    :py:class:`~pvlib.location.Location` classes.
-
-    The :py:class:`~pvlib.tracking.LocalizedSingleAxisTracker` may have bugs
-    due to the difficulty of robustly implementing multiple inheritance. See
-    :py:class:`~pvlib.modelchain.ModelChain` for an alternative paradigm
-    for modeling PV systems at specific locations.
-    """
-
-    def __init__(self, pvsystem=None, location=None, **kwargs):
-
-        new_kwargs = _combine_localized_attributes(
-            pvsystem=pvsystem,
-            location=location,
-            **kwargs,
-        )
-
-        SingleAxisTracker.__init__(self, **new_kwargs)
-        Location.__init__(self, **new_kwargs)
-
-    def __repr__(self):
-        attrs = ['latitude', 'longitude', 'altitude', 'tz']
-        return ('Localized' +
-                super().__repr__() + '\n  ' +
-                '\n  '.join(
-                    f'{attr}: {getattr(self, attr)}' for attr in attrs))
-
-
 def singleaxis(apparent_zenith, apparent_azimuth,
                axis_tilt=0, axis_azimuth=0, max_angle=90,
                backtrack=True, gcr=2.0/7.0, cross_axis_tilt=0):


Test patch:
diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py
index 9959526..dd914eb 100644
--- a/pvlib/tests/test_irradiance.py
+++ b/pvlib/tests/test_irradiance.py
@@ -9,12 +9,14 @@ import pandas as pd
 import pytest
 from numpy.testing import assert_almost_equal, assert_allclose
 
-from conftest import assert_frame_equal, assert_series_equal
-
 from pvlib import irradiance
 
-from conftest import requires_ephem, requires_numba, fail_on_pvlib_version
-from pvlib._deprecation import pvlibDeprecationWarning
+from conftest import (
+    assert_frame_equal,
+    assert_series_equal,
+    requires_ephem,
+    requires_numba
+)
 
 
 # fixtures create realistic test input data
@@ -286,19 +288,6 @@ def test_get_sky_diffuse_invalid():
             model='invalid')
 
 
-@fail_on_pvlib_version('0.9')
-def test_liujordan():
-    expected = pd.DataFrame(np.array(
-        [[863.859736967, 653.123094076, 220.65905025]]),
-        columns=['ghi', 'dni', 'dhi'],
-        index=[0])
-    with pytest.warns(pvlibDeprecationWarning):
-        out = irradiance.liujordan(
-            pd.Series([10]), pd.Series([0.5]), pd.Series([1.1]),
-            dni_extra=1400)
-    assert_frame_equal(out, expected)
-
-
 def test_campbell_norman():
     expected = pd.DataFrame(np.array(
         [[863.859736967, 653.123094076, 220.65905025]]),
diff --git a/pvlib/tests/test_location.py b/pvlib/tests/test_location.py
index 5c517ad..404426c 100644
--- a/pvlib/tests/test_location.py
+++ b/pvlib/tests/test_location.py
@@ -16,7 +16,6 @@ from pvlib.location import Location
 from pvlib.solarposition import declination_spencer71
 from pvlib.solarposition import equation_of_time_spencer71
 from test_solarposition import expected_solpos, golden, golden_mst
-from pvlib._deprecation import pvlibDeprecationWarning
 from conftest import requires_ephem, requires_tables, fail_on_pvlib_version
 
 
@@ -326,8 +325,6 @@ def test_get_sun_rise_set_transit_valueerror(golden):
         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):
+def test_extra_kwargs():
+    with pytest.raises(TypeError, match='arbitrary_kwarg'):
         Location(32.2, -111, arbitrary_kwarg='value')
diff --git a/pvlib/tests/test_modelchain.py b/pvlib/tests/test_modelchain.py
index 10acc46..407c8df 100644
--- a/pvlib/tests/test_modelchain.py
+++ b/pvlib/tests/test_modelchain.py
@@ -1162,16 +1162,6 @@ def test_infer_temp_model_invalid(location, sapm_dc_snl_ac_system):
                    spectral_model='no_loss')
 
 
-# ModelChain.infer_temperature_model. remove or statement in v0.9
-@fail_on_pvlib_version('0.9')
-def test_infer_temp_model_no_params(location, system_no_temp, weather):
-    mc = ModelChain(system_no_temp, location, aoi_model='physical',
-                    spectral_model='no_loss')
-    match = "Reverting to deprecated default: SAPM cell temperature"
-    with pytest.warns(pvlibDeprecationWarning, match=match):
-        mc.run_model(weather)
-
-
 def test_temperature_model_inconsistent(location, sapm_dc_snl_ac_system):
     with pytest.raises(ValueError):
         ModelChain(sapm_dc_snl_ac_system, location,
@@ -1222,20 +1212,6 @@ def test_ac_models(sapm_dc_snl_ac_system, cec_dc_adr_ac_system,
     assert mc.results.ac[1] < 1
 
 
-# TODO in v0.9: remove this test for a deprecation warning
-@pytest.mark.parametrize('ac_model', ['snlinverter', 'adrinverter'])
-def test_ac_models_deprecated(sapm_dc_snl_ac_system, cec_dc_adr_ac_system,
-                              location, ac_model, weather):
-    ac_systems = {'snlinverter': sapm_dc_snl_ac_system,
-                  'adrinverter': cec_dc_adr_ac_system}
-    system = ac_systems[ac_model]
-    warn_txt = "ac_model = '" + ac_model + "' is deprecated and will be" +\
-               " removed in v0.9"
-    with pytest.warns(pvlibDeprecationWarning, match=warn_txt):
-        ModelChain(system, location, ac_model=ac_model,
-                   aoi_model='no_loss', spectral_model='no_loss')
-
-
 def test_ac_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather,
                             mocker):
     m = mocker.spy(sys.modules[__name__], 'acdc')
@@ -1468,26 +1444,8 @@ def test_with_sapm_pvsystem_arrays(sapm_dc_snl_ac_system_Array, location,
     assert mc.results
 
 
-@fail_on_pvlib_version('0.9')
-@pytest.mark.parametrize('ac_model', ['snlinverter', 'adrinverter'])
-def test_deprecated_09(sapm_dc_snl_ac_system, cec_dc_adr_ac_system,
-                       location, ac_model, weather):
-    # ModelChain.ac_model = 'snlinverter' or 'adrinverter' deprecated in v0.8,
-    # removed in v0.9
-    ac_systems = {'snlinverter': sapm_dc_snl_ac_system,
-                  'adrinverter': cec_dc_adr_ac_system}
-    system = ac_systems[ac_model]
-    warn_txt = "ac_model = '" + ac_model + "' is deprecated and will be" +\
-               " removed in v0.9"
-    with pytest.warns(pvlibDeprecationWarning, match=warn_txt):
-        ModelChain(system, location, ac_model=ac_model,
-                   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):
+def test_ModelChain_no_extra_kwargs(sapm_dc_snl_ac_system, location):
+    with pytest.raises(TypeError, match="arbitrary_kwarg"):
         ModelChain(sapm_dc_snl_ac_system, location, arbitrary_kwarg='value')
 
 
diff --git a/pvlib/tests/test_pvsystem.py b/pvlib/tests/test_pvsystem.py
index dfc62df..3118208 100644
--- a/pvlib/tests/test_pvsystem.py
+++ b/pvlib/tests/test_pvsystem.py
@@ -15,9 +15,6 @@ from pvlib import iam as _iam
 from pvlib import irradiance
 from pvlib.location import Location
 from pvlib import temperature
-from pvlib._deprecation import pvlibDeprecationWarning
-
-from conftest import fail_on_pvlib_version
 
 
 @pytest.mark.parametrize('iam_model,model_params', [
@@ -1663,31 +1660,6 @@ def test_PVSystem_strings_per_inverter():
     assert system.strings_per_inverter == 5
 
 
-@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)
-    with pytest.warns(pvlibDeprecationWarning):
-        localized_system = system.localize(location=location)
-
-    assert localized_system.module == 'blah'
-    assert localized_system.inverter == 'blarg'
-    assert localized_system.latitude == 32
-    assert localized_system.longitude == -111
-
-
-@fail_on_pvlib_version('0.9')
-def test_PVSystem_localize_with_latlon():
-    system = pvsystem.PVSystem(module='blah', inverter='blarg')
-    with pytest.warns(pvlibDeprecationWarning):
-        localized_system = system.localize(latitude=32, longitude=-111)
-
-    assert localized_system.module == 'blah'
-    assert localized_system.inverter == 'blarg'
-    assert localized_system.latitude == 32
-    assert localized_system.longitude == -111
-
-
 def test_PVSystem___repr__():
     system = pvsystem.PVSystem(
         module='blah', inverter='blarg', name='pv ftw',
@@ -1745,32 +1717,6 @@ def test_PVSystem_multi_array___repr__():
     assert expected == system.__repr__()
 
 
-@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})
-    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
-  latitude: 32
-  longitude: -111
-  altitude: 0
-  tz: UTC
-  surface_tilt: 0
-  surface_azimuth: 180
-  module: blah
-  inverter: blarg
-  albedo: 0.25
-  racking_model: None
-  module_type: None
-  temperature_model_parameters: {'a': -3.56}"""
-
-    assert localized_system.__repr__() == expected
-
-
 def test_Array___repr__():
     array = pvsystem.Array(
         surface_tilt=10, surface_azimuth=100,
@@ -1796,49 +1742,6 @@ def test_Array___repr__():
     assert array.__repr__() == expected
 
 
-# we could retest each of the models tested above
-# 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():
-    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'
-    assert localized_system.latitude == 32
-    assert localized_system.longitude == -111
-
-
-@fail_on_pvlib_version('0.9')
-def test_LocalizedPVSystem___repr__():
-    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
-  latitude: 32
-  longitude: -111
-  altitude: 0
-  tz: UTC
-  surface_tilt: 0
-  surface_azimuth: 180
-  module: blah
-  inverter: blarg
-  albedo: 0.25
-  racking_model: None
-  module_type: None
-  temperature_model_parameters: {'a': -3.56}"""
-
-    assert localized_system.__repr__() == expected
-
-
 def test_pvwatts_dc_scalars():
     expected = 88.65
     out = pvsystem.pvwatts_dc(900, 30, 100, -0.003)
@@ -2049,22 +1952,6 @@ def test_combine_loss_factors():
     assert_series_equal(expected, out)
 
 
-@fail_on_pvlib_version('0.9')
-def test_deprecated_09(cec_inverter_parameters, adr_inverter_parameters):
-    # deprecated function pvsystem.snlinverter
-    with pytest.warns(pvlibDeprecationWarning):
-        pvsystem.snlinverter(250, 40, cec_inverter_parameters)
-    # deprecated function pvsystem.adrinverter
-    with pytest.warns(pvlibDeprecationWarning):
-        pvsystem.adrinverter(1232, 154, adr_inverter_parameters)
-    # deprecated function pvsystem.spvwatts_ac
-    with pytest.warns(pvlibDeprecationWarning):
-        pvsystem.pvwatts_ac(90, 100, 0.95)
-    # for missing temperature_model_parameters
-    match = "Reverting to deprecated default: SAPM cell temperature"
-    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')
+def test_no_extra_kwargs():
+    with pytest.raises(TypeError, match="arbitrary_kwarg"):
+        pvsystem.PVSystem(arbitrary_kwarg='value')
diff --git a/pvlib/tests/test_tracking.py b/pvlib/tests/test_tracking.py
index 77c0f3a..248e3e3 100644
--- a/pvlib/tests/test_tracking.py
+++ b/pvlib/tests/test_tracking.py
@@ -3,13 +3,11 @@ from numpy import nan
 import pandas as pd
 
 import pytest
-from conftest import assert_frame_equal
 from numpy.testing import assert_allclose
 
 import pvlib
-from pvlib.location import Location
 from pvlib import tracking, pvsystem
-from conftest import DATA_DIR
+from conftest import DATA_DIR, assert_frame_equal
 
 SINGLEAXIS_COL_ORDER = ['tracker_theta', 'aoi',
                         'surface_azimuth', 'surface_tilt']
@@ -371,42 +369,6 @@ def test_SingleAxisTracker_tracking():
     assert_frame_equal(expect, tracker_data)
 
 
-def test_LocalizedSingleAxisTracker_creation():
-    localized_system = tracking.LocalizedSingleAxisTracker(latitude=32,
-                                                           longitude=-111,
-                                                           module='blah',
-                                                           inverter='blarg')
-
-    assert localized_system.module == 'blah'
-    assert localized_system.inverter == 'blarg'
-    assert localized_system.latitude == 32
-    assert localized_system.longitude == -111
-
-
-def test_SingleAxisTracker_localize():
-    system = tracking.SingleAxisTracker(max_angle=45, gcr=.25,
-                                        module='blah', inverter='blarg')
-
-    localized_system = system.localize(latitude=32, longitude=-111)
-
-    assert localized_system.module == 'blah'
-    assert localized_system.inverter == 'blarg'
-    assert localized_system.latitude == 32
-    assert localized_system.longitude == -111
-
-
-def test_SingleAxisTracker_localize_location():
-    system = tracking.SingleAxisTracker(max_angle=45, gcr=.25,
-                                        module='blah', inverter='blarg')
-    location = Location(latitude=32, longitude=-111)
-    localized_system = system.localize(location=location)
-
-    assert localized_system.module == 'blah'
-    assert localized_system.inverter == 'blarg'
-    assert localized_system.latitude == 32
-    assert localized_system.longitude == -111
-
-
 # see test_irradiance for more thorough testing
 def test_get_aoi():
     system = tracking.SingleAxisTracker(max_angle=90, axis_tilt=30,
@@ -495,40 +457,6 @@ def test_SingleAxisTracker___repr__():
     assert system.__repr__() == expected
 
 
-def test_LocalizedSingleAxisTracker___repr__():
-    localized_system = tracking.LocalizedSingleAxisTracker(
-        latitude=32, longitude=-111, module='blah', inverter='blarg',
-        gcr=0.25, temperature_model_parameters={'a': -3.56})
-    # apparently the repr order is different for LocalizedSingleAxisTracker
-    # than for LocalizedPVSystem. maybe a MRO thing.
-    expected = """LocalizedSingleAxisTracker:
-  axis_tilt: 0
-  axis_azimuth: 0
-  max_angle: 90
-  backtrack: True
-  gcr: 0.25
-  cross_axis_tilt: 0.0
-  name: None
-  Array:
-    name: None
-    surface_tilt: None
-    surface_azimuth: None
-    module: blah
-    albedo: 0.25
-    racking_model: None
-    module_type: None
-    temperature_model_parameters: {'a': -3.56}
-    strings: 1
-    modules_per_string: 1
-  inverter: blarg
-  latitude: 32
-  longitude: -111
-  altitude: 0
-  tz: UTC"""
-
-    assert localized_system.__repr__() == expected
-
-
 def test_calc_axis_tilt():
     # expected values
     expected_axis_tilt = 2.239  # [degrees]