pvlib 0.9.5__py3-none-any.whl → 0.10.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 (71) hide show
  1. pvlib/__init__.py +3 -2
  2. pvlib/atmosphere.py +6 -171
  3. pvlib/bifacial/infinite_sheds.py +30 -267
  4. pvlib/bifacial/utils.py +225 -5
  5. pvlib/data/test_psm3_2017.csv +17521 -17521
  6. pvlib/data/test_read_psm3.csv +17522 -17522
  7. pvlib/data/test_read_pvgis_horizon.csv +49 -0
  8. pvlib/data/variables_style_rules.csv +3 -0
  9. pvlib/iam.py +17 -4
  10. pvlib/inverter.py +6 -1
  11. pvlib/iotools/__init__.py +7 -2
  12. pvlib/iotools/acis.py +516 -0
  13. pvlib/iotools/midc.py +4 -4
  14. pvlib/iotools/psm3.py +32 -31
  15. pvlib/iotools/pvgis.py +84 -28
  16. pvlib/iotools/sodapro.py +8 -6
  17. pvlib/iotools/srml.py +121 -18
  18. pvlib/iotools/surfrad.py +2 -2
  19. pvlib/iotools/tmy.py +146 -102
  20. pvlib/irradiance.py +151 -0
  21. pvlib/ivtools/sde.py +11 -7
  22. pvlib/ivtools/sdm.py +16 -10
  23. pvlib/ivtools/utils.py +6 -6
  24. pvlib/location.py +3 -2
  25. pvlib/modelchain.py +67 -70
  26. pvlib/pvsystem.py +160 -532
  27. pvlib/shading.py +41 -0
  28. pvlib/singlediode.py +215 -65
  29. pvlib/soiling.py +3 -3
  30. pvlib/spa.py +327 -368
  31. pvlib/spectrum/__init__.py +8 -2
  32. pvlib/spectrum/mismatch.py +335 -0
  33. pvlib/temperature.py +1 -8
  34. pvlib/tests/bifacial/test_infinite_sheds.py +0 -111
  35. pvlib/tests/bifacial/test_utils.py +101 -4
  36. pvlib/tests/conftest.py +0 -31
  37. pvlib/tests/iotools/test_acis.py +213 -0
  38. pvlib/tests/iotools/test_midc.py +6 -6
  39. pvlib/tests/iotools/test_psm3.py +3 -3
  40. pvlib/tests/iotools/test_pvgis.py +21 -14
  41. pvlib/tests/iotools/test_sodapro.py +1 -1
  42. pvlib/tests/iotools/test_srml.py +71 -6
  43. pvlib/tests/iotools/test_tmy.py +43 -8
  44. pvlib/tests/ivtools/test_sde.py +19 -17
  45. pvlib/tests/ivtools/test_sdm.py +9 -4
  46. pvlib/tests/test_atmosphere.py +6 -62
  47. pvlib/tests/test_iam.py +12 -0
  48. pvlib/tests/test_irradiance.py +40 -2
  49. pvlib/tests/test_location.py +1 -1
  50. pvlib/tests/test_modelchain.py +33 -76
  51. pvlib/tests/test_pvsystem.py +366 -201
  52. pvlib/tests/test_shading.py +28 -0
  53. pvlib/tests/test_singlediode.py +166 -30
  54. pvlib/tests/test_soiling.py +8 -7
  55. pvlib/tests/test_spa.py +6 -7
  56. pvlib/tests/test_spectrum.py +145 -1
  57. pvlib/tests/test_temperature.py +0 -7
  58. pvlib/tests/test_tools.py +25 -0
  59. pvlib/tests/test_tracking.py +0 -149
  60. pvlib/tools.py +26 -1
  61. pvlib/tracking.py +1 -269
  62. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/METADATA +1 -9
  63. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/RECORD +67 -68
  64. pvlib/forecast.py +0 -1211
  65. pvlib/iotools/ecmwf_macc.py +0 -312
  66. pvlib/tests/iotools/test_ecmwf_macc.py +0 -162
  67. pvlib/tests/test_forecast.py +0 -228
  68. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/AUTHORS.md +0 -0
  69. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/LICENSE +0 -0
  70. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/WHEEL +0 -0
  71. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,34 @@ import pytest
7
7
  from pvlib import shading
8
8
 
9
9
 
