99import pandas as pd
1010import 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
1715class 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-
0 commit comments