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
def solve_analytical(self, ctx, eq, fixed): hole, = eq.hole electron, = eq.electron C = fixed Nc = electron.dos.N0(ctx, electron) Nv = hole.dos.N0(ctx, hole) Vt = ctx.varsOf(eq.thermal)['Vt'] Ec = ctx.varsOf(electron)['Ebandv'] Ev = ctx.varsOf(hole)['Ebandv'] uc = exp(Ec/Vt) uv = exp(Ev/Vt) # TODO: rewrite to avoid overflow, reference to middle of bandgap Ef = Vt*log((C*uc + sqrt((C*C*uc + 4*Nc*Nv*uv)*uc))/(2*Nc)) n = Nc*exp((Ef-Ec)/Vt) p = Nv*exp((Ev-Ef)/Vt) return Ef, dict({id(hole): p, id(electron): n})
def evaluate(self, ctx, eq): if ctx.solver.poissonOnly: return transportenergy = ctx.param(eq.transport_eq, 'energy') trate = ctx.param(eq.trap_eq, 'trate') if self.rrate_param: rrate = ctx.param(eq.trap_eq, 'rrate') else: trapenergy = ctx.param(eq.trap_eq, 'energy') depth = (trapenergy - transportenergy) * eq.transport_eq.z rrate = trate * \ exp(-depth / ctx.varsOf(eq.transport_eq.thermal)['Vt']) transportN0 = ctx.param(eq.transport_eq, 'N0') trapN0 = ctx.param(eq.trap_eq, 'N0') ctransport = ctx.varsOf(eq.transport_eq)['c'] ctrap = ctx.varsOf(eq.trap_eq)['c'] if self.fill_trap: a = trate * (trapN0 - ctrap) else: a = trate * trapN0 if self.fill_transport: b = rrate * (transportN0 - ctransport) else: b = rrate * transportN0 g = a * ctransport - b * ctrap self.add(ctx, g, plus=[eq.trap_eq], minus=[eq.transport_eq])
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.)
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)
def g2_En2(nsigma, En2): """ EGDM g2(En2) nsigma=sigma/kT, recommended 3<=nsigma<=6 En2=(E*a/sigma)**2, recommended En2<4. """ return ad.exp(0.44 * (nsigma**(3. / 2.) - 2.2) * (ad.sqrt(1 + 0.8 * En2) - 1))
def g1(nsigma, c): """ EGDM g1 nsigma=sigma/kT: must be nsigma>=1. c must be 0<=c<=1, recommended c<0.1 """ delta = 2.0 * (ad.log(nsigma**2 - nsigma) - ad.log(ad.log(4))) / (nsigma**2) return ad.exp(0.5 * (nsigma**2 - nsigma) * (2 * c)**delta)
def evaluate(self, ctx, eq): assert eq.hole_eq.thermal is eq.electron_eq.thermal if ctx.solver.poissonOnly: return Vt = ctx.varsOf(eq.electron_eq.thermal)['Vt'] Et = ctx.param(eq, 'energy') ni = ctx.param(eq.electron_eq, 'N0') * \ exp((Et - ctx.param(eq.electron_eq, 'energy')) / Vt) pi = ctx.param(eq.hole_eq, 'N0') * \ exp((ctx.param(eq.hole_eq, 'energy') - Et) / Vt) Cn = ctx.param(eq.electron_eq, self.name, 'trate') Cp = ctx.param(eq.hole_eq, self.name, 'trate') n = ctx.varsOf(eq.electron_eq)['c'] p = ctx.varsOf(eq.hole_eq)['c'] g = Cn * Cp * ctx.param(eq, 'N0') * \ (n * p - ni * pi) / (Cn * (n + ni) + Cp * (p + pi)) ctx.outputCell([eq, 'G'], g, mesh=eq.electron_eq.mesh, unit=ctx.units.dconcentration_dt) self.add(ctx, g, minus=[eq.electron_eq, eq.hole_eq])
def dIdb(self, a, b): v = self._nevaluate( nvalue(a), nvalue(b), need_value=True, need_da=not isnvalue(a), need_db=True, need_d2ab=not isnvalue(a), need_d2bb=not isnvalue(b)) i = ad.exp(v['value']) def _dIdb(a, _): return i * v['db'] def _dIdb_deriv(args, value): yield lambda: i * (v['d2ab'] + v['da'] * v['db']) yield lambda: i * (v['d2bb'] + v['db'] * v['db']) return ad.apply(asdifferentiable(_dIdb, _dIdb_deriv), (a, b))
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
def dIdb(self, a, b): v = self._nevaluate(ad.nvalue(a), ad.nvalue(b), need_value=True, need_da=isinstance(a, ad.forward.value), need_db=True, need_d2ab=isinstance(a, ad.forward.value), need_d2bb=isinstance(b, ad.forward.value)) i = ad.exp(v['value']) def _dIdb(a, _): return i * v['db'] def _dIdb_deriv(args, value): yield lambda: i * (v['d2ab'] + v['da'] * v['db']) yield lambda: i * (v['d2bb'] + v['db'] * v['db']) return ad.custom_function(_dIdb, _dIdb_deriv)(a, b)
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])
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)
def mu0t_inverse(nsigma, mu0t): return mu0t / (1.8e-9 * ad.exp(-0.42 * nsigma**2))
def mu0t(nsigma, mu0): """EGDM mu_0(T) - temperature dependent mobility prefactor in function of temperature independent prefactor""" return mu0 * 1.8e-9 * ad.exp(-0.42 * nsigma**2)