def solve_general(self, ctx, eq, fixed): def conc(Ef, numeric_parameters): for s in eq.with_dos: yield s, s.dos.concentrationv(ctx, s, None, Ef, numeric_parameters=numeric_parameters) nfixed = nvalue(fixed) def f(Ef, numeric_parameters=True): if numeric_parameters: charge = nfixed else: charge = fixed for eq, c in conc(Ef, numeric_parameters): charge = charge + c*eq.z return charge bandv = np.asarray([nvalue(ctx.varsOf(s)['Ebandv']) for s in eq.with_dos]) a = np.amax(bandv) b = np.amin(bandv) Ef_value = brent(f, a, b, xtol=self.etol, maxiter=50) g = f(Ef_value, False) if isinstance(g, forward.value): raise NotImplementedError('not tested') dg = f(forward.seed(Ef_value), True).deriv if not isscalar(dg): dg = dg.tocsr().diagonal() # must create value : (Ef_value, 1/dg*g') Ef = custom_function(lambda *args: Ef_value, lambda *args: dg)(g) else: Ef = Ef_value info = dict((id(eq), c) for eq, c in conc(Ef, False)) return Ef, info
def c(self, ctx, eq, Ef, numeric_parameters=False): info = self._load(ctx, eq) N0 = info['N0'] nsigma = info['nsigma'] Vt = info['Vt'] if numeric_parameters: N0 = nvalue(N0) nsigma = nvalue(nsigma) Vt = nvalue(Vt) return N0 * self.impl.I(np.sqrt(2.) * nsigma, b=-Ef / info['Vt'])
def _dvdb_(self, a, b): v = self._nevaluate(nvalue(a), nvalue(b), need_value=False, need_db=True, need_d2ab=not isnvalue(a), need_d2bb=not isnvalue(b)) def _dIdb(a, b): return v['db'] def _dIdb_deriv(args, value): yield lambda: v['d2ab'] yield lambda: v['d2bb'] return ad.apply(asdifferentiable(_dIdb, _dIdb_deriv), (a, b))
def _value_(self, a, b): v = self._nevaluate(nvalue(a), nvalue( b), need_value=True, need_da=not isnvalue(a), need_db=not isnvalue(b)) def _I(a, b): return v['value'] def _I_deriv(args, value): yield lambda: v['da'] yield lambda: v['db'] return ad.apply(asdifferentiable(_I, _I_deriv), (a, b))
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)
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 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 _b_(self, a, v, b0=None): b = self._nsolve_b(nvalue(a), nvalue(v), b0=b0) def _b(a, v): return b def _b_deriv(args, value): a_, i_ = args b = value values = self._nevaluate( a_, b, need_da=not isnvalue(a), need_db=True) dbdI = 1. / values['db'] yield lambda: -dbdI * values['da'] yield lambda: dbdI return ad.apply(asdifferentiable(_b, _b_deriv), (a, v))
def _value(self, a, b): v = self._nevaluate(ad.nvalue(a), ad.nvalue(b), need_value=True, need_da=isinstance(a, ad.forward.value), need_db=isinstance(b, ad.forward.value)) def _I(a, b): return v['value'] def _I_deriv(args, value): yield lambda: v['da'] yield lambda: v['db'] return ad.custom_function(_I, _I_deriv)(a, b)
def _dvdb(self, a, b): v = self._nevaluate(ad.nvalue(a), ad.nvalue(b), need_value=False, need_db=True, need_d2ab=isinstance(a, ad.forward.value), need_d2bb=isinstance(b, ad.forward.value)) def _dIdb(a, b): return v['db'] def _dIdb_deriv(args, value): yield lambda: v['d2ab'] yield lambda: v['d2bb'] return ad.custom_function(_dIdb, _dIdb_deriv)(a, b)
def __init__(self, impl, a, n=1000, k=5): super(UnivariateInterpolatedGaussFermi, self).__init__() self.a = a self.a_max = impl.a_max self.I_min = impl.I_min self.I_max = impl.I_max self.b_min, self.b_max = map( lambda b: impl.b( a, b), (impl.I_min, impl.I_max)) self.btab = np.linspace(self.b_min, self.b_max, n) _i = impl.I(ad.forward.seed(a), self.btab) self.logItab = np.log(ad.nvalue(_i)) self.dlogIdatab = 1. / ad.nvalue(_i) * _i.gradient.tocsr().todense().A1 assert np.all(np.diff(self.logItab) > 0) self.spline = make_interp_spline( self.btab[::-1], self.logItab[::-1], k=k) self.daspline = make_interp_spline( self.btab[::-1], self.dlogIdatab[::-1], k=k)
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 _b(self, a, v, b0=None): b = self._nsolve_b(ad.nvalue(a), ad.nvalue(v), b0=b0) def _b(a, v): return b def _b_deriv(args, value): a_, i_ = args b = value values = self._nevaluate(a_, b, need_da=isinstance(a, ad.forward.value), need_db=True) dbdI = 1. / values['db'] yield lambda: -dbdI * values['da'] yield lambda: dbdI return ad.custom_function(_b, _b_deriv)(a, v)
def load(self, ctx, eq): super(Electroneutrality, self).load(ctx, eq) if isinstance(ctx.solver, solver.RamoShockleyCalculation): return fixed = 0 for s in eq.other: fixed = fixed + nvalue(ctx.varsOf(s)['c']) * s.z if eq.analytical and not self.force_general: Efv, c = self.solve_analytical(ctx, eq, fixed) else: Efv, c = self.solve_general(ctx, eq, fixed) ctx.varsOf(eq)['Efv'] = Efv ctx.varsOf(eq)['conc_Ef'] = c Ef = -ctx.varsOf(eq.poisson)['potential'] + Efv ctx.outputCell([eq, 'Ef'], Ef, unit=ctx.units.eV) ctx.outputCell([eq, 'phi'], -Ef, unit=ctx.units.V) for s, sc in zip(eq.with_dos, self.with_dos): ctx.outputCell([eq, sc.name, 'c'], c[id(s)], unit=ctx.units.concentration)
def _get(self, a): a = ad.nvalue(a) if a.shape: if len(a) > 0: if np.count_nonzero(a != a[0]) > 0: raise ValueError('a must have all values equal') a = a[0] else: # a is empty: return anything if self.d: return next(iter(self.d.values())) else: return self._get(0) a = float(a) u = self.d.get(a, None) if u is None: u = UnivariateInterpolatedGaussFermi( self.impl, np.asarray(a), **self.kwargs) self.d[a] = u return u
def identity(self, x): return self.idx, x - ad.nvalue(x)