from solcore import material
from solcore.structure import Layer, Junction, TunnelJunction
from solcore.solar_cell import SolarCell
from solcore.solar_cell_solver import solar_cell_solver
from solcore.light_source import LightSource
import solcore.poisson_drift_diffusion as PDD

import numpy as np
import matplotlib.pyplot as plt

T = 298
wl = np.linspace(350, 1050, 301) * 1e-9

# First, we create the materials of the QW
QWmat = material("InGaAs")(T=T, In=0.2, strained=True)
Bmat = material("GaAsP")(T=T, P=0.1, strained=True)
i_GaAs = material("GaAs")(T=T)

# The QW is 7 nm wide, with GaAs interlayers 2 nm thick at each side and GaAsP barriers
# 10 nm thick. The final device will have 30 of these QWs.
QW = PDD.QWunit(
    [
        Layer(width=10e-9, material=Bmat, role="barrier"),
        Layer(width=2e-9, material=i_GaAs, role="well"),
        Layer(width=7e-9, material=QWmat, role="well"),
        Layer(width=2e-9, material=i_GaAs, role="well"),
        Layer(width=10e-9, material=Bmat, role="barrier"),
    ],
    T=T,
    repeat=30,
    substrate=i_GaAs,
)

# We solve the quantum properties of the QW, leaving the default values of all
# parameters
QW_list = QW.GetEffectiveQW(wavelengths=wl)

# Materials for the BOTTOM junction
window_bottom = material("GaInP")(T=T, Nd=5e24, In=0.49)
n_GaAs = material("GaAs")(T=T, Nd=1e24)
p_GaAs = material("GaAs")(T=T, Na=8e22)
bsf_bottom = material("GaInP")(T=T, Na=5e24, In=0.49)

# If you want to test the code without QWs, to make ti a bit faster, comment the line
# with QW_list
GaAs_junction = Junction(
    [
        Layer(width=10e-9, material=window_bottom, role="Window"),
        Layer(width=150e-9, material=n_GaAs, role="Emitter"),
    ]
    # Comment the following line to remove the QWs
    + QW_list
    + [
        Layer(width=2000e-9, material=p_GaAs, role="Base"),
        Layer(width=200e-9, material=bsf_bottom, role="BSF"),
    ],
    sn=1e6,
    sp=1e6,
    T=T,
    kind="PDD",
)

# Materials for the TOP junction
window_top = material("AlInP")(
    T=T, Nd=5e23, Al=0.53, electron_mobility=0.01, hole_mobility=7e-4
)
n_GaInP = material("GaInP")(T=T, Nd=5e23, In=0.49)
p_GaInP = material("GaInP")(T=T, Na=8e22, In=0.49)
bsf_top = material("AlInP")(
    T=T, Na=5e23, Al=0.53, electron_mobility=0.01, hole_mobility=7e-4
)

# We create the Top junction, leaving outside the window and bsf layers. They work
# well in the dark, but cause convergence issues under illumination.
GaInP_junction = Junction(
    [
        Layer(width=120e-9, material=n_GaInP, role="Emitter"),
        Layer(width=800e-9, material=p_GaInP, role="Base"),
    ],
    sn=1e3,
    sp=1e3,
    T=T,
    kind="PDD",
)

# We create the tunnel junction
tunnel = TunnelJunction(
    [Layer(width=40e-9, material=n_GaInP, role="TJ")],
    v_peak=0.2,
    j_peak=7.5e4,
    v_valley=1,
    j_valley=4e4,
    prefactor=5,
    j01=1e-23,
    kind="parametric",
    pn=True,
)

# And the materials needed for the anti reflecting coating
MgF2 = material("MgF2")()
ZnS = material("ZnScub")()

# Finally, we put everithing together to make a solar cell
my_solar_cell = SolarCell(
    [
        Layer(width=110e-9, material=MgF2, role="ARC1"),
        Layer(width=60e-9, material=ZnS, role="ARC2"),
        Layer(width=30e-9, material=window_top, role="window"),
        GaInP_junction,
        Layer(width=100e-9, material=bsf_top, role="BSF"),
        tunnel,
        GaAs_junction,
    ],
    T=T,
    substrate=n_GaAs,
)

light_source = LightSource(
    source_type="standard",
    version="AM1.5g",
    x=wl,
    output_units="photon_flux_per_m",
    concentration=1,
)

