Skip to content

Commit 0e9d25c

Browse files
authored
fix erbs near zenith=90 (#683)
* fix erbs near zenith=90 * document return type change * fix copy/paste in test * fix docs * revert accidental changes to test_clearsky
1 parent c9e3314 commit 0e9d25c

File tree

5 files changed

+87
-31
lines changed

5 files changed

+87
-31
lines changed

docs/sphinx/source/whatsnew.rst

Lines changed: 1 addition & 0 deletions
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.6.2.rst
910
.. include:: whatsnew/v0.6.1.rst
1011
.. include:: whatsnew/v0.6.0.rst
1112
.. include:: whatsnew/v0.5.2.rst

docs/sphinx/source/whatsnew/v0.6.2.rst

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,31 @@ date will require Python 3. (:issue:`501`)
1414

1515
API Changes
1616
~~~~~~~~~~~
17-
17+
* :py:func:`~pvlib.irradiance.erbs` *doy* argument changed to
18+
*datetime_or_doy* to be consistent with allowed types and similar
19+
functions (:py:func:`~pvlib.irradiance.disc`,
20+
:py:func:`~pvlib.irradiance.get_extra_radiation`).
21+
* :py:func:`~pvlib.irradiance.erbs` DataFrame vs. OrderedDict return
22+
behavior now determined by type of *datetime_or_doy* instead of
23+
*ghi* or *zenith*.
24+
* Added *min_cos_zenith* and *max_zenith* keyword arguments to
25+
:py:func:`~pvlib.irradiance.erbs`. (:issue:`681`)
1826

1927
Enhancements
2028
~~~~~~~~~~~~
21-
* Add US CRN data reader to `pvlib.iotools`.
22-
* Add SOLRAD data reader to `pvlib.iotools`.
23-
* Add EPW data reader to `pvlib.iotools`. (:issue:`591`)
29+
* Add US CRN data reader to :ref:`iotools`.
30+
* Add SOLRAD data reader to :ref:`iotools`.
31+
* Add EPW data reader to :ref:`iotools`. (:issue:`591`)
2432

2533
Bug fixes
2634
~~~~~~~~~
2735
* Compatibility with pandas 0.24 deprecations. (:issue:`659`)
28-
* pvwatts_ac raised ZeroDivisionError when called with scalar pdc=0
29-
and a RuntimeWarning for array(0) input. Now correctly returns 0s
30-
of the appropriate type. (:issue:`675`)
36+
* :py:func:`~pvlib.pvsystem.pvwatts_ac` raised ``ZeroDivisionError``
37+
when called with scalar ``pdc=0``
38+
and a ``RuntimeWarning`` for ``array(0)`` input. Now correctly returns
39+
0s of the appropriate type. (:issue:`675`)
40+
* Fixed :py:func:`~pvlib.irradiance.erbs` behavior when zenith is
41+
near 90 degrees. (:issue:`681`)
3142

3243

3344
Testing

pvlib/iotools/epw.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ def read_epw(filename, coerce_year=None):
7070
longitude Float site longitude
7171
TZ Float UTC offset
7272
altitude Float site elevation
73-
=============== ====== ========================================
74-
73+
=============== ====== =========================================
7574
7675
7776
============================= ==============================================================================================================================================================

pvlib/irradiance.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,7 +2152,7 @@ def _gti_dirint_gte_90_kt_prime(aoi, solar_zenith, solar_azimuth, times,
21522152
return kt_prime_gte_90
21532153

21542154

2155-
def erbs(ghi, zenith, doy):
2155+
def erbs(ghi, zenith, datetime_or_doy, min_cos_zenith=0.065, max_zenith=87):
21562156
r"""
21572157
Estimate DNI and DHI from GHI using the Erbs model.
21582158
@@ -2179,8 +2179,15 @@ def erbs(ghi, zenith, doy):
21792179
Global horizontal irradiance in W/m^2.
21802180
zenith: numeric
21812181
True (not refraction-corrected) zenith angles in decimal degrees.
2182-
doy: scalar, array or DatetimeIndex
2183-
The day of the year.
2182+
datetime_or_doy : int, float, array, pd.DatetimeIndex
2183+
Day of year or array of days of year e.g.
2184+
pd.DatetimeIndex.dayofyear, or pd.DatetimeIndex.
2185+
min_cos_zenith : numeric, default 0.065
2186+
Minimum value of cos(zenith) to allow when calculating global
2187+
clearness index `kt`. Equivalent to zenith = 86.273 degrees.
2188+
max_zenith : numeric, default 87
2189+
Maximum value of zenith to allow in DNI calculation. DNI will be
2190+
set to 0 for times with zenith values greater than `max_zenith`.
21842191
21852192
Returns
21862193
-------
@@ -2205,14 +2212,10 @@ def erbs(ghi, zenith, doy):
22052212
disc
22062213
"""
22072214

2208-
dni_extra = get_extra_radiation(doy)
2215+
dni_extra = get_extra_radiation(datetime_or_doy)
22092216

2210-
# This Z needs to be the true Zenith angle, not apparent,
2211-
# to get extraterrestrial horizontal radiation)
2212-
i0_h = dni_extra * tools.cosd(zenith)
2213-
2214-
kt = ghi / i0_h
2215-
kt = np.maximum(kt, 0)
2217+
kt = clearness_index(ghi, zenith, dni_extra, min_cos_zenith=min_cos_zenith,
2218+
max_clearness_index=1)
22162219

22172220
# For Kt <= 0.22, set the diffuse fraction
22182221
df = 1 - 0.09*kt
@@ -2229,14 +2232,18 @@ def erbs(ghi, zenith, doy):
22292232
dhi = df * ghi
22302233

22312234
dni = (ghi - dhi) / tools.cosd(zenith)
2235+
bad_values = (zenith > max_zenith) | (ghi < 0) | (dni < 0)
2236+
dni = np.where(bad_values, 0, dni)
2237+
# ensure that closure relationship remains valid
2238+
dhi = np.where(bad_values, ghi, dhi)
22322239

22332240
data = OrderedDict()
22342241
data['dni'] = dni
22352242
data['dhi'] = dhi
22362243
data['kt'] = kt
22372244

2238-
if isinstance(dni, pd.Series):
2239-
data = pd.DataFrame(data)
2245+
if isinstance(datetime_or_doy, pd.DatetimeIndex):
2246+
data = pd.DataFrame(data, index=datetime_or_doy)
22402247

22412248
return data
22422249

pvlib/test/test_irradiance.py

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -620,21 +620,59 @@ def test_gti_dirint():
620620

621621

622622
def test_erbs():
623-
ghi = pd.Series([0, 50, 1000, 1000])
624-
zenith = pd.Series([120, 85, 10, 10])
625-
doy = pd.Series([1, 1, 1, 180])
626-
expected = pd.DataFrame(np.
627-
array([[ -0.00000000e+00, 0.00000000e+00, -0.00000000e+00],
628-
[ 9.67127061e+01, 4.15709323e+01, 4.05715990e-01],
629-
[ 7.94187742e+02, 2.17877755e+02, 7.18119416e-01],
630-
[ 8.42358014e+02, 1.70439297e+02, 7.68919470e-01]]),
631-
columns=['dni', 'dhi', 'kt'])
623+
index = pd.DatetimeIndex(['20190101']*3 + ['20190620'])
624+
ghi = pd.Series([0, 50, 1000, 1000], index=index)
625+
zenith = pd.Series([120, 85, 10, 10], index=index)
626+
expected = pd.DataFrame(np.array(
627+
[[0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
628+
[9.67192672e+01, 4.15703604e+01, 4.05723511e-01],
629+
[7.94205651e+02, 2.17860117e+02, 7.18132729e-01],
630+
[8.42001578e+02, 1.70790318e+02, 7.68214312e-01]]),
631+
columns=['dni', 'dhi', 'kt'], index=index)
632632

633-
out = irradiance.erbs(ghi, zenith, doy)
633+
out = irradiance.erbs(ghi, zenith, index)
634634

635635
assert_frame_equal(np.round(out, 0), np.round(expected, 0))
636636

637637

638+
def test_erbs_min_cos_zenith_max_zenith():
639+
# map out behavior under difficult conditions with various
640+
# limiting kwargs settings
641+
columns = ['dni', 'dhi', 'kt']
642+
times = pd.DatetimeIndex(['2016-07-19 06:11:00'], tz='America/Phoenix')
643+
644+
# max_zenith keeps these results reasonable
645+
out = irradiance.erbs(ghi=1.0, zenith=89.99999,
646+
datetime_or_doy=times, min_cos_zenith=0)
647+
expected = pd.DataFrame(np.array(
648+
[[0., 1., 1.]]),
649+
columns=columns, index=times)
650+
assert_frame_equal(out, expected)
651+
652+
# 4-5 9s will produce bad behavior without max_zenith limit
653+
out = irradiance.erbs(ghi=1.0, zenith=89.99999,
654+
datetime_or_doy=times, max_zenith=100)
655+
expected = pd.DataFrame(np.array(
656+
[[6.00115286e+03, 9.98952601e-01, 1.16377640e-02]]),
657+
columns=columns, index=times)
658+
assert_frame_equal(out, expected)
659+
660+
# 1-2 9s will produce bad behavior without either limit
661+
out = irradiance.erbs(ghi=1.0, zenith=89.99, datetime_or_doy=times,
662+
min_cos_zenith=0, max_zenith=100)
663+
expected = pd.DataFrame(np.array(
664+
[[4.78419761e+03, 1.65000000e-01, 1.00000000e+00]]),
665+
columns=columns, index=times)
666+
assert_frame_equal(out, expected)
667+
668+
# check default behavior under hardest condition
669+
out = irradiance.erbs(ghi=1.0, zenith=90, datetime_or_doy=times)
670+
expected = pd.DataFrame(np.array(
671+
[[0., 1., 0.01163776]]),
672+
columns=columns, index=times)
673+
assert_frame_equal(out, expected)
674+
675+
638676
def test_erbs_all_scalar():
639677
ghi = 1000
640678
zenith = 10

0 commit comments

Comments
 (0)