pvlib 0.9.5__py3-none-any.whl → 0.10.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. pvlib/__init__.py +3 -2
  2. pvlib/atmosphere.py +6 -171
  3. pvlib/bifacial/infinite_sheds.py +30 -267
  4. pvlib/bifacial/utils.py +225 -5
  5. pvlib/data/test_psm3_2017.csv +17521 -17521
  6. pvlib/data/test_read_psm3.csv +17522 -17522
  7. pvlib/data/test_read_pvgis_horizon.csv +49 -0
  8. pvlib/data/variables_style_rules.csv +3 -0
  9. pvlib/iam.py +17 -4
  10. pvlib/inverter.py +6 -1
  11. pvlib/iotools/__init__.py +7 -2
  12. pvlib/iotools/acis.py +516 -0
  13. pvlib/iotools/midc.py +4 -4
  14. pvlib/iotools/psm3.py +32 -31
  15. pvlib/iotools/pvgis.py +84 -28
  16. pvlib/iotools/sodapro.py +8 -6
  17. pvlib/iotools/srml.py +121 -18
  18. pvlib/iotools/surfrad.py +2 -2
  19. pvlib/iotools/tmy.py +146 -102
  20. pvlib/irradiance.py +151 -0
  21. pvlib/ivtools/sde.py +11 -7
  22. pvlib/ivtools/sdm.py +16 -10
  23. pvlib/ivtools/utils.py +6 -6
  24. pvlib/location.py +3 -2
  25. pvlib/modelchain.py +67 -70
  26. pvlib/pvsystem.py +160 -532
  27. pvlib/shading.py +41 -0
  28. pvlib/singlediode.py +215 -65
  29. pvlib/soiling.py +3 -3
  30. pvlib/spa.py +327 -368
  31. pvlib/spectrum/__init__.py +8 -2
  32. pvlib/spectrum/mismatch.py +335 -0
  33. pvlib/temperature.py +1 -8
  34. pvlib/tests/bifacial/test_infinite_sheds.py +0 -111
  35. pvlib/tests/bifacial/test_utils.py +101 -4
  36. pvlib/tests/conftest.py +0 -31
  37. pvlib/tests/iotools/test_acis.py +213 -0
  38. pvlib/tests/iotools/test_midc.py +6 -6
  39. pvlib/tests/iotools/test_psm3.py +3 -3
  40. pvlib/tests/iotools/test_pvgis.py +21 -14
  41. pvlib/tests/iotools/test_sodapro.py +1 -1
  42. pvlib/tests/iotools/test_srml.py +71 -6
  43. pvlib/tests/iotools/test_tmy.py +43 -8
  44. pvlib/tests/ivtools/test_sde.py +19 -17
  45. pvlib/tests/ivtools/test_sdm.py +9 -4
  46. pvlib/tests/test_atmosphere.py +6 -62
  47. pvlib/tests/test_iam.py +12 -0
  48. pvlib/tests/test_irradiance.py +40 -2
  49. pvlib/tests/test_location.py +1 -1
  50. pvlib/tests/test_modelchain.py +33 -76
  51. pvlib/tests/test_pvsystem.py +366 -201
  52. pvlib/tests/test_shading.py +28 -0
  53. pvlib/tests/test_singlediode.py +166 -30
  54. pvlib/tests/test_soiling.py +8 -7
  55. pvlib/tests/test_spa.py +6 -7
  56. pvlib/tests/test_spectrum.py +145 -1
  57. pvlib/tests/test_temperature.py +0 -7
  58. pvlib/tests/test_tools.py +25 -0
  59. pvlib/tests/test_tracking.py +0 -149
  60. pvlib/tools.py +26 -1
  61. pvlib/tracking.py +1 -269
  62. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/METADATA +1 -9
  63. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/RECORD +67 -68
  64. pvlib/forecast.py +0 -1211
  65. pvlib/iotools/ecmwf_macc.py +0 -312
  66. pvlib/tests/iotools/test_ecmwf_macc.py +0 -162
  67. pvlib/tests/test_forecast.py +0 -228
  68. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/AUTHORS.md +0 -0
  69. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/LICENSE +0 -0
  70. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/WHEEL +0 -0
  71. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/top_level.txt +0 -0
pvlib/pvsystem.py CHANGED
@@ -16,11 +16,12 @@ from dataclasses import dataclass
16
16
  from abc import ABC, abstractmethod
17
17
  from typing import Optional
18
18
 
19
- from pvlib._deprecation import deprecated
19
+ from pvlib._deprecation import deprecated, warn_deprecated
20
20
 
21
21
  from pvlib import (atmosphere, iam, inverter, irradiance,
22
- singlediode as _singlediode, temperature)
22
+ singlediode as _singlediode, spectrum, temperature)
23
23
  from pvlib.tools import _build_kwargs, _build_args
24
+ import pvlib.tools as tools
24
25
 
25
26
 
26
27
  # a dict of required parameter names for each DC power model
@@ -68,37 +69,6 @@ def _unwrap_single_value(func):
68
69
  return f
69
70
 
70
71
 
71
- def _check_deprecated_passthrough(func):
72
- """
73
- Decorator to warn or error when getting and setting the "pass-through"
74
- PVSystem properties that have been moved to Array. Emits a warning for
75
- PVSystems with only one Array and raises an error for PVSystems with
76
- more than one Array.
77
- """
78
-
79
- @functools.wraps(func)
80
- def wrapper(self, *args, **kwargs):
81
- pvsystem_attr = func.__name__
82
- class_name = self.__class__.__name__ # PVSystem or SingleAxisTracker
83
- overrides = { # some Array attrs aren't the same as PVSystem
84
- 'strings_per_inverter': 'strings',
85
- }
86
- array_attr = overrides.get(pvsystem_attr, pvsystem_attr)
87
- alternative = f'{class_name}.arrays[i].{array_attr}'
88
-
89
- if len(self.arrays) > 1:
90
- raise AttributeError(
91
- f'{class_name}.{pvsystem_attr} not supported for multi-array '
92
- f'systems. Set {array_attr} for each Array in '
93
- f'{class_name}.arrays instead.')
94
-
95
- wrapped = deprecated('0.9', alternative=alternative, removal='0.10',
96
- name=f"{class_name}.{pvsystem_attr}")(func)
97
- return wrapped(self, *args, **kwargs)
98
-
99
- return wrapper
100
-
101
-
102
72
  # not sure if this belongs in the pvsystem module.
