Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9a120dd

Browse filesBrowse files
mikofskiwholmgren
authored andcommitted
add linke turbidity formulas module to atmosphere (#278)
* add linke turbidity formulas module * add kasten 1996 pyrheliometric formula for linke turbidity factor * move atmosphere into package, just for fun, why not? * make __init__ with same api as before Signed-off-by: Mark Mikofski <mark.mikofski@sunpowercorp.com> * ignore venv, sublime * revert api changes from 6e9aaf0 * append linke_turb_forms.py to atmosphere.py * rm -rf atmosphere/ Signed-off-by: Mark Mikofski <mark.mikofski@sunpowercorp.com> * move demo to tests * import xrange from six for python 3 compatibility * fix pyrheliometric formula is from 1980 * fix docstring * just use `range` instead of importing `six` * change function to `kasten80_lt` to match `gueymard94_pw` * start adding references * test kaste96_lt against linke turbidity using tmy3 at 700nm * add lots of references * cite text quoted directly from Ineichen * acknowledge Armel * fix docstrings for numpydocs * removing filters from kasten pyrhelometric formula * refactor kasten96_lt api simpler * remove alpha argument * update docstring to explain that if molineaux method, then aod is shape is only aod at 700[nm] but if bird-hulstrom, then aod should be both aod380 and aod500 * add see also to angstrom_aod_at_lambda * add warning for air mass or pwat out of range * check for bird-hulstrom, raise type error if neither molineaux nor * also simplify angstrom_aod_at_lambda() api, change aod to aod0, change lambda0 to lambda1, and add lambda0, update docstring * update test_atmosphere.test_kasten96_lt with new changes * add angstrom_alpha() method in atmosphere to calculate alpha * simplify kasten96_lt API * separate arguments so their shapes don't change depending on method, ie: before aod could be 1, 2, N, N x 1 or N x 2 depending on whether Molineaux or Bird-Hulstrom were used, now, there are 3 separate args, aod700, aod380 and aod500. * fix misspelling of Hulstrom, no "d", note several sources also make this error :( * add new methods to api.rst * add test_angstrom tests * add raises value error test for bad method * add lower() to compare only lower case of method name * add new linke and angstrom functions to api.rst * fix more hulstrom errors, no "d" * fix typos in referecnes * add doi to sphinx docs as extlink * add 'doi': ('http://dx/doi.org/%s', 'DOI: ') to extlink in conf.py * change doi links everywhere to use :doi:`doi number` instead of links * make landscape happy and use r"""docstring with :math:`\alpha`""" * instead of """docstring with :math:`\\alpha` * add Co-Action publishing for original Tellus A DOI before moving to Taylor & Francis * reformat long lines to make landscape happy :) * use broadband AOD arg instead of methods * change kasten96_lt AOD args to just a single arg called aod_bb for broadband AOD * remove "method" arg and if-then-else on method.lower() in kasten96_lt() * add note that according to Molineaux, aod at 700 nm is a good approximation for broadband aod * add new function bird_hulstrom80_aod_bb to atmosphere.py * add it to api.rst * add new test for bird_hulstrom80_aod_bb() to test_atmosphere.py * update test_kasten96_lt() to use aod_bb * use verbose names: - s/am/airmass_absolute/g - s/pwat/precipitable_water/g * fix angstrom aod test to use more realistic values * don't use brackets around units * remove test for raises ValueError in test_kasten * use hard coded test values instead of TMY3 * values span pwat range 0 - 5 cm, and airmass between 1 & 2.5 atm * remove tmy3 file * don't try Bird-Hulstrom, not necessary * make pylint happy, isclose(a, b) a & b should be array or iterable * remove unused imports * fix test kasten * fix assert np.isclose() should be scalar only * super simplify test_kasten96_lt() to ranges of inputs and expected outputs, don't need to compare to actual data, just test that method is working nominally * remove clearsky import from test_atmosphere * add missing assert statements for tests * update hulstrom expected value * remove old melbourne-fl test data
1 parent 6a41299 commit 9a120dd
Copy full SHA for 9a120dd

File tree

Expand file treeCollapse file tree

6 files changed

+245
-6
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+245
-6
lines changed

‎.gitignore

Copy file name to clipboardExpand all lines: .gitignore
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var/
2222
Drafts/
2323
*/build/
2424
build/
25+
venv/
2526

2627
*.egg-info/
2728
.installed.cfg
@@ -58,6 +59,8 @@ coverage.xml
5859
.pydevproject
5960
.spyderproject
6061
.idea/
62+
*.sublime-project
63+
*.sublime-workspace
6164

6265
# Rope
6366
.ropeproject

‎docs/sphinx/source/api.rst

Copy file name to clipboardExpand all lines: docs/sphinx/source/api.rst
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ Airmass and atmospheric models
8787
atmosphere.alt2pres
8888
atmosphere.gueymard94_pw
8989
atmosphere.first_solar_spectral_correction
90+
atmosphere.bird_hulstrom80_aod_bb
91+
atmosphere.kasten96_lt
92+
atmosphere.angstrom_aod_at_lambda
93+
atmosphere.angstrom_alpha
9094

9195

9296
Irradiance

‎docs/sphinx/source/conf.py

Copy file name to clipboardExpand all lines: docs/sphinx/source/conf.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ def setup(app):
270270
extlinks = {'issue': ('https://github.com/pvlib/pvlib-python/issues/%s',
271271
'GH'),
272272
'wiki': ('https://github.com/pvlib/pvlib-python/wiki/%s',
273-
'wiki ')}
273+
'wiki '),
274+
'doi': ('http://dx.doi.org/%s', 'DOI: ')}
274275

