pvlib 0.10.4__py3-none-any.whl → 0.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pvlib/__init__.py +1 -0
- pvlib/albedo.py +168 -0
- pvlib/bifacial/utils.py +2 -1
- pvlib/data/ASTMG173.csv +2004 -0
- pvlib/iam.py +28 -28
- pvlib/iotools/__init__.py +0 -1
- pvlib/iotools/midc.py +15 -10
- pvlib/iotools/psm3.py +10 -25
- pvlib/iotools/srml.py +1 -61
- pvlib/iotools/surfrad.py +1 -1
- pvlib/irradiance.py +133 -95
- pvlib/location.py +16 -6
- pvlib/modelchain.py +2 -165
- pvlib/pvarray.py +7 -5
- pvlib/pvsystem.py +75 -106
- pvlib/scaling.py +4 -2
- pvlib/shading.py +350 -0
- pvlib/singlediode.py +37 -9
- pvlib/snow.py +3 -1
- pvlib/spectrum/__init__.py +5 -0
- pvlib/spectrum/mismatch.py +573 -43
- pvlib/spectrum/spectrl2.py +8 -8
- pvlib/tests/bifacial/test_utils.py +6 -5
- pvlib/tests/iotools/test_psm3.py +0 -18
- pvlib/tests/iotools/test_srml.py +1 -43
- pvlib/tests/test_albedo.py +84 -0
- pvlib/tests/test_inverter.py +2 -2
- pvlib/tests/test_irradiance.py +35 -2
- pvlib/tests/test_location.py +26 -18
- pvlib/tests/test_modelchain.py +0 -57
- pvlib/tests/test_pvsystem.py +73 -128
- pvlib/tests/test_shading.py +167 -1
- pvlib/tests/test_singlediode.py +68 -29
- pvlib/tests/test_spectrum.py +283 -22
- pvlib/tests/test_temperature.py +7 -7
- pvlib/tests/test_tools.py +24 -0
- pvlib/tests/test_transformer.py +60 -0
- pvlib/tools.py +27 -0
- pvlib/transformer.py +117 -0
- pvlib/version.py +1 -5
- {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/METADATA +3 -4
- {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/RECORD +46 -42
- {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/WHEEL +1 -1
- pvlib/data/astm_g173_am15g.csv +0 -2003
- {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/AUTHORS.md +0 -0
- {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/LICENSE +0 -0
- {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/top_level.txt +0 -0
pvlib/spectrum/mismatch.py
CHANGED
|
@@ -3,12 +3,25 @@ The ``mismatch`` module provides functions for spectral mismatch calculations.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import pvlib
|
|
6
|
+
from pvlib._deprecation import deprecated
|
|
7
|
+
from pvlib.tools import normalize_max2one
|
|
6
8
|
import numpy as np
|
|
7
9
|
import pandas as pd
|
|
10
|
+
import scipy.constants
|
|
11
|
+
from scipy.integrate import trapezoid
|
|
8
12
|
from scipy.interpolate import interp1d
|
|
9
|
-
import os
|
|
10
13
|
|
|
14
|
+
from pathlib import Path
|
|
11
15
|
from warnings import warn
|
|
16
|
+
from functools import partial
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION = (
|
|
20
|
+
scipy.constants.speed_of_light
|
|
21
|
+
* scipy.constants.Planck
|
|
22
|
+
/ scipy.constants.elementary_charge
|
|
23
|
+
* 1e9
|
|
24
|
+
)
|
|
12
25
|
|
|
13
26
|
|
|
14
27
|
def get_example_spectral_response(wavelength=None):
|
|
@@ -43,14 +56,14 @@ def get_example_spectral_response(wavelength=None):
|
|
|
43
56
|
'''
|
|
44
57
|
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. Aug. 2022
|
|
45
58
|
|
|
46
|
-
SR_DATA = np.array([[
|
|
47
|
-
[
|
|
48
|
-
[
|
|
49
|
-
[
|
|
50
|
-
[
|
|
51
|
-
[
|
|
52
|
-
[
|
|
53
|
-
[
|
|
59
|
+
SR_DATA = np.array([[290, 0.00],
|
|
60
|
+
[350, 0.27],
|
|
61
|
+
[400, 0.37],
|
|
62
|
+
[500, 0.52],
|
|
63
|
+
[650, 0.71],
|
|
64
|
+
[800, 0.88],
|
|
65
|
+
[900, 0.97],
|
|
66
|
+
[950, 1.00],
|
|
54
67
|
[1000, 0.93],
|
|
55
68
|
[1050, 0.58],
|
|
56
69
|
[1100, 0.21],
|
|
@@ -76,8 +89,18 @@ def get_example_spectral_response(wavelength=None):
|
|
|
76
89
|
return sr
|
|
77
90
|
|
|
78
91
|
|
|
92
|
+
@deprecated(
|
|
93
|
+
since="0.11",
|
|
94
|
+
removal="0.12",
|
|
95
|
+
name="pvlib.spectrum.get_am15g",
|
|
96
|
+
alternative="pvlib.spectrum.get_reference_spectra",
|
|
97
|
+
addendum=(
|
|
98
|
+
"The new function reads more data. Use it with "
|
|
99
|
+
+ "standard='ASTM G173-03' and extract the 'global' column."
|
|
100
|
+
),
|
|
101
|
+
)
|
|
79
102
|
def get_am15g(wavelength=None):
|
|
80
|
-
|
|
103
|
+
r"""
|
|
81
104
|
Read the ASTM G173-03 AM1.5 global spectrum on a 37-degree tilted surface,
|
|
82
105
|
optionally interpolated to the specified wavelength(s).
|
|
83
106
|
|
|
@@ -91,19 +114,19 @@ def get_am15g(wavelength=None):
|
|
|
91
114
|
----------
|
|
92
115
|
wavelength: 1-D sequence of numeric, optional
|
|
93
116
|
Wavelengths at which the spectrum is interpolated.
|
|
94
|
-
By default the 2002 wavelengths of the standard are returned. [nm]
|
|
117
|
+
By default the 2002 wavelengths of the standard are returned. [nm].
|
|
95
118
|
|
|
96
119
|
Returns
|
|
97
120
|
-------
|
|
98
121
|
am15g: pandas.Series
|
|
99
|
-
The AM1.5g standard spectrum indexed by ``wavelength``. [
|
|
122
|
+
The AM1.5g standard spectrum indexed by ``wavelength``. [W/(m²nm)].
|
|
100
123
|
|
|
101
124
|
Notes
|
|
102
125
|
-----
|
|
103
126
|
If ``wavelength`` is specified this function uses linear interpolation.
|
|
104
127
|
|
|
105
128
|
If the values in ``wavelength`` are too widely spaced, the integral of the
|
|
106
|
-
spectrum may deviate from the standard value of 1000.37 W/m
|
|
129
|
+
spectrum may deviate from the standard value of 1000.37 W/m².
|
|
107
130
|
|
|
108
131
|
The values in the data file provided with pvlib-python are copied from an
|
|
109
132
|
Excel file distributed by NREL, which is found here:
|
|
@@ -112,32 +135,125 @@ def get_am15g(wavelength=None):
|
|
|
112
135
|
More information about reference spectra is found here:
|
|
113
136
|
https://www.nrel.gov/grid/solar-resource/spectra-am1.5.html
|
|
114
137
|
|
|
138
|
+
See Also
|
|
139
|
+
--------
|
|
140
|
+
pvlib.spectrum.get_reference_spectra : reads also the direct and
|
|
141
|
+
extraterrestrial components of the spectrum.
|
|
142
|
+
|
|
115
143
|
References
|
|
116
144
|
----------
|
|
117
145
|
.. [1] ASTM "G173-03 Standard Tables for Reference Solar Spectral
|
|
118
146
|
Irradiances: Direct Normal and Hemispherical on 37° Tilted Surface."
|
|
119
|
-
|
|
147
|
+
""" # noqa: E501
|
|
120
148
|
# Contributed by Anton Driesse (@adriesse), PV Performance Labs. Aug. 2022
|
|
149
|
+
# modified by @echedey-ls, as a wrapper of spectrum.get_reference_spectra
|
|
150
|
+
standard = get_reference_spectra(wavelength, standard="ASTM G173-03")
|
|
151
|
+
return standard["global"]
|
|
121
152
|
|
|
122
|
-
pvlib_path = pvlib.__path__[0]
|
|
123
|
-
filepath = os.path.join(pvlib_path, 'data', 'astm_g173_am15g.csv')
|
|
124
153
|
|
|
125
|
-
|
|
154
|
+
def get_reference_spectra(wavelengths=None, standard="ASTM G173-03"):
|
|
155
|
+
r"""
|
|
156
|
+
Read a standard spectrum specified by ``standard``, optionally
|
|
157
|
+
interpolated to the specified wavelength(s).
|
|
158
|
+
|
|
159
|
+
Defaults to ``ASTM G173-03`` AM1.5 standard [1]_, which returns
|
|
160
|
+
``extraterrestrial``, ``global`` and ``direct`` spectrum on a 37-degree
|
|
161
|
+
tilted surface, optionally interpolated to the specified wavelength(s).
|
|
162
|
+
|
|
163
|
+
Parameters
|
|
164
|
+
----------
|
|
165
|
+
wavelengths : numeric, optional
|
|
166
|
+
Wavelengths at which the spectrum is interpolated. [nm].
|
|
167
|
+
If not provided, the original wavelengths from the specified standard
|
|
168
|
+
are used. Values outside that range are filled with zeros.
|
|
169
|
+
|
|
170
|
+
standard : str, default "ASTM G173-03"
|
|
171
|
+
The reference standard to be read. Only the reference
|
|
172
|
+
``"ASTM G173-03"`` is available at the moment.
|
|
126
173
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
copy=False,
|
|
133
|
-
assume_sorted=True)
|
|
174
|
+
Returns
|
|
175
|
+
-------
|
|
176
|
+
standard_spectra : pandas.DataFrame
|
|
177
|
+
The standard spectrum by ``wavelength [nm]``. [W/(m²nm)].
|
|
178
|
+
Column names are ``extraterrestrial``, ``direct`` and ``global``.
|
|
134
179
|
|
|
135
|
-
|
|
180
|
+
Notes
|
|
181
|
+
-----
|
|
182
|
+
If ``wavelength`` is specified, linear interpolation is used.
|
|
136
183
|
|
|
137
|
-
|
|
138
|
-
|
|
184
|
+
If the values in ``wavelength`` are too widely spaced, the integral of each
|
|
185
|
+
spectrum may deviate from its standard value.
|
|
186
|
+
For global spectra, it is about 1000.37 W/m².
|
|
139
187
|
|
|
140
|
-
|
|
188
|
+
The values of the ASTM G173-03 provided with pvlib-python are copied from
|
|
189
|
+
an Excel file distributed by NREL, which is found here [2]_:
|
|
190
|
+
https://www.nrel.gov/grid/solar-resource/assets/data/astmg173.xls
|
|
191
|
+
|
|
192
|
+
Examples
|
|
193
|
+
--------
|
|
194
|
+
>>> from pvlib import spectrum
|
|
195
|
+
>>> am15 = spectrum.get_reference_spectra()
|
|
196
|
+
>>> am15_extraterrestrial, am15_global, am15_direct = \
|
|
197
|
+
>>> am15['extraterrestrial'], am15['global'], am15['direct']
|
|
198
|
+
>>> print(am15.head())
|
|
199
|
+
extraterrestrial global direct
|
|
200
|
+
wavelength
|
|
201
|
+
280.0 0.082 4.730900e-23 2.536100e-26
|
|
202
|
+
280.5 0.099 1.230700e-21 1.091700e-24
|
|
203
|
+
281.0 0.150 5.689500e-21 6.125300e-24
|
|
204
|
+
281.5 0.212 1.566200e-19 2.747900e-22
|
|
205
|
+
282.0 0.267 1.194600e-18 2.834600e-21
|
|
206
|
+
|
|
207
|
+
>>> am15 = spectrum.get_reference_spectra([300, 500, 800, 1100])
|
|
208
|
+
>>> print(am15)
|
|
209
|
+
extraterrestrial global direct
|
|
210
|
+
wavelength
|
|
211
|
+
300 0.45794 0.00102 0.000456
|
|
212
|
+
500 1.91600 1.54510 1.339100
|
|
213
|
+
800 1.12480 1.07250 0.988590
|
|
214
|
+
1100 0.60000 0.48577 0.461130
|
|
215
|
+
|
|
216
|
+
References
|
|
217
|
+
----------
|
|
218
|
+
.. [1] ASTM "G173-03 Standard Tables for Reference Solar Spectral
|
|
219
|
+
Irradiances: Direct Normal and Hemispherical on 37° Tilted Surface."
|
|
220
|
+
.. [2] “Reference Air Mass 1.5 Spectra,” www.nrel.gov.
|
|
221
|
+
https://www.nrel.gov/grid/solar-resource/spectra-am1.5.html
|
|
222
|
+
""" # Contributed by Echedey Luis, inspired by Anton Driesse (get_am15g)
|
|
223
|
+
SPECTRA_FILES = {
|
|
224
|
+
"ASTM G173-03": "ASTMG173.csv",
|
|
225
|
+
}
|
|
226
|
+
pvlib_datapath = Path(pvlib.__path__[0]) / "data"
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
filepath = pvlib_datapath / SPECTRA_FILES[standard]
|
|
230
|
+
except KeyError:
|
|
231
|
+
raise ValueError(
|
|
232
|
+
f"Invalid standard identifier '{standard}'. Available "
|
|
233
|
+
+ "identifiers are: "
|
|
234
|
+
+ ", ".join(SPECTRA_FILES.keys())
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
standard = pd.read_csv(
|
|
238
|
+
filepath,
|
|
239
|
+
header=1, # expect first line of description, then column names
|
|
240
|
+
index_col=0, # first column is "wavelength"
|
|
241
|
+
dtype=float,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if wavelengths is not None:
|
|
245
|
+
interpolator = partial(
|
|
246
|
+
np.interp, xp=standard.index, left=0.0, right=0.0
|
|
247
|
+
)
|
|
248
|
+
standard = pd.DataFrame(
|
|
249
|
+
index=wavelengths,
|
|
250
|
+
data={
|
|
251
|
+
col: interpolator(x=wavelengths, fp=standard[col])
|
|
252
|
+
for col in standard.columns
|
|
253
|
+
},
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
return standard
|
|
141
257
|
|
|
142
258
|
|
|
143
259
|
def calc_spectral_mismatch_field(sr, e_sun, e_ref=None):
|
|
@@ -153,7 +269,7 @@ def calc_spectral_mismatch_field(sr, e_sun, e_ref=None):
|
|
|
153
269
|
|
|
154
270
|
e_sun: pandas.DataFrame or pandas.Series
|
|
155
271
|
One or more measured solar irradiance spectra in a pandas.DataFrame
|
|
156
|
-
having wavelength in nm as column index.
|
|
272
|
+
having wavelength in nm as column index. A single spectrum may be
|
|
157
273
|
be given as a pandas.Series having wavelength in nm as index.
|
|
158
274
|
[(W/m^2)/nm]
|
|
159
275
|
|
|
@@ -215,7 +331,7 @@ def calc_spectral_mismatch_field(sr, e_sun, e_ref=None):
|
|
|
215
331
|
|
|
216
332
|
# get the reference spectrum at wavelengths matching the measured spectra
|
|
217
333
|
if e_ref is None:
|
|
218
|
-
e_ref =
|
|
334
|
+
e_ref = get_reference_spectra(wavelengths=e_sun.T.index)["global"]
|
|
219
335
|
|
|
220
336
|
# interpolate the sr at the wavelengths of the spectra
|
|
221
337
|
# reference spectrum wavelengths may differ if e_ref is from caller
|
|
@@ -224,7 +340,7 @@ def calc_spectral_mismatch_field(sr, e_sun, e_ref=None):
|
|
|
224
340
|
|
|
225
341
|
# a helper function to make usable fraction calculations more readable
|
|
226
342
|
def integrate(e):
|
|
227
|
-
return
|
|
343
|
+
return trapezoid(e, x=e.T.index, axis=-1)
|
|
228
344
|
|
|
229
345
|
# calculate usable fractions
|
|
230
346
|
uf_sun = integrate(e_sun * sr_sun) / integrate(e_sun)
|
|
@@ -245,7 +361,7 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
|
|
|
245
361
|
max_precipitable_water=8):
|
|
246
362
|
r"""
|
|
247
363
|
Spectral mismatch modifier based on precipitable water and absolute
|
|
248
|
-
(pressure-adjusted)
|
|
364
|
+
(pressure-adjusted) air mass.
|
|
249
365
|
|
|
250
366
|
Estimates a spectral mismatch modifier :math:`M` representing the effect on
|
|
251
367
|
module short circuit current of variation in the spectral
|
|
@@ -264,11 +380,11 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
|
|
|
264
380
|
SMARTS, spectrums are simulated with all combinations of AMa and
|
|
265
381
|
Pw where:
|
|
266
382
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
383
|
+
* :math:`0.5 \textrm{cm} <= Pw <= 5 \textrm{cm}`
|
|
384
|
+
* :math:`1.0 <= AM_a <= 5.0`
|
|
385
|
+
* Spectral range is limited to that of CMP11 (280 nm to 2800 nm)
|
|
386
|
+
* spectrum simulated on a plane normal to the sun
|
|
387
|
+
* All other parameters fixed at G173 standard
|
|
272
388
|
|
|
273
389
|
From these simulated spectra, M is calculated using the known
|
|
274
390
|
quantum efficiency curves. Multiple linear regression is then
|
|
@@ -283,18 +399,18 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
|
|
|
283
399
|
atmospheric precipitable water. [cm]
|
|
284
400
|
|
|
285
401
|
airmass_absolute : numeric
|
|
286
|
-
absolute (pressure-adjusted)
|
|
402
|
+
absolute (pressure-adjusted) air mass. [unitless]
|
|
287
403
|
|
|
288
404
|
module_type : str, optional
|
|
289
405
|
a string specifying a cell type. Values of 'cdte', 'monosi', 'xsi',
|
|
290
406
|
'multisi', and 'polysi' (can be lower or upper case). If provided,
|
|
291
407
|
module_type selects default coefficients for the following modules:
|
|
292
408
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
409
|
+
* 'cdte' - First Solar Series 4-2 CdTe module.
|
|
410
|
+
* 'monosi', 'xsi' - First Solar TetraSun module.
|
|
411
|
+
* 'multisi', 'polysi' - anonymous multi-crystalline silicon module.
|
|
412
|
+
* 'cigs' - anonymous copper indium gallium selenide module.
|
|
413
|
+
* 'asi' - anonymous amorphous silicon module.
|
|
298
414
|
|
|
299
415
|
The module used to calculate the spectral correction
|
|
300
416
|
coefficients corresponds to the Multi-crystalline silicon
|
|
@@ -570,3 +686,417 @@ def spectral_factor_caballero(precipitable_water, airmass_absolute, aod500,
|
|
|
570
686
|
)
|
|
571
687
|
modifier = f_AM + f_AOD + f_PW # Eq 5
|
|
572
688
|
return modifier
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
def spectral_factor_pvspec(airmass_absolute, clearsky_index,
|
|
692
|
+
module_type=None, coefficients=None):
|
|
693
|
+
r"""
|
|
694
|
+
Estimate a technology-specific spectral mismatch modifier from absolute
|
|
695
|
+
airmass and clear sky index using the PVSPEC model.
|
|
696
|
+
|
|
697
|
+
The PVSPEC spectral mismatch model includes the effects of cloud cover on
|
|
698
|
+
the irradiance spectrum. Model coefficients are derived using spectral
|
|
699
|
+
irradiance and other meteorological data from eight locations. Coefficients
|
|
700
|
+
for six module types are available via the ``module_type`` parameter.
|
|
701
|
+
More details on the model can be found in [1]_.
|
|
702
|
+
|
|
703
|
+
Parameters
|
|
704
|
+
----------
|
|
705
|
+
airmass_absolute : numeric
|
|
706
|
+
absolute (pressure-adjusted) airmass. [unitless]
|
|
707
|
+
|
|
708
|
+
clearsky_index: numeric
|
|
709
|
+
clear sky index. [unitless]
|
|
710
|
+
|
|
711
|
+
module_type : str, optional
|
|
712
|
+
One of the following PV technology strings from [1]_:
|
|
713
|
+
|
|
714
|
+
* ``'fs4-1'`` - First Solar series 4-1 and earlier CdTe module.
|
|
715
|
+
* ``'fs4-2'`` - First Solar 4-2 and later CdTe module.
|
|
716
|
+
* ``'monosi'``, - anonymous monocrystalline Si module.
|
|
717
|
+
* ``'multisi'``, - anonymous multicrystalline Si module.
|
|
718
|
+
* ``'cigs'`` - anonymous copper indium gallium selenide module.
|
|
719
|
+
* ``'asi'`` - anonymous amorphous silicon module.
|
|
720
|
+
|
|
721
|
+
coefficients : array-like, optional
|
|
722
|
+
user-defined coefficients, if not using one of the default coefficient
|
|
723
|
+
sets via the ``module_type`` parameter.
|
|
724
|
+
|
|
725
|
+
Returns
|
|
726
|
+
-------
|
|
727
|
+
mismatch: numeric
|
|
728
|
+
spectral mismatch factor (unitless) which is multiplied
|
|
729
|
+
with broadband irradiance reaching a module's cells to estimate
|
|
730
|
+
effective irradiance, i.e., the irradiance that is converted to
|
|
731
|
+
electrical current.
|
|
732
|
+
|
|
733
|
+
Notes
|
|
734
|
+
-----
|
|
735
|
+
The PVSPEC model parameterises the spectral mismatch factor as a function
|
|
736
|
+
of absolute air mass and the clear sky index as follows:
|
|
737
|
+
|
|
738
|
+
.. math::
|
|
739
|
+
|
|
740
|
+
M = a_1 k_c^{a_2} AM_a^{a_3},
|
|
741
|
+
|
|
742
|
+
where :math:`M` is the spectral mismatch factor, :math:`k_c` is the clear
|
|
743
|
+
sky index, :math:`AM_a` is the absolute air mass, and :math:`a_1, a_2, a_3`
|
|
744
|
+
are module-specific coefficients. In the PVSPEC model publication, absolute
|
|
745
|
+
air mass (denoted as :math:`AM`) is estimated starting from the Kasten and
|
|
746
|
+
Young relative air mass [2]_. The clear sky index, which is the ratio of
|
|
747
|
+
GHI to clear sky GHI, uses the ESRA model [3]_ to estimate the clear sky
|
|
748
|
+
GHI with monthly Linke turbidity values from [4]_ as inputs.
|
|
749
|
+
|
|
750
|
+
References
|
|
751
|
+
----------
|
|
752
|
+
.. [1] Pelland, S., Beswick, C., Thevenard, D., Côté, A., Pai, A. and
|
|
753
|
+
Poissant, Y., 2020. Development and testing of the PVSPEC model of
|
|
754
|
+
photovoltaic spectral mismatch factor. In 2020 47th IEEE Photovoltaic
|
|
755
|
+
Specialists Conference (PVSC) (pp. 1258-1264). IEEE.
|
|
756
|
+
:doi:`10.1109/PVSC45281.2020.9300932`
|
|
757
|
+
.. [2] Kasten, F. and Young, A.T., 1989. Revised optical air mass tables
|
|
758
|
+
and approximation formula. Applied Optics, 28(22), pp.4735-4738.
|
|
759
|
+
:doi:`10.1364/AO.28.004735`
|
|
760
|
+
.. [3] Rigollier, C., Bauer, O. and Wald, L., 2000. On the clear sky model
|
|
761
|
+
of the ESRA—European Solar Radiation Atlas—with respect to the Heliosat
|
|
762
|
+
method. Solar energy, 68(1), pp.33-48.
|
|
763
|
+
:doi:`10.1016/S0038-092X(99)00055-9`
|
|
764
|
+
.. [4] SoDa website monthly Linke turbidity values:
|
|
765
|
+
http://www.soda-pro.com/
|
|
766
|
+
"""
|
|
767
|
+
|
|
768
|
+
_coefficients = {}
|
|
769
|
+
_coefficients['multisi'] = (0.9847, -0.05237, 0.03034)
|
|
770
|
+
_coefficients['monosi'] = (0.9845, -0.05169, 0.03034)
|
|
771
|
+
_coefficients['fs-2'] = (1.002, -0.07108, 0.02465)
|
|
772
|
+
_coefficients['fs-4'] = (0.9981, -0.05776, 0.02336)
|
|
773
|
+
_coefficients['cigs'] = (0.9791, -0.03904, 0.03096)
|
|
774
|
+
_coefficients['asi'] = (1.051, -0.1033, 0.009838)
|
|
775
|
+
|
|
776
|
+
if module_type is not None and coefficients is None:
|
|
777
|
+
coefficients = _coefficients[module_type.lower()]
|
|
778
|
+
elif module_type is None and coefficients is not None:
|
|
779
|
+
pass
|
|
780
|
+
elif module_type is None and coefficients is None:
|
|
781
|
+
raise ValueError('No valid input provided, both module_type and ' +
|
|
782
|
+
'coefficients are None. module_type can be one of ' +
|
|
783
|
+
", ".join(_coefficients.keys()))
|
|
784
|
+
else:
|
|
785
|
+
raise ValueError('Cannot resolve input, must supply only one of ' +
|
|
786
|
+
'module_type and coefficients. module_type can be ' +
|
|
787
|
+
'one of' ", ".join(_coefficients.keys()))
|
|
788
|
+
|
|
789
|
+
coeff = coefficients
|
|
790
|
+
ama = airmass_absolute
|
|
791
|
+
kc = clearsky_index
|
|
792
|
+
mismatch = coeff[0]*np.power(kc, coeff[1])*np.power(ama, coeff[2])
|
|
793
|
+
|
|
794
|
+
return mismatch
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
def spectral_factor_jrc(airmass, clearsky_index, module_type=None,
|
|
798
|
+
coefficients=None):
|
|
799
|
+
r"""
|
|
800
|
+
Estimate a technology-specific spectral mismatch modifier from
|
|
801
|
+
airmass and clear sky index using the JRC model.
|
|
802
|
+
|
|
803
|
+
The JRC spectral mismatch model includes the effects of cloud cover on
|
|
804
|
+
the irradiance spectrum. Model coefficients are derived using measurements
|
|
805
|
+
of irradiance and module performance at the Joint Research Centre (JRC) in
|
|
806
|
+
Ispra, Italy (45.80N, 8.62E). Coefficients for two module types are
|
|
807
|
+
available via the ``module_type`` parameter. More details on the model can
|
|
808
|
+
be found in [1]_.
|
|
809
|
+
|
|
810
|
+
Parameters
|
|
811
|
+
----------
|
|
812
|
+
airmass : numeric
|
|
813
|
+
relative airmass. [unitless]
|
|
814
|
+
|
|
815
|
+
clearsky_index: numeric
|
|
816
|
+
clear sky index. [unitless]
|
|
817
|
+
|
|
818
|
+
module_type : str, optional
|
|
819
|
+
One of the following PV technology strings from [1]_:
|
|
820
|
+
|
|
821
|
+
* ``'cdte'`` - anonymous CdTe module.
|
|
822
|
+
* ``'multisi'`` - anonymous multicrystalline Si module.
|
|
823
|
+
|
|
824
|
+
coefficients : array-like, optional
|
|
825
|
+
user-defined coefficients, if not using one of the default coefficient
|
|
826
|
+
sets via the ``module_type`` parameter.
|
|
827
|
+
|
|
828
|
+
Returns
|
|
829
|
+
-------
|
|
830
|
+
mismatch: numeric
|
|
831
|
+
spectral mismatch factor (unitless) which is multiplied
|
|
832
|
+
with broadband irradiance reaching a module's cells to estimate
|
|
833
|
+
effective irradiance, i.e., the irradiance that is converted to
|
|
834
|
+
electrical current.
|
|
835
|
+
|
|
836
|
+
Notes
|
|
837
|
+
-----
|
|
838
|
+
The JRC model parameterises the spectral mismatch factor as a function
|
|
839
|
+
of air mass and the clear sky index as follows:
|
|
840
|
+
|
|
841
|
+
.. math::
|
|
842
|
+
|
|
843
|
+
M = 1 + a_1(e^{-k_c}-e^{-1}) + a_2(k_c-1)+a_3(AM-1.5),
|
|
844
|
+
|
|
845
|
+
where :math:`M` is the spectral mismatch factor, :math:`k_c` is the clear
|
|
846
|
+
sky index, :math:`AM` is the air mass, :math:`e` is Euler's number, and
|
|
847
|
+
:math:`a_1, a_2, a_3` are module-specific coefficients. The :math:`a_n`
|
|
848
|
+
coefficients available via the ``coefficients`` parameter differ from the
|
|
849
|
+
:math:`k_n` coefficients documented in [1]_ in that they are normalised by
|
|
850
|
+
the specific short-circuit current value, :math:`I_{sc0}^*`, which is the
|
|
851
|
+
expected short-circuit current at standard test conditions indoors. The
|
|
852
|
+
model used to estimate the air mass (denoted as :math:`AM`) is not stated
|
|
853
|
+
in the original publication. The authors of [1]_ used the ESRA model [2]_
|
|
854
|
+
to estimate the clear sky GHI for the clear sky index, which is the ratio
|
|
855
|
+
of GHI to clear sky GHI. Also, prior to the calculation of :math:`k_c`, the
|
|
856
|
+
irradiance measurements were corrected for angle of incidence using the
|
|
857
|
+
Martin and Ruiz model [3]_.
|
|
858
|
+
|
|
859
|
+
References
|
|
860
|
+
----------
|
|
861
|
+
.. [1] Huld, T., Sample, T., and Dunlop, E., 2009. A simple model
|
|
862
|
+
for estimating the influence of spectrum variations on PV performance.
|
|
863
|
+
In Proceedings of the 24th European Photovoltaic Solar Energy
|
|
864
|
+
Conference, Hamburg, Germany pp. 3385-3389. 2009. Accessed at:
|
|
865
|
+
https://www.researchgate.net/publication/256080247
|
|
866
|
+
.. [2] Rigollier, C., Bauer, O., and Wald, L., 2000. On the clear sky model
|
|
867
|
+
of the ESRA—European Solar Radiation Atlas—with respect to the Heliosat
|
|
868
|
+
method. Solar energy, 68(1), pp.33-48.
|
|
869
|
+
:doi:`10.1016/S0038-092X(99)00055-9`
|
|
870
|
+
.. [3] Martin, N. and Ruiz, J. M., 2001. Calculation of the PV modules
|
|
871
|
+
angular losses under field conditions by means of an analytical model.
|
|
872
|
+
Solar Energy Materials and Solar Cells, 70(1), 25-38.
|
|
873
|
+
:doi:`10.1016/S0927-0248(00)00408-6`
|
|
874
|
+
"""
|
|
875
|
+
|
|
876
|
+
_coefficients = {}
|
|
877
|
+
_coefficients['multisi'] = (0.00172, 0.000508, 0.00000357)
|
|
878
|
+
_coefficients['cdte'] = (0.000643, 0.000130, 0.0000108)
|
|
879
|
+
# normalise coefficients by I*sc0, see [1]
|
|
880
|
+
_coefficients = {
|
|
881
|
+
'multisi': tuple(x / 0.00348 for x in _coefficients['multisi']),
|
|
882
|
+
'cdte': tuple(x / 0.001150 for x in _coefficients['cdte'])
|
|
883
|
+
}
|
|
884
|
+
if module_type is not None and coefficients is None:
|
|
885
|
+
coefficients = _coefficients[module_type.lower()]
|
|
886
|
+
elif module_type is None and coefficients is not None:
|
|
887
|
+
pass
|
|
888
|
+
elif module_type is None and coefficients is None:
|
|
889
|
+
raise ValueError('No valid input provided, both module_type and ' +
|
|
890
|
+
'coefficients are None. module_type can be one of ' +
|
|
891
|
+
", ".join(_coefficients.keys()))
|
|
892
|
+
else:
|
|
893
|
+
raise ValueError('Cannot resolve input, must supply only one of ' +
|
|
894
|
+
'module_type and coefficients. module_type can be ' +
|
|
895
|
+
'one of' ", ".join(_coefficients.keys()))
|
|
896
|
+
|
|
897
|
+
coeff = coefficients
|
|
898
|
+
mismatch = (
|
|
899
|
+
1
|
|
900
|
+
+ coeff[0] * (np.exp(-clearsky_index) - np.exp(-1))
|
|
901
|
+
+ coeff[1] * (clearsky_index - 1)
|
|
902
|
+
+ coeff[2] * (airmass - 1.5)
|
|
903
|
+
)
|
|
904
|
+
return mismatch
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def sr_to_qe(sr, wavelength=None, normalize=False):
|
|
908
|
+
"""
|
|
909
|
+
Convert spectral responsivities to quantum efficiencies.
|
|
910
|
+
If ``wavelength`` is not provided, the spectral responsivity ``sr`` must be
|
|
911
|
+
a :py:class:`pandas.Series` or :py:class:`pandas.DataFrame`, with the
|
|
912
|
+
wavelengths in the index.
|
|
913
|
+
|
|
914
|
+
Provide wavelengths in nanometers, [nm].
|
|
915
|
+
|
|
916
|
+
Conversion is described in [1]_.
|
|
917
|
+
|
|
918
|
+
.. versionadded:: 0.11.0
|
|
919
|
+
|
|
920
|
+
Parameters
|
|
921
|
+
----------
|
|
922
|
+
sr : numeric, pandas.Series or pandas.DataFrame
|
|
923
|
+
Spectral response, [A/W].
|
|
924
|
+
Index must be the wavelength in nanometers, [nm].
|
|
925
|
+
|
|
926
|
+
wavelength : numeric, optional
|
|
927
|
+
Points where spectral response is measured, in nanometers, [nm].
|
|
928
|
+
|
|
929
|
+
normalize : bool, default False
|
|
930
|
+
If True, the quantum efficiency is normalized so that the maximum value
|
|
931
|
+
is 1.
|
|
932
|
+
For ``pandas.DataFrame``, normalization is done for each column.
|
|
933
|
+
For 2D arrays, normalization is done for each sub-array.
|
|
934
|
+
|
|
935
|
+
Returns
|
|
936
|
+
-------
|
|
937
|
+
quantum_efficiency : numeric, same type as ``sr``
|
|
938
|
+
Quantum efficiency, in the interval [0, 1].
|
|
939
|
+
|
|
940
|
+
Notes
|
|
941
|
+
-----
|
|
942
|
+
- If ``sr`` is of type ``pandas.Series`` or ``pandas.DataFrame``,
|
|
943
|
+
column names will remain unchanged in the returned object.
|
|
944
|
+
- If ``wavelength`` is provided it will be used independently of the
|
|
945
|
+
datatype of ``sr``.
|
|
946
|
+
|
|
947
|
+
Examples
|
|
948
|
+
--------
|
|
949
|
+
>>> import numpy as np
|
|
950
|
+
>>> import pandas as pd
|
|
951
|
+
>>> from pvlib import spectrum
|
|
952
|
+
>>> wavelengths = np.array([350, 550, 750])
|
|
953
|
+
>>> spectral_response = np.array([0.25, 0.40, 0.57])
|
|
954
|
+
>>> quantum_efficiency = spectrum.sr_to_qe(spectral_response, wavelengths)
|
|
955
|
+
>>> print(quantum_efficiency)
|
|
956
|
+
array([0.88560142, 0.90170326, 0.94227991])
|
|
957
|
+
|
|
958
|
+
>>> spectral_response_series = pd.Series(spectral_response, index=wavelengths, name="dataset")
|
|
959
|
+
>>> qe = spectrum.sr_to_qe(spectral_response_series)
|
|
960
|
+
>>> print(qe)
|
|
961
|
+
350 0.885601
|
|
962
|
+
550 0.901703
|
|
963
|
+
750 0.942280
|
|
964
|
+
Name: dataset, dtype: float64
|
|
965
|
+
|
|
966
|
+
>>> qe = spectrum.sr_to_qe(spectral_response_series, normalize=True)
|
|
967
|
+
>>> print(qe)
|
|
968
|
+
350 0.939850
|
|
969
|
+
550 0.956938
|
|
970
|
+
750 1.000000
|
|
971
|
+
Name: dataset, dtype: float64
|
|
972
|
+
|
|
973
|
+
References
|
|
974
|
+
----------
|
|
975
|
+
.. [1] “Spectral Response,” PV Performance Modeling Collaborative (PVPMC).
|
|
976
|
+
https://pvpmc.sandia.gov/modeling-guide/2-dc-module-iv/effective-irradiance/spectral-response/
|
|
977
|
+
.. [2] “Spectral Response | PVEducation,” www.pveducation.org.
|
|
978
|
+
https://www.pveducation.org/pvcdrom/solar-cell-operation/spectral-response
|
|
979
|
+
|
|
980
|
+
See Also
|
|
981
|
+
--------
|
|
982
|
+
pvlib.spectrum.qe_to_sr
|
|
983
|
+
""" # noqa: E501
|
|
984
|
+
if wavelength is None:
|
|
985
|
+
if hasattr(sr, "index"): # true for pandas objects
|
|
986
|
+
# use reference to index values instead of index alone so
|
|
987
|
+
# sr / wavelength returns a series with the same name
|
|
988
|
+
wavelength = sr.index.array
|
|
989
|
+
else:
|
|
990
|
+
raise TypeError(
|
|
991
|
+
"'sr' must have an '.index' attribute"
|
|
992
|
+
+ " or 'wavelength' must be provided"
|
|
993
|
+
)
|
|
994
|
+
quantum_efficiency = (
|
|
995
|
+
sr
|
|
996
|
+
/ wavelength
|
|
997
|
+
* _PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
if normalize:
|
|
1001
|
+
quantum_efficiency = normalize_max2one(quantum_efficiency)
|
|
1002
|
+
|
|
1003
|
+
return quantum_efficiency
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
def qe_to_sr(qe, wavelength=None, normalize=False):
|
|
1007
|
+
"""
|
|
1008
|
+
Convert quantum efficiencies to spectral responsivities.
|
|
1009
|
+
If ``wavelength`` is not provided, the quantum efficiency ``qe`` must be
|
|
1010
|
+
a :py:class:`pandas.Series` or :py:class:`pandas.DataFrame`, with the
|
|
1011
|
+
wavelengths in the index.
|
|
1012
|
+
|
|
1013
|
+
Provide wavelengths in nanometers, [nm].
|
|
1014
|
+
|
|
1015
|
+
Conversion is described in [1]_.
|
|
1016
|
+
|
|
1017
|
+
.. versionadded:: 0.11.0
|
|
1018
|
+
|
|
1019
|
+
Parameters
|
|
1020
|
+
----------
|
|
1021
|
+
qe : numeric, pandas.Series or pandas.DataFrame
|
|
1022
|
+
Quantum efficiency.
|
|
1023
|
+
If pandas subtype, index must be the wavelength in nanometers, [nm].
|
|
1024
|
+
|
|
1025
|
+
wavelength : numeric, optional
|
|
1026
|
+
Points where quantum efficiency is measured, in nanometers, [nm].
|
|
1027
|
+
|
|
1028
|
+
normalize : bool, default False
|
|
1029
|
+
If True, the spectral response is normalized so that the maximum value
|
|
1030
|
+
is 1.
|
|
1031
|
+
For ``pandas.DataFrame``, normalization is done for each column.
|
|
1032
|
+
For 2D arrays, normalization is done for each sub-array.
|
|
1033
|
+
|
|
1034
|
+
Returns
|
|
1035
|
+
-------
|
|
1036
|
+
spectral_response : numeric, same type as ``qe``
|
|
1037
|
+
Spectral response, [A/W].
|
|
1038
|
+
|
|
1039
|
+
Notes
|
|
1040
|
+
-----
|
|
1041
|
+
- If ``qe`` is of type ``pandas.Series`` or ``pandas.DataFrame``,
|
|
1042
|
+
column names will remain unchanged in the returned object.
|
|
1043
|
+
- If ``wavelength`` is provided it will be used independently of the
|
|
1044
|
+
datatype of ``qe``.
|
|
1045
|
+
|
|
1046
|
+
Examples
|
|
1047
|
+
--------
|
|
1048
|
+
>>> import numpy as np
|
|
1049
|
+
>>> import pandas as pd
|
|
1050
|
+
>>> from pvlib import spectrum
|
|
1051
|
+
>>> wavelengths = np.array([350, 550, 750])
|
|
1052
|
+
>>> quantum_efficiency = np.array([0.86, 0.90, 0.94])
|
|
1053
|
+
>>> spectral_response = spectrum.qe_to_sr(quantum_efficiency, wavelengths)
|
|
1054
|
+
>>> print(spectral_response)
|
|
1055
|
+
array([0.24277287, 0.39924442, 0.56862085])
|
|
1056
|
+
|
|
1057
|
+
>>> quantum_efficiency_series = pd.Series(quantum_efficiency, index=wavelengths, name="dataset")
|
|
1058
|
+
>>> sr = spectrum.qe_to_sr(quantum_efficiency_series)
|
|
1059
|
+
>>> print(sr)
|
|
1060
|
+
350 0.242773
|
|
1061
|
+
550 0.399244
|
|
1062
|
+
750 0.568621
|
|
1063
|
+
Name: dataset, dtype: float64
|
|
1064
|
+
|
|
1065
|
+
>>> sr = spectrum.qe_to_sr(quantum_efficiency_series, normalize=True)
|
|
1066
|
+
>>> print(sr)
|
|
1067
|
+
350 0.426950
|
|
1068
|
+
550 0.702128
|
|
1069
|
+
750 1.000000
|
|
1070
|
+
Name: dataset, dtype: float64
|
|
1071
|
+
|
|
1072
|
+
References
|
|
1073
|
+
----------
|
|
1074
|
+
.. [1] “Spectral Response,” PV Performance Modeling Collaborative (PVPMC).
|
|
1075
|
+
https://pvpmc.sandia.gov/modeling-guide/2-dc-module-iv/effective-irradiance/spectral-response/
|
|
1076
|
+
.. [2] “Spectral Response | PVEducation,” www.pveducation.org.
|
|
1077
|
+
https://www.pveducation.org/pvcdrom/solar-cell-operation/spectral-response
|
|
1078
|
+
|
|
1079
|
+
See Also
|
|
1080
|
+
--------
|
|
1081
|
+
pvlib.spectrum.sr_to_qe
|
|
1082
|
+
""" # noqa: E501
|
|
1083
|
+
if wavelength is None:
|
|
1084
|
+
if hasattr(qe, "index"): # true for pandas objects
|
|
1085
|
+
# use reference to index values instead of index alone so
|
|
1086
|
+
# sr / wavelength returns a series with the same name
|
|
1087
|
+
wavelength = qe.index.array
|
|
1088
|
+
else:
|
|
1089
|
+
raise TypeError(
|
|
1090
|
+
"'qe' must have an '.index' attribute"
|
|
1091
|
+
+ " or 'wavelength' must be provided"
|
|
1092
|
+
)
|
|
1093
|
+
spectral_responsivity = (
|
|
1094
|
+
qe
|
|
1095
|
+
* wavelength
|
|
1096
|
+
/ _PLANCK_BY_LIGHT_SPEED_OVER_ELEMENTAL_CHARGE_BY_BILLION
|
|
1097
|
+
)
|
|
1098
|
+
|
|
1099
|
+
if normalize:
|
|
1100
|
+
spectral_responsivity = normalize_max2one(spectral_responsivity)
|
|
1101
|
+
|
|
1102
|
+
return spectral_responsivity
|