103
73
  # maybe something more like core.py? It may eventually grow to
104
74
  # import a lot more functionality from other modules.
@@ -218,7 +188,6 @@ class PVSystem:
218
188
  See also
219
189
  --------
220
190
  pvlib.location.Location
221
- pvlib.tracking.SingleAxisTracker
222
191
  """
223
192
 
224
193
  def __init__(self,
@@ -636,44 +605,11 @@ class PVSystem:
636
605
  in zip(self.arrays, effective_irradiance, temp_cell)
637
606
  )
638
607
 
639
- @deprecated('0.9', alternative='PVSystem.get_cell_temperature',
640
- removal='0.10.0')
641
- def sapm_celltemp(self, poa_global, temp_air, wind_speed):
642
- """Uses :py:func:`pvlib.temperature.sapm_cell` to calculate cell
643
- temperatures.
644
-
645
- Parameters
646
- ----------
647
- poa_global : numeric or tuple of numeric
648
- Total incident irradiance in W/m^2.
649
-
650
- temp_air : numeric or tuple of numeric
651
- Ambient dry bulb temperature in degrees C.
652
-
653
- wind_speed : numeric or tuple of numeric
654
- Wind speed in m/s at a height of 10 meters.
655
-
656
- Returns
657
- -------
658
- numeric or tuple of numeric
659
- values in degrees C.
660
-
661
- Notes
662
- -----
663
- The `temp_air` and `wind_speed` parameters may be passed as tuples
664
- to provide different values for each Array in the system. If not
665
- passed as a tuple then the same value is used for input to each Array.
666
- If passed as a tuple the length must be the same as the number of
667
- Arrays.
668
- """
669
- return self.get_cell_temperature(poa_global, temp_air, wind_speed,
670
- model='sapm')
671
-
672
608
  @_unwrap_single_value
673
609
  def sapm_spectral_loss(self, airmass_absolute):
674
610
  """
675
- Use the :py:func:`sapm_spectral_loss` function, the input
676
- parameters, and ``self.module_parameters`` to calculate F1.
611
+ Use the :py:func:`pvlib.spectrum.spectral_factor_sapm` function,
612
+ the input parameters, and ``self.module_parameters`` to calculate F1.
677
613
 
678
614
  Parameters
679
615
  ----------
@@ -686,7 +622,8 @@ class PVSystem:
686
622
  The SAPM spectral loss coefficient.
687
623
  """
688
624
  return tuple(
689
- sapm_spectral_loss(airmass_absolute, array.module_parameters)
625
+ spectrum.spectral_factor_sapm(airmass_absolute,
626
+ array.module_parameters)
690
627
  for array in self.arrays
691
628
  )
692
629
 
@@ -729,162 +666,10 @@ class PVSystem:
729
666
  in zip(self.arrays, poa_direct, poa_diffuse, aoi)
730
667
  )
731
668
 
732
- @deprecated('0.9', alternative='PVSystem.get_cell_temperature',
733
- removal='0.10.0')
734
- def pvsyst_celltemp(self, poa_global, temp_air, wind_speed=1.0):
735
- """Uses :py:func:`pvlib.temperature.pvsyst_cell` to calculate cell
736
- temperature.
737
-
738
- Parameters
739
- ----------
740
- poa_global : numeric or tuple of numeric
741
- Total incident irradiance in W/m^2.
742
-
743
- temp_air : numeric or tuple of numeric
744
- Ambient dry bulb temperature in degrees C.
745
-
746
- wind_speed : numeric or tuple of numeric, default 1.0
747
- Wind speed in m/s measured at the same height for which the wind
748
- loss factor was determined. The default value is 1.0, which is
749
- the wind speed at module height used to determine NOCT.
750
-
751
- Returns
752
- -------
753
- numeric or tuple of numeric
754
- values in degrees C.
755
-
756
- Notes
757
- -----
758
- The `temp_air` and `wind_speed` parameters may be passed as tuples
759
- to provide different values for each Array in the system. If not
760
- passed as a tuple then the same value is used for input to each Array.
761
- If passed as a tuple the length must be the same as the number of
762
- Arrays.
763
- """
764
- return self.get_cell_temperature(poa_global, temp_air, wind_speed,
765
- model='pvsyst')
766
-
767
- @deprecated('0.9', alternative='PVSystem.get_cell_temperature',
768
- removal='0.10.0')
769
- def faiman_celltemp(self, poa_global, temp_air, wind_speed=1.0):
770
- """
771
- Use :py:func:`pvlib.temperature.faiman` to calculate cell temperature.
772
-
773
- Parameters
774
- ----------
775
- poa_global : numeric or tuple of numeric
776
- Total incident irradiance [W/m^2].
777
-
778
- temp_air : numeric or tuple of numeric
779
- Ambient dry bulb temperature [C].
780
-
781
- wind_speed : numeric or tuple of numeric, default 1.0
782
- Wind speed in m/s measured at the same height for which the wind
783
- loss factor was determined. The default value 1.0 m/s is the wind
784
- speed at module height used to determine NOCT. [m/s]
785
-
786
- Returns
787
- -------
788
- numeric or tuple of numeric
789
- values in degrees C.
790
-
791
- Notes
792
- -----
793
- The `temp_air` and `wind_speed` parameters may be passed as tuples
794
- to provide different values for each Array in the system. If not
795
- passed as a tuple then the same value is used for input to each Array.
796
- If passed as a tuple the length must be the same as the number of
797
- Arrays.
798
- """
799
- return self.get_cell_temperature(poa_global, temp_air, wind_speed,
800
- model='faiman')
801
-
802
- @deprecated('0.9', alternative='PVSystem.get_cell_temperature',
803
- removal='0.10.0')
804
- def fuentes_celltemp(self, poa_global, temp_air, wind_speed):
805
- """
806
- Use :py:func:`pvlib.temperature.fuentes` to calculate cell temperature.
807
-
808
- Parameters
809
- ----------
810
- poa_global : pandas Series or tuple of Series
811
- Total incident irradiance [W/m^2]
812
-
813
- temp_air : pandas Series or tuple of Series
814
- Ambient dry bulb temperature [C]
815
-
816
- wind_speed : pandas Series or tuple of Series
817
- Wind speed [m/s]
818
-
819
- Returns
820
- -------
821
- temperature_cell : Series or tuple of Series
822
- The modeled cell temperature [C]
823
-
824
- Notes
825
- -----
826
- The Fuentes thermal model uses the module surface tilt for convection
827
- modeling. The SAM implementation of PVWatts hardcodes the surface tilt
828
- value at 30 degrees, ignoring whatever value is used for irradiance
829
- transposition. If you want to match the PVWatts behavior you can
830
- either leave ``surface_tilt`` unspecified to use the PVWatts default
831
- of 30, or specify a ``surface_tilt`` value in the Array's
832
- ``temperature_model_parameters``.
833
-
834
- The `temp_air`, `wind_speed`, and `surface_tilt` parameters may be
835
- passed as tuples
836
- to provide different values for each Array in the system. If not
837
- passed as a tuple then the same value is used for input to each Array.
838
- If passed as a tuple the length must be the same as the number of
839
- Arrays.
840
- """
841
- return self.get_cell_temperature(poa_global, temp_air, wind_speed,
842
- model='fuentes')
843
-
844
- @deprecated('0.9', alternative='PVSystem.get_cell_temperature',
845
- removal='0.10.0')
846
- def noct_sam_celltemp(self, poa_global, temp_air, wind_speed,
847
- effective_irradiance=None):
848
- """
849
- Use :py:func:`pvlib.temperature.noct_sam` to calculate cell
850
- temperature.
851
-
852
- Parameters
853
- ----------
854
- poa_global : numeric or tuple of numeric
855
- Total incident irradiance in W/m^2.
856
-
857
- temp_air : numeric or tuple of numeric
858
- Ambient dry bulb temperature in degrees C.
859
-
860
- wind_speed : numeric or tuple of numeric
861
- Wind speed in m/s at a height of 10 meters.
862
-
863
- effective_irradiance : numeric, tuple of numeric, or None.
864
- The irradiance that is converted to photocurrent. If None,
865
- assumed equal to ``poa_global``. [W/m^2]
866
-
867
- Returns
868
- -------
869
- temperature_cell : numeric or tuple of numeric
870
- The modeled cell temperature [C]
871
-
872
- Notes
873
- -----
874
- The `temp_air` and `wind_speed` parameters may be passed as tuples
875
- to provide different values for each Array in the system. If not
876
- passed as a tuple then the same value is used for input to each Array.
877
- If passed as a tuple the length must be the same as the number of
878
- Arrays.
879
- """
880
- return self.get_cell_temperature(
881
- poa_global, temp_air, wind_speed, model='noct_sam',
882
- effective_irradiance=effective_irradiance)
883
-
884
669
  @_unwrap_single_value
885
670
  def first_solar_spectral_loss(self, pw, airmass_absolute):
886
671
  """
