Skip to content

Commit e0e283d

Browse files
authored
add timezone input to date range formula (#54)
* add tz as argument to d_daterange, fixes #29 * test uncertainty propagation * use try-finally in tests package to close fid if find_module fails * use Uncertainties wrapper to test propagated uncertainty in solar position calculations * travis ci script YAML as list * add uncertainties to requirements * add test folder to pvpower demo
1 parent 0a7a7c8 commit e0e283d

9 files changed

Lines changed: 91 additions & 17 deletions

File tree

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ before_install:
77
- sudo apt-get install -y libatlas-dev liblapack-dev libblas-dev
88
- sudo apt-get install -y libhdf5-serial-dev
99
# command to run tests
10-
script: py.test
11-
script: python setup.py bdist_wheel
10+
script:
11+
- py.test
12+
- python setup.py bdist_wheel
1213
deploy:
1314
- provider: releases
1415
api_key:

carousel/tests/__init__.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
PROJ_PATH = os.path.abspath(os.path.join(
1616
TESTS_DIR, '..', '..', 'examples', PROJECT
1717
))
18-
sys.path.append(PROJ_PATH)
1918

20-
fid, fn, info = imp.find_module(MODEL,
21-
[os.path.join(PROJ_PATH, PROJECT.lower())])
22-
pvpower_models = imp.load_module(MODEL, fid, fn, info)
23-
fid.close()
19+
sys.path.append(PROJ_PATH)
20+
try:
21+
fid, fn, info = imp.find_module(MODEL,
22+
[os.path.join(PROJ_PATH, PROJECT.lower())])
23+
pvpower_models = imp.load_module(MODEL, fid, fn, info)
24+
finally:
25+
fid.close()

carousel/tests/test_calcs.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@
44

55
from nose.tools import ok_, eq_
66
from carousel.core.calculations import Calc
7-
from carousel.tests import PROJ_PATH
7+
from carousel.tests import PROJ_PATH, pvpower_models
88
import os
9+
import uncertainties
10+
from pvlib.solarposition import get_solarposition as solpos
11+
import logging
12+
import numpy as np
13+
14+
LOGGER = logging.getLogger(__name__)
915

1016

1117
def test_calc_metaclass():
@@ -57,3 +63,57 @@ class CalcTest2(Calc):
5763
ok_(isinstance(calc_test2, Calc))
5864
for k, v in calc_test2.parameters.iteritems():
5965
eq_(calc_test1.parameters[k], v)
66+
67+
68+
def test_static_calc_unc():
69+
"""
70+
Test uncertainty propagation in static calculations using Uncertainties.
71+
"""
72+
73+
# FIXME: this shouldn't have to run a model to test the uncertainty
74+
test_model_file = os.path.join(PROJ_PATH, 'models',
75+
'sandia_performance_model-Tuscon.json')
76+
test_model = pvpower_models.SAPM(test_model_file) # create model
77+
test_model.command('start') # start simulation
78+
# get parameters from model
79+
dt = test_model.outputs.reg['timestamps'] # timestamps
80+
latitude = test_model.data.reg['latitude'].m # latitude [degrees]
81+
longitude = test_model.data.reg['longitude'].m # longitude [degrees]
82+
zenith = test_model.outputs.reg['solar_zenith'].m # zenith [degrees]
83+
s_ze_ze = test_model.outputs.reg.variance['solar_zenith']['solar_zenith']
84+
azimuth = test_model.outputs.reg['solar_azimuth'].m # azimuth [degrees]
85+
s_az_az = test_model.outputs.reg.variance['solar_azimuth']['solar_azimuth']
86+
# get uncertainties percentages in base units
87+
lat_unc = test_model.data.reg.uncertainty['latitude']['latitude']
88+
lat_unc = lat_unc.to_base_units().m
89+
lon_unc = test_model.data.reg.uncertainty['longitude']['longitude']
90+
lon_unc = lon_unc.to_base_units().m
91+
# create ufloat Uncertainties from parameters
92+
lat_unc = uncertainties.ufloat(latitude, np.abs(latitude * lat_unc))
93+
lon_unc = uncertainties.ufloat(longitude, np.abs(longitude * lon_unc))
94+
test_unc = [] # empty list to collect return values
95+
for n in xrange(96):
96+
# Uncertainties wrapped functions must return only scalar float
97+
f_ze_unc = uncertainties.wrap(
98+
lambda lat, lon: solpos(dt[n], lat, lon)['apparent_zenith'].item()
99+
)
100+
f_az_unc = uncertainties.wrap(
101+
lambda lat, lon: solpos(dt[n], lat, lon)['azimuth'].item()
102+
)
103+
ze_unc, az_unc = f_ze_unc(lat_unc, lon_unc), f_az_unc(lat_unc, lon_unc)
104+
LOGGER.debug(
105+
'%s: ze = %g +/- %g%%, az = %g +/- %g%%', dt[n].isoformat(),
106+
zenith[n], np.sqrt(s_ze_ze[n]) * 100,
107+
azimuth[n], np.sqrt(s_az_az[n]) * 100
108+
)
109+
LOGGER.debug(
110+
'Uncertainties test %2d: ze = %g +/- %g%%, az = %g +/- %g%%', n,
111+
ze_unc.n, ze_unc.s / ze_unc.n * 100,
112+
az_unc.n, az_unc.s / az_unc.n * 100
113+
)
114+
assert np.isclose(zenith[n], ze_unc.n)
115+
assert np.isclose(np.sqrt(s_ze_ze[n]), ze_unc.s / ze_unc.n)
116+
assert np.isclose(azimuth[n], az_unc.n)
117+
assert np.isclose(np.sqrt(s_az_az[n]), az_unc.s / az_unc.n)
118+
test_unc.append({'ze': ze_unc, 'az': az_unc})
119+
return test_model, test_unc

examples/PVPower/calculations/irradiance.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"args": {
66
"data": {
77
"freq": "HOURLY", "dtstart": "timestamp_start",
8-
"count": "timestamp_count"
8+
"count": "timestamp_count", "tz": "timezone"
99
}
1010
},
1111
"returns": ["timestamps"]

examples/PVPower/formulas/utils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
from scipy import constants as sc_const
99
import itertools
1010
from dateutil import rrule
11+
import pytz
1112

1213

13-
def f_daterange(freq, *args, **kwargs):
14+
def f_daterange(freq, tz='UTC', *args, **kwargs):
1415
"""
1516
Use ``dateutil.rrule`` to create a range of dates. The frequency must be a
1617
string in the following list: YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY,
@@ -22,14 +23,17 @@ def f_daterange(freq, *args, **kwargs):
2223
2324
:param freq: One of the ``dateutil.rrule`` frequencies
2425
:type freq: str
26+
:param tz: One of the ``pytz`` timezones, defaults to UTC
27+
:type tz: str
2528
:param args: start date <datetime>, interval between each frequency <int>,
2629
max number of recurrences <int>, end date <datetime>
2730
:param kwargs: ``dtstart``, ``interval``, ``count``, ``until``
2831
:return: range of dates
2932
:rtype: list
3033
"""
34+
tz = pytz.timezone(tz)
3135
freq = getattr(rrule, freq.upper()) # get frequency enumeration from rrule
32-
return list(rrule.rrule(freq, *args, **kwargs))
36+
return [tz.localize(dt) for dt in rrule.rrule(freq, *args, **kwargs)]
3337

3438

3539
def f_energy(ac_power, times):

examples/PVPower/pvpower/tests/test_pvpower.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515

1616
LOGGER = logging.getLogger(__name__)
1717
LOGGER.setLevel(logging.DEBUG)
18-
PST = pytz.timezone('US/Pacific')
18+
TZ = 'US/Pacific'
19+
PST = pytz.timezone(TZ)
1920
UTIL_FORMULAS = UtilityFormulas()
2021
IRRAD_FORMULAS = IrradianceFormulas()
21-
DTSTART = PST.localize(datetime(2007, 1, 1, 0, 0, 0))
22+
DTSTART = datetime(2007, 1, 1, 0, 0, 0)
2223
MONTHLY_ENERGY = [186000.0, 168000.0, 186000.0, 180000.0, 186000.0, 180000.0,
2324
186000.0, 186000.0, 180000.0, 186000.0, 180000.0, 186000.0]
2425
ZENITH = [
@@ -36,9 +37,14 @@ def test_daterange():
3637
"""
3738
Test date range.
3839
"""
39-
dates = UTIL_FORMULAS['f_daterange']('HOURLY', dtstart=DTSTART, count=12)
40-
for hour in xrange(12):
41-
assert dates[hour] == DTSTART + timedelta(hours=hour)
40+
test_range = 12
41+
dates = UTIL_FORMULAS['f_daterange'](
42+
'HOURLY', TZ, dtstart=DTSTART, count=test_range
43+
)
44+
dtstart_local = PST.localize(DTSTART)
45+
for hour in xrange(test_range):
46+
assert dates[hour] == dtstart_local + timedelta(hours=hour)
47+
assert dates[hour].tzinfo.zone == TZ
4248
return dates
4349

4450

@@ -48,7 +54,7 @@ def test_solarposition():
4854
"""
4955
lat, lon = 38.0 * UREG.degrees, -122.0 * UREG.degrees
5056
times = UTIL_FORMULAS['f_daterange'](
51-
'HOURLY', dtstart=(DTSTART + timedelta(hours=8)), count=9
57+
'HOURLY', TZ, dtstart=(DTSTART + timedelta(hours=8)), count=9
5258
)
5359
cov = np.array([[0.0001, 0], [0, 0.0001]])
5460
solpos = IRRAD_FORMULAS['f_solpos'](times, lat, lon, __covariance__=cov)
1.45 MB
Binary file not shown.
762 KB
Binary file not shown.

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ uncertaintywrapper>=0.4.1
1313
django
1414
mock
1515
h5py
16+
uncertainties

0 commit comments

Comments
 (0)