pvlib 0.11.2__py3-none-any.whl → 0.12.1a1__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.
- pvlib/__init__.py +1 -0
- pvlib/atmosphere.py +40 -40
- pvlib/bifacial/infinite_sheds.py +4 -3
- pvlib/bifacial/utils.py +2 -1
- pvlib/iotools/__init__.py +6 -0
- pvlib/iotools/psm3.py +1 -1
- pvlib/iotools/psm4.py +819 -0
- pvlib/iotools/pvgis.py +10 -2
- pvlib/iotools/tmy.py +3 -69
- pvlib/irradiance.py +38 -15
- pvlib/ivtools/sdm/__init__.py +20 -0
- pvlib/ivtools/sdm/_fit_desoto_pvsyst_sandia.py +585 -0
- pvlib/ivtools/sdm/cec.py +93 -0
- pvlib/ivtools/sdm/desoto.py +401 -0
- pvlib/ivtools/sdm/pvsyst.py +630 -0
- pvlib/location.py +73 -33
- pvlib/modelchain.py +19 -36
- pvlib/pvsystem.py +114 -65
- pvlib/snow.py +64 -28
- pvlib/spectrum/__init__.py +0 -1
- pvlib/spectrum/irradiance.py +2 -64
- pvlib/spectrum/mismatch.py +3 -3
- pvlib/tools.py +6 -5
- {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info}/METADATA +6 -5
- pvlib-0.12.1a1.dist-info/RECORD +80 -0
- {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info}/WHEEL +1 -1
- pvlib/data/BIRD_08_16_2012.csv +0 -8761
- pvlib/data/BIRD_08_16_2012_patm.csv +0 -8761
- pvlib/data/Burlington, United States SolarAnywhere Time Series 2021 Lat_44_465 Lon_-73_205 TMY3 format.csv +0 -8762
- pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv +0 -578
- pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv +0 -74
- pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND +0 -146
- pvlib/data/CRNS0101-05-2019-AZ_Tucson_11_W.txt +0 -4
- pvlib/data/CRN_with_problems.txt +0 -3
- pvlib/data/ET-M772BH550GL.PAN +0 -75
- pvlib/data/NLD_Amsterdam062400_IWEC.epw +0 -8768
- pvlib/data/PVsyst_demo.csv +0 -10757
- pvlib/data/PVsyst_demo_model.csv +0 -3588
- pvlib/data/SRML-day-EUPO1801.txt +0 -1441
- pvlib/data/abq19056.dat +0 -6
- pvlib/data/bishop88_numerical_precision.csv +0 -101
- pvlib/data/bsrn-lr0100-pay0616.dat +0 -86901
- pvlib/data/bsrn-pay0616.dat.gz +0 -0
- pvlib/data/cams_mcclear_1min_verbose.csv +0 -60
- pvlib/data/cams_mcclear_monthly.csv +0 -42
- pvlib/data/cams_radiation_1min_verbose.csv +0 -72
- pvlib/data/cams_radiation_monthly.csv +0 -47
- pvlib/data/detect_clearsky_data.csv +0 -35
- pvlib/data/detect_clearsky_threshold_data.csv +0 -126
- pvlib/data/greensboro_kimber_soil_manwash.dat +0 -8761
- pvlib/data/greensboro_kimber_soil_nowash.dat +0 -8761
- pvlib/data/inverter_fit_snl_meas.csv +0 -127
- pvlib/data/inverter_fit_snl_sim.csv +0 -19
- pvlib/data/ivtools_numdiff.csv +0 -52
- pvlib/data/midc_20181014.txt +0 -1441
- pvlib/data/midc_raw_20181018.txt +0 -1441
- pvlib/data/midc_raw_short_header_20191115.txt +0 -1441
- pvlib/data/msn19056.dat +0 -6
- pvlib/data/precise_iv_curves1.json +0 -10251
- pvlib/data/precise_iv_curves2.json +0 -10251
- pvlib/data/precise_iv_curves_parameter_sets1.csv +0 -33
- pvlib/data/precise_iv_curves_parameter_sets2.csv +0 -33
- pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA2_10kWp_CIS_5_2a_2013_2014.json +0 -1
- pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA_30deg_0deg_2016_2016.csv +0 -35
- pvlib/data/pvgis_tmy_meta.json +0 -32
- pvlib/data/pvgis_tmy_test.csv +0 -8761
- pvlib/data/pvwatts_8760_rackmount.csv +0 -8779
- pvlib/data/pvwatts_8760_roofmount.csv +0 -8779
- pvlib/data/singleaxis_tracker_wslope.csv +0 -8761
- pvlib/data/spectrl2_example_spectra.csv +0 -123
- pvlib/data/surfrad-slv16001.dat +0 -1442
- pvlib/data/test_psm3_2017.csv +0 -17521
- pvlib/data/test_psm3_2019_5min.csv +0 -289
- pvlib/data/test_psm3_tmy-2017.csv +0 -8761
- pvlib/data/test_read_psm3.csv +0 -17523
- pvlib/data/test_read_pvgis_horizon.csv +0 -49
- pvlib/data/tmy_45.000_8.000_2005_2023.csv +0 -8789
- pvlib/data/tmy_45.000_8.000_2005_2023.epw +0 -8768
- pvlib/data/tmy_45.000_8.000_2005_2023.json +0 -1
- pvlib/data/tmy_45.000_8.000_2005_2023.txt +0 -8761
- pvlib/data/tmy_45.000_8.000_userhorizon.json +0 -1
- pvlib/ivtools/sdm.py +0 -1379
- pvlib/spa_c_files/README.md +0 -81
- pvlib/spa_c_files/cspa_py.pxd +0 -43
- pvlib/spa_c_files/spa_py.pyx +0 -30
- pvlib/tests/__init__.py +0 -0
- pvlib/tests/bifacial/__init__.py +0 -0
- pvlib/tests/bifacial/test_infinite_sheds.py +0 -317
- pvlib/tests/bifacial/test_losses_models.py +0 -54
- pvlib/tests/bifacial/test_pvfactors.py +0 -82
- pvlib/tests/bifacial/test_utils.py +0 -192
- pvlib/tests/conftest.py +0 -476
- pvlib/tests/iotools/__init__.py +0 -0
- pvlib/tests/iotools/test_acis.py +0 -213
- pvlib/tests/iotools/test_bsrn.py +0 -131
- pvlib/tests/iotools/test_crn.py +0 -95
- pvlib/tests/iotools/test_epw.py +0 -23
- pvlib/tests/iotools/test_midc.py +0 -89
- pvlib/tests/iotools/test_panond.py +0 -32
- pvlib/tests/iotools/test_psm3.py +0 -198
- pvlib/tests/iotools/test_pvgis.py +0 -644
- pvlib/tests/iotools/test_sodapro.py +0 -298
- pvlib/tests/iotools/test_solaranywhere.py +0 -287
- pvlib/tests/iotools/test_solargis.py +0 -68
- pvlib/tests/iotools/test_solcast.py +0 -324
- pvlib/tests/iotools/test_solrad.py +0 -152
- pvlib/tests/iotools/test_srml.py +0 -124
- pvlib/tests/iotools/test_surfrad.py +0 -75
- pvlib/tests/iotools/test_tmy.py +0 -133
- pvlib/tests/ivtools/__init__.py +0 -0
- pvlib/tests/ivtools/test_sde.py +0 -230
- pvlib/tests/ivtools/test_sdm.py +0 -429
- pvlib/tests/ivtools/test_utils.py +0 -173
- pvlib/tests/spectrum/__init__.py +0 -0
- pvlib/tests/spectrum/conftest.py +0 -40
- pvlib/tests/spectrum/test_irradiance.py +0 -138
- pvlib/tests/spectrum/test_mismatch.py +0 -304
- pvlib/tests/spectrum/test_response.py +0 -124
- pvlib/tests/spectrum/test_spectrl2.py +0 -72
- pvlib/tests/test__deprecation.py +0 -97
- pvlib/tests/test_albedo.py +0 -84
- pvlib/tests/test_atmosphere.py +0 -351
- pvlib/tests/test_clearsky.py +0 -884
- pvlib/tests/test_conftest.py +0 -37
- pvlib/tests/test_iam.py +0 -555
- pvlib/tests/test_inverter.py +0 -213
- pvlib/tests/test_irradiance.py +0 -1487
- pvlib/tests/test_location.py +0 -356
- pvlib/tests/test_modelchain.py +0 -2020
- pvlib/tests/test_numerical_precision.py +0 -124
- pvlib/tests/test_pvarray.py +0 -71
- pvlib/tests/test_pvsystem.py +0 -2511
- pvlib/tests/test_scaling.py +0 -207
- pvlib/tests/test_shading.py +0 -391
- pvlib/tests/test_singlediode.py +0 -608
- pvlib/tests/test_snow.py +0 -212
- pvlib/tests/test_soiling.py +0 -230
- pvlib/tests/test_solarposition.py +0 -966
- pvlib/tests/test_spa.py +0 -454
- pvlib/tests/test_temperature.py +0 -470
- pvlib/tests/test_tools.py +0 -146
- pvlib/tests/test_tracking.py +0 -474
- pvlib/tests/test_transformer.py +0 -60
- pvlib-0.11.2.dist-info/RECORD +0 -191
- {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info/licenses}/AUTHORS.md +0 -0
- {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info/licenses}/LICENSE +0 -0
- {pvlib-0.11.2.dist-info → pvlib-0.12.1a1.dist-info}/top_level.txt +0 -0
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
|
-
|
|
22
|
-
|
|
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
|
|
25
|
+
Location objects have two time-zone attributes:
|
|
25
26
|
|
|
26
|
-
* ``tz`` is
|
|
27
|
-
* ``pytz`` is a pytz
|
|
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
|
|
42
|
-
See
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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__(
|
|
63
|
-
|
|
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='
|
|
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 '
|
|
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
|
|
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
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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',
|
|
@@ -609,7 +611,7 @@ class ModelChain:
|
|
|
609
611
|
"""Infer DC power model from Array module parameters."""
|
|
610
612
|
params = _common_keys(
|
|
611
613
|
tuple(array.module_parameters for array in self.system.arrays))
|
|
612
|
-
if {'A0', 'A1', '
|
|
614
|
+
if {'A0', 'A1', 'C3'} <= params:
|
|
613
615
|
return self.sapm, 'sapm'
|
|
614
616
|
elif {'a_ref', 'I_L_ref', 'I_o_ref', 'R_sh_ref', 'R_s',
|
|
615
617
|
'Adjust'} <= params:
|
|
@@ -855,9 +857,7 @@ class ModelChain:
|
|
|
855
857
|
|
|
856
858
|
@spectral_model.setter
|
|
857
859
|
def spectral_model(self, model):
|
|
858
|
-
if model
|
|
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
|
-
|
|
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
|
-
``'
|
|
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
|
@@ -17,8 +17,6 @@ from dataclasses import dataclass
|
|
|
17
17
|
from abc import ABC, abstractmethod
|
|
18
18
|
from typing import Optional, Union
|
|
19
19
|
|
|
20
|
-
from pvlib._deprecation import deprecated
|
|
21
|
-
|
|
22
20
|
import pvlib # used to avoid albedo name collision in the Array class
|
|
23
21
|
from pvlib import (atmosphere, iam, inverter, irradiance,
|
|
24
22
|
singlediode as _singlediode, spectrum, temperature)
|
|
@@ -29,11 +27,10 @@ import pvlib.tools as tools
|
|
|
29
27
|
# a dict of required parameter names for each DC power model
|
|
30
28
|
_DC_MODEL_PARAMS = {
|
|
31
29
|
'sapm': {
|
|
32
|
-
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'Mbvoc', 'Bvmpo', 'Mbvmp', 'N', 'Cells_in_Series',
|
|
36
|
-
'IXO', 'IXXO', 'FD'},
|
|
30
|
+
# i_x and i_xx params (IXO, IXXO, C4-C7) not required
|
|
31
|
+
'C0', 'C1', 'C2', 'C3',
|
|
32
|
+
'Isco', 'Impo', 'Voco', 'Vmpo', 'Aisc', 'Aimp', 'Bvoco',
|
|
33
|
+
'Mbvoc', 'Bvmpo', 'Mbvmp', 'N', 'Cells_in_Series'},
|
|
37
34
|
'desoto': {
|
|
38
35
|
'alpha_sc', 'a_ref', 'I_L_ref', 'I_o_ref',
|
|
39
36
|
'R_sh_ref', 'R_s'},
|
|
@@ -1710,6 +1707,8 @@ def calcparams_desoto(effective_irradiance, temp_cell,
|
|
|
1710
1707
|
Rs = R_s
|
|
1711
1708
|
|
|
1712
1709
|
numeric_args = (effective_irradiance, temp_cell)
|
|
1710
|
+
# IL: photocurrent, I0: saturation_current, Rs: resistance_series,
|
|
1711
|
+
# Rsh: resistance_shunt
|
|
1713
1712
|
out = (IL, I0, Rs, Rsh, nNsVth)
|
|
1714
1713
|
|
|
1715
1714
|
if all(map(np.isscalar, numeric_args)):
|
|
@@ -1947,35 +1946,23 @@ def calcparams_pvsyst(effective_irradiance, temp_cell,
|
|
|
1947
1946
|
|
|
1948
1947
|
'''
|
|
1949
1948
|
|
|
1950
|
-
|
|
1951
|
-
k = constants.k
|
|
1952
|
-
|
|
1953
|
-
# elementary charge in coulomb
|
|
1954
|
-
q = constants.e
|
|
1949
|
+
gamma = _pvsyst_gamma(temp_cell, gamma_ref, mu_gamma, temp_ref)
|
|
1955
1950
|
|
|
1956
|
-
|
|
1957
|
-
Tref_K = temp_ref + 273.15
|
|
1958
|
-
Tcell_K = temp_cell + 273.15
|
|
1959
|
-
|
|
1960
|
-
gamma = gamma_ref + mu_gamma * (Tcell_K - Tref_K)
|
|
1961
|
-
nNsVth = gamma * k / q * cells_in_series * Tcell_K
|
|
1951
|
+
nNsVth = _pvsyst_nNsVth(temp_cell, gamma, cells_in_series)
|
|
1962
1952
|
|
|
1963
|
-
IL = effective_irradiance
|
|
1964
|
-
|
|
1953
|
+
IL = _pvsyst_IL(effective_irradiance, temp_cell, I_L_ref, alpha_sc,
|
|
1954
|
+
irrad_ref, temp_ref)
|
|
1965
1955
|
|
|
1966
|
-
I0 =
|
|
1967
|
-
(np.exp((q * EgRef) / (k * gamma) * (1 / Tref_K - 1 / Tcell_K)))
|
|
1956
|
+
I0 = _pvsyst_Io(temp_cell, gamma, I_o_ref, EgRef, temp_ref)
|
|
1968
1957
|
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
Rsh_base = np.maximum(0.0, Rsh_tmp)
|
|
1972
|
-
|
|
1973
|
-
Rsh = Rsh_base + (R_sh_0 - Rsh_base) * \
|
|
1974
|
-
np.exp(-R_sh_exp * effective_irradiance / irrad_ref)
|
|
1958
|
+
Rsh = _pvsyst_Rsh(effective_irradiance, R_sh_ref, R_sh_0, R_sh_exp,
|
|
1959
|
+
irrad_ref)
|
|
1975
1960
|
|
|
1976
1961
|
Rs = R_s
|
|
1977
1962
|
|
|
1978
1963
|
numeric_args = (effective_irradiance, temp_cell)
|
|
1964
|
+
# IL: photocurrent, I0: saturation_current, Rs: resistance_series,
|
|
1965
|
+
# Rsh: resistance_shunt
|
|
1979
1966
|
out = (IL, I0, Rs, Rsh, nNsVth)
|
|
1980
1967
|
|
|
1981
1968
|
if all(map(np.isscalar, numeric_args)):
|
|
@@ -1989,6 +1976,54 @@ def calcparams_pvsyst(effective_irradiance, temp_cell,
|
|
|
1989
1976
|
return tuple(pd.Series(a, index=index).rename(None) for a in out)
|
|
1990
1977
|
|
|
1991
1978
|
|
|
1979
|
+
def _pvsyst_Rsh(effective_irradiance, R_sh_ref, R_sh_0, R_sh_exp=5.5,
|
|
1980
|
+
irrad_ref=1000):
|
|
1981
|
+
Rsh_tmp = \
|
|
1982
|
+
(R_sh_ref - R_sh_0 * np.exp(-R_sh_exp)) / (1.0 - np.exp(-R_sh_exp))
|
|
1983
|
+
Rsh_base = np.maximum(0.0, Rsh_tmp)
|
|
1984
|
+
|
|
1985
|
+
Rsh = Rsh_base + (R_sh_0 - Rsh_base) * \
|
|
1986
|
+
np.exp(-R_sh_exp * effective_irradiance / irrad_ref)
|
|
1987
|
+
|
|
1988
|
+
return Rsh
|
|
1989
|
+
|
|
1990
|
+
|
|
1991
|
+
def _pvsyst_IL(effective_irradiance, temp_cell, I_L_ref, alpha_sc,
|
|
1992
|
+
irrad_ref=1000, temp_ref=25):
|
|
1993
|
+
Tref_K = temp_ref + 273.15
|
|
1994
|
+
Tcell_K = temp_cell + 273.15
|
|
1995
|
+
IL = effective_irradiance / irrad_ref * \
|
|
1996
|
+
(I_L_ref + alpha_sc * (Tcell_K - Tref_K))
|
|
1997
|
+
return IL
|
|
1998
|
+
|
|
1999
|
+
|
|
2000
|
+
def _pvsyst_Io(temp_cell, gamma, I_o_ref, EgRef, temp_ref=25):
|
|
2001
|
+
k = constants.k # Boltzmann constant in J/K
|
|
2002
|
+
q = constants.e # elementary charge in coulomb
|
|
2003
|
+
|
|
2004
|
+
Tref_K = temp_ref + 273.15
|
|
2005
|
+
Tcell_K = temp_cell + 273.15
|
|
2006
|
+
|
|
2007
|
+
Io = I_o_ref * ((Tcell_K / Tref_K) ** 3) * \
|
|
2008
|
+
(np.exp((q * EgRef) / (k * gamma) * (1 / Tref_K - 1 / Tcell_K)))
|
|
2009
|
+
|
|
2010
|
+
return Io
|
|
2011
|
+
|
|
2012
|
+
|
|
2013
|
+
def _pvsyst_gamma(temp_cell, gamma_ref, mu_gamma, temp_ref=25):
|
|
2014
|
+
gamma = gamma_ref + mu_gamma * (temp_cell - temp_ref)
|
|
2015
|
+
return gamma
|
|
2016
|
+
|
|
2017
|
+
|
|
2018
|
+
def _pvsyst_nNsVth(temp_cell, gamma, cells_in_series):
|
|
2019
|
+
k = constants.k # Boltzmann constant in J/K
|
|
2020
|
+
q = constants.e # elementary charge in coulomb
|
|
2021
|
+
Tcell_K = temp_cell + 273.15
|
|
2022
|
+
|
|
2023
|
+
nNsVth = gamma * k / q * cells_in_series * Tcell_K
|
|
2024
|
+
return nNsVth
|
|
2025
|
+
|
|
2026
|
+
|
|
1992
2027
|
def retrieve_sam(name=None, path=None):
|
|
1993
2028
|
"""
|
|
1994
2029
|
Retrieve latest module and inverter info from a file bundled with pvlib,
|
|
@@ -2158,25 +2193,32 @@ def _parse_raw_sam_df(csvdata):
|
|
|
2158
2193
|
return df
|
|
2159
2194
|
|
|
2160
2195
|
|
|
2161
|
-
def sapm(effective_irradiance, temp_cell, module
|
|
2196
|
+
def sapm(effective_irradiance, temp_cell, module, *, temperature_ref=25,
|
|
2197
|
+
irradiance_ref=1000):
|
|
2162
2198
|
'''
|
|
2163
2199
|
The Sandia PV Array Performance Model (SAPM) generates 5 points on a
|
|
2164
2200
|
PV module's I-V curve (Voc, Isc, Ix, Ixx, Vmp/Imp) according to
|
|
2165
|
-
SAND2004-3535. Assumes a reference cell temperature of 25
|
|
2201
|
+
SAND2004-3535. Assumes a reference cell temperature of 25°C.
|
|
2166
2202
|
|
|
2167
2203
|
Parameters
|
|
2168
2204
|
----------
|
|
2169
2205
|
effective_irradiance : numeric
|
|
2170
2206
|
Irradiance reaching the module's cells, after reflections and
|
|
2171
|
-
adjustment for spectrum. [
|
|
2207
|
+
adjustment for spectrum. [Wm⁻²]
|
|
2172
2208
|
|
|
2173
2209
|
temp_cell : numeric
|
|
2174
|
-
Cell temperature [C].
|
|
2210
|
+
Cell temperature [°C].
|
|
2175
2211
|
|
|
2176
2212
|
module : dict-like
|
|
2177
2213
|
A dict or Series defining the SAPM parameters. See the notes section
|
|
2178
2214
|
for more details.
|
|
2179
2215
|
|
|
2216
|
+
temperature_ref : numeric, optional
|
|
2217
|
+
Reference temperature [°C]
|
|
2218
|
+
|
|
2219
|
+
irradiance_ref : numeric, optional
|
|
2220
|
+
Reference irradiance [Wm⁻²]
|
|
2221
|
+
|
|
2180
2222
|
Returns
|
|
2181
2223
|
-------
|
|
2182
2224
|
A DataFrame with the columns:
|
|
@@ -2187,18 +2229,33 @@ def sapm(effective_irradiance, temp_cell, module):
|
|
|
2187
2229
|
* v_mp : Voltage at maximum-power point (V)
|
|
2188
2230
|
* p_mp : Power at maximum-power point (W)
|
|
2189
2231
|
* i_x : Current at module V = 0.5Voc, defines 4th point on I-V
|
|
2190
|
-
curve for modeling curve shape
|
|
2232
|
+
curve for modeling curve shape. Omitted if ``IXO``, ``C4``, and
|
|
2233
|
+
``C5`` parameters are not supplied.
|
|
2191
2234
|
* i_xx : Current at module V = 0.5(Voc+Vmp), defines 5th point on
|
|
2192
|
-
I-V curve for modeling curve shape
|
|
2235
|
+
I-V curve for modeling curve shape. Omitted if ``IXXO``, ``C6``,
|
|
2236
|
+
and ``C7`` parameters are not supplied.
|
|
2193
2237
|
|
|
2194
2238
|
Notes
|
|
2195
2239
|
-----
|
|
2196
|
-
The SAPM parameters which are required in ``module`` are
|
|
2197
|
-
listed in the following table.
|
|
2198
|
-
|
|
2199
2240
|
The Sandia module database contains parameter values for a limited set
|
|
2200
2241
|
of modules. The CEC module database does not contain these parameters.
|
|
2201
|
-
Both databases can be accessed using :py:func:`retrieve_sam`.
|
|
2242
|
+
Both databases can be accessed using :py:func:`retrieve_sam`. The full list
|
|
2243
|
+
of SAPM parameters is presented in the table below. Those that are required
|
|
2244
|
+
in the ``module`` parameter to run this model are as follows:
|
|
2245
|
+
|
|
2246
|
+
* ``C0``, ``C1``, ``C2``, ``C3``
|
|
2247
|
+
* ``Isco``
|
|
2248
|
+
* ``Impo``
|
|
2249
|
+
* ``Voco``
|
|
2250
|
+
* ``Vmpo``
|
|
2251
|
+
* ``Aisc``
|
|
2252
|
+
* ``Aimp``
|
|
2253
|
+
* ``Bvoco``
|
|
2254
|
+
* ``Mbvoc``
|
|
2255
|
+
* ``Bvmpo``
|
|
2256
|
+
* ``Mbvmp``
|
|
2257
|
+
* ``N``
|
|
2258
|
+
* ``Cells_in_series``
|
|
2202
2259
|
|
|
2203
2260
|
================ ========================================================
|
|
2204
2261
|
Key Description
|
|
@@ -2214,19 +2271,19 @@ def sapm(effective_irradiance, temp_cell, module):
|
|
|
2214
2271
|
Voco Open circuit voltage at reference condition (amps)
|
|
2215
2272
|
Vmpo Maximum power voltage at reference condition (amps)
|
|
2216
2273
|
Aisc Short circuit current temperature coefficient at
|
|
2217
|
-
reference condition (1
|
|
2274
|
+
reference condition (1/°C)
|
|
2218
2275
|
Aimp Maximum power current temperature coefficient at
|
|
2219
|
-
reference condition (1
|
|
2276
|
+
reference condition (1/°C)
|
|
2220
2277
|
Bvoco Open circuit voltage temperature coefficient at
|
|
2221
|
-
reference condition (V
|
|
2278
|
+
reference condition (V/°C)
|
|
2222
2279
|
Mbvoc Coefficient providing the irradiance dependence for the
|
|
2223
2280
|
BetaVoc temperature coefficient at reference irradiance
|
|
2224
|
-
(V
|
|
2281
|
+
(V/°C)
|
|
2225
2282
|
Bvmpo Maximum power voltage temperature coefficient at
|
|
2226
2283
|
reference condition
|
|
2227
2284
|
Mbvmp Coefficient providing the irradiance dependence for the
|
|
2228
2285
|
BetaVmp temperature coefficient at reference irradiance
|
|
2229
|
-
(V
|
|
2286
|
+
(V/°C)
|
|
2230
2287
|
N Empirically determined "diode factor" (dimensionless)
|
|
2231
2288
|
Cells_in_Series Number of cells in series in a module's cell string(s)
|
|
2232
2289
|
IXO Ix at reference conditions
|
|
@@ -2247,16 +2304,11 @@ def sapm(effective_irradiance, temp_cell, module):
|
|
|
2247
2304
|
pvlib.temperature.sapm_module
|
|
2248
2305
|
'''
|
|
2249
2306
|
|
|
2250
|
-
# TODO: someday, change temp_ref and irrad_ref to reference_temperature and
|
|
2251
|
-
# reference_irradiance and expose
|
|
2252
|
-
temp_ref = 25
|
|
2253
|
-
irrad_ref = 1000
|
|
2254
|
-
|
|
2255
2307
|
q = constants.e # Elementary charge in units of coulombs
|
|
2256
2308
|
kb = constants.k # Boltzmann's constant in units of J/K
|
|
2257
2309
|
|
|
2258
2310
|
# avoid problem with integer input
|
|
2259
|
-
Ee = np.array(effective_irradiance, dtype='float64') /
|
|
2311
|
+
Ee = np.array(effective_irradiance, dtype='float64') / irradiance_ref
|
|
2260
2312
|
|
|
2261
2313
|
# set up masking for 0, positive, and nan inputs
|
|
2262
2314
|
Ee_gt_0 = np.full_like(Ee, False, dtype='bool')
|
|
@@ -2279,31 +2331,34 @@ def sapm(effective_irradiance, temp_cell, module):
|
|
|
2279
2331
|
out = OrderedDict()
|
|
2280
2332
|
|
|
2281
2333
|
out['i_sc'] = (
|
|
2282
|
-
module['Isco'] * Ee * (1 + module['Aisc']*(temp_cell -
|
|
2334
|
+
module['Isco'] * Ee * (1 + module['Aisc']*(temp_cell -
|
|
2335
|
+
temperature_ref)))
|
|
2283
2336
|
|
|
2284
2337
|
out['i_mp'] = (
|
|
2285
2338
|
module['Impo'] * (module['C0']*Ee + module['C1']*(Ee**2)) *
|
|
2286
|
-
(1 + module['Aimp']*(temp_cell -
|
|
2339
|
+
(1 + module['Aimp']*(temp_cell - temperature_ref)))
|
|
2287
2340
|
|
|
2288
2341
|
out['v_oc'] = np.maximum(0, (
|
|
2289
2342
|
module['Voco'] + cells_in_series * delta * logEe +
|
|
2290
|
-
Bvoco*(temp_cell -
|
|
2343
|
+
Bvoco*(temp_cell - temperature_ref)))
|
|
2291
2344
|
|
|
2292
2345
|
out['v_mp'] = np.maximum(0, (
|
|
2293
2346
|
module['Vmpo'] +
|
|
2294
2347
|
module['C2'] * cells_in_series * delta * logEe +
|
|
2295
2348
|
module['C3'] * cells_in_series * ((delta * logEe) ** 2) +
|
|
2296
|
-
Bvmpo*(temp_cell -
|
|
2349
|
+
Bvmpo*(temp_cell - temperature_ref)))
|
|
2297
2350
|
|
|
2298
2351
|
out['p_mp'] = out['i_mp'] * out['v_mp']
|
|
2299
2352
|
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2353
|
+
if 'IXO' in module and 'C4' in module and 'C5' in module:
|
|
2354
|
+
out['i_x'] = (
|
|
2355
|
+
module['IXO'] * (module['C4']*Ee + module['C5']*(Ee**2)) *
|
|
2356
|
+
(1 + module['Aisc']*(temp_cell - temperature_ref)))
|
|
2303
2357
|
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2358
|
+
if 'IXXO' in module and 'C6' in module and 'C7' in module:
|
|
2359
|
+
out['i_xx'] = (
|
|
2360
|
+
module['IXXO'] * (module['C6']*Ee + module['C7']*(Ee**2)) *
|
|
2361
|
+
(1 + module['Aimp']*(temp_cell - temperature_ref)))
|
|
2307
2362
|
|
|
2308
2363
|
if isinstance(out['i_sc'], pd.Series):
|
|
2309
2364
|
out = pd.DataFrame(out)
|
|
@@ -2311,12 +2366,6 @@ def sapm(effective_irradiance, temp_cell, module):
|
|
|
2311
2366
|
return out
|
|
2312
2367
|
|
|
2313
2368
|
|
|
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
2369
|
def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi,
|
|
2321
2370
|
module):
|
|
2322
2371
|
r"""
|