887
- Use :py:func:`pvlib.atmosphere.first_solar_spectral_correction` to
672
+ Use :py:func:`pvlib.spectrum.spectral_factor_firstsolar` to
888
673
  calculate the spectral loss modifier. The model coefficients are
889
674
  specific to the module's cell type, and are determined by searching
890
675
  for one of the following keys in self.module_parameters (in order):
@@ -925,9 +710,8 @@ class PVSystem:
925
710
  module_type = array._infer_cell_type()
926
711
  coefficients = None
927
712
 
928
- return atmosphere.first_solar_spectral_correction(
929
- pw, airmass_absolute,
930
- module_type, coefficients
713
+ return spectrum.spectral_factor_firstsolar(
714
+ pw, airmass_absolute, module_type, coefficients
931
715
  )
932
716
  return tuple(
933
717
  itertools.starmap(_spectral_correction, zip(self.arrays, pw))
@@ -944,14 +728,17 @@ class PVSystem:
944
728
  resistance_series, resistance_shunt, nNsVth,
945
729
  ivcurve_pnts=ivcurve_pnts)
946
730
 
947
- def i_from_v(self, resistance_shunt, resistance_series, nNsVth, voltage,
948
- saturation_current, photocurrent):
731
+ def i_from_v(self, voltage, photocurrent, saturation_current,
732
+ resistance_series, resistance_shunt, nNsVth):
949
733
  """Wrapper around the :py:func:`pvlib.pvsystem.i_from_v` function.
950
734
 
951
- See :py:func:`pvsystem.i_from_v` for details
735
+ See :py:func:`pvlib.pvsystem.i_from_v` for details.
736
+
737
+ .. versionchanged:: 0.10.0
738
+ The function's arguments have been reordered.
952
739
  """
953
- return i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
954
- saturation_current, photocurrent)
740
+ return i_from_v(voltage, photocurrent, saturation_current,
741
+ resistance_series, resistance_shunt, nNsVth)
955
742
 
956
743
  def get_ac(self, model, p_dc, v_dc=None):
957
744
  r"""Calculates AC power from p_dc using the inverter model indicated
@@ -1024,24 +811,6 @@ class PVSystem:
1024
811
  model + ' is not a valid AC power model.',
1025
812
  ' model must be one of "sandia", "adr" or "pvwatts"')
1026
813
 
1027
- @deprecated('0.9', alternative='PVSystem.get_ac', removal='0.10')
1028
- def snlinverter(self, v_dc, p_dc):
1029
- """Uses :py:func:`pvlib.inverter.sandia` to calculate AC power based on
1030
- ``self.inverter_parameters`` and the input voltage and power.
1031
-
1032
- See :py:func:`pvlib.inverter.sandia` for details
1033
- """
1034
- return inverter.sandia(v_dc, p_dc, self.inverter_parameters)
1035
-
1036
- @deprecated('0.9', alternative='PVSystem.get_ac', removal='0.10')
1037
- def adrinverter(self, v_dc, p_dc):
1038
- """Uses :py:func:`pvlib.inverter.adr` to calculate AC power based on
1039
- ``self.inverter_parameters`` and the input voltage and power.
1040
-
1041
- See :py:func:`pvlib.inverter.adr` for details
1042
- """
1043
- return inverter.adr(v_dc, p_dc, self.inverter_parameters)
1044
-
1045
814
  @_unwrap_single_value
1046
815
  def scale_voltage_current_power(self, data):
1047
816
  """
