pvlib 0.10.3__py3-none-any.whl → 0.10.5__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 (53) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/bifacial/utils.py +2 -1
  3. pvlib/clearsky.py +7 -8
  4. pvlib/iam.py +3 -3
  5. pvlib/inverter.py +3 -3
  6. pvlib/iotools/__init__.py +2 -0
  7. pvlib/iotools/solargis.py +214 -0
  8. pvlib/iotools/solcast.py +2 -7
  9. pvlib/iotools/solrad.py +121 -23
  10. pvlib/iotools/srml.py +12 -12
  11. pvlib/iotools/surfrad.py +2 -2
  12. pvlib/irradiance.py +28 -22
  13. pvlib/location.py +3 -1
  14. pvlib/modelchain.py +10 -9
  15. pvlib/pvarray.py +127 -0
  16. pvlib/pvsystem.py +52 -43
  17. pvlib/scaling.py +4 -2
  18. pvlib/shading.py +110 -0
  19. pvlib/singlediode.py +37 -9
  20. pvlib/snow.py +3 -1
  21. pvlib/solarposition.py +38 -30
  22. pvlib/spa.py +3 -11
  23. pvlib/spectrum/mismatch.py +2 -1
  24. pvlib/temperature.py +3 -2
  25. pvlib/tests/bifacial/test_utils.py +6 -5
  26. pvlib/tests/conftest.py +13 -14
  27. pvlib/tests/iotools/test_sodapro.py +2 -1
  28. pvlib/tests/iotools/test_solargis.py +68 -0
  29. pvlib/tests/iotools/test_solcast.py +2 -2
  30. pvlib/tests/iotools/test_solrad.py +58 -7
  31. pvlib/tests/iotools/test_srml.py +7 -14
  32. pvlib/tests/test_clearsky.py +1 -1
  33. pvlib/tests/test_irradiance.py +24 -8
  34. pvlib/tests/test_location.py +1 -1
  35. pvlib/tests/test_modelchain.py +37 -26
  36. pvlib/tests/test_pvarray.py +25 -0
  37. pvlib/tests/test_pvsystem.py +76 -104
  38. pvlib/tests/test_shading.py +130 -11
  39. pvlib/tests/test_singlediode.py +68 -10
  40. pvlib/tests/test_snow.py +1 -1
  41. pvlib/tests/test_solarposition.py +121 -7
  42. pvlib/tests/test_spa.py +5 -15
  43. pvlib/tests/test_temperature.py +4 -4
  44. pvlib/tests/test_tracking.py +2 -2
  45. pvlib/tracking.py +8 -38
  46. pvlib/version.py +1 -5
  47. {pvlib-0.10.3.dist-info → pvlib-0.10.5.dist-info}/METADATA +9 -33
  48. {pvlib-0.10.3.dist-info → pvlib-0.10.5.dist-info}/RECORD +52 -51
  49. {pvlib-0.10.3.dist-info → pvlib-0.10.5.dist-info}/WHEEL +1 -1
  50. pvlib/spa_c_files/SPA_NOTICE.md +0 -39
  51. {pvlib-0.10.3.dist-info → pvlib-0.10.5.dist-info}/AUTHORS.md +0 -0
  52. {pvlib-0.10.3.dist-info → pvlib-0.10.5.dist-info}/LICENSE +0 -0
  53. {pvlib-0.10.3.dist-info → pvlib-0.10.5.dist-info}/top_level.txt +0 -0
@@ -14,11 +14,12 @@ def test_read_srml():
14
14
  srml.read_srml(srml_testfile)
15
15
 
16
16
 
17
- @pytest.mark.skip(reason="SRML server is undergoing maintenance as of 12-2023")
18
17
  @pytest.mark.remote_data
19
18
  @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
20
19
  def test_read_srml_remote():
