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 78 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/sphinx/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ Airmass and atmospheric models
atmosphere.kasten96_lt
atmosphere.angstrom_aod_at_lambda
atmosphere.angstrom_alpha
atmosphere.caballero_spectral_correction


Irradiance
Expand Down
148 changes: 148 additions & 0 deletions pvlib/atmosphere.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,151 @@ def angstrom_alpha(aod1, lambda1, aod2, lambda2):
pvlib.atmosphere.angstrom_aod_at_lambda
"""
return - np.log(aod1 / aod2) / np.log(lambda1 / lambda2)


def caballero_spectral_correction(airmass_absolute, aod500, pw,
module_type=None, coefficients=None,
aod500_ref=0.084, pw_ref=1.42):
r"""
Spectral mismatch modifier based on absolute (pressure-adjusted)
airmass (AM), aerosol optical depth (AOD) at 500 nm and
precipitable water (PW).

Estimates a spectral mismatch modifier :math:`M` representing
the effect on module short circuit current of variation in the
spectral irradiance, :math:`MM` is estimated from absolute
(pressure-adjusted) AM, :math:`ama`, AOD at 500 nm, :math:`aod500`
and PW, :math:`pw` [1].

The best fit polynomial for each atmospheric parameter (AM, AOD, PW)
and PV technology under study has been obtained from synthetic spectra
generated with SMARTS [2], considering the following boundary
conditions:

* :math:`1.0 <= ama <= 5.0`
* :math:`0.05 <= aod500 <= 0.6`
* :math:`0.25 \textrm{cm} <= pw <= 4 \textrm{cm}`
* Spectral range is limited to that of CMP11 (280 nm to 2800 nm)
* All other parameters fixed at G173 standard

Elevation (deg), AOD and PW data were recorded in the city of Jaén,
Spain for one year synchronously with both, broadband and
spectroradiometric measurements of 30º tilted global irradiance
south-facing logged in 5-min intervals. AM was estimated through
elevation data.
Copy link
Member

Choose a reason for hiding this comment

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

Does this estimation refer to solar elevation? Airmass calculations are pretty standard.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @adriesse, yes it does. The solar elevation was measured by means of a solar spectral irradiance meter (SolarSIM-D2) from Spectrafy Inc. Then the AM was computed by using the sun’s zenith angle (z). Reference: F. Kasten and A. T. Young, “Revised optical air mass tables and approxi-mation formula,” Appl. Opt., vol. 28, pp. 4735–4738, Nov. 15, 1989.

Copy link
Member

Choose a reason for hiding this comment

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

My thinking was that your responses might make it into the doc string at some point...


Finally, the spectral mismatch factor was calculated for each
of the PV technologies and a multivariable regression adjustment
as a function of AM, AOD and PW was performed according to [3] and [1].
As such, the polynomial adjustment coefficients included in [1]
were obtained.

Copy link
Member

Choose a reason for hiding this comment

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

I think it is very good to have a couple of paragraphs explaining the basics here, but unfortunately I find them hard to understand. If you recorded AM, AOD, PW and spectra, then where does smarts come in? I guess I should read the paper, but this text should also be consistent and comprehensible on its own.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @adriesse, the reason for using synthetic spectra, generated through SMARTS2, was to ascertain the ideal polynomial type of the experimental equations related to the AM, AOD and PW parameters.


Parameters
----------
airmass_absolute : array-like
absolute (pressure-adjusted) airmass. [unitless]

aod500 : array-like
atmospheric aerosol optical depth at 500 nm. [unitless]

pw : array-like
atmospheric precipitable water. [cm]

module_type : None or string, default None
a string specifying a cell type. Values of 'cdte', 'monosi', 'cigs',
'multisi','asi' and 'pervovskite'. If provided,
module_type selects default coefficients for the following modules:

* '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 : None or array-like, optional
the coefficients employed have been obtained with experimental
data in the city of Jaén, Spain. It is pending to verify if such
coefficients vary in places with extreme climates where AOD and
pw values are frequently high.

aod500_ref : numeric, default 0.084
TODO: description

pw_ref : numeric, default 1.42
TODO: description

Returns
-------
modifier: array-like
spectral mismatch factor (unitless) which is can be 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.
https://doi.org/10.1109/jphotov.2017.2787019
.. [2] Gueymard, Christian. SMARTS2: a simple model of the
atmospheric radiative transfer of sunshine: algorithms
and performance assessment. Cocoa, FL:
Florida Solar Energy Center, 1995.
.. [3] Theristis, M., Fernández, E., Almonacid, F., and
Pérez-Higueras, Pedro. "Spectral Corrections Based
on Air Mass, Aerosol Optical Depth and Precipitable
Water for CPV Performance Modeling.
"IEEE Journal of Photovoltaics 2016, 6(6), 1598-1604.
https://doi.org/10.1109/jphotov.2016.2606702

"""

# Experimental coefficients

_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 None and coefficients is None:
raise TypeError('Invalid input provided, both module_type'
Copy link
Member

Choose a reason for hiding this comment

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

I think both of these should be ValueError

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I understand then, if the coefficients are not exactly the ones defined in the script, the ValueError message will appear.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

+ 'and coefficients are None')
elif module_type is not None and coefficients is None:
raise TypeError('Cannot resolve input, providing the'
+ ' coefficients input is mandatory')
else:
pass

# Evaluate Spectral Shift
coeff = coefficients
ama = airmass_absolute
modifier = (
coeff[0] + (ama) * coeff[1] + (ama * ama) * coeff[2]
+ (ama * ama * ama) * coeff[3] + (ama * ama * ama * ama) * coeff[4]
+ (aod500 - aod500_ref) * coeff[5]
+ ((aod500 - aod500_ref) * (ama) * coeff[6]) * coeff[10]
+ ((aod500 - aod500_ref) * (np.log(ama)) * coeff[6]) * coeff[11]
+ (aod500 - aod500_ref) * (ama * ama) * coeff[7]
+ (pw - pw_ref) * coeff[8] + (pw - pw_ref) * (np.log(ama)) * coeff[9])

return modifier
45 changes: 45 additions & 0 deletions pvlib/tests/test_atmosphere.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,48 @@ def test_bird_hulstrom80_aod_bb():
aod380, aod500 = 0.22072480948195175, 0.1614279181106312
bird_hulstrom = atmosphere.bird_hulstrom80_aod_bb(aod380, aod500)
assert np.isclose(0.11738229553812768, bird_hulstrom)


@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_caballero_spectral_correction(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 = atmosphere.caballero_spectral_correction(ams, aods, pws,
module_type=module_type,
aod500_ref=0.084,
pw_ref=1.42)
assert np.allclose(expected, out, atol=1e-3)


def test_caballero_spectral_correction_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 = atmosphere.caballero_spectral_correction(1,
1, 1, coefficients=coeffs)
expected = 1.0021964
assert_allclose(out, expected, atol=1e-3)


def test_caballero_spectral_correction_supplied_ambiguous():
dummy_coeffs = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
with pytest.raises(TypeError):
atmosphere.caballero_spectral_correction(1, 1, 1,
module_type='cdte',
coefficients=dummy_coeffs)


def test_caballero_spectral_correction_supplied_ambiguous_1():
with pytest.raises(TypeError):
atmosphere.caballero_spectral_correction(1, 1, 1,
module_type=None,
coefficients=None)