pvlib 0.11.2__py3-none-any.whl → 0.12.1a1__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 (147) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/atmosphere.py +40 -40
  3. pvlib/bifacial/infinite_sheds.py +4 -3
  4. pvlib/bifacial/utils.py +2 -1
  5. pvlib/iotools/__init__.py +6 -0
  6. pvlib/iotools/psm3.py +1 -1
  7. pvlib/iotools/psm4.py +819 -0
  8. pvlib/iotools/pvgis.py +10 -2
  9. pvlib/iotools/tmy.py +3 -69
  10. pvlib/irradiance.py +38 -15
  11. pvlib/ivtools/sdm/__init__.py +20 -0
  12. pvlib/ivtools/sdm/_fit_desoto_pvsyst_sandia.py +585 -0
  13. pvlib/ivtools/sdm/cec.py +93 -0
  14. pvlib/ivtools/sdm/desoto.py +401 -0
  15. pvlib/ivtools/sdm/pvsyst.py +630 -0
  16. pvlib/location.py +73 -33
  17. pvlib/modelchain.py +19 -36
  18. pvlib/pvsystem.py +114 -65
  19. pvlib/snow.py +64 -28
  20. pvlib/spectrum/__init__.py +0 -1
  21. pvlib/spectrum/irradiance.py +2 -64
  22. pvlib/spectrum/mismatch.py +3 -3
  23. pvlib/tools.py +6 -5
  24. {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info}/METADATA +6 -5
  25. pvlib-0.12.1a1.dist-info/RECORD +80 -0
  26. {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info}/WHEEL +1 -1
  27. pvlib/data/BIRD_08_16_2012.csv +0 -8761
  28. pvlib/data/BIRD_08_16_2012_patm.csv +0 -8761
  29. pvlib/data/Burlington, United States SolarAnywhere Time Series 2021 Lat_44_465 Lon_-73_205 TMY3 format.csv +0 -8762
  30. pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv +0 -578
  31. pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv +0 -74
  32. pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND +0 -146
  33. pvlib/data/CRNS0101-05-2019-AZ_Tucson_11_W.txt +0 -4
  34. pvlib/data/CRN_with_problems.txt +0 -3
  35. pvlib/data/ET-M772BH550GL.PAN +0 -75
  36. pvlib/data/NLD_Amsterdam062400_IWEC.epw +0 -8768
  37. pvlib/data/PVsyst_demo.csv +0 -10757
  38. pvlib/data/PVsyst_demo_model.csv +0 -3588
  39. pvlib/data/SRML-day-EUPO1801.txt +0 -1441
  40. pvlib/data/abq19056.dat +0 -6
  41. pvlib/data/bishop88_numerical_precision.csv +0 -101
  42. pvlib/data/bsrn-lr0100-pay0616.dat +0 -86901
  43. pvlib/data/bsrn-pay0616.dat.gz +0 -0
  44. pvlib/data/cams_mcclear_1min_verbose.csv +0 -60
  45. pvlib/data/cams_mcclear_monthly.csv +0 -42
  46. pvlib/data/cams_radiation_1min_verbose.csv +0 -72
  47. pvlib/data/cams_radiation_monthly.csv +0 -47
  48. pvlib/data/detect_clearsky_data.csv +0 -35
  49. pvlib/data/detect_clearsky_threshold_data.csv +0 -126
  50. pvlib/data/greensboro_kimber_soil_manwash.dat +0 -8761
  51. pvlib/data/greensboro_kimber_soil_nowash.dat +0 -8761
  52. pvlib/data/inverter_fit_snl_meas.csv +0 -127
  53. pvlib/data/inverter_fit_snl_sim.csv +0 -19
  54. pvlib/data/ivtools_numdiff.csv +0 -52
  55. pvlib/data/midc_20181014.txt +0 -1441
  56. pvlib/data/midc_raw_20181018.txt +0 -1441
  57. pvlib/data/midc_raw_short_header_20191115.txt +0 -1441
  58. pvlib/data/msn19056.dat +0 -6
  59. pvlib/data/precise_iv_curves1.json +0 -10251
  60. pvlib/data/precise_iv_curves2.json +0 -10251
  61. pvlib/data/precise_iv_curves_parameter_sets1.csv +0 -33
  62. pvlib/data/precise_iv_curves_parameter_sets2.csv +0 -33
  63. pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA2_10kWp_CIS_5_2a_2013_2014.json +0 -1
  64. pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA_30deg_0deg_2016_2016.csv +0 -35
  65. pvlib/data/pvgis_tmy_meta.json +0 -32
  66. pvlib/data/pvgis_tmy_test.csv +0 -8761
  67. pvlib/data/pvwatts_8760_rackmount.csv +0 -8779
  68. pvlib/data/pvwatts_8760_roofmount.csv +0 -8779
  69. pvlib/data/singleaxis_tracker_wslope.csv +0 -8761
  70. pvlib/data/spectrl2_example_spectra.csv +0 -123
  71. pvlib/data/surfrad-slv16001.dat +0 -1442
  72. pvlib/data/test_psm3_2017.csv +0 -17521
  73. pvlib/data/test_psm3_2019_5min.csv +0 -289
  74. pvlib/data/test_psm3_tmy-2017.csv +0 -8761
  75. pvlib/data/test_read_psm3.csv +0 -17523
  76. pvlib/data/test_read_pvgis_horizon.csv +0 -49
  77. pvlib/data/tmy_45.000_8.000_2005_2023.csv +0 -8789
  78. pvlib/data/tmy_45.000_8.000_2005_2023.epw +0 -8768
  79. pvlib/data/tmy_45.000_8.000_2005_2023.json +0 -1
  80. pvlib/data/tmy_45.000_8.000_2005_2023.txt +0 -8761
  81. pvlib/data/tmy_45.000_8.000_userhorizon.json +0 -1
  82. pvlib/ivtools/sdm.py +0 -1379
  83. pvlib/spa_c_files/README.md +0 -81
  84. pvlib/spa_c_files/cspa_py.pxd +0 -43
  85. pvlib/spa_c_files/spa_py.pyx +0 -30
  86. pvlib/tests/__init__.py +0 -0
  87. pvlib/tests/bifacial/__init__.py +0 -0
  88. pvlib/tests/bifacial/test_infinite_sheds.py +0 -317
  89. pvlib/tests/bifacial/test_losses_models.py +0 -54
  90. pvlib/tests/bifacial/test_pvfactors.py +0 -82
  91. pvlib/tests/bifacial/test_utils.py +0 -192
  92. pvlib/tests/conftest.py +0 -476
  93. pvlib/tests/iotools/__init__.py +0 -0
  94. pvlib/tests/iotools/test_acis.py +0 -213
  95. pvlib/tests/iotools/test_bsrn.py +0 -131
  96. pvlib/tests/iotools/test_crn.py +0 -95
  97. pvlib/tests/iotools/test_epw.py +0 -23
  98. pvlib/tests/iotools/test_midc.py +0 -89
  99. pvlib/tests/iotools/test_panond.py +0 -32
  100. pvlib/tests/iotools/test_psm3.py +0 -198
  101. pvlib/tests/iotools/test_pvgis.py +0 -644
  102. pvlib/tests/iotools/test_sodapro.py +0 -298
  103. pvlib/tests/iotools/test_solaranywhere.py +0 -287
  104. pvlib/tests/iotools/test_solargis.py +0 -68
  105. pvlib/tests/iotools/test_solcast.py +0 -324
  106. pvlib/tests/iotools/test_solrad.py +0 -152
  107. pvlib/tests/iotools/test_srml.py +0 -124
  108. pvlib/tests/iotools/test_surfrad.py +0 -75
  109. pvlib/tests/iotools/test_tmy.py +0 -133
  110. pvlib/tests/ivtools/__init__.py +0 -0
  111. pvlib/tests/ivtools/test_sde.py +0 -230
  112. pvlib/tests/ivtools/test_sdm.py +0 -429
  113. pvlib/tests/ivtools/test_utils.py +0 -173
  114. pvlib/tests/spectrum/__init__.py +0 -0
  115. pvlib/tests/spectrum/conftest.py +0 -40
  116. pvlib/tests/spectrum/test_irradiance.py +0 -138
  117. pvlib/tests/spectrum/test_mismatch.py +0 -304
  118. pvlib/tests/spectrum/test_response.py +0 -124
  119. pvlib/tests/spectrum/test_spectrl2.py +0 -72
  120. pvlib/tests/test__deprecation.py +0 -97
  121. pvlib/tests/test_albedo.py +0 -84
  122. pvlib/tests/test_atmosphere.py +0 -351
  123. pvlib/tests/test_clearsky.py +0 -884
  124. pvlib/tests/test_conftest.py +0 -37
  125. pvlib/tests/test_iam.py +0 -555
  126. pvlib/tests/test_inverter.py +0 -213
  127. pvlib/tests/test_irradiance.py +0 -1487
  128. pvlib/tests/test_location.py +0 -356
  129. pvlib/tests/test_modelchain.py +0 -2020
  130. pvlib/tests/test_numerical_precision.py +0 -124
  131. pvlib/tests/test_pvarray.py +0 -71
  132. pvlib/tests/test_pvsystem.py +0 -2511
  133. pvlib/tests/test_scaling.py +0 -207
  134. pvlib/tests/test_shading.py +0 -391
  135. pvlib/tests/test_singlediode.py +0 -608
  136. pvlib/tests/test_snow.py +0 -212
  137. pvlib/tests/test_soiling.py +0 -230
  138. pvlib/tests/test_solarposition.py +0 -966
  139. pvlib/tests/test_spa.py +0 -454
  140. pvlib/tests/test_temperature.py +0 -470
  141. pvlib/tests/test_tools.py +0 -146
  142. pvlib/tests/test_tracking.py +0 -474
  143. pvlib/tests/test_transformer.py +0 -60
  144. pvlib-0.11.2.dist-info/RECORD +0 -191
  145. {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info/licenses}/AUTHORS.md +0 -0
  146. {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info/licenses}/LICENSE +0 -0
  147. {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info}/top_level.txt +0 -0
