Skip to content

Commit d997923

Browse files
committed
Merge branch 'JrtPec-solar-irradiance' into develop
2 parents 9c9ae52 + 9aa6334 commit d997923

File tree

2 files changed

+256
-1
lines changed

2 files changed

+256
-1
lines changed

opengrid/library/solarmodel.py

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
__author__ = 'Jan Pecinovsky'
2+
3+
import geocoder
4+
import astral
5+
import math
6+
import pandas as pd
7+
8+
class SolarInsolation(object):
9+
"""
10+
Module to calculate Solar Insolation (direct intensity, global intensity, air mass) and
11+
basic solar parameters (angle) based on a location and a date.
12+
Formulas from pveducation.org
13+
"""
14+
def __init__(self, location):
15+
"""
16+
Parameters
17+
----------
18+
location: String
19+
"""
20+
self.location = geocoder.google(location)
21+
self.elevation = geocoder.google(self.location.latlng, method='Elevation').elevation
22+
self.astral = astral.Astral()
23+
24+
def _airMass(self, angleFromVertical):
25+
"""
26+
Raw formula to calculate air mass
27+
28+
Parameters
29+
----------
30+
angleFromVertical: float
31+
in radians
32+
33+
Returns
34+
-------
35+
float
36+
"""
37+
denom = math.cos(angleFromVertical) + 0.50572 * (96.07995 - angleFromVertical) ** -1.6364
38+
if denom >= 0:
39+
return 1 / denom
40+
else:
41+
return -1
42+
43+
def airMass(self, datetime):
44+
"""
45+
Calculate air mass for a given date and time
46+
47+
Parameters
48+
----------
49+
datetime: datetime.datetime
50+
51+
Returns
52+
-------
53+
float
54+
"""
55+
angleFromVertical = (math.pi/2) - self.solarElevation(datetime)
56+
return self._airMass(angleFromVertical)
57+
58+
def _directIntensity(self, elevation, airMass):
59+
"""
60+
Raw Formula to calculate direct solar beam intensity
61+
62+
Parameters
63+
---------
64+
elevation: float
65+
in meters
66+
airMass: float
67+
68+
Returns
69+
-------
70+
float
71+
in W/m**2
72+
"""
73+
elevation = elevation / 1000 #formula uses km
74+
di = 1.353 * ((1 - 0.14*elevation) * 0.7**airMass**0.678 + 0.14*elevation)
75+
return di*1000 #formula output is kW/m**2
76+
77+
def directIntensity(self, datetime):
78+
"""
79+
Calculate direct solar beam intensity for a given date and time
80+
81+
Parameters
82+
----------
83+
datetime: datetime.datetime
84+
85+
Returns
86+
-------
87+
float
88+
in W/m**2
89+
"""
90+
airMass = self.airMass(datetime)
91+
if airMass == -1:
92+
return 0
93+
return self._directIntensity(self.elevation, airMass)
94+
95+
def _backgroundIrradiance(self, directIntensity):
96+
"""
97+
Calculate the background irradiance, which is 10% of the direct intensity
98+
:param directIntensity: float
99+
:return: float
100+
"""
101+
102+
return 0.1 * directIntensity
103+
104+
def _globalIrradiance(self, directIntensity):
105+
"""
106+
Raw formula to calculate global solar irradiance
107+
108+
Parameters
109+
----------
110+
directIntensity: float
111+
in W/m**2
112+
113+
Returns
114+
-------
115+
float
116+
in W/m**2
117+
"""
118+
return directIntensity + self._backgroundIrradiance(directIntensity)
119+
120+
def globalIrradiance(self, datetime):
121+
"""
122+
Calculate global solar irradiance for a given date and time
123+
124+
Parameters
125+
----------
126+
datetime: datetime.datetime
127+
128+
Returns
129+
-------
130+
float
131+
in W/m**2
132+
"""
133+
directIntensity = self.directIntensity(datetime)
134+
return self._globalIrradiance(directIntensity)
135+
136+
def solarElevation(self, datetime):
137+
"""
138+
Calculate angle of the sun above the horizon
139+
140+
Parameters
141+
----------
142+
datetime: datetime.datetime
143+
144+
Returns
145+
-------
146+
float
147+
in radians
148+
"""
149+
deg = self.astral.solar_elevation(dateandtime = datetime,
150+
latitude = self.location.lat,
151+
longitude = self.location.lng)
152+
return math.radians(deg)
153+
154+
def df(self, start, end):
155+
"""
156+
Creates a dataframe with the insolation in W/m**2 in hourly resolution
157+
158+
Parameters
159+
----------
160+
start, end: datetime.datetime
161+
162+
Returns
163+
-------
164+
Pandas Dataframe
165+
"""
166+
167+
hours = pd.date_range(start=start, end=end, freq='h')
168+
gis = []
169+
for hour in hours:
170+
gis.append(self.globalIrradiance(hour))
171+
172+
df = pd.DataFrame(gis, index=hours, columns = ['insolation'])
173+
return df.tz_localize('UTC')
174+
175+
def solarAzimuth(self, datetime):
176+
"""
177+
Calculate the azimuth of the sun
178+
179+
:param datetime: datetime.datetime
180+
:return: float
181+
in radians
182+
"""
183+
184+
deg = self.astral.solar_azimuth(dateandtime=datetime,
185+
latitude=self.location.lat,
186+
longitude=self.location.lng)
187+
return math.radians(deg)
188+
189+
class PVModel(SolarInsolation):
190+
"""
191+
Module that models a theoretically perfect PV installation,
192+
extending the Solar Insolation Model, but adding PV orientation and tilt.
193+
"""
194+
195+
def __init__(self, location, orient=180, tilt=35):
196+
"""
197+
Parameters
198+
----------
199+
location: String
200+
orient: number (optional, default=180 (south))
201+
degrees (0-360)
202+
tilt: number (optional, default=35)
203+
degrees
204+
"""
205+
206+
super(PVModel, self).__init__(location = location)
207+
self.orient = math.radians(orient)
208+
self.tilt = math.radians(tilt)
209+
210+
def directIntensity(self, datetime):
211+
"""
212+
Calculate the direct solar beam intensity for a given date and time,
213+
but compensate for the PV orientation and tilt
214+
215+
Parameters
216+
----------
217+
datetime: datetime.datetime
218+
219+
Returns
220+
-------
221+
float
222+
in W/m**2
223+
"""
224+
di = super(PVModel, self).directIntensity(datetime)
225+
a = self.solarElevation(datetime)
226+
b = self.tilt
227+
c = self.orient
228+
d = self.solarAzimuth(datetime)
229+
230+
PVdi = di * (math.cos(a)*math.sin(b)*math.cos(c-d) + math.sin(a)*math.cos(b))
231+
232+
#PVdi cannot be negative
233+
return max(0, PVdi)
234+
235+
def globalIrradiance(self, datetime):
236+
"""
237+
Calculate global solar irradiance for a given date and time
238+
needed to override, because background irradiance is not influenced by PV orientation and tilt
239+
240+
Parameters
241+
----------
242+
datetime: datetime.datetime
243+
244+
Returns
245+
-------
246+
float
247+
in W/m**2
248+
"""
249+
#calculate the direct intensity without influence of tilt and orientation
250+
di = super(PVModel, self).directIntensity(datetime)
251+
252+
#add the tilted and oriented direct intensity to the background irradiance
253+
return self.directIntensity(datetime) + self._backgroundIrradiance(di)

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ python-coveralls
1616
coverage
1717
pint
1818
beautifulsoup4
19-
iso8601
19+
iso8601
20+
geocoder
21+
astral

0 commit comments

Comments
 (0)