#!/usr/bin env python3
from formulary import *
from scipy.constants import gas_constant, Avogadro, elementary_charge

Faraday = Avogadro * elementary_charge

# BUTLER VOLMER / TAFEL

@np.vectorize
def fbutler_volmer_anode(ac, z, eta, T):
    return np.exp((1-ac)*z*Faraday*eta/(gas_constant*T))

@np.vectorize
def fbutler_volmer_cathode(ac, z, eta, T):
    return -np.exp(-ac*z*Faraday*eta/(gas_constant*T))

def fbutler_volmer(ac, z, eta, T):
    return fbutler_volmer_anode(ac, z, eta, T) + fbutler_volmer_cathode(ac, z, eta, T)

def butler_volmer():
    fig, ax = plt.subplots(figsize=size_half_third)
    ax.set_xlabel("$\\eta$ [V]")
    ax.set_ylabel("$j/j_0$")
    etas = np.linspace(-0.1, 0.1, 400)
    T = 300
    z = 1.0
    # other a
    ac2, ac3 = 0.2, 0.8
    i2 = fbutler_volmer(0.2, z, etas, T)
    i3 = fbutler_volmer(0.8, z, etas, T)
    ax.plot(etas, i2, color="blue", linestyle="dashed", label=f"$\\alpha_\\text{{C}}={ac2}$")
    ax.plot(etas, i3, color="green", linestyle="dashed", label=f"$\\alpha_\\text{{C}}={ac3}$")
    # 0.5
    ac = 0.5
    irel_anode = fbutler_volmer_anode(ac, z, etas, T)
    irel_cathode = fbutler_volmer_cathode(ac, z, etas, T)
    ax.plot(etas, irel_anode,  color="gray")
    ax.plot(etas, irel_cathode, color="gray")
    ax.plot(etas, irel_cathode + irel_anode, color="black", label=f"$\\alpha_\\text{{C}}=0.5$")
    ax.grid()
    ax.legend()
    ylim = 6
    ax.set_ylim(-ylim, ylim)
    return fig

@np.vectorize
def ftafel_anode(ac, z, eta, T):
    return 10**((1-ac)*z*Faraday*eta/(gas_constant*T*np.log(10)))

@np.vectorize
def ftafel_cathode(ac, z, eta, T):
    return -10**(-ac*z*Faraday*eta/(gas_constant*T*np.log(10)))

def tafel():
    i0 = 1
    ac = 0.2
    z = 1
    T = 300
    eta_max = 0.2
    etas = np.linspace(-eta_max, eta_max, 400)
    i = np.abs(fbutler_volmer(ac, z, etas ,T))
    iright = i0 * np.abs(ftafel_cathode(ac, z, etas, T))
    ileft = i0 * ftafel_anode(ac, z, etas, T)

    fig, ax = plt.subplots(figsize=size_half_third)
    ax.set_xlabel("$\\eta$ [V]")
    ax.set_ylabel("$\\log_{10}\\left(\\frac{|j|}{j_0}\\right)$")
    # ax.set_ylabel("$\\log_{10}\\left(|j|/j_0\\right)$")
    ax.set_yscale("log")
    # ax.plot(etas, linear, label="Tafel slope")
    ax.plot(etas[etas >= 0], ileft[etas >= 0],  linestyle="dashed", color="gray", label="Tafel Approximation")
    ax.plot(etas[etas <= 0], iright[etas <= 0], linestyle="dashed", color="gray")
    ax.plot(etas, i, label=f"Butler-Volmer $\\alpha_\\text{{C}}={ac:.1f}$")
    ax.legend()
    ax.grid()
    return fig


# NYQUIST
@np.vectorize
def fZ_ohm(R, omega):
    return R

@np.vectorize
def fZ_cap(C, omega):
    return 1/(1j*omega*C)

@np.vectorize
def fZ_ind(L, omega):
    return (1j*omega*L)


