Beispiel #1
0
def B(x):
    """
    Bernoulli function B(x)=x/(exp(x)-1), implemented for double precision
    calculation
    """

    # For small x, Taylor expansion should be used instead of direct
    # formula which will is not even defined for x=0.
    BP = 0.0031769106669272879

    # Taylor series
    x2 = x**2.
    taylor = 1. - x / 2. + x2 / 12. - x2**2 / 720.

    # Direct calculation
    # 1e-20 is added to avoid NaN for small x. It only modifies
    # the result if abs(x)<BP. In such a case, the direct formula
    # is not used anyway.
    # In order to avoid overflow errors, maximum x is limited to 700
    # to ensure that exp(xn) is always representable in double precision.
    # Anyway xn/(exp(xn)-1.) is almost 0. for large x
    xn = ad.where(x < 700., x, 700.)
    direct = xn / ((ad.exp(xn) - 1.) + 1e-20)

    # Breakpoint BP is chosen so that Taylor series and direct
    # calculation coincide in double precision.
    return ad.where(abs(x) < BP, taylor, direct)
Beispiel #2
0
 def c(self, ctx, eq, Ef, numeric_parameters=False):
     Ef_raw = Ef
     if self.Ef_max is not None:
         Ef = where(Ef < self.Ef_max, Ef, self.Ef_max)
         if self.logger.isEnabledFor(logging.INFO):
             if np.any(nvalue(Ef) != nvalue(Ef_raw)):
                 self.logger.info(
                     'c(%r): clipping Ef>%r, max(Ef)=%r' %
                     (eq.prefix, self.Ef_max, np.amax(
                         nvalue(Ef_raw))))
     N0 = self.N0(ctx, eq)
     if numeric_parameters:
         N0 = nvalue(N0)
     Vt = ctx.varsOf(eq.thermal)['Vt']
     if numeric_parameters:
         Vt = nvalue(Vt)
     v = Ef / Vt
     if self.c_limit:
         v_raw = v
         v = where(v <= 0., v, 0.)
         if self.logger.isEnabledFor(logging.INFO):
             if np.any(nvalue(v_raw) != nvalue(v)):
                 self.logger.info(
                     'c(%r): clipping Ef/kT>0, max(Ef/kT)=%r' %
                     (eq.prefix, np.amax(
                         nvalue(v_raw))))
     c = exp(v) * N0
     return c
Beispiel #3
0
 def mobility(self, parent, ctx, eq):
     mu0 = ctx.param(eq, 'mu0')
     gamma = ctx.param(eq, 'gamma')
     v = gamma * sqrt(ctx.varsOf(eq.poisson)['Ecellm'] + 1e-10)
     v_max = log(1e10)
     v_min = -v_max
     v = where(v < v_max, v, v_max)
     v = where(v > v_min, v, v_min)
     mu = mu0 * exp(v)
     mu_face = eq.mesh.faceaverage(mu)
     ctx.varsOf(eq)['mu_face'] = mu_face
     ctx.varsOf(eq)['mu_cell'] = mu
Beispiel #4
0
def species_v_D_limited(ctx, eq):
    "Species with concentration limitation"
    v, D = species_v_D_charged_from_params(ctx, eq)
    c = ctx.varsOf(eq)['c']
    c = where(v > 0, c[eq.mesh.faces['+']], c[eq.mesh.faces['-']])
    v = v * (1. - c / ctx.param(eq, 'N0'))
    return v, D
Beispiel #5
0
def Aux2(x):
    """
    Auxiliary function Aux2(x) = 1./(exp(x)+1)
    Properties: Aux2(x)+Aux2(-x)=1
    """
    xn = ad.where(x < 700., x, 700.)
    return 1. / (ad.exp(xn) + 1.)
Beispiel #6
0
 def image_correction(self, ctx, eq, ixto):
     if not self.image_force:
         return 0.
     n = eq.mesh.boundaries[self.name]['normal']
     F = eq.z * eq.mesh.dotv(ctx.varsOf(eq.poisson)['Ecellv'][ixto], n)
     F = where(F > self.F_eps, F, self.F_eps)
     return -eq.z * \
         functions.EmtageODwyerBarrierLowering(
             F, getitem(ctx.varsOf(eq.poisson)['epsilon'], ixto))
Beispiel #7
0
 def Ef(self, ctx, eq):
     c = ctx.varsOf(eq)['c']
     c_raw = c
     c = where(c > self.c_eps, c, self.c_eps)  # avoid NaN
     if self.logger.isEnabledFor(logging.INFO):
         if np.any(nvalue(c_raw) != nvalue(c)):
             self.logger.info(
                 'Ef(%r): clipping c<%r, min(c)=%r' %
                 (eq.prefix, self.c_eps, np.amin(nvalue(c_raw))))
     N0 = self.N0(ctx, eq)
     return ctx.varsOf(eq.thermal)['Vt'] * np.log(c / N0)