10
+ @pytest.fixture
11
+ def test_system():
12
+ syst = {'height': 1.0,
13
+ 'pitch': 2.,
14
+ 'surface_tilt': 30.,
15
+ 'surface_azimuth': 180.,
16
+ 'rotation': -30.} # rotation of right edge relative to horizontal
17
+ syst['gcr'] = 1.0 / syst['pitch']
18
+ return syst
19
+
20
+
21
+ def test__ground_angle(test_system):
22
+ ts = test_system
23
+ x = np.array([0., 0.5, 1.0])
24
+ angles = shading.ground_angle(
25
+ ts['surface_tilt'], ts['gcr'], x)
26
+ expected_angles = np.array([0., 5.866738789543952, 9.896090638982903])
27
+ assert np.allclose(angles, expected_angles)
28
+
29
+
30
+ def test__ground_angle_zero_gcr():
31
+ surface_tilt = 30.0
32
+ x = np.array([0.0, 0.5, 1.0])
33
+ angles = shading.ground_angle(surface_tilt, 0, x)
34
+ expected_angles = np.array([0, 0, 0])
35
+ assert np.allclose(angles, expected_angles)
36
+
37
+
10
38
  @pytest.fixture
11
39
  def surface_tilt():
12
40
  idx = pd.date_range('2019-01-01', freq='h', periods=3)
@@ -8,6 +8,7 @@ import scipy
8
8
  from pvlib import pvsystem
9
9
  from pvlib.singlediode import (bishop88_mpp, estimate_voc, VOLTAGE_BUILTIN,
10
10
  bishop88, bishop88_i_from_v, bishop88_v_from_i)
11
+ from pvlib._deprecation import pvlibDeprecationWarning
11
12
  import pytest
12
13
  from .conftest import DATA_DIR
13
14
 
@@ -25,22 +26,16 @@ def test_method_spr_e20_327(method, cec_module_spr_e20_327):
25
26
  I_L_ref=spr_e20_327['I_L_ref'], I_o_ref=spr_e20_327['I_o_ref'],
26
27
  R_sh_ref=spr_e20_327['R_sh_ref'], R_s=spr_e20_327['R_s'],
27
28
  EgRef=1.121, dEgdT=-0.0002677)
28
- il, io, rs, rsh, nnsvt = x
29
29
  pvs = pvsystem.singlediode(*x, method='lambertw')
30
30
  out = pvsystem.singlediode(*x, method=method)
31
- isc, voc, imp, vmp, pmp, ix, ixx = out.values()
32
- assert np.isclose(pvs['i_sc'], isc)
33
- assert np.isclose(pvs['v_oc'], voc)
34
- # the singlediode method doesn't actually get the MPP correct
35
- pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw')
36
- pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw')
37
- assert np.isclose(pvs_imp, imp)
38
- assert np.isclose(pvs_vmp, vmp)
39
- assert np.isclose(pvs['p_mp'], pmp)
40
- assert np.isclose(pvs['i_x'], ix)
41
- pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp)/2, io, il,
42
- method='lambertw')
43
- assert np.isclose(pvs_ixx, ixx)
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'])
44
39
 
45
40
 
46
41
  @pytest.mark.parametrize('method', ['brentq', 'newton'])
@@ -53,23 +48,16 @@ def test_newton_fs_495(method, cec_module_fs_495):
53
48
  I_L_ref=fs_495['I_L_ref'], I_o_ref=fs_495['I_o_ref'],
54
49
  R_sh_ref=fs_495['R_sh_ref'], R_s=fs_495['R_s'],
55
50
  EgRef=1.475, dEgdT=-0.0003)
56
- il, io, rs, rsh, nnsvt = x
57
- x += (101, )
58
51
  pvs = pvsystem.singlediode(*x, method='lambertw')
59
52
  out = pvsystem.singlediode(*x, method=method)
