def process_params(self): if self.is_set('f'): # Nonlinear source if self.is_set('g'): raise cir.CircuitError( '{0}: can not specify both g and f'.format( self.instanceName)) # test f expression to make sure it is valid try: vc = .5 result = eval(self.f) except Exception as e: raise cir.CircuitError( '{0}: Invalid expression: {1} ({2})'.format( self.instanceName, self.f, e)) try: abs(result) except TypeError: raise cir.CircuitError( '{0}: Invalid expression: {1} result not a number)'.format( self.instanceName, self.m)) # Set nonlinear attributes self.isNonlinear = True self.controlPorts = [(2, 3)] self.csOutPorts = [(0, 1)] self.qsOutPorts = [] else: # linear source self.linearVCCS = [((2,3), (0,1), self.g)]
def process_params(self): # Called once the external terminals have been connected and # the non-default parameters have been set. Make sanity checks # here. Internal terminals/devices should also be defined # here. Raise cir.CircuitError if a fatal error is found. # remove any existing internal connections self.clean_internal_terms() # Test parameters if not self.rleak: raise cir.CircuitError(self.instanceName + ': leackage resistance can not be zero') if not self.c: raise cir.CircuitError(self.instanceName + ': capacitance can not be zero') # test m expression to make sure it is valid try: q = .5 result = eval(self.m) except Exception as e: raise cir.CircuitError( '{0}: Invalid expression: {1} ({2})'.format(self.instanceName, self.m, e)) try: abs(result) except TypeError: raise cir.CircuitError( '{0}: Invalid expression: {1} (result not a number)'.format( self.instanceName, self.m)) # Connect internal terminal tim = self.add_internal_term('im', '{0} A'.format(glVar.gyr)) tvc = self.add_internal_term('vc', 'V') tref = self.add_reference_term() # Set up source if q0 is given if self.q0 != 0.: self.isDCSource = True self.sourceOutput = (tref, tvc) self._i0 = self.q0 / self.c / self.rleak # Setup gyrator # Access to global variables is through the glVar self.linearVCCS = [((0,1), (tref, tim), glVar.gyr), ((tim, tref), (0,1), glVar.gyr), ((tim, tref), (tref, tvc), glVar.gyr), ((tvc, tref), (tvc, tref), 1./self.rleak)] self.linearVCQS = [((tvc, tref), (tvc, tref), self.c)] self.controlPorts = [(tim, tref), (tvc, tref)] self.csOutPorts = [(tim, tref)] self.qsOutPorts = []
def process_params(self, thermal=False): # Called once the external terminals have been connected and # the non-default parameters have been set. Make sanity checks # here. Internal terminals/devices should also be defined # here. Raise cir.CircuitError if a fatal error is found. ad.delete_tape(self) if self.type == 'n': self._tf = 1. elif self.type == 'p': self._tf = -1. else: raise cir.CircuitError( '{0}: unrecognized type: {1}. Valid types are "n" or "p"'. format(self.instanceName, self.type)) # Make sure vth is positive for calculations self._vth = abs(self.vth) self._tcv = np.abs(self.tcv) # Nominal abs temperature self._Tn = const.T0 + self.tnom # Nominal Thermal voltage self._Vtn = const.k * self._Tn / const.q self._mu = self.isq * 2. / (self.n * self.cox * self._Vtn**2) if not thermal: # Calculate temperature-dependent variables self.set_temp_vars(self.temp)
def process_params(self): # Called once the external terminals have been connected and # the non-default parameters have been set. Make sanity checks # here. Internal terminals/devices should also be defined # here. Raise cir.CircuitError if a fatal error is found. # Access to global variables is through the glVar if not self.c: raise cir.CircuitError(self.instanceName + ': Capacitance can not be zero') # Adjust according to temperature (not needed so far) # self.set_temp_vars(self.temp) self.linearVCQS = [((0, 1), (0, 1), self.c)]
def process_params(self): # Called once the external terminals have been connected and # the non-default parameters have been set. Make sanity checks # here. Internal terminals/devices should also be defined # here. Raise cir.CircuitError if a fatal error is found. # remove any existing internal connections self.clean_internal_terms() if not self.l: raise cir.CircuitError(self.instanceName + ': Inductance can not be zero') # Connect internal terminal til = self.add_internal_term('il', '{0} A'.format(glVar.gyr)) tref = self.add_reference_term() # Setup gyrator # Access to global variables is through the glVar self.linearVCCS = [((0,1), (tref, til), glVar.gyr), ((til, tref), (0,1), glVar.gyr)] cap = self.l * glVar.gyr**2 self.linearVCQS = [((til, tref), (til, tref), cap)]
def process_params(self, thermal=False): # Called once the external terminals have been connected and # the non-default parameters have been set. Make sanity checks # here. Internal terminals/devices should also be defined # here. Raise cir.CircuitError if a fatal error is found. # Delete AD tape (if any) ad.delete_tape(self) # Access to global variables is through the glVar if not self.r and not (self.rsh and self.l and self.w): raise cir.CircuitError(self.instanceName + ': Resistance can not be zero') if self.r != 0.: # if R is given it overrides rsh et ale self.g = 1. / self.r else: self.g = (self.w - self.narrow) / (self.l - self.narrow) / self.rsh if not thermal: # Adjust according to temperature self.set_temp_vars(self.temp)
def process_params(self, thermal=False): # Called once the external terminals have been connected and # the non-default parameters have been set. Make sanity checks # here. Internal terminals/devices should also be defined # here. Raise cir.CircuitError if a fatal error is found. ad.delete_tape(self) if self.type == 'n': self._tf = 1. elif self.type == 'p': self._tf = -1. else: raise cir.CircuitError( '{0}: unrecognized type: {1}. Valid types are "n" or "p"'. format(self.instanceName, self.type)) # Nominal abs temperature self._Tn = const.T0 + self.tnom if not thermal: # Calculate temperature-dependent variables self.set_temp_vars(self.temp)
def process_params(self): """ This should be called each time parameter values are changed """ # Called once the external terminals have been connected and # the non-default parameters have been set. Make sanity checks # here. Internal terminals/devices should also be defined # here. Raise cir.CircuitError if a fatal error is found. # Use the following to make sure connections to internal # terminals are not repeated if this process_params is called # many times. self.clean_internal_terms() # Calculate the capacitance per unit length # k = epsilon_eff self.c = np.sqrt(self.k) / (self.z0mag * const.c0) # Calculate the inductance per unit length self.l = (self.z0mag * self.z0mag) * self.c # convert alpha from db/m to nepers/m self.alpha_nepers = self.alpha / const.Np2dB # If nsect (number of sections) is set then use a # multi-section model for the transmisison line. The strategy # is to expand the transmission line into a number of RLGC # sections. A circuit is built up with series L's and shunt # C's (which support series resistance and shunt capacitance) if self.nsect: # No longer a frequency-defined element self.isFreqDefined = False # Need to check that the local reference terminals are the # same as this is required by the sectional model. if self.connection[1] != self.connection[3]: raise cir.CircuitError('{1}: nsect > 0 but sectional model requires port references to be the same node'.format(self.instanceName)) # Use discrete approximation. Find the number of # subsections and the RLCG parameters of each. First get # the length of each subsection. delta_x = self.length / self.nsect # Scale attenuation if fscale and fopt given if self.fscale * self.fopt != 0.: alphaf = self.alpha_nepers * np.sqrt(self.fopt / self.fscale) else: alphaf = self.alpha_nepers # Now get the R, L, G and C of each subsection. R = 2.0 * alphaf * self.z0mag * delta_x if self.fopt * self.tand != 0.: G = self.tand * 2. * np.pi * self.fopt * self.c * delta_x else: G = 0. L = self.l * delta_x C = self.c * delta_x # Gyrated inductor cap indcap = L * glVar.gyr * glVar.gyr # Reference for all gyrators tref = self.add_reference_term() # nps: nodes added by one section if R: nps = 3 else: nps = 2 # Initialize empty lists self.linearVCCS = list() self.linearVCQS = list() # Expand transmission line sections for i in xrange(self.nsect): # input node number if i: inn = nps*i + tref else: inn = 0 # add gyrator node gnn = self.add_internal_term('il{0}'.format(i), '{0} A'.format(glVar.gyr)) # The last section does not add cap node if i < self.nsect - 1: # Add capacitor terminal cnn = self.add_internal_term('c{0}'.format(i), 'V') else: cnn = 2 if R: # add resistor node rnn = self.add_internal_term('r{0}'.format(i), 'V') # Add gyrator self.linearVCCS += [((inn, rnn), (tref, gnn), glVar.gyr), ((gnn, tref), (inn, rnn), glVar.gyr)] # Add resistor self.linearVCCS.append(((rnn, cnn), (rnn, cnn), 1./R)) else: # Add gyrator self.linearVCCS += [((inn, cnn), (tref, gnn), glVar.gyr), ((gnn, tref), (inn, cnn), glVar.gyr)] # Add inductor self.linearVCQS.append(((gnn, tref), (gnn, tref), indcap)) # Add capacitor self.linearVCQS.append(((cnn, 1), (cnn, 1), C)) if G: # Add conductor self.linearVCCS.append(((cnn, 1), (cnn, 1), G))
def process_params(self, thermal=False): # Called once the external terminals have been connected and # the non-default parameters have been set. Make sanity checks # here. Internal terminals/devices should also be defined # here. Raise cir.CircuitError if a fatal error is found. # import pdb; pdb.set_trace() # Delete AD tape (if any) ad.delete_tape(self) # Any value other than zero uses the simple function if self.ekvint: self.interp = f_simple else: self.interp = f_accurate # Set Constants self.Tref = 300.15 self._Tn = self.tnom + const.T0 vtTref = (const.k * self.Tref) / const.q self.egTref = 1.16 - 0.000702 * self.Tref * self.Tref \ / (self.Tref + 1108.) vtTnom = (const.k * self._Tn) / const.q self.egTnom = 1.16 - .000702 * self._Tn * self._Tn / (self._Tn + 1108.) niTnom = 1.45e10 * (self._Tn / self.Tref) \ * np.exp(self.egTref / (2. * vtTref) - self.egTnom / (2. * vtTnom)) # For N/P channel self._tcv = np.abs(self.tcv) if self.type == 'n': self.eta = 0.5 self._tf = 1. elif self.type == 'p': self.eta = 1. / 3. self._tf = -1. else: raise cir.CircuitError( '{0}: unrecognized type: {1}. Valid types are "n" or "p"'. format(self.instanceName, self.type)) #--------------------------------------------------------------- # Calculate any missing parameters from user-defined settings # COX if (not self.is_set('cox')) and self.tox != None: self.cox = const.epOx / self.tox # GAMMA if (not self.is_set('gamma')) and self.nsub != None: self.gamma = np.sqrt( 2. * const.q * const.epSi * self.nsub * 1.e6) / self.cox # PHI if (not self.is_set('phi')) and self.nsub != None: self.phi = 2. * vtTnom * np.log(self.nsub / niTnom) # VT0: if specified, must be with the correct sign, otherwise # we have to adjust for P-channel if (not self.is_set('vt0')): if self.vfb != None: self.vt0 = self._tf * (self.vfb + self.phi + self.gamma * np.sqrt(self.phi)) else: self.vt0 *= self._tf # Make sure vt0 is positive for calculations self._vt0 = abs(self.vt0) # KP if (not self.is_set('kp')) and self.u0 != None: self.kp = self.u0 * 1.e-4 * self.cox # /*(m^2/cm^2)*/ # UCRIT if (not self.is_set('ucrit')) and (self.vmax != None) and \ (self.u0 != None): self.ucrit = self.vmax / (self.u0 * 1.e-4) # E0: no need for anything since theta != 0 triggers simple # mobility model # Initialize more variables self._weff = self.w + self.dw self._leff = self.l + self.dl # sqrt gate area self._sga = np.sqrt(self.np * self._weff * self.ns * self._leff) self._gammaa = self.gamma + self.agamma / self._sga # Clip to zero if negative if self._gammaa < 0.: self._gammaa = 0. cEps = 0.001936 # was: 4. * pow(22.e-3, 2) cA = 0.028 xi = cA * (10. * self._leff / self.lk - 1.) self._deltavRSCE = 2. * self.q0 / \ (self.cox * pow(1. + 0.5 * (xi + np.sqrt(xi*xi + cEps)), 2)) # constants used in eval_c1qs() self._lc = np.sqrt(const.epSi * self.xj / self.cox) self._lmin = self.ns * self._leff / 10. self._Cox = self.cox * self.np * self._weff * self.ns * self._leff if not thermal: # Calculate temperature-dependent variables self.set_temp_vars(self.temp)
def process_params(self, thermal = False): # Called once the external terminals have been connected and # the non-default parameters have been set. Make sanity checks # here. Internal terminals/devices should also be defined # here. Raise cir.CircuitError if a fatal error is found. ad.delete_tape(self) if self.type == 'n': self._tf = 1. elif self.type == 'p': self._tf = -1. # Change parameter default values if not self.is_set('u0'): self.u0 = 250 else: raise cir.CircuitError( '{0}: unrecognized type: {1}. Valid types are "n" or "p"'.format(self.instanceName, self.type)) # Nominal abs temperature self._Tn = const.T0 + self.tnom # Nominal Thermal voltage self._Vtn = const.k * self._Tn / const.q self.factor1 = np.sqrt(const.epSi / const.epOx * self.tox) Eg0 = 1.16 - 7.02e-4 * (self._Tn**2) / (self._Tn + 1108.0) ni = 1.45e10 * (self._Tn / 300.15) * np.sqrt(self._Tn / 300.15) \ * np.exp(21.5565981 - Eg0 / (2. * self._Vtn)) #self.esi = 11.7 * const.epsilon0 (replaced by const.epSi) self.ldrn = self.l self.wdrn = self.w t0 = pow(self.ldrn, self.lln) t1 = pow(self.wdrn, self.lwn) tmp1 = self.ll / t0 + self.lw / t1 + self.lwl / (t0 * t1) self.dl = self.lint + tmp1 #tmp2 = llc / t0 + lwc / t1 + lwlc / (t0 * t1) #self.dlc = dlc + tmp2 # ??? t2 = pow(self.ldrn, self.wln) t3 = pow(self.wdrn, self.wwn) tmp3 = self.wl / t2 + self.ww / t3 + self.wwl / (t2 * t3) self.dw = self.wint + tmp3 #tmp4 = wlc / t2 + wwc / t3 + wwlc / (t2 * t3) #self.dwc = dwc + tmp4 # ??? self.leff = self.l - 2.0 * self.dl self.weff = self.w - 2.0 * self.dw self.AbulkCVfactor = (1. + pow(self.clc/self.leff, self.cle)) #self.leffCV = l - 2.0 * dlc #self.t11 = leffCV * leffCV # was epOx = 3.453133e-11 self.cox = const.epOx / self.tox self.phi = 2. * self._Vtn * np.log(self.nch / ni) self.sqrtPhi = np.sqrt(self.phi) self.phis3 = self.sqrtPhi * self.phi self.Xdep0 = np.sqrt(2. * const.epSi / (const.q * self.nch * 1e6)) * self.sqrtPhi self.litl = np.sqrt(3. * self.xj * self.tox) self.vbi = self._Vtn * np.log(1.0e20 * self.nch / (ni**2)) self.cdep0 = np.sqrt(const.q * const.epSi * self.nch * 1e6 / 2. / self.phi) if not self.is_set('toxm'): self.toxm = self.tox if not self.is_set('dsub'): self.dsub = self.drout self.ldeb = np.sqrt(const.epSi * self._Vtn / (const.q * self.nch * 1e6)) / 3. #import pdb; pdb.set_trace() if (self.k1enable != 0.) and \ not (self.is_set('k1') or self.is_set('k2')): vbx = self.phi - 7.7348e-4 * self.nch * self.xt**2 # From ngspice vbx = -abs(vbx) Vbm = -abs(self.vbm) gamma1 = 5.753e-12 * np.sqrt(self.nch) / self.cox gamma2 = 5.753e-12 * np.sqrt(self.nsub) / self.cox T0 = gamma1 - gamma2 T1 = np.sqrt(self.phi - vbx) - self.sqrtPhi T2 = np.sqrt(self.phi * (self.phi - Vbm)) - self.phi self._k2 = T0 * T1 / (2. * T2 + Vbm) self._k1 = gamma2 - 2. * self._k2 * np.sqrt(self.phi - Vbm) # print self._k1, self._k2 else: self._k1 = self.k1 self._k2 = self.k2 if not self.is_set('vth0'): self._vth0 = self.vfb + self.phi + self._k1 * self.sqrtPhi else: self._vth0 = abs(self.vth0) self.k1ox = self._k1 * self.tox / self.toxm self.k2ox = self._k2 * self.tox / self.toxm t1 = np.sqrt(const.epSi / const.epOx * self.tox * self.Xdep0) t0 = ad.safe_exp(-0.5 * self.dsub * self.leff / t1) self.theta0vb0 = (t0 + 2.0 * t0**2) # From freeda, ngspice: self._Tox = 1e8 * self.tox #Calculation of vbsc(Vbc) and Vbseff if self._k2 < 0.: self.vbsc = .9 * (self.phi - (.5 * self._k1 / self._k2)**2) if self.vbsc > -3.: self.vbsc = -3. elif self.vbsc < -30.: self.vbsc = -30. else: self.vbsc = -30. if self.u0 > 1.: self._u0 = self.u0 * 1e-4 else: self._u0 = self.u0 if not thermal: # Calculate temperature-dependent variables self.set_temp_vars(self.temp)
def process_params(self, thermal=False): # Remove tape if present ad.delete_tape(self) if thermal: extraTerms = 2 else: extraTerms = 0 # First check external connections if self.numTerms == 3 + extraTerms: self.__addCBjtn = False elif self.numTerms == 4 + extraTerms: self.__addCBjtn = True else: raise cir.CircuitError('{0}: Wrong number of connections. \ Can only be {1} or {2}, {3} found.'.format(self.instanceName, 3 + extraTerms, 4 + extraTerms, self.numTerms)) # Remove internal terminals self.clean_internal_terms() # Tell autothermal to re-generate thermal ports self.__addThermalPorts = True extraVCCS = list() if self.re != None: # Add et node and translate port descriptions self._et = self.add_internal_term('et', 'V') extraVCCS += [((2, self._et), (2, self._et), self.area / self.re)] if self.rc != None: # Add ct node and translate port descriptions self._ct = self.add_internal_term('ct', 'V') extraVCCS += [((0, self._ct), (0, self._ct), self.area / self.rc)] # Process parameters from intrinsic device: emitter and # collector terminals are already substituted. IBJT.process_params(self) # Calculate variables in junction self.cbjtn.process_params(self.iss, self.ns, self.fc, self.cjs, self.vjs, self.mjs, self.xti, self.eg, self.Tnomabs) # Add RE, RC resistors (if any) self.linearVCCS += extraVCCS if self.__addCBjtn: # Add bulk-collector junction self.__bccn = len(self.controlPorts) self.__bcon = len(self.csOutPorts) self.controlPorts.append((3, self._ct)) self.csOutPorts.append((3, self._ct)) if self.cjs != 0.: self.qsOutPorts.append((3, self._ct)) # Initial guess for input ports: try: if len(self.vPortGuess) < len(self.controlPorts): self.vPortGuess = np.concatenate( (self.vPortGuess, [1]), axis=0) except AttributeError: # Ignore if vPortGuess not provided pass # Adjust temperature self.set_temp_vars(self.temp)