@@ -1101,21 +870,6 @@ class PVSystem:
1101
870
  self.losses_parameters)
1102
871
  return pvwatts_losses(**kwargs)
1103
872
 
1104
- @deprecated('0.9', alternative='PVSystem.get_ac', removal='0.10')
1105
- def pvwatts_ac(self, pdc):
1106
- """
1107
- Calculates AC power according to the PVWatts model using
1108
- :py:func:`pvlib.inverter.pvwatts`, `self.module_parameters["pdc0"]`,
1109
- and `eta_inv_nom=self.inverter_parameters["eta_inv_nom"]`.
1110
-
1111
- See :py:func:`pvlib.inverter.pvwatts` for details.
1112
- """
1113
- kwargs = _build_kwargs(['eta_inv_nom', 'eta_inv_ref'],
1114
- self.inverter_parameters)
1115
-
1116
- return inverter.pvwatts(pdc, self.inverter_parameters['pdc0'],
1117
- **kwargs)
1118
-
1119
873
  @_unwrap_single_value
1120
874
  def dc_ohms_from_percent(self):
1121
875
  """
@@ -1127,127 +881,6 @@ class PVSystem:
1127
881
 
1128
882
  return tuple(array.dc_ohms_from_percent() for array in self.arrays)
1129
883
 
1130
- @property
1131
- @_unwrap_single_value
1132
- @_check_deprecated_passthrough
1133
- def module_parameters(self):
1134
- return tuple(array.module_parameters for array in self.arrays)
1135
-
1136
- @module_parameters.setter
1137
- @_check_deprecated_passthrough
1138
- def module_parameters(self, value):
1139
- for array in self.arrays:
1140
- array.module_parameters = value
1141
-
1142
- @property
1143
- @_unwrap_single_value
1144
- @_check_deprecated_passthrough
1145
- def module(self):
1146
- return tuple(array.module for array in self.arrays)
1147
-
1148
- @module.setter
1149
- @_check_deprecated_passthrough
1150
- def module(self, value):
1151
- for array in self.arrays:
1152
- array.module = value
1153
-
1154
- @property
1155
- @_unwrap_single_value
1156
- @_check_deprecated_passthrough
1157
- def module_type(self):
1158
- return tuple(array.module_type for array in self.arrays)
1159
-
1160
- @module_type.setter
1161
- @_check_deprecated_passthrough
1162
- def module_type(self, value):
1163
- for array in self.arrays:
1164
- array.module_type = value
1165
-
1166
- @property
1167
- @_unwrap_single_value
1168
- @_check_deprecated_passthrough
1169
- def temperature_model_parameters(self):
1170
- return tuple(array.temperature_model_parameters
1171
- for array in self.arrays)
1172
-
1173
- @temperature_model_parameters.setter
1174
- @_check_deprecated_passthrough
1175
- def temperature_model_parameters(self, value):
1176
- for array in self.arrays:
1177
- array.temperature_model_parameters = value
1178
-
1179
- @property
1180
- @_unwrap_single_value
1181
- @_check_deprecated_passthrough
1182
- def surface_tilt(self):
1183
- return tuple(array.mount.surface_tilt for array in self.arrays)
1184
-
1185
- @surface_tilt.setter
1186
- @_check_deprecated_passthrough
1187
- def surface_tilt(self, value):
1188
- for array in self.arrays:
1189
- array.mount.surface_tilt = value
1190
-
1191
- @property
1192
- @_unwrap_single_value
1193
- @_check_deprecated_passthrough
1194
- def surface_azimuth(self):
1195
- return tuple(array.mount.surface_azimuth for array in self.arrays)
1196
-
1197
- @surface_azimuth.setter
1198
- @_check_deprecated_passthrough
1199
- def surface_azimuth(self, value):
1200
- for array in self.arrays:
1201
- array.mount.surface_azimuth = value
1202
-
1203
- @property
1204
- @_unwrap_single_value
1205
- @_check_deprecated_passthrough
1206
- def albedo(self):
1207
- return tuple(array.albedo for array in self.arrays)
1208
-
1209
- @albedo.setter
1210
- @_check_deprecated_passthrough
1211
- def albedo(self, value):
1212
- for array in self.arrays:
1213
- array.albedo = value
1214
-
1215
- @property
1216
- @_unwrap_single_value
1217
- @_check_deprecated_passthrough
1218
- def racking_model(self):
1219
- return tuple(array.mount.racking_model for array in self.arrays)
1220
-
1221
- @racking_model.setter
1222
- @_check_deprecated_passthrough
1223
- def racking_model(self, value):
1224
- for array in self.arrays:
1225
- array.mount.racking_model = value
1226
-
1227
- @property
1228
- @_unwrap_single_value
1229
- @_check_deprecated_passthrough
1230
- def modules_per_string(self):
1231
- return tuple(array.modules_per_string for array in self.arrays)
1232
-
1233
- @modules_per_string.setter
1234
- @_check_deprecated_passthrough
1235
- def modules_per_string(self, value):
1236
- for array in self.arrays:
1237
- array.modules_per_string = value
1238
-
1239
- @property
1240
- @_unwrap_single_value
1241
- @_check_deprecated_passthrough
1242
- def strings_per_inverter(self):
1243
- return tuple(array.strings for array in self.arrays)
1244
-
1245
- @strings_per_inverter.setter
1246
- @_check_deprecated_passthrough
1247
- def strings_per_inverter(self, value):
1248
- for array in self.arrays:
1249
- array.strings = value
1250
-
1251
884
  @property
1252
885
  def num_arrays(self):
1253
886
  """The number of Arrays in the system."""
@@ -1598,9 +1231,7 @@ class Array:
1598
1231
  func = temperature.pvsyst_cell
1599
1232
  required = tuple()
1600
1233
  optional = {
1601
- # TODO remove 'eta_m' after deprecation of this parameter
1602
- **_build_kwargs(['eta_m', 'module_efficiency',
1603
- 'alpha_absorption'],
1234
+ **_build_kwargs(['module_efficiency', 'alpha_absorption'],
1604
1235
  self.module_parameters),
1605
1236
  **_build_kwargs(['u_c', 'u_v'],
1606
1237
  self.temperature_model_parameters)
@@ -1910,7 +1541,7 @@ def calcparams_desoto(effective_irradiance, temp_cell,
1910
1541
  saturation_current : numeric
1911
1542
  Diode saturation curent in amperes
1912
1543
 
1913
- resistance_series : float
1544
+ resistance_series : numeric
1914
1545
  Series resistance in ohms
1915
1546
 
1916
1547
  resistance_shunt : numeric
@@ -2033,9 +1664,21 @@ def calcparams_desoto(effective_irradiance, temp_cell,
2033
1664
  # use errstate to silence divide by warning
2034
1665
  with np.errstate(divide='ignore'):
2035
1666
  Rsh = R_sh_ref * (irrad_ref / effective_irradiance)
1667
+
2036
1668
  Rs = R_s
2037
1669
 
2038
- return IL, I0, Rs, Rsh, nNsVth
1670
+ numeric_args = (effective_irradiance, temp_cell)
1671
+ out = (IL, I0, Rs, Rsh, nNsVth)
1672
+
1673
+ if all(map(np.isscalar, numeric_args)):
1674
+ return out
1675
+
1676
+ index = tools.get_pandas_index(*numeric_args)
1677
+
1678
+ if index is None:
1679
+ return np.broadcast_arrays(*out)
1680
+
1681
+ return tuple(pd.Series(a, index=index).rename(None) for a in out)
2039
1682
 
2040
1683
 
2041
1684
  def calcparams_cec(effective_irradiance, temp_cell,
@@ -2114,7 +1757,7 @@ def calcparams_cec(effective_irradiance, temp_cell,
2114
1757
  saturation_current : numeric
2115
1758
  Diode saturation curent in amperes
2116
1759
 
2117
- resistance_series : float
1760
+ resistance_series : numeric
2118
1761
  Series resistance in ohms
2119
1762
 
2120
1763
  resistance_shunt : numeric
@@ -2231,7 +1874,7 @@ def calcparams_pvsyst(effective_irradiance, temp_cell,
2231
1874
  saturation_current : numeric
2232
1875
  Diode saturation current in amperes
2233
1876
 
2234
- resistance_series : float
1877
+ resistance_series : numeric
2235
1878
  Series resistance in ohms
2236
1879
 
2237
1880
  resistance_shunt : numeric
@@ -2290,7 +1933,18 @@ def calcparams_pvsyst(effective_irradiance, temp_cell,
2290
1933
 
2291
1934
  Rs = R_s
2292
1935
 
2293
- return IL, I0, Rs, Rsh, nNsVth
1936
+ numeric_args = (effective_irradiance, temp_cell)
1937
+ out = (IL, I0, Rs, Rsh, nNsVth)
1938
+
1939
+ if all(map(np.isscalar, numeric_args)):
1940
+ return out
1941
+
1942
+ index = tools.get_pandas_index(*numeric_args)
1943
+
1944
+ if index is None:
1945
+ return np.broadcast_arrays(*out)
1946
+
1947
+ return tuple(pd.Series(a, index=index).rename(None) for a in out)
2294
1948
 
2295
1949
 
2296
1950
  def retrieve_sam(name=None, path=None):
@@ -2599,43 +2253,10 @@ def sapm(effective_irradiance, temp_cell, module):
2599
2253
  return out
2600
2254
 
2601
2255
 
2602
- def sapm_spectral_loss(airmass_absolute, module):
2603
- """
2604
- Calculates the SAPM spectral loss coefficient, F1.
2605
-
2606
- Parameters
2607
- ----------
2608
- airmass_absolute : numeric
2609
- Absolute airmass
2610
-
2611
- module : dict-like
2612
- A dict, Series, or DataFrame defining the SAPM performance
2613
- parameters. See the :py:func:`sapm` notes section for more
2614
- details.
2615
-
2616
- Returns
2617
- -------
2618
- F1 : numeric
2619
- The SAPM spectral loss coefficient.
2620
-
2621
- Notes
2622
- -----
2623
- nan airmass values will result in 0 output.
2624
- """
2625
-
2626
- am_coeff = [module['A4'], module['A3'], module['A2'], module['A1'],
2627
- module['A0']]
2628
-
2629
- spectral_loss = np.polyval(am_coeff, airmass_absolute)
2630
-
2631
- spectral_loss = np.where(np.isnan(spectral_loss), 0, spectral_loss)
2632
-
2633
- spectral_loss = np.maximum(0, spectral_loss)
2634
-
2635
- if isinstance(airmass_absolute, pd.Series):
2636
- spectral_loss = pd.Series(spectral_loss, airmass_absolute.index)
2637
-
2638
- return spectral_loss
2256
+ sapm_spectral_loss = deprecated(
2257
+ since='0.10.0',
2258
+ alternative='pvlib.spectrum.spectral_factor_sapm'
2259
+ )(spectrum.spectral_factor_sapm)
2639
2260
 
2640
2261
 
2641
2262
  def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi,
@@ -2695,11 +2316,11 @@ def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi,
2695
2316
  See also
2696
2317
  --------
2697
2318
  pvlib.iam.sapm
2698
- pvlib.pvsystem.sapm_spectral_loss
2319
+ pvlib.spectrum.spectral_factor_sapm
2699
2320
  pvlib.pvsystem.sapm
2700
2321
  """
