Skip to content

update spectral_factor_firstsolar #2100

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

Merged
merged 25 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
78 changes: 48 additions & 30 deletions pvlib/spectrum/mismatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,21 +253,17 @@ def integrate(e):
def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
module_type=None, coefficients=None,
min_precipitable_water=0.1,
max_precipitable_water=8):
max_precipitable_water=8,
min_airmass_absolute=0.58,
max_airmass_absolute=10):
r"""
Spectral mismatch modifier based on precipitable water and absolute
(pressure-adjusted) air mass.

Estimates a spectral mismatch modifier :math:`M` representing the effect on
module short circuit current of variation in the spectral
irradiance. :math:`M` is estimated from absolute (pressure currected) air
mass, :math:`AM_a`, and precipitable water, :math:`Pw`, using the following
function:

.. math::

M = c_1 + c_2 AM_a + c_3 Pw + c_4 AM_a^{0.5}
+ c_5 Pw^{0.5} + c_6 \frac{AM_a} {Pw^{0.5}}
Estimates the spectral mismatch modifier, :math:`M`, representing the
effect of variation in the spectral irradiance on the module short circuit
current :math:`M` is estimated from absolute (pressure corrected) air
mass, :math:`AM_a`, and precipitable water, :math:`Pw`

Default coefficients are determined for several cell types with
known quantum efficiency curves, by using the Simple Model of the
Expand All @@ -281,7 +277,7 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
* spectrum simulated on a plane normal to the sun
* All other parameters fixed at G173 standard

From these simulated spectra, M is calculated using the known
From these simulated spectra, :math:`M` is calculated using the known
quantum efficiency curves. Multiple linear regression is then
applied to fit Eq. 1 to determine the coefficients for each module.

Expand Down Expand Up @@ -325,12 +321,20 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
min_precipitable_water : float, default 0.1
minimum atmospheric precipitable water. Any ``precipitable_water``
value lower than ``min_precipitable_water``
is set to ``min_precipitable_water`` to avoid model divergence. [cm]
is set to ``min_precipitable_water``. [cm]

max_precipitable_water : float, default 8
maximum atmospheric precipitable water. Any ``precipitable_water``
value greater than ``max_precipitable_water``
is set to ``np.nan`` to avoid model divergence. [cm]
is set to ``np.nan``. [cm]

min_airmass_absolute : float, default 0.58
minimum absolute airmass. Any ``airmass_absolute`` value lower than
``min_airmass_absolute`` is set to ``min_airmass_absolute``.

max_airmass_absolute : float, default 10
minimum absolute airmass. Any ``airmass_absolute`` value greater than
``max_airmass_absolute`` is set to ``max_airmass_absolute``.

Returns
-------
Expand All @@ -340,6 +344,20 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
effective irradiance, i.e., the irradiance that is converted to
electrical current.

Notes
----
The ``spectral_factor_firstsolar`` model takes the following form:

.. math::

M = c_1 + c_2 AM_a + c_3 Pw + c_4 AM_a^{0.5}
+ c_5 Pw^{0.5} + c_6 \frac{AM_a} {Pw^{0.5}}.

The default values for the limits applied to :math::`AM_a` and :math::`Pw`
via the ``min_precipitable_water``, ``max_precipitable_water``,
``min_airmass_absolute``, and ``max_airmass_absolute`` are set to prevent
divergence of the model presented above.

References
----------
.. [1] Gueymard, Christian. SMARTS2: a simple model of the atmospheric
Expand All @@ -360,31 +378,31 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
# --- Screen Input Data ---

# *** Pw ***
# Replace Pw Values below 0.1 cm with 0.1 cm to prevent model from
# diverging"
# Replace Pw Values below min_pw with min_pw"
pw = np.atleast_1d(precipitable_water)
pw = pw.astype('float64')
if np.min(pw) < min_precipitable_water:
pw = np.maximum(pw, min_precipitable_water)
warn('Exceptionally low pw values replaced with '
f'{min_precipitable_water} cm to prevent model divergence')
warn('Low pw values replaced with 'f'{min_precipitable_water} cm in '
'the calculation of spectral mismatch.')

# Warn user about Pw data that is exceptionally high
if np.max(pw) > max_precipitable_water:
pw[pw > max_precipitable_water] = np.nan
warn('Exceptionally high pw values replaced by np.nan: '
'check input data.')
warn('High pw values replaced with np.nan in '
'the calculation of spectral mismatch.')

# *** AMa ***
# Replace Extremely High AM with AM 10 to prevent model divergence
# Replace Extremely High AM with max_am
# AM > 10 will only occur very close to sunset
if np.max(airmass_absolute) > 10:
airmass_absolute = np.minimum(airmass_absolute, 10)

# Warn user about AMa data that is exceptionally low
if np.min(airmass_absolute) < 0.58:
warn('Exceptionally low air mass: ' +
'model not intended for extra-terrestrial use')
if np.max(airmass_absolute) > max_airmass_absolute:
airmass_absolute = np.minimum(airmass_absolute, max_airmass_absolute)
warn('High AMa values replaced with 'f'{max_airmass_absolute} in the'
' calculation of spectral mismatch.')

if np.min(airmass_absolute) < min_airmass_absolute:
airmass_absolute = np.maximum(airmass_absolute, min_airmass_absolute)
warn('Low AMa values replaced with 'f'{min_airmass_absolute} in the'
' calculation of spectral mismatch.')
# pvl_absoluteairmass(1,pvl_alt2pres(4340)) = 0.58 Elevation of
# Mina Pirquita, Argentian = 4340 m. Highest elevation city with
# population over 50,000.
Expand Down Expand Up @@ -476,7 +494,7 @@ def spectral_factor_caballero(precipitable_water, airmass_absolute, aod500,
available here via the ``module_type`` parameter were determined
by fitting the model equations to spectral factors calculated from
global tilted spectral irradiance measurements taken in the city of
Jaén, Spain. See [1]_ for details.
Jaén, Spain. See [1]_ for details.

Parameters
----------
Expand Down
17 changes: 11 additions & 6 deletions pvlib/tests/test_spectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,35 +225,40 @@ def test_spectral_factor_firstsolar_ambiguous_both():


def test_spectral_factor_firstsolar_large_airmass():
# test that airmass > 10 is treated same as airmass==10
# test that airmass > 10 is treated same as airmass=10
m_eq10 = spectrum.spectral_factor_firstsolar(1, 10, 'monosi')
m_gt10 = spectrum.spectral_factor_firstsolar(1, 15, 'monosi')
assert_allclose(m_eq10, m_gt10)
with pytest.warns(UserWarning, match='High AMa values replaced with'):
_ = spectrum.spectral_factor_firstsolar(1, 15, 'monosi')


def test_spectral_factor_firstsolar_low_airmass():
with pytest.warns(UserWarning, match='Exceptionally low air mass'):
m_eq58 = spectrum.spectral_factor_firstsolar(1, 0.58, 'monosi')
m_lt58 = spectrum.spectral_factor_firstsolar(1, 0.1, 'monosi')
assert_allclose(m_eq58, m_lt58)
with pytest.warns(UserWarning, match='Low AMa values replaced with'):
_ = spectrum.spectral_factor_firstsolar(1, 0.1, 'monosi')


def test_spectral_factor_firstsolar_range():
with pytest.warns(UserWarning, match='Exceptionally high pw values'):
with pytest.warns(UserWarning, match='High pw values replaced with'):
out = spectrum.spectral_factor_firstsolar(np.array([.1, 3, 10]),
np.array([1, 3, 5]),
module_type='monosi')
expected = np.array([0.96080878, 1.03055092, np.nan])
assert_allclose(out, expected, atol=1e-3)
with pytest.warns(UserWarning, match='Exceptionally high pw values'):
with pytest.warns(UserWarning, match='High pw values replaced with'):
out = spectrum.spectral_factor_firstsolar(6, 1.5,
max_precipitable_water=5,
module_type='monosi')
with pytest.warns(UserWarning, match='Exceptionally low pw values'):
with pytest.warns(UserWarning, match='Low pw values replaced with'):
out = spectrum.spectral_factor_firstsolar(np.array([0, 3, 8]),
np.array([1, 3, 5]),
module_type='monosi')
expected = np.array([0.96080878, 1.03055092, 1.04932727])
assert_allclose(out, expected, atol=1e-3)
with pytest.warns(UserWarning, match='Exceptionally low pw values'):
with pytest.warns(UserWarning, match='Low pw values replaced with'):
out = spectrum.spectral_factor_firstsolar(0.2, 1.5,
min_precipitable_water=1,
module_type='monosi')
Expand Down
Loading