From e41c057c4b2b84aa4ecb3f988f4c3b8cb4f8db23 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Mon, 8 Jun 2026 11:30:38 +0100 Subject: [PATCH 1/9] Add support for return_components to Reindl --- pvlib/irradiance.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 50f02426de..1a30713fdf 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -873,7 +873,7 @@ def haydavies(surface_tilt, surface_azimuth, dhi, dni, dni_extra, def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, - solar_zenith, solar_azimuth): + solar_zenith, solar_azimuth, return_components=False): r''' Determine the diffuse irradiance from the sky on a tilted surface using the Reindl (1990) model. @@ -912,6 +912,10 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, solar_azimuth : numeric Solar azimuth angles. See :term:`solar_azimuth`. [°] + return_components : bool, default False + Flag used to decide whether to return the calculated diffuse components + or not. + Returns ------- poa_sky_diffuse : numeric @@ -973,16 +977,32 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, HB = dni * cos_solar_zenith HB = np.maximum(HB, 0) - # these are the () and [] sub-terms of the second term of eqn 8 - term1 = 1 - AI - term2 = 0.5 * (1 + tools.cosd(surface_tilt)) + I = (1 + tools.cosd(surface_tilt)) / 2 + with np.errstate(invalid='ignore', divide='ignore'): hb_to_ghi = np.where(ghi == 0, 0, np.divide(HB, ghi)) - term3 = 1 + np.sqrt(hb_to_ghi) * (tools.sind(0.5 * surface_tilt)**3) - sky_diffuse = dhi * (AI * Rb + term1 * term2 * term3) - sky_diffuse = np.maximum(sky_diffuse, 0) + h = np.sqrt(hb_to_ghi) * (tools.sind(surface_tilt / 2) ** 3) - return sky_diffuse + term1 = (1 - AI) * I + term2 = AI * Rb + term3 = (1 - AI) * I * h + + sky_diffuse = dhi * (term1 + term2 + term3) + + if return_components: + diffuse_components = OrderedDict() + diffuse_components['poa_sky_diffuse'] = sky_diffuse + + # Calculate the individual components + diffuse_components['poa_isotropic'] = dhi * term1 + diffuse_components['poa_circumsolar'] = dhi * term2 + diffuse_components['poa_horizon'] = dhi * term3 + + if isinstance(sky_diffuse, pd.Series): + diffuse_components = pd.DataFrame(diffuse_components) + return diffuse_components + else: + return sky_diffuse def king(surface_tilt, dhi, ghi, solar_zenith): From aa361832d878fbccf219040186c3ddbed5bbf749 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Mon, 8 Jun 2026 12:31:18 +0100 Subject: [PATCH 2/9] Update docstrings for Reindl --- pvlib/irradiance.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 1a30713fdf..449b2680dd 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -918,8 +918,21 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, Returns ------- - poa_sky_diffuse : numeric - The sky diffuse component of the solar radiation. [Wm⁻²] + numeric, OrderedDict, or DataFrame + Return type controlled by ``return_components`` argument. + If ``return_components=False``, `sky_diffuse` is returned. + If ``return_components=True``, `diffuse_components` is returned. + + sky_diffuse : numeric + The sky diffuse component of the solar radiation on a tilted + surface. + + diffuse_components : OrderedDict (array input) or DataFrame (Series input) + Keys/columns are: + * poa_sky_diffuse: Total sky diffuse + * poa_isotropic + * poa_circumsolar + * poa_horizon Notes ----- @@ -943,8 +956,9 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, Implementation is based on Loutzenhiser et al. (2007) [3]_, Equation 8. The beam and ground reflectance portion of the equation have been removed, therefore the model described here generates - ONLY the diffuse radiation from the sky and circumsolar, so the form of the - equation varies slightly from Equation 8 in [3]_. + ONLY the diffuse radiation from the sky, circumsolar, and horizon + brightening, so the form of the equation varies slightly from Equation 8 + in [3]_. References ---------- From 11b0f12ca37e822b94be2ecf6ac884b6feb58eae Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Thu, 11 Jun 2026 14:09:31 +0100 Subject: [PATCH 3/9] Fix ambiguous variable name --- pvlib/irradiance.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 449b2680dd..0481731023 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -991,15 +991,15 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, HB = dni * cos_solar_zenith HB = np.maximum(HB, 0) - I = (1 + tools.cosd(surface_tilt)) / 2 + SVF = (1 + tools.cosd(surface_tilt)) / 2 with np.errstate(invalid='ignore', divide='ignore'): hb_to_ghi = np.where(ghi == 0, 0, np.divide(HB, ghi)) h = np.sqrt(hb_to_ghi) * (tools.sind(surface_tilt / 2) ** 3) - term1 = (1 - AI) * I + term1 = (1 - AI) * SVF term2 = AI * Rb - term3 = (1 - AI) * I * h + term3 = (1 - AI) * SVF * h sky_diffuse = dhi * (term1 + term2 + term3) From efb08e39dd719ff0e153e09c6778aca7af124108 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Fri, 12 Jun 2026 10:39:55 +0100 Subject: [PATCH 4/9] Change OrderedDict output to Dict --- pvlib/irradiance.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 0481731023..79af869ea7 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -918,7 +918,7 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, Returns ------- - numeric, OrderedDict, or DataFrame + numeric, Dict, or DataFrame Return type controlled by ``return_components`` argument. If ``return_components=False``, `sky_diffuse` is returned. If ``return_components=True``, `diffuse_components` is returned. @@ -927,7 +927,7 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, The sky diffuse component of the solar radiation on a tilted surface. - diffuse_components : OrderedDict (array input) or DataFrame (Series input) + diffuse_components : Dict (array input) or DataFrame (Series input) Keys/columns are: * poa_sky_diffuse: Total sky diffuse * poa_isotropic @@ -1004,7 +1004,7 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, sky_diffuse = dhi * (term1 + term2 + term3) if return_components: - diffuse_components = OrderedDict() + diffuse_components = Dict() diffuse_components['poa_sky_diffuse'] = sky_diffuse # Calculate the individual components From f38ef5de018b803e3eb3c2ee42cd607fe71dd297 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Fri, 12 Jun 2026 15:09:44 +0100 Subject: [PATCH 5/9] Dict output for return_components=True --- pvlib/irradiance.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 79af869ea7..bd50e6dac2 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -1004,13 +1004,12 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, sky_diffuse = dhi * (term1 + term2 + term3) if return_components: - diffuse_components = Dict() - diffuse_components['poa_sky_diffuse'] = sky_diffuse - - # Calculate the individual components - diffuse_components['poa_isotropic'] = dhi * term1 - diffuse_components['poa_circumsolar'] = dhi * term2 - diffuse_components['poa_horizon'] = dhi * term3 + diffuse_components = { + 'poa_sky_diffuse': sky_diffuse, + 'poa_isotropic': dhi * term1, + 'poa_circumsolar': dhi * term2, + 'poa_horizon': dhi * term3 + } if isinstance(sky_diffuse, pd.Series): diffuse_components = pd.DataFrame(diffuse_components) From d0e6336e2bc6fc80ed0be2ad3f6def11d4113750 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Fri, 12 Jun 2026 15:50:20 +0100 Subject: [PATCH 6/9] Add Reindl components test --- tests/test_irradiance.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_irradiance.py b/tests/test_irradiance.py index a416636ae9..4b98accacc 100644 --- a/tests/test_irradiance.py +++ b/tests/test_irradiance.py @@ -251,6 +251,41 @@ def test_reindl(irrad_data, ephem_data, dni_et): assert_allclose(result, [0., 27.9412, 104.1317, 34.1663], atol=1e-4) +def test_reindl_components(irrad_data, ephem_data, dni_et): + keys = ['poa_sky_diffuse', 'poa_isotropic', 'poa_circumsolar', + 'poa_horizon'] + expected = pd.DataFrame(np.array( + [[0, 27.941170, 104.131724, 34.166258], + [0, 27.177514, 30.181807, 27.983728], + [0, 0, 72.813055, 5.207138], + [0, 0.763656, 1.136862, 0.975393]]).T, + columns=keys, + index=irrad_data.index + ) + # pandas + result = irradiance.reindl( + 40, 180, irrad_data['dhi'], irrad_data['dni'], irrad_data['ghi'], + dni_et, ephem_data['apparent_zenith'], ephem_data['azimuth'], + return_components=True) + assert_frame_equal(result, expected, check_less_precise=4) + # numpy + result = irradiance.reindl( + 40, 180, irrad_data['dhi'].values, irrad_data['dni'].values, + irrad_data['ghi'].values, dni_et, ephem_data['apparent_zenith'].values, + ephem_data['azimuth'].values, return_components=True) + for key in keys: + assert_allclose(result[key], expected[key], atol=1e-4) + assert isinstance(result, dict) + # scalar + result = irradiance.reindl( + 40, 180, irrad_data['dhi'].values[-1], irrad_data['dni'].values[-1], + irrad_data['ghi'].values[-1], dni_et[-1], ephem_data['apparent_zenith'].values[-1], + ephem_data['azimuth'].values[-1], return_components=True) + for key in keys: + assert_allclose(result[key], expected[key].iloc[-1], atol=1e-4) + assert isinstance(result, dict) + + def test_king(irrad_data, ephem_data): result = irradiance.king(40, irrad_data['dhi'], irrad_data['ghi'], ephem_data['apparent_zenith']) From aed9869cd25cce4fefd5fd921664a5cf92274bf8 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Fri, 12 Jun 2026 15:52:30 +0100 Subject: [PATCH 7/9] Fix long line on test --- tests/test_irradiance.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_irradiance.py b/tests/test_irradiance.py index 4b98accacc..efebd3caa2 100644 --- a/tests/test_irradiance.py +++ b/tests/test_irradiance.py @@ -279,8 +279,10 @@ def test_reindl_components(irrad_data, ephem_data, dni_et): # scalar result = irradiance.reindl( 40, 180, irrad_data['dhi'].values[-1], irrad_data['dni'].values[-1], - irrad_data['ghi'].values[-1], dni_et[-1], ephem_data['apparent_zenith'].values[-1], - ephem_data['azimuth'].values[-1], return_components=True) + irrad_data['ghi'].values[-1], dni_et[-1], + ephem_data['apparent_zenith'].values[-1], + ephem_data['azimuth'].values[-1], + return_components=True) for key in keys: assert_allclose(result[key], expected[key].iloc[-1], atol=1e-4) assert isinstance(result, dict) From 0225746b77cd8142c1f1dfe0d06d30887d5ea6d0 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Fri, 12 Jun 2026 15:55:46 +0100 Subject: [PATCH 8/9] Clarify Reindl component formulation on docstrings --- pvlib/irradiance.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index bd50e6dac2..6b0f79dee4 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -960,6 +960,9 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, brightening, so the form of the equation varies slightly from Equation 8 in [3]_. + For clarity, the horizon component in `reindl` corresponds to the term + added on top of the `hay–davies` formulation, on which `reindl` builds. + References ---------- .. [1] Reindl, D. T., Beckmann, W. A., Duffie, J. A., 1990a. Diffuse From 9ad57f54d80d191f9cf56329e92ccf49300acef7 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Mon, 8 Jun 2026 12:31:18 +0100 Subject: [PATCH 9/9] Update docstrings for Reindl --- pvlib/irradiance.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 6b0f79dee4..3efb1ea206 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -918,7 +918,11 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, Returns ------- +<<<<<<< HEAD numeric, Dict, or DataFrame +======= + numeric, OrderedDict, or DataFrame +>>>>>>> 3cbc4adc (Update docstrings for Reindl) Return type controlled by ``return_components`` argument. If ``return_components=False``, `sky_diffuse` is returned. If ``return_components=True``, `diffuse_components` is returned. @@ -927,7 +931,11 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, The sky diffuse component of the solar radiation on a tilted surface. +<<<<<<< HEAD diffuse_components : Dict (array input) or DataFrame (Series input) +======= + diffuse_components : OrderedDict (array input) or DataFrame (Series input) +>>>>>>> 3cbc4adc (Update docstrings for Reindl) Keys/columns are: * poa_sky_diffuse: Total sky diffuse * poa_isotropic @@ -961,7 +969,7 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra, in [3]_. For clarity, the horizon component in `reindl` corresponds to the term - added on top of the `hay–davies` formulation, on which `reindl` builds. + added on top of the `haydavies` formulation, on which `reindl` builds. References ----------