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
pvlib/__init__.py CHANGED
@@ -27,4 +27,5 @@ from pvlib import ( # noqa: F401
27
27
  temperature,
28
28
  tools,
29
29
  tracking,
30
+ transformer,
30
31
  )
pvlib/atmosphere.py CHANGED
@@ -6,9 +6,6 @@ speed at different heights.
6
6
 
7
7
  import numpy as np
8
8
  import pandas as pd
9
- import pvlib
10
-
11
- from pvlib._deprecation import deprecated
12
9
 
13
10
  APPARENT_ZENITH_MODELS = ('simple', 'kasten1966', 'kastenyoung1989',
14
11
  'gueymard1993', 'pickering2002')
@@ -416,12 +413,6 @@ def tdew_from_rh(temp_air, relative_humidity, coeff=(6.112, 17.62, 243.12)):
416
413
  return dewpoint
417
414
 
418
415
 
419
- first_solar_spectral_correction = deprecated(
420
- since='0.10.0',
421
- alternative='pvlib.spectrum.spectral_factor_firstsolar'
422
- )(pvlib.spectrum.spectral_factor_firstsolar)
423
-
424
-
425
416
  def bird_hulstrom80_aod_bb(aod380, aod500):
426
417
  """
427
418
  Approximate broadband aerosol optical depth.
@@ -260,8 +260,9 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,
260
260
  Returns
261
261
  -------
262
262
  output : dict or DataFrame
263
- Output is a DataFrame when input ghi is a Series. See Notes for
264
- descriptions of content.
263
+ Output is a ``pandas.DataFrame`` when ``ghi`` is a Series.
264
+ Otherwise it is a dict of ``numpy.ndarray``
265
+ See Notes for descriptions of content.
265
266
 
266
267
  Notes
267
268
  -----
@@ -372,7 +373,7 @@ def get_irradiance_poa(surface_tilt, surface_azimuth, solar_zenith,
372
373
  'poa_global': poa_global, 'poa_direct': poa_direct,
373
374
  'poa_diffuse': poa_diffuse, 'poa_ground_diffuse': poa_gnd_pv,
374
375
  'poa_sky_diffuse': poa_sky_pv, 'shaded_fraction': f_x}
375
- if isinstance(poa_global, pd.Series):
376
+ if isinstance(ghi, pd.Series):
376
377
  output = pd.DataFrame(output)
377
378
  return output
378
379
 
pvlib/bifacial/utils.py CHANGED
@@ -87,7 +87,8 @@ def _unshaded_ground_fraction(surface_tilt, surface_azimuth, solar_zenith,
87
87
  surface_azimuth)
88
88
  f_gnd_beam = 1.0 - np.minimum(
89
89
  1.0, gcr * np.abs(cosd(surface_tilt) + sind(surface_tilt) * tan_phi))
90
- np.where(solar_zenith > max_zenith, 0., f_gnd_beam) # [1], Eq. 4
90
+ # [1], Eq. 4
91
+ f_gnd_beam = np.where(solar_zenith > max_zenith, 0., f_gnd_beam)
91
92
  return f_gnd_beam # 1 - min(1, abs()) < 1 always
92
93
 
93
94
 
pvlib/iotools/psm3.py CHANGED
@@ -49,7 +49,7 @@ REQUEST_VARIABLE_MAP = {
49
49
  'ghi_clear': 'clearsky_ghi',
50
50
  'dhi_clear': 'clearsky_dhi',
51
51
  'dni_clear': 'clearsky_dni',
52
- 'zenith': 'solar_zenith_angle',
52
+ 'solar_zenith': 'solar_zenith_angle',
53
53
  'temp_air': 'air_temperature',
54
54
  'temp_dew': 'dew_point',
55
55
  'relative_humidity': 'relative_humidity',
pvlib/iotools/pvgis.py CHANGED
@@ -559,9 +559,17 @@ def _parse_pvgis_tmy_csv(src):
559
559
  inputs['longitude'] = float(src.readline().split(b':')[1])
560
560
  # Elevation (m): 1389.0\r\n
561
561
  inputs['elevation'] = float(src.readline().split(b':')[1])
562
+
563
+ # TMY has an extra line here: Irradiance Time Offset (h): 0.1761\r\n
564
+ line = src.readline()
565
+ if line.startswith(b'Irradiance Time Offset'):
566
+ inputs['irradiance time offset'] = float(line.split(b':')[1])
567
+ src.readline() # skip over the "month,year\r\n"
568
+ else:
569
+ # `line` is already the "month,year\r\n" line, so nothing to do
570
+ pass
562
571
  # then there's a 13 row comma separated table with two columns: month, year
563
- # which contains the year used for that month in the
564
- src.readline() # get "month,year\r\n"
572
+ # which contains the year used for that month in the TMY
565
573
  months_selected = []
566
574
  for month in range(12):
567
575
  months_selected.append(
pvlib/iotools/tmy.py CHANGED
@@ -3,8 +3,6 @@
3
3
  import datetime
4
4
  import re
5
5
  import pandas as pd
6
- import warnings
7
- from pvlib._deprecation import pvlibDeprecationWarning
8
6
 
9
7
  # Dictionary mapping TMY3 names to pvlib names
10
8
  VARIABLE_MAP = {
@@ -24,8 +22,7 @@ VARIABLE_MAP = {
24
22
  }
25
23
 
26
24
 
27
- def read_tmy3(filename, coerce_year=None, map_variables=None, recolumn=None,
28
- encoding=None):
25
+ def read_tmy3(filename, coerce_year=None, map_variables=True, encoding=None):
29
26
  """Read a TMY3 file into a pandas dataframe.
