def fdidv(isat1, isat2, rs, rsh, ic, vc, vt): """ Derivative of IV curve and its derivatives w.r.t. Isat1, Isat2, Rs, Rsh, Ic, Vc and Vt. :param isat1: diode 1 saturation current [A] :param isat2: diode 2 saturation current [A] :param rs: series resistance [ohms] :param rsh: shunt resistance [ohms] :param ic: cell current [A] :param vc: cell voltage [V] :param vt: thermal voltage (kB * Tc / qe = 26[mV] at Tc=298K) [V] :return: derivative of IV curve and its derivatives """ vd, _ = diode.fvd(vc, ic, rs) # vd = vc + ic * rs vstar = vd / vt rstar = rsh / rs exp_vstar, exp_vstar_2 = np.exp(vstar), np.exp(0.5 * vstar) v_sat1_sh, v_sat2_sh = isat1 * rsh, isat2 * rsh v_sat1_sh_exp_vstar = v_sat1_sh * exp_vstar v_sat2_sh_exp_vstar_2 = 0.5 * v_sat2_sh * exp_vstar_2 vsum = v_sat1_sh_exp_vstar + v_sat2_sh_exp_vstar_2 + vt vsum_rstar = vsum + vt * rstar combiterm1 = v_sat1_sh_exp_vstar + 0.5*v_sat2_sh_exp_vstar_2 combiterm2 = isat1*exp_vstar + 0.5*isat2*exp_vstar_2 combiterm3 = vsum / vsum_rstar - 1.0 combiterm4 = vsum_rstar * rs combiterm5 = rstar * combiterm3 / vsum_rstar combiterm6 = combiterm1 * combiterm3 / vt combiterm7 = 1.0 / combiterm4 # dI/dV = derivative of IV curve didv = -vsum / combiterm4 # jacobian didv_isat1 = exp_vstar * combiterm5 didv_isat2 = 0.5 * exp_vstar_2 * combiterm5 didv__r_s = combiterm7 * (combiterm6 * ic + vsum**2.0 / combiterm4) didv_rsh = combiterm7 * (combiterm2 * combiterm3 + vt * vsum / combiterm4) didv_ic = combiterm6 / vsum_rstar didv_vc = (didv + 1.0 / rs) * didv_ic jac = np.array([ didv_isat1, didv_isat2, didv__r_s, didv_rsh, didv_ic, didv_vc ]) return didv, jac
def test_fvd(): """ Test diode voltage. """ # make sympy symbols vd, vc, ic, rs = sympy.symbols(['vd', 'vc', 'ic', 'rs']) # diode voltage vd = vc + ic * rs d_vc = sympy.diff(vd, vc) d_ic = sympy.diff(vd, ic) d_rs = sympy.diff(vd, rs) # evaluate test_data = {'vc': VC, 'ic': IC, 'rs': RS_1} fvd_test, jvd_test = diode.fvd(**test_data) fvd_expected = np.float(vd.evalf(subs=test_data)) jvd_expected = np.array([ d_vc.evalf(subs=test_data), d_ic.evalf(subs=test_data), d_rs.evalf(subs=test_data) ], dtype=np.float) LOGGER.debug('test: %g = expected: %g', fvd_test, fvd_expected) assert np.isclose(fvd_test, fvd_expected) assert np.allclose(jvd_test, jvd_expected.reshape(-1, 1))
def residual_two_diode(x, isc, voc, imp, vmp, tc): """ Objective function to solve 2-diode model. :param x: parameters isat1, isat2, rs and rsh :param isc: short circuit current [A] at tc [C] :param voc: open circuit voltage [V] at tc [C] :param imp: max power current [A] at tc [C] :param vmp: max power voltage [V] at tc [C] :param tc: cell temperature [C] :return: norm of the residuals its sensitivity """ # Constants q = diode.QE # [C/electron] elementary electric charge # (n.b. 1 Coulomb = 1 A * s) kb = diode.KB # [J/K/molecule] Boltzmann's constant tck = tc + 273.15 # [K] reference temperature # Governing Equation vt = kb * tck / q # [V] thermal voltage # Rescale Variables isat1_t0 = np.exp(x[0]) isat2 = np.exp(x[1]) rs = x[2]**2.0 rsh = x[3]**2.0 # first diode saturation current isat1 = diode.isat_t(tc, isat1_t0) # Short Circuit vd_isc, _ = diode.fvd(vc=0.0, ic=isc, rs=rs) id1_isc, _ = diode.fid(isat=isat1, vd=vd_isc, m=1.0, vt=vt) id2_isc, _ = diode.fid(isat=isat2, vd=vd_isc, m=2.0, vt=vt) ish_isc, _ = diode.fish(vd=vd_isc, rsh=rsh) # Photo-generated Current iph = isc + id1_isc + id2_isc + ish_isc # [A] # Open Circuit vd_voc, jvd_voc = diode.fvd(vc=voc, ic=0.0, rs=rs) id1_voc, jid1_voc = diode.fid(isat=isat1, vd=vd_voc, m=1.0, vt=vt) id2_voc, jid2_voc = diode.fid(isat=isat2, vd=vd_voc, m=2.0, vt=vt) ish_voc, jish_voc = diode.fish(vd=vd_voc, rsh=rsh) # Max Power Point vd_mpp, jvd_mpp = diode.fvd(vc=vmp, ic=imp, rs=rs) id1_mpp, jid1_mpp = diode.fid(isat=isat1, vd=vd_mpp, m=1.0, vt=vt) id2_mpp, jid2_mpp = diode.fid(isat=isat2, vd=vd_mpp, m=2.0, vt=vt) ish_mpp, jish_mpp = diode.fish(vd=vd_mpp, rsh=rsh) # Slope at Max Power Point dpdv, jdpdv = two_diode.fdpdv(isat1=isat1, isat2=isat2, rs=rs, rsh=rsh, ic=imp, vc=vmp, vt=vt) # Shunt Resistance frsh, jrsh = two_diode.fjrsh(isat1=isat1, isat2=isat2, rs=rs, rsh=rsh, vt=vt, isc=isc) # Residual # should be (M, ) array with M residual equations (constraints) f2 = np.stack( [ (iph - id1_voc - id2_voc - ish_voc).T, # Open Circuit (iph - id1_mpp - id2_mpp - ish_mpp - imp).T, # Max Power Point dpdv.T, # Slope at Max Power Point frsh.T # Shunt Resistance ], axis=0).flatten() # Jacobian # should be (M, N) array with M residuals and N variables # [[df1/dx1, df1/dx2, ...], [df2/dx1, df2/dx2, ...]] jvoc = np.stack( ( -jid1_voc[0], # d/disat1 -jid2_voc[0], # d/disat2 -jvd_voc[2] * (jid1_voc[1] + jid2_voc[1] + jish_voc[0]), # d/drs -jish_voc[1] # d/drsh ), axis=0).T.reshape(-1, 4) jmpp = np.stack( ( -jid1_mpp[0], # d/disat1 -jid2_mpp[0], # d/disat2 -jvd_mpp[2] * (jid1_mpp[1] + jid2_mpp[1] + jish_mpp[0]), # d/drs -jish_mpp[1] # d.drsh ), axis=0).T.reshape(-1, 4) # Scaling Factors scale_fx = np.array([np.exp(x[0]), np.exp(x[1]), 2 * x[2], 2 * x[3]]) # scales each column by the corresponding element j2 = np.concatenate( (jvoc, jmpp, jdpdv[:4].T.reshape(-1, 4), jrsh[:4].T.reshape(-1, 4)), axis=0) * scale_fx return f2, j2
def fdpdv(isat1, isat2, rs, rsh, ic, vc, vt): """ Derivative of PV curve and its derivatives w.r.t. Isat1, Isat2, Rs, Rsh, Ic, Vc and Vt. :param isat1: diode 1 saturation current [A] :param isat2: diode 2 saturation current [A] :param rs: series resistance [ohms] :param rsh: shunt resistance [ohms] :param ic: cell current [A] :param vc: cell voltage [V] :param vt: thermal voltage (kB * Tc / qe = 26[mV] at Tc=298K) [V] :return: derivative of PV curve and its derivatives """ didv, _ = fdidv(isat1, isat2, rs, rsh, ic, vc, vt) vd, _ = diode.fvd(vc, ic, rs) # vd = vc + ic * rs dpdv = didv * vc + ic dpdv_isat1 = 2.0*rs*rsh*vc*( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )*np.exp(vd/vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 - 2.0*rsh*vc*np.exp(vd/vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) dpdv_isat2 = rs*rsh*vc*( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )*np.exp(0.5*vd/vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 - rsh*vc*np.exp(0.5*vd/vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) dpdv_rs = -vc*( 2.0*isat1*rsh*ic*np.exp(vd/vt)/vt + 0.5*isat2*rsh*ic*np.exp(0.5*vd/vt)/vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) - vc*( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )*( -2.0*isat1*rs*rsh*ic*np.exp(vd/vt)/vt - 2.0*isat1*rsh*np.exp(vd/vt) - 0.5*isat2*rs*rsh*ic*np.exp(0.5*vd/vt)/vt - isat2*rsh*np.exp(0.5*vd/vt) - 2.0*vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 dpdv_rsh = -vc*( 2.0*isat1*np.exp(vd/vt) + isat2*np.exp(0.5*vd/vt) )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) - vc*( -2.0*isat1*rs*np.exp(vd/vt) - isat2*rs*np.exp(0.5*vd/vt) - 2.0*vt )*( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 dpdv_ic = -vc*( 2.0*isat1*rs*rsh*np.exp(vd/vt)/vt + 0.5*isat2*rs*rsh*np.exp(0.5*vd/vt)/vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) - vc*( -2.0*isat1*rs**2*rsh*np.exp(vd/vt)/vt - 0.5*isat2*rs**2*rsh*np.exp(0.5*vd/vt)/vt )*( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 + 1.0 dpdv_vc = -vc*( 2.0*isat1*rsh*(rs*didv + 1)*np.exp(vd/vt)/vt + 0.5*isat2*rsh*(rs*didv + 1)*np.exp(0.5*vd/vt)/vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) - vc*( -2.0*isat1*rs*rsh*(rs*didv + 1)*np.exp(vd/vt)/vt - 0.5*isat2*rs*rsh*(rs*didv + 1)*np.exp(0.5*vd/vt)/vt )*( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 - ( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) + didv jac = np.array([ dpdv_isat1, dpdv_isat2, dpdv_rs, dpdv_rsh, dpdv_ic, dpdv_vc ]) return dpdv, jac
def fjrsh(isat1, isat2, rs, rsh, vt, isc): """ Shunt resistance residual and its derivatives w.r.t. Isat1, Isat2, Rs and Rsh. :param isat1: diode 1 saturation current [A] :param isat2: diode 2 saturation current [A] :param rs: series resistance [ohms] :param rsh: shunt resistance [ohms] :param vt: thermal voltage (kB * Tc / qe = 26[mV] at Tc=298K) [V] :param isc: short circuit current [A] :return: Rsh residual and its derivatives Shunt resistance is assumed to be equal to the inverse of the slope of the IV curve at short circuit. .. math:: Rsh = \\frac{ -1 }{ \\left. \\frac{dI}{dV} \\right|_{V=0} } This assumption is valid when [put condition here]. """ didv, _ = fdidv(isat1, isat2, rs, rsh, ic=isc, vc=0, vt=vt) vd, _ = diode.fvd(0.0, isc, rs) # vd = vc + ic * rs = 0.0 + isc * rs # frsh = rsh + 1/didv frsh = vd * (1.0/rsh + didv) dfrsh_isat1 = vd*( 2.0*rs*rsh*( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )*np.exp(vd/vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 - 2.0*rsh*np.exp(vd/vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) ) dfrsh_isat2 = vd*( rs*rsh*( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )*np.exp(0.5*vd/vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 - rsh*np.exp(0.5*vd/vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) ) dfrsh_rs = ( vd*( -( 2.0*isat1*rsh*isc*np.exp(vd/vt)/vt + 0.5*isat2*rsh*isc*np.exp(0.5*vd/vt)/vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) - ( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )*( -2.0*isat1*rs*rsh*isc*np.exp(vd/vt)/vt - 2.0*isat1*rsh*np.exp(vd/vt) - 0.5*isat2*rs*rsh*isc*np.exp(0.5*vd/vt)/vt - isat2*rsh*np.exp(0.5*vd/vt) - 2.0*vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 ) + ( -(2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) + 1.0/rsh )*isc ) dfrsh_rsh = ( vd*( -(2.0*isat1*np.exp(vd/vt) + isat2*np.exp(0.5*vd/vt))/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) - ( -2.0*isat1*rs*np.exp(vd/vt) - isat2*rs*np.exp(0.5*vd/vt) - 2.0*vt )*(2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 - 1.0/rsh**2 ) ) dfrsh_ic = ( rs*( -(2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) + 1.0/rsh ) + vd*( -( 2.0*isat1*rs*rsh*np.exp(vd/vt)/vt + 0.5*isat2*rs*rsh*np.exp(0.5*vd/vt)/vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) - ( -2.0*isat1*rs**2*rsh*np.exp(vd/vt)/vt - 0.5*isat2*rs**2*rsh*np.exp(0.5*vd/vt)/vt )*( 2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2 ) ) dfrsh_vc = ( vd*(-( 2.0*isat1*rsh*(rs*didv + 1)*np.exp(vd/vt)/vt + 0.5*isat2*rsh*(rs*didv + 1)*np.exp(0.5*vd/vt)/vt )/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) - ( -2.0*isat1*rs*rsh*(rs*didv + 1)*np.exp(vd/vt)/vt - 0.5*isat2*rs*rsh*(rs*didv + 1)*np.exp(0.5*vd/vt)/vt )*(2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt )**2) + (rs*didv + 1)*( -(2.0*isat1*rsh*np.exp(vd/vt) + isat2*rsh*np.exp(0.5*vd/vt) + 2.0*vt)/( 2.0*isat1*rs*rsh*np.exp(vd/vt) + isat2*rs*rsh*np.exp(0.5*vd/vt) + 2.0*rs*vt + 2.0*rsh*vt ) + 1.0/rsh ) ) jac = np.array([ dfrsh_isat1, dfrsh_isat2, dfrsh_rs, dfrsh_rsh, dfrsh_ic, dfrsh_vc ]) return frsh, jac