60
- isc, voc, imp, vmp, pmp, ix, ixx, i, v = out.values()
61
- assert np.isclose(pvs['i_sc'], isc)
62
- assert np.isclose(pvs['v_oc'], voc)
63
- # the singlediode method doesn't actually get the MPP correct
64
- pvs_imp = pvsystem.i_from_v(rsh, rs, nnsvt, vmp, io, il, method='lambertw')
65
- pvs_vmp = pvsystem.v_from_i(rsh, rs, nnsvt, imp, io, il, method='lambertw')
66
- assert np.isclose(pvs_imp, imp)
67
- assert np.isclose(pvs_vmp, vmp)
68
- assert np.isclose(pvs['p_mp'], pmp)
69
- assert np.isclose(pvs['i_x'], ix)
70
- pvs_ixx = pvsystem.i_from_v(rsh, rs, nnsvt, (voc + vmp)/2, io, il,
71
- method='lambertw')
72
- assert np.isclose(pvs_ixx, ixx)
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'])
73
61
 
74
62
 
75
63
  def build_precise_iv_curve_dataframe(file_csv, file_json):
@@ -180,6 +168,19 @@ def test_singlediode_precision(method, precise_iv_curves):
180
168
  assert np.allclose(pc['i_xx'], outs['i_xx'], atol=1e-6, rtol=0)
181
169
 
182
170
 
171
+ def test_singlediode_lambert_negative_voc():
172
+
173
+ # Those values result in a negative v_oc out of `_lambertw_v_from_i`
174
+ x = np.array([0., 1.480501e-11, 0.178, 8000., 1.797559])
175
+ outs = pvsystem.singlediode(*x, method='lambertw')
176
+ assert outs['v_oc'] == 0
177
+
178
+ # Testing for an array
179
+ x = np.array([x, x]).T
180
+ outs = pvsystem.singlediode(*x, method='lambertw')
181
+ assert np.array_equal(outs['v_oc'], [0, 0])
182
+
183
+
183
184
  @pytest.mark.parametrize('method', ['lambertw'])
184
185
  def test_ivcurve_pnts_precision(method, precise_iv_curves):