pvlib/ivtools/sdm.py DELETED
@@ -1,1379 +0,0 @@
1
- """
2
- The ``sdm`` module contains functions to fit single diode models.
3
-
4
- Function names should follow the pattern "fit_" + name of model + "_" +
5
- fitting method.
6
-
7
- """
8
-
9
- import numpy as np
10
-
11
- from scipy import constants
12
- from scipy import optimize
13
- from scipy.special import lambertw
14
-
15
- from pvlib.pvsystem import calcparams_pvsyst, singlediode, v_from_i
16
- from pvlib.singlediode import bishop88_mpp
17
-
18
- from pvlib.ivtools.utils import rectify_iv_curve, _numdiff
19
- from pvlib.ivtools.sde import _fit_sandia_cocontent
20
-
21
- from pvlib.tools import _first_order_centered_difference
22
-
23
-
24
- CONSTANTS = {'E0': 1000.0, 'T0': 25.0, 'k': constants.k, 'q': constants.e}
25
-
26
-
27
- def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc,
28
- gamma_pmp, cells_in_series, temp_ref=25):
29
- """
30
- Estimates parameters for the CEC single diode model (SDM) using the SAM
31
- SDK.
32
-
33
- Parameters
34
- ----------
35
- celltype : str
36
- Value is one of 'monoSi', 'multiSi', 'polySi', 'cis', 'cigs', 'cdte',
37
- 'amorphous'
38
- v_mp : float
39
- Voltage at maximum power point [V]
40
- i_mp : float
41
- Current at maximum power point [A]
42
- v_oc : float
43
- Open circuit voltage [V]
44
- i_sc : float
45
- Short circuit current [A]
46
- alpha_sc : float
47
- Temperature coefficient of short circuit current [A/C]
48
- beta_voc : float
49
- Temperature coefficient of open circuit voltage [V/C]
50
- gamma_pmp : float
51
- Temperature coefficient of power at maximum power point [%/C]
52
- cells_in_series : int
53
- Number of cells in series
54
- temp_ref : float, default 25
55
- Reference temperature condition [C]
56
-
57
- Returns
58
- -------
59
- I_L_ref : float
60
- The light-generated current (or photocurrent) at reference
61
- conditions [A]
62
- I_o_ref : float
63
- The dark or diode reverse saturation current at reference
64
- conditions [A]
65
- R_s : float
66
- The series resistance at reference conditions, in ohms.
67
- R_sh_ref : float
68
- The shunt resistance at reference conditions, in ohms.
69
- a_ref : float
70
- The product of the usual diode ideality factor ``n`` (unitless),
71
- number of cells in series ``Ns``, and cell thermal voltage at
72
- reference conditions [V]
73
- Adjust : float
74
- The adjustment to the temperature coefficient for short circuit
75
- current, in percent.
76
-
77
- Raises
78
- ------
79
- ImportError
80
- if NREL-PySAM is not installed.
81
- RuntimeError
82
- if parameter extraction is not successful.
83
-
84
- Notes
85
- -----
86
- The CEC model and estimation method are described in [1]_.
87
- Inputs ``v_mp``, ``i_mp``, ``v_oc`` and ``i_sc`` are assumed to be from a
88
- single IV curve at constant irradiance and cell temperature. Irradiance is
89
- not explicitly used by the fitting procedure. The irradiance level at which
90
- the input IV curve is determined and the specified cell temperature
91
- ``temp_ref`` are the reference conditions for the output parameters
92
- ``I_L_ref``, ``I_o_ref``, ``R_s``, ``R_sh_ref``, ``a_ref`` and ``Adjust``.
93
-
94
- References
95
- ----------
96
- .. [1] A. Dobos, "An Improved Coefficient Calculator for the California
97
- Energy Commission 6 Parameter Photovoltaic Module Model", Journal of
98
- Solar Energy Engineering, vol 134, 2012. :doi:`10.1115/1.4005759`
99
- """
100
-
101
- try:
102
- from PySAM import PySSC
103
- except ImportError:
104
- raise ImportError("Requires NREL's PySAM package at "
105
- "https://pypi.org/project/NREL-PySAM/.")
106
-
107
- datadict = {'tech_model': '6parsolve', 'financial_model': None,
108
- 'celltype': celltype, 'Vmp': v_mp,
109
- 'Imp': i_mp, 'Voc': v_oc, 'Isc': i_sc, 'alpha_isc': alpha_sc,
110
- 'beta_voc': beta_voc, 'gamma_pmp': gamma_pmp,
111
- 'Nser': cells_in_series, 'Tref': temp_ref}
112
-
113
- result = PySSC.ssc_sim_from_dict(datadict)
114
- if result['cmod_success'] == 1:
115
- return tuple([result[k] for k in ['Il', 'Io', 'Rs', 'Rsh', 'a',
116
- 'Adj']])
117
- else:
118
- raise RuntimeError('Parameter estimation failed')
119
-
120
-
121
- def fit_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series,
122
- EgRef=1.121, dEgdT=-0.0002677, temp_ref=25, irrad_ref=1000,
123
- init_guess={}, root_kwargs={}):
124
- """
125
- Calculates the parameters for the De Soto single diode model.
126
-
127
- This procedure (described in [1]_) fits the De Soto model [2]_ using
128
- common specifications given by manufacturers in the
129
- datasheets of PV modules.
130
-
131
- The solution is found using :py:func:`scipy.optimize.root`,
132
- with the default solver method 'hybr'.
133
- No restriction is put on the fit variables, e.g. series
134
- or shunt resistance could go negative. Nevertheless, if it happens,
135
- check carefully the inputs and their units. For example, ``alpha_sc`` and
136
- ``beta_voc`` are often given in %/K in manufacturers datasheets but should
137
- be given in A/K and V/K here.
138
-
139
- The parameters returned by this function can be used by
140
- :py:func:`pvlib.pvsystem.calcparams_desoto` to calculate single diode
141
- equation parameters at different irradiance and cell temperature.
142
-
143
- Parameters
144
- ----------
145
- v_mp: float
146
- Module voltage at the maximum-power point at reference conditions. [V]
147
- i_mp: float
148
- Module current at the maximum-power point at reference conditions. [A]
149
- v_oc: float
150
- Open-circuit voltage at reference conditions. [V]
151
- i_sc: float
152
- Short-circuit current at reference conditions. [A]
153
- alpha_sc: float
154
- The short-circuit current (``i_sc``) temperature coefficient of the
155
- module. [A/K]
156
- beta_voc: float
157
- The open-circuit voltage (``v_oc``) temperature coefficient of the
158
- module. [V/K]
159
- cells_in_series: integer
160
- Number of cell in the module.
161
- EgRef: float, default 1.121 eV - value for silicon
162
- Energy of bandgap of semi-conductor used. [eV]
163
- dEgdT: float, default -0.0002677 - value for silicon
164
- Variation of bandgap according to temperature. [1/K]
165
- temp_ref: float, default 25
166
- Reference temperature condition. [C]
167
- irrad_ref: float, default 1000
168
- Reference irradiance condition. [Wm⁻²]
169
- init_guess: dict, optional
170
- Initial values for optimization. Keys can be `'Rsh_0'`, `'a_0'`,
171
- `'IL_0'`, `'Io_0'`, `'Rs_0'`.
172
- root_kwargs : dictionary, optional
173
- Dictionary of arguments to pass onto scipy.optimize.root()
174
-
175
- Returns
176
- -------
177
- dict with the following elements:
178
- I_L_ref: float
179
- Light-generated current at reference conditions. [A]
180
- I_o_ref: float
181
- Diode saturation current at reference conditions. [A]
182
- R_s: float
183
- Series resistance. [ohm]
184
- R_sh_ref: float
185
- Shunt resistance at reference conditions. [ohm].
186
- a_ref: float
187
- Modified ideality factor at reference conditions.
188
- The product of the usual diode ideality factor (n, unitless),
189
- number of cells in series (Ns), and cell thermal voltage at
190
- specified effective irradiance and cell temperature.
191
- alpha_sc: float
192
- The short-circuit current (i_sc) temperature coefficient of the
193
- module. [A/K]
194
- EgRef: float
195
- Energy of bandgap of semi-conductor used. [eV]
196
- dEgdT: float
197
- Variation of bandgap according to temperature. [1/K]
198
- irrad_ref: float
199
- Reference irradiance condition. [Wm⁻²]
200
- temp_ref: float
201
- Reference temperature condition. [C]
202
-
203
- scipy.optimize.OptimizeResult
204
- Optimization result of scipy.optimize.root().
205
- See scipy.optimize.OptimizeResult for more details.
206
-
207
- References
208
- ----------
209
- .. [1] J. A Duffie, W. A Beckman, "Solar Engineering of Thermal Processes",
210
- 4th ed., Wiley, 2013. :doi:`10.1002/9781118671603`
211
- .. [2] W. De Soto et al., "Improvement and validation of a model for
212
- photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
213
- 2006. :doi:`10.1016/j.solener.2005.06.010`
214
-
215
- """
216
-
217
- # Constants
218
- k = constants.value('Boltzmann constant in eV/K') # in eV/K
219
- Tref = temp_ref + 273.15 # [K]
220
-
221
- # initial guesses of variables for computing convergence:
222
- # Default values are taken from [1], p753
223
- init_guess_keys = ['IL_0', 'Io_0', 'Rs_0', 'Rsh_0', 'a_0'] # order matters
224
- init = {key: None for key in init_guess_keys}
225
- init['IL_0'] = i_sc
226
- init['a_0'] = 1.5*k*Tref*cells_in_series
227
- init['Io_0'] = i_sc * np.exp(-v_oc/init['a_0'])
228
- init['Rs_0'] = (init['a_0']*np.log1p((init['IL_0'] - i_mp)/init['Io_0'])
229
- - v_mp) / i_mp
230
- init['Rsh_0'] = 100.0
231
- # overwrite if optional init_guess is provided
232
- for key in init_guess:
233
- if key in init_guess_keys:
234
- init[key] = init_guess[key]
235
- else:
236
- raise ValueError(f"'{key}' is not a valid name;"
237
- f" allowed values are {init_guess_keys}")
238
- # params_i : initial values vector
239
- params_i = np.array([init[k] for k in init_guess_keys])
240
-
241
- # specs of module
242
- specs = (i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc, EgRef, dEgdT,
243
- Tref, k)
244
-
245
- # computing with system of equations described in [1]
246
- optimize_result = optimize.root(_system_of_equations_desoto, x0=params_i,
247
- args=(specs,), **root_kwargs)
248
-
249
- if optimize_result.success:
250
- sdm_params = optimize_result.x
251
- else:
252
- raise RuntimeError(
253
- 'Parameter estimation failed:\n' + optimize_result.message)
254
-
255
- # results
256
- return ({'I_L_ref': sdm_params[0],
257
- 'I_o_ref': sdm_params[1],
258
- 'R_s': sdm_params[2],
259
- 'R_sh_ref': sdm_params[3],
260
- 'a_ref': sdm_params[4],
261
- 'alpha_sc': alpha_sc,
262
- 'EgRef': EgRef,
263
- 'dEgdT': dEgdT,
264
- 'irrad_ref': irrad_ref,
265
- 'temp_ref': temp_ref},
266
- optimize_result)
267
-
268
-
269
- def _system_of_equations_desoto(params, specs):
270
- """Evaluates the systems of equations used to solve for the single
271
- diode equation parameters. Function designed to be used by
272
- scipy.optimize.root in fit_desoto.
273
-
274
- Parameters
275
- ----------
276
- params: ndarray
277
- Array with parameters of the De Soto single diode model. Must be
278
- given in the following order: IL, Io, a, Rs, Rsh
279
- specs: tuple
280
- Specifications of pv module given by manufacturer. Must be given
281
- in the following order: Isc, Voc, Imp, Vmp, beta_oc, alpha_sc
282
-
283
- Returns
284
- -------
285
- value of the system of equations to solve with scipy.optimize.root().
286
- """
287
-
288
- # six input known variables
289
- Isc, Voc, Imp, Vmp, beta_oc, alpha_sc, EgRef, dEgdT, Tref, k = specs
290
-
291
- # five parameters vector to find
292
- IL, Io, Rs, Rsh, a = params
293
-
294
- # five equation vector
295
- y = [0, 0, 0, 0, 0]
296
-
297
- # 1st equation - short-circuit - eq(3) in [1]
298
- y[0] = Isc - IL + Io * np.expm1(Isc * Rs / a) + Isc * Rs / Rsh
299
-
300
- # 2nd equation - open-circuit Tref - eq(4) in [1]
301
- y[1] = -IL + Io * np.expm1(Voc / a) + Voc / Rsh
302
-
303
- # 3rd equation - Imp & Vmp - eq(5) in [1]
304
- y[2] = Imp - IL + Io * np.expm1((Vmp + Imp * Rs) / a) \
305
- + (Vmp + Imp * Rs) / Rsh
306
-
307
- # 4th equation - Pmp derivated=0 - eq23.2.6 in [2]
308
- # caution: eq(6) in [1] has a sign error
309
- y[3] = Imp \
310
- - Vmp * ((Io / a) * np.exp((Vmp + Imp * Rs) / a) + 1.0 / Rsh) \
311
- / (1.0 + (Io * Rs / a) * np.exp((Vmp + Imp * Rs) / a) + Rs / Rsh)
312
-
313
- # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1]
314
- T2 = Tref + 2
315
- Voc2 = (T2 - Tref) * beta_oc + Voc # eq (7) in [1]
316
- a2 = a * T2 / Tref # eq (8) in [1]
317
- IL2 = IL + alpha_sc * (T2 - Tref) # eq (11) in [1]
318
- Eg2 = EgRef * (1 + dEgdT * (T2 - Tref)) # eq (10) in [1]
319
- Io2 = Io * (T2 / Tref)**3 * np.exp(1 / k * (EgRef/Tref - Eg2/T2)) # eq (9)
320
- y[4] = -IL2 + Io2 * np.expm1(Voc2 / a2) + Voc2 / Rsh # eq (4) at T2
321
-
322
- return y
323
-
324
-
325
- def fit_pvsyst_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
326
- """
327
- Estimate parameters for the PVsyst module performance model.
328
-
329
- Parameters
330
- ----------
331
- ivcurves : dict
332
- i : array
333
- One array element for each IV curve. The jth element is itself an
334
- array of current for jth IV curve (same length as v[j]) [A]
335
- v : array
336
- One array element for each IV curve. The jth element is itself an
337
- array of voltage for jth IV curve (same length as i[j]) [V]
338
- ee : array
339
- effective irradiance for each IV curve, i.e., POA broadband
340
- irradiance adjusted by solar spectrum modifier [W / m^2]
341
- tc : array
342
- cell temperature for each IV curve [C]
343
- i_sc : array
344
- short circuit current for each IV curve [A]
345
- v_oc : array
346
- open circuit voltage for each IV curve [V]
347
- i_mp : array
348
- current at max power point for each IV curve [A]
349
- v_mp : array
350
- voltage at max power point for each IV curve [V]
351
-
352
- specs : dict
353
- cells_in_series : int
354
- number of cells in series
355
- alpha_sc : float
356
- temperature coefficient of isc [A/C]
357
-
358
- const : dict
359
- E0 : float
360
- effective irradiance at STC, default 1000 [W/m^2]
361
- T0 : float
362
- cell temperature at STC, default 25 [C]
363
- k : float
364
- Boltzmann's constant [J/K]
365
- q : float
366
- elementary charge [Coulomb]
367
-
368
- maxiter : int, default 5
369
- input that sets the maximum number of iterations for the parameter
370
- updating part of the algorithm.
371
-
372
- eps1: float, default 1e-3
373
- Tolerance for the IV curve fitting. The parameter updating stops when
374
- absolute values of the percent change in mean, max and standard
375
- deviation of Imp, Vmp and Pmp between iterations are all less than
376
- eps1, or when the number of iterations exceeds maxiter.
377
-
378
- Returns
379
- -------
380
- dict
381
- I_L_ref : float
382
- light current at STC [A]
383
- I_o_ref : float
384
- dark current at STC [A]
385
- EgRef : float
386
- effective band gap at STC [eV]
387
- R_s : float
388
- series resistance at STC [ohm]
389
- R_sh_ref : float
390
- shunt resistance at STC [ohm]
391
- R_sh_0 : float
392
- shunt resistance at zero irradiance [ohm]
393
- R_sh_exp : float
394
- exponential factor defining decrease in shunt resistance with
395
- increasing effective irradiance
396
- gamma_ref : float
397
- diode (ideality) factor at STC [unitless]
398
- mu_gamma : float
399
- temperature coefficient for diode (ideality) factor [1/K]
400
- cells_in_series : int
401
- number of cells in series
402
- iph : array
403
- light current for each IV curve [A]
404
- io : array
405
- dark current for each IV curve [A]
406
- rs : array
407
- series resistance for each IV curve [ohm]
408
- rsh : array
409
- shunt resistance for each IV curve [ohm]
410
- u : array
411
- boolean for each IV curve indicating that the parameter values
412
- are deemed reasonable by the private function ``_filter_params``
413
-
414
- Notes
415
- -----
416
- The PVsyst module performance model is described in [1]_, [2]_, and [3]_.
417
- The fitting method is documented in [4]_, [5]_, and [6]_.
418
- Ported from PVLib Matlab [7]_.
419
-
420
- References
421
- ----------
422
- .. [1] K. Sauer, T. Roessler, C. W. Hansen, Modeling the Irradiance and
423
- Temperature Dependence of Photovoltaic Modules in PVsyst, IEEE Journal
424
- of Photovoltaics v5(1), January 2015.
425
- :doi:`10.1109/JPHOTOV.2014.2364133`
426
- .. [2] A. Mermoud, PV Modules modeling, Presentation at the 2nd PV
427
- Performance Modeling Workshop, Santa Clara, CA, May 2013
428
- .. [3] A. Mermoud, T. Lejeuene, Performance Assessment of a Simulation
429
- Model for PV modules of any available technology, 25th European
430
- Photovoltaic Solar Energy Conference, Valencia, Spain, Sept. 2010
431
- .. [4] C. Hansen, Estimating Parameters for the PVsyst Version 6
432
- Photovoltaic Module Performance Model, Sandia National Laboratories
433
- Report SAND2015-8598. :doi:`10.2172/1223058`
434
- .. [5] C. Hansen, Parameter Estimation for Single Diode Models of
435
- Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065.
436
- :doi:`10.2172/1177157`
437
- .. [6] C. Hansen, Estimation of Parameters for Single Diode Models using
438
- Measured IV Curves, Proc. of the 39th IEEE PVSC, June 2013.
439
- :doi:`10.1109/PVSC.2013.6744135`
440
- .. [7] PVLib MATLAB https://github.com/sandialabs/MATLAB_PV_LIB
441
- """
442
-
443
- if const is None:
444
- const = CONSTANTS
445
-
446
- ee = ivcurves['ee']
447
- tc = ivcurves['tc']
448
- tck = tc + 273.15
449
- isc = ivcurves['i_sc']
450
- voc = ivcurves['v_oc']
451
- imp = ivcurves['i_mp']
452
- vmp = ivcurves['v_mp']
453
-
454
- # Cell Thermal Voltage
455
- vth = const['k'] / const['q'] * tck
456
-
457
- n = len(ivcurves['v_oc'])
458
-
459
- # Initial estimate of Rsh used to obtain the diode factor gamma0 and diode
460
- # temperature coefficient mu_gamma. Rsh is estimated using the co-content
461
- # integral method.
462
-
463
- rsh = np.ones(n)
464
- for j in range(n):
465
- voltage, current = rectify_iv_curve(ivcurves['v'][j], ivcurves['i'][j])
466
- # initial estimate of Rsh, from integral over voltage regression
467
- # [5] Step 3a; [6] Step 3a
468
- _, _, _, rsh[j], _ = _fit_sandia_cocontent(
469
- voltage, current, vth[j] * specs['cells_in_series'])
470
-
471
- gamma_ref, mu_gamma = _fit_pvsyst_sandia_gamma(voc, isc, rsh, vth, tck,
472
- specs, const)
473
-
474
- badgamma = np.isnan(gamma_ref) or np.isnan(mu_gamma) \
475
- or not np.isreal(gamma_ref) or not np.isreal(mu_gamma)
476
-
477
- if badgamma:
478
- raise RuntimeError(
479
- "Failed to estimate the diode (ideality) factor parameter;"
480
- " aborting parameter estimation.")
481
-
482
- gamma = gamma_ref + mu_gamma * (tc - const['T0'])
483
- nnsvth = gamma * (vth * specs['cells_in_series'])
484
-
485
- # For each IV curve, sequentially determine initial values for Io, Rs,
486
- # and Iph [5] Step 3a; [6] Step 3
487
- iph, io, rs, u = _initial_iv_params(ivcurves, ee, voc, isc, rsh,
488
- nnsvth)
489
-
490
- # Update values for each IV curve to converge at vmp, imp, voc and isc
491
- iph, io, rs, rsh, u = _update_iv_params(voc, isc, vmp, imp, ee,
492
- iph, io, rs, rsh, nnsvth, u,
493
- maxiter, eps1)
494
-
495
- # get single diode models from converged values for each IV curve
496
- pvsyst = _extract_sdm_params(ee, tc, iph, io, rs, rsh, gamma, u,
497
- specs, const, model='pvsyst')
498
- # Add parameters estimated in this function
499
- pvsyst['gamma_ref'] = gamma_ref
500
- pvsyst['mu_gamma'] = mu_gamma
501
- pvsyst['cells_in_series'] = specs['cells_in_series']
502
-
503
- return pvsyst
504
-
505
-
506
- def fit_desoto_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
507
- """
508
- Estimate parameters for the De Soto module performance model.
509
-
510
- Parameters
511
- ----------
512
- ivcurves : dict
513
- i : array
514
- One array element for each IV curve. The jth element is itself an
515
- array of current for jth IV curve (same length as v[j]) [A]
516
- v : array
517
- One array element for each IV curve. The jth element is itself an
518
- array of voltage for jth IV curve (same length as i[j]) [V]
519
- ee : array
520
- effective irradiance for each IV curve, i.e., POA broadband
521
- irradiance adjusted by solar spectrum modifier [W / m^2]
522
- tc : array
523
- cell temperature for each IV curve [C]
524
- i_sc : array
525
- short circuit current for each IV curve [A]
526
- v_oc : array
527
- open circuit voltage for each IV curve [V]
528
- i_mp : array
529
- current at max power point for each IV curve [A]
530
- v_mp : array
531
- voltage at max power point for each IV curve [V]
532
-
533
- specs : dict
534
- cells_in_series : int
535
- number of cells in series
536
- alpha_sc : float
537
- temperature coefficient of Isc [A/C]
538
- beta_voc : float
539
- temperature coefficient of Voc [V/C]
540
-
541
- const : dict
542
- E0 : float
543
- effective irradiance at STC, default 1000 [W/m^2]
544
- T0 : float
545
- cell temperature at STC, default 25 [C]
546
- k : float
547
- Boltzmann's constant [J/K]
548
- q : float
549
- elementary charge [Coulomb]
550
-
551
- maxiter : int, default 5
552
- input that sets the maximum number of iterations for the parameter
553
- updating part of the algorithm.
554
-
555
- eps1: float, default 1e-3
556
- Tolerance for the IV curve fitting. The parameter updating stops when
557
- absolute values of the percent change in mean, max and standard
558
- deviation of Imp, Vmp and Pmp between iterations are all less than
559
- eps1, or when the number of iterations exceeds maxiter.
560
-
561
- Returns
562
- -------
563
- dict
564
- I_L_ref : float
565
- Light current at STC [A]
566
- I_o_ref : float
567
- Dark current at STC [A]
568
- EgRef : float
569
- Effective band gap at STC [eV]
570
- R_s : float
571
- Series resistance at STC [ohm]
572
- R_sh_ref : float
573
- Shunt resistance at STC [ohm]
574
- cells_in_series : int
575
- Number of cells in series
576
- iph : array
577
- Light current for each IV curve [A]
578
- io : array
579
- Dark current for each IV curve [A]
580
- rs : array
581
- Series resistance for each IV curve [ohm]
582
- rsh : array
583
- Shunt resistance for each IV curve [ohm]
584
- a_ref : float
585
- The product of the usual diode ideality factor (n, unitless),
586
- number of cells in series (Ns), and cell thermal voltage at
587
- reference conditions, in units of V.
588
- dEgdT : float
589
- The temperature dependence of the energy bandgap (Eg) at reference
590
- conditions [1/K].
591
- u : array
592
- Boolean for each IV curve indicating that the parameter values
593
- are deemed reasonable by the private function ``_filter_params``
594
-
595
- Notes
596
- -----
597
- The De Soto module performance model is described in [1]_. The fitting
598
- method is documented in [2]_, [3]_. Ported from PVLib Matlab [4]_.
599
-
600
- References
601
- ----------
602
- .. [1] W. De Soto et al., "Improvement and validation of a model for
603
- photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
604
- 2006. :doi:`10.1016/j.solener.2005.06.010`
605
- .. [2] C. Hansen, Parameter Estimation for Single Diode Models of
606
- Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065.
607
- :doi:`10.2172/1177157`
608
- .. [3] C. Hansen, Estimation of Parameters for Single Diode Models using
609
- Measured IV Curves, Proc. of the 39th IEEE PVSC, June 2013.
610
- :doi:`10.1109/PVSC.2013.6744135`
611
- .. [4] PVLib MATLAB https://github.com/sandialabs/MATLAB_PV_LIB
612
- """
613
-
614
- if const is None:
615
- const = CONSTANTS
616
-
617
- ee = ivcurves['ee']
618
- tc = ivcurves['tc']
619
- tck = tc + 273.15
620
- isc = ivcurves['i_sc']
621
- voc = ivcurves['v_oc']
622
- imp = ivcurves['i_mp']
623
- vmp = ivcurves['v_mp']
624
-
625
- # Cell Thermal Voltage
626
- vth = const['k'] / const['q'] * tck
627
-
628
- n = len(voc)
629
-
630
- # Initial estimate of Rsh used to obtain the diode factor gamma0 and diode
631
- # temperature coefficient mu_gamma. Rsh is estimated using the co-content
632
- # integral method.
633
-
634
- rsh = np.ones(n)
635
- for j in range(n):
636
- voltage, current = rectify_iv_curve(ivcurves['v'][j], ivcurves['i'][j])
637
- # initial estimate of Rsh, from integral over voltage regression
638
- # [5] Step 3a; [6] Step 3a
639
- _, _, _, rsh[j], _ = _fit_sandia_cocontent(
640
- voltage, current, vth[j] * specs['cells_in_series'])
641
-
642
- n0 = _fit_desoto_sandia_diode(ee, voc, vth, tc, specs, const)
643
-
644
- bad_n = np.isnan(n0) or not np.isreal(n0)
645
-
646
- if bad_n:
647
- raise RuntimeError(
648
- "Failed to estimate the diode (ideality) factor parameter;"
649
- " aborting parameter estimation.")
650
-
651
- nnsvth = n0 * specs['cells_in_series'] * vth
652
-
653
- # For each IV curve, sequentially determine initial values for Io, Rs,
654
- # and Iph [5] Step 3a; [6] Step 3
655
- iph, io, rs, u = _initial_iv_params(ivcurves, ee, voc, isc, rsh,
656
- nnsvth)
657
-
658
- # Update values for each IV curve to converge at vmp, imp, voc and isc
659
- iph, io, rs, rsh, u = _update_iv_params(voc, isc, vmp, imp, ee,
660
- iph, io, rs, rsh, nnsvth, u,
661
- maxiter, eps1)
662
-
663
- # get single diode models from converged values for each IV curve
664
- desoto = _extract_sdm_params(ee, tc, iph, io, rs, rsh, n0, u,
665
- specs, const, model='desoto')
666
- # Add parameters estimated in this function
667
- desoto['a_ref'] = n0 * specs['cells_in_series'] * const['k'] / \
668
- const['q'] * (const['T0'] + 273.15)
669
- desoto['cells_in_series'] = specs['cells_in_series']
670
-
671
- return desoto
672
-
673
-
674
- def _fit_pvsyst_sandia_gamma(voc, isc, rsh, vth, tck, specs, const):
675
- # Estimate the diode factor gamma from Isc-Voc data. Method incorporates
676
- # temperature dependence by means of the equation for Io
677
-
678
- y = np.log(isc - voc / rsh) - 3. * np.log(tck / (const['T0'] + 273.15))
679
- x1 = const['q'] / const['k'] * (1. / (const['T0'] + 273.15) - 1. / tck)
680
- x2 = voc / (vth * specs['cells_in_series'])
681
- uu = np.logical_or(np.isnan(y), np.isnan(x1), np.isnan(x2))
682
-
683
- x = np.vstack((np.ones(len(x1[~uu])), x1[~uu], -x1[~uu] *
684
- (tck[~uu] - (const['T0'] + 273.15)), x2[~uu],
685
- -x2[~uu] * (tck[~uu] - (const['T0'] + 273.15)))).T
686
- alpha = np.linalg.lstsq(x, y[~uu], rcond=None)[0]
687
-
688
- gamma_ref = 1. / alpha[3]
689
- mu_gamma = alpha[4] / alpha[3] ** 2
690
- return gamma_ref, mu_gamma
691
-
692
-
693
- def _fit_desoto_sandia_diode(ee, voc, vth, tc, specs, const):
694
- # estimates the diode factor for the De Soto model.
695
- # Helper function for fit_desoto_sandia
696
- try:
697
- import statsmodels.api as sm
698
- except ImportError:
699
- raise ImportError(
700
- 'Parameter extraction using Sandia method requires statsmodels')
701
-
702
- x = specs['cells_in_series'] * vth * np.log(ee / const['E0'])
703
- y = voc - specs['beta_voc'] * (tc - const['T0'])
704
- new_x = sm.add_constant(x)
705
- res = sm.RLM(y, new_x).fit()
706
- return np.array(res.params)[1]
707
-
708
-
709
- def _initial_iv_params(ivcurves, ee, voc, isc, rsh, nnsvth):
710
- # sets initial values for iph, io, rs and quality filter u.
711
- # Helper function for fit_<model>_sandia.
712
- n = len(ivcurves['v_oc'])
713
- io = np.ones(n)
714
- iph = np.ones(n)
715
- rs = np.ones(n)
716
-
717
- for j in range(n):
718
-
719
- if rsh[j] > 0:
720
- volt, curr = rectify_iv_curve(ivcurves['v'][j],
721
- ivcurves['i'][j])
722
- # Initial estimate of Io, evaluate the single diode model at
723
- # voc and approximate Iph + Io = Isc [5] Step 3a; [6] Step 3b
724
- io[j] = (isc[j] - voc[j] / rsh[j]) * np.exp(-voc[j] /
725
- nnsvth[j])
726
-
727
- # initial estimate of rs from dI/dV near Voc
728
- # [5] Step 3a; [6] Step 3c
729
- [didv, d2id2v] = _numdiff(volt, curr)
730
- t3 = volt > .5 * voc[j]
731
- t4 = volt < .9 * voc[j]
732
- tmp = -rsh[j] * didv - 1.
733
- with np.errstate(invalid="ignore"): # expect nan in didv
734
- v = np.logical_and.reduce(np.array([t3, t4, ~np.isnan(tmp),
735
- np.greater(tmp, 0)]))
736
- if np.any(v):
737
- vtrs = (nnsvth[j] / isc[j] * (
738
- np.log(tmp[v] * nnsvth[j] / (rsh[j] * io[j]))
739
- - volt[v] / nnsvth[j]))
740
- rs[j] = np.mean(vtrs[vtrs > 0], axis=0)
741
- else:
742
- rs[j] = 0.
743
-
744
- # Initial estimate of Iph, evaluate the single diode model at
745
- # Isc [5] Step 3a; [6] Step 3d
746
- iph[j] = isc[j] + io[j] * np.expm1(isc[j] / nnsvth[j]) \
747
- + isc[j] * rs[j] / rsh[j]
748
-
749
- else:
750
- io[j] = np.nan
751
- rs[j] = np.nan
752
- iph[j] = np.nan
753
-
754
- # Filter IV curves for good initial values
755
- # [5] Step 3b
756
- u = _filter_params(ee, isc, io, rs, rsh)
757
-
758
- # [5] Step 3c
759
- # Refine Io to match Voc
760
- io[u] = _update_io(voc[u], iph[u], io[u], rs[u], rsh[u], nnsvth[u])
761
-
762
- # parameters [6], Step 3c
763
- # Calculate Iph to be consistent with Isc and current values of other
764
- iph = isc + io * np.expm1(rs * isc / nnsvth) + isc * rs / rsh
765
-
766
- return iph, io, rs, u
767
-
768
-
769
- def _update_iv_params(voc, isc, vmp, imp, ee, iph, io, rs, rsh, nnsvth, u,
770
- maxiter, eps1):
771
- # Refine Rsh, Rs, Io and Iph in that order.
772
- # Helper function for fit_<model>_sandia.
773
- counter = 1. # counter variable for parameter updating while loop,
774
- # counts iterations
775
- prevconvergeparams = {}
776
- prevconvergeparams['state'] = 0.0
777
-
778
- not_converged = np.array([True])
779
-
780
- while not_converged.any() and counter <= maxiter:
781
- # update rsh to match max power point using a fixed point method.
782
- rsh[u] = _update_rsh_fixed_pt(vmp[u], imp[u], iph[u], io[u], rs[u],
783
- rsh[u], nnsvth[u])
784
-
785
- # Calculate Rs to be consistent with Rsh and maximum power point
786
- _, phi = _calc_theta_phi_exact(vmp[u], imp[u], iph[u], io[u],
787
- rs[u], rsh[u], nnsvth[u])
788
- rs[u] = (iph[u] + io[u] - imp[u]) * rsh[u] / imp[u] - \
789
- nnsvth[u] * phi / imp[u] - vmp[u] / imp[u]
790
-
791
- # Update filter for good parameters
792
- u = _filter_params(ee, isc, io, rs, rsh)
793
-
794
- # Update value for io to match voc
795
- io[u] = _update_io(voc[u], iph[u], io[u], rs[u], rsh[u], nnsvth[u])
796
-
797
- # Calculate Iph to be consistent with Isc and other parameters
798
- iph = isc + io * np.expm1(rs * isc / nnsvth) + isc * rs / rsh
799
-
800
- # update filter for good parameters
801
- u = _filter_params(ee, isc, io, rs, rsh)
802
-
803
- # compute the IV curve from the current parameter values
804
- result = singlediode(iph[u], io[u], rs[u], rsh[u], nnsvth[u])
805
-
806
- # check convergence criteria
807
- # [5] Step 3d
808
- convergeparams = _check_converge(
809
- prevconvergeparams, result, vmp[u], imp[u], counter)
810
-
811
- prevconvergeparams = convergeparams
812
- counter += 1.
813
- t5 = prevconvergeparams['vmperrmeanchange'] >= eps1
814
- t6 = prevconvergeparams['imperrmeanchange'] >= eps1
815
- t7 = prevconvergeparams['pmperrmeanchange'] >= eps1
816
- t8 = prevconvergeparams['vmperrstdchange'] >= eps1
817
- t9 = prevconvergeparams['imperrstdchange'] >= eps1
818
- t10 = prevconvergeparams['pmperrstdchange'] >= eps1
819
- t11 = prevconvergeparams['vmperrabsmaxchange'] >= eps1
820
- t12 = prevconvergeparams['imperrabsmaxchange'] >= eps1
821
- t13 = prevconvergeparams['pmperrabsmaxchange'] >= eps1
822
- not_converged = np.logical_or.reduce(np.array([t5, t6, t7, t8, t9,
823
- t10, t11, t12, t13]))
824
-
825
- return iph, io, rs, rsh, u
826
-
827
-
828
- def _extract_sdm_params(ee, tc, iph, io, rs, rsh, n, u, specs, const,
829
- model):
830
- # Get single diode model parameters from five parameters iph, io, rs, rsh
831
- # and n vs. effective irradiance and temperature
832
- try:
833
- import statsmodels.api as sm
834
- except ImportError:
835
- raise ImportError(
836
- 'Parameter extraction using Sandia method requires statsmodels')
837
-
838
- tck = tc + 273.15
839
- tok = const['T0'] + 273.15 # convert to to K
840
-
841
- params = {}
842
-
843
- if model == 'pvsyst':
844
- # Estimate I_o_ref and EgRef
845
- x_for_io = const['q'] / const['k'] * (1. / tok - 1. / tck[u]) / n[u]
846
-
847
- # Estimate R_sh_0, R_sh_ref and R_sh_exp
848
- # Initial guesses. R_sh_0 is value at ee=0.
849
- nans = np.isnan(rsh)
850
- if any(ee < 400):
851
- grsh0 = np.mean(rsh[np.logical_and(~nans, ee < 400)])
852
- else:
853
- grsh0 = np.max(rsh)
854
- # Rsh_ref is value at Ee = 1000
855
- if any(ee > 400):
856
- grshref = np.mean(rsh[np.logical_and(~nans, ee > 400)])
857
- else:
858
- grshref = np.min(rsh)
859
- # PVsyst default for Rshexp is 5.5
860
- R_sh_exp = 5.5
861
-
862
- # Find parameters for Rsh equation
863
-
864
- def fun_rsh(x, rshexp, ee, e0, rsh):
865
- tf = np.log10(_rsh_pvsyst(x, R_sh_exp, ee, e0)) - np.log10(rsh)
866
- return tf
867
-
868
- x0 = np.array([grsh0, grshref])
869
- beta = optimize.least_squares(
870
- fun_rsh, x0, args=(R_sh_exp, ee[u], const['E0'], rsh[u]),
871
- bounds=np.array([[1., 1.], [1.e7, 1.e6]]), verbose=2)
872
- # Extract PVsyst parameter values
873
- R_sh_0 = beta.x[0]
874
- R_sh_ref = beta.x[1]
875
-
876
- # parameters unique to PVsyst
877
- params['R_sh_0'] = R_sh_0
878
- params['R_sh_exp'] = R_sh_exp
879
-
880
- elif model == 'desoto':
881
- dEgdT = -0.0002677
882
- x_for_io = const['q'] / const['k'] * (
883
- 1. / tok - 1. / tck[u] + dEgdT * (tc[u] - const['T0']) / tck[u])
884
-
885
- # Estimate R_sh_ref
886
- nans = np.isnan(rsh)
887
- x = const['E0'] / ee[np.logical_and(u, ee > 400, ~nans)]
888
- y = rsh[np.logical_and(u, ee > 400, ~nans)]
889
- new_x = sm.add_constant(x)
890
- beta = sm.RLM(y, new_x).fit()
891
- R_sh_ref = beta.params[1]
892
-
893
- params['dEgdT'] = dEgdT
894
-
895
- # Estimate I_o_ref and EgRef
896
- y = np.log(io[u]) - 3. * np.log(tck[u] / tok)
897
- new_x = sm.add_constant(x_for_io)
898
- res = sm.RLM(y, new_x).fit()
899
- beta = res.params
900
- I_o_ref = np.exp(beta[0])
901
- EgRef = beta[1]
902
-
903
- # Estimate I_L_ref
904
- x = tc[u] - const['T0']
905
- y = iph[u] * (const['E0'] / ee[u])
906
- # average over non-NaN values of Y and X
907
- nans = np.isnan(y - specs['alpha_sc'] * x)
908
- I_L_ref = np.mean(y[~nans] - specs['alpha_sc'] * x[~nans])
909
-
910
- # Estimate R_s
911
- nans = np.isnan(rs)
912
- R_s = np.mean(rs[np.logical_and(u, ee > 400, ~nans)])
913
-
914
- params['I_L_ref'] = I_L_ref
915
- params['I_o_ref'] = I_o_ref
916
- params['EgRef'] = EgRef
917
- params['R_sh_ref'] = R_sh_ref
918
- params['R_s'] = R_s
919
- # save values for each IV curve
920
- params['iph'] = iph
921
- params['io'] = io
922
- params['rsh'] = rsh
923
- params['rs'] = rs
924
- params['u'] = u
925
-
926
- return params
927
-
928
-
929
- def _update_io(voc, iph, io, rs, rsh, nnsvth):
930
- """
931
- Adjusts Io to match Voc using other parameter values.
932
-
933
- Helper function for fit_pvsyst_sandia, fit_desoto_sandia
934
-
935
- Description
936
- -----------
937
- Io is updated iteratively 10 times or until successive
938
- values are less than 0.000001 % different. The updating is similar to
939
- Newton's method.
940
-
941
- Parameters
942
- ----------
943
- voc: a numpy array of length N of values for Voc (V)
944
- iph: a numpy array of length N of values for lighbt current IL (A)
945
- io: a numpy array of length N of initial values for Io (A)
946
- rs: a numpy array of length N of values for the series resistance (ohm)
947
- rsh: a numpy array of length N of values for the shunt resistance (ohm)
948
- nnsvth: a numpy array of length N of values for the diode factor x thermal
949
- voltage for the module, equal to Ns (number of cells in series) x
950
- Vth (thermal voltage per cell).
951
-
952
- Returns
953
- -------
954
- new_io - a numpy array of length N of updated values for io
955
-
956
- References
957
- ----------
958
- .. [1] PVLib MATLAB https://github.com/sandialabs/MATLAB_PV_LIB
959
- .. [2] C. Hansen, Parameter Estimation for Single Diode Models of
960
- Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065
961
- .. [3] C. Hansen, Estimation of Parameteres for Single Diode Models using
962
- Measured IV Curves, Proc. of the 39th IEEE PVSC, June 2013.
963
- """
964
-
965
- eps = 1e-6
966
- niter = 10
967
- k = 1
968
- maxerr = 1
969
-
970
- tio = io # Current Estimate of Io
971
-
972
- while maxerr > eps and k < niter:
973
- # Predict Voc
974
- pvoc = v_from_i(0., iph, tio, rs, rsh, nnsvth)
975
-
976
- # Difference in Voc
977
- dvoc = pvoc - voc
978
-
979
- # Update Io
980
- with np.errstate(invalid="ignore", divide="ignore"):
981
- new_io = tio * (1. + (2. * dvoc) / (2. * nnsvth - dvoc))
982
- # Calculate Maximum Percent Difference
983
- maxerr = np.max(np.abs(new_io - tio) / tio) * 100.
984
-
985
- tio = new_io
986
- k += 1.
987
-
988
- return new_io
989
-
990
-
991
- def _rsh_pvsyst(x, rshexp, g, go):
992
- # computes rsh for PVsyst model where the parameters are in vector xL
993
- # x[0] = Rsh0
994
- # x[1] = Rshref
995
-
996
- rsho = x[0]
997
- rshref = x[1]
998
-
999
- rshb = np.maximum(
1000
- (rshref - rsho * np.exp(-rshexp)) / (1. - np.exp(-rshexp)), 0.)
1001
- rsh = rshb + (rsho - rshb) * np.exp(-rshexp * g / go)
1002
- return rsh
1003
-
1004
-
1005
- def _filter_params(ee, isc, io, rs, rsh):
1006
- # Function _filter_params identifies bad parameter sets. A bad set contains
1007
- # Nan, non-positive or imaginary values for parameters; Rs > Rsh; or data
1008
- # where effective irradiance Ee differs by more than 5% from a linear fit
1009
- # to Isc vs. Ee
1010
-
1011
- badrsh = np.logical_or(rsh < 0., np.isnan(rsh))
1012
- negrs = rs < 0.
1013
- badrs = np.logical_or(rs > rsh, np.isnan(rs))
1014
- imagrs = ~(np.isreal(rs))
1015
- badio = np.logical_or(np.logical_or(~(np.isreal(rs)), io <= 0),
1016
- np.isnan(io))
1017
- goodr = np.logical_and(~badrsh, ~imagrs)
1018
- goodr = np.logical_and(goodr, ~negrs)
1019
- goodr = np.logical_and(goodr, ~badrs)
1020
- goodr = np.logical_and(goodr, ~badio)
1021
-
1022
- matrix = np.vstack((ee / 1000., np.zeros(len(ee)))).T
1023
- eff = np.linalg.lstsq(matrix, isc, rcond=None)[0][0]
1024
- pisc = eff * ee / 1000
1025
- pisc_error = np.abs(pisc - isc) / isc
1026
- # check for departure from linear relation between Isc and Ee
1027
- badiph = pisc_error > .05
1028
-
1029
- u = np.logical_and(goodr, ~badiph)
1030
- return u
1031
-
1032
-
1033
- def _check_converge(prevparams, result, vmp, imp, i):
1034
- """
1035
- Function _check_converge computes convergence metrics for all IV curves.
1036
-
1037
- Helper function for fit_pvsyst_sandia, fit_desoto_sandia
1038
-
1039
- Parameters
1040
- ----------
1041
- prevparams: Convergence Parameters from the previous Iteration (used to
1042
- determine Percent Change in values between iterations)
1043
- result: performacne paramters of the (predicted) single diode fitting,
1044
- which includes Voc, Vmp, Imp, Pmp and Isc
1045
- vmp: measured values for each IV curve
1046
- imp: measured values for each IV curve
1047
- i: Index of current iteration in cec_parameter_estimation
1048
-
1049
- Returns
1050
- -------
1051
- convergeparam: dict containing the following for Imp, Vmp and Pmp:
1052
- - maximum percent difference between measured and modeled values
1053
- - minimum percent difference between measured and modeled values
1054
- - maximum absolute percent difference between measured and modeled
1055
- values
1056
- - mean percent difference between measured and modeled values
1057
- - standard deviation of percent difference between measured and modeled
1058
- values
1059
- - absolute difference for previous and current values of maximum
1060
- absolute percent difference (measured vs. modeled)
1061
- - absolute difference for previous and current values of mean percent
1062
- difference (measured vs. modeled)
1063
- - absolute difference for previous and current values of standard
1064
- deviation of percent difference (measured vs. modeled)
1065
- """
1066
-
1067
- convergeparam = {}
1068
-
1069
- imperror = (result['i_mp'] - imp) / imp * 100.
1070
- vmperror = (result['v_mp'] - vmp) / vmp * 100.
1071
- pmperror = (result['p_mp'] - (imp * vmp)) / (imp * vmp) * 100.
1072
-
1073
- convergeparam['imperrmax'] = max(imperror) # max of the error in Imp
1074
- convergeparam['imperrmin'] = min(imperror) # min of the error in Imp
1075
- # max of the absolute error in Imp
1076
- convergeparam['imperrabsmax'] = max(abs(imperror))
1077
- # mean of the error in Imp
1078
- convergeparam['imperrmean'] = np.mean(imperror, axis=0)
1079
- # std of the error in Imp
1080
- convergeparam['imperrstd'] = np.std(imperror, axis=0, ddof=1)
1081
-
1082
- convergeparam['vmperrmax'] = max(vmperror) # max of the error in Vmp
1083
- convergeparam['vmperrmin'] = min(vmperror) # min of the error in Vmp
1084
- # max of the absolute error in Vmp
1085
- convergeparam['vmperrabsmax'] = max(abs(vmperror))
1086
- # mean of the error in Vmp
1087
- convergeparam['vmperrmean'] = np.mean(vmperror, axis=0)
1088
- # std of the error in Vmp
1089
- convergeparam['vmperrstd'] = np.std(vmperror, axis=0, ddof=1)
1090
-
1091
- convergeparam['pmperrmax'] = max(pmperror) # max of the error in Pmp
1092
- convergeparam['pmperrmin'] = min(pmperror) # min of the error in Pmp
1093
- # max of the abs err. in Pmp
1094
- convergeparam['pmperrabsmax'] = max(abs(pmperror))
1095
- # mean error in Pmp
1096
- convergeparam['pmperrmean'] = np.mean(pmperror, axis=0)
1097
- # std error Pmp
1098
- convergeparam['pmperrstd'] = np.std(pmperror, axis=0, ddof=1)
1099
-
1100
- if prevparams['state'] != 0.0:
1101
- convergeparam['imperrstdchange'] = np.abs(
1102
- convergeparam['imperrstd'] / prevparams['imperrstd'] - 1.)
1103
- convergeparam['vmperrstdchange'] = np.abs(
1104
- convergeparam['vmperrstd'] / prevparams['vmperrstd'] - 1.)
1105
- convergeparam['pmperrstdchange'] = np.abs(
1106
- convergeparam['pmperrstd'] / prevparams['pmperrstd'] - 1.)
1107
- convergeparam['imperrmeanchange'] = np.abs(
1108
- convergeparam['imperrmean'] / prevparams['imperrmean'] - 1.)
1109
- convergeparam['vmperrmeanchange'] = np.abs(
1110
- convergeparam['vmperrmean'] / prevparams['vmperrmean'] - 1.)
1111
- convergeparam['pmperrmeanchange'] = np.abs(
1112
- convergeparam['pmperrmean'] / prevparams['pmperrmean'] - 1.)
1113
- convergeparam['imperrabsmaxchange'] = np.abs(
1114
- convergeparam['imperrabsmax'] / prevparams['imperrabsmax'] - 1.)
1115
- convergeparam['vmperrabsmaxchange'] = np.abs(
1116
- convergeparam['vmperrabsmax'] / prevparams['vmperrabsmax'] - 1.)
1117
- convergeparam['pmperrabsmaxchange'] = np.abs(
1118
- convergeparam['pmperrabsmax'] / prevparams['pmperrabsmax'] - 1.)
1119
- convergeparam['state'] = 1.0
1120
- else:
1121
- convergeparam['imperrstdchange'] = float("Inf")
1122
- convergeparam['vmperrstdchange'] = float("Inf")
1123
- convergeparam['pmperrstdchange'] = float("Inf")
1124
- convergeparam['imperrmeanchange'] = float("Inf")
1125
- convergeparam['vmperrmeanchange'] = float("Inf")
1126
- convergeparam['pmperrmeanchange'] = float("Inf")
1127
- convergeparam['imperrabsmaxchange'] = float("Inf")
1128
- convergeparam['vmperrabsmaxchange'] = float("Inf")
1129
- convergeparam['pmperrabsmaxchange'] = float("Inf")
1130
- convergeparam['state'] = 1.
1131
- return convergeparam
1132
-
1133
-
1134
- def _update_rsh_fixed_pt(vmp, imp, iph, io, rs, rsh, nnsvth):
1135
- """
1136
- Adjust Rsh to match Vmp using other parameter values
1137
-
1138
- Helper function for fit_pvsyst_sandia, fit_desoto_sandia
1139
-
1140
- Description
1141
- -----------
1142
- Rsh is updated iteratively using a fixed point expression
1143
- obtained from combining Vmp = Vmp(Imp) (using the analytic solution to the
1144
- single diode equation) and dP / dI = 0 at Imp. 500 iterations are performed
1145
- because convergence can be very slow.
1146
-
1147
- Parameters
1148
- ----------
1149
- vmp: a numpy array of length N of values for Vmp (V)
1150
- imp: a numpy array of length N of values for Imp (A)
1151
- iph: a numpy array of length N of values for light current IL (A)
1152
- io: a numpy array of length N of values for Io (A)
1153
- rs: a numpy array of length N of values for series resistance (ohm)
1154
- rsh: a numpy array of length N of initial values for shunt resistance (ohm)
1155
- nnsvth: a numpy array length N of values for the diode factor x thermal
1156
- voltage for the module, equal to Ns (number of cells in series) x
1157
- Vth (thermal voltage per cell).
1158
-
1159
- Returns
1160
- -------
1161
- numpy array of length N of updated values for Rsh
1162
-
1163
- References
1164
- ----------
1165
- .. [1] PVLib for MATLAB https://github.com/sandialabs/MATLAB_PV_LIB
1166
- .. [2] C. Hansen, Parameter Estimation for Single Diode Models of
1167
- Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065
1168
- """
1169
- niter = 500
1170
- x1 = rsh
1171
-
1172
- for i in range(niter):
1173
- _, z = _calc_theta_phi_exact(vmp, imp, iph, io, rs, x1, nnsvth)
1174
- with np.errstate(divide="ignore"):
1175
- next_x1 = (1 + z) / z * ((iph + io) * x1 / imp - nnsvth * z / imp
1176
- - 2 * vmp / imp)
1177
- x1 = next_x1
1178
-
1179
- return x1
1180
-
1181
-
1182
- def _calc_theta_phi_exact(vmp, imp, iph, io, rs, rsh, nnsvth):
1183
- """
1184
- _calc_theta_phi_exact computes Lambert W values appearing in the analytic
1185
- solutions to the single diode equation for the max power point.
1186
-
1187
- Helper function for fit_pvsyst_sandia
1188
-
1189
- Parameters
1190
- ----------
1191
- vmp: a numpy array of length N of values for Vmp (V)
1192
- imp: a numpy array of length N of values for Imp (A)
1193
- iph: a numpy array of length N of values for the light current IL (A)
1194
- io: a numpy array of length N of values for Io (A)
1195
- rs: a numpy array of length N of values for the series resistance (ohm)
1196
- rsh: a numpy array of length N of values for the shunt resistance (ohm)
1197
- nnsvth: a numpy array of length N of values for the diode factor x
1198
- thermal voltage for the module, equal to Ns
1199
- (number of cells in series) x Vth
1200
- (thermal voltage per cell).
1201
-
1202
- Returns
1203
- -------
1204
- theta: a numpy array of values for the Lamber W function for solving
1205
- I = I(V)
1206
- phi: a numpy array of values for the Lambert W function for solving
1207
- V = V(I)
1208
-
1209
- Notes
1210
- -----
1211
- _calc_theta_phi_exact calculates values for the Lambert W function which
1212
- are used in the analytic solutions for the single diode equation at the
1213
- maximum power point. For V=V(I),
1214
- phi = W(Io*Rsh/n*Vth * exp((IL + Io - Imp)*Rsh/n*Vth)). For I=I(V),
1215
- theta = W(Rs*Io/n*Vth *
1216
- Rsh/ (Rsh+Rs) * exp(Rsh/ (Rsh+Rs)*((Rs(IL+Io) + V)/n*Vth))
1217
-
1218
- References
1219
- ----------
1220
- .. [1] PVL MATLAB 2065 https://github.com/sandialabs/MATLAB_PV_LIB
1221
- .. [2] C. Hansen, Parameter Estimation for Single Diode Models of
1222
- Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065
1223
- .. [3] A. Jain, A. Kapoor, "Exact analytical solutions of the parameters of
1224
- real solar cells using Lambert W-function", Solar Energy Materials and
1225
- Solar Cells, 81 (2004) 269-277.
1226
- """
1227
- # handle singleton inputs
1228
- vmp = np.asarray(vmp)
1229
- imp = np.asarray(imp)
1230
- iph = np.asarray(iph)
1231
- io = np.asarray(io)
1232
- rs = np.asarray(rs)
1233
- rsh = np.asarray(rsh)
1234
- nnsvth = np.asarray(nnsvth)
1235
-
1236
- # Argument for Lambert W function involved in V = V(I) [2] Eq. 12; [3]
1237
- # Eq. 3
1238
- with np.errstate(over="ignore", divide="ignore", invalid="ignore"):
1239
- argw = np.where(
1240
- nnsvth == 0,
1241
- np.nan,
1242
- rsh * io / nnsvth * np.exp(rsh * (iph + io - imp) / nnsvth))
1243
- phi = np.where(argw > 0, lambertw(argw).real, np.nan)
1244
-
1245
- # NaN where argw overflows. Switch to log space to evaluate
1246
- u = np.isinf(argw)
1247
- if np.any(u):
1248
- logargw = (
1249
- np.log(rsh[u]) + np.log(io[u]) - np.log(nnsvth[u])
1250
- + rsh[u] * (iph[u] + io[u] - imp[u]) / nnsvth[u])
1251
- # Three iterations of Newton-Raphson method to solve w+log(w)=logargW.
1252
- # The initial guess is w=logargW. Where direct evaluation (above)
1253
- # results in NaN from overflow, 3 iterations of Newton's method gives
1254
- # approximately 8 digits of precision.
1255
- x = logargw
1256
- for i in range(3):
1257
- x *= ((1. - np.log(x) + logargw) / (1. + x))
1258
- phi[u] = x
1259
- phi = np.transpose(phi)
1260
-
1261
- # Argument for Lambert W function involved in I = I(V) [2] Eq. 11; [3]
1262
- # E1. 2
1263
- with np.errstate(over="ignore", divide="ignore", invalid="ignore"):
1264
- argw = np.where(
1265
- nnsvth == 0,
1266
- np.nan,
1267
- rsh / (rsh + rs) * rs * io / nnsvth * np.exp(
1268
- rsh / (rsh + rs) * (rs * (iph + io) + vmp) / nnsvth))
1269
- theta = np.where(argw > 0, lambertw(argw).real, np.nan)
1270
-
1271
- # NaN where argw overflows. Switch to log space to evaluate
1272
- u = np.isinf(argw)
1273
- if np.any(u):
1274
- with np.errstate(divide="ignore"):
1275
- logargw = (
1276
- np.log(rsh[u]) - np.log(rsh[u] + rs[u]) + np.log(rs[u])
1277
- + np.log(io[u]) - np.log(nnsvth[u])
1278
- + (rsh[u] / (rsh[u] + rs[u]))
1279
- * (rs[u] * (iph[u] + io[u]) + vmp[u]) / nnsvth[u])
1280
- # Three iterations of Newton-Raphson method to solve w+log(w)=logargW.
1281
- # The initial guess is w=logargW. Where direct evaluation (above)
1282
- # results in NaN from overflow, 3 iterations of Newton's method gives
1283
- # approximately 8 digits of precision.
1284
- x = logargw
1285
- for i in range(3):
1286
- x *= ((1. - np.log(x) + logargw) / (1. + x))
1287
- theta[u] = x
1288
- theta = np.transpose(theta)
1289
-
1290
- return theta, phi
1291
-
1292
-
1293
- def pvsyst_temperature_coeff(alpha_sc, gamma_ref, mu_gamma, I_L_ref, I_o_ref,
1294
- R_sh_ref, R_sh_0, R_s, cells_in_series,
1295
- R_sh_exp=5.5, EgRef=1.121, irrad_ref=1000,
1296
- temp_ref=25):
1297
- r"""
1298
- Calculates the temperature coefficient of power for a pvsyst single
1299
- diode model.
1300
-
1301
- The temperature coefficient is determined as the numerical derivative
1302
- :math:`\frac{dP}{dT}` at the maximum power point at reference conditions
1303
- [1]_.
1304
-
1305
- Parameters
1306
- ----------
1307
- alpha_sc : float
1308
- The short-circuit current temperature coefficient of the module. [A/C]
1309
-
1310
- gamma_ref : float
1311
- The diode ideality factor. [unitless]
1312
-
1313
- mu_gamma : float
1314
- The temperature coefficient for the diode ideality factor. [1/K]
1315
-
1316
- I_L_ref : float
1317
- The light-generated current (or photocurrent) at reference conditions.
1318
- [A]
1319
-
1320
- I_o_ref : float
1321
- The dark or diode reverse saturation current at reference conditions.
1322
- [A]
1323
-
1324
- R_sh_ref : float
1325
- The shunt resistance at reference conditions. [ohm]
1326
-
1327
- R_sh_0 : float
1328
- The shunt resistance at zero irradiance conditions. [ohm]
1329
-
1330
- R_s : float
1331
- The series resistance at reference conditions. [ohm]
1332
-
1333
- cells_in_series : int
1334
- The number of cells connected in series.
1335
-
1336
- R_sh_exp : float, default 5.5
1337
- The exponent in the equation for shunt resistance. [unitless]
1338
-
1339
- EgRef : float, default 1.121
1340
- The energy bandgap of the module's cells at reference temperature.
1341
- Default of 1.121 eV is for crystalline silicon. Must be positive. [eV]
1342
-
1343
- irrad_ref : float, default 1000
1344
- Reference irradiance. [W/m^2].
1345
-
1346
- temp_ref : float, default 25
1347
- Reference cell temperature. [C]
1348
-
1349
-
1350
- Returns
1351
- -------
1352
- gamma_pdc : float
1353
- Temperature coefficient of power at maximum power point at reference
1354
- conditions. [1/C]
1355
-
1356
- References
1357
- ----------
1358
- .. [1] K. Sauer, T. Roessler, C. W. Hansen, Modeling the Irradiance and
1359
- Temperature Dependence of Photovoltaic Modules in PVsyst, IEEE Journal
1360
- of Photovoltaics v5(1), January 2015.
1361
- """
1362
-
1363
- def maxp(temp_cell, irrad_ref, alpha_sc, gamma_ref, mu_gamma, I_L_ref,
1364
- I_o_ref, R_sh_ref, R_sh_0, R_s, cells_in_series, R_sh_exp, EgRef,
1365
- temp_ref):
1366
- params = calcparams_pvsyst(
1367
- irrad_ref, temp_cell, alpha_sc, gamma_ref, mu_gamma, I_L_ref,
1368
- I_o_ref, R_sh_ref, R_sh_0, R_s, cells_in_series, R_sh_exp, EgRef,
1369
- irrad_ref, temp_ref)
1370
- res = bishop88_mpp(*params)
1371
- return res[2]
1372
-
1373
- args = (irrad_ref, alpha_sc, gamma_ref, mu_gamma, I_L_ref,
1374
- I_o_ref, R_sh_ref, R_sh_0, R_s, cells_in_series, R_sh_exp, EgRef,
1375
- temp_ref)
1376
- pmp = maxp(temp_ref, *args)
1377
- gamma_pdc = _first_order_centered_difference(maxp, x0=temp_ref, args=args)
1378
-
1379
- return gamma_pdc / pmp