pvlib 0.10.5__py3-none-any.whl → 0.11.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 (39) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/albedo.py +168 -0
  3. pvlib/data/ASTMG173.csv +2004 -0
  4. pvlib/iam.py +28 -28
  5. pvlib/iotools/__init__.py +0 -1
  6. pvlib/iotools/midc.py +15 -10
  7. pvlib/iotools/psm3.py +10 -25
  8. pvlib/iotools/srml.py +0 -61
  9. pvlib/irradiance.py +133 -95
  10. pvlib/location.py +13 -5
  11. pvlib/modelchain.py +2 -165
  12. pvlib/pvsystem.py +23 -63
  13. pvlib/shading.py +350 -0
  14. pvlib/spectrum/__init__.py +5 -0
  15. pvlib/spectrum/mismatch.py +572 -43
  16. pvlib/spectrum/spectrl2.py +8 -8
  17. pvlib/tests/iotools/test_psm3.py +0 -18
  18. pvlib/tests/iotools/test_srml.py +1 -43
  19. pvlib/tests/test_albedo.py +84 -0
  20. pvlib/tests/test_inverter.py +2 -2
  21. pvlib/tests/test_irradiance.py +35 -2
  22. pvlib/tests/test_location.py +26 -18
  23. pvlib/tests/test_modelchain.py +0 -57
  24. pvlib/tests/test_pvsystem.py +11 -39
  25. pvlib/tests/test_shading.py +167 -1
  26. pvlib/tests/test_singlediode.py +0 -19
  27. pvlib/tests/test_spectrum.py +283 -22
  28. pvlib/tests/test_temperature.py +7 -7
  29. pvlib/tests/test_tools.py +24 -0
  30. pvlib/tests/test_transformer.py +60 -0
  31. pvlib/tools.py +27 -0
  32. pvlib/transformer.py +117 -0
  33. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/METADATA +1 -1
  34. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/RECORD +38 -34
  35. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/WHEEL +1 -1
  36. pvlib/data/astm_g173_am15g.csv +0 -2003
  37. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/AUTHORS.md +0 -0
  38. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/LICENSE +0 -0
  39. {pvlib-0.10.5.dist-info → pvlib-0.11.0.dist-info}/top_level.txt +0 -0
pvlib/iam.py CHANGED
@@ -592,10 +592,10 @@ def marion_diffuse(model, surface_tilt, **kwargs):
592
592
  iam : dict
593
593
  IAM values for each type of diffuse irradiance:
594
594
 
595
- * 'sky': radiation from the sky dome (zenith <= 90)
596
- * 'horizon': radiation from the region of the sky near the horizon
597
- (89.5 <= zenith <= 90)
598
- * 'ground': radiation reflected from the ground (zenith >= 90)
595
+ * 'sky': radiation from the sky dome (zenith <= 90)
596
+ * 'horizon': radiation from the region of the sky near the horizon
597
+ (89.5 <= zenith <= 90)
598
+ * 'ground': radiation reflected from the ground (zenith >= 90)
599
599
 
600
600
  See [1]_ for a detailed description of each class.
601
601
 
@@ -667,10 +667,10 @@ def marion_integrate(function, surface_tilt, region, num=None):
667
667
  region : {'sky', 'horizon', 'ground'}
668
668
  The region to integrate over. Must be one of:
669
669
 
670
- * 'sky': radiation from the sky dome (zenith <= 90)
671
- * 'horizon': radiation from the region of the sky near the horizon
672
- (89.5 <= zenith <= 90)
673
- * 'ground': radiation reflected from the ground (zenith >= 90)
670
+ * 'sky': radiation from the sky dome (zenith <= 90)
671
+ * 'horizon': radiation from the region of the sky near the horizon
672
+ (89.5 <= zenith <= 90)
673
+ * 'ground': radiation reflected from the ground (zenith >= 90)
674
674
 
675
675
  See [1]_ for a detailed description of each class.
676
676
 
@@ -678,8 +678,8 @@ def marion_integrate(function, surface_tilt, region, num=None):
678
678
  The number of increments in the zenith integration.
679
679
  If not specified, N will follow the values used in [1]_:
680
680
 
681
- * 'sky' or 'ground': num = 180
682
- * 'horizon': num = 1800
681
+ * 'sky' or 'ground': num = 180
682
+ * 'horizon': num = 1800
683
683
 
684
684
  Returns
685
685
  -------