185
186
  """
@@ -189,7 +190,10 @@ def test_ivcurve_pnts_precision(method, precise_iv_curves):
189
190
  x, pc = precise_iv_curves
190
191
  pc_i, pc_v = np.stack(pc['Currents']), np.stack(pc['Voltages'])
191
192
  ivcurve_pnts = len(pc['Currents'][0])
192
- outs = pvsystem.singlediode(method=method, ivcurve_pnts=ivcurve_pnts, **x)
193
+
194
+ with pytest.warns(pvlibDeprecationWarning, match='ivcurve_pnts'):
195
+ outs = pvsystem.singlediode(method=method, ivcurve_pnts=ivcurve_pnts,
196
+ **x)
193
197
 
194
198
  assert np.allclose(pc_i, outs['i'], atol=1e-10, rtol=0)
195
199
  assert np.allclose(pc_v, outs['v'], atol=1e-10, rtol=0)
@@ -421,3 +425,135 @@ def test_pvsyst_breakdown(method, brk_params, recomb_params, poa, temp_cell,
421
425
 
422
426
  vsc_88 = bishop88_v_from_i(isc_88, *x, **y, method=method)
423
427
  assert np.isclose(vsc_88, 0.0, *tol)
428
+
429
+
430
+ @pytest.fixture
431
+ def bishop88_arguments():
432
+ pvsyst_fs_495 = get_pvsyst_fs_495()
433
+ # evaluate PVSyst model with thin-film recombination loss current
434
+ # at reference conditions
435
+ x = pvsystem.calcparams_pvsyst(
436
+ effective_irradiance=pvsyst_fs_495['irrad_ref'],
437
+ temp_cell=pvsyst_fs_495['temp_ref'],
438
+ alpha_sc=pvsyst_fs_495['alpha_sc'],
439
+ gamma_ref=pvsyst_fs_495['gamma_ref'],
440
+ mu_gamma=pvsyst_fs_495['mu_gamma'], I_L_ref=pvsyst_fs_495['I_L_ref'],
441
+ I_o_ref=pvsyst_fs_495['I_o_ref'], R_sh_ref=pvsyst_fs_495['R_sh_ref'],
442
+ R_sh_0=pvsyst_fs_495['R_sh_0'], R_sh_exp=pvsyst_fs_495['R_sh_exp'],
443
+ R_s=pvsyst_fs_495['R_s'],
444
+ cells_in_series=pvsyst_fs_495['cells_in_series'],
445
+ EgRef=pvsyst_fs_495['EgRef']
446
+ )
447
+ y = dict(d2mutau=pvsyst_fs_495['d2mutau'],
448
+ NsVbi=VOLTAGE_BUILTIN*pvsyst_fs_495['cells_in_series'])
449
+ # Convert (*x, **y) in a bishop88_.* call to dict of arguments
450
+ args_dict = {
451
+ 'photocurrent': x[0],
452
+ 'saturation_current': x[1],
453
+ 'resistance_series': x[2],
454
+ 'resistance_shunt': x[3],
455
+ 'nNsVth': x[4],
456
+ }
457
+ args_dict.update(y)
458
+ return args_dict
459
+
460
+
461
+ @pytest.mark.parametrize('method, method_kwargs', [
462
+ ('newton', {
463
+ 'tol': 1e-8,
464
+ 'rtol': 1e-8,
465
+ 'maxiter': 30,
466
+ }),
467
+ ('brentq', {
468
+ 'xtol': 1e-8,
469
+ 'rtol': 1e-8,
470
+ 'maxiter': 30,
471
+ })
472
+ ])
473
+ def test_bishop88_kwargs_transfer(method, method_kwargs, mocker,
474
+ bishop88_arguments):
475
+ """test method_kwargs modifying optimizer does not break anything"""
476
+ # patch method namespace at singlediode module namespace
477
+ optimizer_mock = mocker.patch('pvlib.singlediode.' + method)
478
+
479
+ # check kwargs passed to bishop_.* are a subset of the call args
480
+ # since they are called with more keyword arguments
481
+
482
+ bishop88_i_from_v(0, **bishop88_arguments, method=method,
483
+ method_kwargs=method_kwargs)
484
+ _, kwargs = optimizer_mock.call_args
485
+ assert method_kwargs.items() <= kwargs.items()
486
+
487
+ bishop88_v_from_i(0, **bishop88_arguments, method=method,
488
+ method_kwargs=method_kwargs)
489
+ _, kwargs = optimizer_mock.call_args
490
+ assert method_kwargs.items() <= kwargs.items()
491
+
492
+ bishop88_mpp(**bishop88_arguments, method=method,
493
+ method_kwargs=method_kwargs)
494
+ _, kwargs = optimizer_mock.call_args
495
+ assert method_kwargs.items() <= kwargs.items()
496
+
497
+
498
+ @pytest.mark.parametrize('method, method_kwargs', [
499
+ ('newton', {
500
+ 'tol': 1e-4,
501
+ 'rtol': 1e-4,
502
+ 'maxiter': 20,
503
+ '_inexistent_param': "0.01"
504
+ }),
505
+ ('brentq', {
506
+ 'xtol': 1e-4,
507
+ 'rtol': 1e-4,
508
+ 'maxiter': 20,
509
+ '_inexistent_param': "0.01"
510
+ })
511
+ ])
512
+ def test_bishop88_kwargs_fails(method, method_kwargs, bishop88_arguments):
513
+ """test invalid method_kwargs passed onto the optimizer fail"""
514
+
515
+ pytest.raises(TypeError, bishop88_i_from_v,
516
+ 0, **bishop88_arguments, method=method,
517
+ method_kwargs=method_kwargs)
518
+
519
+ pytest.raises(TypeError, bishop88_v_from_i,
520
+ 0, **bishop88_arguments, method=method,
521
+ method_kwargs=method_kwargs)
522
+
523
+ pytest.raises(TypeError, bishop88_mpp,
524
+ **bishop88_arguments, method=method,
525
+ method_kwargs=method_kwargs)
526
+
527
+
528
+ @pytest.mark.parametrize('method', ['newton', 'brentq'])
529
+ def test_bishop88_full_output_kwarg(method, bishop88_arguments):
530
+ """test call to bishop88_.* with full_output=True return values are ok"""
531
+ method_kwargs = {'full_output': True}
532
+
533
+ ret_val = bishop88_i_from_v(0, **bishop88_arguments, method=method,
534
+ method_kwargs=method_kwargs)
535
+ assert isinstance(ret_val, tuple) # ret_val must be a tuple
536
+ assert len(ret_val) == 2 # of two elements
537
+ assert isinstance(ret_val[0], float) # first one has bishop88 result
538
+ assert isinstance(ret_val[1], tuple) # second is output from optimizer
539
+ # any root finder returns at least 2 elements with full_output=True
540
+ assert len(ret_val[1]) >= 2
541
+
542
+ ret_val = bishop88_v_from_i(0, **bishop88_arguments, method=method,
543
+ method_kwargs=method_kwargs)
544
+ assert isinstance(ret_val, tuple) # ret_val must be a tuple
545
+ assert len(ret_val) == 2 # of two elements
546
+ assert isinstance(ret_val[0], float) # first one has bishop88 result
547
+ assert isinstance(ret_val[1], tuple) # second is output from optimizer
548
+ # any root finder returns at least 2 elements with full_output=True
549
+ assert len(ret_val[1]) >= 2
550
+
551
+ ret_val = bishop88_mpp(**bishop88_arguments, method=method,
552
+ method_kwargs=method_kwargs)
553
+ assert isinstance(ret_val, tuple) # ret_val must be a tuple
554
+ assert len(ret_val) == 2 # of two elements
555
+ assert isinstance(ret_val[0], tuple) # first one has bishop88 result
556
+ assert len(ret_val[0]) == 3 # of three elements (I,V,P)
557
+ assert isinstance(ret_val[1], tuple) # second is output from optimizer
558
+ # any root finder returns at least 2 elements with full_output=True
559
+ assert len(ret_val[1]) >= 2
@@ -92,7 +92,7 @@ def test_hsu_no_cleaning(rainfall_input, expected_output):
92
92
  tilt = 0.
93
93
  expected_no_cleaning = expected_output
94
94
 
95
- result = hsu(rainfall=rainfall, cleaning_threshold=10., tilt=tilt,
95
+ result = hsu(rainfall=rainfall, cleaning_threshold=10., surface_tilt=tilt,
96
96
  pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc,
97
97
  rain_accum_period=pd.Timedelta('1h'))
98
98
  assert_series_equal(result, expected_no_cleaning)
@@ -108,7 +108,7 @@ def test_hsu(rainfall_input, expected_output_2):
108
108
  tilt = 0.
109
109
 
110
110
  # three cleaning events at 4:00-6:00, 8:00-11:00, and 17:00-20:00
111
- result = hsu(rainfall=rainfall, cleaning_threshold=0.5, tilt=tilt,
111
+ result = hsu(rainfall=rainfall, cleaning_threshold=0.5, surface_tilt=tilt,
112
112
  pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc,
113
113
  rain_accum_period=pd.Timedelta('3h'))
114
114
 
@@ -120,8 +120,8 @@ def test_hsu_defaults(rainfall_input, expected_output_1):
120
120
  Test Soiling HSU function with default deposition velocity and default rain
121
121
  accumulation period.
122
122
  """
