GaAs diode: transient with traps (1D).

(source code)

Simulating transient charge transport in a GaAs p-i-n diode with an electron trap.

module Ex109_Traps

using ChargeTransport
using ExtendableGrids
using PyPlot

# function to initialize the grid for a possible extension to other p-i-n devices.
function initialize_pin_grid(refinementfactor, h_ndoping, h_intrinsic, h_pdoping)
    coord_ndoping = collect(range(0.0, stop = h_ndoping, length = 3 * refinementfactor))
    coord_intrinsic = collect(range(h_ndoping, stop = (h_ndoping + h_intrinsic), length = 3 * refinementfactor))
    coord_pdoping = collect(
        range(
            (h_ndoping + h_intrinsic),
            stop = (h_ndoping + h_intrinsic + h_pdoping),
            length = 3 * refinementfactor
        )
    )
    coord = glue(coord_ndoping, coord_intrinsic)
    coord = glue(coord, coord_pdoping)

    return coord
end

you can also use other Plotters, if you add them to the example file you can set verbose also to true to display some solver information

function main(; n = 3, Plotter = PyPlot, plotting = false, verbose = "", test = false, unknown_storage = :sparse)

    if plotting
        Plotter.close("all")
    end
    ################################################################################
    if test == false
        println("Set up grid and regions")
    end
    ################################################################################

    # region numbers
    regionAcceptor = 1                           # p doped region
    regionIntrinsic = 2                           # intrinsic region
    regionDonor = 3                           # n doped region
    regions = [regionAcceptor, regionIntrinsic, regionDonor]
    numberOfRegions = length(regions)

    # boundary region numbers
    bregionAcceptor = 1
    bregionDonor = 2
    bregionJunction1 = 3
    bregionJunction2 = 4

    # grid
    refinementfactor = 2^(n - 1)
    h_pdoping = 2.0 * μm
    h_intrinsic = 2.0 * μm
    h_ndoping = 2.0 * μm
    h_total = h_pdoping + h_intrinsic + h_ndoping
    w_device = 0.5 * μm  # width of device
    z_device = 1.0e-4 * cm  # depth of device

    coord = initialize_pin_grid(
        refinementfactor,
        h_pdoping,
        h_intrinsic,
        h_ndoping
    )

    grid = simplexgrid(coord)

    # set different regions in grid
    cellmask!(grid, [0.0 * μm], [h_pdoping], regionAcceptor)  # p-doped
    cellmask!(grid, [h_pdoping], [h_pdoping + h_intrinsic], regionIntrinsic) # intrinsic
    cellmask!(grid, [h_pdoping + h_intrinsic], [h_total], regionDonor)     # n-doped

    bfacemask!(grid, [h_pdoping], [h_pdoping], bregionJunction1, tol = 1.0e-18)
    bfacemask!(grid, [h_pdoping + h_intrinsic], [h_pdoping + h_intrinsic], bregionJunction2, tol = 1.0e-18)

    if plotting
        gridplot(grid, Plotter = Plotter, legend = :lt)
        Plotter.title("Grid")
    end

    if test == false
        println("*** done\n")
    end

    ################################################################################
    if test == false
        println("Define physical parameters and model")
    end
    ################################################################################

    iphin = 1 # index electron quasi Fermi potential
    iphip = 2 # index hole quasi Fermi potential
    iphit = 3 # index trap quasi Fermi potential
    numberOfCarriers = 3 # electrons, holes and traps

    # physical data
    Ec = 1.424 * eV
    Ev = 0.0 * eV
    Et = 0.6 * eV
    Nc = 4.35195989587969e17 / (cm^3)
    Nv = 9.139615903601645e18 / (cm^3)
    Nt = 1.0e16 / (cm^3)
    mun = 8500.0 * (cm^2) / (V * s)
    mup = 400.0 * (cm^2) / (V * s)
    mut = 0.0 * (cm^2) / (V * s)  # such that there is no flux
    εr = 12.9 * 1.0              # relative dielectric permittivity of GAs
    T = 300.0 * K

    # recombination parameters
    ni = sqrt(Nc * Nv) * exp(-(Ec - Ev) / (2 * kB * T))        # intrinsic concentration
    n0 = Nc * Boltzmann((Et - Ec) / (kB * T))                    # Boltzmann equilibrium concentration
    p0 = ni^2 / n0                                             # Boltzmann equilibrium concentration
    Auger = 1.0e-29 * cm^6 / s          # 1.0e-41
    SRH_LifeTime = 1.0e-3 * ns
    Radiative = 1.0e-10 * cm^3 / s          # 1.0e-16
    G = 1.0e25 / (cm^3 * s)

    # doping -- trap doping will not be set and thus automatically zero
    dopingFactorNd = 1.0
    dopingFactorNa = 0.46
    Nd = dopingFactorNd * Nc
    Na = dopingFactorNa * Nv

    # contact voltage
    voltageAcceptor = 1.5 * V

    if test == false
        println("*** done\n")
    end
    ################################################################################
    if test == false
        println("Define System and fill in information about model")
    end
    ################################################################################

    # Initialize Data instance and fill in data
    data = Data(grid, numberOfCarriers)

    # Possible choices: Stationary, Transient
    data.modelType = Transient

    # Possible choices: Boltzmann, FermiDiracOneHalfBednarczyk, FermiDiracOneHalfTeSCA,
    # FermiDiracMinusOne, Blakemore
    data.F .= [FermiDiracOneHalfTeSCA, FermiDiracOneHalfTeSCA, FermiDiracMinusOne]

    data.bulkRecombination = set_bulk_recombination(;
        iphin = iphin, iphip = iphip,
        bulk_recomb_Auger = true,
        bulk_recomb_radiative = true,
        bulk_recomb_SRH = true
    )

    # Here, we enable the traps and parse the respective index and the regions where the trap is defined.
    enable_trap_carrier!(; data = data, trapCarrier = iphit, regions = regions)

    # Possible choices: GenerationNone, GenerationUniform
    data.generationModel = GenerationUniform

    # Possible choices: OhmicContact, SchottkyContact (outer boundary) and InterfaceNone,
    # InterfaceRecombination (inner boundary).
    data.boundaryType[bregionAcceptor] = OhmicContact
    data.boundaryType[bregionDonor] = OhmicContact

    data.ohmicContactModel = OhmicContactRobin

    # Choose flux discretization scheme: ScharfetterGummel, ScharfetterGummelGraded,
    # ExcessChemicalPotential, ExcessChemicalPotentialGraded, DiffusionEnhanced, GeneralizedSG
    data.fluxApproximation .= ExcessChemicalPotential

    if test == false
        println("*** done\n")
    end

    ################################################################################
    if test == false
        println("Define Params and fill in physical parameters")
    end
    ################################################################################

    params = Params(grid[NumCellRegions], grid[NumBFaceRegions], numberOfCarriers)

    params.temperature = T
    params.UT = (kB * params.temperature) / q
    params.chargeNumbers[iphin] = -1
    params.chargeNumbers[iphip] = 1
    params.chargeNumbers[iphit] = -1 # trap charge number determines whether hole or electron trap is used

    for ireg in 1:numberOfRegions           # region data

        params.dielectricConstant[ireg] = εr * ε0

        # effective DOS, band-edge energy and mobilities
        params.densityOfStates[iphin, ireg] = Nc
        params.densityOfStates[iphip, ireg] = Nv
        params.densityOfStates[iphit, ireg] = Nt
        params.bandEdgeEnergy[iphin, ireg] = Ec
        params.bandEdgeEnergy[iphip, ireg] = Ev
        params.bandEdgeEnergy[iphit, ireg] = Et
        params.mobility[iphin, ireg] = mun
        params.mobility[iphip, ireg] = mup
        params.mobility[iphit, ireg] = mut

        # recombination parameters
        params.recombinationRadiative[ireg] = Radiative
        params.recombinationSRHLifetime[iphin, ireg] = SRH_LifeTime
        params.recombinationSRHLifetime[iphip, ireg] = SRH_LifeTime
        params.recombinationSRHTrapDensity[iphin, ireg] = n0
        params.recombinationSRHTrapDensity[iphip, ireg] = p0
        params.recombinationAuger[iphin, ireg] = Auger
        params.recombinationAuger[iphip, ireg] = Auger
        params.generationUniform[ireg] = G

    end

    # doping
    params.doping[iphin, regionDonor] = Nd
    params.doping[iphin, regionIntrinsic] = ni
    params.doping[iphip, regionIntrinsic] = 0.0
    params.doping[iphip, regionAcceptor] = Na

    data.params = params
    ctsys = System(grid, data, unknown_storage = unknown_storage)

    if test == false
        show_params(ctsys)
        println("*** done\n")
    end
    ################################################################################
    if test == false
        println("Define control parameters for Solver")
    end
    ################################################################################

    control = SolverControl()
    control.verbose = verbose
    control.tol_round = 1.0e-4
    control.damp_initial = 0.5
    control.damp_growth = 1.61
    control.max_round = 3

    if test == false
        println("*** done\n")
    end

    ################################################################################
    if test == false
        println("Compute solution in thermodynamic equilibrium")
    end
    ################################################################################

    # solve thermodynamic equilibrium and update initial guess
    solution = equilibrium_solve!(ctsys, control = control)
    inival = solution

    if test == false
        println("*** done\n")
    end

    if plotting
        label_solution, label_density, label_energy = set_plotting_labels(data)

        # add labels for traps
        label_energy[1, iphit] = "\$E_{\\tau}-q\\psi\$"; label_energy[2, iphit] = "\$ - q \\varphi_{\\tau}\$"
        label_density[iphit] = "\$n_{\\tau}\$";        label_solution[iphit] = "\$ \\varphi_{\\tau}\$"

        Plotter.figure()
        plot_energies(Plotter, ctsys, solution, "Equilibrium", label_energy)
        Plotter.figure()
        plot_densities(Plotter, ctsys, solution, "Equilibrium", label_density)
        Plotter.figure()
        plot_solution(Plotter, ctsys, solution, "Equilibrium", label_solution)
    end

    ################################################################################
    if test == false
        println("Loop for putting generation on")
    end
    ################################################################################

    # Scan rate and time steps
    scanrate = 1.0 * V / s
    number_tsteps = 25
    endVoltage = voltageAcceptor # bias goes until the given voltage at acceptor boundary

    # with fixed timestep sizes we can calculate the times
    # a priori
    tend = endVoltage / scanrate
    tvalues = range(0.0, stop = tend, length = number_tsteps)
    Δt = tvalues[2] - tvalues[1]

