pvlib 0.11.2__py3-none-any.whl → 0.12.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 (139) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/atmosphere.py +0 -9
  3. pvlib/bifacial/infinite_sheds.py +4 -3
  4. pvlib/bifacial/utils.py +2 -1
  5. pvlib/iotools/psm3.py +1 -1
  6. pvlib/iotools/pvgis.py +10 -2
  7. pvlib/iotools/tmy.py +3 -69
  8. pvlib/irradiance.py +14 -0
  9. pvlib/location.py +73 -33
  10. pvlib/modelchain.py +18 -35
  11. pvlib/pvsystem.py +7 -10
  12. pvlib/snow.py +64 -28
  13. pvlib/spectrum/__init__.py +0 -1
  14. pvlib/spectrum/irradiance.py +0 -63
  15. pvlib/spectrum/mismatch.py +3 -3
  16. pvlib/tools.py +6 -5
  17. {pvlib-0.11.2.dist-info → pvlib-0.12.0.dist-info}/METADATA +5 -3
  18. pvlib-0.12.0.dist-info/RECORD +75 -0
  19. {pvlib-0.11.2.dist-info → pvlib-0.12.0.dist-info}/WHEEL +1 -1
  20. pvlib/data/BIRD_08_16_2012.csv +0 -8761
  21. pvlib/data/BIRD_08_16_2012_patm.csv +0 -8761
  22. pvlib/data/Burlington, United States SolarAnywhere Time Series 2021 Lat_44_465 Lon_-73_205 TMY3 format.csv +0 -8762
  23. pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv +0 -578
  24. pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv +0 -74
  25. pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND +0 -146
  26. pvlib/data/CRNS0101-05-2019-AZ_Tucson_11_W.txt +0 -4
  27. pvlib/data/CRN_with_problems.txt +0 -3
  28. pvlib/data/ET-M772BH550GL.PAN +0 -75
  29. pvlib/data/NLD_Amsterdam062400_IWEC.epw +0 -8768
  30. pvlib/data/PVsyst_demo.csv +0 -10757
  31. pvlib/data/PVsyst_demo_model.csv +0 -3588
  32. pvlib/data/SRML-day-EUPO1801.txt +0 -1441
  33. pvlib/data/abq19056.dat +0 -6
  34. pvlib/data/bishop88_numerical_precision.csv +0 -101
  35. pvlib/data/bsrn-lr0100-pay0616.dat +0 -86901
  36. pvlib/data/bsrn-pay0616.dat.gz +0 -0
  37. pvlib/data/cams_mcclear_1min_verbose.csv +0 -60
  38. pvlib/data/cams_mcclear_monthly.csv +0 -42
  39. pvlib/data/cams_radiation_1min_verbose.csv +0 -72
  40. pvlib/data/cams_radiation_monthly.csv +0 -47
  41. pvlib/data/detect_clearsky_data.csv +0 -35
  42. pvlib/data/detect_clearsky_threshold_data.csv +0 -126
  43. pvlib/data/greensboro_kimber_soil_manwash.dat +0 -8761
  44. pvlib/data/greensboro_kimber_soil_nowash.dat +0 -8761
  45. pvlib/data/inverter_fit_snl_meas.csv +0 -127
  46. pvlib/data/inverter_fit_snl_sim.csv +0 -19
  47. pvlib/data/ivtools_numdiff.csv +0 -52
  48. pvlib/data/midc_20181014.txt +0 -1441
  49. pvlib/data/midc_raw_20181018.txt +0 -1441
  50. pvlib/data/midc_raw_short_header_20191115.txt +0 -1441
  51. pvlib/data/msn19056.dat +0 -6
  52. pvlib/data/precise_iv_curves1.json +0 -10251
  53. pvlib/data/precise_iv_curves2.json +0 -10251
  54. pvlib/data/precise_iv_curves_parameter_sets1.csv +0 -33
  55. pvlib/data/precise_iv_curves_parameter_sets2.csv +0 -33
  56. pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA2_10kWp_CIS_5_2a_2013_2014.json +0 -1
  57. pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA_30deg_0deg_2016_2016.csv +0 -35
  58. pvlib/data/pvgis_tmy_meta.json +0 -32
  59. pvlib/data/pvgis_tmy_test.csv +0 -8761
  60. pvlib/data/pvwatts_8760_rackmount.csv +0 -8779
  61. pvlib/data/pvwatts_8760_roofmount.csv +0 -8779
  62. pvlib/data/singleaxis_tracker_wslope.csv +0 -8761
  63. pvlib/data/spectrl2_example_spectra.csv +0 -123
  64. pvlib/data/surfrad-slv16001.dat +0 -1442
  65. pvlib/data/test_psm3_2017.csv +0 -17521
  66. pvlib/data/test_psm3_2019_5min.csv +0 -289
  67. pvlib/data/test_psm3_tmy-2017.csv +0 -8761
  68. pvlib/data/test_read_psm3.csv +0 -17523
  69. pvlib/data/test_read_pvgis_horizon.csv +0 -49
  70. pvlib/data/tmy_45.000_8.000_2005_2023.csv +0 -8789
  71. pvlib/data/tmy_45.000_8.000_2005_2023.epw +0 -8768
  72. pvlib/data/tmy_45.000_8.000_2005_2023.json +0 -1
  73. pvlib/data/tmy_45.000_8.000_2005_2023.txt +0 -8761
  74. pvlib/data/tmy_45.000_8.000_userhorizon.json +0 -1
  75. pvlib/spa_c_files/README.md +0 -81
  76. pvlib/spa_c_files/cspa_py.pxd +0 -43
  77. pvlib/spa_c_files/spa_py.pyx +0 -30
  78. pvlib/tests/__init__.py +0 -0
  79. pvlib/tests/bifacial/__init__.py +0 -0
  80. pvlib/tests/bifacial/test_infinite_sheds.py +0 -317
  81. pvlib/tests/bifacial/test_losses_models.py +0 -54
  82. pvlib/tests/bifacial/test_pvfactors.py +0 -82
  83. pvlib/tests/bifacial/test_utils.py +0 -192
  84. pvlib/tests/conftest.py +0 -476
  85. pvlib/tests/iotools/__init__.py +0 -0
  86. pvlib/tests/iotools/test_acis.py +0 -213
  87. pvlib/tests/iotools/test_bsrn.py +0 -131
  88. pvlib/tests/iotools/test_crn.py +0 -95
  89. pvlib/tests/iotools/test_epw.py +0 -23
  90. pvlib/tests/iotools/test_midc.py +0 -89
  91. pvlib/tests/iotools/test_panond.py +0 -32
  92. pvlib/tests/iotools/test_psm3.py +0 -198
  93. pvlib/tests/iotools/test_pvgis.py +0 -644
  94. pvlib/tests/iotools/test_sodapro.py +0 -298
  95. pvlib/tests/iotools/test_solaranywhere.py +0 -287
  96. pvlib/tests/iotools/test_solargis.py +0 -68
  97. pvlib/tests/iotools/test_solcast.py +0 -324
  98. pvlib/tests/iotools/test_solrad.py +0 -152
  99. pvlib/tests/iotools/test_srml.py +0 -124
  100. pvlib/tests/iotools/test_surfrad.py +0 -75
  101. pvlib/tests/iotools/test_tmy.py +0 -133
  102. pvlib/tests/ivtools/__init__.py +0 -0
  103. pvlib/tests/ivtools/test_sde.py +0 -230
  104. pvlib/tests/ivtools/test_sdm.py +0 -429
  105. pvlib/tests/ivtools/test_utils.py +0 -173
  106. pvlib/tests/spectrum/__init__.py +0 -0
  107. pvlib/tests/spectrum/conftest.py +0 -40
  108. pvlib/tests/spectrum/test_irradiance.py +0 -138
  109. pvlib/tests/spectrum/test_mismatch.py +0 -304
  110. pvlib/tests/spectrum/test_response.py +0 -124
  111. pvlib/tests/spectrum/test_spectrl2.py +0 -72
  112. pvlib/tests/test__deprecation.py +0 -97
  113. pvlib/tests/test_albedo.py +0 -84
  114. pvlib/tests/test_atmosphere.py +0 -351
  115. pvlib/tests/test_clearsky.py +0 -884
  116. pvlib/tests/test_conftest.py +0 -37
  117. pvlib/tests/test_iam.py +0 -555
  118. pvlib/tests/test_inverter.py +0 -213
  119. pvlib/tests/test_irradiance.py +0 -1487
  120. pvlib/tests/test_location.py +0 -356
  121. pvlib/tests/test_modelchain.py +0 -2020
  122. pvlib/tests/test_numerical_precision.py +0 -124
  123. pvlib/tests/test_pvarray.py +0 -71
  124. pvlib/tests/test_pvsystem.py +0 -2511
  125. pvlib/tests/test_scaling.py +0 -207
  126. pvlib/tests/test_shading.py +0 -391
  127. pvlib/tests/test_singlediode.py +0 -608
  128. pvlib/tests/test_snow.py +0 -212
  129. pvlib/tests/test_soiling.py +0 -230
  130. pvlib/tests/test_solarposition.py +0 -966
  131. pvlib/tests/test_spa.py +0 -454
  132. pvlib/tests/test_temperature.py +0 -470
  133. pvlib/tests/test_tools.py +0 -146
  134. pvlib/tests/test_tracking.py +0 -474
  135. pvlib/tests/test_transformer.py +0 -60
  136. pvlib-0.11.2.dist-info/RECORD +0 -191
  137. {pvlib-0.11.2.dist-info → pvlib-0.12.0.dist-info/licenses}/AUTHORS.md +0 -0
  138. {pvlib-0.11.2.dist-info → pvlib-0.12.0.dist-info/licenses}/LICENSE +0 -0
  139. {pvlib-0.11.2.dist-info → pvlib-0.12.0.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()