Skip to content

Commit 3fb07d4

Browse files
authored
fix dropped nighttime values in perez, propagate airmass to perez in ModelChain (#192)
* send am through mc to perez function. fix perez nans * really fix the nan/dropped data problem in perez * update whatsnew * rebase on master
1 parent ab63c91 commit 3fb07d4

File tree

6 files changed

+231
-217
lines changed

6 files changed

+231
-217
lines changed

docs/sphinx/source/whatsnew/v0.3.3.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ Bug fixes
3737
dict-like object. (:issue:`157`)
3838
* Fix numpy 1.11 deprecation warnings caused by some functions using
3939
non-integer indices.
40+
* Propagate airmass data through ModelChain's get_irradiance call so that
41+
the perez model can use it, if necessary. (:issue:`172`)
42+
* Fix problem in which the perez function dropped nighttime values.
43+
Nighttime values are now set to 0.
44+
(:issue:`191`)
45+
4046

4147
Documentation
4248
~~~~~~~~~~~~~

docs/tutorials/irradiance.ipynb

Lines changed: 133 additions & 144 deletions
Large diffs are not rendered by default.

pvlib/irradiance.py

Lines changed: 49 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -967,32 +967,30 @@ def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra,
967967
solar_zenith, solar_azimuth, airmass,
968968
modelt='allsitescomposite1990'):
969969
'''
970-
Determine diffuse irradiance from the sky on a tilted surface using one of
971-
the Perez models.
970+
Determine diffuse irradiance from the sky on a tilted surface using
971+
one of the Perez models.
972972
973973
Perez models determine the diffuse irradiance from the sky (ground
974974
reflected irradiance is not included in this algorithm) on a tilted
975975
surface using the surface tilt angle, surface azimuth angle, diffuse
976976
horizontal irradiance, direct normal irradiance, extraterrestrial
977977
irradiance, sun zenith angle, sun azimuth angle, and relative (not
978-
pressure-corrected) airmass. Optionally a selector may be used to use
979-
any of Perez's model coefficient sets.
978+
pressure-corrected) airmass. Optionally a selector may be used to
979+
use any of Perez's model coefficient sets.
980980
981981
982982
Parameters
983983
----------
984984
985985
surface_tilt : float or Series
986-
Surface tilt angles in decimal degrees.
987-
surface_tilt must be >=0 and <=180. The tilt angle is defined as
988-
degrees from horizontal (e.g. surface facing up = 0, surface facing
989-
horizon = 90)
986+
Surface tilt angles in decimal degrees. surface_tilt must be >=0
987+
and <=180. The tilt angle is defined as degrees from horizontal
988+
(e.g. surface facing up = 0, surface facing horizon = 90)
990989
991990
surface_azimuth : float or Series
992-
Surface azimuth angles in decimal degrees.
993-
surface_azimuth must be >=0 and <=360. The Azimuth convention is defined
994-
as degrees east of north (e.g. North = 0, South=180 East = 90,
995-
West = 270).
991+
Surface azimuth angles in decimal degrees. surface_azimuth must
992+
be >=0 and <=360. The Azimuth convention is defined as degrees
993+
east of north (e.g. North = 0, South=180 East = 90, West = 270).
996994
997995
dhi : float or Series
998996
Diffuse horizontal irradiance in W/m^2.
@@ -1011,22 +1009,20 @@ def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra,
10111009
solar_zenith must be >=0 and <=180.
10121010
10131011
solar_azimuth : float or Series
1014-
Sun azimuth angles in decimal degrees.
1015-
solar_azimuth must be >=0 and <=360. The Azimuth convention is defined
1016-
as degrees east of north (e.g. North = 0, East = 90, West = 270).
1012+
Sun azimuth angles in decimal degrees. solar_azimuth must be >=0
1013+
and <=360. The Azimuth convention is defined as degrees east of
1014+
north (e.g. North = 0, East = 90, West = 270).
10171015
10181016
airmass : float or Series
1019-
relative (not pressure-corrected) airmass
1020-
values. If AM is a DataFrame it must be of the same size as all other
1021-
DataFrame inputs. AM must be >=0 (careful using the 1/sec(z) model of
1022-
AM generation)
1017+
Relative (not pressure-corrected) airmass values. If AM is a
1018+
DataFrame it must be of the same size as all other DataFrame
1019+
inputs. AM must be >=0 (careful using the 1/sec(z) model of AM
1020+
generation)
10231021
10241022
model : string (optional, default='allsitescomposite1990')
1025-
1026-
A string which selects the desired set of Perez
1027-
coefficients. If model is not provided as an input, the default,
1028-
'1990' will be used.
1029-
All possible model selections are:
1023+
A string which selects the desired set of Perez coefficients. If
1024+
model is not provided as an input, the default, '1990' will be
1025+
used. All possible model selections are:
10301026
10311027
* '1990'
10321028
* 'allsitescomposite1990' (same as '1990')
@@ -1046,44 +1042,42 @@ def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra,
10461042
10471043
float or Series
10481044
1049-
The diffuse component of the solar radiation on an
1050-
arbitrarily tilted surface defined by the Perez model as given in
1051-
reference [3].
1052-
SkyDiffuse is the diffuse component ONLY and does not include the
1053-
ground reflected irradiance or the irradiance due to the beam.
1045+
The diffuse component of the solar radiation on an arbitrarily
1046+
tilted surface defined by the Perez model as given in reference
1047+
[3]. SkyDiffuse is the diffuse component ONLY and does not
1048+
include the ground reflected irradiance or the irradiance due to
1049+
the beam.
10541050
10551051
10561052
References
10571053
----------
10581054
1059-
[1] Loutzenhiser P.G. et. al. "Empirical validation of models to compute
1060-
solar irradiance on inclined surfaces for building energy simulation"
1061-
2007, Solar Energy vol. 81. pp. 254-267
1055+
[1] Loutzenhiser P.G. et. al. "Empirical validation of models to
1056+
compute solar irradiance on inclined surfaces for building energy
1057+
simulation" 2007, Solar Energy vol. 81. pp. 254-267
10621058
1063-
[2] Perez, R., Seals, R., Ineichen, P., Stewart, R., Menicucci, D., 1987.
1064-
A new simplified version of the Perez diffuse irradiance model for tilted
1065-
surfaces. Solar Energy 39(3), 221-232.
1059+
[2] Perez, R., Seals, R., Ineichen, P., Stewart, R., Menicucci, D.,
1060+
1987. A new simplified version of the Perez diffuse irradiance model
1061+
for tilted surfaces. Solar Energy 39(3), 221-232.
10661062
1067-
[3] Perez, R., Ineichen, P., Seals, R., Michalsky, J., Stewart, R., 1990.
1068-
Modeling daylight availability and irradiance components from direct
1069-
and global irradiance. Solar Energy 44 (5), 271-289.
1063+
[3] Perez, R., Ineichen, P., Seals, R., Michalsky, J., Stewart, R.,
1064+
1990. Modeling daylight availability and irradiance components from
1065+
direct and global irradiance. Solar Energy 44 (5), 271-289.
10701066
10711067
[4] Perez, R. et. al 1988. "The Development and Verification of the
10721068
Perez Diffuse Radiation Model". SAND88-7030
10731069
'''
10741070

