Skip to content

Commit 948d820

Browse files
Jacc0027kandersolarcwhanseadriesse
authored
Implement Caballero et al. spectral factor model (#1296)
* Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Merge branch 'Spectral-corrections' of https://github.com/Jacc0027/pvlib-python into Spectral-corrections * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update pvlib/atmosphere.py Co-authored-by: Kevin Anderson <[email protected]> * relocation of parameter descriptions according to the order of input parameters * Update api.rst Adding atmosphere.AM_AOD_PW_spectral correction as requested in #1296 * remove input screening * move reference values to be optional parameters * fix implementation issues * first cut at tests using file from Jacc0027 * CI correction. Line #215 * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Testing tests * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update atmosphere.py * Test Review * Update atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py * Update test_atmosphere.py * Update atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update pvlib/atmosphere.py Co-authored-by: Cliff Hansen <[email protected]> * Update atmosphere.py Added the requested changes * Update atmosphere.py Fixed 2 blank line errors. * Update test_atmosphere.py Updated test function name to: caballero_spectral_correction * Update api.rst Updated the atmosphere function name from AM_AOD_PW_spectral_correction to caballero_spectral_correction * Update test_atmosphere.py Updated some functions calls from AM_AOD_PW_spectral_correction to caballero_spectral_correction * Update atmosphere.py Updated the logic to show errors. * Update pvlib/atmosphere.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/atmosphere.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/atmosphere.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/atmosphere.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/atmosphere.py Co-authored-by: Cliff Hansen <[email protected]> * Update pvlib/atmosphere.py Co-authored-by: Cliff Hansen <[email protected]> * Update atmosphere.py * Update atmosphere.py * Update atmosphere.py Trying to solve #814 and #817 issues. * Update atmosphere.py * Update api.rst * Delete api.rst * Update test_atmosphere.py * Update atmosphere.py * Update test_atmosphere.py * Update atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * Update test_atmosphere.py * update function logic and get tests working * Update atmosphere.py Updated both "TODO" issues. * Update atmosphere.py * Update v0.9.6.rst Added caballero spectral correction as a new function. * Update airmass_atmospheric.rst Added a new function: atmosphere.caballero_spectral_correction * Update pvlib/atmosphere.py Co-authored-by: Anton Driesse <[email protected]> * rename function and move to pvlib.spectrum following the conventions set out in #1628 * misc cleanup - reorder and rename parameters to match #1768 - remove reference parameters - shorten/simplify docstring - restructure calculation to be more readable --------- Co-authored-by: Kevin Anderson <[email protected]> Co-authored-by: Kevin Anderson <[email protected]> Co-authored-by: Cliff Hansen <[email protected]> Co-authored-by: Anton Driesse <[email protected]> Co-authored-by: Kevin Anderson <[email protected]>
1 parent bc0f0ff commit 948d820

File tree

5 files changed

+169
-0
lines changed

5 files changed

+169
-0
lines changed

docs/sphinx/source/reference/effects_on_pv_system_output/spectrum.rst

+1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ Spectrum
1010
spectrum.get_example_spectral_response
1111
spectrum.get_am15g
1212
spectrum.calc_spectral_mismatch_field
13+
spectrum.spectral_factor_caballero
1314
spectrum.spectral_factor_firstsolar
1415
spectrum.spectral_factor_sapm

docs/sphinx/source/whatsnew/v0.9.6.rst

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ Deprecations
4949

5050
Enhancements
5151
~~~~~~~~~~~~
52+
* Added a function :py:func:`pvlib.spectrum.spectral_factor_caballero`
53+
to estimate spectral mismatch modifiers from atmospheric conditions. (:pull:`1296`)
5254
* Added a new irradiance decomposition model :py:func:`pvlib.irradiance.louche`. (:pull:`1705`)
5355
* Add optional encoding parameter to :py:func:`pvlib.iotools.read_tmy3`.
5456
(:issue:`1732`, :pull:`1737`)
@@ -112,6 +114,7 @@ Contributors
112114
* Siddharth Kaul (:ghuser:`k10blogger`)
113115
* Kshitiz Gupta (:ghuser:`kshitiz305`)
114116
* Stefan de Lange (:ghuser:`langestefan`)
117+
* Jose Antonio Caballero (:ghuser:`Jacc0027`)
115118
* Andy Lam (:ghuser:`@andylam598`)
116119
* :ghuser:`ooprathamm`
117120
* Kevin Anderson (:ghuser:`kandersolar`)

pvlib/spectrum/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
calc_spectral_mismatch_field,
44
get_am15g,
55
get_example_spectral_response,
6+
spectral_factor_caballero,
67
spectral_factor_firstsolar,
78
spectral_factor_sapm,
89
)

