Skip to content
Merged
10 changes: 10 additions & 0 deletions docs/sphinx/source/whatsnew/v0.7.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
v0.7.2 (Month day, year)
-------------------------

API Changes
~~~~~~~~~~~
* :py:class:`pvlib.forecast.ForecastModel` now requires ``start`` and ``end``
arguments to be tz-localized. (:issue:`877`, :pull:`879`)

Enhancements
~~~~~~~~~~~~
* TMY3 dataframe returned by :py:func:`~pvlib.iotools.read_tmy3` now contains
Expand All @@ -15,6 +20,8 @@ Bug fixes
a leap year (:pull:`866`)
* Implement NREL Developer Network API key for consistent success with API
calls in :py:mod:`pvlib.tests.iotools.test_psm3` (:pull:`873`)
* Fix issue with :py:class:`pvlib.location.Location` creation when
passing ``tz=datetime.timezone.utc`` (:pull:`879`)

Documentation
~~~~~~~~~~~~~
Expand All @@ -29,3 +36,6 @@ Contributors
* Mark Mikofski (:ghuser:`mikofski`)
* Cliff Hansen (:ghuser:`cwhanse`)
* Cameron T. Stark (:ghuser:`camerontstark`)
* Will Holmgren (:ghuser:`wholmgren`)
* Kevin Anderson (:ghuser:`kanderso-nrel`)
* Karthikeyan Singaravelan (:ghuser:`tirkarthi`)
51 changes: 34 additions & 17 deletions pvlib/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,25 @@ def set_dataset(self):
self.ncss = NCSS(self.access_url)
self.query = self.ncss.query()

def set_query_time_range(self, start, end):
"""
Parameters
----------
start : datetime.datetime, pandas.Timestamp
Must be tz-localized.
end : datetime.datetime, pandas.Timestamp
Must be tz-localized.

Notes
-----
Assigns ``self.start``, ``self.end``. Modifies ``self.query``
"""
self.start = pd.Timestamp(start)
self.end = pd.Timestamp(end)
if self.start.tz is None or self.end.tz is None:
raise TypeError('start and end must be tz-localized')
self.query.time_range(self.start, self.end)

def set_query_latlon(self):
'''
Sets the NCSS query location latitude and longitude.
Expand All @@ -180,24 +199,24 @@ def set_query_latlon(self):
self.lbox = False
self.query.lonlat_point(self.longitude, self.latitude)

def set_location(self, time, latitude, longitude):
def set_location(self, tz, latitude, longitude):
'''
Sets the location for the query.

Parameters
----------
time: datetime or DatetimeIndex
Time range of the query.
'''
if isinstance(time, datetime.datetime):
tzinfo = time.tzinfo
else:
tzinfo = time.tz
tz: tzinfo
Timezone of the query
latitude: float
Latitude of the query
longitude: float
Longitude of the query

if tzinfo is None:
self.location = Location(latitude, longitude)
else:
self.location = Location(latitude, longitude, tz=tzinfo)
Notes
-----
Assigns ``self.location``.
'''
self.location = Location(latitude, longitude, tz=tz)

