Skip to content

Commit c243183

Browse files
authored
Deprecate pvlib.forecast (#1426)
* deprecate pvlib.forecast classes * catch warnings in tests * add warning admonition to forecasts.rst * whatsnew * stickler * pin pytest < 7.1.0 * pin pytest in the right place this time * more warning suppression in tests * unpin pytest * Update docs/sphinx/source/whatsnew/v0.9.1.rst * copy warning to reference/forecasting.rst
1 parent e3baa12 commit c243183

File tree

5 files changed

+72
-14
lines changed

5 files changed

+72
-14
lines changed

docs/sphinx/source/reference/forecasting.rst

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
Forecasting
44
===========
55

6+
.. warning::
7+
8+
All functionality in the ``pvlib.forecast`` module is deprecated as of
9+
pvlib v0.9.1. For details, see :ref:`forecasts`.
10+
11+
612
Forecast models
713
---------------
814

docs/sphinx/source/user_guide/forecasts.rst

+19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@
44
Forecasting
55
***********
66

7+
.. warning::
8+
9+
The ``pvlib.forecast`` module is deprecated as of version ``0.9.1``.
10+
11+
Because none of the current pvlib team members are able to continue
12+
maintaining it, the functionality in ``pvlib.forecast`` is deprecated
13+
and will be removed without replacement in a future version. If you
14+
are interested in maintaining this functionality, please let us know.
15+
16+
You can fetch forecast data yourself using ``siphon`` (see the
17+
docs below this warning) and the code from pvlib v0.9.0 as a reference:
18+
https://github.com/pvlib/pvlib-python/blob/v0.9.0/pvlib/forecast.py
19+
20+
The `Solar Forecast Arbiter Core
21+
<https://solarforecastarbiter-core.readthedocs.io/en/stable/reference-forecasts.html>`_
22+
offers similar (and more robust) forecast processing functionality
23+
and may be a suitable replacement for some users.
24+
25+
726
pvlib python provides a set of functions and classes that make it easy
827
to obtain weather forecast data and convert that data into a PV power
928
forecast. Users can retrieve standardized weather forecast data relevant

docs/sphinx/source/whatsnew/v0.9.1.rst

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Deprecations
1616
:py:meth:`pvlib.modelchain.ModelChain.with_sapm` for alternative simplified
1717
:py:class:`~pvlib.modelchain.ModelChain` interfaces, although note that the
1818
inputs do not directly translate. (:pull:`1401`)
19+
* All functionality in the ``pvlib.forecast`` module is deprecated.
20+
For details, see :ref:`forecasts`. (:issue:`1057`, :pull:`1426`)
1921

2022
Enhancements
2123
~~~~~~~~~~~~

pvlib/forecast.py

+15
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,23 @@
1515
from siphon.ncss import NCSS
1616

1717
import warnings
18+
from pvlib._deprecation import deprecated
1819

1920

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

26+
_forecast_deprecated = deprecated(
27+
since='0.9.1',
28+
removal='a future release',
29+
addendum='For details, see https://pvlib-python.readthedocs.io/en/stable/user_guide/forecasts.html' # noqa: E501
30+
)
2531

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

686695

696+
@_forecast_deprecated
687697
class GFS(ForecastModel):
688698
"""
689699
Subclass of the ForecastModel class representing GFS
@@ -785,6 +795,7 @@ def process_data(self, data, cloud_cover='total_clouds', **kwargs):
785795
return data[self.output_variables]
786796

787797

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

877888

889+
@_forecast_deprecated
878890
class NAM(ForecastModel):
879891
"""
880892
Subclass of the ForecastModel class representing NAM
@@ -956,6 +968,7 @@ def process_data(self, data, cloud_cover='total_clouds', **kwargs):
956968
return data[self.output_variables]
957969

958970

971+
@_forecast_deprecated
959972
class HRRR(ForecastModel):
960973
"""
961974
Subclass of the ForecastModel class representing HRRR
@@ -1044,6 +1057,7 @@ def process_data(self, data, cloud_cover='total_clouds', **kwargs):
10441057
return data[self.output_variables]
10451058

10461059

1060+
@_forecast_deprecated
10471061
class NDFD(ForecastModel):
10481062
"""
10491063
Subclass of the ForecastModel class representing NDFD forecast
@@ -1112,6 +1126,7 @@ def process_data(self, data, **kwargs):
11121126
return data[self.output_variables]
11131127

11141128

1129+
@_forecast_deprecated
11151130
class RAP(ForecastModel):
11161131
"""
11171132
Subclass of the ForecastModel class representing RAP forecast model.

pvlib/tests/test_forecast.py

+30-14
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
)
1515
from .conftest import RERUNS, RERUNS_DELAY
1616

17+
from pvlib._deprecation import pvlibDeprecationWarning
18+
1719
pytestmark = pytest.mark.skipif(not has_siphon, reason='requires siphon')
1820

1921

@@ -52,7 +54,8 @@
5254
@requires_siphon
5355
@pytest.fixture(scope='module', params=_modelclasses)
5456
def model(request):
55-
amodel = request.param()
57+
with pytest.warns(pvlibDeprecationWarning):
58+
amodel = request.param()
5659
try:
5760
raw_data = amodel.get_data(_latitude, _longitude, _start, _end)
5861
except Exception as e:
@@ -90,7 +93,8 @@ def test_process_data(model):
9093
def test_bad_kwarg_get_data():
9194
# For more information on why you would want to pass an unknown keyword
9295
# argument, see Github issue #745.
93-
amodel = NAM()
96+
with pytest.warns(pvlibDeprecationWarning):
97+
amodel = NAM()
9498
data = amodel.get_data(_latitude, _longitude, _start, _end,
9599
bad_kwarg=False)
96100
assert not data.empty
@@ -103,7 +107,8 @@ def test_bad_kwarg_get_data():
103107
def test_bad_kwarg_get_processed_data():
104108
# For more information on why you would want to pass an unknown keyword
105109
# argument, see Github issue #745.
106-
amodel = NAM()
110+
with pytest.warns(pvlibDeprecationWarning):
111+
amodel = NAM()
107112
data = amodel.get_processed_data(_latitude, _longitude, _start, _end,
108113
bad_kwarg=False)
109114
assert not data.empty
@@ -114,7 +119,8 @@ def test_bad_kwarg_get_processed_data():
114119
@pytest.mark.remote_data
115120
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
116121
def test_how_kwarg_get_processed_data():
117-
amodel = NAM()
122+
with pytest.warns(pvlibDeprecationWarning):
123+
amodel = NAM()
118124
data = amodel.get_processed_data(_latitude, _longitude, _start, _end,
119125
how='clearsky_scaling')
120126
assert not data.empty
@@ -125,7 +131,8 @@ def test_how_kwarg_get_processed_data():
125131
@pytest.mark.remote_data
126132
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
127133
def test_vert_level():
128-
amodel = NAM()
134+
with pytest.warns(pvlibDeprecationWarning):
135+
amodel = NAM()
129136
vert_level = 5000
130137
amodel.get_processed_data(_latitude, _longitude, _start, _end,
131138
vert_level=vert_level)
@@ -136,7 +143,8 @@ def test_vert_level():
136143
@pytest.mark.remote_data
137144
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
138145
def test_datetime():
139-
amodel = NAM()
146+
with pytest.warns(pvlibDeprecationWarning):
147+
amodel = NAM()
140148
start = datetime.now(tz=timezone.utc)
141149
end = start + timedelta(days=1)
142150
amodel.get_processed_data(_latitude, _longitude, start, end)
@@ -147,7 +155,8 @@ def test_datetime():
147155
@pytest.mark.remote_data
148156
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
149157
def test_queryvariables():
150-
amodel = GFS()
158+
with pytest.warns(pvlibDeprecationWarning):
159+
amodel = GFS()
151160
new_variables = ['u-component_of_wind_height_above_ground']
152161
data = amodel.get_data(_latitude, _longitude, _start, _end,
153162
query_variables=new_variables)
@@ -156,16 +165,19 @@ def test_queryvariables():
156165

157166
@requires_siphon
158167
def test_latest():
159-
GFS(set_type='latest')
168+
with pytest.warns(pvlibDeprecationWarning):
169+
GFS(set_type='latest')
160170

161171

162172
@requires_siphon
163173
def test_full():
164-
GFS(set_type='full')
174+
with pytest.warns(pvlibDeprecationWarning):
175+
GFS(set_type='full')
165176

166177

167178
def test_temp_convert():
168-
amodel = GFS()
179+
with pytest.warns(pvlibDeprecationWarning):
180+
amodel = GFS()
169181
data = pd.DataFrame({'temp_air': [273.15]})
170182
data['temp_air'] = amodel.kelvin_to_celsius(data['temp_air'])
171183

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

184196

185197
def test_set_location():
186-
amodel = GFS()
198+
with pytest.warns(pvlibDeprecationWarning):
199+
amodel = GFS()
187200
latitude, longitude = 32.2, -110.9
188201
time = 'UTC'
189202
amodel.set_location(time, latitude, longitude)
190203

191204

192205
def test_set_query_time_range_tzfail():
193-
amodel = GFS()
206+
with pytest.warns(pvlibDeprecationWarning):
207+
amodel = GFS()
194208
with pytest.raises(TypeError):
195209
amodel.set_query_time_range(datetime.now(), datetime.now())
196210

197211

198212
def test_cloud_cover_to_transmittance_linear():
199-
amodel = GFS()
213+
with pytest.warns(pvlibDeprecationWarning):
214+
amodel = GFS()
200215
assert_allclose(amodel.cloud_cover_to_transmittance_linear(0), 0.75)
201216
assert_allclose(amodel.cloud_cover_to_transmittance_linear(100), 0.0)
202217
assert_allclose(amodel.cloud_cover_to_transmittance_linear(0, 0.5), 0.5)
203218

204219

205220
def test_cloud_cover_to_ghi_linear():
206-
amodel = GFS()
221+
with pytest.warns(pvlibDeprecationWarning):
222+
amodel = GFS()
207223
ghi_clear = 1000
208224
offset = 25
209225
out = amodel.cloud_cover_to_ghi_linear(0, ghi_clear, offset=offset)

0 commit comments

Comments
 (0)