From 4f0b2b9ef80d885bcf617b1cd7b78a2eccff0a36 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 22 Dec 2023 11:55:55 -0700 Subject: [PATCH 01/22] implement Huld --- pvlib/pvsystem.py | 74 ++++++++++++++++++++++++++++++++++++ pvlib/tests/test_pvsystem.py | 22 +++++++++++ 2 files changed, 96 insertions(+) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 7b2f662b13..14a932f63b 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2845,6 +2845,80 @@ def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.): return pdc +def _infer_k_huld(cell_type): + # from PVGIS documentation, Section 5.2.3, accessed 12/22/2023 + huld_params = {'cSi': (-0.017237, -0.040465, -0.004702, 0.000149, + 0.000170, 0.000005), + 'CIS': (-0.005554, -0.038724, -0.003723, -0.000905, + -0.001256, 0.000001), + 'CdTe': (-0.046689, -0.072844, -0.002262, 0.000276, + -0.000159, -0.000006)} + return huld_params[cell_type] + + +def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): + r""" + Power (DC) using the Huld model. + + The Huld model [1]_ is given by + + + .. math:: + + P_{dc} = G` ( P_{dc0} k_1 log(G') + k_2 log^2 (G') + k_3 T' log(G') + + k_5 T' log^2 (G') + k_6 T'^2) + + G' = \frac{G_{poa eff}}{1000} + + T' = T_{mod} - 25 \degree C + + + Parameters + ---------- + effective_irradiance : numeric + The irradiance that is converted to photocurrent. [W/m^2] + temp_mod: numeric + Module back-surface temperature [C]. + pdc0: numeric + Power of the modules at 1000 W/m^2 and cell reference temperature. [W] + k : tuple, optional + 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'`. Used to look + up default values for `k` if `k` is not specified. + + Returns + ------- + pdc: numeric + DC power. [W] + + Raises + ------ + ValueError + If neither `k` nor `cell_type` are specified. + + References + ---------- + .. [1] T. Huld, G. Friesen, A. Skoczek, K. 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. + 10.1016/j.solmat.2011.07.026. + """ + if k is None: + if not cell_type is None: + k = _infer_k_huld(cell_type) + else: + raise ValueError('Either k or cell_type must be specified') + + gprime = effective_irradiance / 1000 + tprime = temp_mod - 25 + # Eq. 1 in [1] + pdc = gprime * (pdc0 + k[0] * np.log(gprime) + k[1] * np.log(gprime)**2 + + k[2] * tprime + k[3] * tprime * np.log(gprime) + + k[4] * tprime * np.log(gprime)**2 + k[5] * tprime**2) + return pdc + + def pvwatts_losses(soiling=2, shading=3, snow=0, mismatch=2, wiring=2, connections=0.5, lid=1.5, nameplate_rating=1, age=0, availability=3): diff --git a/pvlib/tests/test_pvsystem.py b/pvlib/tests/test_pvsystem.py index 6557d77e10..961d3e09d1 100644 --- a/pvlib/tests/test_pvsystem.py +++ b/pvlib/tests/test_pvsystem.py @@ -2549,3 +2549,25 @@ def test_Array_temperature_missing_parameters(model, keys): array.temperature_model_parameters = params with pytest.raises(KeyError, match=match): array.get_cell_temperature(irrads, temps, winds, model) + + +def test_huld(): + pdc0 = 100 + res = pvsystem.huld(1000, 25, pdc0, cell_type='cSi') + assert np.isclose(res, pdc0) + exp_sum = np.exp(1) * (np.sum(pvsystem._infer_k_huld('cSi')) + pdc0) + res = pvsystem.huld(1000*np.exp(1), 26, pdc0, cell_type='cSi') + assert np.isclose(res, exp_sum) + res = pvsystem.huld(100, 30, pdc0, k=(1, 1, 1, 1, 1, 1)) + exp_100 = 0.1 * (pdc0 + np.log(0.1) + np.log(0.1)**2 + + 5 + 5*np.log(0.1) + + 5*np.log(0.1)**2 + 25) + assert np.isclose(res, exp_100) + # Series input + eff_irr = pd.Series([1000, 100]) + tm = pd.Series([25, 30]) + expected = pd.Series([pdc0, exp_100]) + res = pvsystem.huld(eff_irr, tm, pdc0, k=(1, 1, 1, 1, 1, 1)) + assert_allclose(res, expected) + with pytest.raises(ValueError, + match='Either k or cell_type must be specified'): + res = pvsystem.huld(1000, 25, 100) From 50a28cbb4d3935d6effae471581b6c8464d18c26 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 22 Dec 2023 14:18:41 -0700 Subject: [PATCH 02/22] documents --- .../sphinx/source/reference/pv_modeling/system_models.rst | 8 ++++++++ docs/sphinx/source/whatsnew/v0.10.4.rst | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/reference/pv_modeling/system_models.rst b/docs/sphinx/source/reference/pv_modeling/system_models.rst index c2c55d2580..72407e5557 100644 --- a/docs/sphinx/source/reference/pv_modeling/system_models.rst +++ b/docs/sphinx/source/reference/pv_modeling/system_models.rst @@ -47,3 +47,11 @@ ADR model pvarray.pvefficiency_adr pvarray.fit_pvefficiency_adr + +PVGIS model +^^^^^^^^^^^ + +.. autosummary:: + :toctree: ../generated/ + + pvsystem.huld diff --git a/docs/sphinx/source/whatsnew/v0.10.4.rst b/docs/sphinx/source/whatsnew/v0.10.4.rst index 83b04cc01e..92500d458e 100644 --- a/docs/sphinx/source/whatsnew/v0.10.4.rst +++ b/docs/sphinx/source/whatsnew/v0.10.4.rst @@ -7,6 +7,7 @@ v0.10.4 (Anticipated March, 2024) Enhancements ~~~~~~~~~~~~ +* Added the Huld PV model (:pull:`XXXX`) Bug fixes @@ -27,4 +28,4 @@ Requirements Contributors ~~~~~~~~~~~~ - +* Cliff Hansen (:ghuser:`cwhanse`) From 2a9257272aed881ee6c504b2a80c54577744d011 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 22 Dec 2023 14:21:45 -0700 Subject: [PATCH 03/22] is not, not not is --- pvlib/pvsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 14a932f63b..f384e87411 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2905,7 +2905,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): 10.1016/j.solmat.2011.07.026. """ if k is None: - if not cell_type is None: + if cell_type is not None: k = _infer_k_huld(cell_type) else: raise ValueError('Either k or cell_type must be specified') From c17924f4560b7c69c5d9846bb72f993a279bd720 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 22 Dec 2023 16:58:15 -0700 Subject: [PATCH 04/22] Apply suggestions from code review Co-authored-by: Kevin Anderson Co-authored-by: Adam R. Jensen <39184289+AdamRJensen@users.noreply.github.com> --- docs/sphinx/source/whatsnew/v0.10.4.rst | 2 +- pvlib/pvsystem.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.10.4.rst b/docs/sphinx/source/whatsnew/v0.10.4.rst index 92500d458e..8f75f10a19 100644 --- a/docs/sphinx/source/whatsnew/v0.10.4.rst +++ b/docs/sphinx/source/whatsnew/v0.10.4.rst @@ -7,7 +7,7 @@ v0.10.4 (Anticipated March, 2024) Enhancements ~~~~~~~~~~~~ -* Added the Huld PV model (:pull:`XXXX`) +* Added the Huld PV model (:pull:`1940`) Bug fixes diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index f384e87411..dd98acca49 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2852,7 +2852,7 @@ def _infer_k_huld(cell_type): 'CIS': (-0.005554, -0.038724, -0.003723, -0.000905, -0.001256, 0.000001), 'CdTe': (-0.046689, -0.072844, -0.002262, 0.000276, - -0.000159, -0.000006)} + 0.000159, -0.000006)} return huld_params[cell_type] @@ -2860,17 +2860,17 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): r""" Power (DC) using the Huld model. - The Huld model [1]_ is given by + The Huld model [1]_ is used by PVGIS and is given by .. math:: - P_{dc} = G` ( P_{dc0} k_1 log(G') + k_2 log^2 (G') + k_3 T' log(G') + - k_5 T' log^2 (G') + k_6 T'^2) + P_{dc} = G' ( P_{dc0} + k_1 \log(G') + k_2 \log^2 (G') + k_3 T' + + k_4 T' \log(G') + k_5 T' \log^2 (G') + k_6 T'^2) G' = \frac{G_{poa eff}}{1000} - T' = T_{mod} - 25 \degree C + T' = T_{mod} - 25 °C Parameters @@ -2878,7 +2878,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): effective_irradiance : numeric The irradiance that is converted to photocurrent. [W/m^2] temp_mod: numeric - Module back-surface temperature [C]. + Module back-surface temperature. [C] pdc0: numeric Power of the modules at 1000 W/m^2 and cell reference temperature. [W] k : tuple, optional @@ -2899,10 +2899,10 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): References ---------- - .. [1] T. Huld, G. Friesen, A. Skoczek, K. R. Kenny, T. Sample, M. Field, + .. [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. - 10.1016/j.solmat.2011.07.026. + :doi:`10.1016/j.solmat.2011.07.026`. """ if k is None: if cell_type is not None: From 2d4c33aaab33e914dbea79600f35b1dda93b3140 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 22 Dec 2023 16:59:09 -0700 Subject: [PATCH 05/22] edit docstring --- pvlib/pvsystem.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index dd98acca49..ee2bbde716 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2882,7 +2882,8 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): pdc0: numeric Power of the modules at 1000 W/m^2 and cell reference temperature. [W] k : tuple, optional - Length 6. If `k` is not provided, `cell_type` must be specified. + Empirical coefficients used in the power 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'`. Used to look up default values for `k` if `k` is not specified. From 2c7b49dd6eab303315e85abe3f63a7d9e9a6c108 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 9 Jan 2024 10:08:10 -0700 Subject: [PATCH 06/22] move to pvarray, address review comments --- .../reference/pv_modeling/system_models.rst | 2 +- pvlib/pvarray.py | 81 +++++++++++++++++++ pvlib/pvsystem.py | 75 ----------------- pvlib/tests/test_pvarray.py | 25 ++++++ pvlib/tests/test_pvsystem.py | 23 ------ 5 files changed, 107 insertions(+), 99 deletions(-) diff --git a/docs/sphinx/source/reference/pv_modeling/system_models.rst b/docs/sphinx/source/reference/pv_modeling/system_models.rst index 72407e5557..55e01f437e 100644 --- a/docs/sphinx/source/reference/pv_modeling/system_models.rst +++ b/docs/sphinx/source/reference/pv_modeling/system_models.rst @@ -54,4 +54,4 @@ PVGIS model .. autosummary:: :toctree: ../generated/ - pvsystem.huld + pvarray.huld diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index e8968886e1..e2e0825bf3 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -223,3 +223,84 @@ def adr_wrapper(xdata, *params): return dict(zip(P_NAMES, popt)) else: return popt + + +def _infer_k_huld(cell_type): + # from PVGIS documentation, "PVGIS data sources & calculation methods", + # Section 5.2.3, accessed 12/22/2023 + huld_params = {'csi': (-0.017237, -0.040465, -0.004702, 0.000149, + 0.000170, 0.000005), + 'cis': (-0.005554, -0.038724, -0.003723, -0.000905, + -0.001256, 0.000001), + 'cdte': (-0.046689, -0.072844, -0.002262, 0.000276, + 0.000159, -0.000006)} + return huld_params[cell_type.lower()] + + +def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): + r""" + Power (DC) using the Huld model. + + The Huld model [1]_ is used by PVGIS and is given by + + + .. math:: + + P_{dc} &= G' ( P_{dc0} + k_1 \log(G') + k_2 \log^2 (G') + k_3 T' + + k_4 T' \log(G') + k_5 T' \log^2 (G') + k_6 T'^2) + + G' &= \frac{G_{poa eff}}{1000} + + T' &= T_{mod} - 25 °C + + + Parameters + ---------- + effective_irradiance : numeric + The irradiance that is converted to photocurrent. [W/m^2] + temp_mod: numeric + Module back-surface temperature. [C] + pdc0: numeric + Power of the modules at 1000 W/m^2 and cell reference temperature. [W] + k : tuple, optional + Empirical coefficients used in the power 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'`. Used to look + up default values for `k` if `k` is not specified. + + Returns + ------- + pdc: numeric + DC power. [W] + + Raises + ------ + ValueError + If neither `k` nor `cell_type` are specified. + + 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`. + """ + if k is None: + if cell_type is not None: + k = _infer_k_huld(cell_type) + else: + raise ValueError('Either k or cell_type must be specified') + + 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)) + # Eq. 1 in [1] + pdc = gprime * (pdc0 + k[0] * logGprime + k[1] * logGprime**2 + + k[2] * tprime + k[3] * tprime * logGprime + + k[4] * tprime * logGprime**2 + k[5] * tprime**2) + return pdc + diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index ee2bbde716..7b2f662b13 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -2845,81 +2845,6 @@ def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.): return pdc -def _infer_k_huld(cell_type): - # from PVGIS documentation, Section 5.2.3, accessed 12/22/2023 - huld_params = {'cSi': (-0.017237, -0.040465, -0.004702, 0.000149, - 0.000170, 0.000005), - 'CIS': (-0.005554, -0.038724, -0.003723, -0.000905, - -0.001256, 0.000001), - 'CdTe': (-0.046689, -0.072844, -0.002262, 0.000276, - 0.000159, -0.000006)} - return huld_params[cell_type] - - -def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): - r""" - Power (DC) using the Huld model. - - The Huld model [1]_ is used by PVGIS and is given by - - - .. math:: - - P_{dc} = G' ( P_{dc0} + k_1 \log(G') + k_2 \log^2 (G') + k_3 T' + - k_4 T' \log(G') + k_5 T' \log^2 (G') + k_6 T'^2) - - G' = \frac{G_{poa eff}}{1000} - - T' = T_{mod} - 25 °C - - - Parameters - ---------- - effective_irradiance : numeric - The irradiance that is converted to photocurrent. [W/m^2] - temp_mod: numeric - Module back-surface temperature. [C] - pdc0: numeric - Power of the modules at 1000 W/m^2 and cell reference temperature. [W] - k : tuple, optional - Empirical coefficients used in the power 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'`. Used to look - up default values for `k` if `k` is not specified. - - Returns - ------- - pdc: numeric - DC power. [W] - - Raises - ------ - ValueError - If neither `k` nor `cell_type` are specified. - - 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`. - """ - if k is None: - if cell_type is not None: - k = _infer_k_huld(cell_type) - else: - raise ValueError('Either k or cell_type must be specified') - - gprime = effective_irradiance / 1000 - tprime = temp_mod - 25 - # Eq. 1 in [1] - pdc = gprime * (pdc0 + k[0] * np.log(gprime) + k[1] * np.log(gprime)**2 + - k[2] * tprime + k[3] * tprime * np.log(gprime) + - k[4] * tprime * np.log(gprime)**2 + k[5] * tprime**2) - return pdc - - def pvwatts_losses(soiling=2, shading=3, snow=0, mismatch=2, wiring=2, connections=0.5, lid=1.5, nameplate_rating=1, age=0, availability=3): diff --git a/pvlib/tests/test_pvarray.py b/pvlib/tests/test_pvarray.py index 6dcacdefe1..73e78f6f05 100644 --- a/pvlib/tests/test_pvarray.py +++ b/pvlib/tests/test_pvarray.py @@ -1,5 +1,8 @@ import numpy as np +import pandas as pd from numpy.testing import assert_allclose +from .conftest import assert_series_equal +import pytest from pvlib import pvarray @@ -44,3 +47,25 @@ def test_pvefficiency_adr_round_trip(): params = pvarray.fit_pvefficiency_adr(g, t, eta, dict_output=False) result = pvarray.pvefficiency_adr(g, t, *params) assert_allclose(result, eta, atol=1e-6) + + +def test_huld(): + pdc0 = 100 + res = pvarray.huld(1000, 25, pdc0, cell_type='cSi') + assert np.isclose(res, pdc0) + exp_sum = np.exp(1) * (np.sum(pvarray._infer_k_huld('cSi')) + pdc0) + res = pvarray.huld(1000*np.exp(1), 26, pdc0, cell_type='cSi') + assert np.isclose(res, exp_sum) + res = pvarray.huld(100, 30, pdc0, k=(1, 1, 1, 1, 1, 1)) + exp_100 = 0.1 * (pdc0 + np.log(0.1) + np.log(0.1)**2 + 5 + 5*np.log(0.1) + + 5*np.log(0.1)**2 + 25) + assert np.isclose(res, exp_100) + # Series input, and irradiance = 0 + eff_irr = pd.Series([1000, 100, 0]) + tm = pd.Series([25, 30, 30]) + expected = pd.Series([pdc0, exp_100, 0]) + res = pvarray.huld(eff_irr, tm, pdc0, k=(1, 1, 1, 1, 1, 1)) + assert_series_equal(res, expected) + with pytest.raises(ValueError, + match='Either k or cell_type must be specified'): + res = pvarray.huld(1000, 25, 100) diff --git a/pvlib/tests/test_pvsystem.py b/pvlib/tests/test_pvsystem.py index 961d3e09d1..ebc59a838a 100644 --- a/pvlib/tests/test_pvsystem.py +++ b/pvlib/tests/test_pvsystem.py @@ -12,7 +12,6 @@ import unittest.mock as mock from pvlib import inverter, pvsystem -from pvlib import atmosphere from pvlib import iam as _iam from pvlib import irradiance from pvlib import spectrum @@ -2549,25 +2548,3 @@ def test_Array_temperature_missing_parameters(model, keys): array.temperature_model_parameters = params with pytest.raises(KeyError, match=match): array.get_cell_temperature(irrads, temps, winds, model) - - -def test_huld(): - pdc0 = 100 - res = pvsystem.huld(1000, 25, pdc0, cell_type='cSi') - assert np.isclose(res, pdc0) - exp_sum = np.exp(1) * (np.sum(pvsystem._infer_k_huld('cSi')) + pdc0) - res = pvsystem.huld(1000*np.exp(1), 26, pdc0, cell_type='cSi') - assert np.isclose(res, exp_sum) - res = pvsystem.huld(100, 30, pdc0, k=(1, 1, 1, 1, 1, 1)) - exp_100 = 0.1 * (pdc0 + np.log(0.1) + np.log(0.1)**2 + + 5 + 5*np.log(0.1) - + 5*np.log(0.1)**2 + 25) - assert np.isclose(res, exp_100) - # Series input - eff_irr = pd.Series([1000, 100]) - tm = pd.Series([25, 30]) - expected = pd.Series([pdc0, exp_100]) - res = pvsystem.huld(eff_irr, tm, pdc0, k=(1, 1, 1, 1, 1, 1)) - assert_allclose(res, expected) - with pytest.raises(ValueError, - match='Either k or cell_type must be specified'): - res = pvsystem.huld(1000, 25, 100) From e5f1e8164088b6482e8d8b5c029672ebaf845e3f Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 9 Jan 2024 10:26:29 -0700 Subject: [PATCH 07/22] formatting --- pvlib/pvarray.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index e2e0825bf3..737805243b 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -251,7 +251,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): G' &= \frac{G_{poa eff}}{1000} - T' &= T_{mod} - 25 °C + T' &= T_{mod} - 25^{\circ}C Parameters @@ -263,11 +263,11 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): pdc0: numeric Power of the modules at 1000 W/m^2 and cell reference temperature. [W] k : tuple, optional - Empirical coefficients used in the power model. Length 6. If `k` is - not provided, `cell_type` must be specified. + Empirical coefficients used in the power 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'`. Used to look - up default values for `k` if `k` is not specified. + If provided, must be one of ``'cSi'``, ``'CIS'``, or ``'CdTe'``. + Used to look up default values for ``k`` if ``k`` is not specified. Returns ------- @@ -277,7 +277,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): Raises ------ ValueError - If neither `k` nor `cell_type` are specified. + If neither ``k`` nor ``cell_type`` are specified. References ---------- @@ -297,10 +297,9 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): # accomodate gprime<=0 with np.errstate(divide='ignore'): logGprime = np.log(gprime, out=np.zeros_like(gprime), - where=np.array(gprime > 0)) + where=np.array(gprime > 0)) # Eq. 1 in [1] pdc = gprime * (pdc0 + k[0] * logGprime + k[1] * logGprime**2 + k[2] * tprime + k[3] * tprime * logGprime + k[4] * tprime * logGprime**2 + k[5] * tprime**2) return pdc - From 4ad5a2a79b86cf5a10009daeb9dc16cc2faab079 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 9 Jan 2024 13:37:09 -0700 Subject: [PATCH 08/22] fix look-up parameters --- pvlib/pvarray.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index 737805243b..fc62c38e0f 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -225,16 +225,21 @@ def adr_wrapper(xdata, *params): return popt -def _infer_k_huld(cell_type): +def _infer_k_huld(cell_type, pdc0): # from PVGIS documentation, "PVGIS data sources & calculation methods", # Section 5.2.3, accessed 12/22/2023 + # The parameters in PVGIS' documentation are for a version of Huld's + # equation that has factored Pdc0 out of the polynomial: + # P = G/1000 * Pdc0 * (1 + k1 log(Geff) + ...) so these parameters are + # multiplied by pdc0 huld_params = {'csi': (-0.017237, -0.040465, -0.004702, 0.000149, 0.000170, 0.000005), 'cis': (-0.005554, -0.038724, -0.003723, -0.000905, -0.001256, 0.000001), 'cdte': (-0.046689, -0.072844, -0.002262, 0.000276, 0.000159, -0.000006)} - return huld_params[cell_type.lower()] + k = tuple([x*pdc0 for x in huld_params[cell_type.lower()]]) + return k def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): @@ -288,7 +293,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): """ if k is None: if cell_type is not None: - k = _infer_k_huld(cell_type) + k = _infer_k_huld(cell_type, pdc0) else: raise ValueError('Either k or cell_type must be specified') From 975b33e1aecd416b50699c378b277e2f680e8fe0 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 9 Jan 2024 13:59:35 -0700 Subject: [PATCH 09/22] fix test --- pvlib/tests/test_pvarray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/tests/test_pvarray.py b/pvlib/tests/test_pvarray.py index 73e78f6f05..693ef78b2a 100644 --- a/pvlib/tests/test_pvarray.py +++ b/pvlib/tests/test_pvarray.py @@ -53,7 +53,7 @@ def test_huld(): pdc0 = 100 res = pvarray.huld(1000, 25, pdc0, cell_type='cSi') assert np.isclose(res, pdc0) - exp_sum = np.exp(1) * (np.sum(pvarray._infer_k_huld('cSi')) + pdc0) + exp_sum = np.exp(1) * (np.sum(pvarray._infer_k_huld('cSi', pdc0)) + pdc0) res = pvarray.huld(1000*np.exp(1), 26, pdc0, cell_type='cSi') assert np.isclose(res, exp_sum) res = pvarray.huld(100, 30, pdc0, k=(1, 1, 1, 1, 1, 1)) From 34b3824d5cb4e87bb9c344ae9c5c22affa3c4988 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 10 Jan 2024 14:13:01 -0700 Subject: [PATCH 10/22] add Note to docstring --- pvlib/pvarray.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index fc62c38e0f..ef7688addb 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -253,6 +253,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): P_{dc} &= G' ( P_{dc0} + k_1 \log(G') + k_2 \log^2 (G') + k_3 T' + k_4 T' \log(G') + k_5 T' \log^2 (G') + k_6 T'^2) + :label: main G' &= \frac{G_{poa eff}}{1000} @@ -284,6 +285,33 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): ValueError If neither ``k`` nor ``cell_type`` are specified. + Notes + ----- + Eq. :eq:`main` is from [1]_. The expression used in PVGIS documentation is + different, factoring :math:`P_{dc0}` out of the polynomial. PVGIS + documentation shows a table of default parameters :math:`k'` for different + cell types. The parameters :math:`k'` differ from the parameters expected + by :py:func:`huld` by the factor ``pdc0``, that is, + + .. math:: + + k = P_{dc0} k' + + :py:func:`huld` is a component of the PV performance model implemented in + PVGIS. Among other components, the full PVGIS model includes: + - the Faiman model for cell temperature + :py:func:`pvlib.temperature.faiman` + - the Martin and Ruiz model for the incidence angle modifier (IAM) + :py:func:`pvlib.iam.martin_ruiz` + - a custom model for a spectral adjustment factor + The PVGIS API (see :py:func:`pvlib.iotools.pvgis.get_pvgis_hourly) returns + broadband plane-of-array irradiance (``poa_global``) and DC power (``P``). + ``poa_global`` is irradiance before applying the IAM and spectral + adjustments, where as ``effective_irradiance`` input to :py:func:`huld` + should be after these adjustments are applied. Users comparing output of + :py:func:`huld` to PVGIS' ``P`` should expect differences unless + ``effective_irradiance`` is computed the same way as done by PVGIS. + References ---------- .. [1] T. Huld, G. Friesen, A. Skoczek, R. Kenny, T. Sample, M. Field, From 7d59f98914aa94ab7856818e8f742bdc81be5d5c Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 10 Jan 2024 14:24:18 -0700 Subject: [PATCH 11/22] edit the Note --- pvlib/pvarray.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index ef7688addb..1dcd1c2a3c 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -253,7 +253,6 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): P_{dc} &= G' ( P_{dc0} + k_1 \log(G') + k_2 \log^2 (G') + k_3 T' + k_4 T' \log(G') + k_5 T' \log^2 (G') + k_6 T'^2) - :label: main G' &= \frac{G_{poa eff}}{1000} @@ -287,11 +286,17 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): Notes ----- - Eq. :eq:`main` is from [1]_. The expression used in PVGIS documentation is - different, factoring :math:`P_{dc0}` out of the polynomial. PVGIS - documentation shows a table of default parameters :math:`k'` for different - cell types. The parameters :math:`k'` differ from the parameters expected - by :py:func:`huld` by the factor ``pdc0``, that is, + The equation for :math:`P_{dc}` is from [1]_. The expression used in PVGIS + documentation is differs by factoring :math:`P_{dc0}` out of the + polynomial: + + ..math:: + P_{dc} = G' P_{dc0} (1 + k'_1 \log(G') + k'_2 \log^2 (G') + k''_3 T' + + k'_4 T' \log(G') + k'_5 T' \log^2 (G') + k'_6 T'^2) + + PVGIS documentation shows a table of default parameters :math:`k'` for + different cell types. The parameters :math:`k'` differ from the parameters + :math:`k` expected by :py:func:`huld` by the factor ``pdc0``, that is, .. math:: @@ -304,13 +309,13 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): - the Martin and Ruiz model for the incidence angle modifier (IAM) :py:func:`pvlib.iam.martin_ruiz` - a custom model for a spectral adjustment factor - The PVGIS API (see :py:func:`pvlib.iotools.pvgis.get_pvgis_hourly) returns + The PVGIS API (see :py:func:`pvlib.iotools.pvgis.get_pvgis_hourly`) returns broadband plane-of-array irradiance (``poa_global``) and DC power (``P``). ``poa_global`` is irradiance before applying the IAM and spectral - adjustments, where as ``effective_irradiance`` input to :py:func:`huld` - should be after these adjustments are applied. Users comparing output of - :py:func:`huld` to PVGIS' ``P`` should expect differences unless - ``effective_irradiance`` is computed the same way as done by PVGIS. + adjustments. In contrast the ``effective_irradiance`` for :py:func:`huld` + is after these adjustments are applied. Users comparing output of + :py:func:`huld` to PVGIS' ``P`` values should expect differences unless + ``effective_irradiance`` is computed in the same way as done by PVGIS. References ---------- From b47563bce90a2ae432b7fa3d8536c85e8ca657c3 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 10 Jan 2024 14:33:46 -0700 Subject: [PATCH 12/22] edit the Note some more --- pvlib/pvarray.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index 1dcd1c2a3c..eb62115fb5 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -291,7 +291,8 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): polynomial: ..math:: - P_{dc} = G' P_{dc0} (1 + k'_1 \log(G') + k'_2 \log^2 (G') + k''_3 T' + + + P_{dc} = G' P_{dc0} (1 + k'_1 \log(G') + k'_2 \log^2 (G') + k'_3 T' + k'_4 T' \log(G') + k'_5 T' \log^2 (G') + k'_6 T'^2) PVGIS documentation shows a table of default parameters :math:`k'` for @@ -309,7 +310,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): - the Martin and Ruiz model for the incidence angle modifier (IAM) :py:func:`pvlib.iam.martin_ruiz` - a custom model for a spectral adjustment factor - The PVGIS API (see :py:func:`pvlib.iotools.pvgis.get_pvgis_hourly`) returns + The PVGIS API (see :py:func:`pvlib.iotools.get_pvgis_hourly`) returns broadband plane-of-array irradiance (``poa_global``) and DC power (``P``). ``poa_global`` is irradiance before applying the IAM and spectral adjustments. In contrast the ``effective_irradiance`` for :py:func:`huld` From 00b125cdc6cfbbcebc70e9388bfe44dd03deb60e Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 10 Jan 2024 15:20:10 -0700 Subject: [PATCH 13/22] edit the Note a third time --- pvlib/pvarray.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index eb62115fb5..ee2fc5d0ec 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -290,7 +290,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): documentation is differs by factoring :math:`P_{dc0}` out of the polynomial: - ..math:: + .. math:: P_{dc} = G' P_{dc0} (1 + k'_1 \log(G') + k'_2 \log^2 (G') + k'_3 T' + k'_4 T' \log(G') + k'_5 T' \log^2 (G') + k'_6 T'^2) @@ -314,7 +314,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): broadband plane-of-array irradiance (``poa_global``) and DC power (``P``). ``poa_global`` is irradiance before applying the IAM and spectral adjustments. In contrast the ``effective_irradiance`` for :py:func:`huld` - is after these adjustments are applied. Users comparing output of + should have the IAM and spectral adjustments. Users comparing output of :py:func:`huld` to PVGIS' ``P`` values should expect differences unless ``effective_irradiance`` is computed in the same way as done by PVGIS. From 681d3cb8f1c6e72916d8a17afee0b31e0149455b Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sat, 13 Jan 2024 09:31:45 -0700 Subject: [PATCH 14/22] Update pvlib/pvarray.py Co-authored-by: Kevin Anderson --- pvlib/pvarray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index ee2fc5d0ec..7a34926fe9 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -287,7 +287,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): Notes ----- The equation for :math:`P_{dc}` is from [1]_. The expression used in PVGIS - documentation is differs by factoring :math:`P_{dc0}` out of the + documentation differs by factoring :math:`P_{dc0}` out of the polynomial: .. math:: From b9ba90068d38ec94e6fcaa43872c89459d16a2c7 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 16 Jan 2024 09:32:47 -0700 Subject: [PATCH 15/22] Update pvlib/pvarray.py --- pvlib/pvarray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index 7a34926fe9..d662c0a037 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -305,7 +305,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): :py:func:`huld` is a component of the PV performance model implemented in PVGIS. Among other components, the full PVGIS model includes: - - the Faiman model for cell temperature + - the Faiman model for module temperature :py:func:`pvlib.temperature.faiman` - the Martin and Ruiz model for the incidence angle modifier (IAM) :py:func:`pvlib.iam.martin_ruiz` From 7b2cf1af001f9eb455d7c3dc80f3762326306405 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 17 Jan 2024 10:05:50 -0700 Subject: [PATCH 16/22] Update pvlib/pvarray.py Co-authored-by: Anton Driesse --- pvlib/pvarray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index d662c0a037..fda2afd6aa 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -266,7 +266,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): temp_mod: numeric Module back-surface temperature. [C] pdc0: numeric - Power of the modules at 1000 W/m^2 and cell reference temperature. [W] + Power of the modules at reference conditions 1000 W/m^2 and 25°C. [W] k : tuple, optional Empirical coefficients used in the power model. Length 6. If ``k`` is not provided, ``cell_type`` must be specified. From c4f64d9c9125e484d244f5971025e983b8405745 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 17 Jan 2024 10:14:53 -0700 Subject: [PATCH 17/22] Apply suggestions from code review --- pvlib/pvarray.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index fda2afd6aa..b9abe72ec3 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -262,11 +262,12 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): Parameters ---------- effective_irradiance : numeric - The irradiance that is converted to photocurrent. [W/m^2] + The irradiance that is converted to photocurrent. [:math:`W/m^2`] temp_mod: numeric Module back-surface temperature. [C] pdc0: numeric - Power of the modules at reference conditions 1000 W/m^2 and 25°C. [W] + Power of the modules at reference conditions :math:`1000 W/m^2` + and :math:`25^{\circ}C`. [W] k : tuple, optional Empirical coefficients used in the power model. Length 6. If ``k`` is not provided, ``cell_type`` must be specified. From 6cdd7b09dcef64759348a8b49e45b5d75e8f4a0c Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 17 Jan 2024 10:25:12 -0700 Subject: [PATCH 18/22] Update pvlib/pvarray.py --- pvlib/pvarray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index b9abe72ec3..10d516434b 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -266,7 +266,7 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): temp_mod: numeric Module back-surface temperature. [C] pdc0: numeric - Power of the modules at reference conditions :math:`1000 W/m^2` + 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 From 7030840b372018fe6d464d261f8c7b8b4cdb0508 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 30 Jan 2024 14:14:12 -0700 Subject: [PATCH 19/22] add note about negative power at low irradiance --- pvlib/pvarray.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index ee2fc5d0ec..709cd08362 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -295,6 +295,10 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): P_{dc} = G' P_{dc0} (1 + k'_1 \log(G') + k'_2 \log^2 (G') + k'_3 T' + k'_4 T' \log(G') + k'_5 T' \log^2 (G') + k'_6 T'^2) + + Users should be aware that at very low irradiance, i.e., + :math:`G' < 20 W/m^2`, :math:`P_{dc}` may be negative. + PVGIS documentation shows a table of default parameters :math:`k'` for different cell types. The parameters :math:`k'` differ from the parameters :math:`k` expected by :py:func:`huld` by the factor ``pdc0``, that is, From f7ed02e7de1f6a433d49dce4afa2d11100742b17 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 30 Jan 2024 14:50:30 -0700 Subject: [PATCH 20/22] Update docs/sphinx/source/whatsnew/v0.10.4.rst Co-authored-by: Adam R. Jensen <39184289+AdamRJensen@users.noreply.github.com> --- 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 d1d81d245f..adb9c3e251 100644 --- a/docs/sphinx/source/whatsnew/v0.10.4.rst +++ b/docs/sphinx/source/whatsnew/v0.10.4.rst @@ -7,7 +7,7 @@ v0.10.4 (Anticipated March, 2024) Enhancements ~~~~~~~~~~~~ -* Added the Huld PV model (:pull:`1940`) +* Added the Huld PV model used by PVGIS (:pull:`1940`) Bug fixes From fdeeef88c0f56e939085c05a2629e2fb8a392292 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 31 Jan 2024 12:33:40 -0700 Subject: [PATCH 21/22] Update pvlib/pvarray.py --- pvlib/pvarray.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index b64136cfe4..721043903d 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -297,8 +297,9 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): k'_4 T' \log(G') + k'_5 T' \log^2 (G') + k'_6 T'^2) - Users should be aware that at very low irradiance, i.e., - :math:`G' < 20 W/m^2`, :math:`P_{dc}` may be negative. + With default values for ``k``, at very low irradiance, i.e., + :math:`G' < 20 W/m^2`, :math:`P_{dc}` may be negative + due to the terms involving :math:`\log(G')`. PVGIS documentation shows a table of default parameters :math:`k'` for different cell types. The parameters :math:`k'` differ from the parameters From d86de4503639c378f4c9adcb9380a96319edd13f Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 31 Jan 2024 12:37:22 -0700 Subject: [PATCH 22/22] tweak the note --- pvlib/pvarray.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pvlib/pvarray.py b/pvlib/pvarray.py index 721043903d..a8bbd36eb3 100644 --- a/pvlib/pvarray.py +++ b/pvlib/pvarray.py @@ -297,18 +297,18 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None): k'_4 T' \log(G') + k'_5 T' \log^2 (G') + k'_6 T'^2) - With default values for ``k``, at very low irradiance, i.e., - :math:`G' < 20 W/m^2`, :math:`P_{dc}` may be negative - due to the terms involving :math:`\log(G')`. - PVGIS documentation shows a table of default parameters :math:`k'` for different cell types. The parameters :math:`k'` differ from the parameters - :math:`k` expected by :py:func:`huld` by the factor ``pdc0``, that is, + :math:`k` for :py:func:`huld` by the factor ``pdc0``, that is, .. math:: k = P_{dc0} k' + With default values for :math:`k`, at very low irradiance, i.e., + :math:`G' < 20 W/m^2`, :math:`P_{dc}` may be negative + due to the terms involving :math:`\log(G')`. + :py:func:`huld` is a component of the PV performance model implemented in PVGIS. Among other components, the full PVGIS model includes: - the Faiman model for module temperature