1075-
pvl_logger.debug('diffuse_sky.perez()')
1076-
10771071
kappa = 1.041 # for solar_zenith in radians
10781072
z = np.radians(solar_zenith) # convert to radians
10791073

10801074
# epsilon is the sky's "clearness"
10811075
eps = ((dhi + dni) / dhi + kappa * (z ** 3)) / (1 + kappa * (z ** 3))
10821076

1083-
# Perez et al define clearness bins according to the following rules.
1084-
# 1 = overcast ... 8 = clear
1085-
# (these names really only make sense for small zenith angles, but...)
1086-
# these values will eventually be used as indicies for coeffecient look ups
1077+
# Perez et al define clearness bins according to the following
1078+
# rules. 1 = overcast ... 8 = clear (these names really only make
1079+
# sense for small zenith angles, but...) these values will
1080+
# eventually be used as indicies for coeffecient look ups
10871081
ebin = eps.copy()
10881082
ebin[(eps < 1.065)] = 1
10891083
ebin[(eps >= 1.065) & (eps < 1.23)] = 2
@@ -1100,31 +1094,29 @@ def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra,
11001094
ebin = ebin.dropna().astype(int)
11011095

11021096
# This is added because in cases where the sun is below the horizon
1103-
# (var.solar_zenith > 90) but there is still diffuse horizontal light
1104-
# (var.DHI>0), it is possible that the airmass (var.AM) could be NaN, which
1105-
# messes up later calculations. Instead, if the sun is down, and there is
1106-
# still var.DHI, we set the airmass to the airmass value on the horizon
1107-
# (approximately 37-38).
1097+
# (var.solar_zenith > 90) but there is still diffuse horizontal
1098+
# light (var.DHI>0), it is possible that the airmass (var.AM) could
1099+
# be NaN, which messes up later calculations. Instead, if the sun is
1100+
# down, and there is still var.DHI, we set the airmass to the
1101+
# airmass value on the horizon (approximately 37-38).
11081102
# var.AM(var.solar_zenith >=90 & var.DHI >0) = 37;
11091103

