pvlib 0.11.1__py3-none-any.whl → 0.11.2__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 (43) hide show
  1. pvlib/_deprecation.py +73 -0
  2. pvlib/atmosphere.py +79 -0
  3. pvlib/clearsky.py +35 -22
  4. pvlib/data/pvgis_tmy_test.csv +8761 -0
  5. pvlib/data/tmy_45.000_8.000_2005_2023.csv +8789 -0
  6. pvlib/data/tmy_45.000_8.000_2005_2023.epw +8768 -0
  7. pvlib/data/tmy_45.000_8.000_2005_2023.json +1 -0
  8. pvlib/data/tmy_45.000_8.000_2005_2023.txt +8761 -0
  9. pvlib/data/tmy_45.000_8.000_userhorizon.json +1 -1
  10. pvlib/iam.py +4 -4
  11. pvlib/iotools/midc.py +1 -1
  12. pvlib/iotools/pvgis.py +0 -10
  13. pvlib/irradiance.py +98 -55
  14. pvlib/ivtools/sdm.py +75 -52
  15. pvlib/pvsystem.py +132 -84
  16. pvlib/solarposition.py +46 -30
  17. pvlib/spa.py +4 -2
  18. pvlib/spectrum/irradiance.py +2 -1
  19. pvlib/spectrum/spectrl2.py +2 -1
  20. pvlib/temperature.py +49 -3
  21. pvlib/tests/iotools/test_pvgis.py +9 -9
  22. pvlib/tests/ivtools/test_sdm.py +23 -1
  23. pvlib/tests/test__deprecation.py +97 -0
  24. pvlib/tests/test_atmosphere.py +147 -0
  25. pvlib/tests/test_clearsky.py +7 -1
  26. pvlib/tests/test_conftest.py +0 -44
  27. pvlib/tests/test_irradiance.py +56 -10
  28. pvlib/tests/test_pvsystem.py +17 -1
  29. pvlib/tests/test_solarposition.py +33 -0
  30. pvlib/tests/test_spa.py +29 -0
  31. {pvlib-0.11.1.dist-info → pvlib-0.11.2.dist-info}/METADATA +11 -10
  32. {pvlib-0.11.1.dist-info → pvlib-0.11.2.dist-info}/RECORD +36 -37
  33. {pvlib-0.11.1.dist-info → pvlib-0.11.2.dist-info}/WHEEL +1 -1
  34. pvlib/data/aod550_tcwv_20121101_test.nc +0 -0
  35. pvlib/data/pvgis_tmy_test.dat +0 -8761
  36. pvlib/data/tmy_45.000_8.000_2005_2020.csv +0 -8789
  37. pvlib/data/tmy_45.000_8.000_2005_2020.epw +0 -8768
  38. pvlib/data/tmy_45.000_8.000_2005_2020.json +0 -1
  39. pvlib/data/tmy_45.000_8.000_2005_2020.txt +0 -8761
  40. pvlib/data/variables_style_rules.csv +0 -56
  41. {pvlib-0.11.1.dist-info → pvlib-0.11.2.dist-info}/AUTHORS.md +0 -0
  42. {pvlib-0.11.1.dist-info → pvlib-0.11.2.dist-info}/LICENSE +0 -0
  43. {pvlib-0.11.1.dist-info → pvlib-0.11.2.dist-info}/top_level.txt +0 -0