pvlib/spectrum/mismatch.py

+120
Original file line numberDiff line numberDiff line change
@@ -450,3 +450,123 @@ def spectral_factor_sapm(airmass_absolute, module):
450450
spectral_loss = pd.Series(spectral_loss, airmass_absolute.index)
451451

452452
return spectral_loss
453+
454+
455+
def spectral_factor_caballero(precipitable_water, airmass_absolute, aod500,
456+
module_type=None, coefficients=None):
457+
r"""
458+
Estimate a technology-specific spectral mismatch modifier from
459+
airmass, aerosol optical depth, and atmospheric precipitable water,
460+
using the Caballero model.
461+
462+
The model structure was motivated by examining the effect of these three
463+
atmospheric parameters on simulated irradiance spectra and spectral
464+
modifiers. However, the coefficient values reported in [1]_ and
465+
available here via the ``module_type`` parameter were determined
466+
by fitting the model equations to spectral factors calculated from
467+
global tilted spectral irradiance measurements taken in the city of
468+
Jaén, Spain. See [1]_ for details.
469+
470+
Parameters
471+
----------
472+
precipitable_water : numeric
473+
atmospheric precipitable water. [cm]
474+
475+
airmass_absolute : numeric
476+
absolute (pressure-adjusted) airmass. [unitless]
477+
478+
aod500 : numeric
479+
atmospheric aerosol optical depth at 500 nm. [unitless]
480+
481+
module_type : str, optional
482+
One of the following PV technology strings from [1]_:
483+
484+
* ``'cdte'`` - anonymous CdTe module.
485+
* ``'monosi'``, - anonymous sc-si module.
486+
* ``'multisi'``, - anonymous mc-si- module.
487+
* ``'cigs'`` - anonymous copper indium gallium selenide module.
488+
* ``'asi'`` - anonymous amorphous silicon module.
489+
* ``'perovskite'`` - anonymous pervoskite module.
490+
491+
coefficients : array-like, optional
492+
user-defined coefficients, if not using one of the default coefficient
493+
sets via the ``module_type`` parameter.
494+
495+
Returns
496+
-------
497+
modifier: numeric
498+
spectral mismatch factor (unitless) which is multiplied
499+
with broadband irradiance reaching a module's cells to estimate
500+
effective irradiance, i.e., the irradiance that is converted to
501+
electrical current.
502+
503+
References
504+
----------
505+
.. [1] Caballero, J.A., Fernández, E., Theristis, M.,
506+
Almonacid, F., and Nofuentes, G. "Spectral Corrections Based on
507+
Air Mass, Aerosol Optical Depth and Precipitable Water
508+
for PV Performance Modeling."
509+
IEEE Journal of Photovoltaics 2018, 8(2), 552-558.
510+
:doi:`10.1109/jphotov.2017.2787019`
511+
"""
512+
513+
if module_type is None and coefficients is None:
514+
raise ValueError('Must provide either `module_type` or `coefficients`')
515+
if module_type is not None and coefficients is not None:
516+
raise ValueError('Only one of `module_type` and `coefficients` should '
517+
'be provided')
518+
519+
# Experimental coefficients from [1]_.
520+
# The extra 0/1 coefficients at the end are used to enable/disable
521+
# terms to match the different equation forms in Table 1.
522+
_coefficients = {}
523+
_coefficients['cdte'] = (
524+
1.0044, 0.0095, -0.0037, 0.0002, 0.0000, -0.0046,
525+
-0.0182, 0, 0.0095, 0.0068, 0, 1)
526+
_coefficients['monosi'] = (
527+
0.9706, 0.0377, -0.0123, 0.0025, -0.0002, 0.0159,
528+
-0.0165, 0, -0.0016, -0.0027, 1, 0)
529+
_coefficients['multisi'] = (
530+
0.9836, 0.0254, -0.0085, 0.0016, -0.0001, 0.0094,
531+
-0.0132, 0, -0.0002, -0.0011, 1, 0)
532+
_coefficients['cigs'] = (
533+
0.9801, 0.0283, -0.0092, 0.0019, -0.0001, 0.0117,
534+
-0.0126, 0, -0.0011, -0.0019, 1, 0)
535+
_coefficients['asi'] = (
536+
1.1060, -0.0848, 0.0302, -0.0076, 0.0006, -0.1283,
537+
0.0986, -0.0254, 0.0156, 0.0146, 1, 0)
538+
_coefficients['perovskite'] = (
539+
1.0637, -0.0491, 0.0180, -0.0047, 0.0004, -0.0773,
540+
0.0583, -0.0159, 0.01251, 0.0109, 1, 0)
541+
542+
if module_type is not None:
543+
coeff = _coefficients[module_type]
544+
else:
545+
coeff = coefficients
546+
547+
# Evaluate spectral correction factor
548+
ama = airmass_absolute
549+
aod500_ref = 0.084
550+
pw_ref = 1.4164
551+
552+
f_AM = (
553+
coeff[0]
554+
+ coeff[1] * ama
555+
+ coeff[2] * ama**2
556+
+ coeff[3] * ama**3
557+
+ coeff[4] * ama**4
558+
)
559+
# Eq 6, with Table 1
560+
f_AOD = (aod500 - aod500_ref) * (
561+
coeff[5]
562+
+ coeff[10] * coeff[6] * ama
563+
+ coeff[11] * coeff[6] * np.log(ama)
564+
+ coeff[7] * ama**2
565+
)
566+
# Eq 7, with Table 1
567+
f_PW = (precipitable_water - pw_ref) * (
568+
coeff[8]
569+
+ coeff[9] * np.log(ama)
570+
)
571+
modifier = f_AM + f_AOD + f_PW # Eq 5
572+
return modifier