2701
2322
 
2702
- F1 = sapm_spectral_loss(airmass_absolute, module)
2323
+ F1 = spectrum.spectral_factor_sapm(airmass_absolute, module)
2703
2324
  F2 = iam.sapm(aoi, module)
2704
2325
 
2705
2326
  Ee = F1 * (poa_direct * F2 + module['FD'] * poa_diffuse)
@@ -2711,7 +2332,7 @@ def singlediode(photocurrent, saturation_current, resistance_series,
2711
2332
  resistance_shunt, nNsVth, ivcurve_pnts=None,
2712
2333
  method='lambertw'):
2713
2334
  r"""
2714
- Solve the single-diode equation to obtain a photovoltaic IV curve.
2335
+ Solve the single diode equation to obtain a photovoltaic IV curve.
2715
2336
 
2716
2337
  Solves the single diode equation [1]_
2717
2338
 
@@ -2724,11 +2345,10 @@ def singlediode(photocurrent, saturation_current, resistance_series,
2724
2345
  \frac{V + I R_s}{R_{sh}}
2725
2346
 
2726
2347
  for :math:`I` and :math:`V` when given :math:`I_L, I_0, R_s, R_{sh},` and
2727
- :math:`n N_s V_{th}` which are described later. Returns a DataFrame
2728
- which contains the 5 points on the I-V curve specified in
2729
- [3]_. If all :math:`I_L, I_0, R_s, R_{sh},` and
2730
- :math:`n N_s V_{th}` are scalar, a single curve is returned, if any
2731
- are Series (of the same length), multiple IV curves are calculated.
2348
+ :math:`n N_s V_{th}` which are described later. The five points on the I-V
2349
+ curve specified in [3]_ are returned. If :math:`I_L, I_0, R_s, R_{sh},` and
2350
+ :math:`n N_s V_{th}` are all scalars, a single curve is returned. If any
2351
+ are array-like (of the same length), multiple IV curves are calculated.
2732
2352
 
2733
2353
  The input parameters can be calculated from meteorological data using a
2734
2354
  function for a single diode model, e.g.,
@@ -2766,35 +2386,33 @@ def singlediode(photocurrent, saturation_current, resistance_series,
2766
2386
  Number of points in the desired IV curve. If None or 0, no points on
2767
2387
  the IV curves will be produced.
2768
2388
 
2389
+ .. deprecated:: 0.10.0
2390
+ Use :py:func:`pvlib.pvsystem.v_from_i` and
2391
+ :py:func:`pvlib.pvsystem.i_from_v` instead.
2392
+
2769
2393
  method : str, default 'lambertw'
2770
2394
  Determines the method used to calculate points on the IV curve. The
2771
2395
  options are ``'lambertw'``, ``'newton'``, or ``'brentq'``.
2772
2396
 
2773
2397
  Returns
2774
2398
  -------
2775
- OrderedDict or DataFrame
2399
+ dict or pandas.DataFrame
2400
+ The returned dict-like object always contains the keys/columns:
2776
2401
 
2777
- The returned dict-like object always contains the keys/columns:
2402
+ * i_sc - short circuit current in amperes.
2403
+ * v_oc - open circuit voltage in volts.
2404
+ * i_mp - current at maximum power point in amperes.
2405
+ * v_mp - voltage at maximum power point in volts.
2406
+ * p_mp - power at maximum power point in watts.
2407
+ * i_x - current, in amperes, at ``v = 0.5*v_oc``.
2408
+ * i_xx - current, in amperes, at ``v = 0.5*(v_oc+v_mp)``.
2778
2409
 
2779
- * i_sc - short circuit current in amperes.
2780
- * v_oc - open circuit voltage in volts.
2781
- * i_mp - current at maximum power point in amperes.
2782
- * v_mp - voltage at maximum power point in volts.
2783
- * p_mp - power at maximum power point in watts.
2784
- * i_x - current, in amperes, at ``v = 0.5*v_oc``.
2785
- * i_xx - current, in amperes, at ``V = 0.5*(v_oc+v_mp)``.
2410
+ A dict is returned when the input parameters are scalars or
2411
+ ``ivcurve_pnts > 0``. If ``ivcurve_pnts > 0``, the output dictionary
2412
+ will also include the keys:
2786
2413
 
2787
- If ivcurve_pnts is greater than 0, the output dictionary will also
2788
- include the keys:
2789
-
2790
- * i - IV curve current in amperes.
2791
- * v - IV curve voltage in volts.
2792
-
2793
- The output will be an OrderedDict if photocurrent is a scalar,
2794
- array, or ivcurve_pnts is not None.
2795
-
2796
- The output will be a DataFrame if photocurrent is a Series and
2797
- ivcurve_pnts is None.
2414
+ * i - IV curve current in amperes.
2415
+ * v - IV curve voltage in volts.
2798
2416
 
2799
2417
  See also
2800
2418
  --------
@@ -2841,22 +2459,25 @@ def singlediode(photocurrent, saturation_current, resistance_series,
2841
2459
  photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
2842
2460
  https://doi.org/10.1016/0379-6787(88)90059-2
2843
2461
  """
