Skip to content

Commit 6c625e8

Browse files
authored
Merge branch 'main' into get_era5
2 parents 91a73c3 + 90af1f2 commit 6c625e8

12 files changed

Lines changed: 498 additions & 34 deletions

File tree

docs/sphinx/source/reference/pv_modeling/sdm.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Functions relevant for single diode models.
1717
pvsystem.v_from_i
1818
pvsystem.max_power_point
1919
ivtools.sdm.pvsyst_temperature_coeff
20+
singlediode.batzelis
2021

2122
Low-level functions for solving the single diode equation.
2223

@@ -37,3 +38,4 @@ Functions for fitting diode models
3738
ivtools.sde.fit_sandia_simple
3839
ivtools.sdm.fit_cec_sam
3940
ivtools.sdm.fit_desoto
41+
ivtools.sdm.fit_desoto_batzelis

docs/sphinx/source/reference/pv_modeling/system_models.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,11 @@ PVGIS model
5555
:toctree: ../generated/
5656

5757
pvarray.huld
58+
59+
Other
60+
^^^^^
61+
62+
.. autosummary::
63+
:toctree: ../generated/
64+
65+
pvarray.batzelis

docs/sphinx/source/whatsnew/v0.13.2.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ Bug fixes
2121

2222
Enhancements
2323
~~~~~~~~~~~~
24+
* Add :py:func:`~pvlib.ivtools.sdm.fit_desoto_batzelis`, a function to estimate
25+
parameters for the De Soto single-diode model from datasheet values. (:pull:`2563`)
26+
* Add :py:func:`~pvlib.singlediode.batzelis`, a function to estimate
27+
maximum power, open circuit, and short circuit points using parameters for
28+
the single-diode equation. (:pull:`2563`)
29+
* Add :py:func:`~pvlib.pvarray.batzelis`, a function to estimate maximum power
30+
open circuit, and short circuit points from datasheet values. (:pull:`2563`)
2431
* Add ``method='chandrupatla'`` (faster than ``brentq`` and slower than ``newton``,
2532
but convergence is guaranteed) as an option for
2633
:py:func:`pvlib.pvsystem.singlediode`,
@@ -30,6 +37,8 @@ Enhancements
3037
:py:func:`~pvlib.singlediode.bishop88_mpp`,
3138
:py:func:`~pvlib.singlediode.bishop88_v_from_i`, and
3239
:py:func:`~pvlib.singlediode.bishop88_i_from_v`. (:issue:`2497`, :pull:`2498`)
40+
* Accelerate :py:func:`~pvlib.pvsystem.singlediode` when scipy>=1.15 is
41+
installed. (:issue:`2497`, :pull:`2571`)
3342
* Add :py:func:`~pvlib.iotools.get_era5`, a function for accessing
3443
ERA5 reanalysis data. (:pull:`2573`)
3544

@@ -57,4 +66,4 @@ Maintenance
5766

5867
Contributors
5968
~~~~~~~~~~~~
60-
69+
* Cliff Hansen (:ghuser:`cwhanse`)

pvlib/ivtools/sdm/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
from pvlib.ivtools.sdm.desoto import ( # noqa: F401
1212
fit_desoto,
13-
fit_desoto_sandia
13+
fit_desoto_batzelis,
14+
fit_desoto_sandia,
1415
)
1516

