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,63 @@ 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+
9086 def __repr__ (self ):
9187 return ('{}: latitude={}, longitude={}, tz={}, altitude={}'
92- .format (self .name , self .latitude , self .longitude ,
88+ .format (self .name , self .latitude , self .longitude ,
9389 self .tz , self .altitude ))
94-
95-
90+
91+
9692 @classmethod
9793 def from_tmy (cls , tmy_metadata , tmy_data = None , ** kwargs ):
9894 """
99- Create an object based on a metadata
95+ Create an object based on a metadata
10096 dictionary from tmy2 or tmy3 data readers.
101-
97+
10298 Parameters
10399 ----------
104100 tmy_metadata : dict
105101 Returned from tmy.readtmy2 or tmy.readtmy3
106102 tmy_data : None or DataFrame
107103 Optionally attach the TMY data to this object.
108-
104+
109105 Returns
110106 -------
111107 Location object (or the child class of Location that you
112108 called this method from).
113109 """
114110 # not complete, but hopefully you get the idea.
115111 # might need code to handle the difference between tmy2 and tmy3
116-
112+
117113 # determine if we're dealing with TMY2 or TMY3 data
118114 tmy2 = tmy_metadata .get ('City' , False )
119-
115+
120116 latitude = tmy_metadata ['latitude' ]
121117 longitude = tmy_metadata ['longitude' ]
122-
118+
123119 if tmy2 :
124120 name = tmy_metadata ['City' ]
125- else :
121+ else :
126122 name = tmy_metadata ['Name' ]
127-
123+
128124 tz = tmy_metadata ['TZ' ]
129125 altitude = tmy_metadata ['altitude' ]
130126
131127 new_object = cls (latitude , longitude , tz = tz , altitude = altitude ,
132128 name = name , ** kwargs )
133-
129+
134130 # not sure if this should be assigned regardless of input.
135131 if tmy_data is not None :
136132 new_object .tmy_data = tmy_data
137-
133+
138134 return new_object
139135
140136
@@ -143,7 +139,7 @@ def get_solarposition(self, times, pressure=None, temperature=12,
143139 """
144140 Uses the :py:func:`solarposition.get_solarposition` function
145141 to calculate the solar zenith, azimuth, etc. at this location.
146-
142+
147143 Parameters
148144 ----------
149145 times : DatetimeIndex
@@ -153,7 +149,7 @@ def get_solarposition(self, times, pressure=None, temperature=12,
153149 temperature : None, float, or array-like
154150
155151 kwargs passed to :py:func:`solarposition.get_solarposition`
156-
152+
157153 Returns
158154 -------
159155 solar_position : DataFrame
@@ -175,22 +171,24 @@ def get_clearsky(self, times, model='ineichen', **kwargs):
175171 """
176172 Calculate the clear sky estimates of GHI, DNI, and/or DHI
177173 at this location.
178-
174+
179175 Parameters
180176 ----------
181177 times : DatetimeIndex
182-
178+
183179 model : str
184- The clear sky model to use.
185-
186- kwargs passed to the relevant function(s).
187-
180+ The clear sky model to use. Must be one of
181+ 'ineichen', 'haurwitz', 'simplified_solis'.
182+
183+ kwargs passed to the relevant functions. Climatological values
184+ are assumed in many cases. See code for details.
185+
188186 Returns
189187 -------
190188 clearsky : DataFrame
191189 Column names are: ``ghi, dni, dhi``.
192190 """
193-
191+
194192 if model == 'ineichen' :
195193 cs = clearsky .ineichen (times , latitude = self .latitude ,
196194 longitude = self .longitude ,
@@ -199,18 +197,42 @@ def get_clearsky(self, times, model='ineichen', **kwargs):
199197 elif model == 'haurwitz' :
200198 solpos = self .get_solarposition (times , ** kwargs )
201199 cs = clearsky .haurwitz (solpos ['apparent_zenith' ])
200+ elif model == 'simplified_solis' :
201+
202+ # these try/excepts define default values that are only
203+ # evaluated if necessary. ineichen does some of this internally
204+ try :
205+ dni_extra = kwargs .pop ('dni_extra' )
206+ except KeyError :
207+ dni_extra = irradiance .extraradiation (times .dayofyear )
208+
209+ try :
210+ pressure = kwargs .pop ('pressure' )
211+ except KeyError :
212+ pressure = atmosphere .alt2pres (self .altitude )
213+
214+ try :
215+ apparent_elevation = kwargs .pop ('apparent_elevation' )
216+ except KeyError :
217+ solpos = self .get_solarposition (
218+ times , pressure = pressure , ** kwargs )
219+ apparent_elevation = solpos ['apparent_elevation' ]
220+
221+ cs = clearsky .simplified_solis (
222+ apparent_elevation , pressure = pressure , dni_extra = dni_extra ,
223+ ** kwargs )
202224 else :
203225 raise ValueError ('{} is not a valid clear sky model'
204226 .format (model ))
205227
206228 return cs
207-
208-
229+
230+
209231 def get_airmass (self , times = None , solar_position = None ,
210232 model = 'kastenyoung1989' ):
211233 """
212234 Calculate the relative and absolute airmass.
213-
235+
214236 Automatically chooses zenith or apparant zenith
215237 depending on the selected model.
216238
@@ -222,7 +244,7 @@ def get_airmass(self, times=None, solar_position=None,
222244 DataFrame with with columns 'apparent_zenith', 'zenith'.
223245 model : str
224246 Relative airmass model
225-
247+
226248 Returns
227249 -------
228250 airmass : DataFrame
@@ -250,4 +272,3 @@ def get_airmass(self, times=None, solar_position=None,
250272 airmass ['airmass_absolute' ] = airmass_absolute
251273
252274 return airmass
253-
0 commit comments