From 55c95305c63b26b6476f35b1551581332765dd5b Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 23 Feb 2024 15:01:55 -0700 Subject: [PATCH 01/13] add fit_huld --- pvlib/pvarray.py | 31 +++++++++++++++++++++++++++++++ pvlib/tests/test_pvarray.py | 15 +++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index a8bbd36eb3..774724f2f3 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -11,6 +11,7 @@ import numpy as np from scipy.optimize import curve_fit from scipy.special import exp10 +import statsmodels.api as sm def pvefficiency_adr(effective_irradiance, temp_cell, @@ -348,3 +349,33 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): k[2] * tprime + k[3] * tprime * logGprime + k[4] * tprime * logGprime**2 + k[5] * tprime**2) return pdc + + +def _build_iec61853(): + ee = np.array([100, 100, 200, 200, 400, 400, 400, 600, 600, 600, 600, + 800, 800, 800, 800, 1000, 1000, 1000, 1000, 1100, 1100, + 1100]).T + tc = np.array([15, 25, 15, 25, 15, 25, 50, 15, 25, 50, 75, + 15, 25, 50, 75, 15, 25, 50, 75, 25, 50, 75]).T + return ee, tc + + +def fit_huld(effective_irradiance, temp_mod, pdc): + gprime = effective_irradiance / 1000 + tprime = temp_mod - 25 + # accomodate gprime<=0 + with np.errstate(divide='ignore'): + logGprime = np.log(gprime, out=np.zeros_like(gprime), + where=np.array(gprime > 0)) + Y = np.divide(pdc, gprime, out=np.zeros_like(gprime), + where=np.array(gprime > 0)) + + X = np.stack((logGprime, logGprime**2, tprime, tprime*logGprime, + tprime*logGprime**2, tprime**2), axis=0).T + X = sm.add_constant(X) + + rlm_model = sm.RLM(Y, X) + rlm_result = rlm_model.fit() + pdc0 = rlm_result.params[0] + k = rlm_result.params[1:] + return pdc0, k diff --git a/pvlib/tests/test_pvarray.py b/pvlib/tests/test_pvarray.py index 693ef78b2a..a059373655 100644 --- a/pvlib/tests/test_pvarray.py +++ b/pvlib/tests/test_pvarray.py @@ -69,3 +69,18 @@ def test_huld(): with pytest.raises(ValueError, match='Either k or cell_type must be specified'): res = pvarray.huld(1000, 25, 100) + + +def test_fit_huld(): + # test is to recover the parameters in _infer_huld_k for each cell type + # IEC61853 conditions to make data for fitting + ee, tc = pvarray._build_iec61853() + techs = ['csi', 'cis', 'cdte'] + pdc0 = 250 + for tech in techs: + k0 = pvarray._infer_k_huld(tech, pdc0) + pdc = pvarray.huld(ee, tc, pdc0, cell_type=tech) + m_pdc0, k = pvarray.fit_huld(ee, tc, pdc) + expected = np.array([pdc0,] + [v for v in k0], dtype=float) + modeled = np.hstack((m_pdc0, k)) + assert_allclose(expected, modeled, rtol=1e-8) From 119b0b8c5001e6f6ffca3eb87154de27302fec8b Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 23 Feb 2024 15:04:37 -0700 Subject: [PATCH 02/13] docs --- docs/sphinx/source/reference/pv_modeling/system_models.rst | 1 + docs/sphinx/source/whatsnew/v0.10.4.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/sphinx/source/reference/pv_modeling/system_models.rst b/docs/sphinx/source/reference/pv_modeling/system_models.rst index 55e01f437e..bde7e5808c 100644 --- a/docs/sphinx/source/reference/pv_modeling/system_models.rst +++ b/docs/sphinx/source/reference/pv_modeling/system_models.rst @@ -55,3 +55,4 @@ PVGIS model :toctree: ../generated/ pvarray.huld + pvarray.fit_huld diff --git a/docs/sphinx/source/whatsnew/v0.10.4.rst b/docs/sphinx/source/whatsnew/v0.10.4.rst index 997ca4c249..2f0b8bfbbc 100644 --- a/docs/sphinx/source/whatsnew/v0.10.4.rst +++ b/docs/sphinx/source/whatsnew/v0.10.4.rst @@ -8,6 +8,7 @@ v0.10.4 (Anticipated March, 2024) Enhancements ~~~~~~~~~~~~ * Added the Huld PV model used by PVGIS (:pull:`1940`) +* Added a method to fit the Huld PV model (:pull:`XXXX`) Bug fixes From 007cb40305b402e640dffcd71e2b07c2520684ec Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 23 Feb 2024 15:06:06 -0700 Subject: [PATCH 03/13] Update v0.10.4.rst --- docs/sphinx/source/whatsnew/v0.10.4.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.10.4.rst b/docs/sphinx/source/whatsnew/v0.10.4.rst index 2f0b8bfbbc..48652da643 100644 --- a/docs/sphinx/source/whatsnew/v0.10.4.rst +++ b/docs/sphinx/source/whatsnew/v0.10.4.rst @@ -8,7 +8,7 @@ v0.10.4 (Anticipated March, 2024) Enhancements ~~~~~~~~~~~~ * Added the Huld PV model used by PVGIS (:pull:`1940`) -* Added a method to fit the Huld PV model (:pull:`XXXX`) +* Added a method to fit the Huld PV model (:pull:`1979`) Bug fixes From 52c0d42c19ae988c66c8424b329b0de8996208d1 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 23 Feb 2024 16:30:58 -0700 Subject: [PATCH 04/13] formatting, mark for scipy --- pvlib/pvarray.py | 4 ++-- pvlib/tests/test_pvarray.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index 774724f2f3..38570a806b 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -369,11 +369,11 @@ def fit_huld(effective_irradiance, temp_mod, pdc): where=np.array(gprime > 0)) Y = np.divide(pdc, gprime, out=np.zeros_like(gprime), where=np.array(gprime > 0)) - + X = np.stack((logGprime, logGprime**2, tprime, tprime*logGprime, tprime*logGprime**2, tprime**2), axis=0).T X = sm.add_constant(X) - + rlm_model = sm.RLM(Y, X) rlm_result = rlm_model.fit() pdc0 = rlm_result.params[0] diff --git a/pvlib/tests/test_pvarray.py b/pvlib/tests/test_pvarray.py index a059373655..adc4bbb14d 100644 --- a/pvlib/tests/test_pvarray.py +++ b/pvlib/tests/test_pvarray.py @@ -3,7 +3,7 @@ from numpy.testing import assert_allclose from .conftest import assert_series_equal import pytest - +from pvlib.tests.conftest import requires_statsmodels from pvlib import pvarray @@ -70,7 +70,7 @@ def test_huld(): match='Either k or cell_type must be specified'): res = pvarray.huld(1000, 25, 100) - +@requires_statsmodels def test_fit_huld(): # test is to recover the parameters in _infer_huld_k for each cell type # IEC61853 conditions to make data for fitting @@ -81,6 +81,6 @@ def test_fit_huld(): k0 = pvarray._infer_k_huld(tech, pdc0) pdc = pvarray.huld(ee, tc, pdc0, cell_type=tech) m_pdc0, k = pvarray.fit_huld(ee, tc, pdc) - expected = np.array([pdc0,] + [v for v in k0], dtype=float) + expected = np.array([pdc0, ] + [v for v in k0], dtype=float) modeled = np.hstack((m_pdc0, k)) assert_allclose(expected, modeled, rtol=1e-8) From 0c68e787901d05dd0fbf39fc1de96b3ff4119388 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 23 Feb 2024 16:47:24 -0700 Subject: [PATCH 05/13] adjust import, add docstring --- pvlib/pvarray.py | 38 +++++++++++++++++++++++++++++++++++-- pvlib/tests/test_pvarray.py | 1 + 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index 38570a806b..645450e418 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -11,7 +11,6 @@ import numpy as np from scipy.optimize import curve_fit from scipy.special import exp10 -import statsmodels.api as sm def pvefficiency_adr(effective_irradiance, temp_cell, @@ -270,7 +269,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): Power of the modules at reference conditions 1000 :math:`W/m^2` and :math:`25^{\circ}C`. [W] k : tuple, optional - Empirical coefficients used in the power model. Length 6. If ``k`` is + Empirical coefficients used in the Huld model. Length 6. If ``k`` is not provided, ``cell_type`` must be specified. cell_type : str, optional If provided, must be one of ``'cSi'``, ``'CIS'``, or ``'CdTe'``. @@ -361,6 +360,41 @@ def _build_iec61853(): def fit_huld(effective_irradiance, temp_mod, pdc): + r''' + Fit the Huld model to the input data. + + Parameters + ---------- + effective_irradiance : numeric + The irradiance that is converted to photocurrent. [:math:`W/m^2`] + temp_mod: numeric + Module back-surface temperature. [C] + pdc: numeric + DC power at ``effectuve_irradiance`` and ``temp_mod``. [W] + + Returns + ------- + pdc0: numeric + Power of the modules at reference conditions 1000 :math:`W/m^2` + and :math:`25^{\circ}C`. [W] + k : tuple + Empirical coefficients used in the Huld model. Length 6. + + Notes + ----- + Requires ``statsmodels``. + + See Also + -------- + pvlib.pvarray.huld + ''' + + try: + import statsmodels.api as sm + except ImportError: + raise ImportError( + 'fit_huld requires statsmodels') + gprime = effective_irradiance / 1000 tprime = temp_mod - 25 # accomodate gprime<=0 diff --git a/pvlib/tests/test_pvarray.py b/pvlib/tests/test_pvarray.py index adc4bbb14d..83dc90aca0 100644 --- a/pvlib/tests/test_pvarray.py +++ b/pvlib/tests/test_pvarray.py @@ -70,6 +70,7 @@ def test_huld(): match='Either k or cell_type must be specified'): res = pvarray.huld(1000, 25, 100) + @requires_statsmodels def test_fit_huld(): # test is to recover the parameters in _infer_huld_k for each cell type From 7197b8aa8e1831a0b3ee0b49d731124b1a7c5ddf Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 4 Apr 2024 13:53:12 -0600 Subject: [PATCH 06/13] handle NaN, add note about regression method, use temp_module --- pvlib/pvarray.py | 65 ++++++++++++++++++++++++++++--------- pvlib/tests/test_pvarray.py | 29 +++++++++++++++-- 2 files changed, 77 insertions(+), 17 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index 645450e418..85ca736043 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -242,7 +242,7 @@ def _infer_k_huld(cell_type, pdc0): return k -def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): +def huld(effective_irradiance, temp_module, pdc0, k=None, cell_type=None): r""" Power (DC) using the Huld model. @@ -263,7 +263,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): ---------- effective_irradiance : numeric The irradiance that is converted to photocurrent. [:math:`W/m^2`] - temp_mod: numeric + temp_module: numeric Module back-surface temperature. [C] pdc0: numeric Power of the modules at reference conditions 1000 :math:`W/m^2` @@ -330,6 +330,10 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): E. Dunlop. A power-rating model for crystalline silicon PV modules. Solar Energy Materials and Solar Cells 95, (2011), pp. 3359-3369. :doi:`10.1016/j.solmat.2011.07.026`. + + See Also + -------- + pvlib.pvarray.fit_huld """ if k is None: if cell_type is not None: @@ -338,7 +342,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): raise ValueError('Either k or cell_type must be specified') gprime = effective_irradiance / 1000 - tprime = temp_mod - 25 + tprime = temp_module - 25 # accomodate gprime<=0 with np.errstate(divide='ignore'): logGprime = np.log(gprime, out=np.zeros_like(gprime), @@ -353,24 +357,27 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): def _build_iec61853(): ee = np.array([100, 100, 200, 200, 400, 400, 400, 600, 600, 600, 600, 800, 800, 800, 800, 1000, 1000, 1000, 1000, 1100, 1100, - 1100]).T + 1100], dtype=np.float64).T tc = np.array([15, 25, 15, 25, 15, 25, 50, 15, 25, 50, 75, - 15, 25, 50, 75, 15, 25, 50, 75, 25, 50, 75]).T + 15, 25, 50, 75, 15, 25, 50, 75, 25, 50, 75], + dtype=np.float64).T return ee, tc -def fit_huld(effective_irradiance, temp_mod, pdc): +def fit_huld(effective_irradiance, temp_module, pdc, method='ols'): r''' Fit the Huld model to the input data. Parameters ---------- - effective_irradiance : numeric + effective_irradiance : array-like The irradiance that is converted to photocurrent. [:math:`W/m^2`] - temp_mod: numeric + temp_module: array-like Module back-surface temperature. [C] - pdc: numeric - DC power at ``effectuve_irradiance`` and ``temp_mod``. [W] + pdc: array-like + DC power at ``effective_irradiance`` and ``temp_module``. [W] + method : string, default 'ols' + The regression method. Must be either ``'ols'`` or ``'robust'``. Returns ------- @@ -384,6 +391,28 @@ def fit_huld(effective_irradiance, temp_mod, pdc): ----- Requires ``statsmodels``. + Input data ``effective_irradiance``, ``temp_module`` and ``pdc`` must be + equal length. Data are aligned as columns and any row with a NaN in any + column is dropped. Remaining data must be at least length 7. + + Huld [1]_ describes fitting by least-squares but doesn't detail the + procedure, although it is reasonable that ordinary least-squares regression + was used. Ordinary least-squares regression can be unduly influenced by + data points with large deviations from the mean of the dependent variable + ``pdc``. For example, these data may occur when short-duration shading is + present. If filtering is applied to exclude such data, a suitable model + can be obtained with ordinary least-squares regression. As an alternative, + this function also provides a 'robust' regression option (an iteratively + reweighted least-squares method) that is more tolerant of outliers in the + dependent variable. + + References + ---------- + .. [1] T. Huld, G. Friesen, A. Skoczek, R. Kenny, T. Sample, M. Field, + E. Dunlop. A power-rating model for crystalline silicon PV modules. + Solar Energy Materials and Solar Cells 95, (2011), pp. 3359-3369. + :doi:`10.1016/j.solmat.2011.07.026`. + See Also -------- pvlib.pvarray.huld @@ -396,7 +425,7 @@ def fit_huld(effective_irradiance, temp_mod, pdc): 'fit_huld requires statsmodels') gprime = effective_irradiance / 1000 - tprime = temp_mod - 25 + tprime = temp_module - 25 # accomodate gprime<=0 with np.errstate(divide='ignore'): logGprime = np.log(gprime, out=np.zeros_like(gprime), @@ -408,8 +437,14 @@ def fit_huld(effective_irradiance, temp_mod, pdc): tprime*logGprime**2, tprime**2), axis=0).T X = sm.add_constant(X) - rlm_model = sm.RLM(Y, X) - rlm_result = rlm_model.fit() - pdc0 = rlm_result.params[0] - k = rlm_result.params[1:] + if method=='ols': + model = sm.OLS(Y, X, missing='drop') + elif method=='robust': + model = sm.RLM(Y, X, missing='drop') + else: + raise ValueError("method must be ols or robust") + + result = model.fit() + pdc0 = result.params[0] + k = result.params[1:] return pdc0, k diff --git a/pvlib/tests/test_pvarray.py b/pvlib/tests/test_pvarray.py index 83dc90aca0..c569c819a8 100644 --- a/pvlib/tests/test_pvarray.py +++ b/pvlib/tests/test_pvarray.py @@ -71,8 +71,9 @@ def test_huld(): res = pvarray.huld(1000, 25, 100) +@pytest.mark.parametrize('method', ['ols', 'robust']) @requires_statsmodels -def test_fit_huld(): +def test_fit_huld(method): # test is to recover the parameters in _infer_huld_k for each cell type # IEC61853 conditions to make data for fitting ee, tc = pvarray._build_iec61853() @@ -81,7 +82,31 @@ def test_fit_huld(): for tech in techs: k0 = pvarray._infer_k_huld(tech, pdc0) pdc = pvarray.huld(ee, tc, pdc0, cell_type=tech) - m_pdc0, k = pvarray.fit_huld(ee, tc, pdc) + m_pdc0, k = pvarray.fit_huld(ee, tc, pdc, method=method) expected = np.array([pdc0, ] + [v for v in k0], dtype=float) modeled = np.hstack((m_pdc0, k)) assert_allclose(expected, modeled, rtol=1e-8) + # once more to check that NaNs are handled + ee[7] = np.nan + tc[9] = np.nan + k0 = pvarray._infer_k_huld('csi', pdc0) + pdc = pvarray.huld(ee, tc, pdc0, cell_type='csi') + pdc[11] = np.nan + m_pdc0, k = pvarray.fit_huld(ee, tc, pdc, method='ols') + expected = np.array([2.07118648e+02, -8.35033390e+01, -3.83844606e+01, + 2.75629738e+00, 1.87571414e+00, -2.86429730e-01, + -6.00418853e-02]) + modeled = np.hstack((m_pdc0, k)) + assert_allclose(expected, modeled, rtol=1e-5) + + +@requires_statsmodels +def test_fit_huld_method_error(): + # test is to recover the parameters in _infer_huld_k for each cell type + # IEC61853 conditions to make data for fitting + ee, tc = pvarray._build_iec61853() + pdc0 = 250 + pdc = pvarray.huld(ee, tc, pdc0, cell_type='csi') + method='brute_force' + with pytest.raises(ValueError, match="method must be ols or robust"): + m_pdc0, k = pvarray.fit_huld(ee, tc, pdc, method=method) From c24b7cc47f2c3d6c89be80e0c3207636da15c4bf Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 4 Apr 2024 13:53:47 -0600 Subject: [PATCH 07/13] forgot to save --- pvlib/tests/test_pvarray.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pvlib/tests/test_pvarray.py b/pvlib/tests/test_pvarray.py index c569c819a8..4b5a1aead2 100644 --- a/pvlib/tests/test_pvarray.py +++ b/pvlib/tests/test_pvarray.py @@ -93,6 +93,7 @@ def test_fit_huld(method): pdc = pvarray.huld(ee, tc, pdc0, cell_type='csi') pdc[11] = np.nan m_pdc0, k = pvarray.fit_huld(ee, tc, pdc, method='ols') + # known solution for OLS obtained with statsmodels v0.14.0 expected = np.array([2.07118648e+02, -8.35033390e+01, -3.83844606e+01, 2.75629738e+00, 1.87571414e+00, -2.86429730e-01, -6.00418853e-02]) From 1250bcd01f54276f14be1ae04f2864f6373f8c9b Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 4 Apr 2024 14:09:02 -0600 Subject: [PATCH 08/13] lint --- pvlib/pvarray.py | 4 ++-- pvlib/tests/test_pvarray.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index 85ca736043..0966c5b5b1 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -437,9 +437,9 @@ def fit_huld(effective_irradiance, temp_module, pdc, method='ols'): tprime*logGprime**2, tprime**2), axis=0).T X = sm.add_constant(X) - if method=='ols': + if method == 'ols': model = sm.OLS(Y, X, missing='drop') - elif method=='robust': + elif method == 'robust': model = sm.RLM(Y, X, missing='drop') else: raise ValueError("method must be ols or robust") diff --git a/pvlib/tests/test_pvarray.py b/pvlib/tests/test_pvarray.py index 4b5a1aead2..c165199729 100644 --- a/pvlib/tests/test_pvarray.py +++ b/pvlib/tests/test_pvarray.py @@ -108,6 +108,6 @@ def test_fit_huld_method_error(): ee, tc = pvarray._build_iec61853() pdc0 = 250 pdc = pvarray.huld(ee, tc, pdc0, cell_type='csi') - method='brute_force' + method = 'brute_force' with pytest.raises(ValueError, match="method must be ols or robust"): m_pdc0, k = pvarray.fit_huld(ee, tc, pdc, method=method) From 464749e5d928149886854109f0bf838fa25c852b Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 18 Apr 2024 14:51:53 -0600 Subject: [PATCH 09/13] respond to comments --- docs/sphinx/source/whatsnew/v0.10.4.rst | 71 ------------------------- docs/sphinx/source/whatsnew/v0.10.5.rst | 2 +- pvlib/pvarray.py | 6 +-- pvlib/tests/test_pvarray.py | 9 +--- 4 files changed, 6 insertions(+), 82 deletions(-) delete mode 100644 docs/sphinx/source/whatsnew/v0.10.4.rst diff --git a/docs/sphinx/source/whatsnew/v0.10.4.rst b/docs/sphinx/source/whatsnew/v0.10.4.rst deleted file mode 100644 index 40ccd25b20..0000000000 --- a/docs/sphinx/source/whatsnew/v0.10.4.rst +++ /dev/null @@ -1,71 +0,0 @@ -.. _whatsnew_01040: - - -v0.10.4 (March 19, 2024) ------------------------- - - -Enhancements -~~~~~~~~~~~~ -* Added the Huld PV model used by PVGIS (:pull:`1940`) -* Added a method to fit the Huld PV model (:pull:`1979`) -* Add :py:func:`~pvlib.iotools.get_solargis` for retrieving Solargis - irradiance data. (:pull:`1969`) -* Added function :py:func:`pvlib.shading.projected_solar_zenith_angle`, - a common calculation in shading and tracking. (:issue:`1734`, :pull:`1904`) -* Added :py:func:`~pvlib.iotools.get_solrad` for fetching irradiance data from - the SOLRAD ground station network. (:pull:`1967`) -* Added metadata parsing to :py:func:`~pvlib.iotools.read_solrad` to follow the standard iotools - convention of returning a tuple of (data, meta). Previously the function only returned a dataframe. (:pull:`1968`) - - -Bug fixes -~~~~~~~~~ -* Fixed an error in solar position calculations when using - :py:class:`pandas.DatetimeIndex` with ``unit`` other than ``'ns'`` (:issue:`1932`). - The following functions were affected: - - - :py:class:`~pvlib.modelchain.ModelChain` and :py:func:`~pvlib.solarposition.get_solarposition` with the ``nrel_numpy`` and ``nrel_numba`` methods - - :py:func:`~pvlib.solarposition.spa_python` - - :py:func:`~pvlib.solarposition.sun_rise_set_transit_spa` - - :py:func:`~pvlib.solarposition.nrel_earthsun_distance` - - :py:func:`~pvlib.solarposition.hour_angle` - - :py:func:`~pvlib.solarposition.sun_rise_set_transit_geometric` - -* :py:class:`~pvlib.modelchain.ModelChain` now raises a more useful error when - ``temperature_model_parameters`` are specified on the passed ``system`` instead of on its ``arrays``. (:issue:`1759`). -* :py:func:`pvlib.irradiance.ghi_from_poa_driesse_2023` now correctly makes use - of the ``xtol`` argument. Previously, it was ignored. (:issue:`1970`, :pull:`1971`) -* Fixed incorrect unit conversion of precipitable water used for the Solcast iotools functions. (:pull:`1969`) -* :py:class:`~pvlib.modelchain.ModelChain.infer_temperature_model` now raises a more useful error when - the temperature model cannot be inferred (:issue:`1946`) -* The default URL for retrieving irradiance data from the SRML network was updated in - :py:func:`~pvlib.iotools.get_srml` (:pull:`1957`, :issue:`1922`) - - -Documentation -~~~~~~~~~~~~~ -* Improved references and description for :py:func:`~pvlib.irradiance.get_ground_diffuse`. (:pull:`1883`, :pull:`1953`) -* Fixed broken URLs in various places. (:pull:`1957`, :pull:`1960`) -* Added a gallery example with a model for OEDI system 9068. (:pull:`1985`) - - -Requirements -~~~~~~~~~~~~ -* Minimum version of pandas advanced from 0.25.0 to 1.3.0. (:pull:`1969`) -* Minimum version of numpy advanced from 1.16.0 to 1.17.3. (:pull:`1969`) - - -Contributors -~~~~~~~~~~~~ -* Patrick Sheehan (:ghuser:`patricksheehan`) -* Echedey Luis (:ghuser:`echedey-ls`) -* Kevin Anderson (:ghuser:`kandersolar`) -* Cliff Hansen (:ghuser:`cwhanse`) -* Roma Koulikov (:ghuser:`matsuobasho`) -* Adam R. Jensen (:ghuser:`AdamRJensen`) -* Peter Dudfield (:ghuser:`peterdudfield`) -* Anton Driesse (:ghuser:`adriesse`) -* Mark Mikofski (:ghuser:`mikofski`) -* Will Holmgren (:ghuser:`wholmgren`) -* Jules Chéron (:ghuser:`jules-ch`) diff --git a/docs/sphinx/source/whatsnew/v0.10.5.rst b/docs/sphinx/source/whatsnew/v0.10.5.rst index fe64879c8a..e2378cf615 100644 --- a/docs/sphinx/source/whatsnew/v0.10.5.rst +++ b/docs/sphinx/source/whatsnew/v0.10.5.rst @@ -11,7 +11,7 @@ Deprecations Enhancements ~~~~~~~~~~~~ - +* Added a method to fit the Huld PV model (:pull:`1979`) Bug fixes ~~~~~~~~~ diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index 0966c5b5b1..21c23a5a80 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -427,10 +427,10 @@ def fit_huld(effective_irradiance, temp_module, pdc, method='ols'): gprime = effective_irradiance / 1000 tprime = temp_module - 25 # accomodate gprime<=0 + logGprime = np.log(gprime, out=np.nan*gprime, + where=np.array(gprime > 0)) with np.errstate(divide='ignore'): - logGprime = np.log(gprime, out=np.zeros_like(gprime), - where=np.array(gprime > 0)) - Y = np.divide(pdc, gprime, out=np.zeros_like(gprime), + Y = np.divide(pdc, gprime, out=np.nan*gprime, where=np.array(gprime > 0)) X = np.stack((logGprime, logGprime**2, tprime, tprime*logGprime, diff --git a/pvlib/tests/test_pvarray.py b/pvlib/tests/test_pvarray.py index c165199729..309c492c53 100644 --- a/pvlib/tests/test_pvarray.py +++ b/pvlib/tests/test_pvarray.py @@ -93,18 +93,13 @@ def test_fit_huld(method): pdc = pvarray.huld(ee, tc, pdc0, cell_type='csi') pdc[11] = np.nan m_pdc0, k = pvarray.fit_huld(ee, tc, pdc, method='ols') - # known solution for OLS obtained with statsmodels v0.14.0 - expected = np.array([2.07118648e+02, -8.35033390e+01, -3.83844606e+01, - 2.75629738e+00, 1.87571414e+00, -2.86429730e-01, - -6.00418853e-02]) + expected = np.array([pdc0, ] + [v for v in k0], dtype=float) modeled = np.hstack((m_pdc0, k)) - assert_allclose(expected, modeled, rtol=1e-5) + assert_allclose(expected, modeled, rtol=1e-8) @requires_statsmodels def test_fit_huld_method_error(): - # test is to recover the parameters in _infer_huld_k for each cell type - # IEC61853 conditions to make data for fitting ee, tc = pvarray._build_iec61853() pdc0 = 250 pdc = pvarray.huld(ee, tc, pdc0, cell_type='csi') From e0e637e4dd10e486cc5bccf146d4c980408e4b0c Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 18 Apr 2024 14:55:43 -0600 Subject: [PATCH 10/13] put v0.10.4.rst back --- docs/sphinx/source/whatsnew/v0.10.4.rst | 70 +++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 docs/sphinx/source/whatsnew/v0.10.4.rst diff --git a/docs/sphinx/source/whatsnew/v0.10.4.rst b/docs/sphinx/source/whatsnew/v0.10.4.rst new file mode 100644 index 0000000000..a194c5e522 --- /dev/null +++ b/docs/sphinx/source/whatsnew/v0.10.4.rst @@ -0,0 +1,70 @@ +.. _whatsnew_01040: + + +v0.10.4 (March 19, 2024) +------------------------ + + +Enhancements +~~~~~~~~~~~~ +* Added the Huld PV model used by PVGIS (:pull:`1940`) +* Add :py:func:`~pvlib.iotools.get_solargis` for retrieving Solargis + irradiance data. (:pull:`1969`) +* Added function :py:func:`pvlib.shading.projected_solar_zenith_angle`, + a common calculation in shading and tracking. (:issue:`1734`, :pull:`1904`) +* Added :py:func:`~pvlib.iotools.get_solrad` for fetching irradiance data from + the SOLRAD ground station network. (:pull:`1967`) +* Added metadata parsing to :py:func:`~pvlib.iotools.read_solrad` to follow the standard iotools + convention of returning a tuple of (data, meta). Previously the function only returned a dataframe. (:pull:`1968`) + + +Bug fixes +~~~~~~~~~ +* Fixed an error in solar position calculations when using + :py:class:`pandas.DatetimeIndex` with ``unit`` other than ``'ns'`` (:issue:`1932`). + The following functions were affected: + + - :py:class:`~pvlib.modelchain.ModelChain` and :py:func:`~pvlib.solarposition.get_solarposition` with the ``nrel_numpy`` and ``nrel_numba`` methods + - :py:func:`~pvlib.solarposition.spa_python` + - :py:func:`~pvlib.solarposition.sun_rise_set_transit_spa` + - :py:func:`~pvlib.solarposition.nrel_earthsun_distance` + - :py:func:`~pvlib.solarposition.hour_angle` + - :py:func:`~pvlib.solarposition.sun_rise_set_transit_geometric` + +* :py:class:`~pvlib.modelchain.ModelChain` now raises a more useful error when + ``temperature_model_parameters`` are specified on the passed ``system`` instead of on its ``arrays``. (:issue:`1759`). +* :py:func:`pvlib.irradiance.ghi_from_poa_driesse_2023` now correctly makes use + of the ``xtol`` argument. Previously, it was ignored. (:issue:`1970`, :pull:`1971`) +* Fixed incorrect unit conversion of precipitable water used for the Solcast iotools functions. (:pull:`1969`) +* :py:class:`~pvlib.modelchain.ModelChain.infer_temperature_model` now raises a more useful error when + the temperature model cannot be inferred (:issue:`1946`) +* The default URL for retrieving irradiance data from the SRML network was updated in + :py:func:`~pvlib.iotools.get_srml` (:pull:`1957`, :issue:`1922`) + + +Documentation +~~~~~~~~~~~~~ +* Improved references and description for :py:func:`~pvlib.irradiance.get_ground_diffuse`. (:pull:`1883`, :pull:`1953`) +* Fixed broken URLs in various places. (:pull:`1957`, :pull:`1960`) +* Added a gallery example with a model for OEDI system 9068. (:pull:`1985`) + + +Requirements +~~~~~~~~~~~~ +* Minimum version of pandas advanced from 0.25.0 to 1.3.0. (:pull:`1969`) +* Minimum version of numpy advanced from 1.16.0 to 1.17.3. (:pull:`1969`) + + +Contributors +~~~~~~~~~~~~ +* Patrick Sheehan (:ghuser:`patricksheehan`) +* Echedey Luis (:ghuser:`echedey-ls`) +* Kevin Anderson (:ghuser:`kandersolar`) +* Cliff Hansen (:ghuser:`cwhanse`) +* Roma Koulikov (:ghuser:`matsuobasho`) +* Adam R. Jensen (:ghuser:`AdamRJensen`) +* Peter Dudfield (:ghuser:`peterdudfield`) +* Anton Driesse (:ghuser:`adriesse`) +* Mark Mikofski (:ghuser:`mikofski`) +* Will Holmgren (:ghuser:`wholmgren`) +* Jules Chéron (:ghuser:`jules-ch`) From 1305f6e710fb383983d6e5011134d5fab10955a2 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 31 May 2024 11:04:13 -0700 Subject: [PATCH 11/13] move to v0.11.0 --- docs/sphinx/source/whatsnew/v0.11.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.11.0.rst b/docs/sphinx/source/whatsnew/v0.11.0.rst index 22d6046774..09d97a49ce 100644 --- a/docs/sphinx/source/whatsnew/v0.11.0.rst +++ b/docs/sphinx/source/whatsnew/v0.11.0.rst @@ -21,7 +21,7 @@ Enhancements shade perpendicular to ``axis_azimuth``. The function is applicable to both fixed-tilt and one-axis tracking systems. (:issue:`1689`, :pull:`1725`, :pull:`1962`) - +* Added a method to fit the Huld PV model (:pull:`1979`) Bug fixes ~~~~~~~~~ From e1873fc5afca6fc6cb944917dc9413c80bef718a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 31 May 2024 11:05:16 -0700 Subject: [PATCH 12/13] restore v0.10.5 whatsnew --- docs/sphinx/source/whatsnew/v0.10.5.rst | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.10.5.rst b/docs/sphinx/source/whatsnew/v0.10.5.rst index 77dad0dc19..00daed3a0f 100644 --- a/docs/sphinx/source/whatsnew/v0.10.5.rst +++ b/docs/sphinx/source/whatsnew/v0.10.5.rst @@ -1,17 +1,6 @@ .. _whatsnew_01050: -v0.10.5 (Anticipated June 2024) -------------------------------- - - -Deprecations -~~~~~~~~~~~~ - - -Enhancements -~~~~~~~~~~~~ -* Added a method to fit the Huld PV model (:pull:`1979`) v0.10.5 (May 6, 2024) --------------------- From 6437c14b9043883b96be9bfcba75133de686376d Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 31 May 2024 11:08:43 -0700 Subject: [PATCH 13/13] Update v0.11.0.rst --- docs/sphinx/source/whatsnew/v0.11.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.11.0.rst b/docs/sphinx/source/whatsnew/v0.11.0.rst index 09d97a49ce..cbf9c35bd9 100644 --- a/docs/sphinx/source/whatsnew/v0.11.0.rst +++ b/docs/sphinx/source/whatsnew/v0.11.0.rst @@ -9,6 +9,7 @@ Breaking changes ~~~~~~~~~~~~~~~~ * The deprecated ``pvlib.modelchain.basic_chain`` has now been removed. (:pull:`1862`) * Remove the `poa_horizontal_ratio` function and all of its references. (:issue:`1697`, :pull:`2021`) +* Parameter ``temp_mod`` in :py:func:`pvlib.pvarray.huld` is renamed to ``temp_module``. (:pull:`1979`) Deprecations