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

Implement Caballero et al. spectral factor model #1296

Merged
merged 114 commits into from Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from 113 commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
f634e39
Update atmosphere.py
Jacc0027 Aug 26, 2021
b5d7fec
Update atmosphere.py
Jacc0027 Aug 26, 2021
eb948e0
Update atmosphere.py
Jacc0027 Sep 25, 2021
e27c688
Update atmosphere.py
Jacc0027 Sep 25, 2021
03920a6
Update atmosphere.py
Jacc0027 Sep 25, 2021
be39d2e
Merge branch 'Spectral-corrections' of https://github.com/Jacc0027/pv…
Jacc0027 Sep 25, 2021
c9fefeb
Update atmosphere.py
Jacc0027 Sep 25, 2021
125bc16
Update atmosphere.py
Jacc0027 Sep 25, 2021
6a43156
Update atmosphere.py
Jacc0027 Sep 25, 2021
5b890ab
Update atmosphere.py
Jacc0027 Sep 25, 2021
c602a32
Update atmosphere.py
Jacc0027 Sep 25, 2021
97d0e65
Update atmosphere.py
Jacc0027 Sep 25, 2021
0e2d832
Update atmosphere.py
Jacc0027 Sep 25, 2021
8a473ba
Update atmosphere.py
Jacc0027 Sep 25, 2021
397c3e8
Update atmosphere.py
Jacc0027 Sep 25, 2021
7382e36
Update atmosphere.py
Jacc0027 Sep 25, 2021
ed2d4f6
Update atmosphere.py
Jacc0027 Sep 25, 2021
394c4bc
Update atmosphere.py
Jacc0027 Sep 25, 2021
e89248f
Update atmosphere.py
Jacc0027 Sep 25, 2021
25b43df
Update atmosphere.py
Jacc0027 Sep 25, 2021
d9f466e
Update atmosphere.py
Jacc0027 Sep 25, 2021
cdc64fc
Update atmosphere.py
Jacc0027 Sep 25, 2021
422f49f
Update atmosphere.py
Jacc0027 Sep 25, 2021
48474a7
Update atmosphere.py
Jacc0027 Sep 25, 2021
6e9b9d8
Update atmosphere.py
Jacc0027 Sep 25, 2021
43a188c
Update atmosphere.py
Jacc0027 Sep 25, 2021
710cdd3
Update atmosphere.py
Jacc0027 Sep 25, 2021
46519a8
Update atmosphere.py
Jacc0027 Sep 25, 2021
7bc182f
Update pvlib/atmosphere.py
Jacc0027 Oct 3, 2021
2c36c00
relocation of parameter descriptions according to the order of input …
Jacc0027 Oct 3, 2021
8124675
Merge branch 'Spectral-corrections' of https://github.com/Jacc0027/pv…
Jacc0027 Oct 3, 2021
094d737
Update api.rst
Jacc0027 Oct 3, 2021
f79b8ab
remove input screening
kandersolar Oct 31, 2021
30a6284
move reference values to be optional parameters
kandersolar Oct 31, 2021
38dd836
fix implementation issues
kandersolar Oct 31, 2021
7e0d7df
first cut at tests using file from Jacc0027
kandersolar Oct 31, 2021
3bbf743
Merge pull request #2 from kanderso-nrel/pr1296
Jacc0027 Nov 1, 2021
38fea4d
CI correction. Line #215
Jacc0027 Nov 1, 2021
92bfc55
Update test_atmosphere.py
Jacc0027 Nov 1, 2021
5463533
Update test_atmosphere.py
Jacc0027 Nov 1, 2021
c6cd809
Update test_atmosphere.py
Jacc0027 Nov 1, 2021
2d338b9
Merge branch 'pvlib:master' into Spectral-corrections
Jacc0027 Nov 2, 2021
04339ad
Update atmosphere.py
Jacc0027 Nov 2, 2021
c487317
Update atmosphere.py
Jacc0027 Nov 2, 2021
0744b80
Update atmosphere.py
Jacc0027 Nov 2, 2021
6bc7920
Update atmosphere.py
Jacc0027 Nov 2, 2021
ec20bbc
Testing tests
Jacc0027 Nov 2, 2021
9ebd10e
Update test_atmosphere.py
Jacc0027 Nov 2, 2021
43a30e5
Update test_atmosphere.py
Jacc0027 Nov 2, 2021
39c542b
Update test_atmosphere.py
Jacc0027 Nov 2, 2021
516c059
Update test_atmosphere.py
Jacc0027 Nov 2, 2021
acaf38d
Update test_atmosphere.py
Jacc0027 Nov 2, 2021
f937bba
Update atmosphere.py
Jacc0027 Nov 2, 2021
cab0dfa
Merge remote-tracking branch 'origin/patch-1' into Spectral-corrections
Jacc0027 Nov 2, 2021
fd7ec4a
Test Review
Jacc0027 Nov 2, 2021
f08b846
Update atmosphere.py
Jacc0027 Nov 2, 2021
ee614f5
Update test_atmosphere.py
Jacc0027 Nov 2, 2021
2e02b94
Update test_atmosphere.py
Jacc0027 Nov 2, 2021
4b4e27f
Update test_atmosphere.py
Jacc0027 Nov 2, 2021
d71455d
Update test_atmosphere.py
Jacc0027 Nov 2, 2021
e59f7d6
Update atmosphere.py
Jacc0027 Nov 2, 2021
7bf4e52
Update atmosphere.py
Jacc0027 Nov 2, 2021
dc4af99
Update atmosphere.py
Jacc0027 Nov 3, 2021
c09c462
Update atmosphere.py
Jacc0027 Nov 3, 2021
6334d86
Update test_atmosphere.py
Jacc0027 Nov 3, 2021
72f6cf1
Update atmosphere.py
Jacc0027 Nov 3, 2021
b37cbdb
Update test_atmosphere.py
Jacc0027 Nov 3, 2021
ff3f660
Update test_atmosphere.py
Jacc0027 Nov 3, 2021
b4c9dfb
Update test_atmosphere.py
Jacc0027 Nov 3, 2021
f4ab78f
Update test_atmosphere.py
Jacc0027 Nov 3, 2021
55ac6a6
Update test_atmosphere.py
Jacc0027 Nov 3, 2021
d2e7f70
Update pvlib/atmosphere.py
Jacc0027 Nov 14, 2021
bc353f2
Update atmosphere.py
Jacc0027 May 14, 2023
d93b187
Update atmosphere.py
Jacc0027 May 14, 2023
5165060
Update test_atmosphere.py
Jacc0027 May 14, 2023
5b16ce3
Update api.rst
Jacc0027 May 14, 2023
eb26a2d
Update test_atmosphere.py
Jacc0027 May 14, 2023
4957436
Update atmosphere.py
Jacc0027 May 14, 2023
6e10781
Update pvlib/atmosphere.py
Jacc0027 May 16, 2023
4f23ad6
Update pvlib/atmosphere.py
Jacc0027 May 16, 2023
a7b9309
Update pvlib/atmosphere.py
Jacc0027 May 16, 2023
a4560cc
Update pvlib/atmosphere.py
Jacc0027 May 16, 2023
f6d33d4
Update pvlib/atmosphere.py
Jacc0027 May 16, 2023
834fd91
Update pvlib/atmosphere.py
Jacc0027 May 16, 2023
b7d643e
Update atmosphere.py
Jacc0027 May 16, 2023
a07c7b6
Update atmosphere.py
Jacc0027 May 16, 2023
f9df15b
Update atmosphere.py
Jacc0027 May 17, 2023
e1633bf
Update atmosphere.py
Jacc0027 May 17, 2023
fea2314
Update api.rst
Jacc0027 May 17, 2023
b3b5ee3
Merge remote-tracking branch 'upstream/main' into Spectral-corrections
Jacc0027 May 17, 2023
1be7250
Delete api.rst
Jacc0027 May 17, 2023
6a5bbcb
Merge branch 'Spectral-corrections' of https://github.com/Jacc0027/pv…
Jacc0027 May 17, 2023
fc37b2d
Update test_atmosphere.py
Jacc0027 May 18, 2023
231129f
Update atmosphere.py
Jacc0027 May 18, 2023
ce47bf9
Update test_atmosphere.py
Jacc0027 May 18, 2023
5acd22c
Update atmosphere.py
Jacc0027 May 18, 2023
91e6726
Update test_atmosphere.py
Jacc0027 May 18, 2023
0c61d41
Merge branch 'Spectral-corrections' of https://github.com/Jacc0027/pv…
Jacc0027 May 18, 2023
7132f5d
Update test_atmosphere.py
Jacc0027 May 18, 2023
ffdd0ba
Update test_atmosphere.py
Jacc0027 May 18, 2023
af41b57
Update test_atmosphere.py
Jacc0027 May 18, 2023
3ad15bb
Update test_atmosphere.py
Jacc0027 May 18, 2023
c314d3c
Update test_atmosphere.py
Jacc0027 May 18, 2023
b56307b
update function logic and get tests working
kandersolar May 19, 2023
8fa44af
Update atmosphere.py
Jacc0027 May 19, 2023
cda92be
Update atmosphere.py
Jacc0027 May 19, 2023
c872a3d
Update v0.9.6.rst
Jacc0027 May 23, 2023
3bb26c4
Update airmass_atmospheric.rst
Jacc0027 May 23, 2023
b6a6a08
Update pvlib/atmosphere.py
Jacc0027 May 24, 2023
5b10b84
Merge branch 'main' into pr/1296
kandersolar Jun 12, 2023
440ef38
Merge remote-tracking branch 'upstream/main' into pr/1296
kandersolar Jun 12, 2023
f229255
rename function and move to pvlib.spectrum
kandersolar Jun 12, 2023
4ac3a89
misc cleanup
kandersolar Jun 12, 2023
dcf7891
Merge branch 'main' into Spectral-corrections
kandersolar Jun 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -10,5 +10,6 @@ Spectrum
spectrum.get_example_spectral_response
spectrum.get_am15g
spectrum.calc_spectral_mismatch_field
spectrum.spectral_factor_caballero
spectrum.spectral_factor_firstsolar
spectrum.spectral_factor_sapm
3 changes: 3 additions & 0 deletions docs/sphinx/source/whatsnew/v0.9.6.rst
Expand Up @@ -33,6 +33,8 @@ Deprecations

