From b297c91fb2f49ebc22b2b10446cab710aafa7fb0 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Thu, 23 Jul 2026 10:53:31 +0100 Subject: [PATCH 1/9] Add support for schlick to ModelChain --- docs/sphinx/source/whatsnew/v0.15.3.rst | 2 ++ pvlib/iam.py | 3 ++- pvlib/modelchain.py | 12 ++++++++++-- pvlib/pvsystem.py | 4 ++-- tests/test_modelchain.py | 4 ++-- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.15.3.rst b/docs/sphinx/source/whatsnew/v0.15.3.rst index 23d89b06e5..565b2b38f2 100644 --- a/docs/sphinx/source/whatsnew/v0.15.3.rst +++ b/docs/sphinx/source/whatsnew/v0.15.3.rst @@ -19,6 +19,8 @@ Bug fixes Enhancements ~~~~~~~~~~~~ * Ensure all timezones are available in all OSs. (:issue:`2795`, :pull:`2809`) +* Add support for :py:func:`pvlib.iam.schlick` to :py:class:`pvlib.modelchain.ModelChain` +(:issue:`2828`) Documentation diff --git a/pvlib/iam.py b/pvlib/iam.py index 161de84589..c4981d7273 100644 --- a/pvlib/iam.py +++ b/pvlib/iam.py @@ -21,7 +21,8 @@ 'physical': {'n', 'K', 'L'}, 'martin_ruiz': {'a_r'}, 'sapm': {'B0', 'B1', 'B2', 'B3', 'B4', 'B5'}, - 'interp': {'theta_ref', 'iam_ref'} + 'interp': {'theta_ref', 'iam_ref'}, + 'schlick': set() } diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 45374bd6fa..c599bd6b2c 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -336,8 +336,8 @@ class ModelChain: If not specified, the model will be inferred from the parameters that are common to all of system.arrays[i].module_parameters. Valid strings are 'physical', 'ashrae', 'sapm', 'martin_ruiz', - 'interp' and 'no_loss'. The ModelChain instance will be passed as the - first argument to a user-defined function. + 'schlick', 'interp' and 'no_loss'. The ModelChain instance will be + passed as the first argument to a user-defined function. spectral_model : str or function, optional Valid strings are: @@ -787,6 +787,8 @@ def aoi_model(self, model): self._aoi_model = self.sapm_aoi_loss elif model == 'martin_ruiz': self._aoi_model = self.martin_ruiz_aoi_loss + elif model == 'schlick': + self._aoi_model = self.schlick_aoi_loss elif model == 'interp': self._aoi_model = self.interp_aoi_loss elif model == 'no_loss': @@ -846,6 +848,12 @@ def martin_ruiz_aoi_loss(self): ) return self + def schlick_aoi_loss(self): + self.results.aoi_modifier = self.system.get_iam( + self.results.aoi, iam_model='schlick' + ) + return self + def interp_aoi_loss(self): self.results.aoi_modifier = self.system.get_iam( self.results.aoi, diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 3e39012a8f..417a683dc3 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1190,7 +1190,7 @@ def get_iam(self, aoi, iam_model='physical'): iam_model : string, default 'physical' The IAM model to be used. Valid strings are 'physical', 'ashrae', - 'martin_ruiz', 'sapm' and 'interp'. + 'martin_ruiz', 'sapm', 'interp' and 'schlick'. Returns ------- @@ -1203,7 +1203,7 @@ def get_iam(self, aoi, iam_model='physical'): if `iam_model` is not a valid model name. """ model = iam_model.lower() - if model in ['ashrae', 'physical', 'martin_ruiz', 'interp']: + if model in ['ashrae', 'physical', 'martin_ruiz', 'interp', 'schlick']: func = getattr(iam, model) # get function at pvlib.iam # get all parameters from function signature to retrieve them from # module_parameters if present diff --git a/tests/test_modelchain.py b/tests/test_modelchain.py index 9ea804f014..880427dde0 100644 --- a/tests/test_modelchain.py +++ b/tests/test_modelchain.py @@ -1451,7 +1451,7 @@ def constant_aoi_loss(mc): @pytest.mark.parametrize('aoi_model', [ - 'sapm', 'ashrae', 'physical', 'martin_ruiz' + 'sapm', 'ashrae', 'physical', 'martin_ruiz', 'schlick' ]) def test_aoi_models(sapm_dc_snl_ac_system, location, aoi_model, weather, mocker): @@ -1467,7 +1467,7 @@ def test_aoi_models(sapm_dc_snl_ac_system, location, aoi_model, @pytest.mark.parametrize('aoi_model', [ - 'sapm', 'ashrae', 'physical', 'martin_ruiz' + 'sapm', 'ashrae', 'physical', 'martin_ruiz', 'schlick' ]) def test_aoi_models_singleon_weather_single_array( sapm_dc_snl_ac_system, location, aoi_model, weather): From e47f431bf94aac116762194c3931f4ca5f9da1a9 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Thu, 23 Jul 2026 10:59:17 +0100 Subject: [PATCH 2/9] Edit whatsnew entry --- docs/sphinx/source/whatsnew/v0.15.3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.15.3.rst b/docs/sphinx/source/whatsnew/v0.15.3.rst index 565b2b38f2..f05f4f8216 100644 --- a/docs/sphinx/source/whatsnew/v0.15.3.rst +++ b/docs/sphinx/source/whatsnew/v0.15.3.rst @@ -20,7 +20,7 @@ Enhancements ~~~~~~~~~~~~ * Ensure all timezones are available in all OSs. (:issue:`2795`, :pull:`2809`) * Add support for :py:func:`pvlib.iam.schlick` to :py:class:`pvlib.modelchain.ModelChain` -(:issue:`2828`) +(:issue:`2828`, :pull:`2832`) Documentation From f0b8beedcf6542ad0deb097b9c69d4b3700f2239 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Thu, 23 Jul 2026 10:59:47 +0100 Subject: [PATCH 3/9] Edit whatsnew entry --- docs/sphinx/source/whatsnew/v0.15.3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.15.3.rst b/docs/sphinx/source/whatsnew/v0.15.3.rst index f05f4f8216..02f834cb48 100644 --- a/docs/sphinx/source/whatsnew/v0.15.3.rst +++ b/docs/sphinx/source/whatsnew/v0.15.3.rst @@ -20,7 +20,7 @@ Enhancements ~~~~~~~~~~~~ * Ensure all timezones are available in all OSs. (:issue:`2795`, :pull:`2809`) * Add support for :py:func:`pvlib.iam.schlick` to :py:class:`pvlib.modelchain.ModelChain` -(:issue:`2828`, :pull:`2832`) + (:issue:`2828`, :pull:`2832`) Documentation From 953729e206b24237f67e4252ee592ba5b72ab863 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Thu, 23 Jul 2026 14:45:55 +0100 Subject: [PATCH 4/9] Remove schlick from _IAM_MODEL_PARAMS --- pvlib/iam.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pvlib/iam.py b/pvlib/iam.py index c4981d7273..161de84589 100644 --- a/pvlib/iam.py +++ b/pvlib/iam.py @@ -21,8 +21,7 @@ 'physical': {'n', 'K', 'L'}, 'martin_ruiz': {'a_r'}, 'sapm': {'B0', 'B1', 'B2', 'B3', 'B4', 'B5'}, - 'interp': {'theta_ref', 'iam_ref'}, - 'schlick': set() + 'interp': {'theta_ref', 'iam_ref'} } From 9109bba6a5592987c67c7ad22d1a7269160b99cb Mon Sep 17 00:00:00 2001 From: cbcrespo <97249533+cbcrespo@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:11:57 +0100 Subject: [PATCH 5/9] Apply suggestion from @RDaxini Co-authored-by: Rajiv Daxini <143435106+RDaxini@users.noreply.github.com> --- pvlib/pvsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 417a683dc3..ca7da19ce5 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -397,7 +397,7 @@ def get_iam(self, aoi, iam_model='physical'): iam_model : string, default 'physical' The IAM model to be used. Valid strings are 'physical', 'ashrae', - 'martin_ruiz', 'sapm' and 'interp'. + 'martin_ruiz', 'sapm', 'interp', and 'schlick'. Returns ------- iam : numeric or tuple of numeric From 81ad3bcb6b10cb4aa796bba663c61aaafbba1688 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Thu, 6 Aug 2026 10:15:38 +0100 Subject: [PATCH 6/9] Move to v0.16.0 --- docs/sphinx/source/whatsnew/v0.16.0.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.16.0.rst b/docs/sphinx/source/whatsnew/v0.16.0.rst index 69283b27ce..31a481ca0a 100644 --- a/docs/sphinx/source/whatsnew/v0.16.0.rst +++ b/docs/sphinx/source/whatsnew/v0.16.0.rst @@ -35,6 +35,8 @@ Enhancements * Map beam horizontal irradiance to ``bhi`` when :py:func:`~pvlib.iotools.get_era5` is called with ``map_variables=True``. (:pull:`2819`) +* Add support for :py:func:`pvlib.iam.schlick` to :py:class:`pvlib.modelchain.ModelChain` + (:issue:`2828`, :pull:`2832`) Documentation ~~~~~~~~~~~~~ From 4ea1a645a46f6c373897a1d0436f9e21aa517b10 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Thu, 6 Aug 2026 10:30:08 +0100 Subject: [PATCH 7/9] Add warning to schlick and schlick_diffuse --- pvlib/iam.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pvlib/iam.py b/pvlib/iam.py index a540dca0ca..9ba981c5ea 100644 --- a/pvlib/iam.py +++ b/pvlib/iam.py @@ -816,6 +816,10 @@ def schlick(aoi): integrable alternative to the Fresnel equations for estimating IAM for diffuse irradiance [2]_ (see :py:func:`schlick_diffuse`). + .. warning:: The Schlick IAM model has not been validated for PV + performance modeling and is not commonly used in PV applications. + Users should consider these limitations when selecting models. + Parameters ---------- aoi : numeric @@ -874,6 +878,10 @@ def schlick_diffuse(surface_tilt): This function implements the integration of the Schlick approximation provided by Xie et al. [2]_. + .. warning:: The Schlick IAM model has not been validated for PV + performance modeling and is not commonly used in PV applications. + Users should consider these limitations when selecting models. + Parameters ---------- surface_tilt : numeric From 9a07cb49c9010c373b19c31f17956c8d67c58bd5 Mon Sep 17 00:00:00 2001 From: cbcrespo Date: Fri, 7 Aug 2026 11:26:45 +0100 Subject: [PATCH 8/9] Add PVSystem test --- pvlib/modelchain.py | 4 ++++ tests/test_pvsystem.py | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index df12f30c48..cf7a5bf7e5 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -812,6 +812,10 @@ def infer_aoi_model(self): return self.martin_ruiz_aoi_loss elif iam._IAM_MODEL_PARAMS['interp'] <= params: return self.interp_aoi_loss + # 'schlick' is intentionally excluded from inference. Since it + # requires no parameters, it would always match and effectively + # become the default, which is undesirable because it is not + # commonly used for PV applications. else: raise ValueError('could not infer AOI model from ' 'system.arrays[i].module_parameters. Check that ' diff --git a/tests/test_pvsystem.py b/tests/test_pvsystem.py index 22a7789537..78aaed70c4 100644 --- a/tests/test_pvsystem.py +++ b/tests/test_pvsystem.py @@ -75,6 +75,15 @@ def test_PVSystem_get_iam_interp(mocker): spy.assert_called_once_with(aoi[0], **interp_module_params) +def test_PVSystem_get_iam_schlick(mocker): + system = pvsystem.PVSystem() + mocker.spy(_iam, 'schlick') + aoi = 0 + out = system.get_iam(aoi, 'schlick') + _iam.schlick.assert_called_once_with(aoi) + assert_allclose(out, 1.0, atol=0.01) + + def test__normalize_sam_product_names(): BAD_NAMES = [' -.()[]:+/",', 'Module[1]'] From 543fcf7c45aaad1e16c52e027db76131bb2f5fee Mon Sep 17 00:00:00 2001 From: cbcrespo <97249533+cbcrespo@users.noreply.github.com> Date: Fri, 7 Aug 2026 11:36:55 +0100 Subject: [PATCH 9/9] Update docs/sphinx/source/whatsnew/v0.16.0.rst Co-authored-by: Kevin Anderson --- docs/sphinx/source/whatsnew/v0.16.0.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.16.0.rst b/docs/sphinx/source/whatsnew/v0.16.0.rst index 31a481ca0a..ef9a42de2a 100644 --- a/docs/sphinx/source/whatsnew/v0.16.0.rst +++ b/docs/sphinx/source/whatsnew/v0.16.0.rst @@ -35,7 +35,8 @@ Enhancements * Map beam horizontal irradiance to ``bhi`` when :py:func:`~pvlib.iotools.get_era5` is called with ``map_variables=True``. (:pull:`2819`) -* Add support for :py:func:`pvlib.iam.schlick` to :py:class:`pvlib.modelchain.ModelChain` +* Add support for :py:func:`pvlib.iam.schlick` to :py:class:`pvlib.modelchain.ModelChain`, + :py:meth:`pvlib.pvsystem.PVSystem.get_iam`, and :py:meth:`pvlib.pvsystem.Array.get_iam`. (:issue:`2828`, :pull:`2832`) Documentation