|
| 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() |
0 commit comments