def nyquist():
    fig, ax = plt.subplots(figsize=(full/2, full/3))
    split_z = lambda Z: (Z.real, -Z.imag)
    ax.grid()
    ax.set_xlabel("$\\text{Re}(Z)$ [\\si{\\ohm}]")
    ax.set_ylabel("$-\\text{Im}(Z)$ [\\si{\\ohm}]")
    # ax.scatter(*split_z(Z_series), label="series")


    R1 = 20
    R2 = 5
    RS = 7.5
    C1 = 1e-4
    C2 = 1e-6
    ws1 = np.power(10, np.linspace(1, 8, 1000))


    Z_ohm1 = fZ_ohm(R1, ws1)
    Z_ohm2 = fZ_ohm(R2, ws1)
    Z_ohmS = fZ_ohm(RS, ws1)
    Z_cap1 = fZ_cap(C1, ws1)
    Z_cap2 = fZ_cap(C2, ws1)
    Z_parallel1 = 1/(1/Z_ohm1 + 1/Z_cap1)
    Z_parallel2 = 1/(1/Z_ohm2 + 1/Z_cap2)
    Z_cell = Z_parallel1 + Z_parallel2 + Z_ohmS
    ax.scatter(*split_z(Z_parallel1), label="Parallel $C_1,R_1$")
    ax.scatter(*split_z(Z_parallel2), label="Parallel $C_2,R_2$")
    ax.scatter(*split_z(Z_cell), label="P1 + $R_3$ + P2")
    ax.scatter(*split_z(Z_cap1), label=f"$C_1=\\SI{{{C1:.0e}}}{{\\farad}}$")
    ax.scatter(*split_z(Z_ohm1), label=f"$R_1 = \\SI{{{R1}}}{{\\ohm}}$")

    # wmax1 = 1/(R1 * C1)
    # ZatWmax1 = Z_parallel1[np.argmin(ws1 - wmax1)]
    # print(ws1[0], ws1[-1])
    # print(wmax1, ZatWmax1)
    # ax.scatter(*split_z(ZatWmax1), color="red")
    # ax.scatter(*split_z(Z_cell1), label="cell")
    # ax.scatter(*split_z(Z_ohm2), label="ohmic")
    # ax.scatter(*split_z(Z_cell2), label="cell")
    ax.axis('equal')
    ax.set_ylim(0,R1*1.1)
    ax.legend()
    return fig

def fZ_tlm(Rel, Rion, Rct, Cct, ws, N):
    Zion = fZ_ohm(Rion, ws)
    Zel = fZ_ohm(Rel, ws)
    Zct = 1/(1/fZ_ohm(Rct, ws) + 1/fZ_cap(Cct, ws))

    Z = Zct
    for _ in range(N):
        Z = Zion + 1/(1/Zct + 1/Z)
    Z += Zel
    return Z

def nyquist_tlm():
    fig, ax = plt.subplots(figsize=(full/2, full/4))
    split_z = lambda Z: (Z.real, -Z.imag)
    ax.grid()
    ax.set_xlabel("$\\text{Re}(Z)$ [\\si{\\ohm}]")
    ax.set_ylabel("$-\\text{Im}(Z)$ [\\si{\\ohm}]")
    Rct1 = 300
    Rct2 = 100
    Rion = 10
    ws = np.power(10, np.linspace(1e-6, 5, 1000))
    Z1 = fZ_tlm(0, Rion, Rct1, 1e-4, ws, 100)
    Z2 = fZ_tlm(0, Rion, Rct2, 1e-4, ws, 100)
    ax.scatter(*split_z(Z1), label=f"$R_\\text{{ct}} = \\SI{{{Rct1}}}{{\\ohm}}$", marker=".")
    ax.scatter(*split_z(Z2), label=f"$R_\\text{{ct}} = \\SI{{{Rct2}}}{{\\ohm}}$", marker=".")
    ax.axis('equal')
    # ax.set_ylim(0,R1*1.1)
    ax.legend()
    return fig

def fkohlrausch(L0, K, c):
    return L0 - K*np.sqrt(c)

def kohlrausch():
    fig, ax = plt.subplots(figsize=(full/4, full/4))
    ax.grid()
    ax.set_xlabel("$c_\\text{salt}$")
    ax.set_ylabel("$\\Lambda_\\text{M}$")
    L0 = 10
    K1 = 1
    K2 = 2
    cs = np.linspace(0, 10)
    L1 = fkohlrausch(L0, K1, cs)
    L2 = fkohlrausch(L0, K2, cs)
    ax.plot(cs, L1, label=f"$K={K1}$")
    ax.plot(cs, L2, label=f"$K={K2}$")
    ax.legend()
    return fig

if __name__ == '__main__':
    export(butler_volmer(), "ch_butler_volmer")
    export(tafel(), "ch_tafel")
    export(nyquist(), "ch_nyquist")
    export(nyquist_tlm(), "ch_nyquist_tlm")
    export(kohlrausch(), "ch_kohlrausch")


