From ef8be37ef8656f88eb2eebde11c090b18847f0fe Mon Sep 17 00:00:00 2001 From: Karel De Brabandere Date: Fri, 24 Mar 2023 11:41:12 +0100 Subject: [PATCH 1/4] =?UTF-8?q?[FIX]=20iam.physical=20returns=20nan=20for?= =?UTF-8?q?=20aoi=20>=2090=C2=B0=20when=20n=20=3D=201=20(#1706)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pvlib/iam.py | 6 ++++++ pvlib/tests/test_iam.py | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/pvlib/iam.py b/pvlib/iam.py index 3eaa6b4c8e..aed1abf8a6 100644 --- a/pvlib/iam.py +++ b/pvlib/iam.py @@ -215,6 +215,12 @@ def physical(aoi, n=1.526, K=4.0, L=0.002, *, n_ar=None): # incidence angle modifier iam = (tau_s + tau_p) / 2 / tau_0 + # for light coming from behind the plane, none can enter the module (necessary only when n2 = 1) + if np.isclose(n2, 1).any(): + iam = np.where(aoi >= 90, 0, iam) + if isinstance(aoi, pd.Series): + iam = pd.Series(iam, index=aoi.index) + return iam diff --git a/pvlib/tests/test_iam.py b/pvlib/tests/test_iam.py index eba1c66cb0..90a383f9ab 100644 --- a/pvlib/tests/test_iam.py +++ b/pvlib/tests/test_iam.py @@ -51,6 +51,13 @@ def test_physical(): assert_series_equal(iam, expected) +def test_physical_n1_L0(): + aoi = np.array([0, 22.5, 45, 67.5, 90, 100, np.nan]) + expected = np.array([1, 1, 1, 1, 0, 0, np.nan]) + iam = _iam.physical(aoi, n=1, L=0) + assert_allclose(iam, expected, equal_nan=True) + + def test_physical_ar(): aoi = np.array([0, 22.5, 45, 67.5, 90, 100, np.nan]) expected = np.array([1, 0.99944171, 0.9917463, 0.91506158, 0, 0, np.nan]) From cc4f1466e7d8677283f5d2273ac9333e05fd02fa Mon Sep 17 00:00:00 2001 From: Karel De Brabandere Date: Fri, 24 Mar 2023 11:57:35 +0100 Subject: [PATCH 2/4] address stickler-ci and codecov alerts --- pvlib/iam.py | 3 ++- pvlib/tests/test_iam.py | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pvlib/iam.py b/pvlib/iam.py index aed1abf8a6..51319b094a 100644 --- a/pvlib/iam.py +++ b/pvlib/iam.py @@ -215,7 +215,8 @@ def physical(aoi, n=1.526, K=4.0, L=0.002, *, n_ar=None): # incidence angle modifier iam = (tau_s + tau_p) / 2 / tau_0 - # for light coming from behind the plane, none can enter the module (necessary only when n2 = 1) + # for light coming from behind the plane, none can enter the module + # when n2 > 1, this is already the case if np.isclose(n2, 1).any(): iam = np.where(aoi >= 90, 0, iam) if isinstance(aoi, pd.Series): diff --git a/pvlib/tests/test_iam.py b/pvlib/tests/test_iam.py index 90a383f9ab..3d017ef7c2 100644 --- a/pvlib/tests/test_iam.py +++ b/pvlib/tests/test_iam.py @@ -57,6 +57,11 @@ def test_physical_n1_L0(): iam = _iam.physical(aoi, n=1, L=0) assert_allclose(iam, expected, equal_nan=True) + aoi = pd.Series(aoi) + expected = pd.Series(expected) + iam = _iam.physical(aoi, n=1, L=0) + assert_series_equal(iam, expected) + def test_physical_ar(): aoi = np.array([0, 22.5, 45, 67.5, 90, 100, np.nan]) From 0cc34aa6e36438e3946a6e6d68f053405f3994c6 Mon Sep 17 00:00:00 2001 From: Kevin Anderson Date: Mon, 12 Jun 2023 11:51:08 -0400 Subject: [PATCH 3/4] suppress obnoxious numpy warnings --- pvlib/iam.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pvlib/iam.py b/pvlib/iam.py index 51319b094a..ca8f89468e 100644 --- a/pvlib/iam.py +++ b/pvlib/iam.py @@ -175,8 +175,12 @@ def physical(aoi, n=1.526, K=4.0, L=0.002, *, n_ar=None): n2costheta2 = n2 * costheta # reflectance of s-, p-polarized, and normal light by the first interface - rho12_s = ((n1costheta1 - n2costheta2) / (n1costheta1 + n2costheta2)) ** 2 - rho12_p = ((n1costheta2 - n2costheta1) / (n1costheta2 + n2costheta1)) ** 2 + with np.errstate(divide='ignore', invalid='ignore'): + rho12_s = \ + ((n1costheta1 - n2costheta2) / (n1costheta1 + n2costheta2)) ** 2 + rho12_p = \ + ((n1costheta2 - n2costheta1) / (n1costheta2 + n2costheta1)) ** 2 + rho12_0 = ((n1 - n2) / (n1 + n2)) ** 2 # transmittance through the first interface @@ -208,8 +212,10 @@ def physical(aoi, n=1.526, K=4.0, L=0.002, *, n_ar=None): tau_0 *= (1 - rho23_0) / (1 - rho23_0 * rho12_0) # transmittance after absorption in the glass - tau_s *= np.exp(-K * L / costheta) - tau_p *= np.exp(-K * L / costheta) + with np.errstate(divide='ignore', invalid='ignore'): + tau_s *= np.exp(-K * L / costheta) + tau_p *= np.exp(-K * L / costheta) + tau_0 *= np.exp(-K * L) # incidence angle modifier From d03704c72329577bff6c9e986383aadfec1c17c1 Mon Sep 17 00:00:00 2001 From: Kevin Anderson Date: Mon, 12 Jun 2023 11:58:04 -0400 Subject: [PATCH 4/4] whatsnew --- docs/sphinx/source/whatsnew/v0.9.6.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.9.6.rst b/docs/sphinx/source/whatsnew/v0.9.6.rst index 889f456e0c..9aececf50d 100644 --- a/docs/sphinx/source/whatsnew/v0.9.6.rst +++ b/docs/sphinx/source/whatsnew/v0.9.6.rst @@ -55,6 +55,9 @@ Bug fixes :py:meth:`pvlib.modelchain.ModelChain.run_model_from_effective_irradiance`. (:issue:`1713`, :pull:`1720`) * ``d2mutau`` and ``NsVbi`` were hardcoded in :py:func:`pvlib.pvsystem.max_power_point` instead of passing through the arguments to the function. (:pull:`1733`) +* :py:func:`pvlib.iam.physical` no longer returns NaN when ``n=1`` and ``aoi>90``. + This bug was introduced in v0.9.5. (:issue:`1706`, :pull:`1707`) + Testing ~~~~~~~