@@ -1107,14 +1107,14 @@ def convert(source_name, source_params, target_name, weight=_sin_weight,
1107
1107
  source_params : dict
1108
1108
  A dictionary of parameters for the source model.
1109
1109
 
1110
- If source model is ``'ashrae'``, the dictionary must contain
1111
- the key ``'b'``.
1110
+ If source model is ``'ashrae'``, the dictionary must contain
1111
+ the key ``'b'``.
1112
1112
 
1113
- If source model is ``'martin_ruiz'``, the dictionary must
1114
- contain the key ``'a_r'``.
1113
+ If source model is ``'martin_ruiz'``, the dictionary must
1114
+ contain the key ``'a_r'``.
1115
1115
 
1116
- If source model is ``'physical'``, the dictionary must
1117
- contain the keys ``'n'``, ``'K'``, and ``'L'``.
1116
+ If source model is ``'physical'``, the dictionary must
1117
+ contain the keys ``'n'``, ``'K'``, and ``'L'``.
1118
1118
 
1119
1119
  target_name : str
1120
1120
  Name of the target model. Must be ``'ashrae'``, ``'martin_ruiz'``, or
@@ -1146,14 +1146,14 @@ def convert(source_name, source_params, target_name, weight=_sin_weight,
1146
1146
  dict
1147
1147
  Parameters for the target model.
1148
1148
 
1149
- If target model is ``'ashrae'``, the dictionary will contain
1150
- the key ``'b'``.
1149
+ If target model is ``'ashrae'``, the dictionary will contain
1150
+ the key ``'b'``.
1151
1151
 
1152
- If target model is ``'martin_ruiz'``, the dictionary will
1153
- contain the key ``'a_r'``.
1152
+ If target model is ``'martin_ruiz'``, the dictionary will
1153
+ contain the key ``'a_r'``.
1154
1154
 
1155
- If target model is ``'physical'``, the dictionary will
1156
- contain the keys ``'n'``, ``'K'``, and ``'L'``.
1155
+ If target model is ``'physical'``, the dictionary will
1156
+ contain the keys ``'n'``, ``'K'``, and ``'L'``.
1157
1157
 
1158
1158
  Note
1159
1159
  ----
@@ -1243,14 +1243,14 @@ def fit(measured_aoi, measured_iam, model_name, weight=_sin_weight, xtol=None):
1243
1243
  dict
1244
1244
  Parameters for target model.
1245
1245
 
1246
- If target model is ``'ashrae'``, the dictionary will contain
1247
- the key ``'b'``.
1246
+ If target model is ``'ashrae'``, the dictionary will contain
1247
+ the key ``'b'``.
1248
1248
 
1249
- If target model is ``'martin_ruiz'``, the dictionary will
1250
- contain the key ``'a_r'``.
1249
+ If target model is ``'martin_ruiz'``, the dictionary will
1250
+ contain the key ``'a_r'``.
1251
1251
 
1252
- If target model is ``'physical'``, the dictionary will
1253
- contain the keys ``'n'``, ``'K'``, and ``'L'``.
1252
+ If target model is ``'physical'``, the dictionary will
1253
+ contain the keys ``'n'``, ``'K'``, and ``'L'``.
1254
1254
 
1255
1255
  References
1256
1256
  ----------
pvlib/iotools/__init__.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from pvlib.iotools.tmy import read_tmy2, read_tmy3 # noqa: F401
2
2
  from pvlib.iotools.epw import read_epw, parse_epw # noqa: F401
3
3
  from pvlib.iotools.srml import read_srml # noqa: F401
4
- from pvlib.iotools.srml import read_srml_month_from_solardat # noqa: F401
5
4
  from pvlib.iotools.srml import get_srml # noqa: F401
6
5
  from pvlib.iotools.surfrad import read_surfrad # noqa: F401
7
6
  from pvlib.iotools.midc import read_midc # noqa: F401
pvlib/iotools/midc.py CHANGED
@@ -20,15 +20,21 @@ import pandas as pd
20
20
  MIDC_VARIABLE_MAP = {
21
21
  'BMS': {
22
22
  'Global CMP22 (vent/cor) [W/m^2]': 'ghi',
23
- 'Direct NIP [W/m^2]': 'dni',
23
+ 'Direct CHP1-1 [W/m^2]': 'dni_chp1',
24
+ # NIP was mapped to dni for pvlib<=0.10.5
25
+ 'Direct NIP [W/m^2]': 'dni_nip',
24
26
  'Diffuse CM22-1 (vent/cor) [W/m^2]': 'dhi',
25
27
  'Avg Wind Speed @ 6ft [m/s]': 'wind_speed',
26
28
  'Tower Dry Bulb Temp [deg C]': 'temp_air',
27
29
  'Tower RH [%]': 'relative_humidity'},
28
30
  'UOSMRL': {
29
31
  'Global CMP22 [W/m^2]': 'ghi',
30
- 'Direct NIP [W/m^2]': 'dni',
31
- 'Diffuse Schenk [W/m^2]': 'dhi',
32
+ 'Direct CHP1 [W/m^2]': 'dni_chp1',
33
+ 'Diffuse [W/m^2]': 'dhi',
34
+ # NIP was mapped to dni for pvlib<=0.10.5
35
+ 'Direct NIP [W/m^2]': 'dni_nip',
36
+ # Schenk was mapped to dhi for pvlib<=0.10.5
37
+ # 'Diffuse Schenk [W/m^2]': 'dhi',
32
38
  'Air Temperature [deg C]': 'temp_air',
33
39
  'Relative Humidity [%]': 'relative_humidity',
34
40
  'Avg Wind Speed @ 10m [m/s]': 'wind_speed'},
@@ -80,18 +86,17 @@ MIDC_VARIABLE_MAP = {
80
86
  'Air Temperature [deg C]': 'temp_air',
81
87
  'Rel Humidity [%]': 'relative_humidity',
82
88
  'Avg Wind Speed @ 3m [m/s]': 'wind_speed'},
83
- 'VTIF': {
89
+ 'NWTC': {
84
90
  'Global Horizontal [W/m^2]': 'ghi',
85
91
  'Direct Normal [W/m^2]': 'dni',
86
92
  'Diffuse Horizontal [W/m^2]': 'dhi',
87
- 'Air Temperature [deg C]': 'temp_air',
88
- 'Avg Wind Speed @ 3m [m/s]': 'wind_speed',
89
- 'Rel Humidity [%]': 'relative_humidity'},
90
- 'NWTC': {
91
- 'Global PSP [W/m^2]': 'ghi',
93
+ # PSP instrument was removed Feb. 2021
94
+ # PSP was mapped to ghi for pvlib<=0.10.5
95
+ # 'Global PSP [W/m^2]': 'ghi',
92
96
  'Temperature @ 2m [deg C]': 'temp_air',
93
97
  'Avg Wind Speed @ 2m [m/s]': 'wind_speed',
94
- 'Relative Humidity [%]': 'relative_humidity'}}
98
+ 'Relative Humidity [%]': 'relative_humidity'},
99
+ }
95
100
 
96
101
 
97
102
  # Maps problematic timezones to 'Etc/GMT' for parsing.
pvlib/iotools/psm3.py CHANGED
@@ -62,8 +62,8 @@ REQUEST_VARIABLE_MAP = {
62
62
 
63
63
 
64
64
  def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60,
65
- attributes=ATTRIBUTES, leap_day=None, full_name=PVLIB_PYTHON,
66
- affiliation=PVLIB_PYTHON, map_variables=None, url=None,
65
+ attributes=ATTRIBUTES, leap_day=True, full_name=PVLIB_PYTHON,
66
+ affiliation=PVLIB_PYTHON, map_variables=True, url=None,
67
67
  timeout=30):
68
68
  """
69
69
  Retrieve NSRDB PSM3 timeseries weather data from the PSM3 API. The NSRDB
@@ -105,14 +105,14 @@ def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60,
105
105
  for lists of available fields. Alternatively, pvlib names may also be
106
106
  used (e.g. 'ghi' rather than 'GHI'); see :const:`REQUEST_VARIABLE_MAP`.
107
107
  To retrieve all available fields, set ``attributes=[]``.
108
- leap_day : boolean, default False
108
+ leap_day : bool, default : True
109
109
  include leap day in the results. Only used for single-year requests
110
110
  (i.e., it is ignored for tmy/tgy/tdy requests).
111
111
  full_name : str, default 'pvlib python'
112
112
  optional
113
113
  affiliation : str, default 'pvlib python'
114
114
  optional
115
- map_variables : boolean, optional
115
+ map_variables : bool, default True
116
116
  When true, renames columns of the Dataframe to pvlib variable names
117
117
  where applicable. See variable :const:`VARIABLE_MAP`.
118
118
  url : str, optional
@@ -179,14 +179,6 @@ def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60,
179
179
  # convert pvlib names in attributes to psm3 convention
180
180
  attributes = [REQUEST_VARIABLE_MAP.get(a, a) for a in attributes]
181
181
 
182
- if (leap_day is None) and (not names.startswith('t')):
183
- warnings.warn(
184
- 'The ``get_psm3`` function will default to leap_day=True '
185
- 'starting in pvlib 0.11.0. Specify leap_day=True '
186
- 'to enable this behavior now, or specify leap_day=False '
187
- 'to hide this warning.', pvlibDeprecationWarning)
188
- leap_day = False
189
-
190
182
  # required query-string parameters for request to PSM3 API
191
183
  params = {
192
184
  'api_key': api_key,
@@ -227,7 +219,7 @@ def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60,
227
219
  return parse_psm3(fbuf, map_variables)
228
220
 
229
221
 
230
- def parse_psm3(fbuf, map_variables=None):
222
+ def parse_psm3(fbuf, map_variables=True):
231
223
  """
232
224
  Parse an NSRDB PSM3 weather file (formatted as SAM CSV). The NSRDB
233
225
  is described in [1]_ and the SAM CSV format is described in [2]_.
@@ -241,9 +233,9 @@ def parse_psm3(fbuf, map_variables=None):
241
233
  ----------
242
234
  fbuf: file-like object
243
235
  File-like object containing data to read.
244
- map_variables: bool
236
+ map_variables: bool, default True
245
237
  When true, renames columns of the Dataframe to pvlib variable names
246
- where applicable. See variable VARIABLE_MAP.
238
+ where applicable. See variable :const:`VARIABLE_MAP`.
247
239
 
248
240
  Returns
249
241
  -------
@@ -356,13 +348,6 @@ def parse_psm3(fbuf, map_variables=None):
356
348
  tz = 'Etc/GMT%+d' % -metadata['Time Zone']
357
349
  data.index = pd.DatetimeIndex(dtidx).tz_localize(tz)
358
350
 
359
- if map_variables is None:
360
- warnings.warn(
361
- 'PSM3 variable names will be renamed to pvlib conventions by '
362
- 'default starting in pvlib 0.11.0. Specify map_variables=True '
363
- 'to enable that behavior now, or specify map_variables=False '
364
- 'to hide this warning.', pvlibDeprecationWarning)
365
- map_variables = False
366
351
  if map_variables:
367
352
  data = data.rename(columns=VARIABLE_MAP)
368
353
  metadata['latitude'] = metadata.pop('Latitude')
@@ -372,7 +357,7 @@ def parse_psm3(fbuf, map_variables=None):
372
357
  return data, metadata
373
358
 
374
359
 
375
- def read_psm3(filename, map_variables=None):
360
+ def read_psm3(filename, map_variables=True):
376
361
  """
377
362
  Read an NSRDB PSM3 weather file (formatted as SAM CSV). The NSRDB
378
363
  is described in [1]_ and the SAM CSV format is described in [2]_.
@@ -386,9 +371,9 @@ def read_psm3(filename, map_variables=None):
386
371
  ----------
387
372
  filename: str
388
373
  Filename of a file containing data to read.
389
- map_variables: bool
374
+ map_variables: bool, default True
390
375
  When true, renames columns of the Dataframe to pvlib variable names
391
- where applicable. See variable VARIABLE_MAP.
376
+ where applicable. See variable :const:`VARIABLE_MAP`.
392
377
 
393
378
  Returns
394
379
  -------
pvlib/iotools/srml.py CHANGED
@@ -6,8 +6,6 @@ import pandas as pd
6
6
  import urllib
7
7
  import warnings
8
8
 
9
- from pvlib._deprecation import deprecated
10
-
11
9
  # VARIABLE_MAP is a dictionary mapping SRML data element numbers to their
12
10
  # pvlib names. For most variables, only the first three digits are used,
13
11
  # the fourth indicating the instrument. Spectral data (7xxx) uses all
@@ -172,65 +170,6 @@ def _format_index(df):
172
170
  return df
173
171
 
174
172
 
175
- @deprecated('0.10.0', alternative='pvlib.iotools.get_srml', removal='0.11.0')
176
- def read_srml_month_from_solardat(station, year, month, filetype='PO',
177
- map_variables=True):
178
- """
179
- Request a month of SRML data and read it into a Dataframe.
180
-
181
- The SRML is described in [1]_.
182
-
183
- Parameters
184
- ----------
185
- station: str
186
- The name of the SRML station to request.
187
- year: int
188
- Year to request data for
189
- month: int
190
- Month to request data for.
191
- filetype: string
192
- SRML file type to gather. See notes for explanation.
193
- map_variables: bool, default: True
194
- When true, renames columns of the DataFrame to pvlib variable names
195
- where applicable. See variable :const:`VARIABLE_MAP`.
196
-
197
- Returns
198
- -------
199
- data: pd.DataFrame
200
- One month of data from SRML.
201
-
202
- Notes
203
- -----
204
- File types designate the time interval of a file and if it contains
205
- raw or processed data. For instance, `RO` designates raw, one minute
206
- data and `PO` designates processed one minute data. The availability
207
- of file types varies between sites. Below is a table of file types
208
- and their time intervals. See [1] for site information.
209
-
210
- ============= ============ ==================
211
- time interval raw filetype processed filetype
212
- ============= ============ ==================
213
- 1 minute RO PO
214
- 5 minute RF PF
215
- 15 minute RQ PQ
216
- hourly RH PH
217
- ============= ============ ==================
218
-
219
- References
220
- ----------
221
- .. [1] University of Oregon Solar Radiation Measurement Laboratory
222
- http://solardata.uoregon.edu/
223
- """
224
- file_name = "{station}{filetype}{year:02d}{month:02d}.txt".format(
225
- station=station,
226
- filetype=filetype,
227
- year=year % 100,
228
- month=month)
229
- url = "http://solardata.uoregon.edu/download/Archive/"
230
- data = read_srml(url + file_name, map_variables=map_variables)
231
- return data
232
-
233
-
234
173
  def get_srml(station, start, end, filetype='PO', map_variables=True,
235
174
  url="http://solardata.uoregon.edu/download/Archive/"):
236
175
  """Request data from UoO SRML and read it into a Dataframe.
pvlib/irradiance.py CHANGED
@@ -16,22 +16,18 @@ from scipy.optimize import bisect
16
16
  from pvlib import atmosphere, solarposition, tools
17
17
  import pvlib # used to avoid dni name collision in complete_irradiance
18
18
 
19
+ from pvlib._deprecation import pvlibDeprecationWarning
20
+ import warnings
19
21
 
20
- # see References section of get_ground_diffuse function
21
- SURFACE_ALBEDOS = {'urban': 0.18,
22
- 'grass': 0.20,
23
- 'fresh grass': 0.26,
24
- 'soil': 0.17,
25
- 'sand': 0.40,
26
- 'snow': 0.65,
27
- 'fresh snow': 0.75,
28
- 'asphalt': 0.12,
29
- 'concrete': 0.30,
30
- 'aluminum': 0.85,
31
- 'copper': 0.74,
32
- 'fresh steel': 0.35,
33
- 'dirty steel': 0.08,
34
- 'sea': 0.06}
22
+
23
+ # Deprecation warning based on https://peps.python.org/pep-0562/
24
+ def __getattr__(attr):
25
+ if attr == 'SURFACE_ALBEDOS':
26
+ warnings.warn(f"{attr} has been moved to the albedo module as of "
27
+ "v0.11.0. Please use pvlib.albedo.SURFACE_ALBEDOS.",
28
+ pvlibDeprecationWarning)
29
+ return pvlib.albedo.SURFACE_ALBEDOS
30
+ raise AttributeError(f"module {__name__!r} has no attribute {attr!r}")
35
31
 
36
32
 
37
33
  def get_extra_radiation(datetime_or_doy, solar_constant=1366.1,
@@ -232,48 +228,6 @@ def aoi(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth):
232
228
  return aoi_value
233
229
 
234
230
 
235
- def poa_horizontal_ratio(surface_tilt, surface_azimuth,
236
- solar_zenith, solar_azimuth):
237
- """
238
- Calculates the ratio of the beam components of the plane of array
239
- irradiance and the horizontal irradiance.
240
-
241
- Input all angles in degrees.
242
-
243
- Parameters
244
- ----------
245
- surface_tilt : numeric
246
- Panel tilt from horizontal.
247
- surface_azimuth : numeric
248
- Panel azimuth from north.
249
- solar_zenith : numeric
250
- Solar zenith angle.
251
- solar_azimuth : numeric
252
- Solar azimuth angle.
253
-
254
- Returns
255
- -------
256
- ratio : numeric
257
- Ratio of the plane of array irradiance to the horizontal plane
258
- irradiance
259
- """
260
-
261
- cos_poa_zen = aoi_projection(surface_tilt, surface_azimuth,
262
- solar_zenith, solar_azimuth)
263
-
264
- cos_solar_zenith = tools.cosd(solar_zenith)
265
-
266
- # ratio of tilted and horizontal beam irradiance
267
- ratio = cos_poa_zen / cos_solar_zenith
268
-
269
- try:
270
- ratio.name = 'poa_ratio'
271
- except AttributeError:
272
- pass
273
-
274
- return ratio
275
-
276
-
277
231
  def beam_component(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth,
278
232
  dni):
279
233
  """
@@ -450,6 +404,11 @@ def get_sky_diffuse(surface_tilt, surface_azimuth,
450
404
  require ``'dni_extra'``. Values can be calculated using
451
405
  :py:func:`~pvlib.irradiance.get_extra_radiation`.
452
406
 
407
+ The ``'Perez'`` transposition model features discontinuities in the
408
+ predicted tilted diffuse irradiance due to relying on discrete input
409
+ values. For applications that benefit from continuous output, consider
410
+ using :py:func:`~pvlib.irradiance.perez_driesse`.
411
+
453
412
  The ``'perez'`` and ``'perez-driesse'`` models require relative airmass
454
413
  (``airmass``) as input. If ``airmass`` is not provided, it is calculated
455
414
  using the defaults in :py:func:`~pvlib.atmosphere.get_relative_airmass`.
@@ -592,7 +551,7 @@ def get_ground_diffuse(surface_tilt, ghi, albedo=.25, surface_type=None):
592
551
  Notes
593
552
  -----
594
553
  Table of albedo values by ``surface_type`` are from [2]_, [3]_, [4]_;
595
- see :py:data:`~pvlib.irradiance.SURFACE_ALBEDOS`.
554
+ see :py:const:`~pvlib.albedo.SURFACE_ALBEDOS`.
596
555
 
597
556
  References
598
557
  ----------
@@ -607,7 +566,7 @@ def get_ground_diffuse(surface_tilt, ghi, albedo=.25, surface_type=None):
607
566
  '''
608
567
 
609
568
  if surface_type is not None:
610
- albedo = SURFACE_ALBEDOS[surface_type]
569
+ albedo = pvlib.albedo.SURFACE_ALBEDOS[surface_type]
611
570
 
612
571
  diffuse_irrad = ghi * albedo * (1 - np.cos(np.radians(surface_tilt))) * 0.5
613
572
 
@@ -896,8 +855,8 @@ def reindl(surface_tilt, surface_azimuth, dhi, dni, ghi, dni_extra,
896
855
 
897
856
  .. math::
898
857
 
899
- I_{d} = DHI (A R_b + (1 - A) (\frac{1 + \cos\beta}{2})
900
- (1 + \sqrt{\frac{I_{hb}}{I_h}} \sin^3(\beta/2)) )
858
+ I_{d} = DHI \left(A R_b + (1 - A) \left(\frac{1 + \cos\beta}{2}\right)
859
+ \left(1 + \sqrt{\frac{I_{hb}}{I_h}} \sin^3(\beta/2)\right) \right)
901
860
 
902
861
  Reindl's 1990 model determines the diffuse irradiance from the sky
903
862
  (ground reflected irradiance is not included in this algorithm) on a
@@ -1050,6 +1009,13 @@ def perez(surface_tilt, surface_azimuth, dhi, dni, dni_extra,
1050
1009
  pressure-corrected) airmass. Optionally a selector may be used to
1051
1010
  use any of Perez's model coefficient sets.
1052
1011
 
1012
+ Warning
1013
+ -------
1014
+ The Perez transposition model features discontinuities in the
1015
+ predicted tilted diffuse irradiance due to relying on discrete input
1016
+ values. For applications that benefit from continuous output, consider
1017
+ using :py:func:`~pvlib.irradiance.perez_driesse`.
1018
+
1053
1019
  Parameters
1054
1020
  ----------
1055
1021
  surface_tilt : numeric
@@ -1551,9 +1517,9 @@ def ghi_from_poa_driesse_2023(surface_tilt, surface_azimuth,
1551
1517
  Solar azimuth angle. [degree]
1552
1518
  poa_global : numeric
1553
1519
  Plane-of-array global irradiance, aka global tilted irradiance. [W/m^2]
1554
- dni_extra : None or numeric, default None
1520
+ dni_extra : numeric, optional
1555
1521
  Extraterrestrial direct normal irradiance. [W/m^2]
1556
- airmass : None or numeric, default None
1522
+ airmass : numeric, optional
1557
1523
  Relative airmass (not adjusted for pressure). [unitless]
1558
1524
  albedo : numeric, default 0.25
1559
1525
  Ground surface albedo. [unitless]
@@ -2334,10 +2300,10 @@ def gti_dirint(poa_global, aoi, solar_zenith, solar_azimuth, times,
2334
2300
  data : DataFrame
2335
2301
  Contains the following keys/columns:
2336
2302
 
2337
- * ``ghi``: the modeled global horizontal irradiance in W/m^2.
2338
- * ``dni``: the modeled direct normal irradiance in W/m^2.
2339
- * ``dhi``: the modeled diffuse horizontal irradiance in
2340
- W/m^2.
2303
+ * ``ghi``: the modeled global horizontal irradiance in W/m^2.
2304
+ * ``dni``: the modeled direct normal irradiance in W/m^2.
2305
+ * ``dhi``: the modeled diffuse horizontal irradiance in
2306
+ W/m^2.
2341
2307
 
2342
2308
  References
2343
2309
  ----------
@@ -2484,7 +2450,6 @@ def _gti_dirint_lt_90(poa_global, aoi, aoi_lt_90, solar_zenith, solar_azimuth,
2484
2450
  else:
2485
2451
  # we are here because we ran out of coeffs to loop over and
2486
2452
  # therefore we have exceeded max_iterations
2487
- import warnings
2488
2453
  failed_points = best_diff[aoi_lt_90][~best_diff_lte_1_lt_90]
2489
2454
  warnings.warn(
2490
2455
  ('%s points failed to converge after %s iterations. best_diff:\n%s'
@@ -2618,11 +2583,11 @@ def erbs(ghi, zenith, datetime_or_doy, min_cos_zenith=0.065, max_zenith=87):
2618
2583
  data : OrderedDict or DataFrame
2619
2584
  Contains the following keys/columns:
2620
2585
 
2621
- * ``dni``: the modeled direct normal irradiance in W/m^2.
2622
- * ``dhi``: the modeled diffuse horizontal irradiance in
2623
- W/m^2.
2624
- * ``kt``: Ratio of global to extraterrestrial irradiance
2625
- on a horizontal plane.
2586
+ * ``dni``: the modeled direct normal irradiance in W/m^2.
2587
+ * ``dhi``: the modeled diffuse horizontal irradiance in
2588
+ W/m^2.
2589
+ * ``kt``: Ratio of global to extraterrestrial irradiance
2590
+ on a horizontal plane.
2626
2591
 
2627
2592
  References
2628
2593
  ----------
@@ -2721,11 +2686,11 @@ def erbs_driesse(ghi, zenith, datetime_or_doy=None, dni_extra=None,
2721
2686
  data : OrderedDict or DataFrame
2722
2687
  Contains the following keys/columns:
2723
2688
 
2724
- * ``dni``: the modeled direct normal irradiance in W/m^2.
2725
- * ``dhi``: the modeled diffuse horizontal irradiance in
2726
- W/m^2.
2727
- * ``kt``: Ratio of global to extraterrestrial irradiance
2728
- on a horizontal plane.
2689
+ * ``dni``: the modeled direct normal irradiance in W/m^2.
2690
+ * ``dhi``: the modeled diffuse horizontal irradiance in
2691
+ W/m^2.
2692
+ * ``kt``: Ratio of global to extraterrestrial irradiance
2693
+ on a horizontal plane.
2729
2694
 
2730
2695
  Raises
2731
2696
  ------
@@ -2839,17 +2804,17 @@ def orgill_hollands(ghi, zenith, datetime_or_doy, dni_extra=None,
2839
2804
  data : OrderedDict or DataFrame
2840
2805
  Contains the following keys/columns:
2841
2806
 
2842
- * ``dni``: the modeled direct normal irradiance in W/m^2.
2843
- * ``dhi``: the modeled diffuse horizontal irradiance in
2844
- W/m^2.
2845
- * ``kt``: Ratio of global to extraterrestrial irradiance
2846
- on a horizontal plane.
2807
+ * ``dni``: the modeled direct normal irradiance in W/m^2.
2808
+ * ``dhi``: the modeled diffuse horizontal irradiance in
2809
+ W/m^2.
2810
+ * ``kt``: Ratio of global to extraterrestrial irradiance
2811
+ on a horizontal plane.
2847
2812
 
2848
2813
  References
2849
2814
  ----------
2850
2815
  .. [1] Orgill, J.F., Hollands, K.G.T., Correlation equation for hourly
2851
- diffuse radiation on a horizontal surface, Solar Energy 19(4), pp 357–359,
2852
- 1977. Eqs. 3(a), 3(b) and 3(c)
2816
+ diffuse radiation on a horizontal surface, Solar Energy 19(4),
2817
+ pp 357–359, 1977. Eqs. 3(a), 3(b) and 3(c)
2853
2818
  :doi:`10.1016/0038-092X(77)90006-8`
2854
2819
 
2855
2820
  See Also
@@ -2934,11 +2899,11 @@ def boland(ghi, solar_zenith, datetime_or_doy, a_coeff=8.645, b_coeff=0.613,
2934
2899
  data : OrderedDict or DataFrame
2935
2900
  Contains the following keys/columns:
2936
2901
 
2937
- * ``dni``: the modeled direct normal irradiance in W/m^2.
2938
- * ``dhi``: the modeled diffuse horizontal irradiance in
2939
- W/m^2.
2940
- * ``kt``: Ratio of global to extraterrestrial irradiance
2941
- on a horizontal plane.
2902
+ * ``dni``: the modeled direct normal irradiance in W/m^2.
2903
+ * ``dhi``: the modeled diffuse horizontal irradiance in
2904
+ W/m^2.
2905
+ * ``kt``: Ratio of global to extraterrestrial irradiance
2906
+ on a horizontal plane.
2942
2907
 
2943
2908
  References
2944
2909
  ----------
@@ -3771,11 +3736,11 @@ def louche(ghi, solar_zenith, datetime_or_doy, max_zenith=90):
3771
3736
  data: OrderedDict or DataFrame
3772
3737
  Contains the following keys/columns:
3773
3738
 
3774
- * ``dni``: the modeled direct normal irradiance in W/m^2.
3775
- * ``dhi``: the modeled diffuse horizontal irradiance in
3776
- W/m^2.
3777
- * ``kt``: Ratio of global to extraterrestrial irradiance
3778
- on a horizontal plane.
3739
+ * ``dni``: the modeled direct normal irradiance in W/m^2.
3740
+ * ``dhi``: the modeled diffuse horizontal irradiance in
3741
+ W/m^2.
3742
+ * ``kt``: Ratio of global to extraterrestrial irradiance
3743
+ on a horizontal plane.
3779
3744
 
3780
3745
  References
3781
3746
  -------
@@ -3808,3 +3773,76 @@ def louche(ghi, solar_zenith, datetime_or_doy, max_zenith=90):
3808
3773
  data = pd.DataFrame(data, index=datetime_or_doy)
3809
3774
 
3810
3775
  return data
3776
+
3777
+
3778
+ def diffuse_par_spitters(daily_solar_zenith, global_diffuse_fraction):
3779
+ r"""
3780
+ Derive daily diffuse fraction of Photosynthetically Active Radiation (PAR)
3781
+ from daily average solar zenith and diffuse fraction of daily insolation.
3782
+
3783
+ The relationship is based on the work of Spitters et al. (1986) [1]_. The
3784
+ resulting value is the fraction of daily PAR that is diffuse.
3785
+
3786
+ .. note::
3787
+ The diffuse fraction is defined as the ratio of
3788
+ diffuse to global daily insolation, in J m⁻² day⁻¹ or equivalent.
3789
+
3790
+ Parameters
3791
+ ----------
3792
+ daily_solar_zenith : numeric
3793
+ Average daily solar zenith angle. In degrees [°].
3794
+
3795
+ global_diffuse_fraction : numeric
3796
+ Fraction of daily global broadband insolation that is diffuse.
3797
+ Unitless [0, 1].
3798
+
3799
+ Returns
3800
+ -------
3801
+ par_diffuse_fraction : numeric
3802
+ Fraction of daily photosynthetically active radiation (PAR) that is
3803
+ diffuse. Unitless [0, 1].
3804
+
3805
+ Notes
3806
+ -----
3807
+ The relationship is given by equations (9) & (10) in [1]_ and (1) in [2]_:
3808
+
3809
+ .. math::
3810
+
3811
+ k_{diffuse\_PAR}^{model} = \frac{PAR_{diffuse}}{PAR_{total}} =
3812
+ \frac{\left[1 + 0.3 \left(1 - \left(k_d\right) ^2\right)\right]
3813
+ k_d}
3814
+ {1 + \left(1 - \left(k_d\right)^2\right) \cos ^2 (90 - \beta)
3815
+ \cos ^3 \beta}
3816
+
3817
+ where :math:`k_d` is the diffuse fraction of the global insolation, and
3818
+ :math:`\beta` is the daily average of the solar elevation angle in degrees.
3819
+
3820
+ A comparison using different models for the diffuse fraction of
3821
+ the global insolation can be found in [2]_ in the context of Sweden.
3822
+
3823
+ References
3824
+ ----------
3825
+ .. [1] C. J. T. Spitters, H. A. J. M. Toussaint, and J. Goudriaan,
3826
+ 'Separating the diffuse and direct component of global radiation and its
3827
+ implications for modeling canopy photosynthesis Part I. Components of
3828
+ incoming radiation', Agricultural and Forest Meteorology, vol. 38,
3829
+ no. 1, pp. 217-229, Oct. 1986, :doi:`10.1016/0168-1923(86)90060-2`.
3830
+ .. [2] S. Ma Lu et al., 'Photosynthetically active radiation decomposition
3831
+ models for agrivoltaic systems applications', Solar Energy, vol. 244,
3832
+ pp. 536-549, Sep. 2022, :doi:`10.1016/j.solener.2022.05.046`.
3833
+ """
3834
+ # notation change:
3835
+ # cosd(90-x) = sind(x) and 90-solar_elevation = solar_zenith
3836
+ cosd_solar_zenith = tools.cosd(daily_solar_zenith)
3837
+ cosd_solar_elevation = tools.sind(daily_solar_zenith)
3838
+ par_diffuse_fraction = (
3839
+ (1 + 0.3 * (1 - global_diffuse_fraction**2))
3840
+ * global_diffuse_fraction
3841
+ / (
3842
+ 1
3843
+ + (1 - global_diffuse_fraction**2)
3844
+ * cosd_solar_zenith**2
3845
+ * cosd_solar_elevation**3
3846
+ )
3847
+ )
3848
+ return par_diffuse_fraction