275276
# -- Options for manual page output ---------------------------------------
276277

‎docs/sphinx/source/whatsnew/v0.4.4.txt

Copy file name to clipboardExpand all lines: docs/sphinx/source/whatsnew/v0.4.4.txt
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ v0.4.4 (January xx, 2017)
66
Enhancements
77
~~~~~~~~~~~~
88

9+
* Added Kasten pyrheliometric formula to calculate Linke turbidity factors with
10+
improvements by Ineichen and Perez to extend range of air mass (:issue:`278`)
11+
912

1013
Documentation
1114
~~~~~~~~~~~~~
@@ -19,3 +22,4 @@ Contributors
1922
~~~~~~~~~~~~
2023

2124
* Will Holmgren
25+
* Mark Mikofski

‎pvlib/atmosphere.py

Copy file name to clipboardExpand all lines: pvlib/atmosphere.py
+191-1Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def gueymard94_pw(temp_air, relative_humidity):
292292
----------
293293
.. [1] W. M. Keogh and A. W. Blakers, Accurate Measurement, Using Natural
294294
Sunlight, of Silicon Solar Cells, Prog. in Photovoltaics: Res.
295-
and Appl. 2004, vol 12, pp. 1-19 (DOI: 10.1002/pip.517)
295+
and Appl. 2004, vol 12, pp. 1-19 (:doi:`10.1002/pip.517`)
296296
297297
.. [2] C. Gueymard, Analysis of Monthly Average Atmospheric Precipitable
298298
Water and Turbidity in Canada and Northern United States,
@@ -467,3 +467,193 @@ def first_solar_spectral_correction(pw, airmass_absolute, module_type=None,
467467
coeff[4]*np.sqrt(pw) + coeff[5]*ama/np.sqrt(pw))
468468

