diff --git a/docs/sphinx/source/reference/effects_on_pv_system_output/spectrum.rst b/docs/sphinx/source/reference/effects_on_pv_system_output/spectrum.rst index 15fd1d09b8..896eb7bb7f 100644 --- a/docs/sphinx/source/reference/effects_on_pv_system_output/spectrum.rst +++ b/docs/sphinx/source/reference/effects_on_pv_system_output/spectrum.rst @@ -16,5 +16,6 @@ Spectrum spectrum.spectral_factor_sapm spectrum.spectral_factor_pvspec spectrum.spectral_factor_jrc + spectrum.spectral_factor_daxini spectrum.sr_to_qe spectrum.qe_to_sr diff --git a/pvlib/spectrum/__init__.py b/pvlib/spectrum/__init__.py index b95f221066..ee84b3b659 100644 --- a/pvlib/spectrum/__init__.py +++ b/pvlib/spectrum/__init__.py @@ -9,6 +9,7 @@ spectral_factor_sapm, spectral_factor_pvspec, spectral_factor_jrc, + spectral_factor_daxini, sr_to_qe, qe_to_sr ) diff --git a/pvlib/spectrum/mismatch.py b/pvlib/spectrum/mismatch.py index 197c226231..e6e99e8212 100644 --- a/pvlib/spectrum/mismatch.py +++ b/pvlib/spectrum/mismatch.py @@ -836,6 +836,132 @@ def spectral_factor_pvspec(airmass_absolute, clearsky_index, return mismatch +def spectral_factor_daxini(ape, band_depth, module_type=None, + coefficients=None): + r""" + Estimate a technology-specific spectral mismatch modifier from the average + photon energy (APE) and the depth of a water absorption band using the + Daxini model. + + Parameters + ---------- + ape : numeric + Average photon energy (APE, :math:`\varphi`) [eV] + + band_depth : numeric + Depth of a spectral band, :math:`\varepsilon` [Wm:math:`^{-2}`] + + module_type : str, optional + One of the following PV technology strings from [1]_: + + * ``asi-t`` - anyonymous triple junction amorphous Si module + * ``'cdte'`` - anyonymous CdTe module. + * ``'multisi'`` - anyonymous multicrystalline Si module + + The default is None. + + coefficients : array-like, optional + User-defined coefficients, if not using one of the default coefficient + sets via the ``module_type`` parameter. The default is None. + + 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 Daxini model parameterises the spectral mismatch factor, :math:`M`, as + a function of the average photon energy, :math:`\varphi`, and the depth of + a water absorption band, :math:`\varepsilon`, as follows: + + .. math:: + + M = a_0 + a_1\varphi + a_2\varepsilon + a_3\varphi^2 + a_4\varepsilon^2 + + a_5\varphi\varepsilon, + + where :math:`a_{0-5}` are module-specific coefficients. In [1]_, + :math:`\varphi` is calculated between the limits of 350nm to 1050nm. + While several atmopsheric windows and water absorption bands within the + spectral irradiance range 350nm-1050nm were tested as candidates for + :math:`\varepsilon`, ultimately the 650nm-670nm is recommended for the PV + devices analysed in the study. It should be noted that "depth" here refers + to the area beneath the spectral irradiance curve within the specified + wavelength limits, which in this case are 650nm-670nm. Therefore, + :math:`\varepsilon` is calculated by integrating the spectral irradiance, + with respect to wavelength, between 650nm-670nm. The purpose of this second + index is to distinguish between different spectra that have the same or + similar APE values, the occurance of which reduces the reliability of a + single-variable APE spectral mismatch estimation function. + + The model is developed and validated using one year of outdoor + meteorological, PV, and spectral irradiance data measured at the National + Renewable Energy Laboratory in Golden, Colorado, USA. The data used are + publicly available and can be found at [2]_ and [3]_. The primary + publications associated with these data releases are [4]_, and [5]_ and + [6]_, respectively. + + References + ---------- + .. [1] Daxini, R., et al., 2023 "Modelling the spectral influence on + photovoltaic device performance using the average photon energy and + the depth of a water absorption band for improved forecasting." + Energy 284: 129046. + :doi:`10.1016/j.energy.2023.129046` + .. [2] Measurement and Instrumentation Data Center (MIDC) + :doi:`10.5439/1052221` + .. [3] Data for Validating Models for PV Module Performance + :doi:`10.21948/1811521` + .. [4] Stoffel, T., and Andreas, A. NREL Solar Radiation Research + laboratory (SRRL): Baseline measurement system (BMS); Golden, + Colorado (data). No. NREL/DA-5500-56488. National Renewable Energy + Laboratory.(NREL), Golden, CO (United States), 1981. + :doi:`10.5439/1052221` + .. [5] Marion, B., et al. Data for validating models for PV module + performance. EMN-DURMAT (EMN-DuraMAT); National Renewable Energy + Laboratory (NREL), Golden, CO (United States), 2021. + :doi:`10.21948/1811521` + .. [6] Marion, B., et al. User's manual for data for validating models for + PV module performance. No. NREL/TP-5200-61610. National Renewable + Energy Laboratory (NREL), Golden, CO (United States), 2014. + :doi:`10.2172/1130632` + """ + + _coefficients = {} + _coefficients['multisi'] = (-0.3998, 1.101, 0.03366, -0.1837, + 1.493e-4, -0.02046) + _coefficients['cdte'] = (-0.5313, 0.7208, 0.02232, 0.05321, + 1.629e-4, -0.01445) + _coefficients['asi-t'] = (-21.94, 22.62, -0.01393, -5.521, + 1.7341e-4, 0.003860) + + if module_type is not None and coefficients is None: + coefficients = _coefficients[module_type.lower()] + 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 + e = band_depth + + mismatch = ( + coeff[0] + coeff[1]*ape + coeff[2]*e + coeff[3]*ape**2 + + coeff[4]*e**2 + coeff[5]*ape*e + ) + + return mismatch + + def spectral_factor_jrc(airmass, clearsky_index, module_type=None, coefficients=None): r""" diff --git a/pvlib/tests/test_spectrum.py b/pvlib/tests/test_spectrum.py index 969fb819b8..b1c4cc4cfb 100644 --- a/pvlib/tests/test_spectrum.py +++ b/pvlib/tests/test_spectrum.py @@ -474,6 +474,43 @@ def test_spectral_factor_jrc_supplied_ambiguous(): coefficients=None) +@pytest.mark.parametrize("module_type,expected", [ + ('multisi', pd.Series([0.98897, 1.000677, 1.00829])), + ('cdte', pd.Series([0.94950, 0.98647, 1.04016])), + ('asi-t', pd.Series([0.92752, 1.02894, 1.13527])), +]) +def test_spectral_factor_daxini_series(module_type, expected): + apes = pd.Series([1.82, 1.87, 1.95]) + bands = pd.Series([2, 4, 8]) + out = spectrum.spectral_factor_daxini(apes, bands, + module_type=module_type) + assert isinstance(out, pd.Series) + assert np.allclose(expected, out, atol=1e-5) + + +def test_spectral_factor_daxini_supplied(): + # use the cdte coeffs + coeffs = (-0.5313, 0.7208, 0.02232, 0.05321, 1.629e-4, -0.01445) + out = spectrum.spectral_factor_daxini(1.87, 4, coefficients=coeffs) + expected = 0.98647 + assert_allclose(out, expected, atol=1e-5) + + +def test_spectral_factor_daxini_supplied_redundant(): + # Error when specifying both module_type and coefficients + coeffs = (-0.5313, 0.7208, 0.02232, 0.05321, 1.629e-4, -0.01445) + with pytest.raises(ValueError, match='supply only one of'): + spectrum.spectral_factor_daxini(1.87, 4, module_type='cdte', + coefficients=coeffs) + + +def test_spectral_factor_daxini_supplied_ambiguous(): + # Error when specifying neither module_type nor coefficients + with pytest.raises(ValueError, match='No valid input provided'): + spectrum.spectral_factor_daxini(1.87, 4, module_type=None, + coefficients=None) + + @pytest.fixture def sr_and_eqe_fixture(): # Just some arbitrary data for testing the conversion functions