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
@@ -1,608 +0,0 @@
1
- """
2
- testing single-diode methods using JW Bishop 1988
3
- """
4
-
5
- import numpy as np
6
- import pandas as pd
7
- import scipy
8
- from pvlib import pvsystem
9
- from pvlib.singlediode import (bishop88_mpp, estimate_voc, VOLTAGE_BUILTIN,
10
- bishop88, bishop88_i_from_v, bishop88_v_from_i)
11
- import pytest
12
- from numpy.testing import assert_array_equal
13
- from .conftest import DATA_DIR
14
-
15
- POA = 888
16
- TCELL = 55
17
-
18
-
19
- @pytest.mark.parametrize('method', ['brentq', 'newton'])
20
- def test_method_spr_e20_327(method, cec_module_spr_e20_327):
21
- """test pvsystem.singlediode with different methods on SPR-E20-327"""
22
- spr_e20_327 = cec_module_spr_e20_327
23
- x = pvsystem.calcparams_desoto(
24
- effective_irradiance=POA, temp_cell=TCELL,
25
- alpha_sc=spr_e20_327['alpha_sc'], a_ref=spr_e20_327['a_ref'],
26
- I_L_ref=spr_e20_327['I_L_ref'], I_o_ref=spr_e20_327['I_o_ref'],
27
- R_sh_ref=spr_e20_327['R_sh_ref'], R_s=spr_e20_327['R_s'],
28
- EgRef=1.121, dEgdT=-0.0002677)
29
- pvs = pvsystem.singlediode(*x, method='lambertw')
30
- out = pvsystem.singlediode(*x, method=method)
31
-
32
- assert np.isclose(pvs['i_sc'], out['i_sc'])
33
- assert np.isclose(pvs['v_oc'], out['v_oc'])
34
- assert np.isclose(pvs['i_mp'], out['i_mp'])
35
- assert np.isclose(pvs['v_mp'], out['v_mp'])
36
- assert np.isclose(pvs['p_mp'], out['p_mp'])
37
- assert np.isclose(pvs['i_x'], out['i_x'])
38
- assert np.isclose(pvs['i_xx'], out['i_xx'])
39
-
40
-
41
- @pytest.mark.parametrize('method', ['brentq', 'newton'])
42
- def test_newton_fs_495(method, cec_module_fs_495):
43
- """test pvsystem.singlediode with different methods on FS495"""
44
- fs_495 = cec_module_fs_495
45
- x = pvsystem.calcparams_desoto(
46
- effective_irradiance=POA, temp_cell=TCELL,
47
- alpha_sc=fs_495['alpha_sc'], a_ref=fs_495['a_ref'],
48
- I_L_ref=fs_495['I_L_ref'], I_o_ref=fs_495['I_o_ref'],
49
- R_sh_ref=fs_495['R_sh_ref'], R_s=fs_495['R_s'],
50
- EgRef=1.475, dEgdT=-0.0003)
51
- pvs = pvsystem.singlediode(*x, method='lambertw')
52
- out = pvsystem.singlediode(*x, method=method)
53
-
54
- assert np.isclose(pvs['i_sc'], out['i_sc'])
55
- assert np.isclose(pvs['v_oc'], out['v_oc'])
56
- assert np.isclose(pvs['i_mp'], out['i_mp'])
57
- assert np.isclose(pvs['v_mp'], out['v_mp'])
58
- assert np.isclose(pvs['p_mp'], out['p_mp'])
59
- assert np.isclose(pvs['i_x'], out['i_x'])
60
- assert np.isclose(pvs['i_xx'], out['i_xx'])
61
-
62
-
63
- def build_precise_iv_curve_dataframe(file_csv, file_json):
64
- """
65
- Reads a precise IV curve parameter set CSV and JSON to create a DataFrame.
66
- The CSV contains the parameters of the single diode equation which are used
67
- to generate the JSON data. The data are calculated using [1]_ with 40
68
- decimal digits of precision in order have at least 16 decimal digits of
69
- precision when they are stored in JSON. The precision is sufficient for the
70
- difference between the left and right side of the single diode equation to
71
- be less than :math:`1 \times 10^{-16}` when the numbers from the JSON are
72
- read as mpmath floats. The code to generate these IV curve data is from
73
- [2]_. The data and tests that use this function were added in :pull:`1573`.
74
-
75
- Parameters
76
- ----------
77
- file_csv: str
78
- Path to a CSV file of IV curve parameter sets.
79
-
80
- file_json: str
81
- Path to a JSON file of precise IV curves.
82
-
83
- Returns
84
- -------
85
- A DataFrame with these columns: ``Index``, ``photocurrent``,
86
- ``saturation_current``, ``resistance_series``, ``resistance_shunt``,
87
- ``n``, ``cells_in_series``, ``Voltages``, ``Currents``,
88
- ``diode_voltage``, ``v_oc``, ``i_sc``, ``v_mp``, ``i_mp``, ``p_mp``,
89
- ``i_x``, ``i_xx`, ``Temperature``, ``Irradiance``, ``Sweep Direction``,
90
- ``Datetime``, ``Boltzmann``, ``Elementary Charge``, and ``Vth``. The
91
- columns ``Irradiance``, ``Sweep Direction`` are None or empty strings.
92
-
93
- References
94
- ----------
95
- .. [1] The mpmath development team. (2023). mpmath: a Python library for
96
- arbitrary-precision floating-point arithmetic (version 1.2.1).
97
- `mpmath <mpmath.org>`_
98
-
99
- .. [2] The ivcurves development team. (2022). Code to generate precise
100
- solutions to the single diode equation.
101
- `ivcurves <github.com/cwhanse/ivcurves>`_
102
- """
103
- params = pd.read_csv(file_csv)
104
- curves_metadata = pd.read_json(file_json)
105
- curves = pd.DataFrame(curves_metadata['IV Curves'].values.tolist())
106
- curves['cells_in_series'] = curves_metadata['cells_in_series']
107
- joined = params.merge(curves, on='Index', how='inner',
108
- suffixes=(None, '_drop'), validate='one_to_one')
109
- joined = joined[(c for c in joined.columns if not c.endswith('_drop'))]
110
-
111
- # parse strings to np.float64
112
- is_array = ['Currents', 'Voltages', 'diode_voltage']
113
- for col in is_array:
114
- joined[col] = [np.asarray(a, dtype=np.float64) for a in joined[col]]
115
- is_number = ['v_oc', 'i_sc', 'v_mp', 'i_mp', 'p_mp', 'i_x', 'i_xx',
116
- 'Temperature']
117
- joined[is_number] = joined[is_number].astype(np.float64)
118
-
119
- joined['Boltzmann'] = scipy.constants.Boltzmann
120
- joined['Elementary Charge'] = scipy.constants.elementary_charge
121
- joined['Vth'] = (
122
- joined['Boltzmann'] * joined['Temperature']
123
- / joined['Elementary Charge']
124
- )
125
-
126
- return joined
127
-
128
-
129
- @pytest.fixture(scope='function', params=[
130
- {
131
- 'csv': f'{DATA_DIR}/precise_iv_curves_parameter_sets1.csv',
132
- 'json': f'{DATA_DIR}/precise_iv_curves1.json'
133
- },
134
- {
135
- 'csv': f'{DATA_DIR}/precise_iv_curves_parameter_sets2.csv',
136
- 'json': f'{DATA_DIR}/precise_iv_curves2.json'
137
- }
138
- ], ids=[1, 2])
139
- def precise_iv_curves(request):
140
- file_csv, file_json = request.param['csv'], request.param['json']
141
- pc = build_precise_iv_curve_dataframe(file_csv, file_json)
142
- params = ['photocurrent', 'saturation_current', 'resistance_series',
143
- 'resistance_shunt']
144
- singlediode_params = pc.loc[:, params]
145
- singlediode_params['nNsVth'] = pc['n'] * pc['cells_in_series'] * pc['Vth']
146
- return singlediode_params, pc
147
-
148
-
149
- @pytest.mark.parametrize('method', ['lambertw', 'brentq', 'newton'])
150
- def test_singlediode_precision(method, precise_iv_curves):
151
- """
152
- Tests the accuracy of singlediode. ivcurve_pnts is not tested.
153
- """
154
- x, pc = precise_iv_curves
155
- outs = pvsystem.singlediode(method=method, **x)
156
-
157
- assert np.allclose(pc['i_sc'], outs['i_sc'], atol=1e-10, rtol=0)
158
- assert np.allclose(pc['v_oc'], outs['v_oc'], atol=1e-10, rtol=0)
159
- assert np.allclose(pc['i_mp'], outs['i_mp'], atol=7e-8, rtol=0)
160
- assert np.allclose(pc['v_mp'], outs['v_mp'], atol=1e-6, rtol=0)
161
- assert np.allclose(pc['p_mp'], outs['p_mp'], atol=1e-10, rtol=0)
162
- assert np.allclose(pc['i_x'], outs['i_x'], atol=1e-10, rtol=0)
163
-
164
- # This test should pass with atol=9e-8 on MacOS and Windows.
165
- # The atol was lowered to pass on Linux when the vectorized umath module
166
- # introduced in NumPy 1.22.0 is used.
167
- assert np.allclose(pc['i_xx'], outs['i_xx'], atol=1e-6, rtol=0)
168
-
169
-
170
- def test_singlediode_lambert_negative_voc(mocker):
171
- """Tests approximation to zero of v_oc when it is negative and small.
172
- See singlediode.py:_lambertw > comment 'Set small elements <0 in v_oc to 0'
173
- """
174
- # Next values should result in a negative v_oc out of `_lambertw_v_from_i`
175
- # however, we can't ensure that the output belongs to (-1e-12, 0), so we
176
- # mock it. It depends on the platform and Python distro. See issue #2000.
177
- patcher = mocker.patch("pvlib.singlediode._lambertw_v_from_i")
178
- x = np.array([0.0, 1.480501e-11, 0.178, 8000.0, 1.797559])
179
- patcher.return_value = -9.999e-13
180
- outs = pvsystem.singlediode(*x, method="lambertw")
181
- assert outs["v_oc"] == 0
182
-
183
- # Testing for an array
184
- patcher.return_value = np.array([-9.999e-13, -1.001e-13])
185
- x = np.array([x, x]).T
186
- outs = pvsystem.singlediode(*x, method="lambertw")
187
- assert_array_equal(outs["v_oc"], [0, 0])
188
-
189
-
190
- @pytest.mark.parametrize('method', ['lambertw', 'brentq', 'newton'])
191
- def test_v_from_i_i_from_v_precision(method, precise_iv_curves):
192
- """
193
- Tests the accuracy of pvsystem.v_from_i and pvsystem.i_from_v.
194
- """
195
- x, pc = precise_iv_curves
196
- pc_i, pc_v = pc['Currents'], pc['Voltages']
197
- for i, v, (_, x_one_curve) in zip(pc_i, pc_v, x.iterrows()):
198
- out_i = pvsystem.i_from_v(voltage=v, method=method, **x_one_curve)
199
- out_v = pvsystem.v_from_i(current=i, method=method, **x_one_curve)
200
-
201
- assert np.allclose(i, out_i, atol=1e-10, rtol=0)
202
- assert np.allclose(v, out_v, atol=1e-10, rtol=0)
203
-
204
-
205
- def get_pvsyst_fs_495():
206
- """
207
- PVsyst parameters for First Solar FS-495 module from PVSyst-6.7.2 database.
208
-
209
- I_L_ref derived from Isc_ref conditions::
210
-
211
- I_L_ref = (I_sc_ref + Id + Ish) / (1 - d2mutau/(Vbi*N_s - Vd))
212
-
213
- where::
214
-
215
- Vd = I_sc_ref * R_s
216
- Id = I_o_ref * (exp(Vd / nNsVt) - 1)
217
- Ish = Vd / R_sh_ref
218
-
219
- """
220
- return {
221
- 'd2mutau': 1.31, 'alpha_sc': 0.00039, 'gamma_ref': 1.48,
222
- 'mu_gamma': 0.001, 'I_o_ref': 9.62e-10, 'R_sh_ref': 5000,
223
- 'R_sh_0': 12500, 'R_sh_exp': 3.1, 'R_s': 4.6, 'beta_oc': -0.2116,
224
- 'EgRef': 1.5, 'cells_in_series': 108, 'cells_in_parallel': 2,
225
- 'I_sc_ref': 1.55, 'V_oc_ref': 86.5, 'I_mp_ref': 1.4, 'V_mp_ref': 67.85,
226
- 'temp_ref': 25, 'irrad_ref': 1000, 'I_L_ref': 1.5743233463848496
227
- }
228
-
229
- # DeSoto @(888[W/m**2], 55[degC]) = {Pmp: 72.71, Isc: 1.402, Voc: 75.42)
230
-
231
-
232
- @pytest.mark.parametrize(
233
- 'poa, temp_cell, expected, tol', [
234
- # reference conditions
235
- (
236
- get_pvsyst_fs_495()['irrad_ref'],
237
- get_pvsyst_fs_495()['temp_ref'],
238
- {
239
- 'pmp': (get_pvsyst_fs_495()['I_mp_ref'] *
240
- get_pvsyst_fs_495()['V_mp_ref']),
241
- 'isc': get_pvsyst_fs_495()['I_sc_ref'],
242
- 'voc': get_pvsyst_fs_495()['V_oc_ref']
243
- },
244
- (5e-4, 0.04)
245
- ),
246
- # other conditions
247
- (
248
- POA,
249
- TCELL,
250
- {
251
- 'pmp': 76.262,
252
- 'isc': 1.3868,
253
- 'voc': 79.292
254
- },
255
- (1e-4, 1e-4)
256
- )
257
- ]
258
- )
259
- @pytest.mark.parametrize('method', ['newton', 'brentq'])
260
- def test_pvsyst_recombination_loss(method, poa, temp_cell, expected, tol):
261
- """test PVSst recombination loss"""
262
- pvsyst_fs_495 = get_pvsyst_fs_495()
263
- # first evaluate PVSyst model with thin-film recombination loss current
264
- # at reference conditions
265
- x = pvsystem.calcparams_pvsyst(
266
- effective_irradiance=poa, temp_cell=temp_cell,
267
- alpha_sc=pvsyst_fs_495['alpha_sc'],
268
- gamma_ref=pvsyst_fs_495['gamma_ref'],
269
- mu_gamma=pvsyst_fs_495['mu_gamma'], I_L_ref=pvsyst_fs_495['I_L_ref'],
270
- I_o_ref=pvsyst_fs_495['I_o_ref'], R_sh_ref=pvsyst_fs_495['R_sh_ref'],
271
- R_sh_0=pvsyst_fs_495['R_sh_0'], R_sh_exp=pvsyst_fs_495['R_sh_exp'],
272
- R_s=pvsyst_fs_495['R_s'],
273
- cells_in_series=pvsyst_fs_495['cells_in_series'],
274
- EgRef=pvsyst_fs_495['EgRef']
275
- )
276
- il_pvsyst, io_pvsyst, rs_pvsyst, rsh_pvsyst, nnsvt_pvsyst = x
277
- voc_est_pvsyst = estimate_voc(photocurrent=il_pvsyst,
278
- saturation_current=io_pvsyst,
279
- nNsVth=nnsvt_pvsyst)
280
- vd_pvsyst = np.linspace(0, voc_est_pvsyst, 1000)
281
- pvsyst = bishop88(
282
- diode_voltage=vd_pvsyst, photocurrent=il_pvsyst,
283
- saturation_current=io_pvsyst, resistance_series=rs_pvsyst,
284
- resistance_shunt=rsh_pvsyst, nNsVth=nnsvt_pvsyst,
285
- d2mutau=pvsyst_fs_495['d2mutau'],
286
- NsVbi=VOLTAGE_BUILTIN*pvsyst_fs_495['cells_in_series']
287
- )
288
- # test max power
289
- assert np.isclose(max(pvsyst[2]), expected['pmp'], *tol)
290
-
291
- # test short circuit current
292
- isc_pvsyst = np.interp(0, pvsyst[1], pvsyst[0])
293
- assert np.isclose(isc_pvsyst, expected['isc'], *tol)
294
-
295
- # test open circuit voltage
296
- voc_pvsyst = np.interp(0, pvsyst[0][::-1], pvsyst[1][::-1])
297
- assert np.isclose(voc_pvsyst, expected['voc'], *tol)
298
-
299
- # repeat tests as above with specialized bishop88 functions
300
- y = dict(d2mutau=pvsyst_fs_495['d2mutau'],
301
- NsVbi=VOLTAGE_BUILTIN*pvsyst_fs_495['cells_in_series'])
302
-
303
- mpp_88 = bishop88_mpp(*x, **y, method=method)
304
- assert np.isclose(mpp_88[2], expected['pmp'], *tol)
305
-
306
- isc_88 = bishop88_i_from_v(0, *x, **y, method=method)
307
- assert np.isclose(isc_88, expected['isc'], *tol)
308
-
309
- voc_88 = bishop88_v_from_i(0, *x, **y, method=method)
310
- assert np.isclose(voc_88, expected['voc'], *tol)
311
-
312
- ioc_88 = bishop88_i_from_v(voc_88, *x, **y, method=method)
313
- assert np.isclose(ioc_88, 0.0, *tol)
314
-
315
- vsc_88 = bishop88_v_from_i(isc_88, *x, **y, method=method)
316
- assert np.isclose(vsc_88, 0.0, *tol)
317
-
318
-
319
- @pytest.mark.parametrize(
320
- 'brk_params, recomb_params, poa, temp_cell, expected, tol', [
321
- # reference conditions without breakdown model
322
- (
323
- (0., -5.5, 3.28),
324
- (get_pvsyst_fs_495()['d2mutau'],
325
- VOLTAGE_BUILTIN * get_pvsyst_fs_495()['cells_in_series']),
326
- get_pvsyst_fs_495()['irrad_ref'],
327
- get_pvsyst_fs_495()['temp_ref'],
328
- {
329
- 'pmp': (get_pvsyst_fs_495()['I_mp_ref'] * # noqa: W504
330
- get_pvsyst_fs_495()['V_mp_ref']),
331
- 'isc': get_pvsyst_fs_495()['I_sc_ref'],
332
- 'voc': get_pvsyst_fs_495()['V_oc_ref']
333
- },
334
- (5e-4, 0.04)
335
- ),
336
- # other conditions with breakdown model on and recombination model off
337
- (
338
- (1.e-4, -5.5, 3.28),
339
- (0., np.inf),
340
- POA,
341
- TCELL,
342
- {
343
- 'pmp': 79.723,
344
- 'isc': 1.4071,
345
- 'voc': 79.646
346
- },
347
- (1e-4, 1e-4)
348
- )
349
- ]
350
- )
351
- @pytest.mark.parametrize('method', ['newton', 'brentq'])
352
- def test_pvsyst_breakdown(method, brk_params, recomb_params, poa, temp_cell,
353
- expected, tol):
354
- """test PVSyst recombination loss"""
355
- pvsyst_fs_495 = get_pvsyst_fs_495()
356
- # first evaluate PVSyst model with thin-film recombination loss current
357
- # at reference conditions
358
- x = pvsystem.calcparams_pvsyst(
359
- effective_irradiance=poa, temp_cell=temp_cell,
360
- alpha_sc=pvsyst_fs_495['alpha_sc'],
361
- gamma_ref=pvsyst_fs_495['gamma_ref'],
362
- mu_gamma=pvsyst_fs_495['mu_gamma'], I_L_ref=pvsyst_fs_495['I_L_ref'],
363
- I_o_ref=pvsyst_fs_495['I_o_ref'], R_sh_ref=pvsyst_fs_495['R_sh_ref'],
364
- R_sh_0=pvsyst_fs_495['R_sh_0'], R_sh_exp=pvsyst_fs_495['R_sh_exp'],
365
- R_s=pvsyst_fs_495['R_s'],
366
- cells_in_series=pvsyst_fs_495['cells_in_series'],
367
- EgRef=pvsyst_fs_495['EgRef']
368
- )
369
- il_pvsyst, io_pvsyst, rs_pvsyst, rsh_pvsyst, nnsvt_pvsyst = x
370
-
371
- d2mutau, NsVbi = recomb_params
372
- breakdown_factor, breakdown_voltage, breakdown_exp = brk_params
373
-
374
- voc_est_pvsyst = estimate_voc(photocurrent=il_pvsyst,
375
- saturation_current=io_pvsyst,
376
- nNsVth=nnsvt_pvsyst)
377
- vd_pvsyst = np.linspace(0, voc_est_pvsyst, 1000)
378
- pvsyst = bishop88(
379
- diode_voltage=vd_pvsyst, photocurrent=il_pvsyst,
380
- saturation_current=io_pvsyst, resistance_series=rs_pvsyst,
381
- resistance_shunt=rsh_pvsyst, nNsVth=nnsvt_pvsyst,
382
- d2mutau=d2mutau, NsVbi=NsVbi,
383
- breakdown_factor=breakdown_factor, breakdown_voltage=breakdown_voltage,
384
- breakdown_exp=breakdown_exp
385
- )
386
- # test max power
387
- assert np.isclose(max(pvsyst[2]), expected['pmp'], *tol)
388
-
389
- # test short circuit current
390
- isc_pvsyst = np.interp(0, pvsyst[1], pvsyst[0])
391
- assert np.isclose(isc_pvsyst, expected['isc'], *tol)
392
-
393
- # test open circuit voltage
394
- voc_pvsyst = np.interp(0, pvsyst[0][::-1], pvsyst[1][::-1])
395
- assert np.isclose(voc_pvsyst, expected['voc'], *tol)
396
-
397
- # repeat tests as above with specialized bishop88 functions
398
- y = {'d2mutau': recomb_params[0], 'NsVbi': recomb_params[1],
399
- 'breakdown_factor': brk_params[0], 'breakdown_voltage': brk_params[1],
400
- 'breakdown_exp': brk_params[2]}
401
-
402
- mpp_88 = bishop88_mpp(*x, **y, method=method)
403
- assert np.isclose(mpp_88[2], expected['pmp'], *tol)
404
-
405
- isc_88 = bishop88_i_from_v(0, *x, **y, method=method)
406
- assert np.isclose(isc_88, expected['isc'], *tol)
407
-
408
- voc_88 = bishop88_v_from_i(0, *x, **y, method=method)
409
- assert np.isclose(voc_88, expected['voc'], *tol)
410
-
411
- ioc_88 = bishop88_i_from_v(voc_88, *x, **y, method=method)
412
- assert np.isclose(ioc_88, 0.0, *tol)
413
-
414
- vsc_88 = bishop88_v_from_i(isc_88, *x, **y, method=method)
415
- assert np.isclose(vsc_88, 0.0, *tol)
416
-
417
-
418
- @pytest.fixture
419
- def bishop88_arguments():
420
- pvsyst_fs_495 = get_pvsyst_fs_495()
421
- # evaluate PVSyst model with thin-film recombination loss current
422
- # at reference conditions
423
- x = pvsystem.calcparams_pvsyst(
424
- effective_irradiance=pvsyst_fs_495['irrad_ref'],
425
- temp_cell=pvsyst_fs_495['temp_ref'],
426
- alpha_sc=pvsyst_fs_495['alpha_sc'],
427
- gamma_ref=pvsyst_fs_495['gamma_ref'],
428
- mu_gamma=pvsyst_fs_495['mu_gamma'], I_L_ref=pvsyst_fs_495['I_L_ref'],
429
- I_o_ref=pvsyst_fs_495['I_o_ref'], R_sh_ref=pvsyst_fs_495['R_sh_ref'],
430
- R_sh_0=pvsyst_fs_495['R_sh_0'], R_sh_exp=pvsyst_fs_495['R_sh_exp'],
431
- R_s=pvsyst_fs_495['R_s'],
432
- cells_in_series=pvsyst_fs_495['cells_in_series'],
433
- EgRef=pvsyst_fs_495['EgRef']
434
- )
435
- y = dict(d2mutau=pvsyst_fs_495['d2mutau'],
436
- NsVbi=VOLTAGE_BUILTIN*pvsyst_fs_495['cells_in_series'])
437
- # Convert (*x, **y) in a bishop88_.* call to dict of arguments
438
- args_dict = {
439
- 'photocurrent': x[0],
440
- 'saturation_current': x[1],
441
- 'resistance_series': x[2],
442
- 'resistance_shunt': x[3],
443
- 'nNsVth': x[4],
444
- }
445
- args_dict.update(y)
446
- return args_dict
447
-
448
-
449
- @pytest.mark.parametrize('method, method_kwargs', [
450
- ('newton', {
451
- 'tol': 1e-8,
452
- 'rtol': 1e-8,
453
- 'maxiter': 30,
454
- }),
455
- ('brentq', {
456
- 'xtol': 1e-8,
457
- 'rtol': 1e-8,
458
- 'maxiter': 30,
459
- })
460
- ])
461
- def test_bishop88_kwargs_transfer(method, method_kwargs, mocker,
462
- bishop88_arguments):
463
- """test method_kwargs modifying optimizer does not break anything"""
464
- # patch method namespace at singlediode module namespace
465
- optimizer_mock = mocker.patch('pvlib.singlediode.' + method)
466
-
467
- # check kwargs passed to bishop_.* are a subset of the call args
468
- # since they are called with more keyword arguments
469
-
470
- bishop88_i_from_v(0, **bishop88_arguments, method=method,
471
- method_kwargs=method_kwargs)
472
- _, kwargs = optimizer_mock.call_args
473
- assert method_kwargs.items() <= kwargs.items()
474
-
475
- bishop88_v_from_i(0, **bishop88_arguments, method=method,
476
- method_kwargs=method_kwargs)
477
- _, kwargs = optimizer_mock.call_args
478
- assert method_kwargs.items() <= kwargs.items()
479
-
480
- bishop88_mpp(**bishop88_arguments, method=method,
481
- method_kwargs=method_kwargs)
482
- _, kwargs = optimizer_mock.call_args
483
- assert method_kwargs.items() <= kwargs.items()
484
-
485
-
486
- @pytest.mark.parametrize('method, method_kwargs', [
487
- ('newton', {
488
- 'tol': 1e-4,
489
- 'rtol': 1e-4,
490
- 'maxiter': 20,
491
- '_inexistent_param': "0.01"
492
- }),
493
- ('brentq', {
494
- 'xtol': 1e-4,
495
- 'rtol': 1e-4,
496
- 'maxiter': 20,
497
- '_inexistent_param': "0.01"
498
- })
499
- ])
500
- def test_bishop88_kwargs_fails(method, method_kwargs, bishop88_arguments):
501
- """test invalid method_kwargs passed onto the optimizer fail"""
502
-
503
- pytest.raises(TypeError, bishop88_i_from_v,
504
- 0, **bishop88_arguments, method=method,
505
- method_kwargs=method_kwargs)
506
-
507
- pytest.raises(TypeError, bishop88_v_from_i,
508
- 0, **bishop88_arguments, method=method,
509
- method_kwargs=method_kwargs)
510
-
511
- pytest.raises(TypeError, bishop88_mpp,
512
- **bishop88_arguments, method=method,
513
- method_kwargs=method_kwargs)
514
-
515
-
516
- @pytest.mark.parametrize('method', ['newton', 'brentq'])
517
- def test_bishop88_full_output_kwarg(method, bishop88_arguments):
518
- """test call to bishop88_.* with full_output=True return values are ok"""
519
- method_kwargs = {'full_output': True}
520
-
521
- ret_val = bishop88_i_from_v(0, **bishop88_arguments, method=method,
522
- method_kwargs=method_kwargs)
523
- assert isinstance(ret_val, tuple) # ret_val must be a tuple
524
- assert len(ret_val) == 2 # of two elements
525
- assert isinstance(ret_val[0], float) # first one has bishop88 result
526
- assert isinstance(ret_val[1], tuple) # second is output from optimizer
527
- # any root finder returns at least 2 elements with full_output=True
528
- assert len(ret_val[1]) >= 2
529
-
530
- ret_val = bishop88_v_from_i(0, **bishop88_arguments, method=method,
531
- method_kwargs=method_kwargs)
532
- assert isinstance(ret_val, tuple) # ret_val must be a tuple
533
- assert len(ret_val) == 2 # of two elements
534
- assert isinstance(ret_val[0], float) # first one has bishop88 result
535
- assert isinstance(ret_val[1], tuple) # second is output from optimizer
536
- # any root finder returns at least 2 elements with full_output=True
537
- assert len(ret_val[1]) >= 2
538
-
539
- ret_val = bishop88_mpp(**bishop88_arguments, method=method,
540
- method_kwargs=method_kwargs)
541
- assert isinstance(ret_val, tuple) # ret_val must be a tuple
542
- assert len(ret_val) == 2 # of two elements
543
- assert isinstance(ret_val[0], tuple) # first one has bishop88 result
544
- assert len(ret_val[0]) == 3 # of three elements (I,V,P)
545
- assert isinstance(ret_val[1], tuple) # second is output from optimizer
546
- # any root finder returns at least 2 elements with full_output=True
547
- assert len(ret_val[1]) >= 2
548
-
549
-
550
- @pytest.mark.parametrize('method', ['newton', 'brentq'])
551
- def test_bishop88_pdSeries_len_one(method, bishop88_arguments):
552
- for k, v in bishop88_arguments.items():
553
- bishop88_arguments[k] = pd.Series([v])
554
-
555
- # should not raise error
556
- bishop88_i_from_v(pd.Series([0]), **bishop88_arguments, method=method)
557
- bishop88_v_from_i(pd.Series([0]), **bishop88_arguments, method=method)
558
- bishop88_mpp(**bishop88_arguments, method=method)
559
-
560
-
561
- def _sde_check_solution(i, v, il, io, rs, rsh, a, d2mutau=0., NsVbi=np.inf):
562
- vd = v + rs * i
563
- return il - io*np.expm1(vd/a) - vd/rsh - il*d2mutau/(NsVbi - vd) - i
564
-
565
-
566
- @pytest.mark.parametrize('method', ['newton', 'brentq'])
567
- def test_bishop88_init_cond(method):
568
- # GH 2013
569
- p = {'alpha_sc': 0.0012256,
570
- 'gamma_ref': 1.2916241612804187,
571
- 'mu_gamma': 0.00047308959960937403,
572
- 'I_L_ref': 3.068717040806731,
573
- 'I_o_ref': 2.2691248021217617e-11,
574
- 'R_sh_ref': 7000,
575
- 'R_sh_0': 7000,
576
- 'R_s': 4.602,
577
- 'cells_in_series': 268,
578
- 'R_sh_exp': 5.5,
579
- 'EgRef': 1.5}
580
- NsVbi = 268 * 0.9
581
- d2mutau = 1.4
582
- irrad = np.arange(20, 1100, 20)
583
- tc = np.arange(-25, 74, 1)
584
- weather = np.array(np.meshgrid(irrad, tc)).T.reshape(-1, 2)
585
- # with the above parameters and weather conditions, a few combinations
586
- # result in voc_est > NsVbi, which causes failure of brentq and newton
587
- # when the recombination parameters NsVbi and d2mutau are used.
588
- sde_params = pvsystem.calcparams_pvsyst(weather[:, 0], weather[:, 1], **p)
589
- # test _mpp
590
- result = bishop88_mpp(*sde_params, d2mutau=d2mutau, NsVbi=NsVbi)
591
- imp, vmp, pmp = result
592
- err = np.abs(_sde_check_solution(
593
- imp, vmp, sde_params[0], sde_params[1], sde_params[2], sde_params[3],
594
- sde_params[4], d2mutau=d2mutau, NsVbi=NsVbi))
595
- bad_results = np.isnan(pmp) | (pmp < 0) | (err > 0.00001) # 0.01mA error
596
- assert not bad_results.any()
597
- # test v_from_i
598
- vmp2 = bishop88_v_from_i(imp, *sde_params, d2mutau=d2mutau, NsVbi=NsVbi)
599
- err = np.abs(_sde_check_solution(imp, vmp2, *sde_params, d2mutau=d2mutau,
600
- NsVbi=NsVbi))
601
- bad_results = np.isnan(vmp2) | (vmp2 < 0) | (err > 0.00001)
602
- assert not bad_results.any()
603
- # test v_from_i
604
- imp2 = bishop88_i_from_v(vmp, *sde_params, d2mutau=d2mutau, NsVbi=NsVbi)
605
- err = np.abs(_sde_check_solution(imp2, vmp, *sde_params, d2mutau=d2mutau,
606
- NsVbi=NsVbi))
607
- bad_results = np.isnan(imp2) | (imp2 < 0) | (err > 0.00001)
608
- assert not bad_results.any()