2

I'm trying to use FiPy to simulate solar cells but I'm struggling to get reasonable results even for simple test cases.

My test problem is an abrupt 1D p-n homojunction in the dark in equilibrium. The governing system of equations are the semiconductor equations with no additional generation or recombination.

Poisson's equation determines the electric field (φ) in a semiconductor with dielectric constant, ε, given the densities of electrons (n), holes (p), donors (ND), and acceptors (NA), where the charge of an electron is q:

∇²φ = q(p − n + ND − NA) / ε

Electrons and holes drift and diffuse with current densities, J, depending on their mobilities (μ) and diffusion constants (D):

Jn = nnE + qDn∇n

Jp = ppEqDp∇n

The evolution of the charge in the system is accounted for with the electron and hole continutiy equations:

∂n/∂t = (∇·Jn) / q

∂p/∂t = − (∇·Jp) / q

which can be expressed in FiPy canonical form as:

∂n/∂t = μn∇·(−nφ) + Dn∇²n

∂p/∂t = − (μp∇·(−pφ) − Dp∇²n)

To attempt to solve the problem in FiPy I first import modules and define the physical parameters.

from __future__ import print_function, division
import fipy
import numpy as np
import matplotlib.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  = 1e22           # 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  = 1e22           # 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   = k*T*mu_p/q     # Hole diffusion constant, m^2/s
D_n   = k*T*mu_n/q     # Electron diffusion constant, m^2/s
eps_r = 11.8           # Relative dielectric constant

n_i   = (5.29e19 * (T/300)**2.54 * np.exp(-6726/T))*1e6       
V_bias = 0             

Then create the mesh, solution variables, and doping profile.

nx = 20000
dx = 0.1e-9
mesh = fipy.Grid1D(dx=dx, nx=nx)
Ln = Lp = (nx/2) * dx

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

Then I set some initial values on the cell centers and impose Dirichlet boundary conditions on all parameters.

x = mesh.cellCenters[0]

n0 = n_i**2 / N_ap
nL = N_dn
p0 = N_ap
pL = n_i**2 / N_dn
phi_min = -(Vth)*np.log(p0/n_i)
phi_max = (Vth)*np.log(nL/n_i) + V_bias

Na.setValue(N_an, where=(x >= Lp))
Na.setValue(N_ap, where=(x < Lp))
Nd.setValue(N_dn, where=(x >= Lp))
Nd.setValue(N_dp, where=(x < Lp))
n.setValue(N_dn, where=(x > Lp))
n.setValue(n_i**2 / N_ap, where=(x < Lp))
p.setValue(n_i**2 / N_dn, where=(x >= Lp))
p.setValue(N_ap, where=(x < Lp))
phi.setValue((phi_max - phi_min)*x/((Ln + Lp)) + phi_min)

phi.constrain(phi_min, mesh.facesLeft)
phi.constrain(phi_max, mesh.facesRight)
n.constrain(nL, mesh.facesRight)
n.constrain(n_i**2 / p0, mesh.facesLeft)
p.constrain(n_i**2 / nL, mesh.facesRight)
p.constrain(p0, mesh.facesLeft)

I express Poisson's equation as

eps = eps_0*eps_r
rho = q * (p - n + Nd - Na)
rho.name = 'rho'

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

the continuity equations as

cont_eqn_n = (fipy.TransientTerm(var=n) == 
              (fipy.ExponentialConvectionTerm(coeff=-phi.faceGrad*mu_n, var=n) 
               + fipy.ImplicitDiffusionTerm(coeff=D_n, var=n)))

cont_eqn_p = (fipy.TransientTerm(var=p) == 
              - (fipy.ExponentialConvectionTerm(coeff=-phi.faceGrad*mu_p, var=p) 
               - fipy.ImplicitDiffusionTerm(coeff=D_p, var=p)))

and solve by coupling the equations and sweeping:

eqn  = poisson & cont_eqn_n & cont_eqn_p

dt = 1e-12
steps = 50
sweeps = 10    

for step in range(steps):
    phi.updateOld()
    n.updateOld()
    p.updateOld()
    for sweep in range(sweeps):
        eqn.sweep(dt=dt)

I have played around with different values for the mesh size, time step, number of time steps, number of sweeps etc. I see some variation but haven't had any luck finding a set of conditions that give me a realistic solution. I think the problem probably lies in the expressions for the current terms.

Usually when solving these equations the current densities are are approximated using the Scharfetter-Gummel (SG) discretization scheme, rather then the direct discretization. In the SG scheme the electron current density (J) through a cell face is approximated as a function of the values of potential (φ) and charge density (n) defined on the centres of cells K and L either side as

Jn,KL=nVT[B(δφ/VT)nLB(−δφ/VT)nK)

where q is the charge on an electron, μn is the electron mobility, VT is the thermal voltage, δφ=φLφK, and B(x) is the Bernoulli function x/(ex−1).

It's not obvious to me how to implement the scheme in FiPy. I have seen there is a scharfetterGummelFaceVariable but I can't work out from the documentation whether it's suitable or intended for this problem. Looking at the code it seems to only calculate the Bernoulli function multiplied by a factor eφL. Is it possible to directly use the scharfetterGummelFaceVariable to solve this type of problem? If so, how? If not, is there an alternative approach that will allow me to simulate semiconductor devices using FiPy?

jmball
  • 31
  • 4
  • I'm getting "Page not found" from both the semiconductor equations link and the github link. It would also be helpful to include the complete code you're trying to run (it's currently missing some parameter values, Na/Nd profiles, IC's, and BC's). – muon Sep 27 '16 at 19:35
  • Also, is there a sign error in one of the convection terms? If these are the Nernst-Planck equations, then the sign should be different if `n` and `p` have different charge. I've also written electromigration terms in `FiPy` using the diffusion term, something like `DiffusionTerm(coeff=p.harmonicFaceValue*mu_p, var=phi)` – muon Sep 27 '16 at 21:00
  • @muon I've fixed the links and added all of my code to the question. There was a sign error for the hole current/continuity equation which has been fixed but I still don't obtain realistic results. I tried replacing the convection terms using your approach and it returned an error, "Factor is exactly singular" although I think something like this could be a good way forward. However, I'm not sure how to express it correctly given the variable in this case should be the gradient of phi rather than phi itself. – jmball Oct 05 '16 at 12:47
  • the code is still missing definitions for `Lp` and `Ln`. – muon Oct 05 '16 at 15:19
  • @muon I've just added them to the code block containing the definition of the mesh. – jmball Oct 05 '16 at 19:43
  • Here is list of open source TCAD tools, many solving the drift diffusion equations. https://www.tcad.com/Software.html I have never seen a cell centered implementation before. Traditional TCAD solvers are vertex centered. – Juan Jul 20 '17 at 20:10

0 Answers0