Skip to content

Commit 457615d

Browse files
committed
add Bird clearsky model
* move clearsky.py into clearsky/core.py just for fun, let's try it out * fix path to data in clearsky to LinkeTurbidities.mat * add BIRD workbook to data for testing Signed-off-by: Mark Mikofski <[email protected]>
1 parent 9d192ba commit 457615d

File tree

4 files changed

+195
-1
lines changed

4 files changed

+195
-1
lines changed

pvlib/clearsky/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Clearsky package"""
2+
3+
from pvlib.clearsky.core import (
4+
haurwitz, ineichen, lookup_linke_turbidity, simplified_solis
5+
)
6+
from pvlib.clearsky.nrel import bird

pvlib/clearsky/core.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ def lookup_linke_turbidity(time, latitude, longitude, filepath=None,
201201
'supply your own turbidities.')
202202

203203
if filepath is None:
204-
pvlib_path = os.path.dirname(os.path.abspath(__file__))
204+
clearsky_path = os.path.dirname(os.path.abspath(__file__))
205+
pvlib_path = os.path.dirname(clearsky_path)
205206
filepath = os.path.join(pvlib_path, 'data', 'LinkeTurbidities.mat')
206207

207208
mat = scipy.io.loadmat(filepath)

pvlib/clearsky/nrel.py

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
"""
2+
Richard E. Bird
3+
Clear Sky Broadband
4+
Solar Radiation Model
5+
From the publication "A Simplified Clear Sky model for Direct and Diffuse
6+
Insolation on Horizontal Surfaces" by R.E. Bird and R.L Hulstrom, SERI Technical
7+
Report SERI/TR-642-761, Feb 1991. Solar Energy Research Institute, Golden, CO.
8+
9+
The model is based on comparisons with results from rigourous radiative transfer
10+
codes. It is composed of simple algebraic expressions with 10 User defined
11+
inputs (green cells to left). Model results should be expected to agree within
12+
+/-10% with rigourous radiative transfer codes. The model computes solar
13+
radiation for every hour of the year, based on the 10 user input parameters.
14+
15+
The graphical presentation includes diurnal clear sky radiation patterns for
16+
every day of the year. The user may copy cells of interest or the graph and
17+
paste it to an unprotected worksheet for manipulation.
18+
19+
The workbook is PROTECTED using the password BIRD (all caps). To generate data
20+
for the entire year, choose TOOLS, PROTECTION, UNPROTECT and enter the password.
21+
Copy row 49 and paste it from row 50 all the way down to row 8761.
22+
23+
NOTE: Columns L to U contain intermediate calculations and have been collapsed
24+
down for convenient pressentation of model results.
25+
26+
Contact:
27+
Daryl R. Myers,
28+
National Renewable Energy Laboratory,
29+
1617 Cole Blvd. MS 3411, Golden CO 80401
30+
(303)-384-6768 e-mail [email protected]
31+
32+
http://rredc.nrel.gov/solar/models/clearsky/
33+
http://rredc.nrel.gov/solar/pubs/pdfs/tr-642-761.pdf
34+
http://rredc.nrel.gov/solar/models/clearsky/error_reports.html
35+
"""
36+
37+
import numpy as np
38+
import pandas as pd
39+
import seaborn as sns
40+
import xlrd
41+
import os
42+
43+
44+
def bird(doy, hr, lat, lon, tz, press_mB, o3_cm, h2o_cm, aod_500nm, aod_380nm,
45+
b_a, alb, lyear=365.0, solar_constant=1367.0):
46+
"""
47+
Bird Simple Clearsky Model
48+
49+
:param doy: day(s) of the year
50+
:type doy: int
51+
:param hr: hour
52+
:param lat: latitude [degrees]
53+
:param lon: longitude [degrees]
54+
:param tz: time zone
55+
:param press_mB: pressure [mBar]
56+
:param o3_cm: atmospheric ozone [cm]
57+
:param h2o_cm: precipital water [cm]
58+
:param aod_500nm: aerosol optical depth [cm] measured at 500[nm]
59+
:param aod_380nm: aerosol optical depth [cm] measured at 380[nm]
60+
:param b_a: asymmetry factor
61+
:param alb: albedo
62+
"""
63+
doy0 = doy - 1.0
64+
patm = 1013.0
65+
day_angle = 6.283185 * doy0 / lyear
66+
# rad2deg = 180.0 / np.pi
67+
dec_rad = (
68+
0.006918 - 0.399912 * np.cos(day_angle) + 0.070257 * np.sin(day_angle) -
69+
0.006758 * np.cos(2.0 * day_angle) +
70+
0.000907 * np.sin(2.0 * day_angle) -
71+
0.002697 * np.cos(3.0 * day_angle) + 0.00148 * np.sin(3.0 * day_angle)
72+
)
73+
declination = np.rad2deg(dec_rad)
74+
# equation of time
75+
eqt = 229.18 * (
76+
0.0000075 + 0.001868 * np.cos(day_angle) -
77+
0.032077 * np.sin(day_angle) - 0.014615 * np.cos(2.0 * day_angle) -
78+
0.040849 * np.sin(2.0 * day_angle)
79+
)
80+
hour_angle = 15.0 * (hr - 12.5) + lon - tz * 15.0 + eqt / 4.0
81+
lat_rad = np.deg2rad(lat)
82+
ze_rad = np.arccos(
83+
np.cos(dec_rad) * np.cos(lat_rad) * np.cos(np.deg2rad(hour_angle)) +
84+
np.sin(dec_rad) * np.sin(lat_rad)
85+
)
86+
zenith = np.rad2deg(ze_rad)
87+
airmass = np.where(zenith < 89,
88+
1.0 / (np.cos(ze_rad) + 0.15 / (93.885 - zenith) ** 1.25), 0.0
89+
)
90+
pstar = press_mB / patm
91+
am_press = pstar*airmass
92+
t_rayliegh = np.where(airmass > 0,
93+
np.exp(-0.0903 * am_press ** 0.84 * (
94+
1.0 + am_press - am_press ** 1.01
95+
)), 0.0
96+
)
97+
am_o3 = o3_cm*airmass
98+
t_ozone = np.where(airmass > 0,
99+
1.0 - 0.1611 * am_o3 * (1.0 + 139.48 * am_o3) ** -0.3034 -
100+
0.002715 * am_o3 / (1.0 + 0.044 * am_o3 + 0.0003 * am_o3 ** 2.0), 0.0)
101+
t_gases = np.where(airmass > 0, np.exp(-0.0127 * am_press ** 0.26), 0.0)
102+
am_h2o = airmass * h2o_cm
103+
t_water = np.where(airmass > 0,
104+
1.0 - 2.4959 * am_h2o / (
105+
(1.0 + 79.034 * am_h2o) ** 0.6828 + 6.385 * am_h2o
106+
), 0.0
107+
)
108+
bird_huldstrom = 0.2758 * aod_380nm + 0.35 * aod_500nm
109+
t_aerosol = np.where(airmass > 0, np.exp(
110+
-(bird_huldstrom ** 0.873) *
111+
(1.0 + bird_huldstrom - bird_huldstrom ** 0.7088) * airmass ** 0.9108
112+
), 0.0)
113+
taa = np.where(airmass > 0,
114+
1.0 - 0.1 * (1.0 - airmass + airmass ** 1.06) * (1.0 - t_aerosol), 0.0
115+
)
116+
rs = np.where(airmass > 0,
117+
0.0685 + (1.0 - b_a) * (1.0 - t_aerosol / taa), 0.0
118+
)
119+
etr_ = etr(doy, lyear, solar_constant)
120+
id_ = np.where(airmass > 0,
121+
0.9662 * etr_ * t_aerosol * t_water * t_gases * t_ozone * t_rayliegh,
122+
0.0
123+
)
124+
id_nh = np.where(zenith < 90, id_ * np.cos(ze_rad), 0.0)
125+
ias = np.where(airmass > 0,
126+
etr_ * np.cos(ze_rad) * 0.79 * t_ozone * t_gases * t_water * taa *
127+
(0.5 * (1.0 - t_rayliegh) + b_a * (1.0 - (t_aerosol / taa))) / (
128+
1.0 - airmass + airmass ** 1.02
129+
), 0.0
130+
)
131+
gh = np.where(airmass > 0, (id_nh + ias) / (1.0 - alb * rs), 0.0)
132+
testvalues = (day_angle, declination, eqt, hour_angle, zenith, airmass)
133+
return id_, id_nh, gh, gh - id_nh, testvalues
134+
135+
136+
def etr(doy, lyear=365.0, solar_constant=1367.0):
137+
a0 = 1.00011
138+
doy0 = doy - 1.0
139+
a1 = 0.034221 * np.cos(2.0 * np.pi * doy0 / lyear)
140+
a2 = 0.00128 * np.sin(2.0 * np.pi * doy0 / lyear)
141+
a3 = 0.000719 * np.cos(2.0 * (2.0 * np.pi * doy0 / lyear))
142+
a4 = 0.000077 * np.sin(2.0 * (2.0 * np.pi * doy0 / lyear))
143+
return solar_constant * (a0 + a1 + a2 + a3 + a4)
144+
145+
146+
def test_bird():
147+
dt = pd.DatetimeIndex(start='1/1/2015 0:00', end='12/31/2015 23:00', freq='H')
148+
kwargs = {
149+
'lat': 40, 'lon': -105, 'tz': -7,
150+
'press_mB': 840,
151+
'o3_cm': 0.3, 'h2o_cm': 1.5,
152+
'aod_500nm': 0.1, 'aod_380nm': 0.15,
153+
'b_a': 0.85,
154+
'alb': 0.2
155+
}
156+
Eb, Ebh, Gh, Dh, tv = bird(dt.dayofyear, np.array(range(24)*365), **kwargs)
157+
day_angle, declination, eqt, hour_angle, zenith, airmass = tv
158+
clearsky_path = os.path.dirname(os.path.abspath(__file__))
159+
pvlib_path = os.path.dirname(clearsky_path)
160+
wb = xlrd.open_workbook(
161+
os.path.join(pvlib_path, 'data', 'BIRD_08_16_2012.xls')
162+
)
163+
sheet = wb.sheets()[0]
164+
headers = [h.value for h in sheet.row(1)][4:]
165+
testdata = pd.DataFrame({h: [c.value for c in sheet.col(n + 4, 2, 49)]
166+
for n, h in enumerate(headers)},
167+
index=dt[1:48])
168+
assert np.allclose(testdata['Dangle'], day_angle[1:48])
169+
assert np.allclose(testdata['DEC'], declination[1:48])
170+
assert np.allclose(testdata['EQT'], eqt[1:48])
171+
assert np.allclose(testdata['Hour Angle'], hour_angle[1:48])
172+
assert np.allclose(testdata['Zenith Ang'], zenith[1:48])
173+
assert np.allclose(testdata['Air Mass'], airmass[1:48])
174+
assert np.allclose(testdata['Direct Beam'], Eb[1:48])
175+
assert np.allclose(testdata['Direct Hz'], Ebh[1:48])
176+
assert np.allclose(testdata['Global Hz'], Gh[1:48])
177+
assert np.allclose(testdata['Dif Hz'], Dh[1:48])
178+
return pd.DataFrame({'Eb': Eb, 'Ebh': Ebh, 'Gh': Gh, 'Dh': Dh}, index=dt)
179+
180+
181+
if __name__ == "__main__":
182+
sns.set_context()
183+
irrad = test_bird()
184+
f = irrad.iloc[:48].plot()
185+
f.set_title('Bird Clear Sky Model Results')
186+
f.set_ylabel('irradiance $[W/m^2]$')
187+
f.figure.show()

pvlib/data/BIRD_08_16_2012.xls

1.23 MB
Binary file not shown.

0 commit comments

Comments
 (0)