123
- result = hsu(rainfall=rainfall_input, cleaning_threshold=0.5, tilt=0.0,
124
- pm2_5=1.0e-2, pm10=2.0e-2)
123
+ result = hsu(rainfall=rainfall_input, cleaning_threshold=0.5,
124
+ surface_tilt=0.0, pm2_5=1.0e-2, pm10=2.0e-2)
125
125
  assert np.allclose(result.values, expected_output_1)
126
126
 
127
127
 
@@ -138,7 +138,7 @@ def test_hsu_variable_time_intervals(rainfall_input, expected_output_3):
138
138
  rain['new_time'] = rain.index + rain['mins_added']
139
139
  rain_var_times = rain.set_index('new_time').iloc[:, 0]
140
140
  result = hsu(
141
- rainfall=rain_var_times, cleaning_threshold=0.5, tilt=50.0,
141
+ rainfall=rain_var_times, cleaning_threshold=0.5, surface_tilt=50.0,
142
142
  pm2_5=1, pm10=2, depo_veloc=depo_veloc,
143
143
  rain_accum_period=pd.Timedelta('2h'))
144
144
  assert np.allclose(result, expected_output_3)
@@ -147,8 +147,9 @@ def test_hsu_variable_time_intervals(rainfall_input, expected_output_3):
147
147
  @pytest.fixture
148
148
  def greensboro_rain():
149
149
  # get TMY3 data with rain
150
- greensboro, _ = read_tmy3(DATA_DIR / '723170TYA.CSV', coerce_year=1990)
151
- return greensboro.Lprecipdepth
150
+ greensboro, _ = read_tmy3(DATA_DIR / '723170TYA.CSV', coerce_year=1990,
151
+ map_variables=True)
152
+ return greensboro['Lprecip depth (mm)']
152
153
 
