diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index 1fcd8271b6..30b9f576b0 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -7,7 +7,9 @@ v0.10.3 (Anticipated December, 2023) Enhancements ~~~~~~~~~~~~ - +* :py:func:`pvlib.bifacial.infinite_sheds.get_irradiance` and + :py:func:`pvlib.bifacial.infinite_sheds.get_irradiance_poa` now include + shaded fraction in returned variables. (:pull:`1871`) Bug fixes ~~~~~~~~~ @@ -26,3 +28,4 @@ Contributors ~~~~~~~~~~~~ * Arjan Keeman (:ghuser:`akeeman`) * Miguel Sánchez de León Peque (:ghuser:`Peque`) +* Will Hobbs (:ghuser:`williamhobbs`) \ No newline at end of file diff --git a/pvlib/bifacial/infinite_sheds.py b/pvlib/bifacial/infinite_sheds.py index 7d84d4c3f4..9f8a3787ae 100644 --- a/pvlib/bifacial/infinite_sheds.py +++ b/pvlib/bifacial/infinite_sheds.py @@ -276,6 +276,8 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith, [W/m^2] - ``poa_ground_diffuse`` : total ground-reflected diffuse irradiance on the plane of array. [W/m^2] + - ``shaded_fraction`` : fraction of row slant height from the bottom that + is shaded from direct irradiance by adjacent rows. [unitless] References ---------- @@ -369,7 +371,7 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith, output = { 'poa_global': poa_global, 'poa_direct': poa_direct, 'poa_diffuse': poa_diffuse, 'poa_ground_diffuse': poa_gnd_pv, - 'poa_sky_diffuse': poa_sky_pv} + 'poa_sky_diffuse': poa_sky_pv, 'shaded_fraction': f_x} if isinstance(poa_global, pd.Series): output = pd.DataFrame(output) return output @@ -502,6 +504,9 @@ def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, cells from the front surface. [W/m^2] - ``poa_front_ground_diffuse`` : ground-reflected diffuse irradiance reaching the module cells from the front surface. [W/m^2] + - ``shaded_fraction_front`` : fraction of row slant height from the bottom + that is shaded from direct irradiance on the front surface by adjacent + rows. [unitless] - ``poa_back_direct`` : direct irradiance reaching the module cells from the back surface. [W/m^2] - ``poa_back_diffuse`` : total diffuse irradiance reaching the module @@ -510,6 +515,9 @@ def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, cells from the back surface. [W/m^2] - ``poa_back_ground_diffuse`` : ground-reflected diffuse irradiance reaching the module cells from the back surface. [W/m^2] + - ``shaded_fraction_back`` : fraction of row slant height from the bottom + that is shaded from direct irradiance on the back surface by adjacent + rows. [unitless] References ---------- @@ -545,6 +553,7 @@ def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, 'poa_diffuse': 'poa_front_diffuse', 'poa_sky_diffuse': 'poa_front_sky_diffuse', 'poa_ground_diffuse': 'poa_front_ground_diffuse', + 'shaded_fraction': 'shaded_fraction_front', } colmap_back = { 'poa_global': 'poa_back', @@ -552,6 +561,7 @@ def get_irradiance(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, 'poa_diffuse': 'poa_back_diffuse', 'poa_sky_diffuse': 'poa_back_sky_diffuse', 'poa_ground_diffuse': 'poa_back_ground_diffuse', + 'shaded_fraction': 'shaded_fraction_back', } if isinstance(ghi, pd.Series): diff --git a/pvlib/tests/bifacial/test_infinite_sheds.py b/pvlib/tests/bifacial/test_infinite_sheds.py index 017b15f943..1f6dadfd5f 100644 --- a/pvlib/tests/bifacial/test_infinite_sheds.py +++ b/pvlib/tests/bifacial/test_infinite_sheds.py @@ -100,9 +100,11 @@ def test_get_irradiance_poa(): expected_diffuse = np.array([300.]) expected_direct = np.array([700.]) expected_global = expected_diffuse + expected_direct + expected_shaded_fraction = np.array([0.]) assert np.isclose(res['poa_global'], expected_global) assert np.isclose(res['poa_diffuse'], expected_diffuse) assert np.isclose(res['poa_direct'], expected_direct) + assert np.isclose(res['shaded_fraction'], expected_shaded_fraction) # vector inputs surface_tilt = np.array([0., 0., 0., 0.]) height = 1. @@ -115,6 +117,8 @@ def test_get_irradiance_poa(): expected_direct = np.array( [700., 350. * np.sqrt(2), 350. * np.sqrt(2), 0.]) expected_global = expected_diffuse + expected_direct + expected_shaded_fraction = np.array( + [0., 0., 0., 0.]) res = infinite_sheds.get_irradiance_poa( surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, gcr, height, pitch, ghi, dhi, dni, @@ -122,6 +126,7 @@ def test_get_irradiance_poa(): assert np.allclose(res['poa_global'], expected_global) assert np.allclose(res['poa_diffuse'], expected_diffuse) assert np.allclose(res['poa_direct'], expected_direct) + assert np.allclose(res['shaded_fraction'], expected_shaded_fraction) # series inputs surface_tilt = pd.Series(surface_tilt) surface_azimuth = pd.Series(data=surface_azimuth, index=surface_tilt.index) @@ -133,15 +138,19 @@ def test_get_irradiance_poa(): data=expected_direct, index=surface_tilt.index) expected_global = expected_diffuse + expected_direct expected_global.name = 'poa_global' # to match output Series + expected_shaded_fraction = pd.Series( + data=expected_shaded_fraction, index=surface_tilt.index) + expected_shaded_fraction.name = 'shaded_fraction' # to match output Series res = infinite_sheds.get_irradiance_poa( surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, gcr, height, pitch, ghi, dhi, dni, albedo, iam=iam, npoints=npoints) assert isinstance(res, pd.DataFrame) assert_series_equal(res['poa_global'], expected_global) + assert_series_equal(res['shaded_fraction'], expected_shaded_fraction) assert all(k in res.columns for k in [ 'poa_global', 'poa_diffuse', 'poa_direct', 'poa_ground_diffuse', - 'poa_sky_diffuse']) + 'poa_sky_diffuse', 'shaded_fraction']) def test__backside_tilt(): @@ -177,10 +186,16 @@ def test_get_irradiance(vectorize): expected_front_diffuse = np.array([300.]) expected_front_direct = np.array([700.]) expected_front_global = expected_front_diffuse + expected_front_direct + expected_shaded_fraction_front = np.array([0.]) + expected_shaded_fraction_back = np.array([0.]) assert np.isclose(result['poa_front'], expected_front_global) assert np.isclose(result['poa_front_diffuse'], expected_front_diffuse) assert np.isclose(result['poa_front_direct'], expected_front_direct) assert np.isclose(result['poa_global'], result['poa_front']) + assert np.isclose(result['shaded_fraction_front'], + expected_shaded_fraction_front) + assert np.isclose(result['shaded_fraction_back'], + expected_shaded_fraction_back) # series inputs ghi = pd.Series([1000., 500., 500., np.nan]) dhi = pd.Series([300., 500., 500., 500.], index=ghi.index) @@ -200,7 +215,12 @@ def test_get_irradiance(vectorize): expected_poa_global = pd.Series( [1000., 500., result_front['poa_global'][2] * (1 + 0.8 * 0.98), np.nan], index=ghi.index, name='poa_global') + expected_shaded_fraction = pd.Series( + result_front['shaded_fraction'], index=ghi.index, + name='shaded_fraction_front') assert_series_equal(result['poa_global'], expected_poa_global) + assert_series_equal(result['shaded_fraction_front'], + expected_shaded_fraction) def test_get_irradiance_limiting_gcr(): @@ -230,6 +250,8 @@ def test_get_irradiance_limiting_gcr(): expected_direct = np.array([0.]) expected_diffuse = expected_ground_diffuse + expected_sky_diffuse expected_poa = expected_diffuse + expected_direct + expected_shaded_fraction_front = np.array([0.]) + expected_shaded_fraction_back = np.array([0.]) assert np.isclose(result['poa_front'], expected_poa, rtol=0.01) assert np.isclose(result['poa_front_diffuse'], expected_diffuse, rtol=0.01) assert np.isclose(result['poa_front_direct'], expected_direct) @@ -244,6 +266,10 @@ def test_get_irradiance_limiting_gcr(): result['poa_back_sky_diffuse']) assert np.isclose(result['poa_front_ground_diffuse'], result['poa_back_ground_diffuse']) + assert np.isclose(result['shaded_fraction_front'], + expected_shaded_fraction_front) + assert np.isclose(result['shaded_fraction_back'], + expected_shaded_fraction_back) def test_get_irradiance_with_haydavies(): @@ -272,10 +298,16 @@ def test_get_irradiance_with_haydavies(): expected_front_diffuse = np.array([151.38]) expected_front_direct = np.array([848.62]) expected_front_global = expected_front_diffuse + expected_front_direct + expected_shaded_fraction_front = np.array([0.]) + expected_shaded_fraction_back = np.array([0.]) assert np.isclose(result['poa_front'], expected_front_global) assert np.isclose(result['poa_front_diffuse'], expected_front_diffuse) assert np.isclose(result['poa_front_direct'], expected_front_direct) assert np.isclose(result['poa_global'], result['poa_front']) + assert np.isclose(result['shaded_fraction_front'], + expected_shaded_fraction_front) + assert np.isclose(result['shaded_fraction_back'], + expected_shaded_fraction_back) # test for when dni_extra is not supplied with pytest.raises(ValueError, match='supply dni_extra for haydavies'): result = infinite_sheds.get_irradiance(