-
Notifications
You must be signed in to change notification settings - Fork 1.1k
ENH: implementing pvsyst recombination loss current for CdTe and a:Si #504
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
4b0df1b
2bd8b13
1650bc6
9d96585
f5d6dda
a62759e
bfa9c5f
79fde5b
1e9434a
3d0deb2
8a1efcb
21157e2
adde5bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,8 @@ | |
# rename newton and set keyword arguments | ||
newton = partial(_array_newton, tol=1e-6, maxiter=100, fprime2=None) | ||
|
||
VOLTAGE_BUILTIN = 0.9 # (V) intrinsic voltage for a:Si, CdTe, Mertens et al. | ||
|
||
|
||
def estimate_voc(photocurrent, saturation_current, nNsVth): | ||
""" | ||
|
@@ -62,7 +64,9 @@ def estimate_voc(photocurrent, saturation_current, nNsVth): | |
|
||
|
||
def bishop88(diode_voltage, photocurrent, saturation_current, | ||
resistance_series, resistance_shunt, nNsVth, gradients=False): | ||
resistance_series, resistance_shunt, nNsVth, cells_in_series=None, | ||
d2mutau=0, voltage_builtin=VOLTAGE_BUILTIN, | ||
gradients=False): | ||
""" | ||
Explicit calculation of points on the IV curve described by the single | ||
diode equation [1]. | ||
|
@@ -97,21 +101,31 @@ def bishop88(diode_voltage, photocurrent, saturation_current, | |
:math:`\\frac{dI}{dV}`, :math:`\\frac{dP}{dV}`, and | ||
:math:`\\frac{d^2 P}{dV dV_d}` | ||
""" | ||
# check if need to calculate recombination loss current | ||
i_recomb, v_recomb = 0, np.inf | ||
if d2mutau > 0: | ||
v_recomb = voltage_builtin * cells_in_series - diode_voltage | ||
i_recomb = photocurrent * d2mutau / v_recomb | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would go with
instead of defining them first, then potentially redefining them. Seems cleaner, no? |
||
# calculate temporary values to simplify calculations | ||
v_star = diode_voltage / nNsVth # non-dimensional diode voltage | ||
g_sh = 1.0 / resistance_shunt # conductance | ||
i = (photocurrent - saturation_current * np.expm1(v_star) | ||
- diode_voltage * g_sh) | ||
- diode_voltage * g_sh - i_recomb) | ||
v = diode_voltage - i * resistance_series | ||
retval = (i, v, i*v) | ||
if gradients: | ||
# check again if need to calculate recombination loss current gradients | ||
grad_i_recomb = grad_2i_recomb = 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No offense, but I don't care for this assignment pattern. Two lines, please. |
||
if d2mutau > 0: | ||
grad_i_recomb = i_recomb / v_recomb | ||
grad_2i_recomb = 2 * grad_i_recomb / v_recomb | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here, too, I prefer an else statement. |
||
g_diode = saturation_current * np.exp(v_star) / nNsVth # conductance | ||
grad_i = -g_diode - g_sh # di/dvd | ||
grad_i = -g_diode - g_sh - grad_i_recomb # di/dvd | ||
grad_v = 1.0 - grad_i * resistance_series # dv/dvd | ||
# dp/dv = d(iv)/dv = v * di/dv + i | ||
grad = grad_i / grad_v # di/dv | ||
grad_p = v * grad + i # dp/dv | ||
grad2i = -g_diode / nNsVth # d2i/dvd | ||
grad2i = -g_diode / nNsVth - grad_2i_recomb # d2i/dvd | ||
grad2v = -grad2i * resistance_series # d2v/dvd | ||
grad2p = ( | ||
grad_v * grad + v * (grad2i/grad_v - grad_i*grad2v/grad_v**2) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
|
||
import numpy as np | ||
from pvlib import pvsystem | ||
from pvlib.singlediode_methods import bishop88, estimate_voc | ||
import pytest | ||
from conftest import requires_scipy | ||
|
||
POA = 888 | ||
|
@@ -100,7 +102,7 @@ def test_brentq_spr_e20_327(): | |
|
||
@requires_scipy | ||
def test_brentq_fs_495(): | ||
"""test pvsystem.singlediode with Brent method on SPR-E20-327""" | ||
"""test pvsystem.singlediode with Brent method on FS495""" | ||
fs_495 = CECMOD.First_Solar_FS_495 | ||
x = pvsystem.calcparams_desoto( | ||
effective_irradiance=POA, temp_cell=TCELL, | ||
|
@@ -125,3 +127,66 @@ def test_brentq_fs_495(): | |
method='lambertw') | ||
assert np.isclose(pvs_ixx, ixx) | ||
return isc, voc, imp, vmp, pmp, i, v, pvs | ||
|
||
|
||
# PVsyst parameters for First Solar FS-495 module from PVSyst-6.7.2 database | ||
# I_L_ref derived from Isc_ref conditions: | ||
# I_L_ref = (I_sc_ref + Id + Ish) / (1 - d2mutau/(Vbi*N_s - Vd)) | ||
# where | ||
# Vd = I_sc_ref * R_s | ||
# Id = I_o_ref * (exp(Vd / nNsVt) - 1) | ||
# Ish = Vd / R_sh_ref | ||
PVSYST_FS_495 = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dangerous to use a module level dictionary in tests because it can be modified. Suggest turning this into a pytest fixture @pytest.fixture
def PVSYST_FS_495():
return {...} probably with a lowercase name but no big deal here. |
||
'd2mutau': 1.31, 'alpha_sc': 0.00039, 'gamma_ref': 1.48, 'mu_gamma': 0.001, | ||
'I_o_ref': 9.62e-10, 'R_sh_ref': 5000, 'R_sh_0': 12500, 'R_sh_exp': 3.1, | ||
'R_s': 4.6, 'beta_oc': -0.2116, 'EgRef': 1.5, 'cells_in_series': 108, | ||
'cells_in_parallel': 2, 'I_sc_ref': 1.55, 'V_oc_ref': 86.5, | ||
'I_mp_ref': 1.4, 'V_mp_ref': 67.85, 'temp_ref': 25, 'irrad_ref': 1000, | ||
'I_L_ref': 1.5743233463848496 | ||
} | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'poa, temp_cell, expected, tol', | ||
[(PVSYST_FS_495['irrad_ref'], PVSYST_FS_495['temp_ref'], | ||
{'pmp': PVSYST_FS_495['I_mp_ref']*PVSYST_FS_495['V_mp_ref'], | ||
'isc': PVSYST_FS_495['I_sc_ref'], 'voc': PVSYST_FS_495['V_oc_ref']}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't recall if these dict look ups will work if you turn PVSYST_FS_495 into a fixture. If not, I guess it's ok to leave as is for now. |
||
(5e-4, 0.04)), | ||
(POA, TCELL, {'pmp': 76.26, 'isc': 1.387, 'voc': 79.29}, (1e-3, 1e-3))] | ||
) # DeSoto @(888[W/m**2], 55[degC]) = {Pmp: 72.71, Isc: 1.402, Voc: 75.42) | ||
def test_pvsyst_recombination_loss(poa, temp_cell, expected, tol): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if |
||
"""test PVSst recombination loss""" | ||
# first evaluate PVSyst model with thin-film recombination loss current | ||
# at reference conditions | ||
pvsyst_fs_495 = PVSYST_FS_495 | ||
x = pvsystem.calcparams_pvsyst( | ||
effective_irradiance=poa, temp_cell=temp_cell, | ||
alpha_sc=pvsyst_fs_495['alpha_sc'], | ||
gamma_ref=pvsyst_fs_495['gamma_ref'], | ||
mu_gamma=pvsyst_fs_495['mu_gamma'], I_L_ref=pvsyst_fs_495['I_L_ref'], | ||
I_o_ref=pvsyst_fs_495['I_o_ref'], R_sh_ref=pvsyst_fs_495['R_sh_ref'], | ||
R_sh_0=pvsyst_fs_495['R_sh_0'], R_sh_exp=pvsyst_fs_495['R_sh_exp'], | ||
R_s=pvsyst_fs_495['R_s'], | ||
cells_in_series=pvsyst_fs_495['cells_in_series'], | ||
EgRef=pvsyst_fs_495['EgRef'] | ||
) | ||
il_pvsyst, io_pvsyst, rs_pvsyst, rsh_pvsyst, nnsvt_pvsyst = x | ||
voc_est_pvsyst = estimate_voc(photocurrent=il_pvsyst, | ||
saturation_current=io_pvsyst, | ||
nNsVth=nnsvt_pvsyst) | ||
vd_pvsyst = np.linspace(0, voc_est_pvsyst, 1000) | ||
pvsyst = bishop88( | ||
diode_voltage=vd_pvsyst, photocurrent=il_pvsyst, | ||
saturation_current=io_pvsyst, resistance_series=rs_pvsyst, | ||
resistance_shunt=rsh_pvsyst, nNsVth=nnsvt_pvsyst, | ||
d2mutau=pvsyst_fs_495['d2mutau'], | ||
cells_in_series=pvsyst_fs_495['cells_in_series'] | ||
) | ||
# test max power | ||
assert np.isclose(max(pvsyst[2]), expected['pmp'], *tol) | ||
# test short circuit current | ||
isc_pvsyst = np.interp(0, pvsyst[1], pvsyst[0]) | ||
assert np.isclose(isc_pvsyst, expected['isc'], *tol) | ||
# test open circuit current | ||
voc_pvsyst = np.interp(0, pvsyst[0][::-1], pvsyst[1][::-1]) | ||
assert np.isclose(voc_pvsyst, expected['voc'], *tol) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d2mutau : numeric
implies that it can be an array (see here). Thisif
statement will error ifd2mutau
is an array. Perhaps the docstring should be changed toscalar
?