469469
return modifier
470+
471+
472+
def bird_hulstrom80_aod_bb(aod380, aod500):
473+
"""
474+
Approximate broadband aerosol optical depth.
475+
476+
Bird and Hulstrom developed a correlation for broadband aerosol optical
477+
depth (AOD) using two wavelengths, 380 nm and 500 nm.
478+
479+
Parameters
480+
----------
481+
aod380 : numeric
482+
AOD measured at 380 nm
483+
aod500 : numeric
484+
AOD measured at 500 nm
485+
486+
Returns
487+
-------
488+
aod_bb : numeric
489+
broadband AOD
490+
491+
See also
492+
--------
493+
kasten96_lt
494+
495+
References
496+
----------
497+
[1] Bird and Hulstrom, "Direct Insolation Models" (1980)
498+
`SERI/TR-335-344 <http://www.nrel.gov/docs/legosti/old/344.pdf>`_
499+
500+
[2] R. E. Bird and R. L. Hulstrom, "Review, Evaluation, and Improvement of
501+
Direct Irradiance Models", Journal of Solar Energy Engineering 103(3),
502+
pp. 182-192 (1981)
503+
:doi:`10.1115/1.3266239`
504+
"""
505+
# approximate broadband AOD using (Bird-Hulstrom 1980)
506+
return 0.27583 * aod380 + 0.35 * aod500
507+
508+
509+
def kasten96_lt(airmass_absolute, precipitable_water, aod_bb):
510+
"""
511+
Calculate Linke turbidity factor using Kasten pyrheliometric formula.
512+
513+
Note that broadband aerosol optical depth (AOD) can be approximated by AOD
514+
measured at 700 nm according to Molineaux [4] . Bird and Hulstrom offer an
515+
alternate approximation using AOD measured at 380 nm and 500 nm.
516+
517+
Based on original implementation by Armel Oumbe.
518+
519+
.. warning::
520+
These calculations are only valid for air mass less than 5 atm and
521+
precipitable water less than 5 cm.
522+
523+
Parameters
524+
----------
525+
airmass_absolute : numeric
526+
airmass, pressure corrected in atmospheres
527+
precipitable_water : numeric
528+
precipitable water or total column water vapor in centimeters
529+
aod_bb : numeric
530+
broadband AOD
531+
532+
Returns
533+
-------
534+
lt : numeric
535+
Linke turbidity
536+
537+
See also
538+
--------
539+
bird_hulstrom80_aod_bb
540+
angstrom_aod_at_lambda
541+
542+
References
543+
----------
544+
[1] F. Linke, "Transmissions-Koeffizient und Trubungsfaktor", Beitrage
545+
zur Physik der Atmosphare, Vol 10, pp. 91-103 (1922)
546+
547+
[2] F. Kasten, "A simple parameterization of the pyrheliometric formula for
548+
determining the Linke turbidity factor", Meteorologische Rundschau 33,
549+
pp. 124-127 (1980)
550+
551+
[3] Kasten, "The Linke turbidity factor based on improved values of the
552+
integral Rayleigh optical thickness", Solar Energy, Vol. 56, No. 3,
553+
pp. 239-244 (1996)
554+
:doi:`10.1016/0038-092X(95)00114-7`
555+
556+
[4] B. Molineaux, P. Ineichen, N. O'Neill, "Equivalence of pyrheliometric
557+
and monochromatic aerosol optical depths at a single key wavelength",
558+
Applied Optics Vol. 37, issue 10, 7008-7018 (1998)
559+
:doi:`10.1364/AO.37.007008`
560+
561+
[5] P. Ineichen, "Conversion function between the Linke turbidity and the
562+
atmospheric water vapor and aerosol content", Solar Energy 82,
563+
pp. 1095-1097 (2008)
564+
:doi:`10.1016/j.solener.2008.04.010`
565+
566+
[6] P. Ineichen and R. Perez, "A new airmass independent formulation for
567+
the Linke Turbidity coefficient", Solar Energy, Vol. 73, no. 3, pp. 151-157
568+
(2002)
569+
:doi:`10.1016/S0038-092X(02)00045-2`
570+
"""
571+
# "From numerically integrated spectral simulations done with Modtran
572+
# (Berk, 1989), Molineaux (1998) obtained for the broadband optical depth
573+
# of a clean and dry atmospshere (fictitious atmosphere that comprises only
574+
# the effects of Rayleigh scattering and absorption by the atmosphere gases
575+
# other than the water vapor) the following expression"
576+
# - P. Ineichen (2008)
577+
delta_cda = -0.101 + 0.235 * airmass_absolute ** (-0.16)
578+
# "and the broadband water vapor optical depth where pwat is the integrated
579+
# precipitable water vapor content of the atmosphere expressed in cm and am
580+
# the optical air mass. The precision of these fits is better than 1% when
581+
# compared with Modtran simulations in the range 1 < am < 5 and
582+
# 0 < pwat < 5 cm at sea level" - P. Ineichen (2008)
583+
delta_w = 0.112 * airmass_absolute ** (-0.55) * precipitable_water ** 0.34
584+
# broadband AOD
585+
delta_a = aod_bb
586+
# "Then using the Kasten pyrheliometric formula (1980, 1996), the Linke
587+
# turbidity at am = 2 can be written. The extension of the Linke turbidity
588+
# coefficient to other values of air mass was published by Ineichen and
589+
# Perez (2002)" - P. Ineichen (2008)
590+
lt = -(9.4 + 0.9 * airmass_absolute) * np.log(
591+
np.exp(-airmass_absolute * (delta_cda + delta_w + delta_a))
592+
) / airmass_absolute
593+
# filter out of extrapolated values
594+
return lt
595+
596+
597+
def angstrom_aod_at_lambda(aod0, lambda0, alpha, lambda1=700.0):
598+
r"""
599+
Get AOD at specified wavelength using Angstrom turbidity model.
600+
601+
Parameters
602+
----------
603+
aod0 : numeric
604+
aerosol optical depth (AOD) measured at known wavelength
605+
lambda0 : numeric
606+
wavelength in nanometers corresponding to ``aod0``
607+
alpha : numeric
608+
Angstrom :math:`\alpha` exponent corresponding to ``aod0``
609+
lambda1 : numeric
610+
desired wavelength in nanometers, defaults to 700 nm
611+
612+
Returns
613+
-------
614+
aod1 : numeric
615+
AOD at desired wavelength, ``lambda1``
616+
617+
See also
618+
--------
619+
angstrom_alpha
620+
621+
References
622+
----------
623+
[1] Anders Angstrom, "On the Atmospheric Transmission of Sun Radiation and
624+
On Dust in the Air", Geografiska Annaler Vol. 11, pp. 156-166 (1929) JSTOR
625+
:doi:`10.2307/519399`
626+
627+
[2] Anders Angstrom, "Techniques of Determining the Turbidity of the
628+
Atmosphere", Tellus 13:2, pp. 214-223 (1961) Taylor & Francis
629+
:doi:`10.3402/tellusa.v13i2.9493` and Co-Action Publishing
630+
:doi:`10.1111/j.2153-3490.1961.tb00078.x`
631+
"""
632+
return aod0 * ((lambda1 / lambda0) ** (-alpha))
633+
634+
635+
def angstrom_alpha(aod1, lambda1, aod2, lambda2):
636+
r"""
637+
Calculate Angstrom alpha exponent.
638+
639+
Parameters
640+
----------
641+
aod1 : numeric
642+
first aerosol optical depth
643+
lambda1 : numeric
644+
wavelength in nanometers corresponding to ``aod1``
645+
aod2 : numeric
646+
second aerosol optical depth
647+
lambda2 : numeric
648+
wavelength in nanometers corresponding to ``aod2``
649+
650+
Returns
651+
-------
652+
alpha : numeric
653+
Angstrom :math:`\alpha` exponent for AOD in ``(lambda1, lambda2)``
654+
655+
See also
656+
--------
657+
angstrom_aod_at_lambda
658+
"""
659+
return - np.log(aod1 / aod2) / np.log(lambda1 / lambda2)

