Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method to fit Huld model #1979

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/sphinx/source/reference/pv_modeling/system_models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ PVGIS model
:toctree: ../generated/

pvarray.huld
pvarray.fit_huld
1 change: 1 addition & 0 deletions docs/sphinx/source/whatsnew/v0.10.4.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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`)
cwhanse marked this conversation as resolved.
Show resolved Hide resolved
* Add :py:func:`~pvlib.iotools.get_solargis` for retrieving Solargis
irradiance data. (:pull:`1969`)
* Added function :py:func:`pvlib.shading.projected_solar_zenith_angle`,
Expand Down
67 changes: 66 additions & 1 deletion pvlib/pvarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@
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'``.
Expand Down Expand Up @@ -348,3 +348,68 @@
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):
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
cwhanse marked this conversation as resolved.
Show resolved Hide resolved
Module back-surface temperature. [C]
pdc: numeric
cwhanse marked this conversation as resolved.
Show resolved Hide resolved
DC power at ``effectuve_irradiance`` and ``temp_mod``. [W]
cwhanse marked this conversation as resolved.
Show resolved Hide resolved

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
cwhanse marked this conversation as resolved.
Show resolved Hide resolved
'''

try:
import statsmodels.api as sm
except ImportError:
raise ImportError(

Check warning on line 395 in pvlib/pvarray.py

View check run for this annotation

Codecov / codecov/patch

pvlib/pvarray.py#L394-L395

Added lines #L394 - L395 were not covered by tests
'fit_huld requires statsmodels')

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))
kandersolar marked this conversation as resolved.
Show resolved Hide resolved

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)
cwhanse marked this conversation as resolved.
Show resolved Hide resolved
rlm_result = rlm_model.fit()
pdc0 = rlm_result.params[0]
k = rlm_result.params[1:]
return pdc0, k
18 changes: 17 additions & 1 deletion pvlib/tests/test_pvarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -69,3 +69,19 @@ def test_huld():
with pytest.raises(ValueError,
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
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)
Loading