153
154
 
154
155
  @pytest.fixture
pvlib/tests/test_spa.py CHANGED
@@ -154,13 +154,12 @@ class SpaBase:
154
154
  def test_moon_ascending_longitude(self):
155
155
  assert_almost_equal(X4, self.spa.moon_ascending_longitude(JCE), 6)
156
156
 
157
- def test_longitude_nutation(self):
158
- assert_almost_equal(dPsi, self.spa.longitude_nutation(
159
- JCE, X0, X1, X2, X3, X4), 6)
160
-
161
- def test_obliquity_nutation(self):
162
- assert_almost_equal(dEpsilon, self.spa.obliquity_nutation(
163
- JCE, X0, X1, X2, X3, X4), 6)
157
+ def test_longitude_obliquity_nutation(self):
158
+ out = np.empty((2,))
159
+ self.spa.longitude_obliquity_nutation(JCE, X0, X1, X2, X3, X4, out)
160
+ _dPsi, _dEpsilon = out[0], out[1]
161
+ assert_almost_equal(dPsi, _dPsi, 6)
162
+ assert_almost_equal(dEpsilon, _dEpsilon, 6)
164
163
 
165
164
  def test_mean_ecliptic_obliquity(self):
166
165
  assert_almost_equal(epsilon0, self.spa.mean_ecliptic_obliquity(JME), 6)
@@ -4,7 +4,7 @@ import pandas as pd
4
4
  import numpy as np
5
5
  from pvlib import spectrum
6
6
 
7
- from .conftest import DATA_DIR
7
+ from .conftest import DATA_DIR, assert_series_equal
8
8
 
9
9
  SPECTRL2_TEST_DATA = DATA_DIR / 'spectrl2_example_spectra.csv'
10
10
 
@@ -171,3 +171,147 @@ def test_calc_spectral_mismatch_field(spectrl2_data):
171
171
  mm = spectrum.calc_spectral_mismatch_field(sr, e_sun=e_sun)
172
172
  assert mm.index is e_sun.index
173
173
  assert_allclose(mm, expected, rtol=1e-6)