# The definitions are all done, so we just start solving the properties,
# starting with the QE. We calculate the QE curve under illumination
solar_cell_solver(
    my_solar_cell,
    "qe",
    user_options={
        "light_source": light_source,
        "wavelength": wl,
        "optics_method": "TMM",
    },
)

# And now, the IV curves under various concentration levels.
# NOTE: Due to the presence of QWs and the fact we calculate things a 19 different
# concentrations, this might take a while (~4 hours).
# Remove the QWs as indicated above to test the code much faster.

num_con = 19
con = np.logspace(0, 3, num_con)
vint = np.linspace(-3.5, 4, 600)
V = np.linspace(-3.5, 0, 300)

allI = []
isc = []
voc = []
FF = []
pmpp = []

fig3, axIV = plt.subplots(1, 1, figsize=(6, 4))
for c in con:
    light_source.concentration = c

    solar_cell_solver(
        my_solar_cell,
        "iv",
        user_options={
            "light_source": light_source,
            "wavelength": wl,
            "optics_method": None,
            "light_iv": True,
            "mpp": True,
            "voltages": V,
            "internal_voltages": vint,
        },
    )

    isc.append(my_solar_cell.iv["Isc"])
    voc.append(my_solar_cell.iv["Voc"])
    FF.append(my_solar_cell.iv["FF"])
    pmpp.append(my_solar_cell.iv["Pmpp"])
    allI.append(my_solar_cell.iv["IV"][1])

    # And now, everything is plotting...
    axIV.plot(-V, my_solar_cell.iv["IV"][1] / isc[-1], label=int(c))

axIV.legend(loc="lower left", frameon=False)
axIV.set_ylim(0, 1.1)
axIV.set_xlim(0, 3.5)
axIV.set_xlabel("Voltage (V)")
axIV.set_ylabel("Normalized current (-)")

plt.tight_layout()

fig2, axes = plt.subplots(2, 2, figsize=(11.25, 8))

axes[0, 0].semilogx(con, np.array(pmpp) / con / 10, "r-o")
axes[0, 0].set_xlabel("Concentration (suns)")
axes[0, 0].set_ylabel("Efficiency (%)")

axes[0, 1].loglog(con, abs(np.array(isc)), "b-o")
axes[0, 1].set_xlabel("Concentration (suns)")
axes[0, 1].set_ylabel("I$_{SC}$ (Am$^{-2}$)")

axes[1, 0].semilogx(con, abs(np.array(voc)), "g-o")
axes[1, 0].set_xlabel("Concentration (suns)")
axes[1, 0].set_ylabel("V$_{OC}$ (V)")

axes[1, 1].semilogx(con, abs(np.array(FF)) * 100, "k-o")
axes[1, 1].set_xlabel("Concentration (suns)")
axes[1, 1].set_ylabel("Fill Factor (%)")

plt.tight_layout()

# We can plot the electron and hole densities in equilibrium and at short circuit
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11.25, 4))
for j in my_solar_cell.junction_indices:
    zz = (
        my_solar_cell[j].short_circuit_data.Bandstructure["x"] + my_solar_cell[j].offset
    )
    n = my_solar_cell[j].short_circuit_data.Bandstructure["n"]
    p = my_solar_cell[j].short_circuit_data.Bandstructure["p"]
    ax1.semilogy(zz * 1e9, n, "b")
    ax1.semilogy(zz * 1e9, p, "r")

    zz = my_solar_cell[j].equilibrium_data.Bandstructure["x"] + my_solar_cell[j].offset
    n = my_solar_cell[j].equilibrium_data.Bandstructure["n"]
    p = my_solar_cell[j].equilibrium_data.Bandstructure["p"]
    ax1.semilogy(zz * 1e9, n, "b--")
    ax1.semilogy(zz * 1e9, p, "r--")

ax1.set_xlabel("Position (nm)")
ax1.set_ylabel("Carrier density (m$^{-3}$)")
plt.tight_layout()

# And we plot the QE
labels = ["EQE GaInP", "EQE GaAs"]
colours = ["b", "r"]
for i, j in enumerate(my_solar_cell.junction_indices):
    ax2.plot(wl * 1e9, my_solar_cell[j].eqe(wl), colours[i], label=labels[i])

ax2.plot(wl * 1e9, my_solar_cell.absorbed, "k", label="Total Absorbed")

ax2.legend(loc="upper right", frameon=False)
ax2.set_xlabel("Wavelength (nm)")
ax2.set_ylabel("EQE")
ax2.set_ylim(0, 1.1)
ax2.set_xlim(350, 1150)

plt.tight_layout()

plt.show()