11101104
# var.DNI_ET[var.DNI_ET==0] = .00000001 #very hacky, fix this
11111105

11121106
# delta is the sky's "brightness"
11131107
delta = dhi * airmass / dni_extra
11141108

1115-
# keep only valid times
1116-
delta = delta[ebin.index]
1117-
z = z[ebin.index]
1118-
11191109
# The various possible sets of Perez coefficients are contained
11201110
# in a subfunction to clean up the code.
11211111
F1c, F2c = _get_perez_coefficients(modelt)
11221112

1123-
F1 = F1c[ebin, 0] + F1c[ebin, 1] * delta + F1c[ebin, 2] * z
1113+
F1 = (F1c[ebin, 0] + F1c[ebin, 1] * delta[ebin.index] +
1114+
F1c[ebin, 2] * z[ebin.index])
11241115
F1[F1 < 0] = 0
11251116
F1 = F1.astype(float)
11261117

1127-
F2 = F2c[ebin, 0] + F2c[ebin, 1] * delta + F2c[ebin, 2] * z
1118+
F2 = (F2c[ebin, 0] + F2c[ebin, 1] * delta[ebin.index] +
1119+
F2c[ebin, 2] * z[ebin.index])
11281120
F2[F2 < 0] = 0
11291121
F2 = F2.astype(float)
11301122

@@ -1141,8 +1133,9 @@ def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra,
11411133
term2 = F1 * A[ebin.index] / B[ebin.index]
11421134
term3 = F2 * tools.sind(surface_tilt)
11431135

1144-
sky_diffuse = dhi[ebin.index] * (term1 + term2 + term3)
1136+
sky_diffuse = dhi * (term1 + term2 + term3)
11451137
sky_diffuse[sky_diffuse < 0] = 0
1138+
sky_diffuse[airmass.isnull()] = 0
11461139

11471140
return sky_diffuse
11481141

pvlib/modelchain.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,12 +268,12 @@ def __init__(self, system, location,
268268

269269
# calls setter
270270
self.orientation_strategy = orientation_strategy
271-
271+
272272
def __repr__(self):
273-
return ('ModelChain for: '+ str(self.system) +
273+
return ('ModelChain for: '+ str(self.system) +
274274
' orientation_startegy: ' + str(self.orientation_strategy) +
275275
' clearsky_model: ' + str(self.clearsky_model) +
276-
'transposition_model: ' + str(self.transposition_model) +
276+
'transposition_model: ' + str(self.transposition_model) +
277277
' solar_position_method: ' + str(self.solar_position_method) +
278278
'airmass_model: ' + str(self.airmass_model))
279279

@@ -360,7 +360,8 @@ def run_model(self, times, irradiance=None, weather=None):
360360
self.irradiance['dni'],
361361
self.irradiance['ghi'],
362362
self.irradiance['dhi'],
363-
model=self.transposition_model)
363+
model=self.transposition_model,
364+
airmass=self.airmass['airmass_relative'])
364365

