-
Notifications
You must be signed in to change notification settings - Fork 1.1k
add linke turbidity formulas module to atmosphere #278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
6e9aaf0
4a2dde7
c492804
f0b5246
17f3b9f
33f6374
086b640
eda94b8
02d1fe5
a5221a4
645b2fb
ec3521c
1964e20
6c3f380
fd382bf
bbe2740
8f667c2
e64345e
74524c1
00166d0
59368c8
13bb4cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -292,7 +292,7 @@ def gueymard94_pw(temp_air, relative_humidity): | |
---------- | ||
.. [1] W. M. Keogh and A. W. Blakers, Accurate Measurement, Using Natural | ||
Sunlight, of Silicon Solar Cells, Prog. in Photovoltaics: Res. | ||
and Appl. 2004, vol 12, pp. 1-19 (DOI: 10.1002/pip.517) | ||
and Appl. 2004, vol 12, pp. 1-19 (:doi:`10.1002/pip.517`) | ||
|
||
.. [2] C. Gueymard, Analysis of Monthly Average Atmospheric Precipitable | ||
Water and Turbidity in Canada and Northern United States, | ||
|
@@ -467,3 +467,193 @@ def first_solar_spectral_correction(pw, airmass_absolute, module_type=None, | |
coeff[4]*np.sqrt(pw) + coeff[5]*ama/np.sqrt(pw)) | ||
|
||
return modifier | ||
|
||
|
||
def bird_hulstrom80_aod_bb(aod380, aod500): | ||
""" | ||
Approximate broadband aerosol optical depth. | ||
|
||
Bird and Hulstrom developed a correlation for broadband aerosol optical | ||
depth (AOD) using two wavelengths, 380 nm and 500 nm. | ||
|
||
Parameters | ||
---------- | ||
aod380 : numeric | ||
AOD measured at 380 nm | ||
aod500 : numeric | ||
AOD measured at 500 nm | ||
|
||
Returns | ||
------- | ||
aod_bb : numeric | ||
broadband AOD | ||
|
||
See also | ||
-------- | ||
kasten96_lt | ||
|
||
References | ||
---------- | ||
[1] Bird and Hulstrom, "Direct Insolation Models" (1980) | ||
`SERI/TR-335-344 <http://www.nrel.gov/docs/legosti/old/344.pdf>`_ | ||
|
||
[2] R. E. Bird and R. L. Hulstrom, "Review, Evaluation, and Improvement of | ||
Direct Irradiance Models", Journal of Solar Energy Engineering 103(3), | ||
pp. 182-192 (1981) | ||
:doi:`10.1115/1.3266239` | ||
""" | ||
# approximate broadband AOD using (Bird-Hulstrom 1980) | ||
return 0.27583 * aod380 + 0.35 * aod500 | ||
|
||
|
||
def kasten96_lt(airmass_absolute, precipitable_water, aod_bb): | ||
""" | ||
Calculate Linke turbidity factor using Kasten pyrheliometric formula. | ||
|
||
Note that broadband aerosol optical depth (AOD) can be approximated by AOD | ||
measured at 700 nm according to Molineaux [4] . Bird and Hulstrom offer an | ||
alternate approximation using AOD measured at 380 nm and 500 nm. | ||
|
||
Based on original implementation by Armel Oumbe. | ||
|
||
.. warning:: | ||
These calculations are only valid for air mass less than 5 atm and | ||
precipitable water less than 5 cm. | ||
|
||
Parameters | ||
---------- | ||
airmass_absolute : numeric | ||
airmass, pressure corrected in atmospheres | ||
precipitable_water : numeric | ||
precipitable water or total column water vapor in centimeters | ||
aod_bb : numeric | ||
broadband AOD | ||
|
||
Returns | ||
------- | ||
lt : numeric | ||
Linke turbidity | ||
|
||
See also | ||
-------- | ||
bird_hulstrom80_aod_bb | ||
angstrom_aod_at_lambda | ||
|
||
References | ||
---------- | ||
[1] F. Linke, "Transmissions-Koeffizient und Trubungsfaktor", Beitrage | ||
zur Physik der Atmosphare, Vol 10, pp. 91-103 (1922) | ||
|
||
[2] F. Kasten, "A simple parameterization of the pyrheliometric formula for | ||
determining the Linke turbidity factor", Meteorologische Rundschau 33, | ||
pp. 124-127 (1980) | ||
|
||
[3] Kasten, "The Linke turbidity factor based on improved values of the | ||
integral Rayleigh optical thickness", Solar Energy, Vol. 56, No. 3, | ||
pp. 239-244 (1996) | ||
:doi:`10.1016/0038-092X(95)00114-7` | ||
|
||
[4] B. Molineaux, P. Ineichen, N. O'Neill, "Equivalence of pyrheliometric | ||
and monochromatic aerosol optical depths at a single key wavelength", | ||
Applied Optics Vol. 37, issue 10, 7008-7018 (1998) | ||
:doi:`10.1364/AO.37.007008` | ||
|
||
[5] P. Ineichen, "Conversion function between the Linke turbidity and the | ||
atmospheric water vapor and aerosol content", Solar Energy 82, | ||
pp. 1095-1097 (2008) | ||
:doi:`10.1016/j.solener.2008.04.010` | ||
|
||
[6] P. Ineichen and R. Perez, "A new airmass independent formulation for | ||
the Linke Turbidity coefficient", Solar Energy, Vol. 73, no. 3, pp. 151-157 | ||
(2002) | ||
:doi:`10.1016/S0038-092X(02)00045-2` | ||
""" | ||
# "From numerically integrated spectral simulations done with Modtran | ||
# (Berk, 1989), Molineaux (1998) obtained for the broadband optical depth | ||
# of a clean and dry atmospshere (fictitious atmosphere that comprises only | ||
# the effects of Rayleigh scattering and absorption by the atmosphere gases | ||
# other than the water vapor) the following expression" | ||
# - P. Ineichen (2008) | ||
delta_cda = -0.101 + 0.235 * airmass_absolute ** (-0.16) | ||
# "and the broadband water vapor optical depth where pwat is the integrated | ||
# precipitable water vapor content of the atmosphere expressed in cm and am | ||
# the optical air mass. The precision of these fits is better than 1% when | ||
# compared with Modtran simulations in the range 1 < am < 5 and | ||
# 0 < pwat < 5 cm at sea level" - P. Ineichen (2008) | ||
delta_w = 0.112 * airmass_absolute ** (-0.55) * precipitable_water ** 0.34 | ||
# broadband AOD | ||
delta_a = aod_bb | ||
# "Then using the Kasten pyrheliometric formula (1980, 1996), the Linke | ||
# turbidity at am = 2 can be written. The extension of the Linke turbidity | ||
# coefficient to other values of air mass was published by Ineichen and | ||
# Perez (2002)" - P. Ineichen (2008) | ||
lt = -(9.4 + 0.9 * airmass_absolute) * np.log( | ||
np.exp(-airmass_absolute * (delta_cda + delta_w + delta_a)) | ||
) / airmass_absolute | ||
# filter out of extrapolated values | ||
return lt | ||
|
||
|
||
def angstrom_aod_at_lambda(aod0, lambda0, alpha, lambda1=700.0): | ||
r""" | ||
Get AOD at specified wavelength using Angstrom turbidity model. | ||
|
||
Parameters | ||
---------- | ||
aod0 : numeric | ||
aerosol optical depth (AOD) measured at known wavelength | ||
lambda0 : numeric | ||
wavelength in nanometers corresponding to ``aod0`` | ||
alpha : numeric | ||
Angstrom :math:`\alpha` exponent corresponding to ``aod0`` | ||
lambda1 : numeric | ||
desired wavelength in nanometers, defaults to 700 nm | ||
|
||
Returns | ||
------- | ||
aod1 : numeric | ||
AOD at desired wavelength, ``lambda1`` | ||
|
||
See also | ||
-------- | ||
angstrom_alpha | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need a Returns section There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ |
||
References | ||
---------- | ||
[1] Anders Angstrom, "On the Atmospheric Transmission of Sun Radiation and | ||
On Dust in the Air", Geografiska Annaler Vol. 11, pp. 156-166 (1929) JSTOR | ||
:doi:`10.2307/519399` | ||
|
||
[2] Anders Angstrom, "Techniques of Determining the Turbidity of the | ||
Atmosphere", Tellus 13:2, pp. 214-223 (1961) Taylor & Francis | ||
:doi:`10.3402/tellusa.v13i2.9493` and Co-Action Publishing | ||
:doi:`10.1111/j.2153-3490.1961.tb00078.x` | ||
""" | ||
return aod0 * ((lambda1 / lambda0) ** (-alpha)) | ||
|
||
|
||
def angstrom_alpha(aod1, lambda1, aod2, lambda2): | ||
r""" | ||
Calculate Angstrom alpha exponent. | ||
|
||
Parameters | ||
---------- | ||
aod1 : numeric | ||
first aerosol optical depth | ||
lambda1 : numeric | ||
wavelength in nanometers corresponding to ``aod1`` | ||
aod2 : numeric | ||
second aerosol optical depth | ||
lambda2 : numeric | ||
wavelength in nanometers corresponding to ``aod2`` | ||
|
||
Returns | ||
------- | ||
alpha : numeric | ||
Angstrom :math:`\alpha` exponent for AOD in ``(lambda1, lambda2)`` | ||
|
||
See also | ||
-------- | ||
angstrom_aod_at_lambda | ||
""" | ||
return - np.log(aod1 / aod2) / np.log(lambda1 / lambda2) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
import datetime | ||
import itertools | ||
|
||
import numpy as np | ||
|
@@ -7,10 +6,9 @@ | |
import pytest | ||
from numpy.testing import assert_allclose | ||
|
||
from pvlib.location import Location | ||
from pvlib import solarposition | ||
from pvlib import clearsky | ||
from pvlib import atmosphere | ||
|
||
from pvlib import solarposition | ||
|
||
latitude, longitude, tz, altitude = 32.2, -111, 'US/Arizona', 700 | ||
|
||
|
@@ -113,3 +111,82 @@ def test_first_solar_spectral_correction_supplied(): | |
def test_first_solar_spectral_correction_ambiguous(): | ||
with pytest.raises(TypeError): | ||
atmosphere.first_solar_spectral_correction(1, 1) | ||
|
||
|
||
def test_kasten96_lt(): | ||
"""Test Linke turbidity factor calculated from AOD, Pwat and AM""" | ||
timestamps = pd.DatetimeIndex(MELBOURNE_FL[0]) | ||
timestamps = timestamps.tz_localize('UTC').tz_convert('Etc/GMT+5') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. one more that I just noticed... this line errors on the python27-min configuration because I don't understand why the python-36 build completed, but we should still fix it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was not tz aware on my machine, it gets converted to a naive date time index in utc, that's why I had to localize as UTC then convert to us/Eastern. Is this a pandas or pytz version issue? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is what I get with pandas In [21]: pd.DatetimeIndex(MELBOURNE_FL[0])
Out[21]:
DatetimeIndex(['1999-01-31 17:00:00', '2000-02-20 20:00:00',
'2000-02-22 18:00:00', '2000-02-24 20:00:00',
'1995-03-02 19:00:00', '1995-03-11 17:00:00',
'1995-03-12 18:00:00', '1995-03-20 16:00:00',
'1995-03-20 19:00:00', '1995-03-22 16:00:00',
'1995-03-22 19:00:00', '1995-04-07 14:00:00',
'1995-04-10 14:00:00', '1995-04-21 14:00:00',
'2004-05-01 13:00:00', '2004-05-03 13:00:00',
'2004-05-05 18:00:00', '2004-05-16 14:00:00',
'2004-05-21 20:00:00', '2004-05-24 16:00:00',
'2004-05-31 14:00:00', '2002-06-04 21:00:00',
'2002-06-16 22:00:00', '2002-06-17 13:00:00',
'2000-07-01 13:00:00', '2000-07-05 12:00:00',
'2000-07-06 12:00:00', '2000-07-06 22:00:00',
'2000-07-25 20:00:00', '2001-10-14 18:00:00',
'2001-10-15 16:00:00', '2003-11-04 19:00:00',
'1999-12-20 18:00:00'],
dtype='datetime64[ns]', freq=None)
In [22]: pd.__version__
Out[22]: u'0.19.1' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no change in newest pandas or pytz In [3]: pd.__version__
Out[3]: u'0.19.2'
In [4]: pd.DatetimeIndex(MELBOURNE_FL[0])
Out[4]:
DatetimeIndex(['1999-01-31 17:00:00', '2000-02-20 20:00:00',
'2000-02-22 18:00:00', '2000-02-24 20:00:00',
'1995-03-02 19:00:00', '1995-03-11 17:00:00',
'1995-03-12 18:00:00', '1995-03-20 16:00:00',
'1995-03-20 19:00:00', '1995-03-22 16:00:00',
'1995-03-22 19:00:00', '1995-04-07 14:00:00',
'1995-04-10 14:00:00', '1995-04-21 14:00:00',
'2004-05-01 13:00:00', '2004-05-03 13:00:00',
'2004-05-05 18:00:00', '2004-05-16 14:00:00',
'2004-05-21 20:00:00', '2004-05-24 16:00:00',
'2004-05-31 14:00:00', '2002-06-04 21:00:00',
'2002-06-16 22:00:00', '2002-06-17 13:00:00',
'2000-07-01 13:00:00', '2000-07-05 12:00:00',
'2000-07-06 12:00:00', '2000-07-06 22:00:00',
'2000-07-25 20:00:00', '2001-10-14 18:00:00',
'2001-10-15 16:00:00', '2003-11-04 19:00:00',
'1999-12-20 18:00:00'],
dtype='datetime64[ns]', freq=None)
In [6]: pytz.__version__
Out[6]: '2016.10' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get the same thing on pandas 0.19.2. But also this... In [11]: pd.Timestamp(MELBOURNE_FL[0][0])
Out[11]: Timestamp('1999-01-31 12:00:00-0500', tz='pytz.FixedOffset(-300)') There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
melbourne_fl = pd.DataFrame(MELBOURNE_FL[1], index=timestamps) | ||
aod_bb = melbourne_fl['AOD'] | ||
pwat_cm = melbourne_fl['Pwat'] | ||
pressure = melbourne_fl['Pressure'] | ||
dry_temp = melbourne_fl['DryBulb'] | ||
lat, lon, alt = 28.117, -80.65, 11.0 | ||
sp = solarposition.get_solarposition( | ||
timestamps, lat, lon, alt, pressure=pressure, temperature=dry_temp | ||
) | ||
am = atmosphere.relativeairmass(sp.apparent_zenith) | ||
amp = atmosphere.absoluteairmass(am, pressure=pressure) | ||
lt_kasten = atmosphere.kasten96_lt( | ||
airmass_absolute=amp, precipitable_water=pwat_cm, aod_bb=aod_bb | ||
) | ||
lt = clearsky.lookup_linke_turbidity(timestamps, lat, lon) | ||
assert np.allclose(lt, lt_kasten, 1e-3) | ||
return lt, lt_kasten | ||
|
||
|
||
def test_angstrom_aod(): | ||
"""Test Angstrom turbidity model functions.""" | ||
aod550 = 0.15 | ||
aod1240 = 0.05 | ||
alpha = atmosphere.angstrom_alpha(aod550, 550, aod1240, 1240) | ||
np.isclose(alpha, [1.3513924317859232]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to assert np.allclose or use np.testing.assert_allclose There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Weird. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. *iterable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed these back to scalars, although pylint now complains, oh well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, it's pylint that's the trouble: In [4]: np.isclose(2, [2])
Out[4]: array([ True], dtype=bool)
In [5]: np.isclose(2, 2)
Out[5]: True There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's that you have to assert something is true or not. np.isclose only returns an array. |
||
aod700 = atmosphere.angstrom_aod_at_lambda(aod550, 550, alpha) | ||
np.isclose(aod700, [0.10828110997681031]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same assert There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
|
||
|
||
def test_bird_hulstrom80_aod_bb(): | ||
"""Test Bird_Hulstrom broadband AOD.""" | ||
aod380, aod500 = 0.22072480948195175, 0.1614279181106312 | ||
bird_hulstrom = atmosphere.bird_hulstrom80_aod_bb(aod380, aod500) | ||
np.isclose([0.09823143641608373], bird_hulstrom) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same assert There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
|
||
|
||
MELBOURNE_FL = ( | ||
['1999-01-31T12:00:00-05:00', '2000-02-20T15:00:00-05:00', | ||
'2000-02-22T13:00:00-05:00', '2000-02-24T15:00:00-05:00', | ||
'1995-03-02T14:00:00-05:00', '1995-03-11T12:00:00-05:00', | ||
'1995-03-12T13:00:00-05:00', '1995-03-20T11:00:00-05:00', | ||
'1995-03-20T14:00:00-05:00', '1995-03-22T11:00:00-05:00', | ||
'1995-03-22T14:00:00-05:00', '1995-04-07T09:00:00-05:00', | ||
'1995-04-10T09:00:00-05:00', '1995-04-21T09:00:00-05:00', | ||
'2004-05-01T08:00:00-05:00', '2004-05-03T08:00:00-05:00', | ||
'2004-05-05T13:00:00-05:00', '2004-05-16T09:00:00-05:00', | ||
'2004-05-21T15:00:00-05:00', '2004-05-24T11:00:00-05:00', | ||
'2004-05-31T09:00:00-05:00', '2002-06-04T16:00:00-05:00', | ||
'2002-06-16T17:00:00-05:00', '2002-06-17T08:00:00-05:00', | ||
'2000-07-01T08:00:00-05:00', '2000-07-05T07:00:00-05:00', | ||
'2000-07-06T07:00:00-05:00', '2000-07-06T17:00:00-05:00', | ||
'2000-07-25T15:00:00-05:00', '2001-10-14T13:00:00-05:00', | ||
'2001-10-15T11:00:00-05:00', '2003-11-04T14:00:00-05:00', | ||
'1999-12-20T13:00:00-05:00'], | ||
{'AOD': [0.062, 0.082, 0.084, 0.087, 0.097, 0.110, 0.111, 0.119, | ||
0.119, 0.121, 0.121, 0.128, 0.129, 0.137, 0.150, 0.152, | ||
0.154, 0.164, 0.167, 0.169, 0.171, 0.173, 0.188, 0.191, | ||
0.237, 0.248, 0.251, 0.251, 0.244, 0.119, 0.118, 0.101, | ||
0.085], | ||
'Pwat': [2.7, 2.2, 1.6, 2.3, 1.9, 1.9, 1.9, 2.0, 2.0, 1.9, 1.9, | ||
2.6, 2.5, 2.6, 4.0, 4.1, 1.8, 3.6, 3.0, 2.3, 3.7, 4.1, | ||
4.9, 4.6, 4.2, 4.3, 4.3, 4.3, 4.6, 4.8, 4.9, 4.7, 2.7], | ||
'Pressure': [101500., 101700., 101200., 101900., 101700., 102900., | ||
102800., 101600., 101400., 101400., 101200., 101200., | ||
101400., 101700., 101700., 101500., 101900., 102200., | ||
102000., 101800., 101500., 101500., 101200., 101400., | ||
101800., 102000., 101900., 101700., 101700., 101300., | ||
101700., 101500., 101400.], | ||
'DryBulb': [23.0, 19.0, 24.0, 23.0, 18.3, 21.6, 23.3, 22.7, 24.4, | ||
24.4, 28.3, 19.4, 25.0, 27.2, 25.0, 25.0, 25.0, 25.0, | ||
28.0, 28.0, 31.0, 29.0, 24.0, 24.0, 25.0, 26.0, 23.0, | ||
32.0, 29.0, 29.0, 28.0, 26.6, 20.0]} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are a handful of typos in the references
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1-3 good references might be more useful than 8.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fixed the references errors. Not sure I agree about the references. It's hard for me to tease out all of these pieces from just one or two references. I suppose we could just use the Ineichen references which include almost all of the other ones, although some are incorrect:
What is your preference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with a longer references section if that's your preference.