Beispiel #8
0
 def _load(self, ctx, eq):
     variables = ctx.varsOf(eq)
     if 'gdos_data' in variables:
         return variables['gdos_data']
     info = self.disorder.disorder_parameters(self, ctx, eq)
     nsigma = info['nsigma']
     nsigma = where(nsigma > self.nsigma_min, nsigma, self.nsigma_min)
     nsigma = where(nsigma < self.nsigma_max, nsigma, self.nsigma_max)
     if np.any(nsigma != info['nsigma']):
         self.logger.info(
             'nsigma clipped to ensure numerical stability: probably not calculating what you want. This is OK in initial Newton iterations, but not in the final one')
     N0 = info['N0']
     c = ctx.varsOf(eq)['c'] / N0
     assert self.c_eps >= self.impl.I_min
     assert self.c_max <= self.impl.I_max
     c = where(c > self.c_eps, c, self.c_eps)
     c = where(c < self.c_max, c, self.c_max)
     b = self.impl.b(np.sqrt(2.) * nsigma, c)
     info.update(b=b, c=c, nsigma=nsigma)
     variables['gdos_data'] = info
     return info
Beispiel #9
0
 def mobility(self, parent, ctx, eq):
     info = self._load(ctx, eq)
     a = _egdm.N0toa(info['N0'])
     nsigma = info['nsigma']
     sigma = info['sigma']
     mu_cell = _egdm.mu0t(nsigma, info['mu0'])
     c = info['c']
     if self.use_g1:
         mu_cell = mu_cell * \
             _egdm.g1(nsigma, where(c < self.g1_max_c, c, self.g1_max_c))
     mu_face = eq.mesh.faceaverage(mu_cell)
     E = ctx.varsOf(eq.poisson)['E']
     if self.use_g2:
         En = (E * a) / sigma
         En2 = En**2
         En2_max = self.g2_max_En**2
         mu_face = mu_face * \
             _egdm.g2_En2(nsigma, where(En2 < En2_max, En2, En2_max))
     ctx.varsOf(eq).update(
         mu_face=mu_face,
         mu_cell=eq.mesh.cellaveragev(mu_face))
Beispiel #10
0
    def D_mu(self, parent, ctx, eq):
        info = self._load(ctx, eq)
        assert self.g3_max_c <= self.gdosImpl.I_max
        assert self.c_eps >= self.gdosImpl.I_min
        assert self.nsigma_max * np.sqrt(2.) <= self.gdosImpl.a_max
        c = info['c']
        nsigma = info['nsigma']
        inlimit_g3 = c < self.g3_max_c

        def branch_if_inlimit(ix):
            return getitem(info['b'], ix)

        def branch_cut_limit(ix):
            return self.impl.b(
                np.sqrt(2.) * getitem(nsigma, ix), self.g3_max_c)
        b_g3 = branch(inlimit_g3, branch_if_inlimit, branch_cut_limit)
        c_g3 = where(inlimit_g3, c, self.g3_max_c)
        g3 = _egdm.g3(nsigma, c_g3, impl=self.impl, b=b_g3)
        return eq.mesh.faceaverage(g3*info['Vt'])
Beispiel #11
0
    def evaluate(self, ctx, eq):
        if ctx.solver.poissonOnly:
            return
        assert eq.electron_eq.poisson is eq.hole_eq.poisson
        assert eq.electron_eq.thermal is eq.hole_eq.thermal
        poissonvars = ctx.varsOf(eq.electron_eq.poisson)
        epsilon = poissonvars['epsilon']
        Vt = ctx.varsOf(eq.electron_eq.thermal)['Vt']
        nvars = ctx.varsOf(eq.electron_eq)
        pvars = ctx.varsOf(eq.hole_eq)
        evars = ctx.varsOf(eq.eq)

        if self.binding_energy_param:
            Eb = ctx.param(eq.eq, 'binding_energy')
            a = scipy.constants.elementary_charge / \
                (Eb * 4 * np.pi * epsilon)
        else:
            a = ctx.param(eq.eq, 'distance')
            Eb = scipy.constants.elementary_charge / \
                (4 * np.pi * epsilon * a)
        u = 3. / (4. * np.pi * a**3)  # m^-3
        v = exp(-Eb / Vt)  # 1
        b = (scipy.constants.elementary_charge / (8 * np.pi)) * \
            poissonvars['Ecellm'] / (epsilon * Vt**2)  # 1
        # b=0.
        t = functions.OnsagerFunction(
            where(b < self.b_max, b, self.b_max) + self.b_eps)  # 1
        gamma = scipy.constants.elementary_charge * \
            (nvars['mu_cell'] + pvars['mu_cell']) / \
            epsilon  # m^3 /s
        r = gamma * (nvars['c'] * pvars['c'] - ctx.common_param(
            [eq.electron_eq, eq.hole_eq], 'npi'))  # 1/(m^3 s) # TODO
        d = gamma * evars['c'] * u * v * t
        ctx.outputCell([eq.eq, self.name, 'recombination'],
                       r,
                       unit=ctx.units.dconcentration_dt)
        ctx.output([eq.eq, self.name, 'dissociation'],
                   d,
                   unit=ctx.units.dconcentration_dt)
        f = r - d
        self.add(ctx, f, plus=[eq.eq], minus=[eq.hole_eq, eq.electron_eq])
Beispiel #12
0
 def value(self, ctx, eq):
     if isinstance(ctx.solver, solver.RamoShockleyCalculation):
         return where(self.name == ctx.solver.boundary_name, 1, 0)
     return self.potential(ctx, eq)
Beispiel #13
0
 def I(self, a, b):
     v = self._value(a, b)
     l_max = np.log(self.I_max)
     v = ad.where(v <= l_max, v, l_max)
     return ad.exp(v)