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 1 commit
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
50 changes: 30 additions & 20 deletions pvlib/spectrum/mismatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,16 @@ 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
irradiance. :math:`M` is estimated from absolute (pressure corrected) air
mass, :math:`AM_a`, and precipitable water, :math:`Pw`, using the following
function:

Expand Down Expand Up @@ -325,12 +327,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 Down Expand Up @@ -360,31 +370,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.')
pw = np.minimum(pw, max_precipitable_water)
warn('High pw values replaced with 'f'{max_precipitable_water} cm 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
19 changes: 12 additions & 7 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])
expected = np.array([0.96080878, 1.03055092, 1.04932727])
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