21
- srml.read_srml('http://solardat.uoregon.edu/download/Archive/EUPO1801.txt')
20
+ srml.read_srml(
21
+ 'http://solardata.uoregon.edu/download/Archive/EUPO1801.txt'
22
+ )
22
23
 
23
24
 
24
25
  def test_read_srml_columns_exist():
@@ -47,11 +48,10 @@ def test_read_srml_nans_exist():
47
48
  assert data['dni_0_flag'].iloc[1119] == 99
48
49
 
49
50
 
50
- @pytest.mark.skip(reason="SRML server is undergoing maintenance as of 12-2023")
51
51
  @pytest.mark.parametrize('url,year,month', [
52
- ('http://solardat.uoregon.edu/download/Archive/EUPO1801.txt',
52
+ ('http://solardata.uoregon.edu/download/Archive/EUPO1801.txt',
53
53
  2018, 1),
54
- ('http://solardat.uoregon.edu/download/Archive/EUPO1612.txt',
54
+ ('http://solardata.uoregon.edu/download/Archive/EUPO1612.txt',
55
55
  2016, 12),
56
56
  ])
57
57
  @pytest.mark.remote_data
@@ -78,30 +78,27 @@ def test__map_columns(column, expected):
78
78
  assert srml._map_columns(column) == expected
79
79
 
80
80
 
81
- @pytest.mark.skip(reason="SRML server is undergoing maintenance as of 12-2023")
82
81
  @pytest.mark.remote_data
83
82
  @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
84
83
  def test_get_srml():
85
- url = 'http://solardat.uoregon.edu/download/Archive/EUPO1801.txt'
84
+ url = 'http://solardata.uoregon.edu/download/Archive/EUPO1801.txt'
86
85
  file_data = srml.read_srml(url)
87
86
  requested, _ = srml.get_srml(station='EU', start='2018-01-01',
88
87
  end='2018-01-31')
89
88
  assert_frame_equal(file_data, requested)
90
89
 
91
90
 
92
- @pytest.mark.skip(reason="SRML server is undergoing maintenance as of 12-2023")
93
91
  @fail_on_pvlib_version('0.11')
94
92
  @pytest.mark.remote_data
95
93
  @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
96
94
  def test_read_srml_month_from_solardat():
97
- url = 'http://solardat.uoregon.edu/download/Archive/EUPO1801.txt'
95
+ url = 'http://solardata.uoregon.edu/download/Archive/EUPO1801.txt'
98
96
  file_data = srml.read_srml(url)
99
97
  with pytest.warns(pvlibDeprecationWarning, match='get_srml instead'):
100
98
  requested = srml.read_srml_month_from_solardat('EU', 2018, 1)
101
99
  assert file_data.equals(requested)
102
100
 
103
101
 
104
- @pytest.mark.skip(reason="SRML server is undergoing maintenance as of 12-2023")
105
102
  @fail_on_pvlib_version('0.11')
106
103
  @pytest.mark.remote_data
107
104
  @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
@@ -117,7 +114,6 @@ def test_15_minute_dt_index():
117
114
  assert (data.index[3::4].minute == 45).all()
118
115
 
119
116
 
120
- @pytest.mark.skip(reason="SRML server is undergoing maintenance as of 12-2023")
121
117
  @fail_on_pvlib_version('0.11')
122
118
  @pytest.mark.remote_data
123
119
  @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
@@ -133,7 +129,6 @@ def test_hourly_dt_index():
133
129
  assert (data.index.minute == 0).all()
134
130
 
135
131
 
136
- @pytest.mark.skip(reason="SRML server is undergoing maintenance as of 12-2023")
137
132
  @pytest.mark.remote_data
138
133
  @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
139
134
  def test_get_srml_hourly():
@@ -144,7 +139,6 @@ def test_get_srml_hourly():
144
139
  assert_index_equal(data.index, expected_index)
145
140
 
146
141
 
147
- @pytest.mark.skip(reason="SRML server is undergoing maintenance as of 12-2023")
148
142
  @pytest.mark.remote_data
149
143
  @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
150
144
  def test_get_srml_minute():
@@ -162,7 +156,6 @@ def test_get_srml_minute():
162
156
  assert meta['filenames'] == ['EUPO1801.txt']
163
157
 
164
158
 
165
- @pytest.mark.skip(reason="SRML server is undergoing maintenance as of 12-2023")
166
159
  @pytest.mark.remote_data
167
160
  @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
168
161
  def test_get_srml_nonexisting_month_warning():
@@ -745,7 +745,7 @@ def test__calc_stats(detect_clearsky_helper_data):
745
745
  def test_bird():
746
746
  """Test Bird/Hulstrom Clearsky Model"""
747
747
  times = pd.date_range(start='1/1/2015 0:00', end='12/31/2015 23:00',
748
- freq='H')
748
+ freq='h')
749
749
  tz = -7 # test timezone
750
750
  gmt_tz = pytz.timezone('Etc/GMT%+d' % -(tz))
751
751
  times = times.tz_localize(gmt_tz) # set timezone
@@ -27,7 +27,7 @@ from .conftest import (
27
27
  @pytest.fixture
28
28
  def times():
29
29
  # must include night values
30
- return pd.date_range(start='20140624', freq='6H', periods=4,
30
+ return pd.date_range(start='20140624', freq='6h', periods=4,
31
31
  tz='US/Arizona')
32
32
 
33
33
 
@@ -349,7 +349,7 @@ def test_perez_driesse_components(irrad_data, ephem_data, dni_et,
349
349
 
350
350
 
351
351
  def test_perez_negative_horizon():
352
- times = pd.date_range(start='20190101 11:30:00', freq='1H',
352
+ times = pd.date_range(start='20190101 11:30:00', freq='1h',
353
353
  periods=5, tz='US/Central')
354
354
 
355
355
  # Avoid test dependencies on functionality not being tested by hard-coding
@@ -711,7 +711,7 @@ def test_dirint_value():
711
711
 
712
712
 
713
713
  def test_dirint_nans():
714
- times = pd.date_range(start='2014-06-24T12-0700', periods=5, freq='6H')
714
+ times = pd.date_range(start='2014-06-24T12-0700', periods=5, freq='6h')
715
715
  ghi = pd.Series([np.nan, 1038.62, 1038.62, 1038.62, 1038.62], index=times)
716
716
  zenith = pd.Series([10.567, np.nan, 10.567, 10.567, 10.567], index=times)
717
717
  pressure = pd.Series([93193., 93193., np.nan, 93193., 93193.], index=times)
@@ -782,7 +782,7 @@ def test_dirint_min_cos_zenith_max_zenith():
782
782
  assert_series_equal(out, expected, check_less_precise=True)
783
783
 
784
784
 
785
- def test_ghi_from_poa_driesse():
785
+ def test_ghi_from_poa_driesse(mocker):
786
786
  # inputs copied from test_gti_dirint
787
787
  times = pd.DatetimeIndex(
788
788
  ['2014-06-24T06-0700', '2014-06-24T09-0700', '2014-06-24T12-0700'])
@@ -825,6 +825,22 @@ def test_ghi_from_poa_driesse():
825
825
  expected = [0, -1, 0]
826
826
  assert_allclose(expected, niter)
827
827
 
828
+ # test xtol argument
829
+ poa_global = pd.Series([20, 300, 1000], index=times)
830
+ # test exception
831
+ xtol = -3.14159 # negative value raises exception in scipy.optimize.bisect
832
+ with pytest.raises(ValueError, match=rf"xtol too small \({xtol:g} <= 0\)"):
833
+ output = irradiance.ghi_from_poa_driesse_2023(
834
+ surface_tilt, surface_azimuth, zenith, azimuth,
835
+ poa_global, dni_extra=1366.1, xtol=xtol)
836
+ # test propagation
837
+ xtol = 3.141592
838
+ bisect_spy = mocker.spy(irradiance, "bisect")
839
+ output = irradiance.ghi_from_poa_driesse_2023(
840
+ surface_tilt, surface_azimuth, zenith, azimuth,
841
+ poa_global, dni_extra=1366.1, xtol=xtol)
842
+ assert bisect_spy.call_args[1]["xtol"] == xtol
843
+
828
844
 
829
845
  def test_gti_dirint():
830
846
  times = pd.DatetimeIndex(
@@ -1210,7 +1226,7 @@ def test_clearsky_index():
1210
1226
  expected = 0.01
1211
1227
  assert_allclose(out, expected, atol=0.001)
1212
1228
  # series
1213
- times = pd.date_range(start='20180601', periods=2, freq='12H')
1229
+ times = pd.date_range(start='20180601', periods=2, freq='12h')
1214
1230
  ghi_measured = pd.Series([100, 500], index=times)
1215
1231
  ghi_modeled = pd.Series([500, 1000], index=times)
1216
1232
  out = irradiance.clearsky_index(ghi_measured, ghi_modeled)
@@ -1266,7 +1282,7 @@ def test_clearness_index():
1266
1282
  expected = 0.725
1267
1283
  assert_allclose(out, expected, atol=0.001)
1268
1284
  # series
1269
- times = pd.date_range(start='20180601', periods=2, freq='12H')
1285
+ times = pd.date_range(start='20180601', periods=2, freq='12h')
1270
1286
  ghi = pd.Series([0, 1000], index=times)
1271
1287
  solar_zenith = pd.Series([90, 0], index=times)
1272
1288
  extra_radiation = pd.Series([1360, 1400], index=times)
@@ -1300,7 +1316,7 @@ def test_clearness_index_zenith_independent(airmass_kt):
1300
1316
  expected = 0.443
1301
1317
  assert_allclose(out, expected, atol=0.001)
1302
1318
  # series
1303
- times = pd.date_range(start='20180601', periods=2, freq='12H')
1319
+ times = pd.date_range(start='20180601', periods=2, freq='12h')
1304
1320
  clearness_index = pd.Series([0, .5], index=times)
1305
1321
  airmass = pd.Series([np.nan, 2], index=times)
1306
1322
  out = irradiance.clearness_index_zenith_independent(clearness_index,
@@ -1311,7 +1327,7 @@ def test_clearness_index_zenith_independent(airmass_kt):
1311
1327
 
1312
1328
  def test_complete_irradiance():
1313
1329
  # Generate dataframe to test on
1314
- times = pd.date_range('2010-07-05 7:00:00-0700', periods=2, freq='H')
1330
+ times = pd.date_range('2010-07-05 7:00:00-0700', periods=2, freq='h')
1315
1331
  i = pd.DataFrame({'ghi': [372.103976116, 497.087579068],
1316
1332
  'dhi': [356.543700, 465.44400],
1317
1333
  'dni': [49.63565561689957, 62.10624908037814]},
@@ -74,7 +74,7 @@ def test_location_print_pytz():
74
74
  def times():
75
75
  return pd.date_range(start='20160101T0600-0700',
76
76
  end='20160101T1800-0700',
77
- freq='3H')
77
+ freq='3h')
78
78
 
79
79
 
80
80
  def test_get_clearsky(mocker, times):
@@ -268,7 +268,7 @@ def location():
268
268
 
269
269
  @pytest.fixture
270
270
  def weather():
271
- times = pd.date_range('20160101 1200-0700', periods=2, freq='6H')
271
+ times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
272
272
  weather = pd.DataFrame({'ghi': [500, 0], 'dni': [800, 0], 'dhi': [100, 0]},
273
273
  index=times)
274
274
  return weather
@@ -350,7 +350,7 @@ def test_with_pvwatts(pvwatts_dc_pvwatts_ac_system, location, weather):
350
350
 
351
351
  def test_run_model_with_irradiance(sapm_dc_snl_ac_system, location):
352
352
  mc = ModelChain(sapm_dc_snl_ac_system, location)
353
- times = pd.date_range('20160101 1200-0700', periods=2, freq='6H')
353
+ times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
354
354
  irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
355
355
  index=times)
356
356
  ac = mc.run_model(irradiance).results.ac
@@ -417,7 +417,7 @@ def test_run_model_from_irradiance_arrays_no_loss(
417
417
  spectral_model='no_loss',
418
418
  losses_model='no_loss'
419
419
  )
420
- times = pd.date_range('20160101 1200-0700', periods=2, freq='6H')
420
+ times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
421
421
  irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
422
422
  index=times)
423
423
  mc_one.run_model(irradiance)
@@ -457,7 +457,7 @@ def test_run_model_from_irradiance_arrays_no_loss_input_type(
457
457
  spectral_model='no_loss',
458
458
  losses_model='no_loss'
459
459
  )
460
- times = pd.date_range('20160101 1200-0700', periods=2, freq='6H')
460
+ times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
461
461
  irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
462
462
  index=times)
463
463
  mc_one.run_model(irradiance)
@@ -487,7 +487,7 @@ def test_ModelChain_invalid_inverter_params_arrays(
487
487
  def test_prepare_inputs_multi_weather(
488
488
  sapm_dc_snl_ac_system_Array, location, input_type):
489
489
  times = pd.date_range(start='20160101 1200-0700',
490
- end='20160101 1800-0700', freq='6H')
490
+ end='20160101 1800-0700', freq='6h')
491
491
  mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
492
492
  weather = pd.DataFrame({'ghi': 1, 'dhi': 1, 'dni': 1},
493
493
  index=times)
@@ -502,7 +502,7 @@ def test_prepare_inputs_multi_weather(
502
502
  def test_prepare_inputs_albedo_in_weather(
503
503
  sapm_dc_snl_ac_system_Array, location, input_type):
504
504
  times = pd.date_range(start='20160101 1200-0700',
505
- end='20160101 1800-0700', freq='6H')
505
+ end='20160101 1800-0700', freq='6h')
506
506
  mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
507
507
  weather = pd.DataFrame({'ghi': 1, 'dhi': 1, 'dni': 1, 'albedo': 0.5},
508
508
  index=times)
@@ -564,14 +564,14 @@ def test_ModelChain_times_error_arrays(sapm_dc_snl_ac_system_Array, location):
564
564
  error_str = r"Input DataFrames must have same index\."
565
565
  mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
566
566
  irradiance = {'ghi': [1, 2], 'dhi': [1, 2], 'dni': [1, 2]}
567
- times_one = pd.date_range(start='1/1/2020', freq='6H', periods=2)
568
- times_two = pd.date_range(start='1/1/2020 00:15', freq='6H', periods=2)
567
+ times_one = pd.date_range(start='1/1/2020', freq='6h', periods=2)
568
+ times_two = pd.date_range(start='1/1/2020 00:15', freq='6h', periods=2)
569
569
  weather_one = pd.DataFrame(irradiance, index=times_one)
570
570
  weather_two = pd.DataFrame(irradiance, index=times_two)
571
571
  with pytest.raises(ValueError, match=error_str):
572
572
  mc.prepare_inputs((weather_one, weather_two))
573
573
  # test with overlapping, but differently sized indices.
574
- times_three = pd.date_range(start='1/1/2020', freq='6H', periods=3)
574
+ times_three = pd.date_range(start='1/1/2020', freq='6h', periods=3)
575
575
  irradiance_three = irradiance
576
576
  irradiance_three['ghi'].append(3)
577
577
  irradiance_three['dhi'].append(3)
@@ -588,7 +588,7 @@ def test_ModelChain_times_arrays(sapm_dc_snl_ac_system_Array, location):
588
588
  mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
589
589
  irradiance_one = {'ghi': [1, 2], 'dhi': [1, 2], 'dni': [1, 2]}
590
590
  irradiance_two = {'ghi': [2, 1], 'dhi': [2, 1], 'dni': [2, 1]}
591
- times = pd.date_range(start='1/1/2020', freq='6H', periods=2)
591
+ times = pd.date_range(start='1/1/2020', freq='6h', periods=2)
592
592
  weather_one = pd.DataFrame(irradiance_one, index=times)
593
593
  weather_two = pd.DataFrame(irradiance_two, index=times)
594
594
  mc.prepare_inputs((weather_one, weather_two))
@@ -617,7 +617,7 @@ def test_run_model_arrays_weather(sapm_dc_snl_ac_system_same_arrays,
617
617
  'pvwatts': pvwatts_dc_pvwatts_ac_system_arrays}
618
618
  mc = ModelChain(system[ac_model], location, aoi_model='no_loss',
619
619
  spectral_model='no_loss')
620
- times = pd.date_range('20200101 1200-0700', periods=2, freq='2H')
620
+ times = pd.date_range('20200101 1200-0700', periods=2, freq='2h')
621
621
  weather_one = pd.DataFrame({'dni': [900, 800],
622
622
  'ghi': [600, 500],
623
623
  'dhi': [150, 100]},
@@ -634,7 +634,7 @@ def test_run_model_arrays_weather(sapm_dc_snl_ac_system_same_arrays,
634
634
  def test_run_model_perez(sapm_dc_snl_ac_system, location):
635
635
  mc = ModelChain(sapm_dc_snl_ac_system, location,
636
636
  transposition_model='perez')
637
- times = pd.date_range('20160101 1200-0700', periods=2, freq='6H')
637
+ times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
638
638
  irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
639
639
  index=times)
640
640
  ac = mc.run_model(irradiance).results.ac
@@ -648,7 +648,7 @@ def test_run_model_gueymard_perez(sapm_dc_snl_ac_system, location):
648
648
  mc = ModelChain(sapm_dc_snl_ac_system, location,
649
649
  airmass_model='gueymard1993',
650
650
  transposition_model='perez')
651
- times = pd.date_range('20160101 1200-0700', periods=2, freq='6H')
651
+ times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
652
652
  irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
653
653
  index=times)
654
654
  ac = mc.run_model(irradiance).results.ac
@@ -812,7 +812,7 @@ def test_prepare_inputs_from_poa_arrays_different_indices(
812
812
  mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
813
813
  poa = pd.concat([weather, total_irrad], axis=1)
814
814
  with pytest.raises(ValueError, match=error_str):
815
- mc.prepare_inputs_from_poa((poa, poa.shift(periods=1, freq='6H')))
815
+ mc.prepare_inputs_from_poa((poa, poa.shift(periods=1, freq='6h')))
816
816
 
817
817
 
818
818
  def test_prepare_inputs_from_poa_arrays_missing_column(
@@ -1078,7 +1078,7 @@ def test_run_model_from_effective_irradiance_arrays_error(
1078
1078
  with pytest.raises(ValueError,
1079
1079
  match=r"Input DataFrames must have same index\."):
1080
1080
  mc.run_model_from_effective_irradiance(
1081
- (data, data.shift(periods=1, freq='6H'))
1081
+ (data, data.shift(periods=1, freq='6h'))
1082
1082
  )
1083
1083
 
1084
1084
 
@@ -1305,6 +1305,20 @@ def test_temperature_model_inconsistent(location, sapm_dc_snl_ac_system):
1305
1305
  spectral_model='no_loss', temperature_model='pvsyst')
1306
1306
 
1307
1307
 
1308
+ def test_temperature_model_not_specified():
1309
+ location = Location(latitude=32.2, longitude=-110.9)
1310
+ arrays = [pvsystem.Array(pvsystem.FixedMount(),
1311
+ module_parameters={'pdc0': 1, 'gamma_pdc': 0})]
1312
+ system = pvsystem.PVSystem(arrays,
1313
+ temperature_model_parameters={'u0': 1, 'u1': 1},
1314
+ inverter_parameters={'pdc0': 1})
1315
+ with pytest.raises(ValueError,
1316
+ match='Could not infer temperature model from '
1317
+ 'ModelChain.system.'):
1318
+ _ = ModelChain(system, location,
1319
+ aoi_model='no_loss', spectral_model='no_loss')
1320
+
1321
+
1308
1322
  def test_dc_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather,
1309
1323
  mocker):
1310
1324
  m = mocker.spy(sys.modules[__name__], 'poadc')
@@ -1776,7 +1790,7 @@ def test_ModelChain_no_extra_kwargs(sapm_dc_snl_ac_system, location):
1776
1790
  def test_basic_chain_alt_az(sam_data, cec_inverter_parameters,
1777
1791
  sapm_temperature_cs5p_220m):
1778
1792
  times = pd.date_range(start='20160101 1200-0700',
1779
- end='20160101 1800-0700', freq='6H')
1793
+ end='20160101 1800-0700', freq='6h')
1780
1794
  latitude = 32.2
1781
1795
  longitude = -111
1782
1796
  surface_tilt = 0
@@ -1798,7 +1812,7 @@ def test_basic_chain_alt_az(sam_data, cec_inverter_parameters,
1798
1812
  def test_basic_chain_altitude_pressure(sam_data, cec_inverter_parameters,
1799
1813
  sapm_temperature_cs5p_220m):
1800
1814
  times = pd.date_range(start='20160101 1200-0700',
1801
- end='20160101 1800-0700', freq='6H')
1815
+ end='20160101 1800-0700', freq='6h')
1802
1816
  latitude = 32.2
1803
1817
  longitude = -111
1804
1818
  altitude = 700
@@ -1833,7 +1847,7 @@ def test_basic_chain_altitude_pressure(sam_data, cec_inverter_parameters,
1833
1847
  def test_complete_irradiance_clean_run(sapm_dc_snl_ac_system, location):
1834
1848
  """The DataFrame should not change if all columns are passed"""
1835
1849
  mc = ModelChain(sapm_dc_snl_ac_system, location)
1836
- times = pd.date_range('2010-07-05 9:00:00', periods=2, freq='H')
1850
+ times = pd.date_range('2010-07-05 9:00:00', periods=2, freq='h')
1837
1851
  i = pd.DataFrame(
1838
1852
  {'dni': [2, 3], 'dhi': [4, 6], 'ghi': [9, 5]}, index=times)
1839
1853
 
@@ -1850,7 +1864,7 @@ def test_complete_irradiance_clean_run(sapm_dc_snl_ac_system, location):
1850
1864
  def test_complete_irradiance(sapm_dc_snl_ac_system, location, mocker):
1851
1865
  """Check calculations"""
1852
1866
  mc = ModelChain(sapm_dc_snl_ac_system, location)
1853
- times = pd.date_range('2010-07-05 7:00:00-0700', periods=2, freq='H')
1867
+ times = pd.date_range('2010-07-05 7:00:00-0700', periods=2, freq='h')
1854
1868
  i = pd.DataFrame({'dni': [49.756966, 62.153947],
1855
1869
  'ghi': [372.103976116, 497.087579068],
1856
1870
  'dhi': [356.543700, 465.44400]}, index=times)
@@ -1883,7 +1897,7 @@ def test_complete_irradiance_arrays(
1883
1897
  sapm_dc_snl_ac_system_same_arrays, location, input_type):
1884
1898
  """ModelChain.complete_irradiance can accept a tuple of weather
1885
1899
  DataFrames."""
1886
- times = pd.date_range(start='2020-01-01 0700-0700', periods=2, freq='H')
1900
+ times = pd.date_range(start='2020-01-01 0700-0700', periods=2, freq='h')
1887
1901
  weather = pd.DataFrame({'dni': [2, 3],
1888
1902
  'dhi': [4, 6],
1889
1903
  'ghi': [9, 5]}, index=times)
@@ -1918,7 +1932,7 @@ def test_complete_irradiance_arrays(
1918
1932
  def test_complete_irradiance_arrays_wrong_length(
1919
1933
  sapm_dc_snl_ac_system_same_arrays, location, input_type):
1920
1934
  mc = ModelChain(sapm_dc_snl_ac_system_same_arrays, location)
1921
- times = pd.date_range(start='2020-01-01 0700-0700', periods=2, freq='H')
1935
+ times = pd.date_range(start='2020-01-01 0700-0700', periods=2, freq='h')
1922
1936
  weather = pd.DataFrame({'dni': [2, 3],
1923
1937
  'dhi': [4, 6],
1924
1938
  'ghi': [9, 5]}, index=times)
@@ -1941,11 +1955,8 @@ def test_inconsistent_array_params(location,
1941
1955
  cec_module_params):
1942
1956
  module_error = ".* selected for the DC model but one or more Arrays are " \
1943
1957
  "missing one or more required parameters"
1944
- temperature_error = "could not infer temperature model from " \
1945
- r"system\.temperature_model_parameters\. Check " \
1946
- r"that all Arrays in system\.arrays have " \
1947
- r"parameters for the same temperature model\. " \
1948
- r"Common temperature model parameters: .*"
1958
+ temperature_error = 'Could not infer temperature model from ' \
1959
+ 'ModelChain.system. '
1949
1960
  different_module_system = pvsystem.PVSystem(
1950
1961
  arrays=[
1951
1962
  pvsystem.Array(
@@ -1,5 +1,8 @@
1
1
  import numpy as np
2
+ import pandas as pd
2
3
  from numpy.testing import assert_allclose
4
+ from .conftest import assert_series_equal
5
+ import pytest
3
6
 
4
7
  from pvlib import pvarray
5
8
 
@@ -44,3 +47,25 @@ def test_pvefficiency_adr_round_trip():
44
47
  params = pvarray.fit_pvefficiency_adr(g, t, eta, dict_output=False)
45
48
  result = pvarray.pvefficiency_adr(g, t, *params)
46
49
  assert_allclose(result, eta, atol=1e-6)
50
+
51
+
52
+ def test_huld():
53
+ pdc0 = 100
54
+ res = pvarray.huld(1000, 25, pdc0, cell_type='cSi')
55
+ assert np.isclose(res, pdc0)
56
+ exp_sum = np.exp(1) * (np.sum(pvarray._infer_k_huld('cSi', pdc0)) + pdc0)
57
+ res = pvarray.huld(1000*np.exp(1), 26, pdc0, cell_type='cSi')
58
+ assert np.isclose(res, exp_sum)
59
+ res = pvarray.huld(100, 30, pdc0, k=(1, 1, 1, 1, 1, 1))
60
+ exp_100 = 0.1 * (pdc0 + np.log(0.1) + np.log(0.1)**2 + 5 + 5*np.log(0.1)
61
+ + 5*np.log(0.1)**2 + 25)
62
+ assert np.isclose(res, exp_100)
63
+ # Series input, and irradiance = 0
64
+ eff_irr = pd.Series([1000, 100, 0])
65
+ tm = pd.Series([25, 30, 30])
66
+ expected = pd.Series([pdc0, exp_100, 0])
67
+ res = pvarray.huld(eff_irr, tm, pdc0, k=(1, 1, 1, 1, 1, 1))
68
+ assert_series_equal(res, expected)
69
+ with pytest.raises(ValueError,
70
+ match='Either k or cell_type must be specified'):
71
+ res = pvarray.huld(1000, 25, 100)