174
+
175
+
176
+ @pytest.mark.parametrize("module_type,expect", [
177
+ ('cdte', np.array(
178
+ [[ 0.99051020, 0.97640320, 0.93975028],
179
+ [ 1.02928735, 1.01881074, 0.98578821],
180
+ [ 1.04750335, 1.03814456, 1.00623986]])),
181
+ ('monosi', np.array(
182
+ [[ 0.97769770, 1.02043409, 1.03574032],
183
+ [ 0.98630905, 1.03055092, 1.04736262],
184
+ [ 0.98828494, 1.03299036, 1.05026561]])),
185
+ ('polysi', np.array(
186
+ [[ 0.97704080, 1.01705849, 1.02613202],
187
+ [ 0.98992828, 1.03173953, 1.04260662],
188
+ [ 0.99352435, 1.03588785, 1.04730718]])),
189
+ ('cigs', np.array(
190
+ [[ 0.97459190, 1.02821696, 1.05067895],
191
+ [ 0.97529378, 1.02967497, 1.05289307],
192
+ [ 0.97269159, 1.02730558, 1.05075651]])),
193
+ ('asi', np.array(
194
+ [[ 1.05552750, 0.87707583, 0.72243772],
195
+ [ 1.11225204, 0.93665901, 0.78487953],
196
+ [ 1.14555295, 0.97084011, 0.81994083]]))
197
+ ])
198
+ def test_spectral_factor_firstsolar(module_type, expect):
199
+ ams = np.array([1, 3, 5])
200
+ pws = np.array([1, 3, 5])
201
+ ams, pws = np.meshgrid(ams, pws)
202
+ out = spectrum.spectral_factor_firstsolar(pws, ams, module_type)
203
+ assert_allclose(out, expect, atol=0.001)
204
+
205
+
206
+ def test_spectral_factor_firstsolar_supplied():
207
+ # use the cdte coeffs
208
+ coeffs = (0.87102, -0.040543, -0.00929202, 0.10052, 0.073062, -0.0034187)
209
+ out = spectrum.spectral_factor_firstsolar(1, 1, coefficients=coeffs)
210
+ expected = 0.99134828
211
+ assert_allclose(out, expected, atol=1e-3)
212
+
213
+
214
+ def test_spectral_factor_firstsolar_ambiguous():
215
+ with pytest.raises(TypeError):
216
+ spectrum.spectral_factor_firstsolar(1, 1)
217
+
218
+
219
+ def test_spectral_factor_firstsolar_ambiguous_both():
220
+ # use the cdte coeffs
221
+ coeffs = (0.87102, -0.040543, -0.00929202, 0.10052, 0.073062, -0.0034187)
222
+ with pytest.raises(TypeError):
223
+ spectrum.spectral_factor_firstsolar(1, 1, 'cdte', coefficients=coeffs)
224
+
225
+
226
+ def test_spectral_factor_firstsolar_large_airmass():
227
+ # test that airmass > 10 is treated same as airmass==10
228
+ m_eq10 = spectrum.spectral_factor_firstsolar(1, 10, 'monosi')
229
+ m_gt10 = spectrum.spectral_factor_firstsolar(1, 15, 'monosi')
230
+ assert_allclose(m_eq10, m_gt10)
231
+
232
+
233
+ def test_spectral_factor_firstsolar_low_airmass():
234
+ with pytest.warns(UserWarning, match='Exceptionally low air mass'):
235
+ _ = spectrum.spectral_factor_firstsolar(1, 0.1, 'monosi')
236
+
237
+
238
+ def test_spectral_factor_firstsolar_range():
239
+ with pytest.warns(UserWarning, match='Exceptionally high pw values'):
240
+ out = spectrum.spectral_factor_firstsolar(np.array([.1, 3, 10]),
241
+ np.array([1, 3, 5]),
242
+ module_type='monosi')
243
+ expected = np.array([0.96080878, 1.03055092, np.nan])
244
+ assert_allclose(out, expected, atol=1e-3)
245
+ with pytest.warns(UserWarning, match='Exceptionally high pw values'):
246
+ out = spectrum.spectral_factor_firstsolar(6, 1.5,
247
+ max_precipitable_water=5,
248
+ module_type='monosi')
249
+ with pytest.warns(UserWarning, match='Exceptionally low pw values'):
250
+ out = spectrum.spectral_factor_firstsolar(np.array([0, 3, 8]),
251
+ np.array([1, 3, 5]),
252
+ module_type='monosi')
253
+ expected = np.array([0.96080878, 1.03055092, 1.04932727])
254
+ assert_allclose(out, expected, atol=1e-3)
255
+ with pytest.warns(UserWarning, match='Exceptionally low pw values'):
256
+ out = spectrum.spectral_factor_firstsolar(0.2, 1.5,
257
+ min_precipitable_water=1,
258
+ module_type='monosi')
259
+
260
+
261
+ @pytest.mark.parametrize('airmass,expected', [
262
+ (1.5, 1.00028714375),
263
+ (np.array([[10, np.nan]]), np.array([[0.999535, 0]])),
264
+ (pd.Series([5]), pd.Series([1.0387675]))
265
+ ])
266
+ def test_spectral_factor_sapm(sapm_module_params, airmass, expected):
267
+
268
+ out = spectrum.spectral_factor_sapm(airmass, sapm_module_params)
269
+
270
+ if isinstance(airmass, pd.Series):
271
+ assert_series_equal(out, expected, check_less_precise=4)
272
+ else:
273
+ assert_allclose(out, expected, atol=1e-4)
274
+
275
+
276
+ @pytest.mark.parametrize("module_type,expected", [
277
+ ('asi', np.array([0.9108, 0.9897, 0.9707, 1.0265, 1.0798, 0.9537])),
278
+ ('perovskite', np.array([0.9422, 0.9932, 0.9868, 1.0183, 1.0604, 0.9737])),
279
+ ('cdte', np.array([0.9824, 1.0000, 1.0065, 1.0117, 1.042, 0.9979])),
280
+ ('multisi', np.array([0.9907, 0.9979, 1.0203, 1.0081, 1.0058, 1.019])),
281
+ ('monosi', np.array([0.9935, 0.9987, 1.0264, 1.0074, 0.9999, 1.0263])),
282
+ ('cigs', np.array([1.0014, 1.0011, 1.0270, 1.0082, 1.0029, 1.026])),
283
+ ])
284
+ def test_spectral_factor_caballero(module_type, expected):
285
+ ams = np.array([3.0, 1.5, 3.0, 1.5, 1.5, 3.0])
286
+ aods = np.array([1.0, 1.0, 0.02, 0.02, 0.08, 0.08])
287
+ pws = np.array([1.42, 1.42, 1.42, 1.42, 4.0, 1.0])
288
+ out = spectrum.spectral_factor_caballero(pws, ams, aods,
289
+ module_type=module_type)
290
+ assert np.allclose(expected, out, atol=1e-3)
291
+
292
+
293
+ def test_spectral_factor_caballero_supplied():
294
+ # use the cdte coeffs
295
+ coeffs = (
296
+ 1.0044, 0.0095, -0.0037, 0.0002, 0.0000, -0.0046,
297
+ -0.0182, 0, 0.0095, 0.0068, 0, 1)
298
+ out = spectrum.spectral_factor_caballero(1, 1, 1, coefficients=coeffs)
299
+ expected = 1.0021964
300
+ assert_allclose(out, expected, atol=1e-3)
301
+
302
+
303
+ def test_spectral_factor_caballero_supplied_redundant():
304
+ # Error when specifying both module_type and coefficients
305
+ coeffs = (
306
+ 1.0044, 0.0095, -0.0037, 0.0002, 0.0000, -0.0046,
307
+ -0.0182, 0, 0.0095, 0.0068, 0, 1)
308
+ with pytest.raises(ValueError):
309
+ spectrum.spectral_factor_caballero(1, 1, 1, module_type='cdte',
310
+ coefficients=coeffs)
311
+
312
+
313
+ def test_spectral_factor_caballero_supplied_ambiguous():
314
+ # Error when specifying neither module_type nor coefficients
315
+ with pytest.raises(ValueError):
316
+ spectrum.spectral_factor_caballero(1, 1, 1, module_type=None,
317
+ coefficients=None)
@@ -99,13 +99,6 @@ def test_pvsyst_cell_series():
99
99
  assert_series_equal(expected, result)
