pvlib 0.10.5__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 (39) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/albedo.py +168 -0
  3. pvlib/data/ASTMG173.csv +2004 -0
  4. pvlib/iam.py +28 -28
  5. pvlib/iotools/__init__.py +0 -1
  6. pvlib/iotools/midc.py +15 -10
  7. pvlib/iotools/psm3.py +10 -25
  8. pvlib/iotools/srml.py +0 -61
  9. pvlib/irradiance.py +133 -95
  10. pvlib/location.py +13 -5
  11. pvlib/modelchain.py +2 -165
  12. pvlib/pvsystem.py +23 -63
  13. pvlib/shading.py +350 -0
  14. pvlib/spectrum/__init__.py +5 -0
  15. pvlib/spectrum/mismatch.py +572 -43
  16. pvlib/spectrum/spectrl2.py +8 -8
  17. pvlib/tests/iotools/test_psm3.py +0 -18
  18. pvlib/tests/iotools/test_srml.py +1 -43
  19. pvlib/tests/test_albedo.py +84 -0
  20. pvlib/tests/test_inverter.py +2 -2
  21. pvlib/tests/test_irradiance.py +35 -2
  22. pvlib/tests/test_location.py +26 -18
  23. pvlib/tests/test_modelchain.py +0 -57
  24. pvlib/tests/test_pvsystem.py +11 -39
  25. pvlib/tests/test_shading.py +167 -1
  26. pvlib/tests/test_singlediode.py +0 -19
  27. pvlib/tests/test_spectrum.py +283 -22
  28. pvlib/tests/test_temperature.py +7 -7
  29. pvlib/tests/test_tools.py +24 -0
  30. pvlib/tests/test_transformer.py +60 -0
  31. pvlib/tools.py +27 -0
  32. pvlib/transformer.py +117 -0
  33. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/METADATA +1 -1
  34. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/RECORD +38 -34
  35. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/WHEEL +1 -1
  36. pvlib/data/astm_g173_am15g.csv +0 -2003
  37. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/AUTHORS.md +0 -0
  38. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/LICENSE +0 -0
  39. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/top_level.txt +0 -0
@@ -3,13 +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
8
- from scipy.interpolate import interp1d
10
+ import scipy.constants
9
11
  from scipy.integrate import trapezoid
10
- import os
12
+ from scipy.interpolate import interp1d
11
13
 
14
+ from pathlib import Path
12
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
+ )
13
25
 
14
26
 
15
27
  def get_example_spectral_response(wavelength=None):
@@ -44,14 +56,14 @@ def get_example_spectral_response(wavelength=None):
44
56
  '''
45
57
  # Contributed by Anton Driesse (@adriesse), PV Performance Labs. Aug. 2022
46
58
 
47
- SR_DATA = np.array([[ 290, 0.00],
48
- [ 350, 0.27],
49
- [ 400, 0.37],
50
- [ 500, 0.52],
51
- [ 650, 0.71],
52
- [ 800, 0.88],
53
- [ 900, 0.97],
54
- [ 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],
55
67
  [1000, 0.93],
56
68
  [1050, 0.58],
57
69
  [1100, 0.21],
@@ -77,8 +89,18 @@ def get_example_spectral_response(wavelength=None):
77
89
  return sr
78
90
 
79
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
+ )
80
102
  def get_am15g(wavelength=None):
81
- '''
103
+ r"""
82
104
  Read the ASTM G173-03 AM1.5 global spectrum on a 37-degree tilted surface,
83
105
  optionally interpolated to the specified wavelength(s).
84
106
 
@@ -92,19 +114,19 @@ def get_am15g(wavelength=None):
92
114
  ----------
93
115
  wavelength: 1-D sequence of numeric, optional
94
116
  Wavelengths at which the spectrum is interpolated.
95
- By default the 2002 wavelengths of the standard are returned. [nm]
117
+ By default the 2002 wavelengths of the standard are returned. [nm].
96
118
 
97
119
  Returns
98
120
  -------
99
121
  am15g: pandas.Series
100
- The AM1.5g standard spectrum indexed by ``wavelength``. [(W/m^2)/nm]
122
+ The AM1.5g standard spectrum indexed by ``wavelength``. [W/(m²nm)].
101
123
 
102
124
  Notes
103
125
  -----
104
126
  If ``wavelength`` is specified this function uses linear interpolation.
105
127
 
106
128
  If the values in ``wavelength`` are too widely spaced, the integral of the
107
- 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².
108
130
 
109
131
  The values in the data file provided with pvlib-python are copied from an
110
132
  Excel file distributed by NREL, which is found here:
@@ -113,32 +135,125 @@ def get_am15g(wavelength=None):
113
135
  More information about reference spectra is found here:
114
136
  https://www.nrel.gov/grid/solar-resource/spectra-am1.5.html
115
137
 
138
+ See Also
139
+ --------
140
+ pvlib.spectrum.get_reference_spectra : reads also the direct and
141
+ extraterrestrial components of the spectrum.
142
+
116
143
  References
117
144
  ----------
118
145
  .. [1] ASTM "G173-03 Standard Tables for Reference Solar Spectral
119
146
  Irradiances: Direct Normal and Hemispherical on 37° Tilted Surface."
120
- '''
147
+ """ # noqa: E501
121
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"]
152
+
153
+
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.
173
+
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``.
179
+
180
+ Notes
181
+ -----
182
+ If ``wavelength`` is specified, linear interpolation is used.
122
183
 
