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 <[email protected]> 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
> [email protected]
> 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
[email protected]
http://www.ctcms.nist.gov/fipy
[ NIST internal ONLY: https://email.nist.gov/mailman/listinfo/fipy ]