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.
Files changed (47) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/albedo.py +168 -0
  3. pvlib/bifacial/utils.py +2 -1
  4. pvlib/data/ASTMG173.csv +2004 -0
  5. pvlib/iam.py +28 -28
  6. pvlib/iotools/__init__.py +0 -1
  7. pvlib/iotools/midc.py +15 -10
  8. pvlib/iotools/psm3.py +10 -25
  9. pvlib/iotools/srml.py +1 -61
  10. pvlib/iotools/surfrad.py +1 -1
  11. pvlib/irradiance.py +133 -95
  12. pvlib/location.py +16 -6
  13. pvlib/modelchain.py +2 -165
  14. pvlib/pvarray.py +7 -5
  15. pvlib/pvsystem.py +75 -106
  16. pvlib/scaling.py +4 -2
  17. pvlib/shading.py +350 -0
  18. pvlib/singlediode.py +37 -9
  19. pvlib/snow.py +3 -1
  20. pvlib/spectrum/__init__.py +5 -0
  21. pvlib/spectrum/mismatch.py +573 -43
  22. pvlib/spectrum/spectrl2.py +8 -8
  23. pvlib/tests/bifacial/test_utils.py +6 -5
  24. pvlib/tests/iotools/test_psm3.py +0 -18
  25. pvlib/tests/iotools/test_srml.py +1 -43
  26. pvlib/tests/test_albedo.py +84 -0
  27. pvlib/tests/test_inverter.py +2 -2
  28. pvlib/tests/test_irradiance.py +35 -2
  29. pvlib/tests/test_location.py +26 -18
  30. pvlib/tests/test_modelchain.py +0 -57
  31. pvlib/tests/test_pvsystem.py +73 -128
  32. pvlib/tests/test_shading.py +167 -1
  33. pvlib/tests/test_singlediode.py +68 -29
  34. pvlib/tests/test_spectrum.py +283 -22
  35. pvlib/tests/test_temperature.py +7 -7
  36. pvlib/tests/test_tools.py +24 -0
  37. pvlib/tests/test_transformer.py +60 -0
  38. pvlib/tools.py +27 -0
  39. pvlib/transformer.py +117 -0
  40. pvlib/version.py +1 -5
  41. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/METADATA +3 -4
  42. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/RECORD +46 -42
  43. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/WHEEL +1 -1
  44. pvlib/data/astm_g173_am15g.csv +0 -2003
  45. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/AUTHORS.md +0 -0
  46. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/LICENSE +0 -0
  47. {pvlib-0.10.4.dist-info → pvlib-0.11.0.dist-info}/top_level.txt +0 -0
@@ -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([[ 290, 0.00],
47
- [ 350, 0.27],
48
- [ 400, 0.37],
49
- [ 500, 0.52],
50
- [ 650, 0.71],
51
- [ 800, 0.88],
52
- [ 900, 0.97],
53
- [ 950, 1.00],
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``. [(W/m^2)/nm]
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^2.
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
- am15g = pd.read_csv(filepath, index_col=0).squeeze()
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
- if wavelength is not None:
128
- interpolator = interp1d(am15g.index, am15g,
129
- kind='linear',
130
- bounds_error=False,
131
- fill_value=0.0,
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
- am15g = pd.Series(data=interpolator(wavelength), index=wavelength)
180
+ Notes
181
+ -----
182
+ If ``wavelength`` is specified, linear interpolation is used.
136
183
 
137
- am15g.index.name = 'wavelength'
138
- am15g.name = 'am15g'
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
- return am15g
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. A single spectrum may be
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 = get_am15g(wavelength=e_sun.T.index)
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 np.trapz(e, x=e.T.index, axis=-1)
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) airmass.
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
- * :math:`0.5 \textrm{cm} <= Pw <= 5 \textrm{cm}`
268
- * :math:`1.0 <= AM_a <= 5.0`
269
- * Spectral range is limited to that of CMP11 (280 nm to 2800 nm)
270
- * spectrum simulated on a plane normal to the sun
271
- * All other parameters fixed at G173 standard
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) airmass. [unitless]
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
- * 'cdte' - First Solar Series 4-2 CdTe module.
294
- * 'monosi', 'xsi' - First Solar TetraSun module.
295
- * 'multisi', 'polysi' - anonymous multi-crystalline silicon module.
296
- * 'cigs' - anonymous copper indium gallium selenide module.
297
- * 'asi' - anonymous amorphous silicon module.
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