Enhancements
~~~~~~~~~~~~
* Added a function :py:func:`pvlib.spectrum.spectral_factor_caballero`
to estimate spectral mismatch modifiers from atmospheric conditions. (:pull:`1296`)
* Added a new irradiance decomposition model :py:func:`pvlib.irradiance.louche`. (:pull:`1705`)
* Add optional encoding parameter to :py:func:`pvlib.iotools.read_tmy3`.
(:issue:`1732`, :pull:`1737`)
Expand Down Expand Up @@ -90,6 +92,7 @@ Contributors
* Siddharth Kaul (:ghuser:`k10blogger`)
* Kshitiz Gupta (:ghuser:`kshitiz305`)
* Stefan de Lange (:ghuser:`langestefan`)
* Jose Antonio Caballero (:ghuser:`Jacc0027`)
* Andy Lam (:ghuser:`@andylam598`)
* :ghuser:`ooprathamm`
* Kevin Anderson (:ghuser:`kandersolar`)
Expand Down
1 change: 1 addition & 0 deletions pvlib/spectrum/__init__.py
Expand Up @@ -3,6 +3,7 @@
calc_spectral_mismatch_field,
get_am15g,
get_example_spectral_response,
spectral_factor_caballero,
spectral_factor_firstsolar,
spectral_factor_sapm,
)
122 changes: 121 additions & 1 deletion pvlib/spectrum/mismatch.py
Expand Up @@ -320,7 +320,7 @@ def spectral_factor_firstsolar(pw, airmass_absolute, module_type=None,
Returns
-------
modifier: array-like
spectral mismatch factor (unitless) which is can be multiplied
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.
Expand Down Expand Up @@ -446,3 +446,123 @@ def spectral_factor_sapm(airmass_absolute, module):
spectral_loss = pd.Series(spectral_loss, airmass_absolute.index)

return spectral_loss


def spectral_factor_caballero(precipitable_water, airmass_absolute, aod500,
module_type=None, coefficients=None):
r"""
Estimate a technology-specific spectral mismatch modifier from
airmass, aerosol optical depth, and atmospheric precipitable water,
using the Caballero model.

The model structure was motivated by examining the effect of these three
atmospheric parameters on simulated irradiance spectra and spectral
modifiers. However, the coefficient values reported in [1]_ and
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.

Parameters
----------
precipitable_water : numeric
atmospheric precipitable water. [cm]

airmass_absolute : numeric
absolute (pressure-adjusted) airmass. [unitless]

aod500 : numeric
atmospheric aerosol optical depth at 500 nm. [unitless]

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

* ``'cdte'`` - anonymous CdTe module.
* ``'monosi'``, - anonymous sc-si module.
* ``'multisi'``, - anonymous mc-si- module.
* ``'cigs'`` - anonymous copper indium gallium selenide module.
* ``'asi'`` - anonymous amorphous silicon module.
* ``'perovskite'`` - anonymous pervoskite module.

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

Returns
-------
modifier: 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.

References
----------
.. [1] Caballero, J.A., Fernández, E., Theristis, M.,
Almonacid, F., and Nofuentes, G. "Spectral Corrections Based on
Air Mass, Aerosol Optical Depth and Precipitable Water
for PV Performance Modeling."
IEEE Journal of Photovoltaics 2018, 8(2), 552-558.
:doi:`10.1109/jphotov.2017.2787019`
"""

if module_type is None and coefficients is None:
raise ValueError('Must provide either `module_type` or `coefficients`')
if module_type is not None and coefficients is not None:
raise ValueError('Only one of `module_type` and `coefficients` should '
'be provided')

# Experimental coefficients from [1]_.
# The extra 0/1 coefficients at the end are used to enable/disable
# terms to match the different equation forms in Table 1.
_coefficients = {}
_coefficients['cdte'] = (
1.0044, 0.0095, -0.0037, 0.0002, 0.0000, -0.0046,
-0.0182, 0, 0.0095, 0.0068, 0, 1)
_coefficients['monosi'] = (
0.9706, 0.0377, -0.0123, 0.0025, -0.0002, 0.0159,
-0.0165, 0, -0.0016, -0.0027, 1, 0)
_coefficients['multisi'] = (
0.9836, 0.0254, -0.0085, 0.0016, -0.0001, 0.0094,
-0.0132, 0, -0.0002, -0.0011, 1, 0)
_coefficients['cigs'] = (
0.9801, 0.0283, -0.0092, 0.0019, -0.0001, 0.0117,
-0.0126, 0, -0.0011, -0.0019, 1, 0)
_coefficients['asi'] = (
1.1060, -0.0848, 0.0302, -0.0076, 0.0006, -0.1283,
0.0986, -0.0254, 0.0156, 0.0146, 1, 0)
_coefficients['perovskite'] = (
1.0637, -0.0491, 0.0180, -0.0047, 0.0004, -0.0773,
0.0583, -0.0159, 0.01251, 0.0109, 1, 0)

if module_type is not None:
coeff = _coefficients[module_type]
else:
coeff = coefficients

# Evaluate spectral correction factor
ama = airmass_absolute
aod500_ref = 0.084
pw_ref = 1.4164

f_AM = (
coeff[0]
+ coeff[1] * ama
+ coeff[2] * ama**2
+ coeff[3] * ama**3
+ coeff[4] * ama**4
)
# Eq 6, with Table 1
f_AOD = (aod500 - aod500_ref) * (
coeff[5]
+ coeff[10] * coeff[6] * ama
+ coeff[11] * coeff[6] * np.log(ama)
+ coeff[7] * ama**2
)
# Eq 7, with Table 1
f_PW = (precipitable_water - pw_ref) * (
coeff[8]
+ coeff[9] * np.log(ama)
)
Comment on lines +552 to +570
Copy link
Member

Choose a reason for hiding this comment

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

There are a several options for making this more efficient: Horner's method, np.polyval, pre-calculating ama powers, expanding **3 and **4, adding coefficients before multiplying where applicable. Just thought I'd mention that since it came up in other places in the past. They would all reduce the much-appreciated code clarity to varying degrees, so I'll stop short of making a recommendation.

Copy link
Member

Choose a reason for hiding this comment

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

I felt the same conflict between code clarity and efficiency. I settled in favor of clarity after seeing that this inefficient implementation is still plenty fast by my standards (computes ~10 million values in one second).

Still, it does offend the sensibilities to calculate x**3 on one line and x**4 on the next :(

modifier = f_AM + f_AOD + f_PW # Eq 5
return modifier
44 changes: 44 additions & 0 deletions pvlib/tests/test_spectrum.py
Expand Up @@ -269,3 +269,47 @@ def test_spectral_factor_sapm(sapm_module_params, airmass, expected):
assert_series_equal(out, expected, check_less_precise=4)
else:
assert_allclose(out, expected, atol=1e-4)


@pytest.mark.parametrize("module_type,expected", [
('asi', np.array([0.9108, 0.9897, 0.9707, 1.0265, 1.0798, 0.9537])),
('perovskite', np.array([0.9422, 0.9932, 0.9868, 1.0183, 1.0604, 0.9737])),
('cdte', np.array([0.9824, 1.0000, 1.0065, 1.0117, 1.042, 0.9979])),
('multisi', np.array([0.9907, 0.9979, 1.0203, 1.0081, 1.0058, 1.019])),
('monosi', np.array([0.9935, 0.9987, 1.0264, 1.0074, 0.9999, 1.0263])),
('cigs', np.array([1.0014, 1.0011, 1.0270, 1.0082, 1.0029, 1.026])),
])
def test_spectral_factor_caballero(module_type, expected):
ams = np.array([3.0, 1.5, 3.0, 1.5, 1.5, 3.0])
aods = np.array([1.0, 1.0, 0.02, 0.02, 0.08, 0.08])
pws = np.array([1.42, 1.42, 1.42, 1.42, 4.0, 1.0])
out = spectrum.spectral_factor_caballero(pws, ams, aods,
module_type=module_type)
assert np.allclose(expected, out, atol=1e-3)


def test_spectral_factor_caballero_supplied():
# use the cdte coeffs
coeffs = (
1.0044, 0.0095, -0.0037, 0.0002, 0.0000, -0.0046,
-0.0182, 0, 0.0095, 0.0068, 0, 1)
out = spectrum.spectral_factor_caballero(1, 1, 1, coefficients=coeffs)
expected = 1.0021964
assert_allclose(out, expected, atol=1e-3)


def test_spectral_factor_caballero_supplied_redundant():
# Error when specifying both module_type and coefficients
coeffs = (
1.0044, 0.0095, -0.0037, 0.0002, 0.0000, -0.0046,
-0.0182, 0, 0.0095, 0.0068, 0, 1)
with pytest.raises(ValueError):
spectrum.spectral_factor_caballero(1, 1, 1, module_type='cdte',
coefficients=coeffs)


def test_spectral_factor_caballero_supplied_ambiguous():
# Error when specifying neither module_type nor coefficients
with pytest.raises(ValueError):
spectrum.spectral_factor_caballero(1, 1, 1, module_type=None,
coefficients=None)