Qucs-core  0.0.19
device.cpp
Go to the documentation of this file.
00001 /*
00002  * device.cpp - device class implementation
00003  *
00004  * Copyright (C) 2004, 2005, 2006 Stefan Jahn <stefan@lkcc.org>
00005  *
00006  * This is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2, or (at your option)
00009  * any later version.
00010  *
00011  * This software is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this package; see the file COPYING.  If not, write to
00018  * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020  *
00021  * $Id$
00022  *
00023  */
00024 
00025 #if HAVE_CONFIG_H
00026 # include <config.h>
00027 #endif
00028 
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <cmath>
00032 #include <algorithm>
00033 
00034 #include "complex.h"
00035 #include "object.h"
00036 #include "node.h"
00037 #include "circuit.h"
00038 #include "net.h"
00039 #include "constants.h"
00040 #include "device.h"
00041 #include "netdefs.h"
00042 #include "resistor.h"
00043 #include "capacitor.h"
00044 
00045 using namespace qucs;
00046 using namespace qucs::device;
00047 
00048 /* This function can be used to create an extra resistor circuit.  If
00049    the 'res' argument is NULL then the new circuit is created, the
00050    nodes get re-arranged and it is inserted into the given
00051    netlist. The given arguments can be explained as follows.
00052    base:     calling circuit (this)
00053    res:      additional resistor circuit (can be NULL)
00054    c:        name of the additional circuit
00055    n:        name of the inserted (internal) node
00056    internal: number of new internal node (the original external node) */
00057 circuit * device::splitResistor (circuit * base, circuit * res,
00058                                  const char * c, const char * n,
00059                                  int internal) {
00060   if (res == NULL) {
00061     res = new resistor ();
00062     const std::string &name = circuit::createInternal (c, base->getName ());
00063     const std::string &node = circuit::createInternal (n, base->getName ());
00064     res->setName (name);
00065     res->setNode (0, base->getNode(internal)->getName ());
00066     res->setNode (1, node, 1);
00067     base->getNet()->insertCircuit (res);
00068   }
00069   base->setNode (internal, res->getNode(1)->getName (), 1);
00070   return res;
00071 }
00072 
00073 /* This function is the counterpart of the above routine.  It removes
00074    the resistor circuit from the netlist and re-assigns the original
00075    node. */
00076 void device::disableResistor (circuit * base, circuit * res, int internal) {
00077   if (res != NULL) {
00078     base->getNet()->removeCircuit (res, 0);
00079     base->setNode (internal, res->getNode(1)->getName (), 0);
00080   }
00081 }
00082 
00083 /* This function creates a new capacitor circuit if the given one is
00084    not NULL.  The new circuit is connected between the given nodes and
00085    a name is applied based upon the parents (base) name and the given
00086    name 'c'.  The circuit is then put into the netlist. */
00087 circuit * device::splitCapacitor (circuit * base, circuit * cap,
00088                                   const char * c, node * n1, node * n2) {
00089   if (cap == NULL) {
00090     cap = new capacitor ();
00091     const std::string &name = circuit::createInternal (c, base->getName ());
00092     cap->setName (name);
00093     cap->setNode (0, n1->getName ());
00094     cap->setNode (1, n2->getName ());
00095   }
00096   base->getNet()->insertCircuit (cap);
00097   return cap;
00098 }
00099 
00100 // The function removes the given capacitor circuit from the netlist.
00101 void device::disableCapacitor (circuit * base, circuit * cap) {
00102   if (cap != NULL) {
00103     base->getNet()->removeCircuit (cap, 0);
00104   }
00105 }
00106 
00107 /* This function checks whether the given circuit object exists and is
00108    chained within the current netlist.  It returns non-zero if so and
00109    zero otherwise. */
00110 int device::deviceEnabled (circuit * c) {
00111   if (c != NULL && c->isEnabled ())
00112     return 1;
00113   return 0;
00114 }
00115 
00116 /* The function limits the forward pn-voltage for each DC iteration in
00117    order to avoid numerical overflows and thereby improve the
00118    convergence. */
00119 nr_double_t device::pnVoltage (nr_double_t Ud, nr_double_t Uold,
00120                                nr_double_t Ut, nr_double_t Ucrit) {
00121   nr_double_t arg;
00122   if (Ud > Ucrit && fabs (Ud - Uold) > 2 * Ut) {
00123     if (Uold > 0) {
00124       arg = (Ud - Uold) / Ut;
00125       if (arg > 0)
00126         Ud = Uold + Ut * (2 + log (arg - 2));
00127       else
00128         Ud = Uold - Ut * (2 + log (2 - arg));
00129     }
00130     else Ud = Uold < 0 ? Ut * log (Ud / Ut) : Ucrit;
00131   }
00132   else {
00133     if (Ud < 0) {
00134       arg = Uold > 0 ? -1 - Uold : 2 * Uold - 1;
00135       if (Ud < arg) Ud = arg;
00136     }
00137   }
00138   return Ud;
00139 }
00140 
00141 // Computes current and its derivative for a MOS pn-junction.
00142 void device::pnJunctionMOS (nr_double_t Upn, nr_double_t Iss, nr_double_t Ute,
00143                             nr_double_t& I, nr_double_t& g) {
00144   if (Upn <= 0) {
00145     g = Iss / Ute;
00146     I = g * Upn;
00147   }
00148   else {
00149     nr_double_t e = exp (std::min (Upn / Ute, 709.0));
00150     I = Iss * (e - 1);
00151     g = Iss * e / Ute;
00152   }
00153 }
00154 
00155 // Computes current and its derivative for a bipolar pn-junction.
00156 void device::pnJunctionBIP (nr_double_t Upn, nr_double_t Iss, nr_double_t Ute,
00157                             nr_double_t& I, nr_double_t& g) {
00158   if (Upn < -3 * Ute) {
00159     nr_double_t a = 3 * Ute / (Upn * euler);
00160     a = cubic (a);
00161     I = -Iss * (1 + a);
00162     g = +Iss * 3 * a / Upn;
00163   }
00164   else {
00165     nr_double_t e = exp (std::min (Upn / Ute, 709.0));
00166     I = Iss * (e - 1);
00167     g = Iss * e / Ute;
00168   }
00169 }
00170 
00171 // The function computes the exponential pn-junction current.
00172 nr_double_t
00173 device::pnCurrent (nr_double_t Upn, nr_double_t Iss, nr_double_t Ute) {
00174   return Iss * (exp (std::min (Upn / Ute, 709.0)) - 1);
00175 }
00176 
00177 // The function computes the exponential pn-junction current's derivative.
00178 nr_double_t
00179 device::pnConductance (nr_double_t Upn, nr_double_t Iss, nr_double_t Ute) {
00180   return Iss * exp (std::min (Upn / Ute, 709.0)) / Ute;
00181 }
00182 
00183 // Computes pn-junction depletion capacitance.
00184 nr_double_t
00185 device::pnCapacitance (nr_double_t Uj, nr_double_t Cj, nr_double_t Vj,
00186                        nr_double_t Mj, nr_double_t Fc) {
00187   nr_double_t c;
00188   if (Uj <= Fc * Vj)
00189     c = Cj * exp (-Mj * log (1 - Uj / Vj));
00190   else
00191     c = Cj * exp (-Mj * log (1 - Fc)) *
00192       (1 + Mj * (Uj - Fc * Vj) / Vj / (1 - Fc));
00193   return c;
00194 }
00195 
00196 // Computes pn-junction depletion charge.
00197 nr_double_t device::pnCharge (nr_double_t Uj, nr_double_t Cj, nr_double_t Vj,
00198                               nr_double_t Mj, nr_double_t Fc) {
00199   nr_double_t q, a, b;
00200   if (Uj <= Fc * Vj) {
00201     a = 1 - Uj / Vj;
00202     b = exp ((1 - Mj) * log (a));
00203     q = Cj * Vj / (1 - Mj) * (1 - b);
00204   }
00205   else {
00206 #if 0
00207     a = 1 - Fc;
00208     b = exp ((1 - Mj) * log (a));
00209     a = exp ((1 + Mj) * log (a));
00210     nr_double_t c = 1 - Fc * (1 + Mj);
00211     nr_double_t d = Fc * Vj;
00212     nr_double_t e = Vj * (1 - b) / (1 - Mj);
00213     q = Cj * (e + (c * (Uj - d) + Mj / 2 / Vj * (sqr (Uj) - sqr (d))) / a);
00214 #else /* this variant is numerically more stable */
00215     a = 1 - Fc;
00216     b = exp (-Mj * log (a));
00217     nr_double_t f = Fc * Vj;
00218     nr_double_t c = Cj * (1 - Fc * (1 + Mj)) * b / a;
00219     nr_double_t d = Cj * Mj * b / a / Vj;
00220     nr_double_t e = Cj * Vj * (1 - a * b) / (1 - Mj) - d / 2 * f * f - f * c;
00221     q = e + Uj * (c + Uj * d / 2);
00222 #endif
00223   }
00224   return q;
00225 }
00226 
00227 /* This function computes the pn-junction depletion capacitance with
00228    no linearization factor given. */
00229 nr_double_t
00230 device::pnCapacitance (nr_double_t Uj, nr_double_t Cj, nr_double_t Vj,
00231                        nr_double_t Mj) {
00232   nr_double_t c;
00233   if (Uj <= 0)
00234     c = Cj * exp (-Mj * log (1 - Uj / Vj));
00235   else
00236     c = Cj * (1 + Mj * Uj / Vj);
00237   return c;
00238 }
00239 
00240 /* This function computes the pn-junction depletion charge with no
00241    linearization factor given. */
00242 nr_double_t device::pnCharge (nr_double_t Uj, nr_double_t Cj, nr_double_t Vj,
00243                               nr_double_t Mj) {
00244   nr_double_t q;
00245   if (Uj <= 0)
00246     q = Cj * Vj / (1 - Mj) * (1 - exp ((1 - Mj) * log (1 - Uj / Vj)));
00247   else
00248     q = Cj * Uj * (1 + Mj * Uj / 2 / Vj);
00249   return q;
00250 }
00251 
00252 // Compute critical voltage of pn-junction.
00253 nr_double_t device::pnCriticalVoltage (nr_double_t Iss, nr_double_t Ute) {
00254   return Ute * log (Ute / sqrt2 / Iss);
00255 }
00256 
00257 /* The function limits the forward fet-voltage for each DC iteration
00258    in order to avoid numerical overflows and thereby improve the
00259    convergence. */
00260 nr_double_t
00261 device::fetVoltage (nr_double_t Ufet, nr_double_t Uold, nr_double_t Uth) {
00262   nr_double_t Utsthi = fabs (2 * (Uold - Uth)) + 2.0;
00263   nr_double_t Utstlo = Utsthi / 2;
00264   nr_double_t Utox   = Uth + 3.5;
00265   nr_double_t DeltaU = Ufet - Uold;
00266 
00267   if (Uold >= Uth) { /* FET is on */
00268     if (Uold >= Utox) {
00269       if (DeltaU <= 0) { /* going off */
00270         if (Ufet >= Utox) {
00271           if (-DeltaU > Utstlo) {
00272             Ufet = Uold - Utstlo;
00273           }
00274         } else {
00275           Ufet = std::max (Ufet, Uth + 2);
00276         }
00277       } else { /* staying on */
00278         if (DeltaU >= Utsthi) {
00279           Ufet = Uold + Utsthi;
00280         }
00281       }
00282     } else { /* middle region */
00283       if (DeltaU <= 0) { /* decreasing */
00284         Ufet = std::max (Ufet, Uth - 0.5);
00285       } else { /* increasing */
00286         Ufet = std::min (Ufet, Uth + 4);
00287       }
00288     }
00289   } else { /* FET is off */
00290     if (DeltaU <= 0) { /* staying off */
00291       if (-DeltaU > Utsthi) {
00292         Ufet = Uold - Utsthi;
00293       }
00294     } else { /* going on */
00295       if (Ufet <= Uth + 0.5) {
00296         if (DeltaU > Utstlo) {
00297           Ufet = Uold + Utstlo;
00298         }
00299       } else {
00300         Ufet = Uth + 0.5;
00301       }
00302     }
00303   }
00304   return Ufet;
00305 }
00306 
00307 /* The function limits the drain-source voltage for each DC iteration
00308    in order to avoid numerical overflows and thereby improve the
00309    convergence. */
00310 nr_double_t device::fetVoltageDS (nr_double_t Ufet, nr_double_t Uold) {
00311   if (Uold >= 3.5) {
00312     if (Ufet > Uold) {
00313       Ufet = std::min (Ufet, 3 * Uold + 2);
00314     } else if (Ufet < 3.5) {
00315       Ufet = std::max (Ufet, 2.0);
00316     }
00317   } else {
00318     if (Ufet > Uold) {
00319       Ufet = std::min (Ufet, 4.0);
00320     } else {
00321       Ufet = std::max (Ufet, -0.5);
00322     }
00323   }
00324   return Ufet;
00325 }
00326 
00327 /* This function calculates the overlap capacitance for MOS based upon
00328    the given voltages, surface potential and the zero-bias oxide
00329    capacitance. */
00330 void
00331 device::fetCapacitanceMeyer (nr_double_t Ugs, nr_double_t Ugd, nr_double_t Uth,
00332                              nr_double_t Udsat, nr_double_t Phi,
00333                              nr_double_t Cox, nr_double_t& Cgs,
00334                              nr_double_t& Cgd, nr_double_t& Cgb) {
00335 
00336   nr_double_t Utst = Ugs - Uth;
00337   if (Utst <= -Phi) { // cutoff regions
00338     Cgb = Cox;
00339     Cgs = 0;
00340     Cgd = 0;
00341   } else if (Utst <= -Phi / 2) {
00342     Cgb = -Utst * Cox / Phi;
00343     Cgs = 0;
00344     Cgd = 0;
00345   } else if (Utst <= 0) { // depletion region
00346     Cgb = -Utst * Cox / Phi;
00347     Cgs = Utst * Cox * 4 / 3 / Phi + 2 * Cox / 3;
00348     Cgd = 0;
00349   } else {
00350     Cgb = 0;
00351     nr_double_t Uds = Ugs - Ugd;
00352     if (Udsat <= Uds) { // saturation region
00353       Cgs = 2 * Cox / 3;
00354       Cgd = 0;
00355     } else { // linear region
00356       nr_double_t Sqr1 = sqr (Udsat - Uds);
00357       nr_double_t Sqr2 = sqr (2 * Udsat - Uds);
00358       Cgs = Cox * (1 - Sqr1 / Sqr2) * 2 / 3;
00359       Cgd = Cox * (1 - Udsat * Udsat / Sqr2) * 2 / 3;
00360     }
00361   }
00362 }
00363 
00364 // Computes temperature dependency of energy bandgap.
00365 nr_double_t device::Egap (nr_double_t T, nr_double_t Eg0) {
00366   nr_double_t a = 7.02e-4;
00367   nr_double_t b = 1108;
00368   return Eg0 - (a * sqr (T)) / (T + b);
00369 }
00370 
00371 // Computes temperature dependency of intrinsic density.
00372 nr_double_t device::intrinsicDensity (nr_double_t T, nr_double_t Eg0) {
00373   nr_double_t TR = 300.00;
00374   nr_double_t E1 = Egap (TR, Eg0);
00375   nr_double_t E2 = Egap (T, Eg0);
00376   nr_double_t NI = NiSi / 1e6;
00377   return  NI * exp (1.5 * log (T / TR) + (E1 / TR - E2 / T) / kBoverQ / 2);
00378 }
00379 
00380 // Calculates temperature dependence for saturation current.
00381 nr_double_t
00382 device::pnCurrent_T (nr_double_t T1, nr_double_t T2, nr_double_t Is,
00383                      nr_double_t Eg, nr_double_t N,  nr_double_t Xti) {
00384   nr_double_t Vt, TR;
00385   TR = T2 / T1;
00386   Vt = T2 * kBoverQ;
00387   return Is * exp (Xti / N * log (TR) - Eg / N / Vt * (1 - TR));
00388 }
00389 
00390 // Calculates temperature dependence for junction potential.
00391 nr_double_t
00392 device::pnPotential_T (nr_double_t T1, nr_double_t T2, nr_double_t Vj,
00393                        nr_double_t Eg0) {
00394   nr_double_t Vt, TR, E1, E2;
00395   TR = T2 / T1;
00396   E1 = Egap (T1, Eg0);
00397   E2 = Egap (T2, Eg0);
00398   Vt = T2 * kBoverQ;
00399   return TR * Vj - 3 * Vt * log (TR) - (TR * E1 - E2);
00400 }
00401 
00402 // Calculates temperature dependence for junction capacitance.
00403 nr_double_t
00404 device::pnCapacitance_T (nr_double_t T1, nr_double_t T2, nr_double_t M,
00405                          nr_double_t VR, nr_double_t Cj) {
00406   return Cj * pnCapacitance_F (T1, T2, M, VR);
00407 }
00408 
00409 // Calculates temperature dependence for junction capacitance.
00410 nr_double_t
00411 device::pnCapacitance_F (nr_double_t T1, nr_double_t T2, nr_double_t M,
00412                          nr_double_t VR) {
00413   nr_double_t DT = T2 - T1;
00414   return 1 + M * (4e-4 * DT - VR + 1);
00415 }