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