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 JRC spectral factor correction #2088

Merged
merged 22 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 14 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
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ Spectrum
spectrum.spectral_factor_firstsolar
spectrum.spectral_factor_sapm
spectrum.spectral_factor_pvspec
spectrum.spectral_factor_jrc
spectrum.sr_to_qe
spectrum.qe_to_sr
8 changes: 8 additions & 0 deletions docs/sphinx/source/whatsnew/v0.11.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ Enhancements
efficiency ([unitless]) and vice versa. The conversion functions are
:py:func:`pvlib.spectrum.sr_to_qe` and :py:func:`pvlib.spectrum.qe_to_sr`
respectively. (:issue:`2040`, :pull:`2041`)
* Add function :py:func:`pvlib.spectrum.spectral_factor_pvspec`, which
calculates the spectral mismatch factor as a function of absolute airmass and
clearsky index using the PVSPEC model.
(:issue:`1950`, :issue:`2065`, :pull:`2072`)
RDaxini marked this conversation as resolved.
Show resolved Hide resolved
* Add function :py:func:`pvlib.spectrum.spectral_factor_jrc`, which calculates
the spectral mismatch factor as a function of absolute airmass and clearsky
index using the JRC model.
(:issue:`1950`, :issue:`2065`, :issue:`2087`, :pull:`2088`)
* Add function :py:func:`pvlib.spectrum.spectral_factor_pvspec`, which calculates the
spectral mismatch factor as a function of absolute airmass and clearsky index
using the PVSPEC model. (:issue:`1950`, :issue:`2065`, :pull:`2072`)
Expand Down
1 change: 1 addition & 0 deletions pvlib/spectrum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
spectral_factor_firstsolar,
spectral_factor_sapm,
spectral_factor_pvspec,
spectral_factor_jrc,
sr_to_qe,
qe_to_sr
)
109 changes: 109 additions & 0 deletions pvlib/spectrum/mismatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,115 @@ def spectral_factor_pvspec(airmass_absolute, clearsky_index,
return mismatch


def spectral_factor_jrc(airmass, clearsky_index, module_type=None,
coefficients=None):
r"""
Estimate a technology-specific spectral mismatch modifier from absolute
airmass and clear sky index using the JRC model.

The JRC spectral mismatch model includes the effects of cloud cover on
the irradiance spectrum. Model coefficients are derived using measurements
of irradiance and module performance at the Joint Research Centre (JRC) in
Ispra, Italy (45°48'N, 8°37'E). Coefficients for two module types are
RDaxini marked this conversation as resolved.
Show resolved Hide resolved
available via the ``module_type`` parameter. More details on the model can
be found in [1]_.

Parameters
----------
airmass_absolute : numeric
absolute (pressure-adjusted) airmass. [unitless]
RDaxini marked this conversation as resolved.
Show resolved Hide resolved

clearsky_index: numeric
clear sky index. [unitless]

module_type : str, optional
One of the following PV technology strings from [1]_:

* ``'cdte'``, - anonymous CdTe module.
RDaxini marked this conversation as resolved.
Show resolved Hide resolved
* ``'multisi'`` - anonymous multicrystalline Si module.

coefficients : array-like, optional
user-defined coefficients, if not using one of the default coefficient
sets via the ``module_type`` parameter.

Returns
-------
mismatch: numeric
spectral mismatch factor (unitless) which is multiplied
with broadband irradiance reaching a module's cells to estimate
effective irradiance, i.e., the irradiance that is converted to
electrical current.

Notes
-----
The JRC model parameterises the spectral mismatch factor as a function
of absolute air mass and the clear sky index as follows:

.. math::

M = 1 + a_1(e^{-k_c}-e^{-1}) + a_2(k_c-1)+a_3(AM-1.5),
RDaxini marked this conversation as resolved.
Show resolved Hide resolved

where :math:`M` is the spectral mismatch factor, :math:`k_c` is the clear
sky index, :math:`AM` is the air mass, :math:`e` is Euler's number, and
:math:`a_1, a_2, a_3` are module-specific coefficients. The :math:`a_n`
coefficients available via the ``coefficients`` parameter differ from the
:math:`k_n` coefficients documented in [1]_ in that they are normalised by
the specific short-circuit current value, :math:`I_{sc0}^*`, which is the
expected short-circuit current at standard test conditions indoors. The
model used to estimate the air mass (denoted as :math:`AM`) is not stated
in the original publication. The clear sky index, which is the ratio of GHI
to clear sky GHI, uses the ESRA model [2]_ to estimate the clear sky GHI.
Prior to the calculation of :math:`k_c`, the irradiance measurements are
corrected for angle of incidence using the Martin and Ruiz model [3]_.
RDaxini marked this conversation as resolved.
Show resolved Hide resolved

References
----------
.. [1] Huld, T., Tony, S., and Ewan, D., 2009. A simple model
RDaxini marked this conversation as resolved.
Show resolved Hide resolved
for estimating the influence of spectrum variations on PV performance.
In Proceedings of the 24th European Photovoltaic Solar Energy
Conference, Hamburg, Germany pp. 3385-3389. 2009. Accessed at:
https://www.researchgate.net/publication/256080247
.. [2] Rigollier, C., Bauer, O., and Wald, L., 2000. On the clear sky model
of the ESRA—European Solar Radiation Atlas—with respect to the Heliosat
method. Solar energy, 68(1), pp.33-48.
:doi:`10.1016/S0038-092X(99)00055-9`
.. [3] Martin, N. and Ruiz, J. M., 2001. Calculation of the PV modules
angular losses under field conditions by means of an analytical model.
Solar Energy Materials and Solar Cells, 70(1), 25-38.
:doi:`10.1016/S0927-0248(00)00408-6`
"""

_coefficients = {}
_coefficients['multisi'] = (0.00172, 0.000508, 0.00000357)
_coefficients['cdte'] = (0.000643, 0.000130, 0.0000108)
# normalise coefficients by I*sc0, see [1]
_coefficients = {
'multisi': tuple(x / 0.00348 for x in _coefficients['multisi']),
'cdte': tuple(x / 0.001150 for x in _coefficients['cdte'])
}
if module_type is not None and coefficients is None:
coefficients = _coefficients[module_type.lower()]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit in favour of handling KeyError's and having custom error messages with the available keys, but I remember some comment somewhere that deemed that unnecessary. Just in case someone else wants to weight in, I'm not requesting changes.

elif module_type is None and coefficients is not None:
pass
elif module_type is None and coefficients is None:
raise ValueError('No valid input provided, both module_type and ' +
'coefficients are None. module_type can be one of ' +
", ".join(_coefficients.keys()))
else:
raise ValueError('Cannot resolve input, must supply only one of ' +
'module_type and coefficients. module_type can be ' +
'one of' ", ".join(_coefficients.keys()))

coeff = coefficients
mismatch = (
1
+ coeff[0] * (np.exp(-clearsky_index) - np.exp(-1))
+ coeff[1] * (clearsky_index - 1)
+ coeff[2] * (airmass - 1.5)
)
return mismatch


def sr_to_qe(sr, wavelength=None, normalize=False):
"""
Convert spectral responsivities to quantum efficiencies.
Expand Down
48 changes: 48 additions & 0 deletions pvlib/tests/test_spectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,54 @@ def test_spectral_factor_pvspec_supplied_ambiguous():
coefficients=None)


@pytest.mark.parametrize("module_type,expected", [
('multisi', np.array([1.06129, 1.03098, 1.01155, 0.99849])),
('cdte', np.array([1.09657, 1.05594, 1.02763, 0.97740])),
])
def test_spectral_factor_jrc(module_type, expected):
ams = np.array([1.0, 1.5, 2.0, 1.5])
kcs = np.array([0.4, 0.6, 0.8, 1.4])
out = spectrum.spectral_factor_jrc(ams, kcs,
module_type=module_type)
assert np.allclose(expected, out, atol=1e-4)


@pytest.mark.parametrize("module_type,expected", [
('multisi', np.array([1.06129, 1.03098, 1.01155, 0.99849])),
('cdte', np.array([1.09657, 1.05594, 1.02763, 0.97740])),
])
def test_spectral_factor_jrc_series(module_type, expected):
ams = pd.Series([1.0, 1.5, 2.0, 1.5])
kcs = pd.Series([0.4, 0.6, 0.8, 1.4])
out = spectrum.spectral_factor_jrc(ams, kcs,
module_type=module_type)
assert isinstance(out, pd.Series)
assert np.allclose(expected, out, atol=1e-4)


def test_spectral_factor_jrc_supplied():
# use the multisi coeffs
coeffs = (0.494, 0.146, 0.00103)
out = spectrum.spectral_factor_jrc(1.0, 0.8, coefficients=coeffs)
expected = 1.01052106
assert_allclose(out, expected, atol=1e-4)


def test_spectral_factor_jrc_supplied_redundant():
# Error when specifying both module_type and coefficients
coeffs = (0.494, 0.146, 0.00103)
with pytest.raises(ValueError, match='supply only one of'):
spectrum.spectral_factor_jrc(1.0, 0.8, module_type='multisi',
coefficients=coeffs)


def test_spectral_factor_jrc_supplied_ambiguous():
# Error when specifying neither module_type nor coefficients
with pytest.raises(ValueError, match='No valid input provided'):
spectrum.spectral_factor_jrc(1.0, 0.8, module_type=None,
coefficients=None)


@pytest.fixture
def sr_and_eqe_fixture():
# Just some arbitrary data for testing the conversion functions
Expand Down
Loading