pvlib/_deprecation.py CHANGED
@@ -316,3 +316,76 @@ def deprecated(since, message='', name='', alternative='', pending=False,
316
316
  return finalize(wrapper, new_doc)
317
317
 
318
318
  return deprecate
319
+
320
+
321
+ def renamed_kwarg_warning(since, old_param_name, new_param_name, removal=""):
322
+ """
323
+ Decorator to mark a possible keyword argument as deprecated and replaced
324
+ with other name.
325
+
326
+ Raises a warning when the deprecated argument is used, and replaces the
327
+ call with the new argument name. Does not modify the function signature.
328
+
329
+ .. warning::
330
+ Ensure ``removal`` date with a ``fail_on_pvlib_version`` decorator in
331
+ the test suite.
332
+
333
+ .. note::
334
+ Not compatible with positional-only arguments.
335
+
336
+ .. note::
337
+ Documentation for the function may updated to reflect the new parameter
338
+ name; it is suggested to add a |.. versionchanged::| directive.
339
+
340
+ Parameters
341
+ ----------
342
+ since : str
343
+ The release at which this API became deprecated.
344
+ old_param_name : str
345
+ The name of the deprecated parameter.
346
+ new_param_name : str
347
+ The name of the new parameter.
348
+ removal : str, optional
349
+ The expected removal version, in order to compose the Warning message.
350
+
351
+ Examples
352
+ --------
353
+ >>> @renamed_kwarg_warning("1.4.0", "old_name", "new_name", "1.6.0")
354
+ >>> def some_function(new_name=None):
355
+ >>> pass
356
+ >>> some_function(old_name=1)
357
+ Parameter 'old_name' has been renamed since 1.4.0. and
358
+ will be removed in 1.6.0. Please use 'new_name' instead.
359
+
360
+ >>> @renamed_kwarg_warning("1.4.0", "old_name", "new_name")
361
+ >>> def some_function(new_name=None):
362
+ >>> pass
363
+ >>> some_function(old_name=1)
364
+ Parameter 'old_name' has been renamed since 1.4.0. and
365
+ will be removed soon. Please use 'new_name' instead.
366
+ """
367
+
368
+ def deprecate(func, old=old_param_name, new=new_param_name, since=since):
369
+ def wrapper(*args, **kwargs):
370
+ if old in kwargs:
371
+ if new in kwargs:
372
+ raise ValueError(
373
+ f"{func.__name__} received both '{old}' and '{new}', "
374
+ "which are mutually exclusive since they refer to the "
375
+ f"same parameter. Please remove deprecated '{old}'."
376
+ )
377
+ warnings.warn(
378
+ f"Parameter '{old}' has been renamed since {since}. "
379
+ f"and will be removed "
380
+ + (f"in {removal}" if removal else "soon")
381
+ + f". Please use '{new}' instead.",
382
+ _projectWarning,
383
+ stacklevel=2,
384
+ )
385
+ kwargs[new] = kwargs.pop(old)
386
+ return func(*args, **kwargs)
387
+
388
+ wrapper = functools.wraps(func)(wrapper)
389
+ return wrapper
390
+
391
+ return deprecate
pvlib/atmosphere.py CHANGED
@@ -337,6 +337,85 @@ def gueymard94_pw(temp_air, relative_humidity):
337
337
  return pw
338
338
 
339
339
 
340
+ def rh_from_tdew(temp_air, temp_dew, coeff=(6.112, 17.62, 243.12)):
341
+ """
342
+ Calculate relative humidity from dewpoint temperature using the Magnus
343
+ equation.
344
+
345
+ Parameters
346
+ ----------
347
+ temp_air : numeric
348
+ Air temperature (dry-bulb temperature). [°C]
349
+ temp_dew : numeric
350
+ Dew-point temperature. [°C]
351
+ coeff : tuple, default (6.112, 17.62, 243.12)
352
+ Magnus equation coefficients (A, B, C). The default values are those
353
+ recommended by the WMO [1]_.
354
+
355
+ Returns
356
+ -------
357
+ numeric
358
+ Relative humidity (0.0-100.0). [%]
359
+
360
+ References
361
+ ----------
362
+ .. [1] "Guide to Instruments and Methods of Observation",
363
+ World Meteorological Organization, WMO-No. 8, 2023.
364
+ https://library.wmo.int/idurl/4/68695
365
+ """
366
+
367
+ # Calculate vapor pressure (e) and saturation vapor pressure (es)
368
+ e = coeff[0] * np.exp((coeff[1] * temp_air) / (coeff[2] + temp_air))
369
+ es = coeff[0] * np.exp((coeff[1] * temp_dew) / (coeff[2] + temp_dew))
370
+
371
+ # Calculate relative humidity as percentage
372
+ relative_humidity = 100 * (es / e)
373
+
374
+ return relative_humidity
375
+
376
+
377
+ def tdew_from_rh(temp_air, relative_humidity, coeff=(6.112, 17.62, 243.12)):
378
+ """
379
+ Calculate dewpoint temperature using the Magnus equation.
380
+ This is a reversal of the calculation in :py:func:`rh_from_tdew`.
381
+
382
+ Parameters
383
+ ----------
384
+ temp_air : numeric
385
+ Air temperature (dry-bulb temperature). [°C]
386
+ relative_humidity : numeric
387
+ Relative humidity (0-100). [%]
388
+ coeff: tuple, default (6.112, 17.62, 243.12)
389
+ Magnus equation coefficients (A, B, C). The default values are those
390
+ recommended by the WMO [1]_.
391
+
392
+ Returns
393
+ -------
394
+ numeric
395
+ Dewpoint temperature. [°C]
396
+
397
+ References
398
+ ----------
399
+ .. [1] "Guide to Instruments and Methods of Observation",
400
+ World Meteorological Organization, WMO-No. 8, 2023.
401
+ https://library.wmo.int/idurl/4/68695
402
+ """
403
+ # Calculate the term inside the log
404
+ # From RH = 100 * (es/e), we get es = (RH/100) * e
405
+ # Substituting the Magnus equation and solving for dewpoint
406
+
407
+ # First calculate ln(es/A)
408
+ ln_term = (
409
+ (coeff[1] * temp_air) / (coeff[2] + temp_air)
410
+ + np.log(relative_humidity/100)
411
+ )
412
+
413
+ # Then solve for dewpoint
414
+ dewpoint = coeff[2] * ln_term / (coeff[1] - ln_term)
415
+
416
+ return dewpoint
417
+
418
+
340
419
  first_solar_spectral_correction = deprecated(
341
420
  since='0.10.0',
342
421
  alternative='pvlib.spectrum.spectral_factor_firstsolar'
pvlib/clearsky.py CHANGED
@@ -327,13 +327,13 @@ def haurwitz(apparent_zenith):
327
327
  '''
328
328
 
329
329
  cos_zenith = tools.cosd(apparent_zenith.values)
330
- clearsky_ghi = np.zeros_like(apparent_zenith.values)
330
+ ghi_clear = np.zeros_like(apparent_zenith.values)
331
331
  cos_zen_gte_0 = cos_zenith > 0
332
- clearsky_ghi[cos_zen_gte_0] = (1098.0 * cos_zenith[cos_zen_gte_0] *
333
- np.exp(-0.059/cos_zenith[cos_zen_gte_0]))
332
+ ghi_clear[cos_zen_gte_0] = (1098.0 * cos_zenith[cos_zen_gte_0] *
333
+ np.exp(-0.059/cos_zenith[cos_zen_gte_0]))
334
334
 
335
335
  df_out = pd.DataFrame(index=apparent_zenith.index,
336
- data=clearsky_ghi,
336
+ data=ghi_clear,
337
337
  columns=['ghi'])
338
338
 
339
339
  return df_out
@@ -683,22 +683,26 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False,
683
683
  var_diff=0.005, slope_dev=8, max_iterations=20,
684
684
  return_components=False):
685
685
  """
686
- Detects clear sky times according to the algorithm developed by Reno
687
- and Hansen for GHI measurements. The algorithm [1]_ was designed and
688
- validated for analyzing GHI time series only. Users may attempt to
689
- apply it to other types of time series data using different filter
690
- settings, but should be skeptical of the results.
686
+ Detects clear sky times using the algorithm developed by Reno
687
+ and Hansen.
691
688
 
692
- The algorithm detects clear sky times by comparing statistics for a
689
+ The algorithm [1]_ was designed and
690
+ validated for analyzing GHI time series. Jordan and Hansen [2]_ extended
691
+ the algorithm to plane-of-array (POA) irradiance measurements.
692
+
693
+ The algorithm [1]_ detects clear sky times by comparing statistics for a
693
694
  measured time series and an expected clearsky time series.
694
695
  Statistics are calculated using a sliding time window (e.g., 10
695
696
  minutes). An iterative algorithm identifies clear periods, uses the
696
697
  identified periods to estimate bias in the clearsky data, scales the
697
698
  clearsky data and repeats.
698
699
 
699
- Clear times are identified by meeting 5 criteria. Default values for
700
+ Clear times are identified by meeting five criteria. Default values for
700
701
  these thresholds are appropriate for 10 minute windows of 1 minute
701
- GHI data.
702
+ GHI data. For data at longer intervals, it is recommended
703
+ to set ``infer_limits=True`` to use the thresholds from [2]_.
704
+
705
+ For POA data, ``clearsky`` must be on the same plane as ``measured``.
702
706
 
703
707
  Parameters
704
708
  ----------
@@ -713,8 +717,8 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False,
713
717
  If True, does not use passed in kwargs (or defaults), but instead
714
718
  interpolates these values from Table 1 in [2]_.
715
719
  window_length : int, default 10
716
- Length of sliding time window in minutes. Must be greater than 2
717
- periods.
720
+ Length of sliding time window in minutes. Each window must contain at
721
+ least three data points.
718
722
  mean_diff : float, default 75
719
723
  Threshold value for agreement between mean values of measured
720
724
  and clearsky in each interval, see Eq. 6 in [1]. [W/m2]
@@ -723,8 +727,6 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False,
723
727
  clearsky values in each interval, see Eq. 7 in [1]. [W/m2]
724
728
  lower_line_length : float, default -5
725
729
  Lower limit of line length criterion from Eq. 8 in [1].
726
- Criterion satisfied when lower_line_length < line length difference
727
- < upper_line_length.
728
730
  upper_line_length : float, default 10
729
731
  Upper limit of line length criterion from Eq. 8 in [1].
730
732
  var_diff : float, default 0.005
@@ -736,7 +738,7 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False,
736
738
  change in successive values, see Eqs. 12 through 14 in [1].
737
739
  max_iterations : int, default 20
738
740
  Maximum number of times to apply a different scaling factor to
739
- the clearsky and redetermine clear_samples. Must be 1 or larger.
741
+ the clearsky and redetermine ``clear_samples``. Must be 1 or larger.
740
742
  return_components : bool, default False
741
743
  Controls if additional output should be returned. See below.
742
744
 
@@ -748,19 +750,23 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False,
748
750
 
749
751
  components : OrderedDict, optional
750
752
  Dict of arrays of whether or not the given time window is clear
751
- for each condition. Only provided if return_components is True.
753
+ for each condition. Only provided if ``return_components`` is True.
752
754
 
753
755
  alpha : scalar, optional
754
- Scaling factor applied to the clearsky_ghi to obtain the
755
- detected clear_samples. Only provided if return_components is
756
+ Scaling factor applied to ``clearsky`` to obtain the
757
+ detected ``clear_samples``. Only provided if ``return_components`` is
756
758
  True.
757
759
 
758
760
  Raises
759
761
  ------
760
762
  ValueError
761
- If measured is not a Series and times is not provided
763
+ If ``measured`` is not a Series and times is not provided.
764
+ ValueError
765
+ If a window contains less than three data points.
766
+ ValueError
767
+ If the measured data is not sufficient to fill a window.
762
768
  NotImplementedError
763
- If timestamps are not equally spaced
769
+ If timestamps are not equally spaced.
764
770
 
765
771
  References
766
772
  ----------
@@ -812,6 +818,13 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False,
812
818
  sample_interval, samples_per_window = \
813
819
  tools._get_sample_intervals(times, window_length)
814
820
 
821
+ if samples_per_window < 3:
822
+ raise ValueError(f"Samples per window of {samples_per_window}"
823
+ " found. Each window must contain at least 3 data"
824
+ " points."
825
+ f" Window length of {window_length} found; increase"
826
+ f" window length to {3*sample_interval} or longer.")
827
+
815
828
  # if infer_limits, find threshold values using the sample interval
816
829
  if infer_limits:
817
830
  window_length, mean_diff, max_diff, lower_line_length, \