Skip to content

Commit 2ecda80

Browse files
committed
add simplified solis option to location.get_clearsky. reorder simplified solis output to match ineichen
1 parent 6c472c2 commit 2ecda80

File tree

5 files changed

+185
-63
lines changed

5 files changed

+185
-63
lines changed

pvlib/clearsky.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,10 @@ def simplified_solis(apparent_elevation, aod700=0.1, precipitable_water=1.,
368368
Returns
369369
--------
370370
clearsky : pd.DataFrame or np.array (determined by ``return_raw``)
371-
DataFrame contains the columns ``ghi, dni, dhi`` with the units
372-
of the ``dni_extra`` input. If ``return_raw=True``, returns the
373-
array [dni, ghi, dhi] with shape determined by the input arrays.
371+
DataFrame contains the columns ``'dhi', 'dni', 'ghi'`` with the
372+
units of the ``dni_extra`` input. If ``return_raw=True``,
373+
returns the array [dhi, dni, ghi] with shape determined by the
374+
input arrays.
374375
375376
References
376377
----------
@@ -415,7 +416,7 @@ def simplified_solis(apparent_elevation, aod700=0.1, precipitable_water=1.,
415416
ghi = i0p * np.exp(-taug/sin_elev**g) * sin_elev
416417
dhi = i0p * np.exp(-taud/sin_elev**d)
417418

418-
irrads = np.array([dni, ghi, dhi])
419+
irrads = np.array([dhi, dni, ghi])
419420

420421
if not return_raw:
421422
if isinstance(dni, pd.Series):
@@ -424,13 +425,13 @@ def simplified_solis(apparent_elevation, aod700=0.1, precipitable_water=1.,
424425
index = None
425426

426427
try:
427-
irrads = pd.DataFrame(irrads.T, columns=['dni', 'ghi', 'dhi'],
428+
irrads = pd.DataFrame(irrads.T, columns=['dhi', 'dni', 'ghi'],
428429
index=index)
429430
except ValueError:
430431
# probably all scalar input, so we
431432
# need to increase the dimensionality
432433
irrads = pd.DataFrame(np.array([irrads]),
433-
columns=['dni', 'ghi', 'dhi'])
434+
columns=['dhi', 'dni', 'ghi'])
434435
finally:
435436
irrads = irrads.fillna(0)
436437

pvlib/location.py

Lines changed: 79 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9,62 +9,60 @@
99
import pandas as pd
1010
import pytz
1111

12-
from pvlib import solarposition
13-
from pvlib import clearsky
14-
from pvlib import atmosphere
12+
from pvlib import solarposition, clearsky, atmosphere, irradiance
1513

1614

1715
class Location(object):
1816
"""
1917
Location objects are convenient containers for latitude, longitude,
20-
timezone, and altitude data associated with a particular
18+
timezone, and altitude data associated with a particular
2119
geographic location. You can also assign a name to a location object.
22-
23-
Location objects have two timezone attributes:
24-
20+
21+
Location objects have two timezone attributes:
22+
2523
* ``tz`` is a IANA timezone string.
2624
* ``pytz`` is a pytz timezone object.
27-
25+
2826
Location objects support the print method.
29-
27+
3028
Parameters
3129
----------
3230
latitude : float.
3331
Positive is north of the equator.
3432
Use decimal degrees notation.
35-
36-
longitude : float.
33+
34+
longitude : float.
3735
Positive is east of the prime meridian.
3836
Use decimal degrees notation.
39-
40-
tz : str, int, float, or pytz.timezone.
41-
See
37+
38+
tz : str, int, float, or pytz.timezone.
39+
See
4240
http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
4341
for a list of valid time zones.
4442
pytz.timezone objects will be converted to strings.
4543
ints and floats must be in hours from UTC.
46-
47-
alitude : float.
44+
45+
alitude : float.
4846
Altitude from sea level in meters.
49-
50-
name : None or string.
47+
48+
name : None or string.
5149
Sets the name attribute of the Location object.
52-
50+
5351
**kwargs
5452
Arbitrary keyword arguments.
5553
Included for compatibility, but not used.
56-
54+
5755
See also
5856
--------
5957
pvsystem.PVSystem
6058
"""
61-
59+
6260
def __init__(self, latitude, longitude, tz='UTC', altitude=0,
6361
name=None, **kwargs):
64-
62+
6563
self.latitude = latitude
6664
self.longitude = longitude
67-
65+
6866
if isinstance(tz, str):
6967
self.tz = tz
7068
self.pytz = pytz.timezone(tz)
@@ -76,65 +74,65 @@ def __init__(self, latitude, longitude, tz='UTC', altitude=0,
7674
self.pytz = pytz.FixedOffset(tz*60)
7775
else:
7876
raise TypeError('Invalid tz specification')
79-
77+
8078
self.altitude = altitude
81-
79+
8280
self.name = name
83-
81+
8482
# needed for tying together Location and PVSystem in LocalizedPVSystem
8583
# if LocalizedPVSystem signature is reversed
8684
# super(Location, self).__init__(**kwargs)
87-
88-
89-
85+
86+
87+
9088
def __str__(self):
9189
return ('{}: latitude={}, longitude={}, tz={}, altitude={}'
92-
.format(self.name, self.latitude, self.longitude,
90+
.format(self.name, self.latitude, self.longitude,
9391
self.tz, self.altitude))
94-
95-
92+
93+
9694
@classmethod
9795
def from_tmy(cls, tmy_metadata, tmy_data=None, **kwargs):
9896
"""
99-
Create an object based on a metadata
97+
Create an object based on a metadata
10098
dictionary from tmy2 or tmy3 data readers.
101-
99+
102100
Parameters
103101
----------
104102
tmy_metadata : dict
105103
Returned from tmy.readtmy2 or tmy.readtmy3
106104
tmy_data : None or DataFrame
107105
Optionally attach the TMY data to this object.
108-
106+
109107
Returns
110108
-------
111109
Location object (or the child class of Location that you
112110
called this method from).
113111
"""
114112
# not complete, but hopefully you get the idea.
115113
# might need code to handle the difference between tmy2 and tmy3
116-
114+
117115
# determine if we're dealing with TMY2 or TMY3 data
118116
tmy2 = tmy_metadata.get('City', False)
119-
117+
120118
latitude = tmy_metadata['latitude']
121119
longitude = tmy_metadata['longitude']
122-
120+
123121
if tmy2:
124122
name = tmy_metadata['City']
125-
else:
123+
else:
126124
name = tmy_metadata['Name']
127-
125+
128126
tz = tmy_metadata['TZ']
129127
altitude = tmy_metadata['altitude']
130128

131129
new_object = cls(latitude, longitude, tz=tz, altitude=altitude,
132130
name=name, **kwargs)
133-
131+
134132
# not sure if this should be assigned regardless of input.
135133
if tmy_data is not None:
136134
new_object.tmy_data = tmy_data
137-
135+
138136
return new_object
139137

140138

@@ -143,7 +141,7 @@ def get_solarposition(self, times, pressure=None, temperature=12,
143141
"""
144142
Uses the :py:func:`solarposition.get_solarposition` function
145143
to calculate the solar zenith, azimuth, etc. at this location.
146-
144+
147145
Parameters
148146
----------
149147
times : DatetimeIndex
@@ -153,7 +151,7 @@ def get_solarposition(self, times, pressure=None, temperature=12,
153151
temperature : None, float, or array-like
154152
155153
kwargs passed to :py:func:`solarposition.get_solarposition`
156-
154+
157155
Returns
158156
-------
159157
solar_position : DataFrame
@@ -175,22 +173,24 @@ def get_clearsky(self, times, model='ineichen', **kwargs):
175173
"""
176174
Calculate the clear sky estimates of GHI, DNI, and/or DHI
177175
at this location.
178-
176+
179177
Parameters
180178
----------
181179
times : DatetimeIndex
182-
180+
183181
model : str
184-
The clear sky model to use.
185-
186-
kwargs passed to the relevant function(s).
187-
182+
The clear sky model to use. Must be one of
183+
'ineichen', 'haurwitz', 'simplified_solis'.
184+
185+
kwargs passed to the relevant functions. Climatological values
186+
are assumed in many cases. See code for details.
187+
188188
Returns
189189
-------
190190
clearsky : DataFrame
191191
Column names are: ``ghi, dni, dhi``.
192192
"""
193-
193+
194194
if model == 'ineichen':
195195
cs = clearsky.ineichen(times, latitude=self.latitude,
196196
longitude=self.longitude,
@@ -199,18 +199,42 @@ def get_clearsky(self, times, model='ineichen', **kwargs):
199199
elif model == 'haurwitz':
200200
solpos = self.get_solarposition(times, **kwargs)
201201
cs = clearsky.haurwitz(solpos['apparent_zenith'])
202+
elif model == 'simplified_solis':
203+
204+
# these try/excepts define default values that are only
205+
# evaluated if necessary. ineichen does some of this internally
206+
try:
207+
dni_extra = kwargs.pop('dni_extra')
208+
except KeyError:
209+
dni_extra = irradiance.extraradiation(times.dayofyear)
210+
211+
try:
212+
pressure = kwargs.pop('pressure')
213+
except KeyError:
214+
pressure = atmosphere.alt2pres(self.altitude)
215+
216+
try:
217+
apparent_elevation = kwargs.pop('apparent_elevation')
218+
except KeyError:
219+
solpos = self.get_solarposition(
220+
times, pressure=pressure, **kwargs)
221+
apparent_elevation = solpos['apparent_elevation']
222+
223+
cs = clearsky.simplified_solis(
224+
apparent_elevation, pressure=pressure, dni_extra=dni_extra,
225+
**kwargs)
202226
else:
203227
raise ValueError('{} is not a valid clear sky model'
204228
.format(model))
205229

206230
return cs
207-
208-
231+
232+
209233
def get_airmass(self, times=None, solar_position=None,
210234
model='kastenyoung1989'):
211235
"""
212236
Calculate the relative and absolute airmass.
213-
237+
214238
Automatically chooses zenith or apparant zenith
215239
depending on the selected model.
216240
@@ -222,7 +246,7 @@ def get_airmass(self, times=None, solar_position=None,
222246
DataFrame with with columns 'apparent_zenith', 'zenith'.
223247
model : str
224248
Relative airmass model
225-
249+
226250
Returns
227251
-------
228252
airmass : DataFrame
@@ -250,4 +274,3 @@ def get_airmass(self, times=None, solar_position=None,
250274
airmass['airmass_absolute'] = airmass_absolute
251275

252276
return airmass
253-

pvlib/solarposition.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ def _spa_python_import(how):
239239

240240
def spa_python(time, latitude, longitude,
241241
altitude=0, pressure=101325, temperature=12, delta_t=None,
242-
atmos_refract=None, how='numpy', numthreads=4):
242+
atmos_refract=None, how='numpy', numthreads=4, **kwargs):
243243
"""
244244
Calculate the solar position using a python implementation of the
245245
NREL SPA algorithm described in [1].

pvlib/test/test_clearsky.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ def test_simplified_solis_series_elevation():
163163
[ 0. , 0. , 0. ]]),
164164
columns=['dni', 'ghi', 'dhi'],
165165
index=times_localized)
166+
expected = expected[['dhi', 'dni', 'ghi']]
166167

167168
out = clearsky.simplified_solis(ephem_data['apparent_elevation'])
168169
assert_frame_equal(expected, out)
@@ -171,6 +172,7 @@ def test_simplified_solis_series_elevation():
171172
def test_simplified_solis_scalar_elevation():
172173
expected = pd.DataFrame(np.array([[959.335463, 1064.653145, 129.125602]]),
173174
columns=['dni', 'ghi', 'dhi'])
175+
expected = expected[['dhi', 'dni', 'ghi']]
174176

175177
out = clearsky.simplified_solis(80)
176178
assert_frame_equal(expected, out)
@@ -179,6 +181,7 @@ def test_simplified_solis_scalar_elevation():
179181
def test_simplified_solis_array_elevation():
180182
expected = pd.DataFrame(np.array([[959.335463, 1064.653145, 129.125602]]),
181183
columns=['dni', 'ghi', 'dhi'])
184+
expected = expected[['dhi', 'dni', 'ghi']]
182185

183186
out = clearsky.simplified_solis(np.array([80]))
184187
assert_frame_equal(expected, out)
@@ -187,6 +190,7 @@ def test_simplified_solis_array_elevation():
187190
def test_simplified_solis_dni_extra():
188191
expected = pd.DataFrame(np.array([[963.555414, 1069.33637, 129.693603]]),
189192
columns=['dni', 'ghi', 'dhi'])
193+
expected = expected[['dhi', 'dni', 'ghi']]
190194

191195
out = clearsky.simplified_solis(80, dni_extra=1370)
192196
assert_frame_equal(expected, out)
@@ -198,6 +202,7 @@ def test_simplified_solis_pressure():
198202
[ 961.88811874, 1066.36847963, 128.1402539 ],
199203
[ 959.58112234, 1064.81837558, 129.0304193 ]]),
200204
columns=['dni', 'ghi', 'dhi'])
205+
expected = expected[['dhi', 'dni', 'ghi']]
201206

202207
out = clearsky.simplified_solis(
203208
80, pressure=np.array([95000, 98000, 101000]))
@@ -212,6 +217,7 @@ def test_simplified_solis_aod700():
212217
[ 342.45810926, 638.63409683, 77.71786575],
213218
[ 55.24140911, 7.5413313 , 0. ]]),
214219
columns=['dni', 'ghi', 'dhi'])
220+
expected = expected[['dhi', 'dni', 'ghi']]
215221

216222
aod700 = np.array([0.0, 0.05, 0.1, 1, 10])
217223
out = clearsky.simplified_solis(80, aod700=aod700)
@@ -226,6 +232,7 @@ def test_simplified_solis_precipitable_water():
226232
[ 959.3354628 , 1064.65314509, 129.12560167],
227233
[ 872.02335029, 974.18046717, 125.63581346]]),
228234
columns=['dni', 'ghi', 'dhi'])
235+
expected = expected[['dhi', 'dni', 'ghi']]
229236

230237
out = clearsky.simplified_solis(
231238
80, precipitable_water=pd.Series([0.0, 0.2, 0.5, 1.0, 5.0]))
@@ -236,6 +243,7 @@ def test_simplified_solis_small_scalar_pw():
236243
expected = pd.DataFrame(np.
237244
array([[ 1001.15353307, 1107.84678941, 128.58887606]]),
238245
columns=['dni', 'ghi', 'dhi'])
246+
expected = expected[['dhi', 'dni', 'ghi']]
239247

240248
out = clearsky.simplified_solis(80, precipitable_water=0.1)
241249
assert_frame_equal(expected, out)

0 commit comments

Comments
 (0)