My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
spsolver.cpp
Go to the documentation of this file.
1 /*
2  * spsolver.cpp - S-parameter solver class implementation
3  *
4  * Copyright (C) 2003-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: spsolver.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 <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "logging.h"
34 #include "complex.h"
35 #include "object.h"
36 #include "node.h"
37 #include "circuit.h"
38 #include "strlist.h"
39 #include "vector.h"
40 #include "matvec.h"
41 #include "dataset.h"
42 #include "net.h"
43 #include "analysis.h"
44 #include "sweep.h"
45 #include "nodelist.h"
46 #include "netdefs.h"
47 #include "characteristic.h"
48 #include "spsolver.h"
49 #include "constants.h"
51 #include "components/tee.h"
52 #include "components/open.h"
53 #include "components/itrafo.h"
54 #include "components/cross.h"
55 #include "components/ground.h"
56 
57 /* Evolved optimization flags. */
58 #define USE_GROUNDS 1 // use extra grounds ?
59 #define USE_CROSSES 1 // use additional cross connectors ?
60 #define SORTED_LIST 1 // use sorted node list?
61 
62 #define TINYS (NR_TINY * 1.235) // 'tiny' value for singularities
63 
64 // Constructor creates an unnamed instance of the spsolver class.
66  type = ANALYSIS_SPARAMETER;
67  swp = NULL;
68  saveCVs = 0;
69  noise = 0;
70  nlist = NULL;
71  tees = crosses = opens = grounds = 0;
72  gnd = NULL;
73 }
74 
75 // Constructor creates a named instance of the spsolver class.
76 spsolver::spsolver (char * n) : analysis (n) {
78  swp = NULL;
79  saveCVs = 0;
80  noise = 0;
81  nlist = NULL;
82  tees = crosses = opens = grounds = 0;
83  gnd = NULL;
84 }
85 
86 // Destructor deletes the spsolver class object.
88  if (swp) delete swp;
89  if (nlist) delete nlist;
90 }
91 
92 /* The copy constructor creates a new instance of the spsolver class
93  based on the given spsolver object. */
95  tees = n.tees;
96  crosses = n.crosses;
97  opens = n.opens;
98  grounds = n.grounds;
99  noise = n.noise;
100  saveCVs = n.saveCVs;
101  swp = n.swp ? new sweep (*n.swp) : NULL;
102  nlist = n.nlist ? new nodelist (*n.nlist) : NULL;
103  gnd = n.gnd;
104 }
105 
106 /* This function joins two nodes of a single circuit (interconnected
107  nodes) and returns the resulting circuit. */
109 
110  circuit * s = n1->getCircuit ();
111  circuit * result = new circuit (s->getSize () - 2);
112  nr_complex_t p;
113 
114  // allocate S-parameter and noise corellation matrices
115  result->initSP (); if (noise) result->initNoiseSP ();
116 
117  // interconnected port numbers
118  int k = n1->getPort (), l = n2->getPort ();
119 
120  // denominator needs to be calculated only once
121  nr_complex_t d = (1.0 - s->getS (k, l)) * (1.0 - s->getS (l, k)) -
122  s->getS (k, k) * s->getS (l, l);
123 
124  // avoid singularity when two full reflective ports are interconnected
125  nr_double_t tiny1 = (d == 0) ? 1.0 - TINYS : 1.0;
126  nr_double_t tiny2 = tiny1 * tiny1;
127  nr_double_t tiny3 = tiny1 * tiny2;
128  d = (1.0 - s->getS (k, l) * tiny1) * (1.0 - s->getS (l, k) * tiny1) -
129  s->getS (k, k) * s->getS (l, l) * tiny2;
130 
131  int j2; // column index for resulting matrix
132  int i2; // row index for resulting matrix
133  int j1; // column index for S matrix
134  int i1; // row index for S matrix
135 
136  // handle single S block only
137  i2 = j2 = 0;
138  for (j1 = 0; j1 < s->getSize (); j1++) {
139 
140  // skip connected node
141  if (j1 == k || j1 == l) continue;
142 
143  // assign node name of resulting circuit
144  result->setNode (j2, s->getNode(j1)->getName ());
145 
146  // inside S only
147  for (i1 = 0; i1 < s->getSize (); i1++) {
148 
149  // skip connected node
150  if (i1 == k || i1 == l) continue;
151 
152  // compute S'ij
153  p = s->getS (i1, j1);
154  p +=
155  (s->getS (k, j1) * s->getS (i1, l) * (1.0 - s->getS (l, k)) * tiny3 +
156  s->getS (l, j1) * s->getS (i1, k) * (1.0 - s->getS (k, l)) * tiny3 +
157  s->getS (k, j1) * s->getS (l, l) * s->getS (i1, k) * tiny3 +
158  s->getS (l, j1) * s->getS (k, k) * s->getS (i1, l) * tiny3) / d;
159  result->setS (i2++, j2, p);
160  }
161 
162  // next column
163  j2++; i2 = 0;
164  }
165  return result;
166 }
167 
168 /* This function joins two nodes of two different circuits (connected
169  nodes) and returns the resulting circuit. */
171 
172  circuit * s = n1->getCircuit ();
173  circuit * t = n2->getCircuit ();
174  circuit * result = new circuit (s->getSize () + t->getSize () - 2);
175  nr_complex_t p;
176 
177  // allocate S-parameter and noise corellation matrices
178  result->initSP (); if (noise) result->initNoiseSP ();
179 
180  // connected port numbers
181  int k = n1->getPort (), l = n2->getPort ();
182 
183  // denominator needs to be calculated only once
184  nr_complex_t d = 1.0 - s->getS (k, k) * t->getS (l, l);
185 
186  // avoid singularity when two full reflective ports are connected
187  nr_double_t tiny1 = (d == 0) ? 1.0 - TINYS : 1.0;
188  nr_double_t tiny2 = tiny1 * tiny1;
189  nr_double_t tiny3 = tiny1 * tiny2;
190  d = 1.0 - s->getS (k, k) * t->getS (l, l) * tiny2;
191 
192  int j2; // column index for resulting matrix
193  int i2; // row index for resulting matrix
194  int j1; // column index for S matrix
195  int i1; // row index for S matrix
196 
197  // handle S block
198  i2 = j2 = 0;
199  for (j1 = 0; j1 < s->getSize (); j1++) {
200 
201  // skip connected node
202  if (j1 == k) continue;
203 
204  // assign node name of resulting circuit
205  result->setNode (j2, s->getNode(j1)->getName());
206 
207  // inside S
208  for (i1 = 0; i1 < s->getSize (); i1++) {
209 
210  // skip connected node
211  if (i1 == k) continue;
212 
213  // compute S'ij
214  p = s->getS (i1, j1);
215  p += s->getS (k, j1) * t->getS (l, l) * s->getS (i1, k) * tiny3 / d;
216  result->setS (i2++, j2, p);
217  }
218 
219  // across S and T
220  for (i1 = 0; i1 < t->getSize (); i1++) {
221 
222  // skip connected node
223  if (i1 == l) continue;
224 
225  // compute S'mj
226  p = s->getS (k, j1) * t->getS (i1, l) * tiny2 / d;
227  result->setS (i2++, j2, p);
228  }
229  // next column
230  j2++; i2 = 0;
231  }
232 
233  // handle T block
234  for (j1 = 0; j1 < t->getSize (); j1++) {
235 
236  // skip connected node
237  if (j1 == l) continue;
238 
239  // assign node name of resulting circuit
240  result->setNode (j2, t->getNode(j1)->getName ());
241 
242  // across T and S
243  for (i1 = 0; i1 < s->getSize (); i1++) {
244 
245  // skip connected node
246  if (i1 == k) continue;
247 
248  // compute S'mj
249  p = t->getS (l, j1) * s->getS (i1, k) * tiny2 / d;
250  result->setS (i2++, j2, p);
251  }
252 
253  // inside T
254  for (i1 = 0; i1 < t->getSize (); i1++) {
255 
256  // skip connected node
257  if (i1 == l) continue;
258 
259  // compute S'ij
260  p = t->getS (i1, j1);
261  p += t->getS (l, j1) * s->getS (k, k) * t->getS (i1, l) * tiny3 / d;
262  result->setS (i2++, j2, p);
263  }
264 
265  // next column
266  j2++; i2 = 0;
267  }
268 
269  return result;
270 }
271 
272 /* This function joins the two given nodes of a single circuit
273  (interconnected nodes) and modifies the resulting circuit
274  appropriately. */
276 
277  circuit * c = n1->getCircuit ();
278  nr_complex_t p, k1, k2, k3, k4;
279 
280  // interconnected port numbers
281  int k = n1->getPort (), l = n2->getPort ();
282 
283  // denominator needs to be calculated only once
284  nr_complex_t t = (1.0 - c->getS (k, l)) * (1.0 - c->getS (l, k)) -
285  c->getS (k, k) * c->getS (l, l);
286 
287  // avoid singularity when two full reflective ports are interconnected
288  nr_double_t tiny1 = (t == 0) ? 1.0 - TINYS : 1.0;
289  nr_double_t tiny2 = tiny1 * tiny1;
290  t = (1.0 - c->getS (k, l) * tiny1) * (1.0 - c->getS (l, k) * tiny1) -
291  c->getS (k, k) * c->getS (l, l) * tiny2;
292 
293  int j2; // column index for resulting matrix
294  int i2; // row index for resulting matrix
295  int j1; // column index for S matrix
296  int i1; // row index for S matrix
297 
298  // handle single C block only
299  i2 = j2 = 0;
300  for (j1 = 0; j1 < c->getSize (); j1++) {
301 
302  // skip connected node
303  if (j1 == k || j1 == l) continue;
304 
305  // inside C only
306  for (i1 = 0; i1 < c->getSize (); i1++) {
307 
308  // skip connected node
309  if (i1 == k || i1 == l) continue;
310 
311  k1 = (c->getS (i1, l) * (1.0 - c->getS (l, k)) +
312  c->getS (l, l) * c->getS (i1, k)) * tiny2 / t;
313  k2 = (c->getS (i1, k) * (1.0 - c->getS (k, l)) +
314  c->getS (k, k) * c->getS (i1, l)) * tiny2 / t;
315  k3 = (c->getS (j1, l) * (1.0 - c->getS (l, k)) +
316  c->getS (l, l) * c->getS (j1, k)) * tiny2 / t;
317  k4 = (c->getS (j1, k) * (1.0 - c->getS (k, l)) +
318  c->getS (k, k) * c->getS (j1, l)) * tiny2 / t;
319 
320  p =
321  c->getN (i1, j1) + c->getN (k, j1) * k1 + c->getN (l, j1) * k2 +
322  conj (k3) * (c->getN (i1, k) + c->getN (k, k) * k1 +
323  c->getN (l, k) * k2) +
324  conj (k4) * (c->getN (i1, l) + c->getN (k, l) * k1 +
325  c->getN (l, l) * k2);
326  result->setN (i2, j2, p);
327 
328  if (i2 >= j2) break; // the other half need not be computed
329  result->setN (j2, i2, conj (p));
330  i2++;
331  }
332 
333  // next column
334  j2++; i2 = 0;
335  }
336 }
337 
338 
339 /* The following function joins two nodes of two different circuits
340  and saves the noise wave correlation matrix in the resulting
341  circuit. */
342 void spsolver::noiseConnect (circuit * result, node * n1, node * n2) {
343  circuit * c = n1->getCircuit ();
344  circuit * d = n2->getCircuit ();
345  nr_complex_t p;
346 
347  // connected port numbers
348  int k = n1->getPort (), l = n2->getPort ();
349 
350  // denominator needs to be calculated only once
351  nr_complex_t t = 1.0 - c->getS (k, k) * d->getS (l, l);
352 
353  // avoid singularity when two full reflective ports are connected
354  nr_double_t tiny1 = (t == 0) ? 1.0 - TINYS : 1.0;
355  nr_double_t tiny2 = tiny1 * tiny1;
356  nr_double_t tiny3 = tiny1 * tiny2;
357  nr_double_t tiny4 = tiny1 * tiny3;
358  t = 1.0 - c->getS (k, k) * d->getS (l, l) * tiny2;
359 
360  int j2; // column index for resulting matrix
361  int i2; // row index for resulting matrix
362  int j1; // column index for S matrix
363  int i1; // row index for S matrix
364 
365  // handle C block
366  i2 = j2 = 0;
367  for (j1 = 0; j1 < c->getSize (); j1++) {
368 
369  // skip connected node
370  if (j1 == k) continue;
371 
372  // inside C
373  for (i1 = 0; i1 < c->getSize (); i1++) {
374 
375  // skip connected node
376  if (i1 == k) continue;
377 
378  // compute C'ij
379  p = c->getN (i1, j1) +
380  c->getN (k, j1) * d->getS (l, l) * c->getS (i1, k) * tiny2 / t +
381  c->getN (i1, k) * conj (d->getS (l, l) * c->getS (j1, k) * tiny2 / t) +
382  (c->getN (k, k) * norm (d->getS (l, l)) + d->getN (l, l)) *
383  c->getS (i1, k) * conj (c->getS (j1, k)) * tiny4 / norm (t);
384 
385  result->setN (i2, j2, p);
386  if (i2 >= j2) break; // the other half need not be computed
387  result->setN (j2, i2, conj (p));
388  i2++;
389  }
390 
391  /* The formulas "across C and D" are calculated elsewhere by the
392  other half of the matrix (conjugate complex). Therefore, they
393  are missing here. */
394 
395  // next column
396  j2++; i2 = 0;
397  }
398 
399  // handle D block
400  for (j1 = 0; j1 < d->getSize (); j1++) {
401 
402  // skip connected node
403  if (j1 == l) continue;
404 
405  // across D and C
406  for (i1 = 0; i1 < c->getSize (); i1++) {
407 
408  // skip connected node
409  if (i1 == k) continue;
410 
411  // compute C'ij
412  p = (c->getN (k, k) * d->getS (l, l) +
413  d->getN (l, l) * conj (c->getS (k, k))) *
414  c->getS (i1, k) * conj (d->getS (j1, l)) * tiny3 / norm (t) +
415  d->getN (l, j1) * c->getS (i1, k) * tiny1 / t +
416  c->getN (i1, k) * conj (d->getS (j1, l) * tiny1 / t);
417  result->setN (i2, j2, p);
418  result->setN (j2, i2, conj (p));
419  i2++;
420  }
421 
422  // inside D
423  for (i1 = 0; i1 < d->getSize (); i1++) {
424 
425  // skip connected node
426  if (i1 == l) continue;
427 
428  // compute C'ij
429  p = d->getN (i1, j1) +
430  (d->getN (l, l) * norm (c->getS (k, k)) + c->getN (k, k)) *
431  d->getS (i1, l) * conj (d->getS (j1, l)) * tiny4 / norm (t) +
432  d->getN (i1, l) * conj (c->getS (k, k) * d->getS (j1, l) * tiny2 / t) +
433  d->getN (l, j1) * c->getS (k, k) * d->getS (i1, l) * tiny2 / t;
434  result->setN (i2, j2, p);
435  if (i2 >= j2) break; // the other half need not be computed
436  result->setN (j2, i2, conj (p));
437  i2++;
438  }
439 
440  // next column
441  j2++; i2 = 0;
442  }
443 }
444 
445 /* Goes through the list of circuit objects and runs its frequency
446  dependent calcSP() function. */
447 void spsolver::calc (nr_double_t freq) {
448  circuit * root = subnet->getRoot ();
449  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
450  c->calcSP (freq);
451  if (noise) c->calcNoiseSP (freq);
452  }
453 }
454 
455 /* Go through each registered circuit object in the list and find the
456  connection which results in a new subnetwork with the smallest
457  number of s-parameters to calculate. */
458 void spsolver::reduce (void) {
459 
460 #if SORTED_LIST
461  node * n1, * n2;
462  circuit * result, * cand1, * cand2;
463 
464  nlist->sortedNodes (&n1, &n2);
465  cand1 = n1->getCircuit ();
466  cand2 = n2->getCircuit ();
467 #else /* !SORTED_LIST */
468  node * n1, * n2, * cand;
469  circuit * result, * c1, * c2, * cand1, * cand2;
470  int ports;
471  circuit * root = subnet->getRoot ();
472 
473  // initialize local variables
474  result = c1 = c2 = cand1 = cand2 = NULL;
475  n1 = n2 = cand = NULL;
476  ports = 10000; // huge
477 
478  // go through the circuit list
479  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
480 
481  // skip signal ports
482  if (c->getPort ()) continue;
483 
484  // and each node in the circuit
485  for (int i = 0; i < c->getSize (); i++) {
486 
487  // find duplicate node
488  if ((cand = subnet->findConnectedCircuitNode (c->getNode (i))) != NULL) {
489 
490  // save both candidates
491  c1 = c; c2 = cand->getCircuit ();
492  // connected
493  if (c1 != c2) {
494  if (c1->getSize () + c2->getSize () - 2 < ports) {
495  ports = c1->getSize () + c2->getSize () - 2;
496  cand1 = c1; cand2 = c2; n1 = c1->getNode (i); n2 = cand;
497  }
498  }
499  // interconnect
500  else {
501  if (c1->getSize () - 2 < ports) {
502  ports = c1->getSize () - 2;
503  cand1 = c1; cand2 = c2; n1 = c1->getNode (i); n2 = cand;
504  }
505  }
506  }
507  }
508  }
509 #endif /* !SORTED_LIST */
510 
511  // found a connection ?
512  if (cand1 != NULL && cand2 != NULL) {
513  // connected
514  if (cand1 != cand2) {
515 #if DEBUG && 0
516  logprint (LOG_STATUS, "DEBUG: connected node (%s): %s - %s\n",
517  n1->getName (), cand1->getName (), cand2->getName ());
518 #endif /* DEBUG */
519  result = connectedJoin (n1, n2);
520  if (noise) noiseConnect (result, n1, n2);
521  subnet->reducedCircuit (result);
522 #if SORTED_LIST
523  nlist->remove (cand1);
524  nlist->remove (cand2);
525  nlist->insert (result);
526 #endif /* SORTED_LIST */
527  subnet->removeCircuit (cand1);
528  subnet->removeCircuit (cand2);
529  subnet->insertCircuit (result);
530  result->setOriginal (0);
531  }
532  // interconnect
533  else {
534 #if DEBUG && 0
535  logprint (LOG_STATUS, "DEBUG: interconnected node (%s): %s\n",
536  n1->getName (), cand1->getName ());
537 #endif
538  result = interconnectJoin (n1, n2);
539  if (noise) noiseInterconnect (result, n1, n2);
540  subnet->reducedCircuit (result);
541 #if SORTED_LIST
542  nlist->remove (cand1);
543  nlist->insert (result);
544 #endif /* SORTED_LIST */
545  subnet->removeCircuit (cand1);
546  subnet->insertCircuit (result);
547  result->setOriginal (0);
548  }
549  }
550 }
551 
552 /* Goes through the list of circuit objects and runs initializing
553  functions if necessary. */
554 void spsolver::init (void) {
555  circuit * root = subnet->getRoot ();
556  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
557  if (c->isNonLinear ()) c->calcOperatingPoints ();
558  c->initSP ();
559  if (noise) c->initNoiseSP ();
560  }
561 }
562 
563 /* This is the netlist solver. It prepares the circuit list for each
564  requested frequency and solves it then. */
565 int spsolver::solve (void) {
566  nr_double_t freq;
567  int ports;
568  runs++;
569 
570  // fetch simulation properties
571  saveCVs |= !strcmp (getPropertyString ("saveCVs"), "yes") ? SAVE_CVS : 0;
572  saveCVs |= !strcmp (getPropertyString ("saveAll"), "yes") ? SAVE_ALL : 0;
573 
574  // run additional noise analysis ?
575  noise = !strcmp (getPropertyString ("Noise"), "yes") ? 1 : 0;
576 
577  // create frequency sweep if necessary
578  if (swp == NULL) {
579  swp = createSweep ("frequency");
580  }
581 
582  init ();
584 
585 #if SORTED_LIST
586 #if DEBUG
587  logprint (LOG_STATUS, "NOTIFY: %s: creating sorted nodelist for "
588  "SP analysis\n", getName ());
589 #endif
590  nlist = new nodelist (subnet);
591  nlist->sort ();
592 #endif /* SORTED_LIST */
593 
594 #if DEBUG
595  logprint (LOG_STATUS, "NOTIFY: %s: solving SP netlist\n", getName ());
596 #endif
597 
598  swp->reset ();
599  for (int i = 0; i < swp->getSize (); i++) {
600  freq = swp->next ();
601  if (progress) logprogressbar (i, swp->getSize (), 40);
602 
603  ports = subnet->countNodes ();
604  subnet->setReduced (0);
605  calc (freq);
606 
607 #if DEBUG && 0
608  logprint (LOG_STATUS, "NOTIFY: %s: solving netlist for f = %e\n",
609  getName (), (double) freq);
610 #endif
611 
612  while (ports > subnet->getPorts ()) {
613  reduce ();
614  ports -= 2;
615  }
616 
617  saveResults (freq);
618  subnet->getDroppedCircuits (nlist);
619  subnet->deleteUnusedCircuits (nlist);
620  if (saveCVs & SAVE_CVS) saveCharacteristics (freq);
621  }
622  if (progress) logprogressclear (40);
623  dropConnections ();
624 #if SORTED_LIST
625  delete nlist; nlist = NULL;
626 #endif
627  return 0;
628 }
629 
630 /* The function goes through the list of circuit objects and creates
631  tee and cross circuits if necessary. It looks for nodes in the
632  circuit list connected to the given node. */
634 
635  int count = 0;
636  node * nodes[4], * _node;
637  char * _name = n->getName ();
638  circuit * root = subnet->getRoot ();
639 
640 #if USE_GROUNDS
641  if (!strcmp (_name, "gnd")) return;
642 #endif /* USE_GROUNDS */
643 
644  nodes[0] = n;
645 
646  // go through list of circuit objects
647  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
648  // and each node in a circuit
649  for (int i = 0; i < c->getSize (); i++) {
650  _node = c->getNode (i);
651  if (!strcmp (_node->getName (), _name)) {
652  if (_node != n) {
653 
654  // found a connected node
655  nodes[++count] = _node;
656 #if USE_CROSSES
657  if (count == 3) {
658  // create an additional cross and assign its nodes
659  insertCross (nodes, _name);
660  count = 1;
661  }
662 #else /* !USE_CROSSES */
663  if (count == 2) {
664  // create an additional tee and assign its nodes
665  insertTee (nodes, _name);
666  count = 1;
667  }
668 #endif /* !USE_CROSSES */
669  }
670  }
671  }
672  }
673 #if USE_CROSSES
674  /* if using crosses there can be a tee left here */
675  if (count == 2) {
676  insertTee (nodes, _name);
677  }
678 #endif /* USE_CROSSES */
679 }
680 
681 /* The following function creates a tee circuit with the given nodes
682  and the node name. The tee's node names are adjusted to be
683  internal nodes. */
684 void spsolver::insertTee (node ** nodes, char * name) {
685  circuit * result;
686  // create a tee and assign its node names
687  result = new tee ();
688  subnet->insertedCircuit (result);
689  result->setNode (0, name);
690  subnet->insertedNode (result->getNode (1));
691  subnet->insertedNode (result->getNode (2));
692  // rename the nodes connected to the tee
693  nodes[1]->setName (result->getNode(1)->getName ());
694  nodes[2]->setName (result->getNode(2)->getName ());
695  // complete the nodes of the tee
696  result->getNode(1)->setCircuit (result);
697  result->getNode(2)->setCircuit (result);
698  result->getNode(1)->setPort (1);
699  result->getNode(2)->setPort (2);
700  // put the tee into the circuit list and initialize it
701  subnet->insertCircuit (result);
702  result->initSP (); if (noise) result->initNoiseSP ();
703  // put the tee's first node into the node collection
704  nodes[1] = result->getNode (0);
705  tees++;
706 }
707 
708 /* The following function creates a cross circuit with the given nodes
709  and the node name. The cross's node names are adjusted to be
710  internal nodes. */
711 void spsolver::insertCross (node ** nodes, char * name) {
712  circuit * result;
713  // create a cross and assign its node names
714  result = new cross ();
715  subnet->insertedCircuit (result);
716  result->setNode (0, name);
717  subnet->insertedNode (result->getNode (1));
718  subnet->insertedNode (result->getNode (2));
719  subnet->insertedNode (result->getNode (3));
720  // rename the nodes connected to the cross
721  nodes[1]->setName (result->getNode(1)->getName ());
722  nodes[2]->setName (result->getNode(2)->getName ());
723  nodes[3]->setName (result->getNode(3)->getName ());
724  // complete the nodes of the cross
725  result->getNode(1)->setCircuit (result);
726  result->getNode(2)->setCircuit (result);
727  result->getNode(3)->setCircuit (result);
728  result->getNode(1)->setPort (1);
729  result->getNode(2)->setPort (2);
730  result->getNode(3)->setPort (3);
731  // put the cross into the circuit list and initialize it
732  subnet->insertCircuit (result);
733  result->initSP (); if (noise) result->initNoiseSP ();
734  // put the cross's first node into the node collection
735  nodes[1] = result->getNode (0);
736  crosses++;
737 }
738 
739 /* This function removes an inserted tee from the netlist and restores
740  the original node names. */
742  node * n;
743  if (c->getType () == CIR_TEE) {
744  char * name = c->getNode(0)->getName ();
745  n = subnet->findConnectedNode (c->getNode (1)); n->setName (name);
746  n = subnet->findConnectedNode (c->getNode (2)); n->setName (name);
747  c->setOriginal (0);
748  subnet->removeCircuit (c);
749  }
750 }
751 
752 /* This function removes an inserted cross from the netlist and restores
753  the original node names. */
755  node * n;
756  if (c->getType () == CIR_CROSS) {
757  char * name = c->getNode(0)->getName ();
758  n = subnet->findConnectedNode (c->getNode (1)); n->setName (name);
759  n = subnet->findConnectedNode (c->getNode (2)); n->setName (name);
760  n = subnet->findConnectedNode (c->getNode (3)); n->setName (name);
761  c->setOriginal (0);
762  subnet->removeCircuit (c);
763  }
764 }
765 
766 /* The function adds an open to the circuit list if the given node is
767  unconnected. */
769  if (strcmp (n->getName (), "gnd") &&
770  subnet->findConnectedNode (n) == NULL) {
771  circuit * result = new open ();
772  subnet->insertedCircuit (result);
773  result->setNode (0, n->getName ());
774  subnet->insertCircuit (result);
775  result->initSP (); if (noise) result->initNoiseSP ();
776  opens++;
777  }
778 }
779 
780 // This function removes an inserted open from the netlist.
782  if (c->getType () == CIR_OPEN) {
783  c->setOriginal (0);
784  subnet->removeCircuit (c);
785  }
786 }
787 
788 /* The function adds a ground circuit to the circuit list if the given
789  node is a ground connection. */
791  if (!strcmp (n->getName (), "gnd") && !n->getCircuit()->getPort () &&
792  n->getCircuit()->getType () != CIR_GROUND) {
793  circuit * result = new ground ();
794  subnet->insertedCircuit (result);
795  subnet->insertedNode (result->getNode (0));
796  result->getNode(0)->setCircuit (result);
797  result->getNode(0)->setPort (0);
798  n->setName (result->getNode(0)->getName ());
799  subnet->insertCircuit (result);
800  result->initSP (); if (noise) result->initNoiseSP ();
801  grounds++;
802  }
803 }
804 
805 // This function removes an inserted ground from the netlist.
807  if (c->getType () == CIR_GROUND) {
808  node * n = subnet->findConnectedNode (c->getNode (0));
809  n->setName ("gnd");
810  c->setOriginal (0);
811  subnet->removeCircuit (c);
812  }
813 }
814 
815 /* This function prepares the circuit list by adding Ts and opens to
816  the circuit list. With this adjustments the solver is able to
817  solve the circuit. */
819 
820  circuit * root, * c;
821 #if DEBUG
822  logprint (LOG_STATUS, "NOTIFY: %s: preparing circuit for analysis\n",
823  getName ());
824 #endif /* DEBUG */
825 
826 #if USE_GROUNDS
827  // remove original ground circuit from netlist
828  root = subnet->getRoot ();
829  for (c = root; c != NULL; c = (circuit *) c->getNext ()) {
830  if (c->getType () == CIR_GROUND) {
831  gnd = c;
832  subnet->removeCircuit (c, 0);
833  break;
834  }
835  }
836 #endif /* USE_GROUNDS */
837 
838  // insert opens, tee and crosses where necessary
839  tees = crosses = opens = grounds = 0;
840  root = subnet->getRoot ();
841  for (c = root; c != NULL; c = (circuit *) c->getNext ()) {
842  for (int i = 0; i < c->getSize (); i++) {
843  insertConnectors (c->getNode (i));
844  insertOpen (c->getNode (i));
845  }
846  }
847 
848  // insert S-parameter port transformers
850 
851 #if USE_GROUNDS
852  // insert grounds where necessary
853  root = subnet->getRoot ();
854  for (c = root; c != NULL; c = (circuit *) c->getNext ()) {
855  for (int i = 0; i < c->getSize (); i++) {
856  insertGround (c->getNode (i));
857  }
858  }
859 #endif /* USE_GROUNDS */
860 
861 #if DEBUG
862  logprint (LOG_STATUS, "NOTIFY: %s: inserted %d tees, %d crosses, %d opens "
863  "and %d grounds\n",
864  getName (), tees, crosses, opens, grounds);
865 #endif /* DEBUG */
866 }
867 
868 /* The function is the counterpart of insertConnections(). It removes
869  all additional circuits from the netlist which were necessary to
870  run the analysis algorithm. */
872  circuit * next, * cand;
873  int inserted;
874 
875  // drop all additional inserted circuits in correct order
876  do {
877  // find last inserted circuit
878  inserted = -1;
879  cand = NULL;
880  for (circuit * c = subnet->getRoot (); c != NULL; c = next) {
881  next = (circuit *) c->getNext ();
882  if (c->getInserted () > inserted) {
883  inserted = c->getInserted ();
884  cand = c;
885  }
886  }
887  // if found, then drop that circuit
888  if (cand != NULL) {
889  switch (cand->getType ()) {
890  case CIR_OPEN:
891  dropOpen (cand);
892  break;
893  case CIR_TEE:
894  dropTee (cand);
895  break;
896  case CIR_CROSS:
897  dropCross (cand);
898  break;
899  case CIR_GROUND:
900  dropGround (cand);
901  break;
902  case CIR_ITRAFO:
903  dropDifferentialPort (cand);
904  break;
905  }
906  }
907  } while (cand != NULL);
908 
909 #if USE_GROUNDS
910  // attach the original ground to the netlist
911  subnet->insertCircuit (gnd);
912 #endif /* USE_GROUNDS */
913 }
914 
915 /* This function inserts an ideal transformer before an AC power
916  source in order to allow differential S parameter ports. */
918  circuit * root = subnet->getRoot ();
919  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
920  if (c->getPort ()) {
921 
922  // create an ideal transformer and assign its node names
923  circuit * result = new itrafo ();
924  subnet->insertedCircuit (result);
925  subnet->insertedNode (result->getNode (0));
926  result->setNode (1, c->getNode(0)->getName ());
927  result->setNode (2, c->getNode(1)->getName ());
928 
929  // rename the nodes connected to the trafo
930  c->getNode(0)->setName (result->getNode(0)->getName ());
931  c->getNode(1)->setName ("PacGround");
932 
933  // complete the nodes of the trafo
934  result->getNode(0)->setCircuit (result);
935  result->getNode(0)->setPort (0);
936 
937  // pass the port impedance to the ideal trafo
938  result->addProperty ("Z", c->getPropertyDouble ("Z"));
939 
940  // put the trafo in the circuit list
941  subnet->insertCircuit (result);
942 
943  // allocate S-parameter and noise correlation matrices
944  result->initSP (); if (noise) result->initNoiseSP ();
945  }
946  }
947 }
948 
949 /* This function removes an ideal transformer which was necessary to
950  be placed in front of a s-parameter port in order to allow
951  differential s-parameters. It also restores the original node
952  names. */
954  circuit * pac;
955  node * n;
956  if (c->getType () == CIR_ITRAFO) {
957  n = subnet->findConnectedNode (c->getNode (0));
958  pac = n->getCircuit ();
959  pac->getNode(0)->setName (c->getNode(1)->getName ());
960  pac->getNode(1)->setName (c->getNode(2)->getName ());
961  c->setOriginal (0);
962  subnet->removeCircuit (c);
963  }
964 }
965 
966 /* This function saves the results of a single solve() functionality
967  (for the given frequency) into the output dataset. */
968 void spsolver::saveResults (nr_double_t freq) {
969 
970  vector * f;
971  node * sig_i, * sig_j;
972  char * n;
973  int res_i, res_j;
974  circuit * root = subnet->getRoot ();
975 
976  // temporary noise matrices and input port impedance
977  nr_complex_t noise_c[4], noise_s[4];
978  nr_double_t z0 = circuit::z0;
979 
980  // add current frequency to the dependency of the output dataset
981  if ((f = data->findDependency ("frequency")) == NULL) {
982  f = new vector ("frequency");
983  data->addDependency (f);
984  }
985  if (runs == 1) f->add (freq);
986 
987  // go through the list of remaining circuits
988  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
989  // skip signals
990  if (!c->getPort ()) {
991  // handle each s-parameter
992  for (int i = 0; i < c->getSize (); i++) {
993  for (int j = 0; j < c->getSize (); j++) {
994 
995  // generate the appropriate variable name
996  sig_i = subnet->findConnectedNode (c->getNode (i));
997  sig_j = subnet->findConnectedNode (c->getNode (j));
998  res_i = sig_i->getCircuit()->getPropertyInteger ("Num");
999  res_j = sig_j->getCircuit()->getPropertyInteger ("Num");
1000  n = createSP (res_i, res_j);
1001 
1002  // add variable data item to dataset
1003  saveVariable (n, c->getS (i, j), f);
1004 
1005  // if noise analysis is requested
1006  if (noise) {
1007  int ro, co;
1008  int ni = getPropertyInteger ("NoiseIP");
1009  int no = getPropertyInteger ("NoiseOP");
1010  if ((res_i == ni || res_i == no) && (res_j == ni || res_j == no)) {
1011  if (ni == res_i) {
1012  // assign input port impedance
1013  z0 = sig_i->getCircuit()->getPropertyDouble ("Z");
1014  }
1015  ro = (res_i == ni) ? 0 : 1;
1016  co = (res_j == ni) ? 0 : 1;
1017  // save results in temporary data items
1018  noise_c[co + ro * 2] = c->getN (i, j);
1019  noise_s[co + ro * 2] = c->getS (i, j);
1020  }
1021  }
1022  }
1023  }
1024  }
1025  }
1026 
1027  // finally compute and save noise parameters
1028  if (noise) {
1029  saveNoiseResults (noise_s, noise_c, z0, f);
1030  }
1031 }
1032 
1033 /* This function takes the s-parameter matrix and noise wave
1034  correlation matrix and computes the noise parameters based upon
1035  these values. Then it save the results into the dataset. */
1037  nr_double_t z0, vector * f) {
1038  nr_complex_t c22 = c[3], c11 = c[0], c12 = c[1];
1039  nr_complex_t s11 = s[0], s21 = s[2];
1040  nr_complex_t n1, n2, F, Sopt, Fmin, Rn;
1041 
1042  // linear noise figure
1043  F = real (1.0 + c22 / norm (s21));
1044  n1 =
1045  c11 * norm (s21) - 2.0 * real (c12 * s21 * conj (s11)) +
1046  c22 * norm (s11);
1047  n2 = 2.0 * (c22 * s11 - c12 * s21) / (c22 + n1);
1048 
1049  // optimal source reflection coefficient
1050  Sopt = 1.0 - norm (n2);
1051  if (real (Sopt) < 0.0)
1052  Sopt = (1.0 + sqrt (Sopt)) / n2; // avoid a negative radicant
1053  else
1054  Sopt = (1.0 - sqrt (Sopt)) / n2;
1055 
1056  // minimum noise figure
1057  Fmin = real (1.0 + (c22 - n1 * norm (Sopt)) /
1058  norm (s21) / (1.0 + norm (Sopt)));
1059 
1060  // equivalent noise resistance
1061  Rn = real ((c11 - 2.0 * real (c12 * conj ((1.0 + s11) / s21)) +
1062  c22 * norm ((1.0 + s11) / s21)) / 4.0);
1063  Rn = Rn * z0;
1064 
1065  // add variable data items to dataset
1066  saveVariable ("F", F, f);
1067  saveVariable ("Sopt", Sopt, f);
1068  saveVariable ("Fmin", Fmin, f);
1069  saveVariable ("Rn", Rn, f);
1070 }
1071 
1072 // Create an appropriate variable name.
1073 char * spsolver::createSP (int i, int j) {
1074  return matvec::createMatrixString ("S", i - 1, j - 1);
1075 }
1076 
1077 /* Create an appropriate variable name for characteristic values. The
1078  caller is responsible to free() the returned string. */
1079 char * spsolver::createCV (char * c, char * n) {
1080  char * text = (char *) malloc (strlen (c) + strlen (n) + 2);
1081  sprintf (text, "%s.%s", c, n);
1082  return text;
1083 }
1084 
1085 /* Goes through the list of circuit objects and runs its
1086  saveCharacteristics() function. Then puts these values into the
1087  dataset. */
1088 void spsolver::saveCharacteristics (nr_double_t freq) {
1089  circuit * root = subnet->getRoot ();
1090  char * n;
1091  vector * f = data->findDependency ("frequency");
1092  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
1093  c->saveCharacteristics (freq);
1094  if (c->getSubcircuit () && !(saveCVs & SAVE_ALL)) continue;
1095  c->calcCharacteristics (freq);
1096  valuelistiterator<characteristic> it (c->getCharacteristics ());
1097  for (; *it; ++it) {
1098  characteristic * p = it.currentVal ();
1099  n = createCV (c->getName (), p->getName ());
1100  saveVariable (n, p->getValue (), f);
1101  free (n);
1102  }
1103  }
1104 }
1105 
1106 // properties
1107 PROP_REQ [] = {
1108  { "Type", PROP_STR, { PROP_NO_VAL, "lin" }, PROP_RNG_TYP },
1109  PROP_NO_PROP };
1110 PROP_OPT [] = {
1111  { "Noise", PROP_STR, { PROP_NO_VAL, "no" }, PROP_RNG_YESNO },
1112  { "NoiseIP", PROP_INT, { 1, PROP_NO_STR }, PROP_RNGII (1, MAX_PORTS) },
1113  { "NoiseOP", PROP_INT, { 2, PROP_NO_STR }, PROP_RNGII (1, MAX_PORTS) },
1114  { "Start", PROP_REAL, { 1e9, PROP_NO_STR }, PROP_POS_RANGE },
1115  { "Stop", PROP_REAL, { 10e9, PROP_NO_STR }, PROP_POS_RANGE },
1116  { "Points", PROP_INT, { 10, PROP_NO_STR }, PROP_MIN_VAL (2) },
1117  { "Values", PROP_LIST, { 10, PROP_NO_STR }, PROP_POS_RANGE },
1118  { "saveCVs", PROP_STR, { PROP_NO_VAL, "no" }, PROP_RNG_YESNO },
1119  { "saveAll", PROP_STR, { PROP_NO_VAL, "no" }, PROP_RNG_YESNO },
1120  PROP_NO_PROP };
1121 struct define_t spsolver::anadef =