pvlib 0.13.1a1__py3-none-any.whl → 0.14.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/bifacial/infinite_sheds.py +3 -2
- pvlib/inverter.py +2 -2
- pvlib/iotools/__init__.py +2 -3
- pvlib/iotools/acis.py +2 -2
- pvlib/iotools/era5.py +207 -0
- pvlib/iotools/merra2.py +196 -0
- pvlib/iotools/psm4.py +0 -1
- pvlib/irradiance.py +26 -25
- pvlib/ivtools/sdm/__init__.py +2 -1
- pvlib/ivtools/sdm/desoto.py +72 -0
- pvlib/pvarray.py +130 -1
- pvlib/pvsystem.py +135 -46
- pvlib/singlediode.py +249 -52
- pvlib/solarposition.py +1 -1
- pvlib/spa.py +1 -1
- pvlib/spectrum/__init__.py +3 -2
- pvlib/spectrum/mismatch.py +101 -0
- pvlib/temperature.py +11 -12
- {pvlib-0.13.1a1.dist-info → pvlib-0.14.0.dist-info}/METADATA +3 -3
- {pvlib-0.13.1a1.dist-info → pvlib-0.14.0.dist-info}/RECORD +24 -23
- pvlib/iotools/psm3.py +0 -365
- {pvlib-0.13.1a1.dist-info → pvlib-0.14.0.dist-info}/WHEEL +0 -0
- {pvlib-0.13.1a1.dist-info → pvlib-0.14.0.dist-info}/licenses/AUTHORS.md +0 -0
- {pvlib-0.13.1a1.dist-info → pvlib-0.14.0.dist-info}/licenses/LICENSE +0 -0
- {pvlib-0.13.1a1.dist-info → pvlib-0.14.0.dist-info}/top_level.txt +0 -0
pvlib/singlediode.py
CHANGED
|
@@ -3,6 +3,7 @@ Low-level functions for solving the single diode equation.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
6
7
|
from pvlib.tools import _golden_sect_DataFrame
|
|
7
8
|
|
|
8
9
|
from scipy.optimize import brentq, newton
|
|
@@ -109,13 +110,13 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
|
|
|
109
110
|
(a-Si) modules that is the product of the PV module number of series
|
|
110
111
|
cells :math:`N_{s}` and the builtin voltage :math:`V_{bi}` of the
|
|
111
112
|
intrinsic layer. [V].
|
|
112
|
-
breakdown_factor :
|
|
113
|
+
breakdown_factor : numeric, default 0
|
|
113
114
|
fraction of ohmic current involved in avalanche breakdown :math:`a`.
|
|
114
115
|
Default of 0 excludes the reverse bias term from the model. [unitless]
|
|
115
|
-
breakdown_voltage :
|
|
116
|
+
breakdown_voltage : numeric, default -5.5
|
|
116
117
|
reverse breakdown voltage of the photovoltaic junction :math:`V_{br}`
|
|
117
118
|
[V]
|
|
118
|
-
breakdown_exp :
|
|
119
|
+
breakdown_exp : numeric, default 3.28
|
|
119
120
|
avalanche breakdown exponent :math:`m` [unitless]
|
|
120
121
|
gradients : bool
|
|
121
122
|
False returns only I, V, and P. True also returns gradients
|
|
@@ -141,18 +142,20 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
|
|
|
141
142
|
|
|
142
143
|
References
|
|
143
144
|
----------
|
|
144
|
-
.. [1] "Computer simulation of the effects of electrical
|
|
145
|
-
photovoltaic cell interconnection circuits"
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
145
|
+
.. [1] J.W. Bishop, "Computer simulation of the effects of electrical
|
|
146
|
+
mismatches in photovoltaic cell interconnection circuits" Solar Cells,
|
|
147
|
+
vol. 25 no. 1, pp. 73-89, Oct. 1988.
|
|
148
|
+
:doi:`doi.org/10.1016/0379-6787(88)90059-2`
|
|
149
|
+
|
|
150
|
+
.. [2] J. Merten, J. M. Asensi, C. Voz, A. V. Shah, R. Platz and J. Andreu,
|
|
151
|
+
"Improved equivalent circuit and Analytical Model for Amorphous
|
|
152
|
+
Silicon Solar Cells and Modules." , IEEE Transactions
|
|
153
|
+
on Electron Devices, vol. 45, no. 2, pp. 423-429, Feb 1998.
|
|
151
154
|
:doi:`10.1109/16.658676`
|
|
152
155
|
|
|
153
|
-
.. [3] "Performance assessment of a simulation
|
|
154
|
-
available technology",
|
|
155
|
-
2010
|
|
156
|
+
.. [3] A. Mermoud and T. Lejeune, "Performance assessment of a simulation
|
|
157
|
+
model for PV modules of any available technology", In Proc. of the 25th
|
|
158
|
+
European PVSEC, Valencia, ES, 2010.
|
|
156
159
|
:doi:`10.4229/25thEUPVSEC2010-4BV.1.114`
|
|
157
160
|
"""
|
|
158
161
|
# calculate recombination loss current where d2mutau > 0
|
|
@@ -162,12 +165,11 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
|
|
|
162
165
|
# calculate temporary values to simplify calculations
|
|
163
166
|
v_star = diode_voltage / nNsVth # non-dimensional diode voltage
|
|
164
167
|
g_sh = 1.0 / resistance_shunt # conductance
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
i_breakdown = 0.
|
|
168
|
+
|
|
169
|
+
brk_term = 1 - diode_voltage / breakdown_voltage
|
|
170
|
+
brk_pwr = np.power(brk_term, -breakdown_exp)
|
|
171
|
+
i_breakdown = breakdown_factor * diode_voltage * g_sh * brk_pwr
|
|
172
|
+
|
|
171
173
|
i = (photocurrent - saturation_current * np.expm1(v_star) # noqa: W503
|
|
172
174
|
- diode_voltage * g_sh - i_recomb - i_breakdown) # noqa: W503
|
|
173
175
|
v = diode_voltage - i * resistance_series
|
|
@@ -177,18 +179,14 @@ def bishop88(diode_voltage, photocurrent, saturation_current,
|
|
|
177
179
|
grad_i_recomb = np.where(is_recomb, i_recomb / v_recomb, 0)
|
|
178
180
|
grad_2i_recomb = np.where(is_recomb, 2 * grad_i_recomb / v_recomb, 0)
|
|
179
181
|
g_diode = saturation_current * np.exp(v_star) / nNsVth # conductance
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
* (-breakdown_exp - 1) * brk_pwr_2)) # noqa: W503
|
|
189
|
-
else:
|
|
190
|
-
grad_i_brk = 0.
|
|
191
|
-
grad2i_brk = 0.
|
|
182
|
+
brk_pwr_1 = np.power(brk_term, -breakdown_exp - 1)
|
|
183
|
+
brk_pwr_2 = np.power(brk_term, -breakdown_exp - 2)
|
|
184
|
+
brk_fctr = breakdown_factor * g_sh
|
|
185
|
+
grad_i_brk = brk_fctr * (brk_pwr + diode_voltage *
|
|
186
|
+
-breakdown_exp * brk_pwr_1)
|
|
187
|
+
grad2i_brk = (brk_fctr * -breakdown_exp # noqa: W503
|
|
188
|
+
* (2 * brk_pwr_1 + diode_voltage # noqa: W503
|
|
189
|
+
* (-breakdown_exp - 1) * brk_pwr_2)) # noqa: W503
|
|
192
190
|
grad_i = -g_diode - g_sh - grad_i_recomb - grad_i_brk # di/dvd
|
|
193
191
|
grad_v = 1.0 - grad_i * resistance_series # dv/dvd
|
|
194
192
|
# dp/dv = d(iv)/dv = v * di/dv + i
|
|
@@ -247,12 +245,19 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
|
|
|
247
245
|
breakdown_exp : float, default 3.28
|
|
248
246
|
avalanche breakdown exponent :math:`m` [unitless]
|
|
249
247
|
method : str, default 'newton'
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
Either ``'newton'``, ``'brentq'``, or ``'chandrupatla'``.
|
|
249
|
+
''method'' must be ``'newton'`` if ``breakdown_factor`` is not 0.
|
|
250
|
+
|
|
251
|
+
.. note::
|
|
252
|
+
``'chandrupatla'`` requires scipy 1.15 or greater.
|
|
253
|
+
|
|
252
254
|
method_kwargs : dict, optional
|
|
253
|
-
Keyword arguments passed to root finder
|
|
254
|
-
|
|
255
|
-
:py:func:`scipy:scipy.optimize.
|
|
255
|
+
Keyword arguments passed to the root finder. For options, see:
|
|
256
|
+
|
|
257
|
+
* ``method='brentq'``: :py:func:`scipy:scipy.optimize.brentq`
|
|
258
|
+
* ``method='newton'``: :py:func:`scipy:scipy.optimize.newton`
|
|
259
|
+
* ``method='chandrupatla'``: :py:func:`scipy:scipy.optimize.elementwise.find_root`
|
|
260
|
+
|
|
256
261
|
``'full_output': True`` is allowed, and ``optimizer_output`` would be
|
|
257
262
|
returned. See examples section.
|
|
258
263
|
|
|
@@ -291,7 +296,7 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
|
|
|
291
296
|
.. [1] "Computer simulation of the effects of electrical mismatches in
|
|
292
297
|
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
|
|
293
298
|
:doi:`10.1016/0379-6787(88)90059-2`
|
|
294
|
-
"""
|
|
299
|
+
""" # noqa: E501
|
|
295
300
|
# collect args
|
|
296
301
|
args = (photocurrent, saturation_current,
|
|
297
302
|
resistance_series, resistance_shunt, nNsVth, d2mutau, NsVbi,
|
|
@@ -333,6 +338,30 @@ def bishop88_i_from_v(voltage, photocurrent, saturation_current,
|
|
|
333
338
|
vd = newton(func=lambda x, *a: fv(x, voltage, *a), x0=x0,
|
|
334
339
|
fprime=lambda x, *a: bishop88(x, *a, gradients=True)[4],
|
|
335
340
|
args=args, **method_kwargs)
|
|
341
|
+
elif method == 'chandrupatla':
|
|
342
|
+
try:
|
|
343
|
+
from scipy.optimize.elementwise import find_root
|
|
344
|
+
except ModuleNotFoundError as e:
|
|
345
|
+
# TODO remove this when our minimum scipy version is >=1.15
|
|
346
|
+
msg = (
|
|
347
|
+
"method='chandrupatla' requires scipy v1.15 or greater "
|
|
348
|
+
"(available for Python 3.10+). "
|
|
349
|
+
"Select another method, or update your version of scipy."
|
|
350
|
+
)
|
|
351
|
+
raise ImportError(msg) from e
|
|
352
|
+
|
|
353
|
+
voc_est = estimate_voc(photocurrent, saturation_current, nNsVth)
|
|
354
|
+
shape = _shape_of_max_size(voltage, voc_est)
|
|
355
|
+
vlo = np.zeros(shape)
|
|
356
|
+
vhi = np.full(shape, voc_est)
|
|
357
|
+
bounds = (vlo, vhi)
|
|
358
|
+
kwargs_trimmed = method_kwargs.copy()
|
|
359
|
+
kwargs_trimmed.pop("full_output", None) # not valid for find_root
|
|
360
|
+
|
|
361
|
+
result = find_root(fv, bounds, args=(voltage, *args), **kwargs_trimmed)
|
|
362
|
+
vd = result.x
|
|
363
|
+
if method_kwargs.get('full_output'):
|
|
364
|
+
vd = (vd, result) # mimic the other methods
|
|
336
365
|
else:
|
|
337
366
|
raise NotImplementedError("Method '%s' isn't implemented" % method)
|
|
338
367
|
|
|
@@ -388,12 +417,19 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
|
|
|
388
417
|
breakdown_exp : float, default 3.28
|
|
389
418
|
avalanche breakdown exponent :math:`m` [unitless]
|
|
390
419
|
method : str, default 'newton'
|
|
391
|
-
|
|
392
|
-
|
|
420
|
+
Either ``'newton'``, ``'brentq'``, or ``'chandrupatla'``.
|
|
421
|
+
''method'' must be ``'newton'`` if ``breakdown_factor`` is not 0.
|
|
422
|
+
|
|
423
|
+
.. note::
|
|
424
|
+
``'chandrupatla'`` requires scipy 1.15 or greater.
|
|
425
|
+
|
|
393
426
|
method_kwargs : dict, optional
|
|
394
|
-
Keyword arguments passed to root finder
|
|
395
|
-
|
|
396
|
-
:py:func:`scipy:scipy.optimize.
|
|
427
|
+
Keyword arguments passed to the root finder. For options, see:
|
|
428
|
+
|
|
429
|
+
* ``method='brentq'``: :py:func:`scipy:scipy.optimize.brentq`
|
|
430
|
+
* ``method='newton'``: :py:func:`scipy:scipy.optimize.newton`
|
|
431
|
+
* ``method='chandrupatla'``: :py:func:`scipy:scipy.optimize.elementwise.find_root`
|
|
432
|
+
|
|
397
433
|
``'full_output': True`` is allowed, and ``optimizer_output`` would be
|
|
398
434
|
returned. See examples section.
|
|
399
435
|
|
|
@@ -432,7 +468,7 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
|
|
|
432
468
|
.. [1] "Computer simulation of the effects of electrical mismatches in
|
|
433
469
|
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
|
|
434
470
|
:doi:`10.1016/0379-6787(88)90059-2`
|
|
435
|
-
"""
|
|
471
|
+
""" # noqa: E501
|
|
436
472
|
# collect args
|
|
437
473
|
args = (photocurrent, saturation_current,
|
|
438
474
|
resistance_series, resistance_shunt, nNsVth, d2mutau, NsVbi,
|
|
@@ -474,6 +510,29 @@ def bishop88_v_from_i(current, photocurrent, saturation_current,
|
|
|
474
510
|
vd = newton(func=lambda x, *a: fi(x, current, *a), x0=x0,
|
|
475
511
|
fprime=lambda x, *a: bishop88(x, *a, gradients=True)[3],
|
|
476
512
|
args=args, **method_kwargs)
|
|
513
|
+
elif method == 'chandrupatla':
|
|
514
|
+
try:
|
|
515
|
+
from scipy.optimize.elementwise import find_root
|
|
516
|
+
except ModuleNotFoundError as e:
|
|
517
|
+
# TODO remove this when our minimum scipy version is >=1.15
|
|
518
|
+
msg = (
|
|
519
|
+
"method='chandrupatla' requires scipy v1.15 or greater "
|
|
520
|
+
"(available for Python 3.10+). "
|
|
521
|
+
"Select another method, or update your version of scipy."
|
|
522
|
+
)
|
|
523
|
+
raise ImportError(msg) from e
|
|
524
|
+
|
|
525
|
+
shape = _shape_of_max_size(current, voc_est)
|
|
526
|
+
vlo = np.zeros(shape)
|
|
527
|
+
vhi = np.full(shape, voc_est)
|
|
528
|
+
bounds = (vlo, vhi)
|
|
529
|
+
kwargs_trimmed = method_kwargs.copy()
|
|
530
|
+
kwargs_trimmed.pop("full_output", None) # not valid for find_root
|
|
531
|
+
|
|
532
|
+
result = find_root(fi, bounds, args=(current, *args), **kwargs_trimmed)
|
|
533
|
+
vd = result.x
|
|
534
|
+
if method_kwargs.get('full_output'):
|
|
535
|
+
vd = (vd, result) # mimic the other methods
|
|
477
536
|
else:
|
|
478
537
|
raise NotImplementedError("Method '%s' isn't implemented" % method)
|
|
479
538
|
|
|
@@ -526,12 +585,19 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
|
|
|
526
585
|
breakdown_exp : numeric, default 3.28
|
|
527
586
|
avalanche breakdown exponent :math:`m` [unitless]
|
|
528
587
|
method : str, default 'newton'
|
|
529
|
-
|
|
530
|
-
|
|
588
|
+
Either ``'newton'``, ``'brentq'``, or ``'chandrupatla'``.
|
|
589
|
+
''method'' must be ``'newton'`` if ``breakdown_factor`` is not 0.
|
|
590
|
+
|
|
591
|
+
.. note::
|
|
592
|
+
``'chandrupatla'`` requires scipy 1.15 or greater.
|
|
593
|
+
|
|
531
594
|
method_kwargs : dict, optional
|
|
532
|
-
Keyword arguments passed to root finder
|
|
533
|
-
|
|
534
|
-
:py:func:`scipy:scipy.optimize.
|
|
595
|
+
Keyword arguments passed to the root finder. For options, see:
|
|
596
|
+
|
|
597
|
+
* ``method='brentq'``: :py:func:`scipy:scipy.optimize.brentq`
|
|
598
|
+
* ``method='newton'``: :py:func:`scipy:scipy.optimize.newton`
|
|
599
|
+
* ``method='chandrupatla'``: :py:func:`scipy:scipy.optimize.elementwise.find_root`
|
|
600
|
+
|
|
535
601
|
``'full_output': True`` is allowed, and ``optimizer_output`` would be
|
|
536
602
|
returned. See examples section.
|
|
537
603
|
|
|
@@ -571,7 +637,7 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
|
|
|
571
637
|
.. [1] "Computer simulation of the effects of electrical mismatches in
|
|
572
638
|
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
|
|
573
639
|
:doi:`10.1016/0379-6787(88)90059-2`
|
|
574
|
-
"""
|
|
640
|
+
""" # noqa: E501
|
|
575
641
|
# collect args
|
|
576
642
|
args = (photocurrent, saturation_current,
|
|
577
643
|
resistance_series, resistance_shunt, nNsVth, d2mutau, NsVbi,
|
|
@@ -611,6 +677,31 @@ def bishop88_mpp(photocurrent, saturation_current, resistance_series,
|
|
|
611
677
|
vd = newton(func=fmpp, x0=x0,
|
|
612
678
|
fprime=lambda x, *a: bishop88(x, *a, gradients=True)[7],
|
|
613
679
|
args=args, **method_kwargs)
|
|
680
|
+
elif method == 'chandrupatla':
|
|
681
|
+
try:
|
|
682
|
+
from scipy.optimize.elementwise import find_root
|
|
683
|
+
except ModuleNotFoundError as e:
|
|
684
|
+
# TODO remove this when our minimum scipy version is >=1.15
|
|
685
|
+
msg = (
|
|
686
|
+
"method='chandrupatla' requires scipy v1.15 or greater "
|
|
687
|
+
"(available for Python 3.10+). "
|
|
688
|
+
"Select another method, or update your version of scipy."
|
|
689
|
+
)
|
|
690
|
+
raise ImportError(msg) from e
|
|
691
|
+
|
|
692
|
+
vlo = np.zeros_like(photocurrent)
|
|
693
|
+
vhi = np.full_like(photocurrent, voc_est)
|
|
694
|
+
kwargs_trimmed = method_kwargs.copy()
|
|
695
|
+
kwargs_trimmed.pop("full_output", None) # not valid for find_root
|
|
696
|
+
|
|
697
|
+
result = find_root(fmpp,
|
|
698
|
+
(vlo, vhi),
|
|
699
|
+
args=args,
|
|
700
|
+
**kwargs_trimmed)
|
|
701
|
+
vd = result.x
|
|
702
|
+
if method_kwargs.get('full_output'):
|
|
703
|
+
vd = (vd, result) # mimic the other methods
|
|
704
|
+
|
|
614
705
|
else:
|
|
615
706
|
raise NotImplementedError("Method '%s' isn't implemented" % method)
|
|
616
707
|
|
|
@@ -825,10 +916,25 @@ def _lambertw(photocurrent, saturation_current, resistance_series,
|
|
|
825
916
|
v_oc = 0.
|
|
826
917
|
|
|
827
918
|
# Find the voltage, v_mp, where the power is maximized.
|
|
828
|
-
#
|
|
829
|
-
|
|
919
|
+
# use scipy.elementwise if available
|
|
920
|
+
# remove try/except when scipy>=1.15, and golden mean is retired
|
|
921
|
+
try:
|
|
922
|
+
from scipy.optimize.elementwise import find_minimum
|
|
923
|
+
# left negative to insure strict inequality
|
|
924
|
+
init = (-1., 0.8*v_oc, v_oc)
|
|
925
|
+
res = find_minimum(_vmp_opt, init,
|
|
926
|
+
args=(params['photocurrent'],
|
|
927
|
+
params['saturation_current'],
|
|
928
|
+
params['resistance_series'],
|
|
929
|
+
params['resistance_shunt'],
|
|
930
|
+
params['nNsVth'],))
|
|
931
|
+
v_mp = res.x
|
|
932
|
+
p_mp = -1.*res.f_x
|
|
933
|
+
except ModuleNotFoundError:
|
|
934
|
+
# switch to old golden section method
|
|
935
|
+
p_mp, v_mp = _golden_sect_DataFrame(params, 0., v_oc * 1.14,
|
|
936
|
+
_pwr_optfcn)
|
|
830
937
|
|
|
831
|
-
# Find Imp using Lambert W
|
|
832
938
|
i_mp = _lambertw_i_from_v(v_mp, **params)
|
|
833
939
|
|
|
834
940
|
# Find Ix and Ixx using Lambert W
|
|
@@ -850,6 +956,15 @@ def _lambertw(photocurrent, saturation_current, resistance_series,
|
|
|
850
956
|
return out
|
|
851
957
|
|
|
852
958
|
|
|
959
|
+
def _vmp_opt(v, iph, io, rs, rsh, nNsVth):
|
|
960
|
+
'''
|
|
961
|
+
Function to find negative of power from ``i_from_v``.
|
|
962
|
+
'''
|
|
963
|
+
current = _lambertw_i_from_v(v, iph, io, rs, rsh, nNsVth)
|
|
964
|
+
|
|
965
|
+
return -v * current
|
|
966
|
+
|
|
967
|
+
|
|
853
968
|
def _pwr_optfcn(df, loc):
|
|
854
969
|
'''
|
|
855
970
|
Function to find power from ``i_from_v``.
|
|
@@ -861,3 +976,85 @@ def _pwr_optfcn(df, loc):
|
|
|
861
976
|
df['resistance_shunt'], df['nNsVth'])
|
|
862
977
|
|
|
863
978
|
return current * df[loc]
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
def batzelis(photocurrent, saturation_current, resistance_series,
|
|
982
|
+
resistance_shunt, nNsVth):
|
|
983
|
+
"""
|
|
984
|
+
Estimate maximum power, open-circuit, and short-circuit points from
|
|
985
|
+
single-diode equation parameters using Batzelis's method.
|
|
986
|
+
|
|
987
|
+
This method is described in Section II.B of [1]_.
|
|
988
|
+
|
|
989
|
+
Parameters
|
|
990
|
+
----------
|
|
991
|
+
photocurrent : numeric
|
|
992
|
+
Light-generated current. [A]
|
|
993
|
+
saturation_current : numeric
|
|
994
|
+
Diode saturation current. [A]
|
|
995
|
+
resistance_series : numeric
|
|
996
|
+
Series resistance. [Ohm]
|
|
997
|
+
resistance_shunt : numeric
|
|
998
|
+
Shunt resistance. [Ohm]
|
|
999
|
+
nNsVth : numeric
|
|
1000
|
+
The product of the usual diode ideality factor (n, unitless),
|
|
1001
|
+
number of cells in series (Ns), and cell thermal voltage at
|
|
1002
|
+
specified effective irradiance and cell temperature. [V]
|
|
1003
|
+
|
|
1004
|
+
Returns
|
|
1005
|
+
-------
|
|
1006
|
+
dict
|
|
1007
|
+
The returned dict-like object contains the keys/columns:
|
|
1008
|
+
|
|
1009
|
+
* ``p_mp`` - power at maximum power point. [W]
|
|
1010
|
+
* ``i_mp`` - current at maximum power point. [A]
|
|
1011
|
+
* ``v_mp`` - voltage at maximum power point. [V]
|
|
1012
|
+
* ``i_sc`` - short circuit current. [A]
|
|
1013
|
+
* ``v_oc`` - open circuit voltage. [V]
|
|
1014
|
+
|
|
1015
|
+
References
|
|
1016
|
+
----------
|
|
1017
|
+
.. [1] E. I. Batzelis, "Simple PV Performance Equations Theoretically Well
|
|
1018
|
+
Founded on the Single-Diode Model," Journal of Photovoltaics vol. 7,
|
|
1019
|
+
no. 5, pp. 1400-1409, Sep 2017, :doi:`10.1109/JPHOTOV.2017.2711431`
|
|
1020
|
+
"""
|
|
1021
|
+
# convenience variables
|
|
1022
|
+
Iph = photocurrent
|
|
1023
|
+
Is = saturation_current
|
|
1024
|
+
Rsh = resistance_shunt
|
|
1025
|
+
Rs = resistance_series
|
|
1026
|
+
a = nNsVth
|
|
1027
|
+
|
|
1028
|
+
# Eqs 3-4
|
|
1029
|
+
isc = Iph / (Rs / Rsh + 1) # manipulated to handle Rsh=np.inf correctly
|
|
1030
|
+
with np.errstate(divide='ignore'): # zero Iph
|
|
1031
|
+
voc = a * np.log(Iph / Is)
|
|
1032
|
+
|
|
1033
|
+
# Eqs 5-8
|
|
1034
|
+
w = np.real(lambertw(np.e * Iph / Is))
|
|
1035
|
+
# vmp = (1 + Rs/Rsh) * a * (w - 1) - Rs * Iph * (1 - 1/w) # not needed
|
|
1036
|
+
with np.errstate(divide='ignore', invalid='ignore'): # zero Iph -> zero w
|
|
1037
|
+
imp = Iph * (1 - 1/w) - a * (w - 1) / Rsh
|
|
1038
|
+
|
|
1039
|
+
vmp = a * (w - 1) - Rs * imp
|
|
1040
|
+
|
|
1041
|
+
vmp = np.where(Iph > 0, vmp, 0)
|
|
1042
|
+
voc = np.where(Iph > 0, voc, 0)
|
|
1043
|
+
imp = np.where(Iph > 0, imp, 0)
|
|
1044
|
+
isc = np.where(Iph > 0, isc, 0)
|
|
1045
|
+
|
|
1046
|
+
out = {
|
|
1047
|
+
'p_mp': imp * vmp,
|
|
1048
|
+
'i_mp': imp,
|
|
1049
|
+
'v_mp': vmp,
|
|
1050
|
+
'i_sc': isc,
|
|
1051
|
+
'v_oc': voc,
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
# if pandas in, ensure pandas out
|
|
1055
|
+
pandas_inputs = [
|
|
1056
|
+
x for x in [Iph, Is, Rsh, Rs, a] if isinstance(x, pd.Series)]
|
|
1057
|
+
if pandas_inputs:
|
|
1058
|
+
out = pd.DataFrame(out, index=pandas_inputs[0].index)
|
|
1059
|
+
|
|
1060
|
+
return out
|
pvlib/solarposition.py
CHANGED
|
@@ -314,7 +314,7 @@ def spa_python(time, latitude, longitude,
|
|
|
314
314
|
using time.year and time.month from pandas.DatetimeIndex.
|
|
315
315
|
For most simulations the default delta_t is sufficient.
|
|
316
316
|
The USNO has historical and forecasted delta_t [3]_.
|
|
317
|
-
|
|
317
|
+
atmos_refract : float, optional
|
|
318
318
|
The approximate atmospheric refraction (in degrees)
|
|
319
319
|
at sunrise and sunset.
|
|
320
320
|
how : str, optional, default 'numpy'
|
pvlib/spa.py
CHANGED
|
@@ -1057,7 +1057,7 @@ def solar_position(unixtime, lat, lon, elev, pressure, temp, delta_t,
|
|
|
1057
1057
|
degrees C; used for atmospheric correction
|
|
1058
1058
|
delta_t : float or array
|
|
1059
1059
|
Difference between terrestrial time and UT1.
|
|
1060
|
-
|
|
1060
|
+
atmos_refract : float
|
|
1061
1061
|
The approximate atmospheric refraction (in degrees)
|
|
1062
1062
|
at sunrise and sunset.
|
|
1063
1063
|
numthreads: int, optional, default 8
|
pvlib/spectrum/__init__.py
CHANGED
|
@@ -3,9 +3,10 @@ from pvlib.spectrum.mismatch import ( # noqa: F401
|
|
|
3
3
|
calc_spectral_mismatch_field,
|
|
4
4
|
spectral_factor_caballero,
|
|
5
5
|
spectral_factor_firstsolar,
|
|
6
|
-
spectral_factor_sapm,
|
|
7
|
-
spectral_factor_pvspec,
|
|
8
6
|
spectral_factor_jrc,
|
|
7
|
+
spectral_factor_polo,
|
|
8
|
+
spectral_factor_pvspec,
|
|
9
|
+
spectral_factor_sapm,
|
|
9
10
|
)
|
|
10
11
|
from pvlib.spectrum.irradiance import ( # noqa: F401
|
|
11
12
|
get_reference_spectra,
|
pvlib/spectrum/mismatch.py
CHANGED
|
@@ -698,3 +698,104 @@ def spectral_factor_jrc(airmass, clearsky_index, module_type=None,
|
|
|
698
698
|
+ coeff[2] * (airmass - 1.5)
|
|
699
699
|
)
|
|
700
700
|
return mismatch
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def spectral_factor_polo(precipitable_water, airmass_absolute, aod500, aoi,
|
|
704
|
+
pressure, module_type=None, coefficients=None,
|
|
705
|
+
albedo=0.2):
|
|
706
|
+
"""
|
|
707
|
+
Estimate the spectral mismatch for BIPV application in vertical facades.
|
|
708
|
+
|
|
709
|
+
The model's authors note that this model could also be applied to
|
|
710
|
+
vertical bifacial ground-mount systems [1]_, although it has not been
|
|
711
|
+
validated in that context.
|
|
712
|
+
|
|
713
|
+
Parameters
|
|
714
|
+
----------
|
|
715
|
+
precipitable_water : numeric
|
|
716
|
+
Atmospheric precipitable water. [cm]
|
|
717
|
+
airmass_absolute : numeric
|
|
718
|
+
Absolute (pressure-adjusted) airmass. See :term:`airmass_absolute`.
|
|
719
|
+
[unitless]
|
|
720
|
+
aod500 : numeric
|
|
721
|
+
Atmospheric aerosol optical depth at 500 nm. [unitless]
|
|
722
|
+
aoi : numeric
|
|
723
|
+
Angle of incidence on the vertical surface. See :term:`aoi`.
|
|
724
|
+
[degrees]
|
|
725
|
+
pressure : numeric
|
|
726
|
+
Atmospheric pressure. See :term:`pressure`. [Pa]
|
|
727
|
+
module_type : str, optional
|
|
728
|
+
One of the following PV technology strings from [1]_:
|
|
729
|
+
|
|
730
|
+
* ``'cdte'`` - anonymous CdTe module.
|
|
731
|
+
* ``'monosi'`` - anonymous monocrystalline silicon module.
|
|
732
|
+
* ``'cigs'`` - anonymous copper indium gallium selenide module.
|
|
733
|
+
* ``'asi'`` - anonymous amorphous silicon module.
|
|
734
|
+
coefficients : array-like, optional
|
|
735
|
+
User-defined coefficients, if not using one of the coefficient
|
|
736
|
+
sets via the ``module_type`` parameter. Must have nine elements.
|
|
737
|
+
The first six elements correspond to the [p1, p2, p3, p4, b, c]
|
|
738
|
+
parameters of the SMM model. The last three elements corresponds
|
|
739
|
+
to the [c1, c2, c3] parameters of the albedo correction factor.
|
|
740
|
+
albedo : numeric, default 0.2
|
|
741
|
+
Ground albedo. See :term:`albedo`. [unitless]
|
|
742
|
+
|
|
743
|
+
Returns
|
|
744
|
+
-------
|
|
745
|
+
modifier: numeric
|
|
746
|
+
spectral mismatch factor (unitless) which is multiplied
|
|
747
|
+
with broadband irradiance reaching a module's cells to estimate
|
|
748
|
+
effective irradiance, i.e., the irradiance that is converted to
|
|
749
|
+
electrical current.
|
|
750
|
+
|
|
751
|
+
Notes
|
|
752
|
+
-----
|
|
753
|
+
The Polo model was developed using only SMM values computed for scenarios
|
|
754
|
+
when the sun is visible from the module's surface (i.e., for ``aoi<90``),
|
|
755
|
+
and no provision was made in [1]_ for the case of ``aoi>90``. This would
|
|
756
|
+
create issues in the air mass calculation internal to the model.
|
|
757
|
+
Following discussion with the model's author, the pvlib implementation
|
|
758
|
+
handles ``aoi>90`` by truncating the input ``aoi`` to a maximum of
|
|
759
|
+
90 degrees.
|
|
760
|
+
|
|
761
|
+
References
|
|
762
|
+
----------
|
|
763
|
+
.. [1] J. Polo and C. Sanz-Saiz, 'Development of spectral mismatch models
|
|
764
|
+
for BIPV applications in building façades', Renewable Energy, vol. 245,
|
|
765
|
+
p. 122820, Jun. 2025, :doi:`10.1016/j.renene.2025.122820`
|
|
766
|
+
"""
|
|
767
|
+
if module_type is None and coefficients is None:
|
|
768
|
+
raise ValueError('Must provide either `module_type` or `coefficients`')
|
|
769
|
+
if module_type is not None and coefficients is not None:
|
|
770
|
+
raise ValueError('Only one of `module_type` and `coefficients` should '
|
|
771
|
+
'be provided')
|
|
772
|
+
# prevent nan for aoi greater than 90; see docstring Notes
|
|
773
|
+
aoi = np.clip(aoi, a_min=None, a_max=90)
|
|
774
|
+
f_aoi_rel = pvlib.atmosphere.get_relative_airmass(aoi,
|
|
775
|
+
model='kastenyoung1989')
|
|
776
|
+
f_aoi = pvlib.atmosphere.get_absolute_airmass(f_aoi_rel, pressure)
|
|
777
|
+
Ram = f_aoi / airmass_absolute
|
|
778
|
+
_coefficients = {
|
|
779
|
+
'cdte': (-0.0009, 46.80, 49.20, -0.87, 0.00041, 0.053),
|
|
780
|
+
'monosi': (0.0027, 10.34, 9.48, 0.31, 0.00077, 0.006),
|
|
781
|
+
'cigs': (0.0017, 2.33, 1.30, 0.11, 0.00098, -0.018),
|
|
782
|
+
'asi': (0.0024, 7.32, 7.09, -0.72, -0.0013, 0.089),
|
|
783
|
+
}
|
|
784
|
+
c = {
|
|
785
|
+
'asi': (0.0056, -0.020, 1.014),
|
|
786
|
+
'cigs': (-0.0009, -0.0003, 1),
|
|
787
|
+
'cdte': (0.0021, -0.01, 1.01),
|
|
788
|
+
'monosi': (0, -0.003, 1.0),
|
|
789
|
+
}
|
|
790
|
+
if module_type is not None:
|
|
791
|
+
coeff = _coefficients[module_type]
|
|
792
|
+
c_albedo = c[module_type]
|
|
793
|
+
else:
|
|
794
|
+
coeff = coefficients[:6]
|
|
795
|
+
c_albedo = coefficients[6:]
|
|
796
|
+
smm = coeff[0] * Ram + coeff[1] / (coeff[2] + Ram**coeff[3]) \
|
|
797
|
+
+ coeff[4] / aod500 + coeff[5]*np.sqrt(precipitable_water)
|
|
798
|
+
# Ground albedo correction
|
|
799
|
+
g = c_albedo[0] * (albedo/0.2)**2 \
|
|
800
|
+
+ c_albedo[1] * (albedo/0.2) + c_albedo[2]
|
|
801
|
+
return g*smm
|
pvlib/temperature.py
CHANGED
|
@@ -456,14 +456,14 @@ def faiman(poa_global, temp_air, wind_speed=1.0, u0=25.0, u1=6.84):
|
|
|
456
456
|
speed at module height used to determine NOCT. [m/s]
|
|
457
457
|
|
|
458
458
|
u0 : numeric, default 25.0
|
|
459
|
-
Combined heat loss factor coefficient. The default value is
|
|
460
|
-
determined by Faiman for 7 silicon modules
|
|
459
|
+
Combined heat loss factor coefficient. The default value is for module
|
|
460
|
+
temperature determined by Faiman for 7 silicon modules
|
|
461
461
|
in the Negev desert on an open rack at 30.9° tilt.
|
|
462
462
|
:math:`\left[\frac{\text{W}/{\text{m}^2}}{\text{C}}\right]`
|
|
463
463
|
|
|
464
464
|
u1 : numeric, default 6.84
|
|
465
|
-
Combined heat loss factor influenced by wind. The default value is
|
|
466
|
-
determined by Faiman for 7 silicon modules
|
|
465
|
+
Combined heat loss factor influenced by wind. The default value is
|
|
466
|
+
for module temperature determined by Faiman for 7 silicon modules
|
|
467
467
|
in the Negev desert on an open rack at 30.9° tilt.
|
|
468
468
|
:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]`
|
|
469
469
|
|
|
@@ -539,14 +539,14 @@ def faiman_rad(poa_global, temp_air, wind_speed=1.0, ir_down=None,
|
|
|
539
539
|
surface. [W/m^2]
|
|
540
540
|
|
|
541
541
|
u0 : numeric, default 25.0
|
|
542
|
-
Combined heat loss factor coefficient. The default value is
|
|
543
|
-
determined by Faiman for 7 silicon modules
|
|
542
|
+
Combined heat loss factor coefficient. The default value is for module
|
|
543
|
+
temperature determined by Faiman for 7 silicon modules
|
|
544
544
|
in the Negev desert on an open rack at 30.9° tilt.
|
|
545
545
|
:math:`\left[\frac{\text{W}/{\text{m}^2}}{\text{C}}\right]`
|
|
546
546
|
|
|
547
547
|
u1 : numeric, default 6.84
|
|
548
|
-
Combined heat loss factor influenced by wind. The default value is
|
|
549
|
-
determined by Faiman for 7 silicon modules
|
|
548
|
+
Combined heat loss factor influenced by wind. The default value is for
|
|
549
|
+
module temperature determined by Faiman for 7 silicon modules
|
|
550
550
|
in the Negev desert on an open rack at 30.9° tilt.
|
|
551
551
|
:math:`\left[ \frac{\text{W}/\text{m}^2}{\text{C}\ \left( \text{m/s} \right)} \right]`
|
|
552
552
|
|
|
@@ -713,8 +713,7 @@ def ross(poa_global, temp_air, noct=None, k=None):
|
|
|
713
713
|
return temp_air + k * poa_global
|
|
714
714
|
|
|
715
715
|
|
|
716
|
-
def _fuentes_hconv(tave, windmod,
|
|
717
|
-
check_reynold):
|
|
716
|
+
def _fuentes_hconv(tave, windmod, temp_delta, xlen, tilt, check_reynold):
|
|
718
717
|
# Calculate the convective coefficient as in Fuentes 1987 -- a mixture of
|
|
719
718
|
# free, laminar, and turbulent convection.
|
|
720
719
|
densair = 0.003484 * 101325.0 / tave # density
|
|
@@ -836,7 +835,7 @@ def fuentes(poa_global, temp_air, wind_speed, noct_installed, module_height=5,
|
|
|
836
835
|
# convective coefficient of top surface of module at NOCT
|
|
837
836
|
windmod = 1.0
|
|
838
837
|
tave = (tinoct + 293.15) / 2
|
|
839
|
-
hconv = _fuentes_hconv(tave, windmod, tinoct
|
|
838
|
+
hconv = _fuentes_hconv(tave, windmod, tinoct - 293.15, xlen,
|
|
840
839
|
surface_tilt, False)
|
|
841
840
|
|
|
842
841
|
# determine the ground temperature ratio and the ratio of the total
|
|
@@ -896,7 +895,7 @@ def fuentes(poa_global, temp_air, wind_speed, noct_installed, module_height=5,
|
|
|
896
895
|
for j in range(10):
|
|
897
896
|
# overall convective coefficient
|
|
898
897
|
tave = (tmod + tamb) / 2
|
|
899
|
-
hconv = convrat * _fuentes_hconv(tave, windmod,
|
|
898
|
+
hconv = convrat * _fuentes_hconv(tave, windmod,
|
|
900
899
|
abs(tmod-tamb), xlen,
|
|
901
900
|
surface_tilt, True)
|
|
902
901
|
# sky radiation coefficient (Equation 3)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pvlib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: A set of functions and classes for simulating the performance of photovoltaic energy systems.
|
|
5
5
|
Home-page: https://github.com/pvlib/pvlib-python
|
|
6
6
|
Author-email: pvlib python Developers <pvlib-admin@googlegroups.com>
|
|
@@ -29,7 +29,7 @@ Requires-Dist: cython; extra == "optional"
|
|
|
29
29
|
Requires-Dist: ephem; extra == "optional"
|
|
30
30
|
Requires-Dist: nrel-pysam; extra == "optional"
|
|
31
31
|
Requires-Dist: numba>=0.17.0; extra == "optional"
|
|
32
|
-
Requires-Dist: solarfactors; extra == "optional"
|
|
32
|
+
Requires-Dist: solarfactors>=1.6.1; extra == "optional"
|
|
33
33
|
Requires-Dist: statsmodels; extra == "optional"
|
|
34
34
|
Provides-Extra: doc
|
|
35
35
|
Requires-Dist: ipython; extra == "doc"
|
|
@@ -42,7 +42,7 @@ Requires-Dist: docutils==0.21; extra == "doc"
|
|
|
42
42
|
Requires-Dist: pillow; extra == "doc"
|
|
43
43
|
Requires-Dist: sphinx-toggleprompt==0.5.2; extra == "doc"
|
|
44
44
|
Requires-Dist: sphinx-favicon; extra == "doc"
|
|
45
|
-
Requires-Dist: solarfactors; extra == "doc"
|
|
45
|
+
Requires-Dist: solarfactors>=1.6.1; extra == "doc"
|
|
46
46
|
Requires-Dist: sphinx-hoverxref~=1.4.2; extra == "doc"
|
|
47
47
|
Provides-Extra: test
|
|
48
48
|
Requires-Dist: pytest; extra == "test"
|