100
100
 
101
101
 
102
- def test_pvsyst_cell_eta_m_deprecated():
103
- with pytest.warns(pvlibDeprecationWarning):
104
- result = temperature.pvsyst_cell(900, 20, wind_speed=5.0, u_c=23.5,
105
- u_v=6.25, eta_m=0.1)
106
- assert_allclose(result, 33.315, 0.001)
107
-
108
-
109
102
  def test_faiman_default():
110
103
  result = temperature.faiman(900, 20, 5)
111
104
  assert_allclose(result, 35.203, atol=0.001)
pvlib/tests/test_tools.py CHANGED
@@ -2,6 +2,7 @@ import pytest
2
2
 
3
3
  from pvlib import tools
4
4
  import numpy as np
5
+ import pandas as pd
5
6
 
6
7
 
7
8
  @pytest.mark.parametrize('keys, input_dict, expected', [
@@ -95,3 +96,27 @@ def test_degrees_to_index_1():
95
96
  'latitude' or 'longitude' is passed."""
96
97
  with pytest.raises(IndexError): # invalid value for coordinate argument
97
98
  tools._degrees_to_index(degrees=22.0, coordinate='width')
99
+
100
+
101
+ @pytest.mark.parametrize('args, args_idx', [
102
+ # no pandas.Series or pandas.DataFrame args
103
+ ((1,), None),
104
+ (([1],), None),
105
+ ((np.array(1),), None),
106
+ ((np.array([1]),), None),
107
+ # has pandas.Series or pandas.DataFrame args
108
+ ((pd.DataFrame([1], index=[1]),), 0),
109
+ ((pd.Series([1], index=[1]),), 0),
110
+ ((1, pd.Series([1], index=[1]),), 1),
111
+ ((1, pd.DataFrame([1], index=[1]),), 1),
112
+ # first pandas.Series or pandas.DataFrame is used
113
+ ((1, pd.Series([1], index=[1]), pd.DataFrame([2], index=[2]),), 1),
114
+ ((1, pd.DataFrame([1], index=[1]), pd.Series([2], index=[2]),), 1),
115
+ ])
116
+ def test_get_pandas_index(args, args_idx):
117
+ index = tools.get_pandas_index(*args)
118
+
119
+ if args_idx is None:
120
+ assert index is None
121
+ else:
122
+ pd.testing.assert_index_equal(args[args_idx].index, index)