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