pvlib 0.9.4a1__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.
- pvlib/__init__.py +3 -2
- pvlib/atmosphere.py +23 -173
- pvlib/bifacial/infinite_sheds.py +88 -277
- pvlib/bifacial/utils.py +270 -28
- pvlib/data/adr-library-cec-inverters-2019-03-05.csv +5009 -0
- pvlib/data/precise_iv_curves1.json +10251 -0
- pvlib/data/precise_iv_curves2.json +10251 -0
- pvlib/data/precise_iv_curves_parameter_sets1.csv +33 -0
- pvlib/data/precise_iv_curves_parameter_sets2.csv +33 -0
- pvlib/data/test_psm3_2017.csv +17521 -17521
- pvlib/data/test_psm3_2019_5min.csv +288 -288
- pvlib/data/test_read_psm3.csv +17522 -17522
- pvlib/data/test_read_pvgis_horizon.csv +49 -0
- pvlib/data/variables_style_rules.csv +3 -0
- pvlib/iam.py +207 -51
- pvlib/inverter.py +6 -1
- pvlib/iotools/__init__.py +7 -2
- pvlib/iotools/acis.py +516 -0
- pvlib/iotools/midc.py +4 -4
- pvlib/iotools/psm3.py +59 -42
- pvlib/iotools/pvgis.py +84 -28
- pvlib/iotools/sodapro.py +8 -6
- pvlib/iotools/srml.py +121 -18
- pvlib/iotools/surfrad.py +2 -2
- pvlib/iotools/tmy.py +146 -102
- pvlib/irradiance.py +270 -15
- pvlib/ivtools/sde.py +14 -20
- pvlib/ivtools/sdm.py +31 -20
- pvlib/ivtools/utils.py +127 -6
- pvlib/location.py +3 -2
- pvlib/modelchain.py +67 -70
- pvlib/pvarray.py +225 -0
- pvlib/pvsystem.py +169 -539
- pvlib/shading.py +43 -2
- pvlib/singlediode.py +216 -66
- pvlib/snow.py +36 -15
- pvlib/soiling.py +3 -3
- pvlib/spa.py +327 -368
- pvlib/spectrum/__init__.py +8 -2
- pvlib/spectrum/mismatch.py +335 -0
- pvlib/temperature.py +124 -13
- pvlib/tests/bifacial/test_infinite_sheds.py +44 -106
- pvlib/tests/bifacial/test_utils.py +102 -5
- pvlib/tests/conftest.py +0 -31
- pvlib/tests/iotools/test_acis.py +213 -0
- pvlib/tests/iotools/test_midc.py +6 -6
- pvlib/tests/iotools/test_psm3.py +7 -5
- pvlib/tests/iotools/test_pvgis.py +21 -14
- pvlib/tests/iotools/test_sodapro.py +1 -1
- pvlib/tests/iotools/test_srml.py +71 -6
- pvlib/tests/iotools/test_tmy.py +43 -8
- pvlib/tests/ivtools/test_sde.py +19 -17
- pvlib/tests/ivtools/test_sdm.py +9 -4
- pvlib/tests/ivtools/test_utils.py +96 -1
- pvlib/tests/test_atmosphere.py +8 -64
- pvlib/tests/test_clearsky.py +0 -1
- pvlib/tests/test_iam.py +74 -1
- pvlib/tests/test_irradiance.py +56 -2
- pvlib/tests/test_location.py +1 -1
- pvlib/tests/test_modelchain.py +33 -76
- pvlib/tests/test_pvarray.py +46 -0
- pvlib/tests/test_pvsystem.py +366 -201
- pvlib/tests/test_shading.py +35 -0
- pvlib/tests/test_singlediode.py +306 -29
- pvlib/tests/test_snow.py +84 -1
- pvlib/tests/test_soiling.py +8 -7
- pvlib/tests/test_solarposition.py +7 -7
- pvlib/tests/test_spa.py +6 -7
- pvlib/tests/test_spectrum.py +145 -1
- pvlib/tests/test_temperature.py +29 -11
- pvlib/tests/test_tools.py +41 -0
- pvlib/tests/test_tracking.py +0 -149
- pvlib/tools.py +49 -25
- pvlib/tracking.py +1 -269
- pvlib-0.10.0.dist-info/AUTHORS.md +35 -0
- {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/LICENSE +5 -2
- {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/METADATA +3 -13
- {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/RECORD +80 -75
- {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/WHEEL +1 -1
- pvlib/data/adr-library-2013-10-01.csv +0 -1762
- pvlib/forecast.py +0 -1211
- pvlib/iotools/ecmwf_macc.py +0 -312
- pvlib/tests/iotools/test_ecmwf_macc.py +0 -162
- pvlib/tests/test_forecast.py +0 -228
- pvlib-0.9.4a1.dist-info/AUTHORS.md +0 -32
- {pvlib-0.9.4a1.dist-info → pvlib-0.10.0.dist-info}/top_level.txt +0 -0
pvlib/ivtools/sde.py
CHANGED
|
@@ -10,16 +10,6 @@ import numpy as np
|
|
|
10
10
|
from pvlib.ivtools.utils import _schumaker_qspline
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
# set constant for numpy.linalg.lstsq parameter rcond
|
|
14
|
-
# rcond=-1 for numpy<1.14, rcond=None for numpy>=1.14
|
|
15
|
-
# TODO remove after minimum numpy version >= 1.14
|
|
16
|
-
minor = int(np.__version__.split('.')[1])
|
|
17
|
-
if minor < 14:
|
|
18
|
-
RCOND = -1
|
|
19
|
-
else:
|
|
20
|
-
RCOND = None
|
|
21
|
-
|
|
22
|
-
|
|
23
13
|
def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
|
|
24
14
|
vlim=0.2, ilim=0.1):
|
|
25
15
|
r"""
|
|
@@ -74,7 +64,8 @@ def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
|
|
|
74
64
|
|
|
75
65
|
Raises
|
|
76
66
|
------
|
|
77
|
-
RuntimeError
|
|
67
|
+
RuntimeError
|
|
68
|
+
if parameter extraction is not successful.
|
|
78
69
|
|
|
79
70
|
Notes
|
|
80
71
|
-----
|
|
@@ -116,7 +107,7 @@ def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
|
|
|
116
107
|
.. math::
|
|
117
108
|
|
|
118
109
|
I &\approx \frac{I_{L}}{1 + G_{p} R_{s}}
|
|
119
|
-
- \frac{G_{p}}{1 + G_{p}R_{s}} V
|
|
110
|
+
- \frac{G_{p}}{1 + G_{p}R_{s}} V \\
|
|
120
111
|
&= \beta_{0} + \beta_{1} V
|
|
121
112
|
|
|
122
113
|
4. The exponential portion of the IV curve is defined by
|
|
@@ -134,8 +125,8 @@ def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
|
|
|
134
125
|
.. math::
|
|
135
126
|
|
|
136
127
|
\log(\beta_{0} - \beta_{1} V - I)
|
|
137
|
-
&\approx \log(\frac{I_{0}}{1 + G_{p} R_{s}} + \frac{V}{nN_sV_{th}}
|
|
138
|
-
+ \frac{I R_{s}}{nN_sV_{th}}
|
|
128
|
+
&\approx \log(\frac{I_{0}}{1 + G_{p} R_{s}}) + \frac{V}{nN_sV_{th}}
|
|
129
|
+
+ \frac{I R_{s}}{nN_sV_{th}} \\
|
|
139
130
|
&= \beta_{2} + \beta_{3} V + \beta_{4} I
|
|
140
131
|
|
|
141
132
|
6. Calculate values for ``IL, I0, Rs, Rsh,`` and ``nNsVth`` from the
|
|
@@ -149,7 +140,8 @@ def fit_sandia_simple(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
|
|
|
149
140
|
0 86758 909 4
|
|
150
141
|
.. [2] C. B. Jones, C. W. Hansen, "Single Diode Parameter Extraction from
|
|
151
142
|
In-Field Photovoltaic I-V Curves on a Single Board Computer", 46th IEEE
|
|
152
|
-
Photovoltaic Specialist Conference, Chicago, IL, 2019
|
|
143
|
+
Photovoltaic Specialist Conference, Chicago, IL, 2019.
|
|
144
|
+
:doi:`10.1109/PVSC40753.2019.8981330`
|
|
153
145
|
"""
|
|
154
146
|
|
|
155
147
|
# If not provided, extract v_oc, i_sc, v_mp and i_mp from the IV curve data
|
|
@@ -228,7 +220,7 @@ def _sandia_beta3_beta4(voltage, current, beta0, beta1, ilim, i_sc):
|
|
|
228
220
|
x = np.array([np.ones_like(voltage), voltage, current]).T
|
|
229
221
|
# Select points where y > ilim * i_sc to regress log(y) onto x
|
|
230
222
|
idx = (y > ilim * i_sc)
|
|
231
|
-
result = np.linalg.lstsq(x[idx], np.log(y[idx]), rcond=
|
|
223
|
+
result = np.linalg.lstsq(x[idx], np.log(y[idx]), rcond=None)
|
|
232
224
|
coef = result[0]
|
|
233
225
|
beta3 = coef[1].item()
|
|
234
226
|
beta4 = coef[2].item()
|
|
@@ -299,8 +291,10 @@ def _fit_sandia_cocontent(voltage, current, nsvth):
|
|
|
299
291
|
|
|
300
292
|
Raises
|
|
301
293
|
------
|
|
302
|
-
ValueError
|
|
303
|
-
|
|
294
|
+
ValueError
|
|
295
|
+
if ``voltage`` and ``current`` are different lengths.
|
|
296
|
+
ValueError
|
|
297
|
+
if ``len(voltage)`` < 6
|
|
304
298
|
|
|
305
299
|
Notes
|
|
306
300
|
-----
|
|
@@ -444,7 +438,7 @@ def _cocontent_regress(v, i, voc, isc, cci):
|
|
|
444
438
|
sx = np.vstack((s[:, 0], s[:, 1], s[:, 0] * s[:, 1], s[:, 0] * s[:, 0],
|
|
445
439
|
s[:, 1] * s[:, 1], col1)).T
|
|
446
440
|
|
|
447
|
-
gamma = np.linalg.lstsq(sx, scc, rcond=
|
|
441
|
+
gamma = np.linalg.lstsq(sx, scc, rcond=None)[0]
|
|
448
442
|
# coefficients from regression in rotated coordinates
|
|
449
443
|
|
|
450
444
|
# Principle components transformation steps
|
|
@@ -473,5 +467,5 @@ def _cocontent_regress(v, i, voc, isc, cci):
|
|
|
473
467
|
|
|
474
468
|
# translate from coefficients in rotated space (gamma) to coefficients in
|
|
475
469
|
# original coordinates (beta)
|
|
476
|
-
beta = np.linalg.lstsq(np.dot(mb, ma), gamma[0:5], rcond=
|
|
470
|
+
beta = np.linalg.lstsq(np.dot(mb, ma), gamma[0:5], rcond=None)[0]
|
|
477
471
|
return beta
|
pvlib/ivtools/sdm.py
CHANGED
|
@@ -8,10 +8,9 @@ Function names should follow the pattern "fit_" + name of model + "_" +
|
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
from scipy import constants
|
|
12
12
|
from scipy import optimize
|
|
13
13
|
from scipy.special import lambertw
|
|
14
|
-
from scipy.misc import derivative
|
|
15
14
|
|
|
16
15
|
from pvlib.pvsystem import calcparams_pvsyst, singlediode, v_from_i
|
|
17
16
|
from pvlib.singlediode import bishop88_mpp
|
|
@@ -19,6 +18,11 @@ from pvlib.singlediode import bishop88_mpp
|
|
|
19
18
|
from pvlib.ivtools.utils import rectify_iv_curve, _numdiff
|
|
20
19
|
from pvlib.ivtools.sde import _fit_sandia_cocontent
|
|
21
20
|
|
|
21
|
+
from pvlib.tools import _first_order_centered_difference
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
CONSTANTS = {'E0': 1000.0, 'T0': 25.0, 'k': constants.k, 'q': constants.e}
|
|
25
|
+
|
|
22
26
|
|
|
23
27
|
def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc,
|
|
24
28
|
gamma_pmp, cells_in_series, temp_ref=25):
|
|
@@ -72,9 +76,10 @@ def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc,
|
|
|
72
76
|
|
|
73
77
|
Raises
|
|
74
78
|
------
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
ImportError
|
|
80
|
+
if NREL-PySAM is not installed.
|
|
81
|
+
RuntimeError
|
|
82
|
+
if parameter extraction is not successful.
|
|
78
83
|
|
|
79
84
|
Notes
|
|
80
85
|
-----
|
|
@@ -90,7 +95,7 @@ def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc,
|
|
|
90
95
|
----------
|
|
91
96
|
.. [1] A. Dobos, "An Improved Coefficient Calculator for the California
|
|
92
97
|
Energy Commission 6 Parameter Photovoltaic Module Model", Journal of
|
|
93
|
-
Solar Energy Engineering, vol 134, 2012.
|
|
98
|
+
Solar Energy Engineering, vol 134, 2012. :doi:`10.1115/1.4005759`
|
|
94
99
|
"""
|
|
95
100
|
|
|
96
101
|
try:
|
|
@@ -200,11 +205,11 @@ def fit_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series,
|
|
|
200
205
|
----------
|
|
201
206
|
.. [1] W. De Soto et al., "Improvement and validation of a model for
|
|
202
207
|
photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
|
|
203
|
-
2006.
|
|
208
|
+
2006. :doi:`10.1016/j.solener.2005.06.010`
|
|
204
209
|
"""
|
|
205
210
|
|
|
206
211
|
# Constants
|
|
207
|
-
k =
|
|
212
|
+
k = constants.value('Boltzmann constant in eV/K') # in eV/K
|
|
208
213
|
Tref = temp_ref + 273.15 # [K]
|
|
209
214
|
|
|
210
215
|
# initial guesses of variables for computing convergence:
|
|
@@ -340,9 +345,9 @@ def fit_pvsyst_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
|
|
|
340
345
|
T0 : float
|
|
341
346
|
cell temperature at STC, default 25 [C]
|
|
342
347
|
k : float
|
|
343
|
-
|
|
348
|
+
Boltzmann's constant [J/K]
|
|
344
349
|
q : float
|
|
345
|
-
|
|
350
|
+
elementary charge [Coulomb]
|
|
346
351
|
|
|
347
352
|
maxiter : int, default 5
|
|
348
353
|
input that sets the maximum number of iterations for the parameter
|
|
@@ -401,6 +406,7 @@ def fit_pvsyst_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
|
|
|
401
406
|
.. [1] K. Sauer, T. Roessler, C. W. Hansen, Modeling the Irradiance and
|
|
402
407
|
Temperature Dependence of Photovoltaic Modules in PVsyst, IEEE Journal
|
|
403
408
|
of Photovoltaics v5(1), January 2015.
|
|
409
|
+
:doi:`10.1109/JPHOTOV.2014.2364133`
|
|
404
410
|
.. [2] A. Mermoud, PV Modules modeling, Presentation at the 2nd PV
|
|
405
411
|
Performance Modeling Workshop, Santa Clara, CA, May 2013
|
|
406
412
|
.. [3] A. Mermoud, T. Lejeuene, Performance Assessment of a Simulation
|
|
@@ -408,16 +414,18 @@ def fit_pvsyst_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
|
|
|
408
414
|
Photovoltaic Solar Energy Conference, Valencia, Spain, Sept. 2010
|
|
409
415
|
.. [4] C. Hansen, Estimating Parameters for the PVsyst Version 6
|
|
410
416
|
Photovoltaic Module Performance Model, Sandia National Laboratories
|
|
411
|
-
Report SAND2015-8598
|
|
417
|
+
Report SAND2015-8598. :doi:`10.2172/1223058`
|
|
412
418
|
.. [5] C. Hansen, Parameter Estimation for Single Diode Models of
|
|
413
|
-
Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065
|
|
419
|
+
Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065.
|
|
420
|
+
:doi:`10.2172/1177157`
|
|
414
421
|
.. [6] C. Hansen, Estimation of Parameters for Single Diode Models using
|
|
415
422
|
Measured IV Curves, Proc. of the 39th IEEE PVSC, June 2013.
|
|
423
|
+
:doi:`10.1109/PVSC.2013.6744135`
|
|
416
424
|
.. [7] PVLib MATLAB https://github.com/sandialabs/MATLAB_PV_LIB
|
|
417
425
|
"""
|
|
418
426
|
|
|
419
427
|
if const is None:
|
|
420
|
-
const =
|
|
428
|
+
const = CONSTANTS
|
|
421
429
|
|
|
422
430
|
ee = ivcurves['ee']
|
|
423
431
|
tc = ivcurves['tc']
|
|
@@ -520,9 +528,9 @@ def fit_desoto_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
|
|
|
520
528
|
T0 : float
|
|
521
529
|
cell temperature at STC, default 25 [C]
|
|
522
530
|
k : float
|
|
523
|
-
|
|
531
|
+
Boltzmann's constant [J/K]
|
|
524
532
|
q : float
|
|
525
|
-
|
|
533
|
+
elementary charge [Coulomb]
|
|
526
534
|
|
|
527
535
|
maxiter : int, default 5
|
|
528
536
|
input that sets the maximum number of iterations for the parameter
|
|
@@ -570,16 +578,18 @@ def fit_desoto_sandia(ivcurves, specs, const=None, maxiter=5, eps1=1.e-3):
|
|
|
570
578
|
----------
|
|
571
579
|
.. [1] W. De Soto et al., "Improvement and validation of a model for
|
|
572
580
|
photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
|
|
573
|
-
2006.
|
|
581
|
+
2006. :doi:`10.1016/j.solener.2005.06.010`
|
|
574
582
|
.. [2] C. Hansen, Parameter Estimation for Single Diode Models of
|
|
575
|
-
Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065
|
|
583
|
+
Photovoltaic Modules, Sandia National Laboratories Report SAND2015-2065.
|
|
584
|
+
:doi:`10.2172/1177157`
|
|
576
585
|
.. [3] C. Hansen, Estimation of Parameters for Single Diode Models using
|
|
577
586
|
Measured IV Curves, Proc. of the 39th IEEE PVSC, June 2013.
|
|
587
|
+
:doi:`10.1109/PVSC.2013.6744135`
|
|
578
588
|
.. [4] PVLib MATLAB https://github.com/sandialabs/MATLAB_PV_LIB
|
|
579
589
|
"""
|
|
580
590
|
|
|
581
591
|
if const is None:
|
|
582
|
-
const =
|
|
592
|
+
const = CONSTANTS
|
|
583
593
|
|
|
584
594
|
ee = ivcurves['ee']
|
|
585
595
|
tc = ivcurves['tc']
|
|
@@ -938,7 +948,7 @@ def _update_io(voc, iph, io, rs, rsh, nnsvth):
|
|
|
938
948
|
|
|
939
949
|
while maxerr > eps and k < niter:
|
|
940
950
|
# Predict Voc
|
|
941
|
-
pvoc = v_from_i(
|
|
951
|
+
pvoc = v_from_i(0., iph, tio, rs, rsh, nnsvth)
|
|
942
952
|
|
|
943
953
|
# Difference in Voc
|
|
944
954
|
dvoc = pvoc - voc
|
|
@@ -1341,5 +1351,6 @@ def pvsyst_temperature_coeff(alpha_sc, gamma_ref, mu_gamma, I_L_ref, I_o_ref,
|
|
|
1341
1351
|
I_o_ref, R_sh_ref, R_sh_0, R_s, cells_in_series, R_sh_exp, EgRef,
|
|
1342
1352
|
temp_ref)
|
|
1343
1353
|
pmp = maxp(temp_ref, *args)
|
|
1344
|
-
gamma_pdc =
|
|
1354
|
+
gamma_pdc = _first_order_centered_difference(maxp, x0=temp_ref, args=args)
|
|
1355
|
+
|
|
1345
1356
|
return gamma_pdc / pmp
|
pvlib/ivtools/utils.py
CHANGED
|
@@ -6,6 +6,7 @@ working with IV curves, or fitting equations to IV curve data.
|
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import pandas as pd
|
|
9
|
+
from numpy.polynomial.polynomial import Polynomial as Poly
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
# A small number used to decide when a slope is equivalent to zero
|
|
@@ -149,12 +150,12 @@ def rectify_iv_curve(voltage, current, decimals=None):
|
|
|
149
150
|
``rectify_iv_curve`` ensures that the IV curve lies in the first quadrant
|
|
150
151
|
of the (voltage, current) plane. The returned IV curve:
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
* increases in voltage
|
|
154
|
+
* contains no negative current or voltage values
|
|
155
|
+
* contains no NaNs
|
|
156
|
+
* contains no points with duplicate voltage values. Where voltage
|
|
157
|
+
values are repeated, a single data point is substituted with current
|
|
158
|
+
equal to the average of current at duplicated voltages.
|
|
158
159
|
"""
|
|
159
160
|
|
|
160
161
|
df = pd.DataFrame(data=np.vstack((voltage, current)).T, columns=['v', 'i'])
|
|
@@ -423,3 +424,123 @@ def _schumaker_qspline(x, y):
|
|
|
423
424
|
yhat = tmp2[:, 4]
|
|
424
425
|
kflag = tmp2[:, 5]
|
|
425
426
|
return t, c, yhat, kflag
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def astm_e1036(v, i, imax_limits=(0.75, 1.15), vmax_limits=(0.75, 1.15),
|
|
430
|
+
voc_points=3, isc_points=3, mp_fit_order=4):
|
|
431
|
+
'''
|
|
432
|
+
Extract photovoltaic IV parameters according to ASTM E1036. Assumes that
|
|
433
|
+
the power producing portion of the curve is in the first quadrant.
|
|
434
|
+
|
|
435
|
+
Parameters
|
|
436
|
+
----------
|
|
437
|
+
v : array-like
|
|
438
|
+
Voltage points
|
|
439
|
+
i : array-like
|
|
440
|
+
Current points
|
|
441
|
+
imax_limits : tuple, default (0.75, 1.15)
|
|
442
|
+
Two-element tuple (low, high) specifying the fraction of estimated
|
|
443
|
+
Imp within which to fit a polynomial for max power calculation
|
|
444
|
+
vmax_limits : tuple, default (0.75, 1.15)
|
|
445
|
+
Two-element tuple (low, high) specifying the fraction of estimated
|
|
446
|
+
Vmp within which to fit a polynomial for max power calculation
|
|
447
|
+
voc_points : int, default 3
|
|
448
|
+
The number of points near open circuit to use for linear fit
|
|
449
|
+
and Voc calculation
|
|
450
|
+
isc_points : int, default 3
|
|
451
|
+
The number of points near short circuit to use for linear fit and
|
|
452
|
+
Isc calculation
|
|
453
|
+
mp_fit_order : int, default 4
|
|
454
|
+
The order of the polynomial fit of power vs. voltage near maximum
|
|
455
|
+
power
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
Returns
|
|
459
|
+
-------
|
|
460
|
+
dict
|
|
461
|
+
Results. The IV parameters are given by the keys 'voc', 'isc',
|
|
462
|
+
'vmp', 'imp', 'pmp', and 'ff'. The key 'mp_fit' gives the numpy
|
|
463
|
+
Polynomial object for the fit of power vs voltage near maximum
|
|
464
|
+
power.
|
|
465
|
+
|
|
466
|
+
References
|
|
467
|
+
----------
|
|
468
|
+
.. [1] Standard Test Methods for Electrical Performance of Nonconcentrator
|
|
469
|
+
Terrestrial Photovoltaic Modules and Arrays Using Reference Cells,
|
|
470
|
+
ASTM E1036-15(2019), :doi:`10.1520/E1036-15R19`
|
|
471
|
+
'''
|
|
472
|
+
|
|
473
|
+
# Adapted from https://github.com/NREL/iv_params
|
|
474
|
+
# Copyright (c) 2022, Alliance for Sustainable Energy, LLC
|
|
475
|
+
# All rights reserved.
|
|
476
|
+
|
|
477
|
+
df = pd.DataFrame()
|
|
478
|
+
df['v'] = v
|
|
479
|
+
df['i'] = i
|
|
480
|
+
df['p'] = df['v'] * df['i']
|
|
481
|
+
|
|
482
|
+
# determine if we can use voc and isc estimates
|
|
483
|
+
i_min_ind = df['i'].abs().idxmin()
|
|
484
|
+
v_min_ind = df['v'].abs().idxmin()
|
|
485
|
+
voc_est = df['v'][i_min_ind]
|
|
486
|
+
isc_est = df['i'][v_min_ind]
|
|
487
|
+
|
|
488
|
+
# accept the estimates if they are close enough
|
|
489
|
+
# if not, perform a linear fit
|
|
490
|
+
if abs(df['i'][i_min_ind]) <= isc_est * 0.001:
|
|
491
|
+
voc = voc_est
|
|
492
|
+
else:
|
|
493
|
+
df['i_abs'] = df['i'].abs()
|
|
494
|
+
voc_df = df.nsmallest(voc_points, 'i_abs')
|
|
495
|
+
voc_fit = Poly.fit(voc_df['i'], voc_df['v'], 1)
|
|
496
|
+
voc = voc_fit(0)
|
|
497
|
+
|
|
498
|
+
if abs(df['v'][v_min_ind]) <= voc_est * 0.005:
|
|
499
|
+
isc = isc_est
|
|
500
|
+
else:
|
|
501
|
+
df['v_abs'] = df['v'].abs()
|
|
502
|
+
isc_df = df.nsmallest(isc_points, 'v_abs')
|
|
503
|
+
isc_fit = Poly.fit(isc_df['v'], isc_df['i'], 1)
|
|
504
|
+
isc = isc_fit(0)
|
|
505
|
+
|
|
506
|
+
# estimate max power point
|
|
507
|
+
max_index = df['p'].idxmax()
|
|
508
|
+
mp_est = df.loc[max_index]
|
|
509
|
+
|
|
510
|
+
# filter around max power
|
|
511
|
+
mask = (
|
|
512
|
+
(df['i'] >= imax_limits[0] * mp_est['i']) &
|
|
513
|
+
(df['i'] <= imax_limits[1] * mp_est['i']) &
|
|
514
|
+
(df['v'] >= vmax_limits[0] * mp_est['v']) &
|
|
515
|
+
(df['v'] <= vmax_limits[1] * mp_est['v'])
|
|
516
|
+
)
|
|
517
|
+
filtered = df[mask]
|
|
518
|
+
|
|
519
|
+
# fit polynomial and find max
|
|
520
|
+
mp_fit = Poly.fit(filtered['v'], filtered['p'], mp_fit_order)
|
|
521
|
+
# Note that this root finding procedure differs from
|
|
522
|
+
# the suggestion in the standard
|
|
523
|
+
roots = mp_fit.deriv().roots()
|
|
524
|
+
# only consider real roots
|
|
525
|
+
roots = roots.real[abs(roots.imag) < 1e-5]
|
|
526
|
+
# only consider roots in the relevant part of the domain
|
|
527
|
+
roots = roots[(roots < filtered['v'].max()) &
|
|
528
|
+
(roots > filtered['v'].min())]
|
|
529
|
+
vmp = roots[np.argmax(mp_fit(roots))]
|
|
530
|
+
pmp = mp_fit(vmp)
|
|
531
|
+
# Imp isn't mentioned for update in the
|
|
532
|
+
# standard, but this seems to be in the intended spirit
|
|
533
|
+
imp = pmp / vmp
|
|
534
|
+
|
|
535
|
+
ff = pmp / (voc * isc)
|
|
536
|
+
|
|
537
|
+
result = {}
|
|
538
|
+
result['voc'] = voc
|
|
539
|
+
result['isc'] = isc
|
|
540
|
+
result['vmp'] = vmp
|
|
541
|
+
result['imp'] = imp
|
|
542
|
+
result['pmp'] = pmp
|
|
543
|
+
result['ff'] = ff
|
|
544
|
+
result['mp_fit'] = mp_fit
|
|
545
|
+
|
|
546
|
+
return result
|
pvlib/location.py
CHANGED
|
@@ -331,8 +331,9 @@ class Location:
|
|
|
331
331
|
method : str, default 'pyephem'
|
|
332
332
|
'pyephem', 'spa', or 'geometric'
|
|
333
333
|
|
|
334
|
-
kwargs
|
|
335
|
-
|
|
334
|
+
kwargs :
|
|
335
|
+
Passed to the relevant functions. See
|
|
336
|
+
solarposition.sun_rise_set_transit_<method> for details.
|
|
336
337
|
|
|
337
338
|
Returns
|
|
338
339
|
-------
|
pvlib/modelchain.py
CHANGED
|
@@ -14,8 +14,7 @@ from dataclasses import dataclass, field
|
|
|
14
14
|
from typing import Union, Tuple, Optional, TypeVar
|
|
15
15
|
|
|
16
16
|
from pvlib import (atmosphere, clearsky, inverter, pvsystem, solarposition,
|
|
17
|
-
temperature
|
|
18
|
-
from pvlib.tracking import SingleAxisTracker
|
|
17
|
+
temperature)
|
|
19
18
|
import pvlib.irradiance # avoid name conflict with full import
|
|
20
19
|
from pvlib.pvsystem import _DC_MODEL_PARAMS
|
|
21
20
|
from pvlib._deprecation import pvlibDeprecationWarning
|
|
@@ -254,6 +253,33 @@ def get_orientation(strategy, **kwargs):
|
|
|
254
253
|
return surface_tilt, surface_azimuth
|
|
255
254
|
|
|
256
255
|
|
|
256
|
+
def _getmcattr(self, attr):
|
|
257
|
+
"""
|
|
258
|
+
Helper for __repr__ methods, needed to avoid recursion in property
|
|
259
|
+
lookups
|
|
260
|
+
"""
|
|
261
|
+
out = getattr(self, attr)
|
|
262
|
+
try:
|
|
263
|
+
out = out.__name__
|
|
264
|
+
except AttributeError:
|
|
265
|
+
pass
|
|
266
|
+
return out
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _mcr_repr(obj):
|
|
270
|
+
'''
|
|
271
|
+
Helper for ModelChainResult.__repr__
|
|
272
|
+
'''
|
|
273
|
+
if isinstance(obj, tuple):
|
|
274
|
+
return "Tuple (" + ", ".join([_mcr_repr(o) for o in obj]) + ")"
|
|
275
|
+
if isinstance(obj, pd.DataFrame):
|
|
276
|
+
return "DataFrame ({} rows x {} columns)".format(*obj.shape)
|
|
277
|
+
if isinstance(obj, pd.Series):
|
|
278
|
+
return "Series (length {})".format(len(obj))
|
|
279
|
+
# scalar, None, other?
|
|
280
|
+
return repr(obj)
|
|
281
|
+
|
|
282
|
+
|
|
257
283
|
# Type for fields that vary between arrays
|
|
258
284
|
T = TypeVar('T')
|
|
259
285
|
|
|
@@ -385,6 +411,33 @@ class ModelChainResult:
|
|
|
385
411
|
value = self._result_type(value)
|
|
386
412
|
super().__setattr__(key, value)
|
|
387
413
|
|
|
414
|
+
def __repr__(self):
|
|
415
|
+
mc_attrs = dir(self)
|
|
416
|
+
|
|
417
|
+
def _head(obj):
|
|
418
|
+
try:
|
|
419
|
+
return obj[:3]
|
|
420
|
+
except:
|
|
421
|
+
return obj
|
|
422
|
+
|
|
423
|
+
if type(self.dc) is tuple:
|
|
424
|
+
num_arrays = len(self.dc)
|
|
425
|
+
else:
|
|
426
|
+
num_arrays = 1
|
|
427
|
+
|
|
428
|
+
desc1 = ('=== ModelChainResult === \n')
|
|
429
|
+
desc2 = (f'Number of Arrays: {num_arrays} \n')
|
|
430
|
+
attr = 'times'
|
|
431
|
+
desc3 = ('times (first 3)\n' +
|
|
432
|
+
f'{_head(_getmcattr(self, attr))}' +
|
|
433
|
+
'\n')
|
|
434
|
+
lines = []
|
|
435
|
+
for attr in mc_attrs:
|
|
436
|
+
if not (attr.startswith('_') or attr=='times'):
|
|
437
|
+
lines.append(f' {attr}: ' + _mcr_repr(getattr(self, attr)))
|
|
438
|
+
desc4 = '\n'.join(lines)
|
|
439
|
+
return (desc1 + desc2 + desc3 + desc4)
|
|
440
|
+
|
|
388
441
|
|
|
389
442
|
class ModelChain:
|
|
390
443
|
"""
|
|
@@ -465,13 +518,6 @@ class ModelChain:
|
|
|
465
518
|
Name of ModelChain instance.
|
|
466
519
|
"""
|
|
467
520
|
|
|
468
|
-
# list of deprecated attributes
|
|
469
|
-
_deprecated_attrs = ['solar_position', 'airmass', 'total_irrad',
|
|
470
|
-
'aoi', 'aoi_modifier', 'spectral_modifier',
|
|
471
|
-
'cell_temperature', 'effective_irradiance',
|
|
472
|
-
'dc', 'ac', 'diode_params', 'tracking',
|
|
473
|
-
'weather', 'times', 'losses']
|
|
474
|
-
|
|
475
521
|
def __init__(self, system, location,
|
|
476
522
|
clearsky_model='ineichen',
|
|
477
523
|
transposition_model='haydavies',
|
|
@@ -503,26 +549,6 @@ class ModelChain:
|
|
|
503
549
|
|
|
504
550
|
self.results = ModelChainResult()
|
|
505
551
|
|
|
506
|
-
def __getattr__(self, key):
|
|
507
|
-
if key in ModelChain._deprecated_attrs:
|
|
508
|
-
msg = f'ModelChain.{key} is deprecated and will' \
|
|
509
|
-
f' be removed in v0.10. Use' \
|
|
510
|
-
f' ModelChain.results.{key} instead'
|
|
511
|
-
warnings.warn(msg, pvlibDeprecationWarning)
|
|
512
|
-
return getattr(self.results, key)
|
|
513
|
-
# __getattr__ is only called if __getattribute__ fails.
|
|
514
|
-
# In that case we should check if key is a deprecated attribute,
|
|
515
|
-
# and fail with an AttributeError if it is not.
|
|
516
|
-
raise AttributeError
|
|
517
|
-
|
|
518
|
-
def __setattr__(self, key, value):
|
|
519
|
-
if key in ModelChain._deprecated_attrs:
|
|
520
|
-
msg = f'ModelChain.{key} is deprecated from v0.9. Use' \
|
|
521
|
-
f' ModelChain.results.{key} instead'
|
|
522
|
-
warnings.warn(msg, pvlibDeprecationWarning)
|
|
523
|
-
setattr(self.results, key, value)
|
|
524
|
-
else:
|
|
525
|
-
super().__setattr__(key, value)
|
|
526
552
|
|
|
527
553
|
@classmethod
|
|
528
554
|
def with_pvwatts(cls, system, location,
|
|
@@ -560,7 +586,7 @@ class ModelChain:
|
|
|
560
586
|
Examples
|
|
561
587
|
--------
|
|
562
588
|
>>> module_parameters = dict(gamma_pdc=-0.003, pdc0=4500)
|
|
563
|
-
>>> inverter_parameters = dict(
|
|
589
|
+
>>> inverter_parameters = dict(pdc0=4000)
|
|
564
590
|
>>> tparams = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']
|
|
565
591
|
>>> system = PVSystem(surface_tilt=30, surface_azimuth=180,
|
|
566
592
|
... module_parameters=module_parameters,
|
|
@@ -678,18 +704,8 @@ class ModelChain:
|
|
|
678
704
|
'airmass_model', 'dc_model', 'ac_model', 'aoi_model',
|
|
679
705
|
'spectral_model', 'temperature_model', 'losses_model'
|
|
680
706
|
]
|
|
681
|
-
|
|
682
|
-
def getmcattr(self, attr):
|
|
683
|
-
"""needed to avoid recursion in property lookups"""
|
|
684
|
-
out = getattr(self, attr)
|
|
685
|
-
try:
|
|
686
|
-
out = out.__name__
|
|
687
|
-
except AttributeError:
|
|
688
|
-
pass
|
|
689
|
-
return out
|
|
690
|
-
|
|
691
707
|
return ('ModelChain: \n ' + '\n '.join(
|
|
692
|
-
f'{attr}: {
|
|
708
|
+
f'{attr}: {_getmcattr(self, attr)}' for attr in attrs))
|
|
693
709
|
|
|
694
710
|
@property
|
|
695
711
|
def dc_model(self):
|
|
@@ -1535,27 +1551,11 @@ class ModelChain:
|
|
|
1535
1551
|
self._prep_inputs_solar_pos(weather)
|
|
1536
1552
|
self._prep_inputs_airmass()
|
|
1537
1553
|
self._prep_inputs_albedo(weather)
|
|
1554
|
+
self._prep_inputs_fixed()
|
|
1538
1555
|
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
# the differences.
|
|
1543
|
-
if isinstance(self.system, SingleAxisTracker):
|
|
1544
|
-
self._prep_inputs_tracking()
|
|
1545
|
-
get_irradiance = partial(
|
|
1546
|
-
self.system.get_irradiance,
|
|
1547
|
-
self.results.tracking['surface_tilt'],
|
|
1548
|
-
self.results.tracking['surface_azimuth'],
|
|
1549
|
-
self.results.solar_position['apparent_zenith'],
|
|
1550
|
-
self.results.solar_position['azimuth'])
|
|
1551
|
-
else:
|
|
1552
|
-
self._prep_inputs_fixed()
|
|
1553
|
-
get_irradiance = partial(
|
|
1554
|
-
self.system.get_irradiance,
|
|
1555
|
-
self.results.solar_position['apparent_zenith'],
|
|
1556
|
-
self.results.solar_position['azimuth'])
|
|
1557
|
-
|
|
1558
|
-
self.results.total_irrad = get_irradiance(
|
|
1556
|
+
self.results.total_irrad = self.system.get_irradiance(
|
|
1557
|
+
self.results.solar_position['apparent_zenith'],
|
|
1558
|
+
self.results.solar_position['azimuth'],
|
|
1559
1559
|
_tuple_from_dfs(self.results.weather, 'dni'),
|
|
1560
1560
|
_tuple_from_dfs(self.results.weather, 'ghi'),
|
|
1561
1561
|
_tuple_from_dfs(self.results.weather, 'dhi'),
|
|
@@ -1636,10 +1636,7 @@ class ModelChain:
|
|
|
1636
1636
|
self._prep_inputs_solar_pos(data)
|
|
1637
1637
|
self._prep_inputs_airmass()
|
|
1638
1638
|
|
|
1639
|
-
|
|
1640
|
-
self._prep_inputs_tracking()
|
|
1641
|
-
else:
|
|
1642
|
-
self._prep_inputs_fixed()
|
|
1639
|
+
self._prep_inputs_fixed()
|
|
1643
1640
|
|
|
1644
1641
|
return self
|
|
1645
1642
|
|
|
@@ -1686,7 +1683,7 @@ class ModelChain:
|
|
|
1686
1683
|
self.temperature_model()
|
|
1687
1684
|
return self
|
|
1688
1685
|
|
|
1689
|
-
def _prepare_temperature(self, data
|
|
1686
|
+
def _prepare_temperature(self, data):
|
|
1690
1687
|
"""
|
|
1691
1688
|
Sets cell_temperature using inputs in data and the specified
|
|
1692
1689
|
temperature model.
|
|
@@ -1699,7 +1696,7 @@ class ModelChain:
|
|
|
1699
1696
|
|
|
1700
1697
|
Parameters
|
|
1701
1698
|
----------
|
|
1702
|
-
data : DataFrame
|
|
1699
|
+
data : DataFrame
|
|
1703
1700
|
May contain columns ``'cell_temperature'`` or
|
|
1704
1701
|
``'module_temperaure'``.
|
|
1705
1702
|
|
|
@@ -1878,13 +1875,13 @@ class ModelChain:
|
|
|
1878
1875
|
|
|
1879
1876
|
return self
|
|
1880
1877
|
|
|
1881
|
-
def _run_from_effective_irrad(self, data
|
|
1878
|
+
def _run_from_effective_irrad(self, data):
|
|
1882
1879
|
"""
|
|
1883
1880
|
Executes the temperature, DC, losses and AC models.
|
|
1884
1881
|
|
|
1885
1882
|
Parameters
|
|
1886
1883
|
----------
|
|
1887
|
-
data : DataFrame, or tuple of DataFrame
|
|
1884
|
+
data : DataFrame, or tuple of DataFrame
|
|
1888
1885
|
If optional column ``'cell_temperature'`` is provided, these values
|
|
1889
1886
|
are used instead of `temperature_model`. If optional column
|
|
1890
1887
|
`module_temperature` is provided, `temperature_model` must be
|
|
@@ -1907,7 +1904,7 @@ class ModelChain:
|
|
|
1907
1904
|
|
|
1908
1905
|
return self
|
|
1909
1906
|
|
|
1910
|
-
def run_model_from_effective_irradiance(self, data
|
|
1907
|
+
def run_model_from_effective_irradiance(self, data):
|
|
1911
1908
|
"""
|
|
1912
1909
|
Run the model starting with effective irradiance in the plane of array.
|
|
1913
1910
|
|