From 4b0df1bed16dcc4e88da0250685cae2fc2aac90f Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Fri, 13 Jul 2018 17:49:08 -0700 Subject: [PATCH 01/12] implementing pvsyst recombination loss current for CdTe and a:Si * closes #163 * add constant VOLTAGE_BUILTIN = 0.9 (V) to singlediode_methods.py * add arguments d2mutau=0, voltage_builtin=VOLTAGE_BUILTIN, and cells_in_series to calculate i_recomb and v_recomb * add check for d2mutau to calc i_recomb, v_recomb, and gradients * add terms to current and gradients --- pvlib/singlediode_methods.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pvlib/singlediode_methods.py b/pvlib/singlediode_methods.py index 048870d13a..ba4ae24675 100644 --- a/pvlib/singlediode_methods.py +++ b/pvlib/singlediode_methods.py @@ -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, d2mutau=0, + cells_in_series=None, voltage_builtin=VOLTAGE_BUILTIN, + gradients=False): """ Explicit calculation of points on the IV curve described by the single diode equation [1]. @@ -97,21 +101,28 @@ 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 + if d2mutau > 0: + v_recomb = voltage_builtin - diode_voltage / cells_in_series + i_recomb = photocurrent * d2mutau / v_recomb # 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: + if d2mutau > 0: + grad_i_recomb = i_recomb/v_recomb 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 - 2*grad_i_recomb/v_recomb # d2i/dvd grad2v = -grad2i * resistance_series # d2v/dvd grad2p = ( grad_v * grad + v * (grad2i/grad_v - grad_i*grad2v/grad_v**2) From 2bd8b1354839205cb2d2545b585c300410a48cac Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Fri, 27 Jul 2018 23:49:40 -0700 Subject: [PATCH 02/12] ENH: need to set grad_i_recomb if d2mutau not given * also set v_recomb to Inf, so that 1/v_recomb is always zero * but precalculate grad_2i_recomb only if d2mutau > 0, otherwise, use zero --- pvlib/singlediode_methods.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pvlib/singlediode_methods.py b/pvlib/singlediode_methods.py index ba4ae24675..99316ff123 100644 --- a/pvlib/singlediode_methods.py +++ b/pvlib/singlediode_methods.py @@ -102,7 +102,7 @@ def bishop88(diode_voltage, photocurrent, saturation_current, :math:`\\frac{d^2 P}{dV dV_d}` """ # check if need to calculate recombination loss current - i_recomb = v_recomb = 0 + i_recomb, v_recomb = 0, np.inf if d2mutau > 0: v_recomb = voltage_builtin - diode_voltage / cells_in_series i_recomb = photocurrent * d2mutau / v_recomb @@ -114,15 +114,18 @@ def bishop88(diode_voltage, photocurrent, saturation_current, 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 if d2mutau > 0: - grad_i_recomb = i_recomb/v_recomb + grad_i_recomb = i_recomb / v_recomb + grad_2i_recomb = 2 * grad_i_recomb / v_recomb g_diode = saturation_current * np.exp(v_star) / nNsVth # conductance 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 - 2*grad_i_recomb/v_recomb # 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) From 1650bc6383c8cf662094fc553ae444c6c5ebd825 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Sat, 28 Jul 2018 21:18:11 -0700 Subject: [PATCH 03/12] ENH: fix need to multiply vbi by Ns, add test for recombination --- pvlib/singlediode_methods.py | 2 +- pvlib/test/test_singlediode_methods.py | 75 ++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/pvlib/singlediode_methods.py b/pvlib/singlediode_methods.py index 99316ff123..90dc2b2489 100644 --- a/pvlib/singlediode_methods.py +++ b/pvlib/singlediode_methods.py @@ -104,7 +104,7 @@ def bishop88(diode_voltage, photocurrent, saturation_current, # check if need to calculate recombination loss current i_recomb, v_recomb = 0, np.inf if d2mutau > 0: - v_recomb = voltage_builtin - diode_voltage / cells_in_series + v_recomb = voltage_builtin * cells_in_series - diode_voltage i_recomb = photocurrent * d2mutau / v_recomb # calculate temporary values to simplify calculations v_star = diode_voltage / nNsVth # non-dimensional diode voltage diff --git a/pvlib/test/test_singlediode_methods.py b/pvlib/test/test_singlediode_methods.py index 93ad8020ed..c3efe3a269 100644 --- a/pvlib/test/test_singlediode_methods.py +++ b/pvlib/test/test_singlediode_methods.py @@ -4,6 +4,7 @@ import numpy as np from pvlib import pvsystem +from pvlib.singlediode_methods import bishop88, estimate_voc from conftest import requires_scipy POA = 888 @@ -125,3 +126,77 @@ def test_brentq_fs_495(): method='lambertw') assert np.isclose(pvs_ixx, ixx) return isc, voc, imp, vmp, pmp, i, v, pvs + + +def test_fs_495_recombination_loss(): + """test pvsystem.singlediode with Brent method on SPR-E20-327""" + fs_495 = CECMOD.First_Solar_FS_495 + d2mutau_fs_495 = 1.3 # pvsyst recombination loss parameter (volts) + x = pvsystem.calcparams_desoto( + effective_irradiance=POA, temp_cell=TCELL, + alpha_sc=fs_495.alpha_sc, a_ref=fs_495.a_ref, I_L_ref=fs_495.I_L_ref, + I_o_ref=fs_495.I_o_ref, R_sh_ref=fs_495.R_sh_ref, R_s=fs_495.R_s, + EgRef=1.475, dEgdT=-0.0003) + il, io, rs, rsh, nnsvt = x + voc_est = estimate_voc(photocurrent=il, saturation_current=io, + nNsVth=nnsvt) + vd = np.linspace(0, voc_est, 1000) + noloss = bishop88( + diode_voltage=vd, photocurrent=il, saturation_current=io, + resistance_series=rs, resistance_shunt=rsh, nNsVth=nnsvt, + d2mutau=0, cells_in_series=fs_495.N_s/2, gradients=False) + wloss = bishop88( + diode_voltage=vd, photocurrent=il, saturation_current=io, + resistance_series=rs, resistance_shunt=rsh, nNsVth=nnsvt, + d2mutau=d2mutau_fs_495, cells_in_series=fs_495.N_s/2, gradients=False) + # test expected change in max power + assert np.isclose(max(noloss[2]) - max(wloss[2]), 2.906285338766608) + # test expected change in short circuit current + isc_noloss = np.interp(0, noloss[1], noloss[0]) + isc_wloss = np.interp(0, wloss[1], wloss[0]) + assert np.isclose(isc_noloss - isc_wloss, 0.020758929386382796) + # test expected change in open circuit current + voc_noloss = np.interp(0, noloss[0][::-1], noloss[1][::-1]) + voc_wloss = np.interp(0, wloss[0][::-1], wloss[1][::-1]) + assert np.isclose(voc_noloss - voc_wloss, 0.2098581317348902) + return noloss, wloss + + +if __name__ == '__main__': + import matplotlib.pyplot as plt + a, b = test_fs_495_recombination_loss() + ab0 = np.interp(a[1], b[1], b[0]) + pmpa, pmpb = max(a[2]), max(b[2]) + isca = np.interp(0, a[1], a[0]) + iscb = np.interp(0, b[1], b[0]) + voca = np.interp(0, a[0][::-1], a[1][::-1]) + vocb = np.interp(0, b[0][::-1], b[1][::-1]) + f1 = plt.figure('power') + plt.plot(a[1], a[2], a[1], ab0 * a[1], b[1], b[2], '--', + a[1], a[2] - ab0 * a[1]) + plt.plot([a[1][0], a[1][-1]], [pmpa]*2, ':', + [b[1][0], b[1][-1]], [pmpb]*2, ':') + plt.legend(['no loss', 'w/loss interp', 'w/loss', '$\Delta P_{mp}$', + '$P_{mp,noloss}=%4.1f$' % pmpa, + '$P_{mp,wloss}=%4.1f$' % pmpb]) + plt.grid() + plt.xlabel('voltage (V)') + plt.ylabel('power (P)') + plt.title('FS-495 power with and w/o recombination loss') + f1.show() + f2 = plt.figure('current') + plt.plot(a[1], a[0], a[1], ab0, b[1], b[0], '--', a[1], a[0] - ab0) + plt.plot([a[1][0], a[1][-1]], [isca]*2, ':', + [b[1][0], b[1][-1]], [iscb]*2, ':') + plt.plot([voca]*2, [a[0][0], a[0][-1]], ':', + [vocb]*2, [b[0][0], b[0][-1]], ':') + plt.legend(['no loss', 'w/loss interp', 'w/loss', '$\Delta I$', + '$I_{sc,noloss}=%4.2f$' % isca, + '$I_{sc,wloss}=%4.2f$' % iscb, + '$V_{oc,noloss}=%4.1f$' % voca, + '$V_{oc,wloss}=%4.1f$' % vocb]) + plt.grid() + plt.xlabel('voltage (V)') + plt.ylabel('current (I)') + plt.title('FS-495 IV-curve with and w/o recombination loss') + f2.show() From 9d96585cb81678a49a68eaf3749a357eeefe24b3 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Tue, 31 Jul 2018 15:42:13 -0700 Subject: [PATCH 04/12] WIP: ENH: TST: BUG: in test compare desoto vs. pvsyst --- pvlib/test/test_singlediode_methods.py | 142 +++++++++++++++++-------- 1 file changed, 100 insertions(+), 42 deletions(-) diff --git a/pvlib/test/test_singlediode_methods.py b/pvlib/test/test_singlediode_methods.py index c3efe3a269..09dfafb294 100644 --- a/pvlib/test/test_singlediode_methods.py +++ b/pvlib/test/test_singlediode_methods.py @@ -4,7 +4,7 @@ import numpy as np from pvlib import pvsystem -from pvlib.singlediode_methods import bishop88, estimate_voc +from pvlib.singlediode_methods import bishop88, estimate_voc, VOLTAGE_BUILTIN from conftest import requires_scipy POA = 888 @@ -128,43 +128,101 @@ def test_brentq_fs_495(): return isc, voc, imp, vmp, pmp, i, v, pvs -def test_fs_495_recombination_loss(): +def pvsyst_fs_495(): + """ + PVSyst First Solar FS-495 parameters. + + Returns + ------- + dictionary of PVSyst First Solar FS-495 parameters + """ + fs_495 = dict(d2mutau=1.31, alpha_sc=0.00039, gamma_ref=1.48, + mu_gamma=0.001, I_o_ref=0.962e-9, R_sh_ref=5000, + R_sh_0=12500, R_sh_exp=3.1, R_s=4.6, beta_oc=-0.2116, + EgRef=1.475, 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) + Vt = 0.025693001600485238 # thermal voltage at reference (V) + nNsVt = fs_495['cells_in_series'] * fs_495['gamma_ref'] * Vt + Vd = fs_495['I_sc_ref'] * fs_495['R_s'] # diode voltage at short circuit + Id = fs_495['I_o_ref'] * (np.exp(Vd / nNsVt) - 1) # diode current (A) + Ish = Vd / fs_495['R_sh_ref'] # shunt current (A) + # builtin potential difference (V) + dv = VOLTAGE_BUILTIN * fs_495['cells_in_series'] - Vd + # calculate photo-generated current at reference condition (A) + fs_495['I_L_ref'] = ( + (fs_495['I_sc_ref'] + Id + Ish) / (1 - fs_495['d2mutau'] / dv) + ) + return fs_495 + + +PVSYST_FS_495 = pvsyst_fs_495() # PVSyst First Solar FS-495 parameters + + +def test_pvsyst_fs_495_recombination_loss(): """test pvsystem.singlediode with Brent method on SPR-E20-327""" - fs_495 = CECMOD.First_Solar_FS_495 - d2mutau_fs_495 = 1.3 # pvsyst recombination loss parameter (volts) - x = pvsystem.calcparams_desoto( - effective_irradiance=POA, temp_cell=TCELL, - alpha_sc=fs_495.alpha_sc, a_ref=fs_495.a_ref, I_L_ref=fs_495.I_L_ref, - I_o_ref=fs_495.I_o_ref, R_sh_ref=fs_495.R_sh_ref, R_s=fs_495.R_s, - EgRef=1.475, dEgdT=-0.0003) - il, io, rs, rsh, nnsvt = x - voc_est = estimate_voc(photocurrent=il, saturation_current=io, - nNsVth=nnsvt) - vd = np.linspace(0, voc_est, 1000) - noloss = bishop88( - diode_voltage=vd, photocurrent=il, saturation_current=io, - resistance_series=rs, resistance_shunt=rsh, nNsVth=nnsvt, - d2mutau=0, cells_in_series=fs_495.N_s/2, gradients=False) - wloss = bishop88( - diode_voltage=vd, photocurrent=il, saturation_current=io, - resistance_series=rs, resistance_shunt=rsh, nNsVth=nnsvt, - d2mutau=d2mutau_fs_495, cells_in_series=fs_495.N_s/2, gradients=False) + poa, temp_cell = 1000.0, 25.0 # test conditions + + # first evaluate DeSoto model + cec_fs_495 = CECMOD.First_Solar_FS_495 # CEC parameters for + il_cec, io_cec, rs_cec, rsh_cec, nnsvt_cec = pvsystem.calcparams_desoto( + effective_irradiance=poa, temp_cell=temp_cell, + alpha_sc=cec_fs_495.alpha_sc, a_ref=cec_fs_495.a_ref, + I_L_ref=cec_fs_495.I_L_ref, I_o_ref=cec_fs_495.I_o_ref, + R_sh_ref=cec_fs_495.R_sh_ref, R_s=cec_fs_495.R_s, + EgRef=1.475, dEgdT=-0.0003 + ) + voc_est_cec = estimate_voc(photocurrent=il_cec, saturation_current=io_cec, + nNsVth=nnsvt_cec) + vd_cec = np.linspace(0, voc_est_cec, 1000) + desoto = bishop88( + diode_voltage=vd_cec, photocurrent=il_cec, saturation_current=io_cec, + resistance_series=rs_cec, resistance_shunt=rsh_cec, nNsVth=nnsvt_cec + ) + + # now evaluate PVSyst model with thin-film recombination loss current + 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 expected change in max power - assert np.isclose(max(noloss[2]) - max(wloss[2]), 2.906285338766608) + assert np.isclose(max(desoto[2]) - max(pvsyst[2]), 0.01949420697212645) # test expected change in short circuit current - isc_noloss = np.interp(0, noloss[1], noloss[0]) - isc_wloss = np.interp(0, wloss[1], wloss[0]) - assert np.isclose(isc_noloss - isc_wloss, 0.020758929386382796) + isc_desoto = np.interp(0, desoto[1], desoto[0]) + isc_pvsyst = np.interp(0, pvsyst[1], pvsyst[0]) + assert np.isclose(isc_desoto - isc_pvsyst, -7.955827628380874e-05) # test expected change in open circuit current - voc_noloss = np.interp(0, noloss[0][::-1], noloss[1][::-1]) - voc_wloss = np.interp(0, wloss[0][::-1], wloss[1][::-1]) - assert np.isclose(voc_noloss - voc_wloss, 0.2098581317348902) - return noloss, wloss + voc_desoto = np.interp(0, desoto[0][::-1], desoto[1][::-1]) + voc_pvsyst = np.interp(0, pvsyst[0][::-1], pvsyst[1][::-1]) + assert np.isclose(voc_desoto - voc_pvsyst, -0.04184247739671321) + return desoto, pvsyst if __name__ == '__main__': import matplotlib.pyplot as plt - a, b = test_fs_495_recombination_loss() + a, b = test_pvsyst_fs_495_recombination_loss() ab0 = np.interp(a[1], b[1], b[0]) pmpa, pmpb = max(a[2]), max(b[2]) isca = np.interp(0, a[1], a[0]) @@ -176,13 +234,13 @@ def test_fs_495_recombination_loss(): a[1], a[2] - ab0 * a[1]) plt.plot([a[1][0], a[1][-1]], [pmpa]*2, ':', [b[1][0], b[1][-1]], [pmpb]*2, ':') - plt.legend(['no loss', 'w/loss interp', 'w/loss', '$\Delta P_{mp}$', - '$P_{mp,noloss}=%4.1f$' % pmpa, - '$P_{mp,wloss}=%4.1f$' % pmpb]) + plt.legend(['DeSoto', 'PVSyst interpolated', 'PVSyst', '$\Delta P$', + '$P_{mp,DeSoto}=%4.1f$' % pmpa, + '$P_{mp,PVSyst}=%4.1f$' % pmpb]) plt.grid() plt.xlabel('voltage (V)') - plt.ylabel('power (P)') - plt.title('FS-495 power with and w/o recombination loss') + plt.ylabel('power (W)') + plt.title('FS-495 power, DeSoto vs. PVSyst with recombination loss') f1.show() f2 = plt.figure('current') plt.plot(a[1], a[0], a[1], ab0, b[1], b[0], '--', a[1], a[0] - ab0) @@ -190,13 +248,13 @@ def test_fs_495_recombination_loss(): [b[1][0], b[1][-1]], [iscb]*2, ':') plt.plot([voca]*2, [a[0][0], a[0][-1]], ':', [vocb]*2, [b[0][0], b[0][-1]], ':') - plt.legend(['no loss', 'w/loss interp', 'w/loss', '$\Delta I$', - '$I_{sc,noloss}=%4.2f$' % isca, - '$I_{sc,wloss}=%4.2f$' % iscb, - '$V_{oc,noloss}=%4.1f$' % voca, - '$V_{oc,wloss}=%4.1f$' % vocb]) + plt.legend(['DeSoto', 'PVSyst interpolated', 'PVSyst', '$\Delta I$', + '$I_{sc,DeSoto}=%4.2f$' % isca, + '$I_{sc,PVSyst}=%4.2f$' % iscb, + '$V_{oc,DeSoto}=%4.1f$' % voca, + '$V_{oc,PVSyst}=%4.1f$' % vocb]) plt.grid() plt.xlabel('voltage (V)') - plt.ylabel('current (I)') - plt.title('FS-495 IV-curve with and w/o recombination loss') + plt.ylabel('current (A)') + plt.title('FS-495 IV-curve, DeSoto vs. PVSyst with recombination loss') f2.show() From f5d6ddafaea6bbe3e0ad5b53d2f51cedb622a0da Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Thu, 2 Aug 2018 13:34:33 -0700 Subject: [PATCH 05/12] ENH: respond to comments * change order of bishop88 args: cells_in_series to be first before d2mutau and voltage_builtin * hardcode PVSYST_FS495 coefficients * don't compare pvsyst to desoto, instead compare pvsyst to fixed values at two conditions: reference and 888[W/m^2],55[degC] * rename test to be agnostic to module SKU * remove testing script if __name__ == '__main__': section, not for release --- pvlib/singlediode_methods.py | 4 +- pvlib/test/test_singlediode_methods.py | 144 +++++++------------------ 2 files changed, 40 insertions(+), 108 deletions(-) diff --git a/pvlib/singlediode_methods.py b/pvlib/singlediode_methods.py index 90dc2b2489..c6080ec964 100644 --- a/pvlib/singlediode_methods.py +++ b/pvlib/singlediode_methods.py @@ -64,8 +64,8 @@ def estimate_voc(photocurrent, saturation_current, nNsVth): def bishop88(diode_voltage, photocurrent, saturation_current, - resistance_series, resistance_shunt, nNsVth, d2mutau=0, - cells_in_series=None, voltage_builtin=VOLTAGE_BUILTIN, + 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 diff --git a/pvlib/test/test_singlediode_methods.py b/pvlib/test/test_singlediode_methods.py index 09dfafb294..edb36becd7 100644 --- a/pvlib/test/test_singlediode_methods.py +++ b/pvlib/test/test_singlediode_methods.py @@ -4,7 +4,8 @@ import numpy as np from pvlib import pvsystem -from pvlib.singlediode_methods import bishop88, estimate_voc, VOLTAGE_BUILTIN +from pvlib.singlediode_methods import bishop88, estimate_voc +import pytest from conftest import requires_scipy POA = 888 @@ -101,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, @@ -128,60 +129,35 @@ def test_brentq_fs_495(): return isc, voc, imp, vmp, pmp, i, v, pvs -def pvsyst_fs_495(): - """ - PVSyst First Solar FS-495 parameters. - - Returns - ------- - dictionary of PVSyst First Solar FS-495 parameters - """ - fs_495 = dict(d2mutau=1.31, alpha_sc=0.00039, gamma_ref=1.48, - mu_gamma=0.001, I_o_ref=0.962e-9, R_sh_ref=5000, - R_sh_0=12500, R_sh_exp=3.1, R_s=4.6, beta_oc=-0.2116, - EgRef=1.475, 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) - Vt = 0.025693001600485238 # thermal voltage at reference (V) - nNsVt = fs_495['cells_in_series'] * fs_495['gamma_ref'] * Vt - Vd = fs_495['I_sc_ref'] * fs_495['R_s'] # diode voltage at short circuit - Id = fs_495['I_o_ref'] * (np.exp(Vd / nNsVt) - 1) # diode current (A) - Ish = Vd / fs_495['R_sh_ref'] # shunt current (A) - # builtin potential difference (V) - dv = VOLTAGE_BUILTIN * fs_495['cells_in_series'] - Vd - # calculate photo-generated current at reference condition (A) - fs_495['I_L_ref'] = ( - (fs_495['I_sc_ref'] + Id + Ish) / (1 - fs_495['d2mutau'] / dv) - ) - return fs_495 - - -PVSYST_FS_495 = pvsyst_fs_495() # PVSyst First Solar FS-495 parameters - - -def test_pvsyst_fs_495_recombination_loss(): - """test pvsystem.singlediode with Brent method on SPR-E20-327""" - poa, temp_cell = 1000.0, 25.0 # test conditions - - # first evaluate DeSoto model - cec_fs_495 = CECMOD.First_Solar_FS_495 # CEC parameters for - il_cec, io_cec, rs_cec, rsh_cec, nnsvt_cec = pvsystem.calcparams_desoto( - effective_irradiance=poa, temp_cell=temp_cell, - alpha_sc=cec_fs_495.alpha_sc, a_ref=cec_fs_495.a_ref, - I_L_ref=cec_fs_495.I_L_ref, I_o_ref=cec_fs_495.I_o_ref, - R_sh_ref=cec_fs_495.R_sh_ref, R_s=cec_fs_495.R_s, - EgRef=1.475, dEgdT=-0.0003 - ) - voc_est_cec = estimate_voc(photocurrent=il_cec, saturation_current=io_cec, - nNsVth=nnsvt_cec) - vd_cec = np.linspace(0, voc_est_cec, 1000) - desoto = bishop88( - diode_voltage=vd_cec, photocurrent=il_cec, saturation_current=io_cec, - resistance_series=rs_cec, resistance_shunt=rsh_cec, nNsVth=nnsvt_cec - ) - - # now evaluate PVSyst model with thin-film recombination loss current +# 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 = { + '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']}, + (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): + """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, @@ -206,55 +182,11 @@ def test_pvsyst_fs_495_recombination_loss(): d2mutau=pvsyst_fs_495['d2mutau'], cells_in_series=pvsyst_fs_495['cells_in_series'] ) - - # test expected change in max power - assert np.isclose(max(desoto[2]) - max(pvsyst[2]), 0.01949420697212645) - # test expected change in short circuit current - isc_desoto = np.interp(0, desoto[1], desoto[0]) + # 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_desoto - isc_pvsyst, -7.955827628380874e-05) - # test expected change in open circuit current - voc_desoto = np.interp(0, desoto[0][::-1], desoto[1][::-1]) + 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_desoto - voc_pvsyst, -0.04184247739671321) - return desoto, pvsyst - - -if __name__ == '__main__': - import matplotlib.pyplot as plt - a, b = test_pvsyst_fs_495_recombination_loss() - ab0 = np.interp(a[1], b[1], b[0]) - pmpa, pmpb = max(a[2]), max(b[2]) - isca = np.interp(0, a[1], a[0]) - iscb = np.interp(0, b[1], b[0]) - voca = np.interp(0, a[0][::-1], a[1][::-1]) - vocb = np.interp(0, b[0][::-1], b[1][::-1]) - f1 = plt.figure('power') - plt.plot(a[1], a[2], a[1], ab0 * a[1], b[1], b[2], '--', - a[1], a[2] - ab0 * a[1]) - plt.plot([a[1][0], a[1][-1]], [pmpa]*2, ':', - [b[1][0], b[1][-1]], [pmpb]*2, ':') - plt.legend(['DeSoto', 'PVSyst interpolated', 'PVSyst', '$\Delta P$', - '$P_{mp,DeSoto}=%4.1f$' % pmpa, - '$P_{mp,PVSyst}=%4.1f$' % pmpb]) - plt.grid() - plt.xlabel('voltage (V)') - plt.ylabel('power (W)') - plt.title('FS-495 power, DeSoto vs. PVSyst with recombination loss') - f1.show() - f2 = plt.figure('current') - plt.plot(a[1], a[0], a[1], ab0, b[1], b[0], '--', a[1], a[0] - ab0) - plt.plot([a[1][0], a[1][-1]], [isca]*2, ':', - [b[1][0], b[1][-1]], [iscb]*2, ':') - plt.plot([voca]*2, [a[0][0], a[0][-1]], ':', - [vocb]*2, [b[0][0], b[0][-1]], ':') - plt.legend(['DeSoto', 'PVSyst interpolated', 'PVSyst', '$\Delta I$', - '$I_{sc,DeSoto}=%4.2f$' % isca, - '$I_{sc,PVSyst}=%4.2f$' % iscb, - '$V_{oc,DeSoto}=%4.1f$' % voca, - '$V_{oc,PVSyst}=%4.1f$' % vocb]) - plt.grid() - plt.xlabel('voltage (V)') - plt.ylabel('current (A)') - plt.title('FS-495 IV-curve, DeSoto vs. PVSyst with recombination loss') - f2.show() + assert np.isclose(voc_pvsyst, expected['voc'], *tol) From a62759eba309ce5dca9d5e7c9303f18202c50c07 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Thu, 2 Aug 2018 16:46:21 -0700 Subject: [PATCH 06/12] DOC: ENH: update bishop88 documentation for pvsyst thin-film recombination loss --- pvlib/singlediode_methods.py | 48 +++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/pvlib/singlediode_methods.py b/pvlib/singlediode_methods.py index c6080ec964..a0c1c6db1c 100644 --- a/pvlib/singlediode_methods.py +++ b/pvlib/singlediode_methods.py @@ -69,11 +69,15 @@ def bishop88(diode_voltage, photocurrent, saturation_current, gradients=False): """ Explicit calculation of points on the IV curve described by the single - diode equation [1]. + diode equation [1]_. - [1] "Computer simulation of the effects of electrical mismatches in - photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988) - https://doi.org/10.1016/0379-6787(88)90059-2 + .. warning:: + * Do not use ``d2mutau`` with CEC coefficients. + * Usage of ``d2mutau`` with PVSyst coefficients is required for CdTe and + a:Si modules. + * For PVSyst CdTe and a:Si modules, the cells_in_series parameter must + only account for a single parallel sub-string if the module has cells in + parallel greater than 1. Parameters ---------- @@ -90,6 +94,17 @@ def bishop88(diode_voltage, photocurrent, saturation_current, nNsVth : numeric product of thermal voltage ``Vth`` [V], diode ideality factor ``n``, and number of series cells ``Ns`` + cells_in_series : int + number of cells in series per parallel module sub-string, if unset + default is ``None`` which raises ``TypeError`` if ``d2mutau`` is set. + d2mutau : numeric + PVSyst thin-film recombination parameter that is the ratio of thickness + of the intrinsic thin-film layer squared :math:`d^2` and the diffusion + length of charge carriers :math:`\mu \tau`, in volts [V], defaults to + 0[V] + voltage_builtin : numeric + PVSyst thin-film recombination parameter that is the builtin voltage of + the intrinsic thin-film layer, in volts [V], defaults to 0.9[V] gradients : bool False returns only I, V, and P. True also returns gradients @@ -100,6 +115,31 @@ def bishop88(diode_voltage, photocurrent, saturation_current, :math:`\\frac{dI}{dV_d}`, :math:`\\frac{dV}{dV_d}`, :math:`\\frac{dI}{dV}`, :math:`\\frac{dP}{dV}`, and :math:`\\frac{d^2 P}{dV dV_d}` + + Notes + ----- + The PVSyst thin-film recombination losses parameters ``d2mutau`` and + ``voltage_builtin`` are only applied to cadmium-telluride (CdTe) and + amorphous-silicon (a:Si) PV modules, [2]_, [3]_. The builtin voltage should + account for all junctions. _EG_: tandem and triple junction cell would have + builtin voltages of 1.8[V] and 2.7[V] respectively, based on the default of + 0.9[V] for a single junction. + + References + ---------- + .. [1] "Computer simulation of the effects of electrical mismatches in + photovoltaic cell interconnection circuits" JW Bishop, Solar Cell (1988) + :doi:`10.1016/0379-6787(88)90059-2` + + .. [2] "Improved equivalent circuit and Analytical Model for Amorphous + Silicon Solar Cells and Modules." J. Mertens, et al., IEEE Transactions + on Electron Devices, Vol 45, No 2, Feb 1998. + :doi:`10.1109/16.658676` + + .. [3] "Performance assessment of a simulation model for PV modules of any + available technology", André Mermoud and Thibault Lejeune, 25th EUPVSEC, + 2010 + :doi:`10.4229/25thEUPVSEC2010-4BV.1.114` """ # check if need to calculate recombination loss current i_recomb, v_recomb = 0, np.inf From bfa9c5f667264f5f056b7337c45fd662481cd5ff Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Thu, 2 Aug 2018 16:48:37 -0700 Subject: [PATCH 07/12] DOC: ENH: update what's new --- docs/sphinx/source/whatsnew/v0.6.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.6.0.rst b/docs/sphinx/source/whatsnew/v0.6.0.rst index 009f015e62..a462e29a3e 100644 --- a/docs/sphinx/source/whatsnew/v0.6.0.rst +++ b/docs/sphinx/source/whatsnew/v0.6.0.rst @@ -48,6 +48,7 @@ Enhancements :func:`~pvlib.singlediode_methods.bishop88_i_from_v`, :func:`~pvlib.singlediode_methods.bishop88_v_from_i`, and :func:`~pvlib.singlediode_methods.bishop88_mpp`. +* Add PVSyst thin-film recombination losses for CdTe and a:Si (:issue:`163`) Bug fixes From 79fde5b4556d5d49115aa0ef744fe46ec88c9c71 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Thu, 2 Aug 2018 17:43:34 -0700 Subject: [PATCH 08/12] DOC: add utf-8 file encoding --- pvlib/singlediode_methods.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pvlib/singlediode_methods.py b/pvlib/singlediode_methods.py index a0c1c6db1c..4268cf806d 100644 --- a/pvlib/singlediode_methods.py +++ b/pvlib/singlediode_methods.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Low-level functions for solving the single diode equation. """ From 1e9434a45a682360ae877ac42c3570904d4c6a26 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Fri, 3 Aug 2018 00:31:15 -0700 Subject: [PATCH 09/12] DOC: ENH: BUG: minor doc fixes and formatting * need to indent bullets in warning * only need cells in series for PVSyst thin-film recombination loss * escape math latex twice inside docstrings without raw prefix, eg: \\tau without extra escape is a tab character, ha ha ha * in rst, italics is a single asterisk, not underscore, thats markdown --- pvlib/singlediode_methods.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pvlib/singlediode_methods.py b/pvlib/singlediode_methods.py index 4268cf806d..f2eaa1345b 100644 --- a/pvlib/singlediode_methods.py +++ b/pvlib/singlediode_methods.py @@ -75,10 +75,10 @@ def bishop88(diode_voltage, photocurrent, saturation_current, .. warning:: * Do not use ``d2mutau`` with CEC coefficients. * Usage of ``d2mutau`` with PVSyst coefficients is required for CdTe and - a:Si modules. - * For PVSyst CdTe and a:Si modules, the cells_in_series parameter must - only account for a single parallel sub-string if the module has cells in - parallel greater than 1. + a:Si modules. + * For PVSyst CdTe and a:Si modules, the ``cells_in_series`` parameter + must only account for a single parallel sub-string if the module has + cells in parallel greater than 1. Parameters ---------- @@ -96,12 +96,13 @@ def bishop88(diode_voltage, photocurrent, saturation_current, product of thermal voltage ``Vth`` [V], diode ideality factor ``n``, and number of series cells ``Ns`` cells_in_series : int - number of cells in series per parallel module sub-string, if unset - default is ``None`` which raises ``TypeError`` if ``d2mutau`` is set. + number of cells in series per parallel module sub-string, only required + for PVSyst thin-film recombination loss, if unset default is ``None`` + which raises ``TypeError`` if ``d2mutau`` is set. d2mutau : numeric PVSyst thin-film recombination parameter that is the ratio of thickness of the intrinsic thin-film layer squared :math:`d^2` and the diffusion - length of charge carriers :math:`\mu \tau`, in volts [V], defaults to + length of charge carriers :math:`\\mu \\tau`, in volts [V], defaults to 0[V] voltage_builtin : numeric PVSyst thin-film recombination parameter that is the builtin voltage of @@ -122,7 +123,7 @@ def bishop88(diode_voltage, photocurrent, saturation_current, The PVSyst thin-film recombination losses parameters ``d2mutau`` and ``voltage_builtin`` are only applied to cadmium-telluride (CdTe) and amorphous-silicon (a:Si) PV modules, [2]_, [3]_. The builtin voltage should - account for all junctions. _EG_: tandem and triple junction cell would have + account for all junctions. *EG*: tandem and triple junction cell would have builtin voltages of 1.8[V] and 2.7[V] respectively, based on the default of 0.9[V] for a single junction. From 3d0deb2de444c3c3b0abae974e1230dc47df626a Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Fri, 3 Aug 2018 11:40:43 -0700 Subject: [PATCH 10/12] ENH: improve code style, fix d2mutau is array * expand module type abbrev. in warnings * also expand EG to For example, too terse * cells in series can be int _or_ `None` * since d2mutau and vbi are arrays, then ambiguous to test for zero, so use np.where to assign conditionally instead (in two places) --- pvlib/singlediode_methods.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/pvlib/singlediode_methods.py b/pvlib/singlediode_methods.py index f2eaa1345b..f99c8e31bb 100644 --- a/pvlib/singlediode_methods.py +++ b/pvlib/singlediode_methods.py @@ -74,8 +74,8 @@ def bishop88(diode_voltage, photocurrent, saturation_current, .. warning:: * Do not use ``d2mutau`` with CEC coefficients. - * Usage of ``d2mutau`` with PVSyst coefficients is required for CdTe and - a:Si modules. + * Usage of ``d2mutau`` with PVSyst coefficients is required for cadmium- + telluride (CdTe) and amorphous-silicon (a:Si) PV modules only. * For PVSyst CdTe and a:Si modules, the ``cells_in_series`` parameter must only account for a single parallel sub-string if the module has cells in parallel greater than 1. @@ -95,7 +95,7 @@ def bishop88(diode_voltage, photocurrent, saturation_current, nNsVth : numeric product of thermal voltage ``Vth`` [V], diode ideality factor ``n``, and number of series cells ``Ns`` - cells_in_series : int + cells_in_series : None or int number of cells in series per parallel module sub-string, only required for PVSyst thin-film recombination loss, if unset default is ``None`` which raises ``TypeError`` if ``d2mutau`` is set. @@ -123,9 +123,9 @@ def bishop88(diode_voltage, photocurrent, saturation_current, The PVSyst thin-film recombination losses parameters ``d2mutau`` and ``voltage_builtin`` are only applied to cadmium-telluride (CdTe) and amorphous-silicon (a:Si) PV modules, [2]_, [3]_. The builtin voltage should - account for all junctions. *EG*: tandem and triple junction cell would have - builtin voltages of 1.8[V] and 2.7[V] respectively, based on the default of - 0.9[V] for a single junction. + account for all junctions. For example: tandem and triple junction cell + would have builtin voltages of 1.8[V] and 2.7[V] respectively, based on the + default of 0.9[V] for a single junction. References ---------- @@ -143,11 +143,12 @@ def bishop88(diode_voltage, photocurrent, saturation_current, 2010 :doi:`10.4229/25thEUPVSEC2010-4BV.1.114` """ - # 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 + # calculate recombination loss current where d2mutau > 0 + is_recomb = d2mutau > 0 # True where there is thin-film recombination loss + v_recomb = np.where(is_recomb, + voltage_builtin * cells_in_series - diode_voltage, + np.inf) + i_recomb = np.where(is_recomb, photocurrent * d2mutau / v_recomb, 0) # calculate temporary values to simplify calculations v_star = diode_voltage / nNsVth # non-dimensional diode voltage g_sh = 1.0 / resistance_shunt # conductance @@ -156,11 +157,9 @@ def bishop88(diode_voltage, photocurrent, saturation_current, 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 - if d2mutau > 0: - grad_i_recomb = i_recomb / v_recomb - grad_2i_recomb = 2 * grad_i_recomb / v_recomb + # calculate recombination loss current gradients where d2mutau > 0 + grad_i_recomb = np.where(is_recomb, i_recomb / v_recomb, 0) + grad_2i_recomb = np.where(is_recomb, 2 * grad_i_recomb / v_recomb, 0) g_diode = saturation_current * np.exp(v_star) / nNsVth # conductance grad_i = -g_diode - g_sh - grad_i_recomb # di/dvd grad_v = 1.0 - grad_i * resistance_series # dv/dvd From 8a1efcb01d9082665197905479130f54addc88f8 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Fri, 3 Aug 2018 11:57:29 -0700 Subject: [PATCH 11/12] ENH: TST: use fixture instead of module-level dictionary for test * dangerous to use module-level dictionary inside test, b/c dictionary values could be changed inadvertently, but not a fixture * add fixture as argument in test * change parametrize to call fixture instead of module-level dictionary --- pvlib/test/test_singlediode_methods.py | 49 +++++++++++++++----------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/pvlib/test/test_singlediode_methods.py b/pvlib/test/test_singlediode_methods.py index edb36becd7..8345506078 100644 --- a/pvlib/test/test_singlediode_methods.py +++ b/pvlib/test/test_singlediode_methods.py @@ -129,36 +129,45 @@ def test_brentq_fs_495(): 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 = { - '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.fixture +def pvsyst_fs_495(): + """ + 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 + + """ + return { + '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']}, + [(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']}, (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): +def test_pvsyst_recombination_loss(pvsyst_fs_495, poa, temp_cell, expected, + tol): """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'], From 21157e213682f778920e599d386a572be1c6cf14 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Fri, 3 Aug 2018 12:55:08 -0700 Subject: [PATCH 12/12] ENH: replace voltage_builtin with NsVbi and remove cells_in_series * related to #516, the use of cells_in_series here is problematic * to calculate arrays of different cells or modules with different parameters we use np.where(is_recomb, ..., ...) 3d0deb2de but what should cells_in_series be if d2mutau is zero? doesn't make physical sense for it to be zero, but None doesn't work with numbers * remove cells_in_series arg from bishop88 * remove last warning about setting cells_in_series correctly, move it to Notes, but worded differently in the context of setting NsVbi * replace voltage_builtin with NsVbi, default to np.inf * fix docstring parameter descriptions --- pvlib/singlediode_methods.py | 43 +++++++++++--------------- pvlib/test/test_singlediode_methods.py | 4 +-- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/pvlib/singlediode_methods.py b/pvlib/singlediode_methods.py index f99c8e31bb..cb5b8e6b13 100644 --- a/pvlib/singlediode_methods.py +++ b/pvlib/singlediode_methods.py @@ -23,7 +23,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. +# intrinsic voltage per cell junction for a:Si, CdTe, Mertens et al. +VOLTAGE_BUILTIN = 0.9 # [V] def estimate_voc(photocurrent, saturation_current, nNsVth): @@ -65,9 +66,8 @@ def estimate_voc(photocurrent, saturation_current, nNsVth): def bishop88(diode_voltage, photocurrent, saturation_current, - resistance_series, resistance_shunt, nNsVth, cells_in_series=None, - d2mutau=0, voltage_builtin=VOLTAGE_BUILTIN, - gradients=False): + resistance_series, resistance_shunt, nNsVth, d2mutau=0, + NsVbi=np.Inf, gradients=False): """ Explicit calculation of points on the IV curve described by the single diode equation [1]_. @@ -76,9 +76,6 @@ def bishop88(diode_voltage, photocurrent, saturation_current, * Do not use ``d2mutau`` with CEC coefficients. * Usage of ``d2mutau`` with PVSyst coefficients is required for cadmium- telluride (CdTe) and amorphous-silicon (a:Si) PV modules only. - * For PVSyst CdTe and a:Si modules, the ``cells_in_series`` parameter - must only account for a single parallel sub-string if the module has - cells in parallel greater than 1. Parameters ---------- @@ -95,18 +92,14 @@ def bishop88(diode_voltage, photocurrent, saturation_current, nNsVth : numeric product of thermal voltage ``Vth`` [V], diode ideality factor ``n``, and number of series cells ``Ns`` - cells_in_series : None or int - number of cells in series per parallel module sub-string, only required - for PVSyst thin-film recombination loss, if unset default is ``None`` - which raises ``TypeError`` if ``d2mutau`` is set. d2mutau : numeric PVSyst thin-film recombination parameter that is the ratio of thickness - of the intrinsic thin-film layer squared :math:`d^2` and the diffusion - length of charge carriers :math:`\\mu \\tau`, in volts [V], defaults to - 0[V] - voltage_builtin : numeric - PVSyst thin-film recombination parameter that is the builtin voltage of - the intrinsic thin-film layer, in volts [V], defaults to 0.9[V] + of the intrinsic layer squared :math:`d^2` and the diffusion length of + charge carriers :math:`\\mu \\tau`, in volts [V], defaults to 0[V] + NsVbi : numeric + PVSyst thin-film recombination parameter that is the product of the PV + module number of series cells ``Ns`` and the builtin voltage ``Vbi`` of + the intrinsic layer, in volts [V], defaults to ``np.inf`` gradients : bool False returns only I, V, and P. True also returns gradients @@ -121,11 +114,13 @@ def bishop88(diode_voltage, photocurrent, saturation_current, Notes ----- The PVSyst thin-film recombination losses parameters ``d2mutau`` and - ``voltage_builtin`` are only applied to cadmium-telluride (CdTe) and - amorphous-silicon (a:Si) PV modules, [2]_, [3]_. The builtin voltage should - account for all junctions. For example: tandem and triple junction cell - would have builtin voltages of 1.8[V] and 2.7[V] respectively, based on the - default of 0.9[V] for a single junction. + ``NsVbi`` are only applied to cadmium-telluride (CdTe) and amorphous- + silicon (a:Si) PV modules, [2]_, [3]_. The builtin voltage :math:`V_{bi}` + should account for all junctions. For example: tandem and triple junction + cells would have builtin voltages of 1.8[V] and 2.7[V] respectively, based + on the default of 0.9[V] for a single junction. The parameter ``NsVbi`` + should only account for the number of series cells in a single parallel + sub-string if the module has cells in parallel greater than 1. References ---------- @@ -145,9 +140,7 @@ def bishop88(diode_voltage, photocurrent, saturation_current, """ # calculate recombination loss current where d2mutau > 0 is_recomb = d2mutau > 0 # True where there is thin-film recombination loss - v_recomb = np.where(is_recomb, - voltage_builtin * cells_in_series - diode_voltage, - np.inf) + v_recomb = np.where(is_recomb, NsVbi - diode_voltage, np.inf) i_recomb = np.where(is_recomb, photocurrent * d2mutau / v_recomb, 0) # calculate temporary values to simplify calculations v_star = diode_voltage / nNsVth # non-dimensional diode voltage diff --git a/pvlib/test/test_singlediode_methods.py b/pvlib/test/test_singlediode_methods.py index 8345506078..27deaf4276 100644 --- a/pvlib/test/test_singlediode_methods.py +++ b/pvlib/test/test_singlediode_methods.py @@ -4,7 +4,7 @@ import numpy as np from pvlib import pvsystem -from pvlib.singlediode_methods import bishop88, estimate_voc +from pvlib.singlediode_methods import bishop88, estimate_voc, VOLTAGE_BUILTIN import pytest from conftest import requires_scipy @@ -189,7 +189,7 @@ def test_pvsyst_recombination_loss(pvsyst_fs_495, poa, temp_cell, expected, 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'] + NsVbi=VOLTAGE_BUILTIN*pvsyst_fs_495['cells_in_series'] ) # test max power assert np.isclose(max(pvsyst[2]), expected['pmp'], *tol)