Source code for simudo.physics.heterojunction

import attr
import dolfin
import numpy as np

from .poisson_drift_diffusion import Band, NondegenerateBand
from ..mesh.topology import FacetRegion


# TODO: rename this to the type of heterojunction that it is (what is
# the name of the model?)
[docs]@attr.s class ThermionicHeterojunction: """ Thermionic emission heterojunction BC valid with parabolic bands in the Boltzmann approximation. Parameters ---------- band: :py:class:`.Band` Semiconductor band on which the BC is applied. Only MixedQflBand is currently supported. boundary: :py:class:`.FacetRegion` Boundary on which to implement heterojunction boundary condition. Notes ----- See V. Palankovski (2004), eq. 3.72 and K. Yang, J. R. East, G. I. Haddad, Solid State Electronics v.36 (3) p.321-330 (1993) K. Horio, H. Yanai, IEEE Trans. Elec. Devices v.37(4) p.1093-1098 (1990) For a conduction band BC, band.spatial must have attribute "CB/vth" in the barrier region. Similarly for other bands. Unlimited carrier flow from low to barrier region can be resolved, but due to precision issues, cannot resolve Delta_w producing carrier flows from barrier to low region with Delta_w larger than ``|ln(1e-16)| * kT`` in double precision TODO: Add warning/comment that only works for nondegenerate bands """ band: Band = attr.ib() boundary: FacetRegion = attr.ib() @property def vth(self): return self.band.spatial.get(self.band.spatial_prefix + "vth")
[docs] def register(self): """ Apply boundary condition onto the band. """ band = self.band mu = band.pdd.mesh_util U = mu.unit_registry if not isinstance(band, NondegenerateBand): raise NotImplementedError("Degenerate bands not yet supported") # delta: correction for tunneling through the barrier. not implemented. delta = 0.0 sign = band.sign # Thermal velocities # These should be calculated from DOS effective mass (Palakovski 3.75) vth = self.vth # Eb is barrier height. should be positive. Use its sign to determine which # side is the low and which side is the barrier Eb = -sign * (mu.pside(band.energy_level) - mu.mside(band.energy_level)) def Eb_fconditional(fun1, fun2): """Return a function conditional on the sign of Eb """ def func(expr): u = expr.units return u * dolfin.conditional( dolfin.gt(Eb.m, 0), fun1(expr).m_as(u), fun2(expr).m_as(u) ) return func lowside = Eb_fconditional(mu.mside, mu.pside) barside = Eb_fconditional(mu.pside, mu.mside) kT = lowside(band.kT) # T is assumed continuous qe = U.elementary_charge u_bar = barside(band.u) vth_bar = barside(vth) # mu.n points out from whichever surface. So to get current # from lowside to barside, need lowside(mu.n) j_band = lowside(mu.dot(band.j, mu.n)) # Use the ln function down to where we can't resolve it anyway, # then use a linear extrapolation of the log, which doesn't give # nan for negative argument # Apply an arcsinh on that linear extrapolation, to avoid its # growing too large shift = j_band / (sign * qe * vth_bar * (1 + delta) * u_bar) shift = shift.m_as(U.dimensionless) argument = 1 + shift # argument = argument.m_as(U.dimensionless) # Use ln1p if shift is close to 0, for better precision large_target = dolfin.conditional( dolfin.gt(mu.dless(mu.abs(shift)), 1e-6), mu.ln(argument), mu.ln1p(shift), ) eps = dolfin.DOLFIN_EPS small_target = mu.asinh((argument - eps) / eps) + np.log(eps) # Target for Delta_w Delta_w_BC = ( kT / sign * dolfin.conditional( dolfin.gt(argument, eps), large_target, small_target ) ) w0 = band.mixedqfl_base_w # Actual Delta_w Delta_w = barside(w0) - lowside(w0) boundary = self.boundary dS = mu.region_oriented_dS(boundary, orient=False)[1] xi = band.mixedqfl_xi # Remove the standard current due to w-changes from the interfaces band.mixedqfl_drift_diffusion_heterojunction_facet_region |= ( boundary.both() ) # Add the boundary condition band.mixedqfl_drift_diffusion_heterojunction_bc_term += ( -dS * (Delta_w - Delta_w_BC) * lowside(mu.dot(xi, mu.n)) )