365366
if weather is None:
366367
weather = {'wind_speed': 0, 'temp_air': 20}

pvlib/test/test_irradiance.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import logging
22
pvl_logger = logging.getLogger('pvlib')
33

4-
import datetime
5-
64
import numpy as np
75
import pandas as pd
86

@@ -20,15 +18,14 @@
2018
from . import requires_ephem
2119

2220
# setup times and location to be tested.
23-
times = pd.date_range(start=datetime.datetime(2014, 6, 24),
24-
end=datetime.datetime(2014, 6, 26), freq='1Min')
25-
2621
tus = Location(32.2, -111, 'US/Arizona', 700)
2722

28-
times_localized = times.tz_localize(tus.tz)
23+
# must include night values
24+
times = pd.date_range(start='20140624', end='20140626', freq='1Min',
25+
tz=tus.tz)
2926

30-
ephem_data = solarposition.get_solarposition(times, tus.latitude,
31-
tus.longitude, method='nrel_numpy')
27+
ephem_data = solarposition.get_solarposition(
28+
times, tus.latitude, tus.longitude, method='nrel_numpy')
3229

3330
irrad_data = clearsky.ineichen(times, tus.latitude, tus.longitude,
3431
altitude=tus.altitude, linke_turbidity=3,
@@ -141,10 +138,11 @@ def test_king():
141138

142139

143140
def test_perez():
144-
AM = atmosphere.relativeairmass(ephem_data['apparent_zenith'])
145-
irradiance.perez(40, 180, irrad_data['dhi'], irrad_data['dni'],
141+
am = atmosphere.relativeairmass(ephem_data['apparent_zenith'])
142+
out = irradiance.perez(40, 180, irrad_data['dhi'], irrad_data['dni'],
146143
dni_et, ephem_data['apparent_zenith'],
147-
ephem_data['azimuth'], AM)
144+
ephem_data['azimuth'], am)
145+
assert not out.isnull().any()
148146

149147
# klutcher (misspelling) will be removed in 0.3
150148
def test_total_irrad():

pvlib/test/test_modelchain.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,33 @@ def test_run_model_with_irradiance():
9090
assert_series_equal(ac, expected)
9191

9292

93+
def test_run_model_perez():
94+
system, location = mc_setup()
95+
mc = ModelChain(system, location, transposition_model='perez')
96+
times = pd.date_range('20160101 1200-0700', periods=2, freq='6H')
97+
irradiance = pd.DataFrame({'dni':900, 'ghi':600, 'dhi':150},
98+
index=times)
99+
ac = mc.run_model(times, irradiance=irradiance).ac
100+
101+
expected = pd.Series(np.array([ 190.194545796, -2.00000000e-02]),
102+
index=times)
103+
assert_series_equal(ac, expected)
104+
105+
106+
def test_run_model_gueymard_perez():
107+
system, location = mc_setup()
108+
mc = ModelChain(system, location, airmass_model='gueymard1993',
109+
transposition_model='perez')
110+
times = pd.date_range('20160101 1200-0700', periods=2, freq='6H')
111+
irradiance = pd.DataFrame({'dni':900, 'ghi':600, 'dhi':150},
112+
index=times)
113+
ac = mc.run_model(times, irradiance=irradiance).ac
114+
115+
expected = pd.Series(np.array([ 190.194760203, -2.00000000e-02]),
116+
index=times)
117+
assert_series_equal(ac, expected)
118+
119+
93120
def test_run_model_with_weather():
94121
system, location = mc_setup()
95122
mc = ModelChain(system, location)
@@ -220,8 +247,8 @@ def test_basic_chain_altitude_pressure():
220247
expected = pd.Series(np.array([ 1.15771428788e+02, -2.00000000e-02]),
221248
index=times)
222249
assert_series_equal(ac, expected)
223-
224-
250+
251+
225252
def test_ModelChain___repr__():
226253
system = PVSystem()
227254
location = Location(32.2, -111, altitude=700)

0 commit comments

Comments
 (0)