Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate pvlib.forecast #1426

Merged
merged 11 commits into from
Mar 17, 2022
6 changes: 6 additions & 0 deletions docs/sphinx/source/reference/forecasting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
Forecasting
===========

.. warning::

All functionality in the ``pvlib.forecast`` module is deprecated as of
pvlib v0.9.1. For details, see :ref:`forecasts`.


Forecast models
---------------

Expand Down
19 changes: 19 additions & 0 deletions docs/sphinx/source/user_guide/forecasts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@
Forecasting
***********

.. warning::
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this information could go in the whatsnew as well. Open to suggestions both for its location and its content.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's new links to here, so I think it's fine.


The ``pvlib.forecast`` module is deprecated as of version ``0.9.1``.

Because none of the current pvlib team members are able to continue
maintaining it, the functionality in ``pvlib.forecast`` is deprecated
and will be removed without replacement in a future version. If you
are interested in maintaining this functionality, please let us know.

You can fetch forecast data yourself using ``siphon`` (see the
docs below this warning) and the code from pvlib v0.9.0 as a reference:
https://github.com/pvlib/pvlib-python/blob/v0.9.0/pvlib/forecast.py

The `Solar Forecast Arbiter Core
<https://solarforecastarbiter-core.readthedocs.io/en/stable/reference-forecasts.html>`_
offers similar (and more robust) forecast processing functionality
and may be a suitable replacement for some users.


pvlib python provides a set of functions and classes that make it easy
to obtain weather forecast data and convert that data into a PV power
forecast. Users can retrieve standardized weather forecast data relevant
Expand Down
2 changes: 2 additions & 0 deletions docs/sphinx/source/whatsnew/v0.9.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Deprecations
:py:meth:`pvlib.modelchain.ModelChain.with_sapm` for alternative simplified
:py:class:`~pvlib.modelchain.ModelChain` interfaces, although note that the
inputs do not directly translate. (:pull:`1401`)
* All functionality in the ``pvlib.forecast`` module is deprecated.
For details, see :ref:`forecasts`. (:issue:`1057`, :pull:`1426`)

Enhancements
~~~~~~~~~~~~
Expand Down
15 changes: 15 additions & 0 deletions pvlib/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,23 @@
from siphon.ncss import NCSS

import warnings
from pvlib._deprecation import deprecated


warnings.warn(
'The forecast module algorithms and features are highly experimental. '
'The API may change, the functionality may be consolidated into an io '
'module, or the module may be separated into its own package.')

_forecast_deprecated = deprecated(
since='0.9.1',
removal='a future release',
addendum='For details, see https://pvlib-python.readthedocs.io/en/stable/user_guide/forecasts.html' # noqa: E501
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This URL is currently 404 but should work after 0.9.1 is released.

)

# don't decorate the base class to prevent the subclasses from showing
# duplicate warnings:
# @_forecast_deprecated
class ForecastModel:
"""
An object for querying and holding forecast model information for
Expand Down Expand Up @@ -684,6 +693,7 @@ def gust_to_speed(self, data, scaling=1/1.4):
return wind_speed


@_forecast_deprecated
class GFS(ForecastModel):
"""
Subclass of the ForecastModel class representing GFS
Expand Down Expand Up @@ -785,6 +795,7 @@ def process_data(self, data, cloud_cover='total_clouds', **kwargs):
return data[self.output_variables]


@_forecast_deprecated
class HRRR_ESRL(ForecastModel): # noqa: N801
"""
Subclass of the ForecastModel class representing
Expand Down Expand Up @@ -875,6 +886,7 @@ def process_data(self, data, cloud_cover='total_clouds', **kwargs):
return data[self.output_variables]


@_forecast_deprecated
class NAM(ForecastModel):
"""
Subclass of the ForecastModel class representing NAM
Expand Down Expand Up @@ -956,6 +968,7 @@ def process_data(self, data, cloud_cover='total_clouds', **kwargs):
return data[self.output_variables]


@_forecast_deprecated
class HRRR(ForecastModel):
"""
Subclass of the ForecastModel class representing HRRR
Expand Down Expand Up @@ -1044,6 +1057,7 @@ def process_data(self, data, cloud_cover='total_clouds', **kwargs):
return data[self.output_variables]


@_forecast_deprecated
class NDFD(ForecastModel):
"""
Subclass of the ForecastModel class representing NDFD forecast
Expand Down Expand Up @@ -1112,6 +1126,7 @@ def process_data(self, data, **kwargs):
return data[self.output_variables]