2462
+ if ivcurve_pnts:
2463
+ warn_deprecated('0.10.0', name='pvlib.pvsystem.singlediode',
2464
+ alternative=('pvlib.pvsystem.v_from_i and '
2465
+ 'pvlib.pvsystem.i_from_v'),
2466
+ obj_type='parameter ivcurve_pnts',
2467
+ removal='0.11.0')
2468
+ args = (photocurrent, saturation_current, resistance_series,
2469
+ resistance_shunt, nNsVth) # collect args
2844
2470
  # Calculate points on the IV curve using the LambertW solution to the
2845
2471
  # single diode equation
2846
2472
  if method.lower() == 'lambertw':
2847
- out = _singlediode._lambertw(
2848
- photocurrent, saturation_current, resistance_series,
2849
- resistance_shunt, nNsVth, ivcurve_pnts
2850
- )
2851
- i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx = out[:7]
2473
+ out = _singlediode._lambertw(*args, ivcurve_pnts)
2474
+ points = out[:7]
2852
2475
  if ivcurve_pnts:
2853
2476
  ivcurve_i, ivcurve_v = out[7:]
2854
2477
  else:
2855
2478
  # Calculate points on the IV curve using either 'newton' or 'brentq'
2856
2479
  # methods. Voltages are determined by first solving the single diode
2857
2480
  # equation for the diode voltage V_d then backing out voltage
2858
- args = (photocurrent, saturation_current, resistance_series,
2859
- resistance_shunt, nNsVth) # collect args
2860
2481
  v_oc = _singlediode.bishop88_v_from_i(
2861
2482
  0.0, *args, method=method.lower()
2862
2483
  )