‎pvlib/test/test_atmosphere.py

Copy file name to clipboardExpand all lines: pvlib/test/test_atmosphere.py
+41-4Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import datetime
21
import itertools
32

43
import numpy as np
@@ -7,10 +6,8 @@
76
import pytest
87
from numpy.testing import assert_allclose
98

10-
from pvlib.location import Location
11-
from pvlib import solarposition
129
from pvlib import atmosphere
13-
10+
from pvlib import solarposition
1411

1512
latitude, longitude, tz, altitude = 32.2, -111, 'US/Arizona', 700
1613

@@ -113,3 +110,43 @@ def test_first_solar_spectral_correction_supplied():
113110
def test_first_solar_spectral_correction_ambiguous():
114111
with pytest.raises(TypeError):
115112
atmosphere.first_solar_spectral_correction(1, 1)
113+
114+
115+
def test_kasten96_lt():
116+
"""Test Linke turbidity factor calculated from AOD, Pwat and AM"""
117+
amp = np.array([1, 3, 5])
118+
pwat = np.array([0, 2.5, 5])
119+
aod_bb = np.array([0, 0.1, 1])
120+
lt_expected = np.array(
121+
[[[1.3802, 2.4102, 11.6802],
122+
[1.16303976, 2.37303976, 13.26303976],
123+
[1.12101907, 2.51101907, 15.02101907]],
124+
125+
[[2.95546945, 3.98546945, 13.25546945],
126+
[2.17435443, 3.38435443, 14.27435443],
127+
[1.99821967, 3.38821967, 15.89821967]],
128+
129+
[[3.37410769, 4.40410769, 13.67410769],
130+
[2.44311797, 3.65311797, 14.54311797],
131+
[2.23134152, 3.62134152, 16.13134152]]]
132+
)
133+
lt = atmosphere.kasten96_lt(*np.meshgrid(amp, pwat, aod_bb))
134+
assert np.allclose(lt, lt_expected, 1e-3)
135+
return lt
136+
137+
138+
def test_angstrom_aod():
139+
"""Test Angstrom turbidity model functions."""
140+
aod550 = 0.15
141+
aod1240 = 0.05
142+
alpha = atmosphere.angstrom_alpha(aod550, 550, aod1240, 1240)
143+
assert np.isclose(alpha, 1.3513924317859232)
144+
aod700 = atmosphere.angstrom_aod_at_lambda(aod550, 550, alpha)
145+
assert np.isclose(aod700, 0.10828110997681031)
146+
147+
148+
def test_bird_hulstrom80_aod_bb():
149+
"""Test Bird_Hulstrom broadband AOD."""
150+
aod380, aod500 = 0.22072480948195175, 0.1614279181106312
151+
bird_hulstrom = atmosphere.bird_hulstrom80_aod_bb(aod380, aod500)
152+
assert np.isclose(0.11738229553812768, bird_hulstrom)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.