30
27
 
31
28
  Note that values contained in the metadata dictionary are unchanged
@@ -44,13 +41,9 @@ def read_tmy3(filename, coerce_year=None, map_variables=None, recolumn=None,
44
41
  If supplied, the year of the index will be set to ``coerce_year``, except
45
42
  for the last index value which will be set to the *next* year so that
46
43
  the index increases monotonically.
47
- map_variables : bool, optional
44
+ map_variables : bool, default True
48
45
  When True, renames columns of the DataFrame to pvlib variable names
49
46
  where applicable. See variable :const:`VARIABLE_MAP`.
50
- recolumn : bool (deprecated, use map_variables instead)
51
- If ``True``, apply standard names to TMY3 columns. Typically this
52
- results in stripping the units from the column name.
53
- Cannot be used in combination with ``map_variables``.
54
47
  encoding : str, optional
55
48
  Encoding of the file. For files that contain non-UTF8 characters it may
56
49
  be necessary to specify an alternative encoding, e.g., for
@@ -233,74 +226,15 @@ def read_tmy3(filename, coerce_year=None, map_variables=None, recolumn=None,
233
226
  # unit must be in (D,h,m,s,ms,us,ns), but pandas>=0.24 allows unit='hour'
234
227
  data.index = data_ymd + pd.to_timedelta(shifted_hour, unit='h') \
235
228
  + pd.to_timedelta(minutes, unit='min')
236
- # shouldnt' specify both recolumn and map_variables
237
- if recolumn is not None and map_variables is not None:
238
- msg = "`map_variables` and `recolumn` cannot both be specified"
239
- raise ValueError(msg)
240
- elif map_variables is None and recolumn is not None:
241
- warnings.warn(
242
- 'The recolumn parameter is deprecated and will be removed in '
243
- 'pvlib 0.11.0. Use `map_variables` instead, although note that '
244
- 'its behavior is different from `recolumn`.',
245
- pvlibDeprecationWarning)
246
- elif map_variables is None and recolumn is None:
247
- warnings.warn(
248
- 'TMY3 variable names will be renamed to pvlib conventions by '
249
- 'default starting in pvlib 0.11.0. Specify map_variables=True '
250
- 'to enable that behavior now, or specify map_variables=False '
251
- 'to hide this warning.', pvlibDeprecationWarning)
229
+
252
230
  if map_variables:
253
231
  data = data.rename(columns=VARIABLE_MAP)
254
- elif recolumn or (recolumn is None and map_variables is None):
255
- data = _recolumn(data)
256
232
 
257
233
  data = data.tz_localize(int(meta['TZ'] * 3600))
258
234
 
259
235
  return data, meta
260
236
 
261
237
 
262
- def _recolumn(tmy3_dataframe):
263
- """
264
- Rename the columns of the TMY3 DataFrame.
265
-
266
- Parameters
267
- ----------
268
- tmy3_dataframe : DataFrame
269
- inplace : bool
270
- passed to DataFrame.rename()
271
-
272
- Returns
273
- -------
274
- Recolumned DataFrame.
275
- """
276
- # paste in the header as one long line
277
- raw_columns = 'ETR (W/m^2),ETRN (W/m^2),GHI (W/m^2),GHI source,GHI uncert (%),DNI (W/m^2),DNI source,DNI uncert (%),DHI (W/m^2),DHI source,DHI uncert (%),GH illum (lx),GH illum source,Global illum uncert (%),DN illum (lx),DN illum source,DN illum uncert (%),DH illum (lx),DH illum source,DH illum uncert (%),Zenith lum (cd/m^2),Zenith lum source,Zenith lum uncert (%),TotCld (tenths),TotCld source,TotCld uncert (code),OpqCld (tenths),OpqCld source,OpqCld uncert (code),Dry-bulb (C),Dry-bulb source,Dry-bulb uncert (code),Dew-point (C),Dew-point source,Dew-point uncert (code),RHum (%),RHum source,RHum uncert (code),Pressure (mbar),Pressure source,Pressure uncert (code),Wdir (degrees),Wdir source,Wdir uncert (code),Wspd (m/s),Wspd source,Wspd uncert (code),Hvis (m),Hvis source,Hvis uncert (code),CeilHgt (m),CeilHgt source,CeilHgt uncert (code),Pwat (cm),Pwat source,Pwat uncert (code),AOD (unitless),AOD source,AOD uncert (code),Alb (unitless),Alb source,Alb uncert (code),Lprecip depth (mm),Lprecip quantity (hr),Lprecip source,Lprecip uncert (code),PresWth (METAR code),PresWth source,PresWth uncert (code)' # noqa: E501
278
-
279
- new_columns = [
280
- 'ETR', 'ETRN', 'GHI', 'GHISource', 'GHIUncertainty',
281
- 'DNI', 'DNISource', 'DNIUncertainty', 'DHI', 'DHISource',
282
- 'DHIUncertainty', 'GHillum', 'GHillumSource', 'GHillumUncertainty',
283
- 'DNillum', 'DNillumSource', 'DNillumUncertainty', 'DHillum',
284
- 'DHillumSource', 'DHillumUncertainty', 'Zenithlum',
285
- 'ZenithlumSource', 'ZenithlumUncertainty', 'TotCld', 'TotCldSource',
286
- 'TotCldUncertainty', 'OpqCld', 'OpqCldSource', 'OpqCldUncertainty',
287
- 'DryBulb', 'DryBulbSource', 'DryBulbUncertainty', 'DewPoint',
288
- 'DewPointSource', 'DewPointUncertainty', 'RHum', 'RHumSource',
289
- 'RHumUncertainty', 'Pressure', 'PressureSource',
290
- 'PressureUncertainty', 'Wdir', 'WdirSource', 'WdirUncertainty',
291
- 'Wspd', 'WspdSource', 'WspdUncertainty', 'Hvis', 'HvisSource',
292
- 'HvisUncertainty', 'CeilHgt', 'CeilHgtSource', 'CeilHgtUncertainty',
293
- 'Pwat', 'PwatSource', 'PwatUncertainty', 'AOD', 'AODSource',
294
- 'AODUncertainty', 'Alb', 'AlbSource', 'AlbUncertainty',
295
- 'Lprecipdepth', 'Lprecipquantity', 'LprecipSource',
296
- 'LprecipUncertainty', 'PresWth', 'PresWthSource',
297
- 'PresWthUncertainty']
298
-
299
- mapping = dict(zip(raw_columns.split(','), new_columns))
300
-
301
- return tmy3_dataframe.rename(columns=mapping)
302
-
303
-
304
238
  def read_tmy2(filename):
305
239
  """
306
240
  Read a TMY2 file into a DataFrame.
pvlib/irradiance.py CHANGED
@@ -2368,6 +2368,9 @@ def gti_dirint(poa_global, aoi, solar_zenith, solar_azimuth, times,
2368
2368
  irradiance, Solar Energy 122, 1037-1046.
2369
2369
  :doi:`10.1016/j.solener.2015.10.024`
2370
2370
  """
2371
+ # check input data and raise Exceptions where data will cause the
2372
+ # algorithm to fail
2373
+ _gti_dirint_check_input(aoi)
2371
2374
 
2372
2375
  aoi_lt_90 = aoi < 90
2373
2376
 
@@ -2399,6 +2402,17 @@ def gti_dirint(poa_global, aoi, solar_zenith, solar_azimuth, times,
2399
2402
  return output
2400
2403
 
2401
2404
 
2405
+ def _gti_dirint_check_input(aoi):
2406
+ r"""
2407
+ Helper for gti_dirint
2408
+
2409
+ Raises Exceptions from input data that cause the algorithm to fail.
2410
+ """
2411
+ if not (aoi < 90).any():
2412
+ raise ValueError("There are no times with AOI < 90. "
2413
+ "gti_dirint requires some data with AOI < 90.")
2414
+
2415
+
2402
2416
  def _gti_dirint_lt_90(poa_global, aoi, aoi_lt_90, solar_zenith, solar_azimuth,
2403
2417
  times, surface_tilt, surface_azimuth, pressure=101325.,
2404
2418
  use_delta_kt_prime=True, temp_dew=None, albedo=.25,
pvlib/location.py CHANGED
@@ -6,6 +6,7 @@ This module contains the Location class.
6
6
 
7
7
  import pathlib
8
8
  import datetime
9
+ import zoneinfo
9
10
 
10
11
  import pandas as pd
11
12
  import pytz
@@ -18,13 +19,16 @@ from pvlib.tools import _degrees_to_index
18
19
  class Location:
19
20
  """
20
21
  Location objects are convenient containers for latitude, longitude,
21
- timezone, and altitude data associated with a particular
22
- geographic location. You can also assign a name to a location object.
22
+ time zone, and altitude data associated with a particular geographic
23
+ location. You can also assign a name to a location object.
23
24
 
24
- Location objects have two timezone attributes:
25
+ Location objects have two time-zone attributes:
25
26
 
26
- * ``tz`` is a IANA timezone string.
27
- * ``pytz`` is a pytz timezone object.
27
+ * ``tz`` is an IANA time-zone string.
28
+ * ``pytz`` is a pytz-based time-zone object (read only).
29
+
30
+ The read-only ``pytz`` attribute will stay in sync with any changes made
31
+ using ``tz``.
28
32
 
29
33
  Location objects support the print method.
30
34
 
@@ -38,12 +42,16 @@ class Location:
38
42
  Positive is east of the prime meridian.
39
43
  Use decimal degrees notation.
40
44
 
41
- tz : str, int, float, or pytz.timezone, default 'UTC'.
42
- See
43
- http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
44
- for a list of valid time zones.
45
- pytz.timezone objects will be converted to strings.
46
- ints and floats must be in hours from UTC.
45
+ tz : time zone as str, int, float, or datetime.tzinfo, default 'UTC'.
46
+ See http://en.wikipedia.org/wiki/List_of_tz_database_time_zones for a
47
+ list of valid name strings. An `int` or `float` must be a whole-number
48
+ hour offsets from UTC that can be converted to the IANA-supported
49
+ 'Etc/GMT-N' format. (Note the limited range of the offset N and its
50
+ sign-change convention.) Time zones from the pytz and zoneinfo packages
51
+ may also be passed here, as they are subclasses of datetime.tzinfo.
52
+
53
+ The `tz` attribute is represented as a valid IANA time zone name
54
+ string.
47
55
 
48
56
  altitude : float, optional
49
57
  Altitude from sea level in meters.
@@ -54,43 +62,75 @@ class Location:
54
62
  name : string, optional
55
63
  Sets the name attribute of the Location object.
56
64
 
65
+ Raises
66
+ ------
67
+ ValueError
68
+ when the time zone ``tz`` cannot be converted.
69
+
70
+ zoneinfo.ZoneInfoNotFoundError
71
+ when the time zone ``tz`` is not recognizable as an IANA time zone by
72
+ the ``zoneinfo.ZoneInfo`` initializer used for internal time-zone
73
+ representation.
74
+
57
75
  See also
58
76
  --------
59
77
  pvlib.pvsystem.PVSystem
60
78
  """
61
79
 
62
- def __init__(self, latitude, longitude, tz='UTC', altitude=None,
63
- name=None):
64
-
80
+ def __init__(
81
+ self, latitude, longitude, tz='UTC', altitude=None, name=None
82
+ ):
65
83
  self.latitude = latitude
66
84
  self.longitude = longitude
67
-
68
- if isinstance(tz, str):
69
- self.tz = tz
70
- self.pytz = pytz.timezone(tz)
71
- elif isinstance(tz, datetime.timezone):
72
- self.tz = 'UTC'
73
- self.pytz = pytz.UTC
74
- elif isinstance(tz, datetime.tzinfo):
75
- self.tz = tz.zone
76
- self.pytz = tz
77
- elif isinstance(tz, (int, float)):
78
- self.tz = tz
79
- self.pytz = pytz.FixedOffset(tz*60)
80
- else:
81
- raise TypeError('Invalid tz specification')
85
+ self.tz = tz
82
86
 
83
87
  if altitude is None:
84
88
  altitude = lookup_altitude(latitude, longitude)
85
89
 
86
90
  self.altitude = altitude
87
-
88
91
  self.name = name
89
92
 
90
93
  def __repr__(self):
91
94
  attrs = ['name', 'latitude', 'longitude', 'altitude', 'tz']
95
+ # Use None as getattr default in case __repr__ is called during
96
+ # initialization before all attributes have been assigned.
92
97
  return ('Location: \n ' + '\n '.join(
93
- f'{attr}: {getattr(self, attr)}' for attr in attrs))
98
+ f'{attr}: {getattr(self, attr, None)}' for attr in attrs))
99
+
100
+ @property
101
+ def tz(self):
102
+ """The location's IANA time-zone string."""
103
+ return str(self._zoneinfo)
104
+
105
+ @tz.setter
106
+ def tz(self, tz_):
107
+ # self._zoneinfo holds single source of time-zone truth as IANA name.
108
+ if isinstance(tz_, str):
109
+ self._zoneinfo = zoneinfo.ZoneInfo(tz_)
110
+ elif isinstance(tz_, int):
111
+ self._zoneinfo = zoneinfo.ZoneInfo(f"Etc/GMT{-tz_:+d}")
112
+ elif isinstance(tz_, float):
113
+ if tz_ % 1 != 0:
114
+ raise TypeError(
115
+ "Floating-point tz has non-zero fractional part: "
116
+ f"{tz_}. Only whole-number offsets are supported."
117
+ )
118
+
119
+ self._zoneinfo = zoneinfo.ZoneInfo(f"Etc/GMT{-int(tz_):+d}")
120
+ elif isinstance(tz_, datetime.tzinfo):
121
+ # Includes time zones generated by pytz and zoneinfo packages.
122
+ self._zoneinfo = zoneinfo.ZoneInfo(str(tz_))
123
+ else:
124
+ raise TypeError(
125
+ f"invalid tz specification: {tz_}, must be an IANA time zone "
126
+ "string, a whole-number int/float UTC offset, or a "
127
+ "datetime.tzinfo object (including subclasses)"
128
+ )
129
+
130
+ @property
131
+ def pytz(self):
132
+ """The location's pytz time zone (read only)."""
133
+ return pytz.timezone(str(self._zoneinfo))
94
134
 
95
135
  @classmethod
96
136
  def from_tmy(cls, tmy_metadata, tmy_data=None, **kwargs):
@@ -328,7 +368,7 @@ class Location:
328
368
 
329
369
  return airmass
330
370
 
331
- def get_sun_rise_set_transit(self, times, method='pyephem', **kwargs):
371
+ def get_sun_rise_set_transit(self, times, method='spa', **kwargs):
332
372
  """
333
373
  Calculate sunrise, sunset and transit times.
334
374
 
@@ -336,7 +376,7 @@ class Location:
336
376
  ----------
337
377
  times : DatetimeIndex
338
378
  Must be localized to the Location
339
- method : str, default 'pyephem'
379
+ method : str, default 'spa'
340
380
  'pyephem', 'spa', or 'geometric'
341
381
 
342
382
  kwargs :
pvlib/modelchain.py CHANGED
@@ -29,7 +29,7 @@ POA_KEYS = ('poa_global', 'poa_direct', 'poa_diffuse')
29
29
 
30
30
  # Optional keys to communicate temperature data. If provided,
31
31
  # 'cell_temperature' overrides ModelChain.temperature_model and sets
32
- # ModelChain.cell_temperature to the data. If 'module_temperature' is provdied,
32
+ # ModelChain.cell_temperature to the data. If 'module_temperature' is provided,
33
33
  # overrides ModelChain.temperature_model with
34
34
  # pvlib.temperature.sapm_celL_from_module
35
35
  TEMPERATURE_KEYS = ('module_temperature', 'cell_temperature')
@@ -253,7 +253,7 @@ class ModelChainResult:
253
253
  def _head(obj):
254
254
  try:
255
255
  return obj[:3]
256
- except:
256
+ except Exception:
257
257
  return obj
258
258
 
259
259
  if type(self.dc) is tuple:
@@ -269,7 +269,7 @@ class ModelChainResult:
269
269
  '\n')
270
270
  lines = []
271
271
  for attr in mc_attrs:
272
- if not (attr.startswith('_') or attr=='times'):
272
+ if not (attr.startswith('_') or attr == 'times'):
273
273
  lines.append(f' {attr}: ' + _mcr_repr(getattr(self, attr)))
274
274
  desc4 = '\n'.join(lines)
275
275
  return (desc1 + desc2 + desc3 + desc4)
@@ -330,12 +330,15 @@ class ModelChain:
330
330
  'interp' and 'no_loss'. The ModelChain instance will be passed as the
331
331
  first argument to a user-defined function.
332
332
 
333
- spectral_model : str, or function, optional
334
- If not specified, the model will be inferred from the parameters that
335
- are common to all of system.arrays[i].module_parameters.
336
- Valid strings are 'sapm', 'first_solar', 'no_loss'.
333
+ spectral_model : str or function, optional
334
+ Valid strings are:
335
+
336
+ - ``'sapm'``
337
+ - ``'first_solar'``
338
+ - ``'no_loss'``
339
+
337
340
  The ModelChain instance will be passed as the first argument to
338
- a user-defined function.
341
+ a user-defined function. If not specified, ``'no_loss'`` is assumed.
339
342
 
340
343
  temperature_model : str or function, optional
341
344
  Valid strings are: 'sapm', 'pvsyst', 'faiman', 'fuentes', 'noct_sam'.
@@ -386,7 +389,6 @@ class ModelChain:
386
389
 
387
390
  self.results = ModelChainResult()
388
391
 
389
-
390
392
  @classmethod
391
393
  def with_pvwatts(cls, system, location,
392
394
  clearsky_model='ineichen',
@@ -855,9 +857,7 @@ class ModelChain:
855
857
 
856
858
  @spectral_model.setter
857
859
  def spectral_model(self, model):
858
- if model is None:
859
- self._spectral_model = self.infer_spectral_model()
860
- elif isinstance(model, str):
860
+ if isinstance(model, str):
861
861
  model = model.lower()
862
862
  if model == 'first_solar':
863
863
  self._spectral_model = self.first_solar_spectral_loss
@@ -867,30 +867,12 @@ class ModelChain:
867
867
  self._spectral_model = self.no_spectral_loss
868
868
  else:
869
869
  raise ValueError(model + ' is not a valid spectral loss model')
870
- else:
870
+ elif model is None:
871
+ # not setting a model is equivalent to setting no_loss
872
+ self._spectral_model = self.no_spectral_loss
873
+ else: # assume model is callable with 1st argument = the MC instance
871
874
  self._spectral_model = partial(model, self)
872
875
 
873
- def infer_spectral_model(self):
874
- """Infer spectral model from system attributes."""
875
- module_parameters = tuple(
876
- array.module_parameters for array in self.system.arrays)
877
- params = _common_keys(module_parameters)
878
- if {'A4', 'A3', 'A2', 'A1', 'A0'} <= params:
879
- return self.sapm_spectral_loss
880
- elif ((('Technology' in params or
881
- 'Material' in params) and
882
- (self.system._infer_cell_type() is not None)) or
883
- 'first_solar_spectral_coefficients' in params):
884
- return self.first_solar_spectral_loss
885
- else:
886
- raise ValueError('could not infer spectral model from '
887
- 'system.arrays[i].module_parameters. Check that '
888
- 'the module_parameters for all Arrays in '
889
- 'system.arrays contain valid '
890
- 'first_solar_spectral_coefficients, a valid '
891
- 'Material or Technology value, or set '
892
- 'spectral_model="no_loss".')
893
-
894
876
  def first_solar_spectral_loss(self):
895
877
  self.results.spectral_modifier = self.system.first_solar_spectral_loss(
896
878
  _tuple_from_dfs(self.results.weather, 'precipitable_water'),
@@ -1570,7 +1552,7 @@ class ModelChain:
1570
1552
  ----------
1571
1553
  data : DataFrame
1572
1554
  May contain columns ``'cell_temperature'`` or
1573
- ``'module_temperaure'``.
1555
+ ``'module_temperature'``.
1574
1556
 
1575
1557
  Returns
1576
1558
  -------
@@ -1679,6 +1661,7 @@ class ModelChain:
1679
1661
  self.prepare_inputs(weather)
1680
1662
  self.aoi_model()
1681
1663
  self.spectral_model()
1664
+
1682
1665
  self.effective_irradiance_model()
1683
1666
 
1684
1667
  self._run_from_effective_irrad(weather)
pvlib/pvsystem.py CHANGED
@@ -29,11 +29,10 @@ import pvlib.tools as tools
29
29
  # a dict of required parameter names for each DC power model
30
30
  _DC_MODEL_PARAMS = {
31
31
  'sapm': {
32
- 'A0', 'A1', 'A2', 'A3', 'A4', 'B0', 'B1', 'B2', 'B3',
33
- 'B4', 'B5', 'C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6',
34
- 'C7', 'Isco', 'Impo', 'Voco', 'Vmpo', 'Aisc', 'Aimp', 'Bvoco',
32
+ 'C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7',
33
+ 'Isco', 'Impo', 'Voco', 'Vmpo', 'Aisc', 'Aimp', 'Bvoco',
35
34
  'Mbvoc', 'Bvmpo', 'Mbvmp', 'N', 'Cells_in_Series',
36
- 'IXO', 'IXXO', 'FD'},
35
+ 'IXO', 'IXXO'},
37
36
  'desoto': {
38
37
  'alpha_sc', 'a_ref', 'I_L_ref', 'I_o_ref',
39
38
  'R_sh_ref', 'R_s'},
@@ -1710,6 +1709,8 @@ def calcparams_desoto(effective_irradiance, temp_cell,
1710
1709
  Rs = R_s
1711
1710
 
1712
1711
  numeric_args = (effective_irradiance, temp_cell)
1712
+ # IL: photocurrent, I0: saturation_current, Rs: resistance_series,
1713
+ # Rsh: resistance_shunt
1713
1714
  out = (IL, I0, Rs, Rsh, nNsVth)
1714
1715
 
1715
1716
  if all(map(np.isscalar, numeric_args)):
@@ -1976,6 +1977,8 @@ def calcparams_pvsyst(effective_irradiance, temp_cell,
1976
1977
  Rs = R_s
1977
1978
 
1978
1979
  numeric_args = (effective_irradiance, temp_cell)
1980
+ # IL: photocurrent, I0: saturation_current, Rs: resistance_series,
1981
+ # Rsh: resistance_shunt
1979
1982
  out = (IL, I0, Rs, Rsh, nNsVth)
1980
1983
 
1981
1984
  if all(map(np.isscalar, numeric_args)):
@@ -2311,12 +2314,6 @@ def sapm(effective_irradiance, temp_cell, module):
2311
2314
  return out
2312
2315
 
2313
2316
 
2314
- sapm_spectral_loss = deprecated(
2315
- since='0.10.0',
2316
- alternative='pvlib.spectrum.spectral_factor_sapm'
2317
- )(spectrum.spectral_factor_sapm)
2318
-
2319
-
2320
2317
  def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi,
2321
2318
  module):
2322
2319
  r"""