these values are needed for putting the generation slightly on

    I = collect(20:-1:0.0)
    LAMBDA = 10 .^ (-I)

    IV = zeros(0) # for IV values
    biasValues = zeros(0) # for bias values

    for istep in 1:(length(I) - 1)

        # turn slowly generation on
        data.λ2 = LAMBDA[istep + 1]

        if test == false
            println("increase generation with λ2 = $(data.λ2)")
        end

        solution = solve(ctsys, inival = inival, control = control)
        inival = solution

    end # generation loop

    if test == false
        println("*** done\n")
    end
    ################################################################################
    if test == false
        println("IV Measurement loop")
    end
    ################################################################################

    for istep in 2:number_tsteps

        t = tvalues[istep]          # Actual time
        Δu = t * scanrate            # Applied voltage
        Δt = t - tvalues[istep - 1]    # Time step size

        # Apply new voltage: set non equilibrium boundary conditions
        set_contact!(ctsys, bregionAcceptor, Δu = Δu)

        if test == false
            println("time value: t = $(t) s")
        end

        solution = solve(ctsys, inival = inival, control = control, tstep = Δt)

        # get I-V data
        current = get_current_val(ctsys, solution, inival, Δt)

        push!(IV, w_device * z_device * current)
        push!(biasValues, Δu)

        inival = solution

    end # bias loop

    if test == false
        println("*** done\n")
    end

    # plot solution and IV curve
    if plotting
        Plotter.figure()
        plot_energies(Plotter, ctsys, solution, "bias \$\\Delta u\$ = $(endVoltage), \$ t=$(tvalues[number_tsteps])\$", label_energy)
        Plotter.figure()
        plot_densities(Plotter, ctsys, solution, "bias \$\\Delta u\$ = $(endVoltage), \$ t=$(tvalues[number_tsteps])\$", label_density)
        Plotter.figure()
        plot_solution(Plotter, ctsys, solution, "bias \$\\Delta u\$ = $(endVoltage), \$ t=$(tvalues[number_tsteps])\$", label_solution)
        Plotter.figure()
        plot_IV(Plotter, biasValues, IV, "bias \$\\Delta u\$ = $(biasValues[end])", plotGridpoints = true)
    end

    testval = sum(filter(!isnan, solution)) / length(solution) # when using sparse storage, we get NaN values in solution
    return testval

    if test == false
        println("*** done\n")
    end

end #  main

function test()
    testval = 0.9699245385329192
    return main(test = true, unknown_storage = :dense) ≈ testval && main(test = true, unknown_storage = :sparse) ≈ testval
end

if test == false
    println("This message should show when the PIN module has successfully recompiled.")
end

end # module

This page was generated using Literate.jl.