Exemplo n.º 1
0
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
Exemplo n.º 2
0
def test_didv_dpdv_frsh():
    """
    Test derivative of IV curve.
    """
    isat1, isat2, rs, rsh, vt, vc = sympy.symbols([
        'isat1', 'isat2', 'rs', 'rsh', 'vt', 'vc'
    ])
    ic = sympy.Function('ic')('vc')
    isc0, alpha_isc, tc, t0, ee = sympy.symbols([
        'isc', 'alpha_isc', 'tc', 't0', 'ee'
    ])
    # short circuit
    isc = isc0 * (1 + alpha_isc * (tc - t0))
    vd_sc = isc * rs
    id1_sc = isat1 * (sympy.exp(vd_sc / vt) - 1.0)
    id2_sc = isat2 * (sympy.exp(vd_sc / 2.0 / vt) - 1.0)
    ish_sc = vd_sc / rsh
    aph = 1.0 + (id1_sc + id2_sc + ish_sc) / isc
    # any current
    iph = aph * ee * isc
    vd = vc + ic * rs
    id1 = isat1 * (sympy.exp(vd / vt) - 1.0)
    id2 = isat2 * (sympy.exp(vd / 2.0 / vt) - 1.0)
    ish = vd / rsh
    # derivatives
    diph_dv = sympy.diff(iph, vc)
    did1_dv = sympy.diff(id1, vc)
    did2_dv = sympy.diff(id2, vc)
    dish_dv = sympy.diff(ish, vc)
    di_dv = sympy.Derivative(ic, vc)
    # 0 = ic - (iph - id1 - id2 - ish)
    # 0 = dI/dV - d(Iph - Id1 - Id2 - Ish)/dV
    f = di_dv - diph_dv + did1_dv + did2_dv + dish_dv
    solution_set = sympy.solve(f, di_dv)
    didv = solution_set[0]
    # test fdidv
    test_data = {'isat1': ISAT1_2, 'isat2': ISAT2_2, 'rs': RS_2,
                 'rsh': RSH_2, 'ic': IC, 'vc': VC, 'vt': VT}
    fdidv_test, jdidv_test = fdidv(**test_data)
    expected_data = {
        'isat1': ISAT1_2, 'isat2': ISAT2_2, 'rs': RS_2, 'rsh': RSH_2,
        'ic(vc)': IC, 'vc': VC, 'vd': VD_2, 'vt': VT
    }
    didv_simple = didv.subs('vc + ic(vc) * rs', 'vd')
    fdidv_expected = np.float(didv_simple.evalf(subs=expected_data))
    LOGGER.debug('fdidv test: %g, expected: %g', fdidv_test, fdidv_expected)
    assert np.isclose(fdidv_test, fdidv_expected)
    # jacobian
    d_didv_isat1 = didv.diff(isat1).subs('vc + ic(vc) * rs', 'vd')
    d_didv_isat2 = didv.diff(isat2).subs('vc + ic(vc) * rs', 'vd')
    d_didv_rs = didv.diff(rs).subs('vc + ic(vc) * rs', 'vd')
    d_didv_rsh = didv.diff(rsh).subs('vc + ic(vc) * rs', 'vd')
    d_didv_ic = didv.diff(ic).subs('vc + ic(vc) * rs', 'vd')
    d_didv_vc = didv.diff(vc).subs('vc + ic(vc) * rs', 'vd')
    # update expected test data with calculated derivative
    expected_data['Derivative(ic(vc), vc)'] = fdidv_expected
    jdidv_expected = np.array([
        d_didv_isat1.evalf(subs=expected_data),
        d_didv_isat2.evalf(subs=expected_data),
        d_didv_rs.evalf(subs=expected_data),
        d_didv_rsh.evalf(subs=expected_data),
        d_didv_ic.evalf(subs=expected_data),
        d_didv_vc.evalf(subs=expected_data)
    ], dtype=np.float)
    LOGGER.debug(
        '\njdidv test:\n%r\nexpected:\n%r\n', jdidv_test,
        jdidv_expected.reshape(-1, 1))
    assert np.allclose(jdidv_test.flatten(), jdidv_expected)
    # power
    dpdv = didv * vc + ic
    # test fdpdv
    fdpdv_test, jdpdv_test = fdpdv(**test_data)
    dpdv_simple = dpdv.subs('vc + ic(vc) * rs', 'vd')
    fdpdv_expected = np.float(dpdv_simple.evalf(subs=expected_data))
    LOGGER.debug('fdpdv test: %g, expected: %g', fdpdv_test, fdpdv_expected)
    assert np.isclose(fdpdv_test, fdpdv_expected)
    # jacobian
    d_dpdv_isat1 = dpdv.diff(isat1).subs('vc + ic(vc) * rs', 'vd')
    d_dpdv_isat2 = dpdv.diff(isat2).subs('vc + ic(vc) * rs', 'vd')
    d_dpdv_rs = dpdv.diff(rs).subs('vc + ic(vc) * rs', 'vd')
    d_dpdv_rsh = dpdv.diff(rsh).subs('vc + ic(vc) * rs', 'vd')
    d_dpdv_ic = dpdv.diff(ic).subs('vc + ic(vc) * rs', 'vd')
    d_dpdv_vc = dpdv.diff(vc).subs('vc + ic(vc) * rs', 'vd')
    jdpdv_expected = np.array([
        d_dpdv_isat1.evalf(subs=expected_data),
        d_dpdv_isat2.evalf(subs=expected_data),
        d_dpdv_rs.evalf(subs=expected_data),
        d_dpdv_rsh.evalf(subs=expected_data),
        d_dpdv_ic.evalf(subs=expected_data),
        d_dpdv_vc.evalf(subs=expected_data)
    ], dtype=np.float)
    LOGGER.debug(
        '\njdidv test:\n%r\nexpected:\n%r\n', jdpdv_test,
        jdpdv_expected.reshape(-1, 1))
    assert np.allclose(jdpdv_test.flatten(), jdpdv_expected)
    # shunt resistance
    frsh = vd * (1.0 / rsh + didv)
    # update test data
    del test_data['ic'], test_data['vc']  # remove Ic, Vc
    test_data['isc'] = ISC0  # add Isc
    frsh_test, jfrsh_test = fjrsh(**test_data)
    frsh_simple = frsh.subs('vc + ic(vc) * rs', 'vd')
    # update expected test data with calculated derivative
    expected_data['ic(vc)'] = ISC0
    expected_data['vc'] = 0
    expected_data['vd'] = ISC0 * RS_2
    didv_isc = np.float(didv_simple.evalf(subs=expected_data))
    expected_data['Derivative(ic(vc), vc)'] = didv_isc
    frsh_expected = np.float(frsh_simple.evalf(subs=expected_data))
    LOGGER.debug('frsh test: %r, expected: %r', frsh_test, frsh_expected)
    assert np.isclose(frsh_test, frsh_expected)
    # jacobian
    dfrsh_isat1 = frsh.diff(isat1).subs('vc + ic(vc) * rs', 'vd')
    dfrsh_isat2 = frsh.diff(isat2).subs('vc + ic(vc) * rs', 'vd')
    dfrsh_rs = frsh.diff(rs).subs('vc + ic(vc) * rs', 'vd')
    dfrsh_rsh = frsh.diff(rsh).subs('vc + ic(vc) * rs', 'vd')
    dfrsh_ic = frsh.diff(ic).subs('vc + ic(vc) * rs', 'vd')
    dfrsh_vc = frsh.diff(vc).subs('vc + ic(vc) * rs', 'vd')
    jfrsh_expected = np.array([
        dfrsh_isat1.evalf(subs=expected_data),
        dfrsh_isat2.evalf(subs=expected_data),
        dfrsh_rs.evalf(subs=expected_data),
        dfrsh_rsh.evalf(subs=expected_data),
        dfrsh_ic.evalf(subs=expected_data),
        dfrsh_vc.evalf(subs=expected_data),
    ], dtype=np.float)
    LOGGER.debug(
        '\njdidv test:\n%r\nexpected:\n%r\n', jfrsh_test,
        jfrsh_expected.reshape(-1, 1))
    assert np.allclose(jfrsh_test.flatten(), jfrsh_expected)
    return dfrsh_isat1, dfrsh_isat2, dfrsh_rs, dfrsh_rsh, dfrsh_ic, dfrsh_vc