1617
from pvlib.ivtools.sdm.pvsyst import ( # noqa: F401

pvlib/ivtools/sdm/desoto.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from scipy import constants
44
from scipy import optimize
5+
from scipy.special import lambertw
56

67
from pvlib.ivtools.utils import rectify_iv_curve
78
from pvlib.ivtools.sde import _fit_sandia_cocontent
@@ -399,3 +400,74 @@ def _fit_desoto_sandia_diode(ee, voc, vth, tc, specs, const):
399400
new_x = sm.add_constant(x)
400401
res = sm.RLM(y, new_x).fit()
401402
return np.array(res.params)[1]
403+
404+
405+
def fit_desoto_batzelis(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc):
406+
"""
407+
Determine De Soto single-diode model parameters from datasheet values
408+
using Batzelis's method.
409+
410+
This method is described in Section II.C of [1]_ and fully documented
411+
in [2]_.
412+
413+
Parameters
414+
----------
415+
v_mp : float
416+
Maximum power point voltage at STC. [V]
417+
i_mp : float
418+
Maximum power point current at STC. [A]
419+
v_oc : float
420+
Open-circuit voltage at STC. [V]
421+
i_sc : float
422+
Short-circuit current at STC. [A]
423+
alpha_sc : float
424+
Short-circuit current temperature coefficient at STC. [A/K]
425+
beta_voc : float
426+
Open-circuit voltage temperature coefficient at STC. [V/K]
427+
428+
Returns
429+
-------
430+
dict
431+
The returned dict contains the keys:
432+
433+
* ``alpha_sc`` [A/K]
434+
* ``a_ref`` [V]
435+
* ``I_L_ref`` [A]
436+
* ``I_o_ref`` [A]
437+
* ``R_sh_ref`` [Ohm]
438+
* ``R_s`` [Ohm]
439+
440+
References
441+
----------
442+
.. [1] E. I. Batzelis, "Simple PV Performance Equations Theoretically Well
443+
Founded on the Single-Diode Model," Journal of Photovoltaics vol. 7,
444+
no. 5, pp. 1400-1409, Sep 2017, :doi:`10.1109/JPHOTOV.2017.2711431`
445+
.. [2] E. I. Batzelis and S. A. Papathanassiou, "A method for the
446+
analytical extraction of the single-diode PV model parameters,"
447+
IEEE Trans. Sustain. Energy, vol. 7, no. 2, pp. 504-512, Apr 2016.
448+
:doi:`10.1109/TSTE.2015.2503435`
449+
"""
450+
# convert temp coeffs from A/K and V/K to 1/K
451+
alpha_sc = alpha_sc / i_sc
452+
beta_voc = beta_voc / v_oc
453+
454+
# Equation numbers refer to [1]
455+
t0 = 298.15 # K
456+
del0 = (1 - beta_voc * t0) / (50.1 - alpha_sc * t0) # Eq 9
457+
w0 = np.real(lambertw(np.exp(1/del0 + 1)))
458+
459+
# Eqs 11-15
460+
a0 = del0 * v_oc
461+
Rs0 = (a0 * (w0 - 1) - v_mp) / i_mp
462+
Rsh0 = a0 * (w0 - 1) / (i_sc * (1 - 1/w0) - i_mp)
463+
Iph0 = (1 + Rs0 / Rsh0) * i_sc
464+
Isat0 = Iph0 * np.exp(-1/del0)
465+
466+
return {
467+
'alpha_sc': alpha_sc * i_sc, # convert 1/K to A/K
468+
'a_ref': a0,
469+
'I_L_ref': Iph0,
470+
'I_o_ref': Isat0,
471+
'R_sh_ref': Rsh0,
472+
'R_s': Rs0,
473+
}

pvlib/pvarray.py

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
"""
1010

1111
import numpy as np
12+
import pandas as pd
1213
from scipy.optimize import curve_fit
13-
from scipy.special import exp10
14+
from scipy.special import exp10, lambertw
1415

1516

1617
def pvefficiency_adr(effective_irradiance, temp_cell,
@@ -394,3 +395,131 @@ def huld(effective_irradiance, temp_mod, pdc0, k=None, cell_type=None,
394395
k[5] * tprime**2
395396
)
396397
return pdc
398+
399+
400+
def batzelis(effective_irradiance, temp_cell,
401+
v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc):
402+
"""
403+
Compute maximum power point, open circuit, and short circuit
404+
values using Batzelis's method.
405+
406+
Batzelis's method (described in Section III of [1]_) is a fast method
407+
of computing the maximum power current and voltage. The calculations
408+
are rooted in the De Soto single-diode model, but require only typical
409+
datasheet information.
410+
411+
Parameters
412+
----------
413+
effective_irradiance : numeric, non-negative
414+
Effective irradiance incident on the PV module. [Wm⁻²]
415+
temp_cell : numeric
416+
PV module operating temperature. [°C]
417+
v_mp : float
418+
Maximum power point voltage at STC. [V]
419+
i_mp : float
420+
Maximum power point current at STC. [A]
421+
v_oc : float
422+
Open-circuit voltage at STC. [V]
423+
i_sc : float
424+
Short-circuit current at STC. [A]
425+
alpha_sc : float
426+
Short-circuit current temperature coefficient at STC. [A/K]
427+
beta_voc : float
428+
Open-circuit voltage temperature coefficient at STC. [V/K]
429+
430+
Returns
431+
-------
432+
dict
433+
The returned dict-like object contains the keys/columns:
434+
435+
* ``p_mp`` - power at maximum power point. [W]
436+
* ``i_mp`` - current at maximum power point. [A]
437+
* ``v_mp`` - voltage at maximum power point. [V]
438+
* ``i_sc`` - short circuit current. [A]
439+
* ``v_oc`` - open circuit voltage. [V]
440+
441+
Notes
442+
-----
443+
This method is the combination of three sub-methods for:
444+
445+
1. estimating single-diode model parameters from datasheet information
446+
2. translating SDM parameters from STC to operating conditions
447+
(taken from the De Soto model)
448+
3. estimating the MPP, OC, and SC points on the resulting I-V curve.
449+
450+
At extremely low irradiance (e.g. 1e-10 Wm⁻²), this model can produce
451+
negative voltages. This function clips any negative voltages to zero.
452+
453+
References
454+
----------
455+
.. [1] E. I. Batzelis, "Simple PV Performance Equations Theoretically Well
456+
Founded on the Single-Diode Model," Journal of Photovoltaics vol. 7,
457+
no. 5, pp. 1400-1409, Sep 2017, :doi:`10.1109/JPHOTOV.2017.2711431`
458+
459+
Examples
460+
--------
461+
>>> params = {'i_sc': 15.98, 'v_oc': 50.26, 'i_mp': 15.27, 'v_mp': 42.57,
462+
... 'alpha_sc': 0.007351, 'beta_voc': -0.120624}
463+
>>> batzelis(np.array([1000, 800]), np.array([25, 30]), **params)
464+
{'p_mp': array([650.0439 , 512.99199048]),
465+
'i_mp': array([15.27 , 12.23049303]),
466+
'v_mp': array([42.57 , 41.94368856]),
467+
'i_sc': array([15.98 , 12.813404]),
468+
'v_oc': array([50.26 , 49.26532902])}
469+
"""
470+
# convert temp coeffs from A/K and V/K to 1/K
471+
alpha_sc = alpha_sc / i_sc
472+
beta_voc = beta_voc / v_oc
473+
474+
t0 = 298.15
475+
delT = temp_cell - (t0 - 273.15)
476+
lamT = (temp_cell + 273.15) / t0
477+
g = effective_irradiance / 1000
478+
# for zero/negative irradiance, use lnG=large negative number so that
479+
# computed voltages are negative and then clipped to zero
480+
with np.errstate(divide='ignore'): # needed for pandas for some reason
481+
lnG = np.log(g, out=np.full_like(g, -9e9), where=(g > 0))
482+
lnG = np.where(np.isfinite(g), lnG, np.nan) # also preserve nans
483+
484+
# Eq 9-10
485+
del0 = (1 - beta_voc * t0) / (50.1 - alpha_sc * t0)
486+
w0 = np.real(lambertw(np.exp(1/del0 + 1)))
487+
488+
# Eqs 27-28
489+
alpha_imp = alpha_sc + (beta_voc - 1/t0) / (w0 - 1)
490+
beta_vmp = (v_oc / v_mp) * (
491+
beta_voc / (1 + del0) +
492+
(del0 * (w0 - 1) - 1/(1 + del0)) / t0
493+
)
494+
495+
# Eq 26
496+
eps0 = (del0 / (1 + del0)) * (v_oc / v_mp)
497+
eps1 = del0 * (w0 - 1) * (v_oc / v_mp) - 1
498+
499+
# Eqs 22-25
500+
isc = g * i_sc * (1 + alpha_sc * delT)
501+
voc = v_oc * (1 + del0 * lamT * lnG + beta_voc * delT)
502+
imp = g * i_mp * (1 + alpha_imp * delT)
503+
vmp = v_mp * (1 + eps0 * lamT * lnG + eps1 * (1 - g) + beta_vmp * delT)
504+
505+
# handle negative voltages from zero and extremely small irradiance
506+
vmp = np.clip(vmp, a_min=0, a_max=None)
507+
voc = np.clip(voc, a_min=0, a_max=None)
508+
509+
out = {
510+
'p_mp': vmp * imp,
511+
'i_mp': imp,
512+
'v_mp': vmp,
513+
'i_sc': isc,
514+
'v_oc': voc,
515+
}
516+
517+
# if pandas in, ensure pandas out
518+
pandas_inputs = [
519+
x for x in [effective_irradiance, temp_cell]
520+
if isinstance(x, pd.Series)
521+
]
522+
if pandas_inputs:
523+
out = pd.DataFrame(out, index=pandas_inputs[0].index)
524+
525+
return out

pvlib/pvsystem.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2534,28 +2534,35 @@ def singlediode(photocurrent, saturation_current, resistance_series,
25342534
explicit function of :math:`V=f(I)` and :math:`I=f(V)` as shown in [2]_.
25352535
25362536
If the method is ``'newton'`` then the root-finding Newton-Raphson method
2537-
is used. It should be safe for well behaved IV-curves, but the ``'brentq'``
2538-
method is recommended for reliability.
2537+
is used. It should be safe for well-behaved IV curves, otherwise the
2538+
``'chandrupatla``` or ``'brentq'`` methods are recommended for reliability.
25392539
25402540
If the method is ``'brentq'`` then Brent's bisection search method is used
25412541
that guarantees convergence by bounding the voltage between zero and
2542-
open-circuit.
2542+
open-circuit. ``'brentq'`` is generally slower than the other options.
2543+
2544+
If the method is ``'chandrupatla'`` then Chandrupatla's method is used
2545+
that guarantees convergence.
25432546
25442547
References
25452548
----------
2546-
.. [1] S.R. Wenham, M.A. Green, M.E. Watt, "Applied Photovoltaics" ISBN
2547-
0 86758 909 4
2549+
.. [1] S. R. Wenham, M. A. Green, M. E. Watt, "Applied Photovoltaics",
2550+
Centre for Photovoltaic Devices and Systems, 1995. ISBN
2551+
0867589094
25482552
25492553
.. [2] A. Jain, A. Kapoor, "Exact analytical solutions of the
25502554
parameters of real solar cells using Lambert W-function", Solar
2551-
Energy Materials and Solar Cells, 81 (2004) 269-277.
2555+
Energy Materials and Solar Cells, vol. 81 no. 2, pp. 269-277, Feb. 2004.
2556+
:doi:`10.1016/j.solmat.2003.11.018`.
25522557
2553-
.. [3] D. King et al, "Sandia Photovoltaic Array Performance Model",
2554-
SAND2004-3535, Sandia National Laboratories, Albuquerque, NM
2558+
.. [3] D. L. King, E. E. Boyson and J. A. Kratochvil "Photovoltaic Array
2559+
Performance Model", Sandia National Laboratories, Albuquerque, NM, USA.
2560+
Report SAND2004-3535, 2004.
25552561
2556-
.. [4] "Computer simulation of the effects of electrical mismatches in
2557-
photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988)
2558-
https://doi.org/10.1016/0379-6787(88)90059-2
2562+
.. [4] J.W. Bishop, "Computer simulation of the effects of electrical
2563+
mismatches in photovoltaic cell interconnection circuits" Solar Cells,
2564+
vol. 25 no. 1, pp. 73-89, Oct. 1988.
2565+
:doi:`doi.org/10.1016/0379-6787(88)90059-2`
25592566
"""
25602567
args = (photocurrent, saturation_current, resistance_series,
25612568
resistance_shunt, nNsVth) # collect args
@@ -2565,8 +2572,9 @@ def singlediode(photocurrent, saturation_current, resistance_series,
25652572
out = _singlediode._lambertw(*args)
25662573
points = out[:7]
25672574
else:
2568-
# Calculate points on the IV curve using either 'newton' or 'brentq'
2569-
# methods. Voltages are determined by first solving the single diode
2575+
# Calculate points on the IV curve using Bishop's algorithm and solving
2576+
# with 'newton', 'brentq' or 'chandrupatla' method.
2577+
# Voltages are determined by first solving the single diode
25702578
# equation for the diode voltage V_d then backing out voltage
25712579
v_oc = _singlediode.bishop88_v_from_i(
25722580
0.0, *args, method=method.lower()

0 commit comments

Comments
 (0)