From 0967fe11ac65887bfdc080c85bbda65a7461f06e Mon Sep 17 00:00:00 2001 From: Chas Schweizer Date: Fri, 21 May 2021 16:42:07 +0000 Subject: [PATCH 1/4] Merged PR 13463: Horizon coefficient should be allowed to go negative in the Perez transposition model Horizon coefficient should be allowed to go negative in the Perez transposition model Link to the original paper: https://www.osti.gov/servlets/purl/7024029 Section III.2 states this explicitly for the horizon component: "(2) The horizon brightening coefficient, F2, is negative for overcast and low E occurrences -- indicative of brightening of the zenithal region of the sky for these conditions. This becomes positive past intermediate conditions and increases substantially with clearness." Note that this change also impacts results from the DIRINT model (used to generate DNI from GHI). This change corresponds to revision 8 of the SAM vs PVLib comparison, found here: https://cleanpower1.sharepoint.com/sites/SolarAnywhere/Shared%20Documents/Power%20Model%20Discussions/PVLib_Comparison_Rev8_FixPvLibPerezModelF2Bound.zip --- pvlib/irradiance.py | 1 - pvlib/tests/test_irradiance.py | 48 ++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 4f5cad9572..9e51fbad4f 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1126,7 +1126,6 @@ def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra, F1 = np.maximum(F1, 0) F2 = (F2c[ebin, 0] + F2c[ebin, 1] * delta + F2c[ebin, 2] * z) - F2 = np.maximum(F2, 0) A = aoi_projection(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth) diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index 63479899a7..84c3a0476f 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -248,6 +248,44 @@ def test_perez_components(irrad_data, ephem_data, dni_et, relative_airmass): assert_series_equal(sum_components, expected_for_sum, check_less_precise=2) +def test_perez_negative_horizon(): + times = pd.date_range(start='20190101 11:30:00', freq='1H', periods=5, tz='US/Central') + + # Avoid test dependencies on functionality not being tested by hard-coding the inputs. + # This data corresponds to Goodwin Creek in the afternoon on 1/1/2019. + inputs = pd.DataFrame(np.array( + [[ 158 , 19 , 1 , 0 , 0 ], # DNI on a cloudy day, approaching dusk + [ 249 , 165 , 136 , 93 , 50 ], # DHI for the same periods + [ 57.746951, 57.564205, 60.813841, 66.989435, 75.353368], # apparent zenith + [ 171.003315, 187.346924, 202.974357, 216.725599, 228.317233], # solar azimuth + [1414 ,1414 ,1414 ,1414 ,1414 ], # slightly rounded from irradiance.get_extra_radiation + [ 1.869315, 1.859981, 2.044429, 2.544943, 3.900136] # from atmosphere.get_relative_airmass + ]).T, + columns = ['dni', 'dhi', 'solar_zenith', 'solar_azimuth', 'dni_extra', 'airmass'], + index = times + ) + + out = irradiance.perez(34, 180, inputs['dhi'], inputs['dni'], + inputs['dni_extra'], inputs['solar_zenith'], inputs['solar_azimuth'], inputs['airmass'], + model='allsitescomposite1990', return_components=True) + + expected = pd.DataFrame(np.array( + [[281.410185, 152.20879, 123.867898, 82.836412, 43.517015], # sky_diffuse can be less than isotropic under certain conditions + [166.785419, 142.24475, 119.173875, 83.525150, 45.725931], # isotropic + [113.548755, 16.09757, 9.956174, 3.142467, 0 ], # circumsolar + [ 1.076010, -6.13353, -5.262151, -3.831230, -2.208923]]).T, # horizon can be negative + columns=['sky_diffuse', 'isotropic', 'circumsolar', 'horizon'], + index = times + ) + + expected_for_sum = expected['sky_diffuse'].copy() + sum_components = out.iloc[:, 1:].sum(axis=1) + sum_components.name = 'sky_diffuse' + + assert_frame_equal(out, expected, check_less_precise=2) + assert_series_equal(sum_components, expected_for_sum, check_less_precise=2) + + def test_perez_arrays(irrad_data, ephem_data, dni_et, relative_airmass): dni = irrad_data['dni'].copy() dni.iloc[2] = np.nan @@ -608,7 +646,7 @@ def test_gti_dirint(): expected_col_order = ['ghi', 'dni', 'dhi'] expected = pd.DataFrame(array( [[ 21.05796198, 0. , 21.05796198], - [ 288.22574368, 60.59964218, 245.37532576], + [ 291.40037163, 63.41290679, 246.56067523], [ 931.04078010, 695.94965324, 277.06172442]]), columns=expected_col_order, index=times) @@ -632,7 +670,7 @@ def test_gti_dirint(): expected = pd.DataFrame(array( [[ 21.05796198, 0. , 21.05796198], - [ 289.81109139, 60.52460392, 247.01373353], + [ 293.21310935, 63.27500913, 248.47092131], [ 932.46756378, 648.05001357, 323.49974813]]), columns=expected_col_order, index=times) @@ -646,8 +684,8 @@ def test_gti_dirint(): expected = pd.DataFrame(array( [[ 21.3592591, 0. , 21.3592591 ], - [ 292.5162373, 64.42628826, 246.95997198], - [ 941.6753031, 727.16311901, 258.36548605]]), + [ 294.4985420, 66.25848451, 247.64671830], + [ 941.7943404, 727.50552952, 258.16276278]]), columns=expected_col_order, index=times) assert_frame_equal(output, expected) @@ -660,7 +698,7 @@ def test_gti_dirint(): expected = pd.DataFrame(array( [[ 21.05796198, 0. , 21.05796198], - [ 292.40468994, 36.79559287, 266.3862767 ], + [ 295.06070190, 38.20346345, 268.0467738 ], [ 931.79627208, 689.81549269, 283.5817439]]), columns=expected_col_order, index=times) From fbda9540c8a2d72c7223cfc185c856393db17b38 Mon Sep 17 00:00:00 2001 From: Chas Schweizer Date: Fri, 21 May 2021 21:52:27 +0000 Subject: [PATCH 2/4] Merged PR 13472: Fix reported Stickler issues Fix reported Stickler issues This is an attempt to fix all formatting requirement failures reported by Stickler CI for PR 1239 to the pvlib-python project. Related work items: #18784 --- pvlib/tests/test_irradiance.py | 52 ++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index 84c3a0476f..373e032d13 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -249,33 +249,41 @@ def test_perez_components(irrad_data, ephem_data, dni_et, relative_airmass): def test_perez_negative_horizon(): - times = pd.date_range(start='20190101 11:30:00', freq='1H', periods=5, tz='US/Central') - - # Avoid test dependencies on functionality not being tested by hard-coding the inputs. - # This data corresponds to Goodwin Creek in the afternoon on 1/1/2019. + times = pd.date_range(start='20190101 11:30:00', freq='1H', + periods=5, tz='US/Central') + + # Avoid test dependencies on functionality not being tested by hard-coding + # the inputs. This data corresponds to Goodwin Creek in the afternoon on + # 1/1/2019. + # dni_e is slightly rounded from irradiance.get_extra_radiation + # airmass from atmosphere.get_relative_airmas inputs = pd.DataFrame(np.array( - [[ 158 , 19 , 1 , 0 , 0 ], # DNI on a cloudy day, approaching dusk - [ 249 , 165 , 136 , 93 , 50 ], # DHI for the same periods - [ 57.746951, 57.564205, 60.813841, 66.989435, 75.353368], # apparent zenith - [ 171.003315, 187.346924, 202.974357, 216.725599, 228.317233], # solar azimuth - [1414 ,1414 ,1414 ,1414 ,1414 ], # slightly rounded from irradiance.get_extra_radiation - [ 1.869315, 1.859981, 2.044429, 2.544943, 3.900136] # from atmosphere.get_relative_airmass - ]).T, - columns = ['dni', 'dhi', 'solar_zenith', 'solar_azimuth', 'dni_extra', 'airmass'], - index = times + [[ 158, 19, 1, 0, 0], + [ 249, 165, 136, 93, 50], + [ 57.746951, 57.564205, 60.813841, 66.989435, 75.353368], + [ 171.003315, 187.346924, 202.974357, 216.725599, 228.317233], + [1414, 1414, 1414, 1414, 1414], + [ 1.869315, 1.859981, 2.044429, 2.544943, 3.900136]]).T, + columns=['dni', 'dhi', 'solar_zenith', + 'solar_azimuth', 'dni_extra', 'airmass'], + index=times ) - out = irradiance.perez(34, 180, inputs['dhi'], inputs['dni'], - inputs['dni_extra'], inputs['solar_zenith'], inputs['solar_azimuth'], inputs['airmass'], - model='allsitescomposite1990', return_components=True) + out = irradiance.perez(34, 180, inputs['dhi'], inputs['dni'], + inputs['dni_extra'], inputs['solar_zenith'], + inputs['solar_azimuth'], inputs['airmass'], + model='allsitescomposite1990', + return_components=True) + # sky_diffuse can be less than isotropic under certain conditions as + # horizon goes negative expected = pd.DataFrame(np.array( - [[281.410185, 152.20879, 123.867898, 82.836412, 43.517015], # sky_diffuse can be less than isotropic under certain conditions - [166.785419, 142.24475, 119.173875, 83.525150, 45.725931], # isotropic - [113.548755, 16.09757, 9.956174, 3.142467, 0 ], # circumsolar - [ 1.076010, -6.13353, -5.262151, -3.831230, -2.208923]]).T, # horizon can be negative + [[281.410185, 152.20879, 123.867898, 82.836412, 43.517015], + [166.785419, 142.24475, 119.173875, 83.525150, 45.725931], + [113.548755, 16.09757, 9.956174, 3.142467, 0], + [ 1.076010, -6.13353, -5.262151, -3.831230, -2.208923]]).T, columns=['sky_diffuse', 'isotropic', 'circumsolar', 'horizon'], - index = times + index=times ) expected_for_sum = expected['sky_diffuse'].copy() @@ -698,7 +706,7 @@ def test_gti_dirint(): expected = pd.DataFrame(array( [[ 21.05796198, 0. , 21.05796198], - [ 295.06070190, 38.20346345, 268.0467738 ], + [ 295.06070190, 38.20346345, 268.0467738], [ 931.79627208, 689.81549269, 283.5817439]]), columns=expected_col_order, index=times) From 19b3f988e9943819bad57627f88dd3760ddd2f45 Mon Sep 17 00:00:00 2001 From: Chas Schweizer Date: Sat, 22 May 2021 00:06:02 +0000 Subject: [PATCH 3/4] Merged PR 13479: Final changes to meet formatting requirements I was shown how to validate this locally and fixed all issues, so this should be the last round. I couldn't cherry pick due to conflicts. but this shouldn't include any other changes. Related work items: #18784 --- pvlib/tests/test_irradiance.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index 373e032d13..3b52d5e476 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -249,33 +249,33 @@ def test_perez_components(irrad_data, ephem_data, dni_et, relative_airmass): def test_perez_negative_horizon(): - times = pd.date_range(start='20190101 11:30:00', freq='1H', + times = pd.date_range(start='20190101 11:30:00', freq='1H', periods=5, tz='US/Central') - # Avoid test dependencies on functionality not being tested by hard-coding - # the inputs. This data corresponds to Goodwin Creek in the afternoon on + # Avoid test dependencies on functionality not being tested by hard-coding + # the inputs. This data corresponds to Goodwin Creek in the afternoon on # 1/1/2019. # dni_e is slightly rounded from irradiance.get_extra_radiation - # airmass from atmosphere.get_relative_airmas + # airmass from atmosphere.get_relative_airmas inputs = pd.DataFrame(np.array( - [[ 158, 19, 1, 0, 0], - [ 249, 165, 136, 93, 50], + [[ 158, 19, 1, 0, 0], + [ 249, 165, 136, 93, 50], [ 57.746951, 57.564205, 60.813841, 66.989435, 75.353368], [ 171.003315, 187.346924, 202.974357, 216.725599, 228.317233], [1414, 1414, 1414, 1414, 1414], [ 1.869315, 1.859981, 2.044429, 2.544943, 3.900136]]).T, - columns=['dni', 'dhi', 'solar_zenith', - 'solar_azimuth', 'dni_extra', 'airmass'], + columns=['dni', 'dhi', 'solar_zenith', + 'solar_azimuth', 'dni_extra', 'airmass'], index=times ) out = irradiance.perez(34, 180, inputs['dhi'], inputs['dni'], inputs['dni_extra'], inputs['solar_zenith'], inputs['solar_azimuth'], inputs['airmass'], - model='allsitescomposite1990', + model='allsitescomposite1990', return_components=True) - # sky_diffuse can be less than isotropic under certain conditions as + # sky_diffuse can be less than isotropic under certain conditions as # horizon goes negative expected = pd.DataFrame(np.array( [[281.410185, 152.20879, 123.867898, 82.836412, 43.517015], @@ -705,7 +705,7 @@ def test_gti_dirint(): temp_dew=temp_dew) expected = pd.DataFrame(array( - [[ 21.05796198, 0. , 21.05796198], + [[ 21.05796198, 0., 21.05796198], [ 295.06070190, 38.20346345, 268.0467738], [ 931.79627208, 689.81549269, 283.5817439]]), columns=expected_col_order, index=times) From 26262634a20b72588f5fb1d68b7603c16faabe5e Mon Sep 17 00:00:00 2001 From: cpr-chas <81724637+cpr-chas@users.noreply.github.com> Date: Mon, 24 May 2021 14:10:23 -0700 Subject: [PATCH 4/4] Add appropriate details of the bug fix to the 'what's new' content. --- docs/sphinx/source/whatsnew/v0.9.0.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.9.0.rst b/docs/sphinx/source/whatsnew/v0.9.0.rst index 240fad9c03..c0fdb47d3e 100644 --- a/docs/sphinx/source/whatsnew/v0.9.0.rst +++ b/docs/sphinx/source/whatsnew/v0.9.0.rst @@ -133,6 +133,12 @@ Enhancements Bug fixes ~~~~~~~~~ +* Corrected an error in :py:func:`~pvlib.irradiance.perez` where the horizon + irradiance component was prevented from taking negative values. Negative + values are intentional according to the original publication. Changes in + output are expected to be small and primarily occur at low irradiance + conditions. + (:issue:`1238`, :pull:`1239`) * Pass weather data to solar position calculations in :py:meth:`~pvlib.modelchain.ModelChain.prepare_inputs_from_poa`. (:issue:`1065`, :pull:`1140`) @@ -176,3 +182,4 @@ Contributors * Tony Lorenzo (:ghuser:`alorenzo175`) * Damjan Postolovski (:ghuser:`dpostolovski`) * Joe Ranalli (:ghuser:`jranalli`) +* Chas Schweizer (:ghuser:`cpr-chas`)