def get_data(self, latitude, longitude, start, end,
vert_level=None, query_variables=None,
Expand Down Expand Up @@ -243,14 +262,12 @@ def get_data(self, latitude, longitude, start, end,
else:
self.query_variables = query_variables

self.set_query_time_range(start, end)

self.latitude = latitude
self.longitude = longitude
self.set_query_latlon() # modifies self.query
self.set_location(start, latitude, longitude)

self.start = start
self.end = end
self.query.time_range(self.start, self.end)
self.set_location(self.start.tz, latitude, longitude)

if self.vert_level is not None:
self.query.vertical_level(self.vert_level)
Expand Down
2 changes: 1 addition & 1 deletion pvlib/iotools/ecmwf_macc.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def get_ecmwf_macc(filename, params, startdate, stopdate, lookup_params=True,
key. Please read the documentation in `Access ECMWF Public Datasets
<https://confluence.ecmwf.int/display/WEBAPI/Access+ECMWF+Public+Datasets>`_.
Follow the instructions in step 4 and save the ECMWF registration key
as `$HOME\.ecmwfapirc` or set `ECMWF_API_KEY` as the path to the key.
as `$HOME/.ecmwfapirc` or set `ECMWF_API_KEY` as the path to the key.

This function returns a daemon thread that runs in the background. Exiting
Python will kill this thread, however this thread will not block the main
Expand Down
3 changes: 3 additions & 0 deletions pvlib/location.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ def __init__(self, latitude, longitude, tz='UTC', altitude=0,
if isinstance(tz, str):
self.tz = tz
self.pytz = pytz.timezone(tz)
elif isinstance(tz, datetime.timezone):
self.tz = 'UTC'
self.pytz = pytz.UTC
elif isinstance(tz, datetime.tzinfo):
self.tz = tz.zone
self.pytz = tz
Expand Down
8 changes: 0 additions & 8 deletions pvlib/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,6 @@ def inner():
requires_ephem = pytest.mark.skipif(not has_ephem, reason='requires ephem')


def pandas_0_17():
return parse_version(pd.__version__) >= parse_version('0.17.0')


needs_pandas_0_17 = pytest.mark.skipif(
not pandas_0_17(), reason='requires pandas 0.17 or greater')


def numpy_1_10():
return parse_version(np.__version__) >= parse_version('1.10.0')

Expand Down
8 changes: 1 addition & 7 deletions pvlib/tests/iotools/test_psm3.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import os
from pvlib.iotools import psm3
from conftest import needs_pandas_0_22, DATA_DIR
from conftest import DATA_DIR
import numpy as np
import pandas as pd
import pytest
Expand Down Expand Up @@ -70,7 +70,6 @@ def assert_psm3_equal(header, data, expected):
assert (data.index.tzinfo.zone == 'Etc/GMT%+d' % -header['Time Zone'])


@needs_pandas_0_22
@pytest.mark.flaky(reruns=5, reruns_delay=2)
def test_get_psm3_tmy(nrel_api_key):
"""test get_psm3 with a TMY"""
Expand All @@ -80,7 +79,6 @@ def test_get_psm3_tmy(nrel_api_key):
assert_psm3_equal(header, data, expected)


@needs_pandas_0_22
@pytest.mark.flaky(reruns=5, reruns_delay=2)
def test_get_psm3_singleyear(nrel_api_key):
"""test get_psm3 with a single year"""
Expand All @@ -90,7 +88,6 @@ def test_get_psm3_singleyear(nrel_api_key):
assert_psm3_equal(header, data, expected)


@needs_pandas_0_22
@pytest.mark.flaky(reruns=5, reruns_delay=2)
def test_get_psm3_check_leap_day(nrel_api_key):
_, data_2012 = psm3.get_psm3(LATITUDE, LONGITUDE, nrel_api_key,
Expand All @@ -105,7 +102,6 @@ def test_get_psm3_check_leap_day(nrel_api_key):
(LATITUDE, LONGITUDE, nrel_api_key, 'bad', 60),
(LATITUDE, LONGITUDE, nrel_api_key, '2017', 15),
])
@needs_pandas_0_22
@pytest.mark.flaky(reruns=5, reruns_delay=2)
def test_get_psm3_tmy_errors(
latitude, longitude, api_key, names, interval
Expand Down Expand Up @@ -134,15 +130,13 @@ def io_input(request):
return obj


@needs_pandas_0_22
def test_parse_psm3(io_input):
"""test parse_psm3"""
header, data = psm3.parse_psm3(io_input)
expected = pd.read_csv(YEAR_TEST_DATA)
assert_psm3_equal(header, data, expected)


@needs_pandas_0_22
def test_read_psm3():
"""test read_psm3"""
header, data = psm3.read_psm3(MANUAL_TEST_DATA)
Expand Down
15 changes: 9 additions & 6 deletions pvlib/tests/test_forecast.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from datetime import datetime, timedelta
from pytz import timezone
from datetime import datetime, timedelta, timezone
import warnings

import pandas as pd
Expand Down Expand Up @@ -114,7 +113,7 @@ def test_vert_level():
@requires_siphon
def test_datetime():
amodel = NAM()
start = datetime.now()
start = datetime.now(tz=timezone.utc)
end = start + timedelta(days=1)
amodel.get_processed_data(_latitude, _longitude, start, end)

Expand All @@ -138,7 +137,6 @@ def test_full():
GFS(set_type='full')


@requires_siphon
def test_temp_convert():
amodel = GFS()
data = pd.DataFrame({'temp_air': [273.15]})
Expand All @@ -157,14 +155,19 @@ def test_temp_convert():
# variables=new_variables)


@requires_siphon
def test_set_location():
amodel = GFS()
latitude, longitude = 32.2, -110.9
time = datetime.now(timezone('UTC'))
time = 'UTC'
amodel.set_location(time, latitude, longitude)


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


def test_cloud_cover_to_transmittance_linear():
amodel = GFS()
assert_allclose(amodel.cloud_cover_to_transmittance_linear(0), 0.75)
Expand Down
1 change: 1 addition & 0 deletions pvlib/tests/test_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def test_location_all():

@pytest.mark.parametrize('tz', [
pytz.timezone('US/Arizona'), 'America/Phoenix', -7, -7.0,
datetime.timezone.utc
])
def test_location_tz(tz):
Location(32.2, -111, tz)
Expand Down
4 changes: 1 addition & 3 deletions pvlib/tests/test_solarposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
from pvlib.location import Location
from pvlib import solarposition, spa

from conftest import (requires_ephem, needs_pandas_0_17,
requires_spa_c, requires_numba)
from conftest import requires_ephem, requires_spa_c, requires_numba


# setup times and locations to be tested.
Expand Down Expand Up @@ -164,7 +163,6 @@ def test_spa_python_numpy_physical_dst(expected_solpos, golden):
assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns])


@needs_pandas_0_17
def test_sun_rise_set_transit_spa(expected_rise_set_spa, golden):
# solution from NREL SAP web calculator
south = Location(-35.0, 0.0, tz='UTC')
Expand Down