@_forecast_deprecated
class RAP(ForecastModel):
"""
Subclass of the ForecastModel class representing RAP forecast model.
Expand Down
44 changes: 30 additions & 14 deletions pvlib/tests/test_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
)
from .conftest import RERUNS, RERUNS_DELAY

from pvlib._deprecation import pvlibDeprecationWarning

pytestmark = pytest.mark.skipif(not has_siphon, reason='requires siphon')


Expand Down Expand Up @@ -52,7 +54,8 @@
@requires_siphon
@pytest.fixture(scope='module', params=_modelclasses)
def model(request):
amodel = request.param()
with pytest.warns(pvlibDeprecationWarning):
amodel = request.param()
try:
raw_data = amodel.get_data(_latitude, _longitude, _start, _end)
except Exception as e:
Expand Down Expand Up @@ -90,7 +93,8 @@ def test_process_data(model):
def test_bad_kwarg_get_data():
# For more information on why you would want to pass an unknown keyword
# argument, see Github issue #745.
amodel = NAM()
with pytest.warns(pvlibDeprecationWarning):
amodel = NAM()
data = amodel.get_data(_latitude, _longitude, _start, _end,
bad_kwarg=False)
assert not data.empty
Expand All @@ -103,7 +107,8 @@ def test_bad_kwarg_get_data():
def test_bad_kwarg_get_processed_data():
# For more information on why you would want to pass an unknown keyword
# argument, see Github issue #745.
amodel = NAM()
with pytest.warns(pvlibDeprecationWarning):
amodel = NAM()
data = amodel.get_processed_data(_latitude, _longitude, _start, _end,
bad_kwarg=False)
assert not data.empty
Expand All @@ -114,7 +119,8 @@ def test_bad_kwarg_get_processed_data():
@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_how_kwarg_get_processed_data():
amodel = NAM()
with pytest.warns(pvlibDeprecationWarning):
amodel = NAM()
data = amodel.get_processed_data(_latitude, _longitude, _start, _end,
how='clearsky_scaling')
assert not data.empty
Expand All @@ -125,7 +131,8 @@ def test_how_kwarg_get_processed_data():
@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_vert_level():
amodel = NAM()
with pytest.warns(pvlibDeprecationWarning):
amodel = NAM()
vert_level = 5000
amodel.get_processed_data(_latitude, _longitude, _start, _end,
vert_level=vert_level)
Expand All @@ -136,7 +143,8 @@ def test_vert_level():
@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_datetime():
amodel = NAM()
with pytest.warns(pvlibDeprecationWarning):
amodel = NAM()
start = datetime.now(tz=timezone.utc)
end = start + timedelta(days=1)
amodel.get_processed_data(_latitude, _longitude, start, end)
Expand All @@ -147,7 +155,8 @@ def test_datetime():
@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_queryvariables():
amodel = GFS()
with pytest.warns(pvlibDeprecationWarning):
amodel = GFS()
new_variables = ['u-component_of_wind_height_above_ground']
data = amodel.get_data(_latitude, _longitude, _start, _end,
query_variables=new_variables)
Expand All @@ -156,16 +165,19 @@ def test_queryvariables():

@requires_siphon
def test_latest():
GFS(set_type='latest')
with pytest.warns(pvlibDeprecationWarning):
GFS(set_type='latest')


@requires_siphon
def test_full():
GFS(set_type='full')
with pytest.warns(pvlibDeprecationWarning):
GFS(set_type='full')


def test_temp_convert():
amodel = GFS()
with pytest.warns(pvlibDeprecationWarning):
amodel = GFS()
data = pd.DataFrame({'temp_air': [273.15]})
data['temp_air'] = amodel.kelvin_to_celsius(data['temp_air'])

Expand All @@ -183,27 +195,31 @@ def test_temp_convert():


def test_set_location():
amodel = GFS()
with pytest.warns(pvlibDeprecationWarning):
amodel = GFS()
latitude, longitude = 32.2, -110.9
time = 'UTC'
amodel.set_location(time, latitude, longitude)


def test_set_query_time_range_tzfail():
amodel = GFS()
with pytest.warns(pvlibDeprecationWarning):
amodel = GFS()
with pytest.raises(TypeError):
amodel.set_query_time_range(datetime.now(), datetime.now())


def test_cloud_cover_to_transmittance_linear():
amodel = GFS()
with pytest.warns(pvlibDeprecationWarning):
amodel = GFS()
assert_allclose(amodel.cloud_cover_to_transmittance_linear(0), 0.75)
assert_allclose(amodel.cloud_cover_to_transmittance_linear(100), 0.0)
assert_allclose(amodel.cloud_cover_to_transmittance_linear(0, 0.5), 0.5)


def test_cloud_cover_to_ghi_linear():
amodel = GFS()
with pytest.warns(pvlibDeprecationWarning):
amodel = GFS()
ghi_clear = 1000
offset = 25
out = amodel.cloud_cover_to_ghi_linear(0, ghi_clear, offset=offset)
Expand Down