My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
diode.cpp
Go to the documentation of this file.
1 /*
2  * diode.cpp - diode class implementation
3  *
4  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Stefan Jahn <stefan@lkcc.org>
5  *
6  * This is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This software is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this package; see the file COPYING. If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  * $Id: diode.cpp 1825 2011-03-11 20:42:14Z ela $
22  *
23  */
24 
25 #if HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28 
29 #include "component.h"
30 #include "device.h"
31 #include "devstates.h"
32 #include "diode.h"
33 
34 #define NODE_C 0 /* cathode node */
35 #define NODE_A 1 /* anode node */
36 
37 #define StateVars 1 // state variables
38 
39 // state variable indices
40 #define _UdPrev 0
41 
42 // state variable shortcuts
43 #define UdPrev deviceVar (_UdPrev)
44 
45 using namespace device;
46 
47 // Constructor for the diode.
48 diode::diode () : circuit (2) {
49  rs = NULL;
50  type = CIR_DIODE;
51 }
52 
53 // Callback for S-parameter analysis.
54 void diode::calcSP (nr_double_t frequency) {
55  nr_double_t gd = getOperatingPoint ("gd");
56  nr_double_t Cd = getOperatingPoint ("Cd");
57  nr_complex_t y = 2 * z0 * rect (gd, Cd * 2.0 * M_PI * frequency);
58  setS (NODE_C, NODE_C, 1.0 / (1.0 + y));
59  setS (NODE_A, NODE_A, 1.0 / (1.0 + y));
60  setS (NODE_C, NODE_A, y / (1.0 + y));
61  setS (NODE_A, NODE_C, y / (1.0 + y));
62 }
63 
64 // Callback for S-parameter noise analysis.
65 void diode::calcNoiseSP (nr_double_t frequency) {
66 #if MICHAEL /* shot noise only */
67  nr_double_t Id = getOperatingPoint ("Id");
68  nr_double_t Is = getPropertyDouble ("Is") + getPropertyDouble ("Isr");
69 
70  // adjust shot noise current if necessary
71  if (Id < -Is) Id = -Is;
72 
73  nr_double_t gd = getOperatingPoint ("gd");
74  nr_double_t Cd = getOperatingPoint ("Cd");
75 
76  nr_complex_t y = rect (gd, Cd * 2.0 * M_PI * frequency);
77  nr_complex_t f = 2 * z0 * (Id + 2 * Is) / norm (2 * z0 * y + 1) * QoverkB / T0;
78  setN (NODE_C, NODE_C, +f); setN (NODE_A, NODE_A, +f);
79  setN (NODE_C, NODE_A, -f); setN (NODE_A, NODE_C, -f);
80 #else
81  setMatrixN (cytocs (calcMatrixCy (frequency) * z0, getMatrixS ()));
82 #endif
83 }
84 
85 // Computes noise correlation matrix Cy.
86 matrix diode::calcMatrixCy (nr_double_t frequency) {
87  // fetch computed operating points
88  nr_double_t Id = getOperatingPoint ("Id");
89  nr_double_t Is = getPropertyDouble ("Is") + getPropertyDouble ("Isr");
90 
91  // adjust shot noise current if necessary
92  if (Id < -Is) Id = -Is;
93 
94  nr_double_t Kf = getPropertyDouble ("Kf");
95  nr_double_t Af = getPropertyDouble ("Af");
96  nr_double_t Ffe = getPropertyDouble ("Ffe");
97 
98  // build noise current correlation matrix
99  matrix cy (2);
100  nr_double_t i = 2 * (Id + 2 * Is) * QoverkB / T0 + // shot noise
101  Kf * pow (fabs (Id), Af) / pow (frequency, Ffe) / kB / T0; // flicker noise
102  cy.set (NODE_C, NODE_C, +i); cy.set (NODE_A, NODE_A, +i);
103  cy.set (NODE_A, NODE_C, -i); cy.set (NODE_C, NODE_A, -i);
104  return cy;
105 }
106 
107 // Initializes the diode model including temperature and area effects.
108 void diode::initModel (void) {
109  // fetch necessary device properties
110  nr_double_t T = getPropertyDouble ("Temp");
111  nr_double_t Tn = getPropertyDouble ("Tnom");
112  nr_double_t A = getPropertyDouble ("Area");
113 
114  // compute Is temperature and area dependency
115  nr_double_t Is = getPropertyDouble ("Is");
116  nr_double_t N = getPropertyDouble ("N");
117  nr_double_t Xti = getPropertyDouble ("Xti");
118  nr_double_t Eg = getPropertyDouble ("Eg");
119  nr_double_t T1, T2;
120  T2 = kelvin (T);
121  T1 = kelvin (Tn);
122  Is = pnCurrent_T (T1, T2, Is, Eg, N, Xti);
123  setScaledProperty ("Is", Is * A);
124 
125  // compute Isr temperature and area dependency
126  nr_double_t Isr = getPropertyDouble ("Isr");
127  nr_double_t Nr = getPropertyDouble ("Nr");
128  Isr = pnCurrent_T (T1, T2, Isr, Eg, Nr, Xti);
129  setScaledProperty ("Isr", Isr * A);
130 
131  // check unphysical parameters
132  if (Nr < 1.0) {
133  logprint (LOG_ERROR, "WARNING: Unphysical model parameter Nr = %g in "
134  "diode `%s'\n", Nr, getName ());
135  }
136  if (N < 1.0) {
137  logprint (LOG_ERROR, "WARNING: Unphysical model parameter N = %g in "
138  "diode `%s'\n", N, getName ());
139  }
140 
141  // compute Vj temperature dependency
142  nr_double_t Vj = getPropertyDouble ("Vj");
143  nr_double_t VjT;
144  VjT = pnPotential_T (T1,T2, Vj);
145  setScaledProperty ("Vj", VjT);
146 
147  // compute Cj0 temperature and area dependency
148  nr_double_t Cj0 = getPropertyDouble ("Cj0");
149  nr_double_t M = getPropertyDouble ("M");
150  Cj0 = pnCapacitance_T (T1, T2, M, VjT / Vj, Cj0);
151  setScaledProperty ("Cj0", Cj0 * A);
152 
153  // check unphysical parameters
154  if (M > 1.0) {
155  logprint (LOG_ERROR, "WARNING: Unphysical model parameter M = %g in "
156  "Diode `%s'\n", M, getName ());
157  }
158 
159  // compute Bv temperature dependency
160  nr_double_t Bv = getPropertyDouble ("Bv");
161  nr_double_t Tbv = getPropertyDouble ("Tbv");
162  nr_double_t DT = T2 - T1;
163  Bv = Bv - Tbv * DT;
164  setScaledProperty ("Bv", Bv);
165 
166  // compute Tt temperature dependency
167  nr_double_t Tt = getPropertyDouble ("Tt");
168  nr_double_t Ttt1 = getPropertyDouble ("Ttt1");
169  nr_double_t Ttt2 = getPropertyDouble ("Ttt2");
170  Tt = Tt * (1 + Ttt1 * DT + Ttt2 * DT * DT);
171  setScaledProperty ("Tt", Tt);
172 
173  // compute M temperature dependency
174  nr_double_t Tm1 = getPropertyDouble ("Tm1");
175  nr_double_t Tm2 = getPropertyDouble ("Tm2");
176  M = M * (1 + Tm1 * DT + Tm2 * DT * DT);
177  setScaledProperty ("M", M);
178 
179  // compute Rs temperature and area dependency
180  nr_double_t Rs = getPropertyDouble ("Rs");
181  nr_double_t Trs = getPropertyDouble ("Trs");
182  Rs = Rs * (1 + Trs * DT);
183  setScaledProperty ("Rs", Rs / A);
184 }
185 
186 // Prepares DC (i.e. HB) analysis.
187 void diode::prepareDC (void) {
188  // allocate MNA matrices
189  allocMatrixMNA ();
190 
191  // initialize scalability
192  initModel ();
193 
194  // initialize starting values
195  Ud = real (getV (NODE_A) - getV (NODE_C));
196  for (int i = 0; i < deviceStates (); i++) {
197  deviceState (i);
198  UdPrev = Ud;
199  }
200 
201  // get device temperature
202  nr_double_t T = getPropertyDouble ("Temp");
203 
204  // possibly insert series resistance
205  nr_double_t Rs = getScaledProperty ("Rs");
206  if (Rs != 0.0) {
207  // create additional circuit if necessary and reassign nodes
208  rs = splitResistor (this, rs, "Rs", "anode", NODE_A);
209  rs->setProperty ("Temp", T);
210  rs->setProperty ("R", Rs);
211  rs->setProperty ("Controlled", getName ());
212  rs->initDC ();
213  }
214  // no series resistance
215  else {
216  disableResistor (this, rs, NODE_A);
217  }
218 
219  // calculate actual breakdown voltage
220  Bv = getScaledProperty ("Bv");
221  if (Bv != 0) {
222  nr_double_t Ibv, Is, tol, Ut, Xbv, Xibv;
223  Ibv = getPropertyDouble ("Ibv");
224  Is = getScaledProperty ("Is");
225  Ut = kelvin (T) * kBoverQ;
226  // adjust very small breakdown currents
227  if (Ibv < Is * Bv / Ut) {
228  Ibv = Is * Bv / Ut;
229  Xbv = Bv;
230  logprint (LOG_ERROR, "WARNING: Increased breakdown current to %g to "
231  "match the saturation current %g\n", Ibv, Is);
232  }
233  // fit reverse and forward regions
234  else {
235  int good = 0;
236  tol = 1e-3 * Ibv;
237  Xbv = Bv - Ut * log (1 + Ibv / Is);
238  for (int i = 0; i < 25 ; i++) {
239  Xbv = Bv - Ut * log (Ibv / Is + 1 - Xbv / Ut);
240  Xibv = Is * (exp ((Bv - Xbv) / Ut) - 1 + Xbv / Ut);
241  if (fabs (Xibv - Ibv) < tol) {
242  Bv = Xbv;
243  good = 1;
244  break;
245  }
246  }
247  if (!good) {
248  logprint (LOG_ERROR, "WARNING: Unable to fit reverse and forward "
249  "diode regions using Bv=%g and Ibv=%g\n", Bv, Ibv);
250  }
251  }
252  }
253 }
254 
255 // Callback for initializing the DC analysis.
256 void diode::initDC (void) {
257  deviceStates (StateVars, 1);
258  doHB = false;
259  prepareDC ();
260 }
261 
262 // Callback for restarting the DC analysis.
263 void diode::restartDC (void) {
264  // apply starting value to previous iteration value
265  UdPrev = real (getV (NODE_A) - getV (NODE_C));
266 }
267 
268 // Callback for DC analysis.
269 void diode::calcDC (void) {
270  // get device properties
271  nr_double_t Is = getScaledProperty ("Is");
272  nr_double_t N = getPropertyDouble ("N");
273  nr_double_t Isr = getScaledProperty ("Isr");
274  nr_double_t Nr = getPropertyDouble ("Nr");
275  nr_double_t Ikf = getPropertyDouble ("Ikf");
276  nr_double_t T = getPropertyDouble ("Temp");
277 
278  nr_double_t Ut, Ieq, Ucrit, gtiny;
279 
280  T = kelvin (T);
281  Ut = T * kBoverQ;
282  Ud = real (getV (NODE_A) - getV (NODE_C));
283 
284  // critical voltage necessary for bad start values
285  Ucrit = pnCriticalVoltage (Is, N * Ut);
286  if (Bv != 0 && Ud < MIN (0, -Bv + 10 * N * Ut)) {
287  nr_double_t V = -(Ud + Bv);
288  V = pnVoltage (V, -(UdPrev + Bv), Ut * N, Ucrit);
289  Ud = -(V + Bv);
290  }
291  else {
292  Ud = pnVoltage (Ud, UdPrev, Ut * N, Ucrit);
293  }
294  UdPrev = Ud;
295 
296  // tiny derivative for little junction voltage
297  gtiny = (Ud < - 10 * Ut * N && Bv != 0) ? (Is + Isr) : 0;
298 
299  if (Ud >= -3 * N * Ut) { // forward region
300  gd = pnConductance (Ud, Is, Ut * N) + pnConductance (Ud, Isr, Ut * Nr);
301  Id = pnCurrent (Ud, Is, Ut * N) + pnCurrent (Ud, Isr, Ut * Nr);
302  }
303  else if (Bv == 0 || Ud >= -Bv) { // reverse region
304  nr_double_t a = 3 * N * Ut / (Ud * M_E);
305  a = cubic (a);
306  Id = -Is * (1 + a);
307  gd = +Is * 3 * a / Ud;
308  }
309  else { // middle region
310  nr_double_t a = exp (-(Bv + Ud) / N / Ut);
311  Id = -Is * a;
312  gd = +Is * a / Ut / N;
313  }
314 
315  // knee current calculations
316  if (Ikf != 0.0) {
317  nr_double_t a = Ikf / (Ikf + Id);
318  gd *= 0.5 * (2 - Id * a / Ikf) * sqrt (a);
319  Id *= sqrt (a);
320  }
321 
322  Id += gtiny * Ud;
323  gd += gtiny;
324 
325  // HB simulation
326  if (doHB) {
327  Ieq = Id;
328  setGV (NODE_C, -gd * Ud);
329  setGV (NODE_A, +gd * Ud);
330  }
331  // DC and transient simulation
332  else {
333  Ieq = Id - Ud * gd;
334  }
335 
336  // fill in I-Vector
337  setI (NODE_C, +Ieq);
338  setI (NODE_A, -Ieq);
339 
340  // fill in G-Matrix
341  setY (NODE_C, NODE_C, +gd); setY (NODE_A, NODE_A, +gd);
342  setY (NODE_C, NODE_A, -gd); setY (NODE_A, NODE_C, -gd);
343 }
344 
345 // Saves operating points (voltages).
347  nr_double_t Vd = real (getV (NODE_A) - getV (NODE_C));
348  setOperatingPoint ("Vd", Vd);
349 }
350 
351 // Loads operating points (voltages).
353  Ud = getOperatingPoint ("Vd");
354 }
355 
356 // Calculates and saves operating points.
358 
359  // load operating points
361 
362  // get necessary properties
363  nr_double_t M = getScaledProperty ("M");
364  nr_double_t Cj0 = getScaledProperty ("Cj0");
365  nr_double_t Vj = getScaledProperty ("Vj");
366  nr_double_t Fc = getPropertyDouble ("Fc");
367  nr_double_t Cp = getPropertyDouble ("Cp");
368  nr_double_t Tt = getScaledProperty ("Tt");
369 
370  // calculate capacitances and charges
371  nr_double_t Cd;
372  Cd = pnCapacitance (Ud, Cj0, Vj, M, Fc) + Tt * gd + Cp;
373  Qd = pnCharge (Ud, Cj0, Vj, M, Fc) + Tt * Id + Cp * Ud;
374 
375  // save operating points
376  setOperatingPoint ("gd", gd);
377  setOperatingPoint ("Id", Id);
378  setOperatingPoint ("Cd", Cd);
379 }
380 
381 // Callback for initializing the AC analysis.
382 void diode::initAC (void) {
383  allocMatrixMNA ();
384 }
385 
386 // Callback for the AC analysis.
387 void diode::calcAC (nr_double_t frequency) {
388  nr_double_t gd = getOperatingPoint ("gd");
389  nr_double_t Cd = getOperatingPoint ("Cd");
390  nr_complex_t y = rect (gd, Cd * 2.0 * M_PI * frequency);
391  setY (NODE_C, NODE_C, +y); setY (NODE_A, NODE_A, +y);
392  setY (NODE_C, NODE_A, -y); setY (NODE_A, NODE_C, -y);
393 }
394 
395 // Callback for the AC noise analysis.
396 void diode::calcNoiseAC (nr_double_t frequency) {
397  setMatrixN (calcMatrixCy (frequency));
398 }
399 
400 #define qState 0 // charge state
401 #define cState 1 // current state
402 
403 // Callback for initializing the TR analysis.
404 void diode::initTR (void) {
405  setStates (2);
406  initDC ();
407 }
408 
409 // Callback for the TR analysis.
410 void diode::calcTR (nr_double_t) {
411  calcDC ();
414 
415  nr_double_t Cd = getOperatingPoint ("Cd");
416 
417  transientCapacitance (qState, NODE_A, NODE_C, Cd, Ud, Qd);
418 }
419 
420 // Callback for initializing the HB analysis.
421 void diode::initHB (int frequencies) {
422  deviceStates (StateVars, frequencies);
423  doHB = true;
424  prepareDC ();
425  allocMatrixHB ();
426 }
427 
428 // Callback for the HB analysis.
429 void diode::calcHB (int frequency) {
430  // set current frequency state
431  deviceState (frequency);
432 
433  // g's (dI/dU) into Y-Matrix and I's into I-Vector
434  calcDC ();
435 
436  // calculate Q and C
439 
440  nr_double_t Cd = getOperatingPoint ("Cd");
441 
442  // fill in Q's in Q-Vector
443  setQ (NODE_C, +Qd);
444  setQ (NODE_A, -Qd);
445 
446  setCV (NODE_C, -Cd * Ud);
447  setCV (NODE_A, +Cd * Ud);
448 
449  // fill in C's (dQ/dU) into QV-Matrix
450  setQV (NODE_C, NODE_C, +Cd); setQV (NODE_A, NODE_A, +Cd);
451  setQV (NODE_C, NODE_A, -Cd); setQV (NODE_A, NODE_C, -Cd);
452 }
453 
454 // properties
455 PROP_REQ [] = {
456  { "Is", PROP_REAL, { 1e-15, PROP_NO_STR }, PROP_POS_RANGE },
457  { "N", PROP_REAL, { 1, PROP_NO_STR }, PROP_RNGII (1e-6, 100) },
458  { "M", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGII (0, 2) },
459  { "Cj0", PROP_REAL, { 10e-15, PROP_NO_STR }, PROP_POS_RANGE },
460  { "Vj", PROP_REAL, { 0.7, PROP_NO_STR }, PROP_RNGXI (0, 10) },
461  PROP_NO_PROP };
462 PROP_OPT [] = {
463  { "Rs", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
464  { "Isr", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
465  { "Nr", PROP_REAL, { 2, PROP_NO_STR }, PROP_RNGII (0.1, 100) },
466  { "Bv", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
467  { "Ibv", PROP_REAL, { 1e-3, PROP_NO_STR }, PROP_POS_RANGE },
468  { "Ikf", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
469  { "Tt", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
470  { "Fc", PROP_REAL, { 0.5, PROP_NO_STR }, PROP_RNGIX (0, 1) },
471  { "Cp", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
472  { "Kf", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
473  { "Af", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
474  { "Ffe", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGE },
475  { "Temp", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
476  { "Xti", PROP_REAL, { 3, PROP_NO_STR }, PROP_POS_RANGE },
477  { "Eg", PROP_REAL, { EgSi, PROP_NO_STR }, PROP_POS_RANGE },
478  { "Tbv", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
479  { "Trs", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
480  { "Ttt1", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
481  { "Ttt2", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
482  { "Tm1", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
483  { "Tm2", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
484  { "Tnom", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
485  { "Area", PROP_REAL, { 1, PROP_NO_STR }, PROP_POS_RANGEX },
486  PROP_NO_PROP };
487 struct define_t diode::cirdef =