123
- pvlib_path = pvlib.__path__[0]
124
- filepath = os.path.join(pvlib_path, 'data', 'astm_g173_am15g.csv')
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².
125
187
 
126
- am15g = pd.read_csv(filepath, index_col=0).squeeze()
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
127
191
 
128
- if wavelength is not None:
129
- interpolator = interp1d(am15g.index, am15g,
130
- kind='linear',
131
- bounds_error=False,
132
- fill_value=0.0,
133
- copy=False,
134
- assume_sorted=True)
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
135
215
 
136
- am15g = pd.Series(data=interpolator(wavelength), index=wavelength)
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
+ )
137
243
 
138
- am15g.index.name = 'wavelength'
139
- am15g.name = 'am15g'
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
+ )
140
255
 
141
- return am15g
256
+ return standard
142
257
 
143
258
 
144
259
  def calc_spectral_mismatch_field(sr, e_sun, e_ref=None):
@@ -154,7 +269,7 @@ def calc_spectral_mismatch_field(sr, e_sun, e_ref=None):
154
269
 
155
270
  e_sun: pandas.DataFrame or pandas.Series
156
271
  One or more measured solar irradiance spectra in a pandas.DataFrame
157
- 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
158
273
  be given as a pandas.Series having wavelength in nm as index.
159
274
  [(W/m^2)/nm]
160
275
 
@@ -216,7 +331,7 @@ def calc_spectral_mismatch_field(sr, e_sun, e_ref=None):
216
331
 
217
332
  # get the reference spectrum at wavelengths matching the measured spectra
218
333
  if e_ref is None:
219
- e_ref = get_am15g(wavelength=e_sun.T.index)
334
+ e_ref = get_reference_spectra(wavelengths=e_sun.T.index)["global"]
220
335
 
221
336
  # interpolate the sr at the wavelengths of the spectra
222
337
  # reference spectrum wavelengths may differ if e_ref is from caller
@@ -246,7 +361,7 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
246
361
  max_precipitable_water=8):
247
362
  r"""
248
363
  Spectral mismatch modifier based on precipitable water and absolute
249
- (pressure-adjusted) airmass.
364
+ (pressure-adjusted) air mass.
250
365
 
251
366
  Estimates a spectral mismatch modifier :math:`M` representing the effect on
252
367
  module short circuit current of variation in the spectral
@@ -265,11 +380,11 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
265
380
  SMARTS, spectrums are simulated with all combinations of AMa and
266
381
  Pw where:
267
382
 
268
- * :math:`0.5 \textrm{cm} <= Pw <= 5 \textrm{cm}`
269
- * :math:`1.0 <= AM_a <= 5.0`
270
- * Spectral range is limited to that of CMP11 (280 nm to 2800 nm)
271
- * spectrum simulated on a plane normal to the sun
272
- * 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
273
388
 
274
389
  From these simulated spectra, M is calculated using the known
275
390
  quantum efficiency curves. Multiple linear regression is then
@@ -284,18 +399,18 @@ def spectral_factor_firstsolar(precipitable_water, airmass_absolute,
284
399
  atmospheric precipitable water. [cm]
285
400
 
286
401
  airmass_absolute : numeric
287
- absolute (pressure-adjusted) airmass. [unitless]
402
+ absolute (pressure-adjusted) air mass. [unitless]
288
403
 
289
404
  module_type : str, optional
290
405
  a string specifying a cell type. Values of 'cdte', 'monosi', 'xsi',
291
406
  'multisi', and 'polysi' (can be lower or upper case). If provided,
292
407
  module_type selects default coefficients for the following modules:
293
408
 
294
- * 'cdte' - First Solar Series 4-2 CdTe module.
295
- * 'monosi', 'xsi' - First Solar TetraSun module.
296
- * 'multisi', 'polysi' - anonymous multi-crystalline silicon module.
297
- * 'cigs' - anonymous copper indium gallium selenide module.
298
- * '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.
299
414
 
300
415
  The module used to calculate the spectral correction
301
416
  coefficients corresponds to the Multi-crystalline silicon
@@ -571,3 +686,417 @@ def spectral_factor_caballero(precipitable_water, airmass_absolute, aod500,
571
686
  )
572
687
  modifier = f_AM + f_AOD + f_PW # Eq 5
573
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