Skip to content

Commit 28cc9fb

Browse files
authored
Add pw and spectral correction code from pvlib matlab 1.3 (#115)
* add first solar spec correction. needs tests * add calc_pw. needs tests * add lower to coefs input * clean up rebase. shorten long lines * rename, clean docs, add pw test * update fs coeffs, add fs tests * doc fixes * add 040 whatsnew
1 parent f773e2d commit 28cc9fb

File tree

4 files changed

+293
-3
lines changed

4 files changed

+293
-3
lines changed

docs/sphinx/source/whatsnew.rst

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ What's New
66

77
These are new features and improvements of note in each release.
88

9+
.. include:: whatsnew/v0.4.0.txt
910
.. include:: whatsnew/v0.3.3.txt
1011
.. include:: whatsnew/v0.3.2.txt
1112
.. include:: whatsnew/v0.3.1.txt
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
.. _whatsnew_0400:
2+
3+
v0.4.0 (July xx, 2016)
4+
-----------------------
5+
6+
This is a major release from 0.3.3.
7+
We recommend that all users upgrade to this version after reviewing
8+
the API changes.
9+
10+
11+
API Changes
12+
~~~~~~~~~~~
13+
14+
15+
16+
Enhancements
17+
~~~~~~~~~~~~
18+
19+
* Adds the First Solar spectral correction model. (:issue:`115`)
20+
* Adds the Gueymard 1994 integrated precipitable water model. (:issue:`115`)
21+
22+
23+
Bug fixes
24+
~~~~~~~~~
25+
26+
27+
28+
Documentation
29+
~~~~~~~~~~~~~
30+
31+
32+
33+
Other
34+
~~~~~
35+
36+
37+
38+
Code Contributors
39+
~~~~~~~~~~~~~~~~~
40+
41+
* Will Holmgren

pvlib/atmosphere.py

+196
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,199 @@ def relativeairmass(zenith, model='kastenyoung1989'):
242242
am = np.nan if z > 90 else am
243243

244244
return am
245+
246+
247+
def gueymard94_pw(temp_air, relative_humidity):
248+
r"""
249+
Calculates precipitable water (cm) from ambient air temperature (C)
250+
and relatively humidity (%) using an empirical model. The
251+
accuracy of this method is approximately 20% for moderate PW (1-3
252+
cm) and less accurate otherwise.
253+
254+
The model was developed by expanding Eq. 1 in [2]_:
255+
256+
.. math::
257+
258+
w = 0.1 H_v \rho_v
259+
260+
using Eq. 2 in [2]_
261+
262+
.. math::
263+
264+
\rho_v = 216.7 R_H e_s /T
265+
266+
:math:`H_v` is the apparant water vapor scale height (km). The
267+
expression for :math:`H_v` is Eq. 4 in [2]_:
268+
269+
.. math::
270+
271+
H_v = 0.4976 + 1.5265*T/273.15 + \exp(13.6897*T/273.15 - 14.9188*(T/273.15)^3)
272+
273+
:math:`\rho_v` is the surface water vapor density (g/m^3). In the
274+
expression :math:`\rho_v`, :math:`e_s` is the saturation water vapor
275+
pressure (millibar). The
276+
expression for :math:`e_s` is Eq. 1 in [3]_
277+
278+
.. math::
279+
280+
e_s = \exp(22.330 - 49.140*(100/T) - 10.922*(100/T)^2 - 0.39015*T/100)
281+
282+
Parameters
283+
----------
284+
temp_air : array-like
285+
ambient air temperature at the surface (C)
286+
relative_humidity : array-like
287+
relative humidity at the surface (%)
288+
289+
Returns
290+
-------
291+
pw : array-like
292+
precipitable water (cm)
293+
294+
References
295+
----------
296+
.. [1] W. M. Keogh and A. W. Blakers, Accurate Measurement, Using Natural
297+
Sunlight, of Silicon Solar Cells, Prog. in Photovoltaics: Res.
298+
and Appl. 2004, vol 12, pp. 1-19 (DOI: 10.1002/pip.517)
299+
300+
.. [2] C. Gueymard, Analysis of Monthly Average Atmospheric Precipitable
301+
Water and Turbidity in Canada and Northern United States,
302+
Solar Energy vol 53(1), pp. 57-71, 1994.
303+
304+
.. [3] C. Gueymard, Assessment of the Accuracy and Computing Speed of
305+
simplified saturation vapor equations using a new reference
306+
dataset, J. of Applied Meteorology 1993, vol. 32(7), pp.
307+
1294-1300.
308+
"""
309+
310+
T = temp_air + 273.15 # Convert to Kelvin
311+
RH = relative_humidity
312+
313+
theta = T / 273.15
314+
315+
# Eq. 1 from Keogh and Blakers
316+
pw = (
317+
0.1 *
318+
(0.4976 + 1.5265*theta + np.exp(13.6897*theta - 14.9188*(theta)**3)) *
319+
(216.7*RH/(100*T)*np.exp(22.330 - 49.140*(100/T) -
320+
10.922*(100/T)**2 - 0.39015*T/100)))
321+
322+
pw = np.maximum(pw, 0.1)
323+
324+
return pw
325+
326+
327+
def first_solar_spectral_correction(pw, airmass_absolute, module_type=None,
328+
coefficients=None):
329+
r"""
330+
Spectral mismatch modifier based on precipitable water and absolute
331+
(pressure corrected) airmass.
332+
333+
Estimates a spectral mismatch modifier M representing the effect on
334+
module short circuit current of variation in the spectral
335+
irradiance. M is estimated from absolute (pressure currected) air
336+
mass, AMa, and precipitable water, Pwat, using the following
337+
function:
338+
339+
.. math::
340+
341+
M = c_1 + c_2*AMa + c_3*Pwat + c_4*AMa^.5
342+
+ c_5*Pwat^.5 + c_6*AMa/Pwat
343+
344+
Default coefficients are determined for several cell types with
345+
known quantum efficiency curves, by using the Simple Model of the
346+
Atmospheric Radiative Transfer of Sunshine (SMARTS) [1]_. Using
347+
SMARTS, spectrums are simulated with all combinations of AMa and
348+
Pwat where:
349+
350+
* 0.5 cm <= Pwat <= 5 cm
351+
* 0.8 <= AMa <= 4.75 (Pressure of 800 mbar and 1.01 <= AM <= 6)
352+
* Spectral range is limited to that of CMP11 (280 nm to 2800 nm)
353+
* spectrum simulated on a plane normal to the sun
354+
* All other parameters fixed at G173 standard
355+
356+
From these simulated spectra, M is calculated using the known
357+
quantum efficiency curves. Multiple linear regression is then
358+
applied to fit Eq. 1 to determine the coefficients for each module.
359+
360+
Based on the PVLIB Matlab function ``pvl_FSspeccorr`` by Mitchell
361+
Lee and Alex Panchula, at First Solar, 2015.
362+
363+
Parameters
364+
----------
365+
pw : array-like
366+
atmospheric precipitable water (cm).
367+
368+
airmass_absolute :
369+
absolute (pressure corrected) airmass.
370+
371+
module_type : None or string
372+
a string specifying a cell type. Can be lower or upper case
373+
letters. Admits values of 'cdte', 'monosi', 'xsi', 'multisi',
374+
'polysi'. If provided, this input selects coefficients for the
375+
following default modules:
376+
377+
* 'cdte' - First Solar Series 4-2 CdTe modules.
378+
* 'monosi', 'xsi' - First Solar TetraSun modules.
379+
* 'multisi', 'polysi' - multi-crystalline silicon modules.
380+
381+
The module used to calculate the spectral correction
382+
coefficients corresponds to the Mult-crystalline silicon
383+
Manufacturer 2 Model C from [2]_.
384+
385+
coefficients : array-like
386+
allows for entry of user defined spectral correction
387+
coefficients. Coefficients must be of length 6. Derivation of
388+
coefficients requires use of SMARTS and PV module quantum
389+
efficiency curve. Useful for modeling PV module types which are
390+
not included as defaults, or to fine tune the spectral
391+
correction to a particular mono-Si, multi-Si, or CdTe PV module.
392+
Note that the parameters for modules with very similar QE should
393+
be similar, in most cases limiting the need for module specific
394+
coefficients.
395+
396+
Returns
397+
-------
398+
modifier: array-like
399+
spectral mismatch factor (unitless) which is can be multiplied
400+
with broadband irradiance reaching a module's cells to estimate
401+
effective irradiance, i.e., the irradiance that is converted to
402+
electrical current.
403+
404+
References
405+
----------
406+
.. [1] Gueymard, Christian. SMARTS2: a simple model of the atmospheric
407+
radiative transfer of sunshine: algorithms and performance
408+
assessment. Cocoa, FL: Florida Solar Energy Center, 1995.
409+
410+
.. [2] Marion, William F., et al. User's Manual for Data for Validating
411+
Models for PV Module Performance. National Renewable Energy
412+
Laboratory, 2014. http://www.nrel.gov/docs/fy14osti/61610.pdf
413+
"""
414+
415+
_coefficients = {}
416+
_coefficients['cdte'] = (
417+
0.87102, -0.040543, -0.00929202, 0.10052, 0.073062, -0.0034187)
418+
_coefficients['monosi'] = (
419+
0.86588, -0.021637, -0.0030218, 0.12081, 0.017514, -0.0012610)
420+
_coefficients['xsi'] = _coefficients['monosi']
421+
_coefficients['polysi'] = (
422+
0.84674, -0.028568, -0.0051832, 0.13669, 0.029234, -0.0014207)
423+
_coefficients['multisi'] = _coefficients['polysi']
424+
425+
if module_type is not None and coefficients is None:
426+
coefficients = _coefficients[module_type.lower()]
427+
elif module_type is None and coefficients is not None:
428+
pass
429+
else:
430+
raise TypeError('ambiguous input, must supply only 1 of ' +
431+
'module_type and coefficients')
432+
433+
# Evaluate Spectral Shift
434+
coeff = coefficients
435+
AMa = airmass_absolute
436+
modifier = (
437+
coeff[0] + coeff[1]*AMa + coeff[2]*pw + coeff[3]*np.sqrt(AMa) +
438+
+ coeff[4]*np.sqrt(pw) + coeff[5]*AMa/pw)
439+
440+
return modifier

pvlib/test/test_atmosphere.py

+55-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import logging
2-
pvl_logger = logging.getLogger('pvlib')
3-
41
import datetime
2+
import itertools
53

64
import numpy as np
75
import pandas as pd
86

97
from nose.tools import raises
108
from nose.tools import assert_almost_equals
9+
from numpy.testing import assert_allclose
1110

1211
from pvlib.location import Location
1312
from pvlib import solarposition
@@ -65,3 +64,56 @@ def test_absoluteairmass_numeric():
6564
def test_absoluteairmass_nan():
6665
np.testing.assert_equal(np.nan, atmosphere.absoluteairmass(np.nan))
6766

67+
68+
def test_gueymard94_pw():
69+
temp_air = np.array([0, 20, 40])
70+
relative_humidity = np.array([0, 30, 100])
71+
temps_humids = np.array(
72+
list(itertools.product(temp_air, relative_humidity)))
73+
pws = atmosphere.gueymard94_pw(temps_humids[:, 0], temps_humids[:, 1])
74+
75+
expected = np.array(
76+
[ 0.1 , 0.33702061, 1.12340202, 0.1 ,
77+
1.12040963, 3.73469877, 0.1 , 3.44859767, 11.49532557])
78+
79+
assert_allclose(pws, expected, atol=0.01)
80+
81+
82+
def test_first_solar_spectral_correction():
83+
ams = np.array([1, 3, 5])
84+
pws = np.array([1, 3, 5])
85+
ams, pws = np.meshgrid(ams, pws)
86+
87+
expect = {}
88+
expect['cdte'] = np.array(
89+
[[ 0.99134828, 0.97701063, 0.93975103],
90+
[ 1.02852847, 1.01874908, 0.98604776],
91+
[ 1.04722476, 1.03835703, 1.00656735]])
92+
expect['monosi'] = np.array(
93+
[[ 0.9782842 , 1.02092726, 1.03602157],
94+
[ 0.9859024 , 1.0302268 , 1.04700244],
95+
[ 0.98885429, 1.03351495, 1.05062687]])
96+
expect['polysi'] = np.array(
97+
[[ 0.9774921 , 1.01757872, 1.02649543],
98+
[ 0.98947361, 1.0314545 , 1.04226547],
99+
[ 0.99403107, 1.03639082, 1.04758064]])
100+
101+
def run_fs_test(module_type):
102+
out = atmosphere.first_solar_spectral_correction(pws, ams, module_type)
103+
assert_allclose(out, expect[module_type], atol=0.001)
104+
105+
for module_type in expect.keys():
106+
yield run_fs_test, module_type
107+
108+
109+
def test_first_solar_spectral_correction_supplied():
110+
# use the cdte coeffs
111+
coeffs = (0.87102, -0.040543, -0.00929202, 0.10052, 0.073062, -0.0034187)
112+
out = atmosphere.first_solar_spectral_correction(1, 1, coefficients=coeffs)
113+
expected = 0.99134828
114+
assert_allclose(out, expected, atol=1e-3)
115+
116+
117+
@raises(TypeError)
118+
def test_first_solar_spectral_correction_ambiguous():
119+
atmosphere.first_solar_spectral_correction(1, 1)

0 commit comments

Comments
 (0)