Peter -

The semiconductor drift-diffusion equations are notoriously challenging to 
solve. As you may well be aware, many people use alternative formulations such 
as pseudo-Fermi-levels and Slotboom variables to mitigate some of the 
difficulties that arise from the enormous range of concentrations. The 
Scharfetter-Gummel discretization is also widely applied to improve the 
convergence of the combined drift and diffusion terms.

The fact that these flux peaks scale as dx**2 is consistent with this being a 
discretization error. Newton iterations would probably help drive down the 
error.

I'm not sure what was intended by terms like 
`(-phi.arithmeticFaceValue).divergence`. Fluxes (and currents) are naturally 
described at the face centers between cells. As such, I'd define these like:

>>> J_n = -mu_n * n.harmonicFaceValue * phi.faceGrad + D_n * n.faceGrad

There is, unfortunately, no easy way to plot the magnitude of this flux. In the 
attached, I manually use Matplotlib to do it.

- Jon


> On Nov 30, 2018, at 10:25 AM, Peter Abele <ppab...@web.de> wrote:
> 
> Hello,
> 
> I am using fipy to simulate semiconductor pn junctions, see the attached 
> python script "01_p_n.py".
> 
> Calculation of the electrical field, the width of the  space charge region, 
> and the potential in the semiconductor looks fine even with biasing.
> Evaluating the current densities (drift and diffusion) for the electrons and 
> holes results in extreme peaks independent of the applied biasing, see 
> "Current_Density.png".
> This graph was calculated with no bias voltage.
> In steady state current density should be constant along the semiconductor. 
> Outside the space charge region the current density looks ok. I even get the 
> exponential current density increase with applied voltage.
> 
> Observation:
> It seems that the current density peaks coincide with the second derivative 
> of the electron and/or hole concentration.
> When I additionally multiply the second derivative of the electron 
> concentration with the electron concentration, I get nearly the same shape as 
> for the current density!
> >>> viewer_n     = fipy.Viewer(vars=(n.grad[0].grad[0]*n), title="2. 
> >>> derivative n * n", legend='lower left')
> 
> An other observation is that the peak current densities (j_peak) depends on 
> the mesh size (dx), see "eval.eps".
> It seems that j_peak ~ dx**2!
> 
> Do I see the limit of the numerical calculation due to the extreme change in 
> electron / hole concentration at the border of the space charge region?
> Is the big difference in the cell variables (potential (-10 ... 1) and 
> hole/electron concentration (0 ... 1E24)) a problem?
> Any proposal what I can investigate to get a constant current density along 
> the semiconductor?
> 
> 
> Thanks and best regards
>        Peter
> 
> 
> <eval.eps><Current_Density.png><01_p_n.py>_______________________________________________
> fipy mailing list
> fipy@nist.gov
> http://www.ctcms.nist.gov/fipy
>  [ NIST internal ONLY: https://email.nist.gov/mailman/listinfo/fipy ]
from __future__ import print_function, division
import fipy
import numpy as np
from matplotlib import pyplot as plt



eps_0 = 8.8542e-12     # Permittivity of free space, F/m
q     = 1.6022e-19     # Charge of an electron, C
k     = 1.3807e-23     # Boltzmann constant, J/K
T     = 300            # Temperature, K
Vth   = (k*T)/q        # Thermal voltage, eV

N_ap  = 1e24           # Acceptor density in p-type layer, m^-3
N_an  = 0              # Acceptor density in n-type layer, m^-3
N_dp  = 0              # Donor density in p-type layer, m^-3
N_dn  = 1e24           # Donor density in n-type layer, m^-3

mu_n  = 1400.0e-4      # Mobilty of electrons, m^2/Vs
mu_p  =  450.0e-4      # Mobilty of holes, m^2/Vs
D_p   = 1*k*T*mu_p/q   # Hole diffusion constant, m^2/s
D_n   = 1*k*T*mu_n/q   # Electron diffusion constant, m^2/s
eps_r = 11.8           # Relative dielectric constant
n_i_Si = 10E15

# Grenze bei 1E11 !!
tau   = 1E-3

eps = eps_0*eps_r

# ****************
#  generate mesh 
# ****************
Lx = 150E-9
dx = .15E-9
nx = int(Lx/dx)

mesh = fipy.Grid1D(dx=dx, nx=nx)
Lcenter = Lx / 2


n   = fipy.CellVariable(mesh=mesh, hasOld=True, name='n')
p   = fipy.CellVariable(mesh=mesh, hasOld=True, name='p')
phi = fipy.CellVariable(mesh=mesh, hasOld=True, name='phi')

rho = fipy.CellVariable(mesh=mesh, name='rho')
Nd  = fipy.CellVariable(mesh=mesh, name='Nd')
Na  = fipy.CellVariable(mesh=mesh, name='Na')


x = mesh.cellCenters[0]

# ******************************
# ******************************
#           Doping profile
# ******************************
# ******************************

Na.setValue((+N_ap + np.sqrt(N_ap**2 + 4*n_i_Si**2))/2, where=(x<=Lcenter))
p.setValue( (+N_ap + np.sqrt(N_ap**2 + 4*n_i_Si**2))/2, where=(x<=Lcenter))
n.setValue( (-N_ap + np.sqrt(N_ap**2 + 4*n_i_Si**2))/2, where=(x<=Lcenter))

Nd.setValue((+N_dn + np.sqrt(N_dn**2 + 4*n_i_Si**2))/2,          where=(x>Lcenter))
n.setValue( (+N_dn + np.sqrt(N_dn**2 + 4*n_i_Si**2))/2,           where=(x>Lcenter))
p.setValue( (-N_dn + np.sqrt(N_dn**2 + 4*n_i_Si**2))/2, where=(x>Lcenter))




equ_cont_n = fipy.TransientTerm(coeff=1, var=n) ==  (
                                                     - 0.0 * (fipy.ImplicitSourceTerm(coeff=p,var=n) - n_i_Si**2)/tau
                                                     + fipy.ConvectionTerm(coeff=mu_n * -phi.faceGrad, var=n)
                                                     + fipy.DiffusionTerm(coeff=D_n, var=n)
                                                    )  




equ_cont_p = fipy.TransientTerm(coeff=1, var=p) ==  (
                                                     -  0.0 * (fipy.ImplicitSourceTerm(coeff=n,var=p) - n_i_Si**2)/tau
                                                     - fipy.ConvectionTerm(coeff=mu_p * -phi.faceGrad, var=p)
                                                     + fipy.DiffusionTerm(coeff=D_p, var=p)
                                                    ) 


equ_poisson = fipy.ImplicitDiffusionTerm(coeff=-eps, var=phi) ==  rho


# ***************************************
# ***************************************
#   Ohmic contacts boundary condition 
# ***************************************
# ***************************************

# ***************
#    Potential
# ***************

#phi.setValue(0)
phi.faceGrad.constrain(0, mesh.facesRight)
phi.faceGrad.constrain(0, mesh.facesLeft)

# *******************************************
# 
# electrons and holes at the Ohmic contacts
#
# *******************************************

# **************
#   p-contact
# **************
p.constrain((+N_ap + np.sqrt(N_ap**2 + 4*n_i_Si**2))/2, mesh.facesLeft)
n.constrain((-N_ap + np.sqrt(N_ap**2 + 4*n_i_Si**2))/2, mesh.facesLeft)
#n.constrain(1E22, mesh.facesLeft)


# **************
#    n-contact
# **************
n.constrain((+N_dn + np.sqrt(N_dn**2 + 4*n_i_Si**2))/2, mesh.facesRight)
p.constrain((-N_dn + np.sqrt(N_dn**2 + 4*n_i_Si**2))/2, mesh.facesRight)


viewer_np          = fipy.Viewer(vars=(n,p), log=True)
viewer_rho         = fipy.Viewer(vars=rho, limits={'ymin': -180000, 'ymax': 180000})
viewer_phi         = fipy.Viewer(vars=phi)
viewer_EField      = fipy.Viewer(vars=(-phi.arithmeticFaceValue).divergence, limits={'ymin': -50E6, 'ymax': 0}, title="E-Field")

J_n_drift = -mu_n * n.harmonicFaceValue * phi.faceGrad
J_n_drift.name = r"$J_{n,drift}$"
J_n_diff = D_n * n.faceGrad
J_n_diff.name = r"$J_{n,diff}$"
J_n = -mu_n * n.harmonicFaceValue * phi.faceGrad + D_n * n.faceGrad
J_n.name = r"$J_n$"
J_p =  mu_p * p.harmonicFaceValue * phi.faceGrad + D_p * p.faceGrad
J_p.name = r"$J_p$"
J_tot = J_n + J_p
J_tot.name = r"$J_{tot}$"

# viewer_j_n            = fipy.Viewer(vars=J_n[0],  legend='lower left')
# 
# viewer_j_n_drift            = fipy.Viewer(vars=J_n_drift[0],  legend='lower left')
# 
# viewer_j_n_diff            = fipy.Viewer(vars=J_n_diff[0],  legend='lower left')
# 
# viewer_j_tot            = fipy.Viewer(vars=J_tot[0],  legend='lower left')

fig = plt.figure(10)
ax_j = fig.add_subplot(111)
ax_j.plot(mesh.faceCenters[0].value, J_n[0].value, label="$J_n$")
ax_j.plot(mesh.faceCenters[0].value, J_p[0].value, label="$J_p$")
ax_j.plot(mesh.faceCenters[0].value, J_tot[0].value, label="$J_{tot}$")
ax_j.legend(loc="upper right")


dt = 0.9 * dx**2 / (2 * D_n)
dt = 1E-15



steps =1000
sweeps = 8

raw_input("Exit?")

bias_points = [0.0]

for bias in bias_points:

    # ***************
    #    Potential
    # ***************

    V_bi_p = k*T/q*np.arcsinh(-N_ap/(2*n_i_Si))
    V_bi_n = k*T/q*np.arcsinh(+N_dn/(2*n_i_Si)) +  bias

    phi.constrain(V_bi_p, mesh.facesLeft)
    phi.constrain(V_bi_n, mesh.facesRight)

    for step in range(steps):

        rho.setValue(q * (p - n + Nd - Na))
        n.updateOld()
        p.updateOld()
        phi.updateOld()
        
        #rho.updateOld()

        if (step%10==0):
            viewer_np.plot()
            viewer_rho.plot()
            viewer_phi.plot()
            viewer_EField.plot()
            ax_j.cla()
            ax_j.plot(mesh.faceCenters[0].value, J_n[0].value, label="$J_n$")
            ax_j.plot(mesh.faceCenters[0].value, J_p[0].value, label="$J_p$")
            ax_j.plot(mesh.faceCenters[0].value, J_tot[0].value, label="$J_{tot}$")
            ax_j.legend(loc="upper right")
            
#             viewer_j_n.plot()
#             viewer_j_n_drift.plot()
#             viewer_j_n_diff.plot()
#             viewer_j_tot.plot()

        print("Step: "+ str(step))


        res0 = res1 =res2 = 1E100

        for sweep in range(sweeps):
            res0 = equ_poisson.sweep(var=phi, dt=dt)
            rho.setValue(q * (p - n + Nd - Na))
            res1 = equ_cont_n.sweep(var=n, dt=dt)
            rho.setValue(q * (p - n + Nd - Na))
            res2 = equ_cont_p.sweep(var=p, dt=dt)
            rho.setValue(q * (p - n + Nd - Na))

            print("Res: " + str(res0) + "   " + str(res1) + "   " + str(res2))
_______________________________________________
fipy mailing list
fipy@nist.gov
http://www.ctcms.nist.gov/fipy
  [ NIST internal ONLY: https://email.nist.gov/mailman/listinfo/fipy ]

Reply via email to