pvlib/tests/test_spectrum.py

+44
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,47 @@ def test_spectral_factor_sapm(sapm_module_params, airmass, expected):
271271
assert_series_equal(out, expected, check_less_precise=4)
272272
else:
273273
assert_allclose(out, expected, atol=1e-4)
274+
275+
276+
@pytest.mark.parametrize("module_type,expected", [
277+
('asi', np.array([0.9108, 0.9897, 0.9707, 1.0265, 1.0798, 0.9537])),
278+
('perovskite', np.array([0.9422, 0.9932, 0.9868, 1.0183, 1.0604, 0.9737])),
279+
('cdte', np.array([0.9824, 1.0000, 1.0065, 1.0117, 1.042, 0.9979])),
280+
('multisi', np.array([0.9907, 0.9979, 1.0203, 1.0081, 1.0058, 1.019])),
281+
('monosi', np.array([0.9935, 0.9987, 1.0264, 1.0074, 0.9999, 1.0263])),
282+
('cigs', np.array([1.0014, 1.0011, 1.0270, 1.0082, 1.0029, 1.026])),
283+
])
284+
def test_spectral_factor_caballero(module_type, expected):
285+
ams = np.array([3.0, 1.5, 3.0, 1.5, 1.5, 3.0])
286+
aods = np.array([1.0, 1.0, 0.02, 0.02, 0.08, 0.08])
287+
pws = np.array([1.42, 1.42, 1.42, 1.42, 4.0, 1.0])
288+
out = spectrum.spectral_factor_caballero(pws, ams, aods,
289+
module_type=module_type)
290+
assert np.allclose(expected, out, atol=1e-3)
291+
292+
293+
def test_spectral_factor_caballero_supplied():
294+
# use the cdte coeffs
295+
coeffs = (
296+
1.0044, 0.0095, -0.0037, 0.0002, 0.0000, -0.0046,
297+
-0.0182, 0, 0.0095, 0.0068, 0, 1)
298+
out = spectrum.spectral_factor_caballero(1, 1, 1, coefficients=coeffs)
299+
expected = 1.0021964
300+
assert_allclose(out, expected, atol=1e-3)
301+
302+
303+
def test_spectral_factor_caballero_supplied_redundant():
304+
# Error when specifying both module_type and coefficients
305+
coeffs = (
306+
1.0044, 0.0095, -0.0037, 0.0002, 0.0000, -0.0046,
307+
-0.0182, 0, 0.0095, 0.0068, 0, 1)
308+
with pytest.raises(ValueError):
309+
spectrum.spectral_factor_caballero(1, 1, 1, module_type='cdte',
310+
coefficients=coeffs)
311+
312+
313+
def test_spectral_factor_caballero_supplied_ambiguous():
314+
# Error when specifying neither module_type nor coefficients
315+
with pytest.raises(ValueError):
316+
spectrum.spectral_factor_caballero(1, 1, 1, module_type=None,
317+
coefficients=None)

0 commit comments

Comments
 (0)