@@ -2872,6 +2493,7 @@ def singlediode(photocurrent, saturation_current, resistance_series,
2872
2493
  i_xx = _singlediode.bishop88_i_from_v(
2873
2494
  (v_oc + v_mp) / 2.0, *args, method=method.lower()
2874
2495
  )
2496
+ points = i_sc, v_oc, i_mp, v_mp, p_mp, i_x, i_xx
2875
2497
 
2876
2498
  # calculate the IV curve if requested using bishop88
2877
2499
  if ivcurve_pnts:
@@ -2880,22 +2502,23 @@ def singlediode(photocurrent, saturation_current, resistance_series,
2880
2502
  )
2881
2503
  ivcurve_i, ivcurve_v, _ = _singlediode.bishop88(vd, *args)
2882
2504
 
2883
- out = OrderedDict()
2884
- out['i_sc'] = i_sc
2885
- out['v_oc'] = v_oc
2886
- out['i_mp'] = i_mp
2887
- out['v_mp'] = v_mp
2888
- out['p_mp'] = p_mp
2889
- out['i_x'] = i_x
2890
- out['i_xx'] = i_xx
2505
+ columns = ('i_sc', 'v_oc', 'i_mp', 'v_mp', 'p_mp', 'i_x', 'i_xx')
2891
2506
 
2892
- if ivcurve_pnts:
2507
+ if all(map(np.isscalar, args)) or ivcurve_pnts:
2508
+ out = {c: p for c, p in zip(columns, points)}
2509
+
2510
+ if ivcurve_pnts:
2511
+ out.update(i=ivcurve_i, v=ivcurve_v)
2893
2512
 
2894
- out['v'] = ivcurve_v
2895
- out['i'] = ivcurve_i
2513
+ return out
2896
2514
 
2897
- if isinstance(photocurrent, pd.Series) and not ivcurve_pnts:
2898
- out = pd.DataFrame(out, index=photocurrent.index)
2515
+ points = np.atleast_1d(*points) # convert scalars to 1d-arrays
2516
+ points = np.vstack(points).T # collect rows into DataFrame columns
2517
+
2518
+ # save the first available pd.Series index, otherwise set to None
2519
+ index = next((a.index for a in args if isinstance(a, pd.Series)), None)
2520
+
2521
+ out = pd.DataFrame(points, columns=columns, index=index)
2899
2522
 
2900
2523
  return out
2901
2524
 
@@ -2936,7 +2559,7 @@ def max_power_point(photocurrent, saturation_current, resistance_series,
2936
2559
 
2937
2560
  Returns
2938
2561
  -------
2939
- OrderedDict or pandas.Datafrane
2562
+ OrderedDict or pandas.DataFrame
2940
2563
  ``(i_mp, v_mp, p_mp)``
2941
2564
 
2942
2565
  Notes
@@ -2948,8 +2571,7 @@ def max_power_point(photocurrent, saturation_current, resistance_series,
2948
2571
  """
2949
2572
  i_mp, v_mp, p_mp = _singlediode.bishop88_mpp(
2950
2573
  photocurrent, saturation_current, resistance_series,
2951
- resistance_shunt, nNsVth, d2mutau=0, NsVbi=np.Inf,
2952
- method=method.lower()
2574
+ resistance_shunt, nNsVth, d2mutau, NsVbi, method=method.lower()
2953
2575
  )
2954
2576
  if isinstance(photocurrent, pd.Series):
2955
2577
  ivp = {'i_mp': i_mp, 'v_mp': v_mp, 'p_mp': p_mp}
@@ -2962,8 +2584,8 @@ def max_power_point(photocurrent, saturation_current, resistance_series,
2962
2584
  return out
2963
2585
 
2964
2586
 
2965
- def v_from_i(resistance_shunt, resistance_series, nNsVth, current,
2966
- saturation_current, photocurrent, method='lambertw'):
2587
+ def v_from_i(current, photocurrent, saturation_current, resistance_series,
2588
+ resistance_shunt, nNsVth, method='lambertw'):
2967
2589
  '''
2968
2590
  Device voltage at the given device current for the single diode model.
2969
2591
 
@@ -2977,18 +2599,34 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current,
2977
2599
  the caller's responsibility to ensure that the arguments are all float64
2978
2600
  and within the proper ranges.
2979
2601
 
2602
+ .. versionchanged:: 0.10.0
2603
+ The function's arguments have been reordered.
2604
+
2980
2605
  Parameters
2981
2606
  ----------
2982
- resistance_shunt : numeric
2983
- Shunt resistance in ohms under desired IV curve conditions.
2984
- Often abbreviated ``Rsh``.
2985
- 0 < resistance_shunt <= numpy.inf
2607
+ current : numeric
2608
+ The current in amperes under desired IV curve conditions.
2609
+
2610
+ photocurrent : numeric
2611
+ Light-generated current (photocurrent) in amperes under desired
2612
+ IV curve conditions. Often abbreviated ``I_L``.
2613
+ 0 <= photocurrent
2614
+
2615
+ saturation_current : numeric
2616
+ Diode saturation current in amperes under desired IV curve
2617
+ conditions. Often abbreviated ``I_0``.
2618
+ 0 < saturation_current
2986
2619
 
2987
2620
  resistance_series : numeric
2988
2621
  Series resistance in ohms under desired IV curve conditions.
2989
2622
  Often abbreviated ``Rs``.
2990
2623
  0 <= resistance_series < numpy.inf
2991
2624
 
2625
+ resistance_shunt : numeric
2626
+ Shunt resistance in ohms under desired IV curve conditions.
2627
+ Often abbreviated ``Rsh``.
2628
+ 0 < resistance_shunt <= numpy.inf
2629
+
2992
2630
  nNsVth : numeric
2993
2631
  The product of three components. 1) The usual diode ideal factor
2994
2632
  (n), 2) the number of cells in series (Ns), and 3) the cell
@@ -2999,19 +2637,6 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current,
2999
2637
  q is the charge of an electron (coulombs).
3000
2638
  0 < nNsVth
3001
2639
 
3002
- current : numeric
3003
- The current in amperes under desired IV curve conditions.
3004
-
3005
- saturation_current : numeric
3006
- Diode saturation current in amperes under desired IV curve
3007
- conditions. Often abbreviated ``I_0``.
3008
- 0 < saturation_current
3009
-
3010
- photocurrent : numeric
3011
- Light-generated current (photocurrent) in amperes under desired
3012
- IV curve conditions. Often abbreviated ``I_L``.
3013
- 0 <= photocurrent
3014
-
3015
2640
  method : str
3016
2641
  Method to use: ``'lambertw'``, ``'newton'``, or ``'brentq'``. *Note*:
3017
2642
  ``'brentq'`` is limited to 1st quadrant only.
@@ -3028,8 +2653,8 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current,
3028
2653
  '''
3029
2654
  if method.lower() == 'lambertw':
3030
2655
  return _singlediode._lambertw_v_from_i(
3031
- resistance_shunt, resistance_series, nNsVth, current,
3032
- saturation_current, photocurrent
2656
+ current, photocurrent, saturation_current, resistance_series,
2657
+ resistance_shunt, nNsVth
3033
2658
  )
3034
2659
  else:
3035
2660
  # Calculate points on the IV curve using either 'newton' or 'brentq'
@@ -3050,33 +2675,49 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current,
3050
2675
  return V
3051
2676
 
3052
2677
 
3053
- def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
3054
- saturation_current, photocurrent, method='lambertw'):
2678
+ def i_from_v(voltage, photocurrent, saturation_current, resistance_series,
2679
+ resistance_shunt, nNsVth, method='lambertw'):
3055
2680
  '''
3056
2681
  Device current at the given device voltage for the single diode model.
3057
2682
 
3058
2683
  Uses the single diode model (SDM) as described in, e.g.,
3059
- Jain and Kapoor 2004 [1]_.
2684
+ Jain and Kapoor 2004 [1]_.
3060
2685
  The solution is per Eq 2 of [1] except when resistance_series=0,
3061
- in which case the explict solution for current is used.
2686
+ in which case the explict solution for current is used.
3062
2687
  Ideal device parameters are specified by resistance_shunt=np.inf and
3063
- resistance_series=0.
2688
+ resistance_series=0.
3064
2689
  Inputs to this function can include scalars and pandas.Series, but it is
3065
- the caller's responsibility to ensure that the arguments are all float64
3066
- and within the proper ranges.
2690
+ the caller's responsibility to ensure that the arguments are all float64
2691
+ and within the proper ranges.
2692
+
2693
+ .. versionchanged:: 0.10.0
2694
+ The function's arguments have been reordered.
3067
2695
 
3068
2696
  Parameters
3069
2697
  ----------
3070
- resistance_shunt : numeric
3071
- Shunt resistance in ohms under desired IV curve conditions.
3072
- Often abbreviated ``Rsh``.
3073
- 0 < resistance_shunt <= numpy.inf
2698
+ voltage : numeric
2699
+ The voltage in Volts under desired IV curve conditions.
2700
+
2701
+ photocurrent : numeric
2702
+ Light-generated current (photocurrent) in amperes under desired
2703
+ IV curve conditions. Often abbreviated ``I_L``.
2704
+ 0 <= photocurrent
2705
+
2706
+ saturation_current : numeric
2707
+ Diode saturation current in amperes under desired IV curve
2708
+ conditions. Often abbreviated ``I_0``.
2709
+ 0 < saturation_current
3074
2710
 
3075
2711
  resistance_series : numeric
3076
2712
  Series resistance in ohms under desired IV curve conditions.
3077
2713
  Often abbreviated ``Rs``.
3078
2714
  0 <= resistance_series < numpy.inf
3079
2715
 
2716
+ resistance_shunt : numeric
2717
+ Shunt resistance in ohms under desired IV curve conditions.
2718
+ Often abbreviated ``Rsh``.
2719
+ 0 < resistance_shunt <= numpy.inf
2720
+
3080
2721
  nNsVth : numeric
3081
2722
  The product of three components. 1) The usual diode ideal factor
3082
2723
  (n), 2) the number of cells in series (Ns), and 3) the cell
@@ -3087,19 +2728,6 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
3087
2728
  q is the charge of an electron (coulombs).
3088
2729
  0 < nNsVth
3089
2730
 
3090
- voltage : numeric
3091
- The voltage in Volts under desired IV curve conditions.
3092
-
3093
- saturation_current : numeric
3094
- Diode saturation current in amperes under desired IV curve
3095
- conditions. Often abbreviated ``I_0``.
3096
- 0 < saturation_current
3097
-
3098
- photocurrent : numeric
3099
- Light-generated current (photocurrent) in amperes under desired
3100
- IV curve conditions. Often abbreviated ``I_L``.
3101
- 0 <= photocurrent
3102
-
3103
2731
  method : str
3104
2732
  Method to use: ``'lambertw'``, ``'newton'``, or ``'brentq'``. *Note*:
3105
2733
  ``'brentq'`` is limited to 1st quadrant only.
@@ -3116,8 +2744,8 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
3116
2744
  '''
3117
2745
  if method.lower() == 'lambertw':
3118
2746
  return _singlediode._lambertw_i_from_v(
3119
- resistance_shunt, resistance_series, nNsVth, voltage,
3120
- saturation_current, photocurrent
2747
+ voltage, photocurrent, saturation_current, resistance_series,
2748
+ resistance_shunt, nNsVth
3121
2749
  )
3122
2750
  else:
3123
2751
  # Calculate points on the IV curve using either 'newton' or 'brentq'
@@ -3181,9 +2809,9 @@ def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
3181
2809
 
3182
2810
  P_{dc} = \frac{G_{poa eff}}{1000} P_{dc0} ( 1 + \gamma_{pdc} (T_{cell} - T_{ref}))
3183
2811
 
3184
- Note that the pdc0 is also used as a symbol in
3185
- :py:func:`pvlib.inverter.pvwatts`. pdc0 in this function refers to the DC
3186
- power of the modules at reference conditions. pdc0 in
2812
+ Note that ``pdc0`` is also used as a symbol in
2813
+ :py:func:`pvlib.inverter.pvwatts`. ``pdc0`` in this function refers to the DC
2814
+ power of the modules at reference conditions. ``pdc0`` in
3187
2815
  :py:func:`pvlib.inverter.pvwatts` refers to the DC power input limit of
3188
2816
  the inverter.
3189
2817
 
@@ -3208,7 +2836,7 @@ def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
3208
2836
  Returns
3209
2837
  -------
3210
2838
  pdc: numeric
3211
- DC power.
2839
+ DC power. [W]
3212
2840
 
3213
2841
  References
3214
2842
  ----------