My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
nasolver.cpp
Go to the documentation of this file.
1 /*
2  * nasolver.cpp - nodal analysis solver 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: nasolver.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 #include <math.h>
33 #include <float.h>
34 #include <assert.h>
35 
36 #include "logging.h"
37 #include "complex.h"
38 #include "object.h"
39 #include "node.h"
40 #include "circuit.h"
41 #include "vector.h"
42 #include "dataset.h"
43 #include "net.h"
44 #include "analysis.h"
45 #include "nodelist.h"
46 #include "nodeset.h"
47 #include "strlist.h"
48 #include "tvector.h"
49 #include "tmatrix.h"
50 #include "eqnsys.h"
51 #include "constants.h"
52 #include "precision.h"
53 #include "operatingpoint.h"
54 #include "exception.h"
55 #include "exceptionstack.h"
56 #include "nasolver.h"
57 
58 using namespace qucs;
59 
60 // Constructor creates an unnamed instance of the nasolver class.
61 template <class nr_type_t>
63  nlist = NULL;
64  A = C = NULL;
65  z = x = xprev = zprev = NULL;
66  reltol = abstol = vntol = 0;
67  desc = NULL;
68  calculate_func = NULL;
69  convHelper = fixpoint = 0;
71  updateMatrix = 1;
72  gMin = srcFactor = 0;
73  eqns = new eqnsys<nr_type_t> ();
74 }
75 
76 // Constructor creates a named instance of the nasolver class.
77 template <class nr_type_t>
79  nlist = NULL;
80  A = C = NULL;
81  z = x = xprev = zprev = NULL;
82  reltol = abstol = vntol = 0;
83  desc = NULL;
84  calculate_func = NULL;
85  convHelper = fixpoint = 0;
87  updateMatrix = 1;
88  gMin = srcFactor = 0;
89  eqns = new eqnsys<nr_type_t> ();
90 }
91 
92 // Destructor deletes the nasolver class object.
93 template <class nr_type_t>
95  if (nlist) delete nlist;
96  if (C) delete C;
97  delete A;
98  delete z;
99  delete x;
100  delete xprev;
101  delete zprev;
102  delete eqns;
103 }
104 
105 /* The copy constructor creates a new instance of the nasolver class
106  based on the given nasolver object. */
107 template <class nr_type_t>
109  nlist = o.nlist ? new nodelist (*(o.nlist)) : NULL;
110  A = o.A ? new tmatrix<nr_type_t> (*(o.A)) : NULL;
111  C = o.C ? new tmatrix<nr_type_t> (*(o.C)) : NULL;
112  z = o.z ? new tvector<nr_type_t> (*(o.z)) : NULL;
113  x = o.x ? new tvector<nr_type_t> (*(o.x)) : NULL;
114  xprev = zprev = NULL;
115  reltol = o.reltol;
116  abstol = o.abstol;
117  vntol = o.vntol;
118  desc = o.desc;
119  calculate_func = o.calculate_func;
121  eqnAlgo = o.eqnAlgo;
123  fixpoint = o.fixpoint;
124  gMin = o.gMin;
125  srcFactor = o.srcFactor;
126  eqns = new eqnsys<nr_type_t> (*(o.eqns));
127  solution = nasolution<nr_type_t> (o.solution);
128 }
129 
130 /* The function runs the nodal analysis solver once, reports errors if
131  any and save the results into each circuit. */
132 template <class nr_type_t>
134  qucs::exception * e;
135  int error = 0, d;
136 
137  // run the calculation function for each circuit
138  calculate ();
139 
140  // generate A matrix and z vector
141  createMatrix ();
142 
143  // solve equation system
144  try_running () {
145  runMNA ();
146  }
147  // appropriate exception handling
148  catch_exception () {
151  d = top_exception()->getData (); pop_exception ();
152  if (d >= countNodes ()) {
153  d -= countNodes ();
154  e->setText ("voltage source `%s' conflicts with some other voltage "
155  "source", findVoltageSource(d)->getName ());
156  }
157  else {
158  e->setText ("circuit admittance matrix in %s solver is singular at "
159  "node `%s' connected to [%s]", desc, nlist->get (d),
160  nlist->getNodeString (d));
161  }
162  throw_exception (e);
163  error++;
164  break;
165  case EXCEPTION_SINGULAR:
166  do {
167  d = top_exception()->getData (); pop_exception ();
168  if (d < countNodes ()) {
169  logprint (LOG_ERROR, "WARNING: %s: inserted virtual resistance at "
170  "node `%s' connected to [%s]\n", getName (), nlist->get (d),
171  nlist->getNodeString (d));
172  }
173  }
174  while (top_exception() != NULL &&
175  top_exception()->getCode () == EXCEPTION_SINGULAR);
176  break;
177  default:
178  estack.print ();
179  break;
180  }
181 
182  // save results into circuits
183  if (!error) saveSolution ();
184  return error;
185 }
186 
187 /* Run this function after the actual solver run and before evaluating
188  the results. */
189 template <class nr_type_t>
191  delete nlist; nlist = NULL;
192 }
193 
194 /* Run this function before the actual solver. */
195 template <class nr_type_t>
197  // create node list, enumerate nodes and voltage sources
198 #if DEBUG
199  logprint (LOG_STATUS, "NOTIFY: %s: creating node list for %s analysis\n",
200  getName (), desc);
201 #endif
202  nlist = new nodelist (subnet);
203  nlist->assignNodes ();
204  assignVoltageSources ();
205 #if DEBUG && 0
206  nlist->print ();
207 #endif
208 
209  // create matrix, solution vector and right hand side vector
210  int M = countVoltageSources ();
211  int N = countNodes ();
212  if (A != NULL) delete A; A = new tmatrix<nr_type_t> (M + N);
213  if (z != NULL) delete z; z = new tvector<nr_type_t> (N + M);
214  if (x != NULL) delete x; x = new tvector<nr_type_t> (N + M);
215 
216 #if DEBUG
217  logprint (LOG_STATUS, "NOTIFY: %s: solving %s netlist\n", getName (), desc);
218 #endif
219 }
220 
221 /* This function goes through the nodeset list of the current netlist
222  and applies the stored values to the current solution vector. Then
223  the function saves the solution vector back into the actual
224  component nodes. */
225 template <class nr_type_t>
227  if (x == NULL || nlist == NULL) return;
228 
229  // set each solution to zero
230  if (nokeep) for (int i = 0; i < x->getSize (); i++) x->set (i, 0);
231 
232  // then apply the nodeset itself
233  for (nodeset * n = subnet->getNodeset (); n; n = n->getNext ()) {
234  struct nodelist_t * nl = nlist->getNode (n->getName ());
235  if (nl != NULL) {
236  x->set (nl->n, n->getValue ());
237  }
238  else {
239  logprint (LOG_ERROR, "WARNING: %s: no such node `%s' found, cannot "
240  "initialize node\n", getName (), n->getName ());
241  }
242  }
243  if (xprev != NULL) *xprev = *x;
244  saveSolution ();
245 }
246 
247 /* The following function uses the gMin-stepping algorithm in order to
248  solve the given non-linear netlist by continuous iterations. */
249 template <class nr_type_t>
251  qucs::exception * e;
252  int convergence, run = 0, MaxIterations, error = 0;
253  nr_double_t gStep, gPrev;
254 
255  // fetch simulation properties
256  MaxIterations = getPropertyInteger ("MaxIter") / 4 + 1;
257  updateMatrix = 1;
258  fixpoint = 0;
259 
260  // initialize the stepper
261  gPrev = gMin = 0.01;
262  gStep = gMin / 100;
263  gMin -= gStep;
264 
265  do {
266  // run solving loop until convergence is reached
267  run = 0;
268  do {
269  error = solve_once ();
270  if (!error) {
271  // convergence check
272  convergence = (run > 0) ? checkConvergence () : 0;
273  savePreviousIteration ();
274  run++;
275  }
276  else break;
277  }
278  while (!convergence && run < MaxIterations);
279  iterations += run;
280 
281  // not yet converged, so decreased the gMin-step
282  if (run >= MaxIterations || error) {
283  gStep /= 2;
284  // here the absolute minimum step checker
285  if (gStep < NR_EPSI) {
286  error = 1;
288  e->setText ("no convergence in %s analysis after %d gMinStepping "
289  "iterations", desc, iterations);
290  throw_exception (e);
291  break;
292  }
293  gMin = MAX (gPrev - gStep, 0);
294  }
295  // converged, increased the gMin-step
296  else {
297  gPrev = gMin;
298  gMin = MAX (gMin - gStep, 0);
299  gStep *= 2;
300  }
301  }
302  // continue until no additional resistances is necessary
303  while (gPrev > 0);
304 
305  return error;
306 }
307 
308 /* The following function uses the source-stepping algorithm in order
309  to solve the given non-linear netlist by continuous iterations. */
310 template <class nr_type_t>
312  qucs::exception * e;
313  int convergence, run = 0, MaxIterations, error = 0;
314  nr_double_t sStep, sPrev;
315 
316  // fetch simulation properties
317  MaxIterations = getPropertyInteger ("MaxIter") / 4 + 1;
318  updateMatrix = 1;
319  fixpoint = 0;
320 
321  // initialize the stepper
322  sPrev = srcFactor = 0;
323  sStep = 0.01;
324  srcFactor += sStep;
325 
326  do {
327  // run solving loop until convergence is reached
328  run = 0;
329  do {
330  subnet->setSrcFactor (srcFactor);
331  error = solve_once ();
332  if (!error) {
333  // convergence check
334  convergence = (run > 0) ? checkConvergence () : 0;
335  savePreviousIteration ();
336  run++;
337  }
338  else break;
339  }
340  while (!convergence && run < MaxIterations);
341  iterations += run;
342 
343  // not yet converged, so decreased the source-step
344  if (run >= MaxIterations || error) {
345  if (error)
346  sStep *= 0.1;
347  else
348  sStep *= 0.5;
349  restorePreviousIteration ();
350  saveSolution ();
351  // here the absolute minimum step checker
352  if (sStep < NR_EPSI) {
353  error = 1;
355  e->setText ("no convergence in %s analysis after %d sourceStepping "
356  "iterations", desc, iterations);
357  throw_exception (e);
358  break;
359  }
360  srcFactor = MIN (sPrev + sStep, 1);
361  }
362  // converged, increased the source-step
363  else if (run < MaxIterations / 4) {
364  sPrev = srcFactor;
365  srcFactor = MIN (srcFactor + sStep, 1);
366  sStep *= 1.5;
367  }
368  else {
369  srcFactor = MIN (srcFactor + sStep, 1);
370  }
371  }
372  // continue until no source factor is necessary
373  while (sPrev < 1);
374 
375  subnet->setSrcFactor (1);
376  return error;
377 }
378 
379 /* The function returns an appropriate text representation for the
380  currently used convergence helper algorithm. */
381 template <class nr_type_t>
383  if (convHelper == CONV_Attenuation) {
384  return "RHS attenuation";
385  }
386  else if (convHelper == CONV_LineSearch) {
387  return "line search";
388  }
389  else if (convHelper == CONV_SteepestDescent) {
390  return "steepest descent";
391  }
392  else if (convHelper == CONV_GMinStepping) {
393  return "gMin stepping";
394  }
395  else if (convHelper == CONV_SourceStepping) {
396  return "source stepping";
397  }
398  return "none";
399 }
400 
401 /* This is the non-linear iterative nodal analysis netlist solver. */
402 template <class nr_type_t>
404  qucs::exception * e;
405  int convergence, run = 0, MaxIterations, error = 0;
406 
407  // fetch simulation properties
408  MaxIterations = getPropertyInteger ("MaxIter");
409  reltol = getPropertyDouble ("reltol");
410  abstol = getPropertyDouble ("abstol");
411  vntol = getPropertyDouble ("vntol");
412  updateMatrix = 1;
413 
414  // yet another non-linear solver
415  if (convHelper == CONV_GMinStepping) {
416  iterations = 0;
417  error = solve_nonlinear_continuation_gMin ();
418  return error;
419  }
420  else if (convHelper == CONV_SourceStepping) {
421  iterations = 0;
422  error = solve_nonlinear_continuation_Source ();
423  return error;
424  }
425 
426  // run solving loop until convergence is reached
427  do {
428  error = solve_once ();
429  if (!error) {
430  // convergence check
431  convergence = (run > 0) ? checkConvergence () : 0;
432  savePreviousIteration ();
433  run++;
434  // control fixpoint iterations
435  if (fixpoint) {
436  if (convergence && !updateMatrix) {
437  updateMatrix = 1;
438  convergence = 0;
439  }
440  else updateMatrix = 0;
441  }
442  }
443  else break;
444  }
445  while (!convergence &&
446  run < MaxIterations * (1 + convHelper ? 1 : 0));
447 
448  if (run >= MaxIterations || error) {
450  e->setText ("no convergence in %s analysis after %d iterations",
451  desc, run);
452  throw_exception (e);
453  error++;
454  }
455  iterations = run;
456  return error;
457 }
458 
459 /* This is the linear nodal analysis netlist solver. */
460 template <class nr_type_t>
462  updateMatrix = 1;
463  return solve_once ();
464 }
465 
466 /* Applying the MNA (Modified Nodal Analysis) to a circuit with
467  passive elements and independent current and voltage sources
468  results in a matrix equation of the form Ax = z. This function
469  generates the A and z matrix. */
470 template <class nr_type_t>
472 
473  /* Generate the A matrix. The A matrix consists of four (4) minor
474  matrices in the form +- -+
475  A = | G B |
476  | C D |
477  +- -+.
478  Each of these minor matrices is going to be generated here. */
479  if (updateMatrix) {
480  createGMatrix ();
481  createBMatrix ();
482  createCMatrix ();
483  createDMatrix ();
484  }
485 
486  /* Adjust G matrix if requested. */
487  if (convHelper == CONV_GMinStepping) {
488  int N = countNodes ();
489  int M = countVoltageSources ();
490  for (int n = 0; n < N + M; n++) {
491  A->set (n, n, A->get (n, n) + gMin);
492  }
493  }
494 
495  /* Generate the z Matrix. The z Matrix consists of two (2) minor
496  matrices in the form +- -+
497  z = | i |
498  | e |
499  +- -+.
500  Each of these minor matrices is going to be generated here. */
501  createZVector ();
502 }
503 
504 /* This MatVal() functionality is just helper to get the correct
505  values from the circuit's matrices. The additional (unused)
506  argument is used to differentiate between the two possible
507  types. */
508 #define MatVal(x) MatValX (x, (nr_type_t *) 0)
509 
510 template <class nr_type_t>
512  return z;
513 }
514 
515 template <class nr_type_t>
516 nr_type_t nasolver<nr_type_t>::MatValX (nr_complex_t z, nr_double_t *) {
517  return real (z);
518 }
519 
520 /* The B matrix is an MxN matrix with only 0, 1 and -1 elements. Each
521  location in the matrix corresponds to a particular voltage source
522  (first dimension) or a node (second dimension). If the positive
523  terminal of the ith voltage source is connected to node k, then the
524  element (i,k) in the B matrix is a 1. If the negative terminal of
525  the ith voltage source is connected to node k, then the element
526  (i,k) in the B matrix is a -1. Otherwise, elements of the B matrix
527  are zero. */
528 template <class nr_type_t>
530  int N = countNodes ();
531  int M = countVoltageSources ();
532  circuit * vs;
533  struct nodelist_t * n;
534  nr_type_t val;
535 
536  // go through each voltage sources (first dimension)
537  for (int c = 0; c < M; c++) {
538  vs = findVoltageSource (c);
539  // go through each node (second dimension)
540  for (int r = 0; r < N; r++) {
541  val = 0.0;
542  n = nlist->getNode (r);
543  for (int i = 0; i < n->nNodes; i++) {
544  // is voltage source connected to node ?
545  if (n->nodes[i]->getCircuit () == vs) {
546  val += MatVal (vs->getB (n->nodes[i]->getPort (), c));
547  }
548  }
549  // put value into B matrix
550  A->set (r, c + N, val);
551  }
552  }
553 }
554 
555 /* The C matrix is an NxM matrix with only 0, 1 and -1 elements. Each
556  location in the matrix corresponds to a particular node (first
557  dimension) or a voltage source (first dimension). If the positive
558  terminal of the ith voltage source is connected to node k, then the
559  element (k,i) in the C matrix is a 1. If the negative terminal of
560  the ith voltage source is connected to node k, then the element
561  (k,i) in the C matrix is a -1. Otherwise, elements of the C matrix
562  are zero. */
563 template <class nr_type_t>
565  int N = countNodes ();
566  int M = countVoltageSources ();
567  circuit * vs;
568  struct nodelist_t * n;
569  nr_type_t val;
570 
571  // go through each voltage sources (second dimension)
572  for (int r = 0; r < M; r++) {
573  vs = findVoltageSource (r);
574  // go through each node (first dimension)
575  for (int c = 0; c < N; c++) {
576  val = 0.0;
577  n = nlist->getNode (c);
578  for (int i = 0; i < n->nNodes; i++) {
579  // is voltage source connected to node ?
580  if (n->nodes[i]->getCircuit () == vs) {
581  val += MatVal (vs->getC (r, n->nodes[i]->getPort ()));
582  }
583  }
584  // put value into C matrix
585  A->set (r + N, c, val);
586  }
587  }
588 }
589 
590 /* The D matrix is an MxM matrix that is composed entirely of zeros.
591  It can be non-zero if dependent sources are considered. */
592 template <class nr_type_t>
594  int M = countVoltageSources ();
595  int N = countNodes ();
596  circuit * vsr, * vsc;
597  nr_type_t val;
598  for (int r = 0; r < M; r++) {
599  vsr = findVoltageSource (r);
600  for (int c = 0; c < M; c++) {
601  vsc = findVoltageSource (c);
602  val = 0.0;
603  if (vsr == vsc) {
604  val = MatVal (vsr->getD (r, c));
605  }
606  A->set (r + N, c + N, val);
607  }
608  }
609 }
610 
611 /* The G matrix is an NxN matrix formed in two steps.
612  1. Each element in the diagonal matrix is equal to the sum of the
613  conductance of each element connected to the corresponding node.
614  2. The off diagonal elements are the negative conductance of the
615  element connected to the pair of corresponding nodes. Therefore a
616  resistor between nodes 1 and 2 goes into the G matrix at location
617  (1,2) and location (2,1). If an element is grounded, it will only
618  have contribute to one entry in the G matrix -- at the appropriate
619  location on the diagonal. */
620 template <class nr_type_t>
622  int pr, pc, N = countNodes ();
623  nr_type_t g;
624  struct nodelist_t * nr, * nc;
625  circuit * ct;
626 
627  // go through each column of the G matrix
628  for (int c = 0; c < N; c++) {
629  nc = nlist->getNode (c);
630  // go through each row of the G matrix
631  for (int r = 0; r < N; r++) {
632  nr = nlist->getNode (r);
633  g = 0.0;
634  // sum up the conductance of each connected circuit
635  for (int a = 0; a < nc->nNodes; a++)
636  for (int b = 0; b < nr->nNodes; b++)
637  if (nc->nodes[a]->getCircuit () == nr->nodes[b]->getCircuit ()) {
638  ct = nc->nodes[a]->getCircuit ();
639  pc = nc->nodes[a]->getPort ();
640  pr = nr->nodes[b]->getPort ();
641  g += MatVal (ct->getY (pr, pc));
642  }
643  // put value into G matrix
644  A->set (r, c, g);
645  }
646  }
647 }
648 
649 /* The following function creates the (N+M)x(N+M) noise current
650  correlation matrix used during the AC noise computations. */
651 template <class nr_type_t>
653  int pr, pc, N = countNodes ();
654  int M = countVoltageSources ();
655  struct nodelist_t * n;
656  nr_type_t val;
657  int r, c, a, b, ri, ci, i;
658  struct nodelist_t * nr, * nc;
659  circuit * ct;
660 
661  // create new Cy matrix if necessary
662  if (C != NULL) delete C; C = new tmatrix<nr_type_t> (N + M);
663 
664  // go through each column of the Cy matrix
665  for (c = 0; c < N; c++) {
666  nc = nlist->getNode (c);
667  // go through each row of the Cy matrix
668  for (r = 0; r < N; r++) {
669  nr = nlist->getNode (r);
670  val = 0.0;
671  // sum up the noise-correlation of each connected circuit
672  for (a = 0; a < nc->nNodes; a++)
673  for (b = 0; b < nr->nNodes; b++)
674  if (nc->nodes[a]->getCircuit () == nr->nodes[b]->getCircuit ()) {
675  ct = nc->nodes[a]->getCircuit ();
676  pc = nc->nodes[a]->getPort ();
677  pr = nr->nodes[b]->getPort ();
678  val += MatVal (ct->getN (pr, pc));
679  }
680  // put value into Cy matrix
681  C->set (r, c, val);
682  }
683  }
684 
685  // go through each additional voltage source and put coefficients into
686  // the noise current correlation matrix
687  circuit * vsr, * vsc;
688  for (r = 0; r < M; r++) {
689  vsr = findVoltageSource (r);
690  for (c = 0; c < M; c++) {
691  vsc = findVoltageSource (c);
692  val = 0.0;
693  if (vsr == vsc) {
694  ri = vsr->getSize () + r - vsr->getVoltageSource ();
695  ci = vsc->getSize () + c - vsc->getVoltageSource ();
696  val = MatVal (vsr->getN (ri, ci));
697  }
698  C->set (r + N, c + N, val);
699  }
700  }
701 
702  // go through each additional voltage source
703  for (r = 0; r < M; r++) {
704  vsr = findVoltageSource (r);
705  // go through each node
706  for (c = 0; c < N; c++) {
707  val = 0.0;
708  n = nlist->getNode (c);
709  for (i = 0; i < n->nNodes; i++) {
710  // is voltage source connected to node ?
711  if (n->nodes[i]->getCircuit () == vsr) {
712  ri = vsr->getSize () + r - vsr->getVoltageSource ();
713  ci = n->nodes[i]->getPort ();
714  val += MatVal (vsr->getN (ri, ci));
715  }
716  }
717  // put value into Cy matrix
718  C->set (r + N, c, val);
719  }
720  }
721 
722  // go through each voltage source
723  for (c = 0; c < M; c++) {
724  vsc = findVoltageSource (c);
725  // go through each node
726  for (r = 0; r < N; r++) {
727  val = 0.0;
728  n = nlist->getNode (r);
729  for (i = 0; i < n->nNodes; i++) {
730  // is voltage source connected to node ?
731  if (n->nodes[i]->getCircuit () == vsc) {
732  ci = vsc->getSize () + c - vsc->getVoltageSource ();
733  ri = n->nodes[i]->getPort ();
734  val += MatVal (vsc->getN (ri, ci));
735  }
736  }
737  // put value into Cy matrix
738  C->set (r, c + N, val);
739  }
740  }
741 
742 }
743 
744 /* The i matrix is an 1xN matrix with each element of the matrix
745  corresponding to a particular node. The value of each element of i
746  is determined by the sum of current sources into the corresponding
747  node. If there are no current sources connected to the node, the
748  value is zero. */
749 template <class nr_type_t>
751  int N = countNodes ();
752  nr_type_t val;
753  struct nodelist_t * n;
754  circuit * is;
755 
756  // go through each node
757  for (int r = 0; r < N; r++) {
758  val = 0.0;
759  n = nlist->getNode (r);
760  // go through each circuit connected to the node
761  for (int i = 0; i < n->nNodes; i++) {
762  is = n->nodes[i]->getCircuit ();
763  // is this a current source ?
764  if (is->isISource () || is->isNonLinear ()) {
765  val += MatVal (is->getI (n->nodes[i]->getPort ()));
766  }
767  }
768  // put value into i vector
769  z->set (r, val);
770  }
771 }
772 
773 /* The e matrix is a 1xM matrix with each element of the matrix equal
774  in value to the corresponding independent voltage source. */
775 template <class nr_type_t>
777  int N = countNodes ();
778  int M = countVoltageSources ();
779  nr_type_t val;
780  circuit * vs;
781 
782  // go through each voltage source
783  for (int r = 0; r < M; r++) {
784  vs = findVoltageSource (r);
785  val = MatVal (vs->getE (r));
786  // put value into e vector
787  z->set (r + N, val);
788  }
789 }
790 
791 // The function loads the right hand side vector.
792 template <class nr_type_t>
794  createIVector ();
795  createEVector ();
796 }
797 
798 // Returns the number of nodes in the nodelist, excluding the ground node.
799 template <class nr_type_t>
801  return nlist->length () - 1;
802 }
803 
804 // Returns the node number of the give node name.
805 template <class nr_type_t>
807  return nlist->getNodeNr (str);
808 }
809 
810 /* The function returns the assigned node number for the port of the
811  given circuits. It returns -1 if there is no such node. */
812 template <class nr_type_t>
814  int N = countNodes ();
815  struct nodelist_t * n;
816  for (int r = 0; r < N; r++) {
817  n = nlist->getNode (r);
818  for (int i = 0; i < n->nNodes; i++)
819  if (c == n->nodes[i]->getCircuit ())
820  if (port == n->nodes[i]->getPort ())
821  return r;
822  }
823  return -1;
824 }
825 
826 // Returns the number of voltage sources in the nodelist.
827 template <class nr_type_t>
829  return subnet->getVoltageSources ();
830 }
831 
832 /* The function returns the voltage source circuit object
833  corresponding to the given number. If there is no such voltage
834  source it returns NULL. */
835 template <class nr_type_t>
837  circuit * root = subnet->getRoot ();
838  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
839  if (n >= c->getVoltageSource () &&
840  n <= c->getVoltageSource () + c->getVoltageSources () - 1)
841  return c;
842  }
843  return NULL;
844 }
845 
846 /* The function applies unique voltage source identifiers to each
847  voltage source (explicit and built in internal ones) in the list of
848  registered circuits. */
849 template <class nr_type_t>
851  circuit * root = subnet->getRoot ();
852  int nSources = 0;
853  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
854  if (c->getVoltageSources () > 0) {
855  c->setVoltageSource (nSources);
856  nSources += c->getVoltageSources ();
857  }
858  }
859  subnet->setVoltageSources (nSources);
860 }
861 
862 /* The matrix equation Ax = z is solved by x = A^-1*z. The function
863  applies the operation to the previously generated matrices. */
864 template <class nr_type_t>
866 
867  // just solve the equation system here
868  eqns->setAlgo (eqnAlgo);
869  eqns->passEquationSys (updateMatrix ? A : NULL, x, z);
870  eqns->solve ();
871 
872  // if damped Newton-Raphson is requested
873  if (xprev != NULL && top_exception () == NULL) {
874  if (convHelper == CONV_Attenuation) {
875  applyAttenuation ();
876  } else if (convHelper == CONV_LineSearch) {
877  lineSearch ();
878  } else if (convHelper == CONV_SteepestDescent) {
879  steepestDescent ();
880  }
881  }
882 }
883 
884 /* This function applies a damped Newton-Raphson (limiting scheme) to
885  the current solution vector in the form x1 = x0 + a * (x1 - x0). This
886  convergence helper is heuristic and does not ensure global convergence. */
887 template <class nr_type_t>
889  nr_double_t alpha = 1.0, nMax;
890 
891  // create solution difference vector and find maximum deviation
892  tvector<nr_type_t> dx = *x - *xprev;
893  nMax = maxnorm (dx);
894 
895  // compute appropriate damping factor
896  if (nMax > 0.0) {
897  nr_double_t g = 1.0;
898  alpha = MIN (0.9, g / nMax);
899  if (alpha < 0.1) alpha = 0.1;
900  }
901 
902  // apply damped solution vector
903  *x = *xprev + alpha * dx;
904 }
905 
906 /* This is damped Newton-Raphson using nested iterations in order to
907  find a better damping factor. It identifies a damping factor in
908  the interval [0,1] which minimizes the right hand side vector. The
909  algorithm actually ensures global convergence but pushes the
910  solution to local minimums, i.e. where the Jacobian matrix A may be
911  singular. */
912 template <class nr_type_t>
914  nr_double_t alpha = 0.5, n, nMin, aprev = 1.0, astep = 0.5, adiff;
915  int dir = -1;
916 
917  // compute solution deviation vector
918  tvector<nr_type_t> dx = *x - *xprev;
919  nMin = NR_MAX;
920 
921  do {
922  // apply current damping factor and see what happens
923  *x = *xprev + alpha * dx;
924 
925  // recalculate Jacobian and right hand side
926  saveSolution ();
927  calculate ();
928  createZVector ();
929 
930  // calculate norm of right hand side vector
931  n = norm (*z);
932 
933  // TODO: this is not perfect, but usable
934  astep /= 2;
935  adiff = fabs (alpha - aprev);
936  if (adiff > 0.005) {
937  aprev = alpha;
938  if (n < nMin) {
939  nMin = n;
940  if (alpha == 1) dir = -dir;
941  alpha += astep * dir;
942  } else {
943  dir = -dir;
944  alpha += 1.5 * astep * dir;
945  }
946  }
947  }
948  while (adiff > 0.005);
949 
950  // apply final damping factor
951  assert (alpha > 0 && alpha <= 1);
952  *x = *xprev + alpha * dx;
953 }
954 
955 /* The function looks for the optimal gradient for the right hand side
956  vector using the so-called 'steepest descent' method. Though
957  better than the one-dimensional linesearch (it doesn't push
958  iterations into local minimums) it converges painfully slow. */
959 template <class nr_type_t>
961  nr_double_t alpha = 1.0, sl, n;
962 
963  // compute solution deviation vector
964  tvector<nr_type_t> dx = *x - *xprev;
965  tvector<nr_type_t> dz = *z - *zprev;
966  n = norm (*zprev);
967 
968  do {
969  // apply current damping factor and see what happens
970  *x = *xprev + alpha * dx;
971 
972  // recalculate Jacobian and right hand side
973  saveSolution ();
974  calculate ();
975  createZVector ();
976 
977  // check gradient criteria, ThinkME: Is this correct?
978  dz = *z - *zprev;
979  sl = real (sum (dz * -dz));
980  if (norm (*z) < n + alpha * sl) break;
981  alpha *= 0.7;
982  }
983  while (alpha > 0.001);
984 
985  // apply final damping factor
986  *x = *xprev + alpha * dx;
987 }
988 
989 /* The function checks whether the iterative algorithm for linearizing
990  the non-linear components in the network shows convergence. It
991  returns non-zero if it converges and zero otherwise. */
992 template <class nr_type_t>
994  int N = countNodes ();
995  int M = countVoltageSources ();
996  nr_double_t v_abs, v_rel, i_abs, i_rel;
997  int r;
998 
999  for (r = 0; r < N; r++) {
1000  v_abs = abs (x->get (r) - xprev->get (r));
1001  v_rel = abs (x->get (r));
1002  if (v_abs >= vntol + reltol * v_rel) return 0;
1003  if (!convHelper) {
1004  i_abs = abs (z->get (r) - zprev->get (r));
1005  i_rel = abs (z->get (r));
1006  if (i_abs >= abstol + reltol * i_rel) return 0;
1007  }
1008  }
1009  for (r = 0; r < M; r++) {
1010  i_abs = abs (x->get (r + N) - xprev->get (r + N));
1011  i_rel = abs (x->get (r + N));
1012  if (i_abs >= abstol + reltol * i_rel) return 0;
1013  if (!convHelper) {
1014  v_abs = abs (z->get (r + N) - zprev->get (r + N));
1015  v_rel = abs (z->get (r + N));
1016  if (v_abs >= vntol + reltol * v_rel) return 0;
1017  }
1018  }
1019  return 1;
1020 }
1021 
1022 /* The function saves the solution and right hand vector of the previous
1023  iteration. */
1024 template <class nr_type_t>
1026  if (xprev != NULL)
1027  *xprev = *x;
1028  else
1029  xprev = new tvector<nr_type_t> (*x);
1030  if (zprev != NULL)
1031  *zprev = *z;
1032  else
1033  zprev = new tvector<nr_type_t> (*z);
1034 }
1035 
1036 /* The function restores the solution and right hand vector of the
1037  previous (successful) iteration. */
1038 template <class nr_type_t>
1040  if (xprev != NULL) *x = *xprev;
1041  if (zprev != NULL) *z = *zprev;
1042 }
1043 
1044 /* The function restarts the NR iteration for each non-linear
1045  circuit. */
1046 template <class nr_type_t>
1048  circuit * root = subnet->getRoot ();
1049  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
1050  if (c->isNonLinear ()) c->restartDC ();
1051  }
1052 }
1053 
1054 /* This function goes through solution (the x vector) and saves the
1055  node voltages of the last iteration into each non-linear
1056  circuit. */
1057 template <class nr_type_t>
1059  int N = countNodes ();
1060  struct nodelist_t * n;
1061  // save all nodes except reference node
1062  for (int r = 0; r < N; r++) {
1063  n = nlist->getNode (r);
1064  for (int i = 0; i < n->nNodes; i++) {
1065  n->nodes[i]->getCircuit()->setV (n->nodes[i]->getPort (), x->get (r));
1066  }
1067  }
1068  // save reference node
1069  n = nlist->getNode (-1);
1070  for (int i = 0; i < n->nNodes; i++) {
1071  n->nodes[i]->getCircuit()->setV (n->nodes[i]->getPort (), 0.0);
1072  }
1073 }
1074 
1075 /* This function goes through solution (the x vector) and saves the
1076  branch currents through the voltage sources of the last iteration
1077  into each circuit. */
1078 template <class nr_type_t>
1080  int N = countNodes ();
1081  int M = countVoltageSources ();
1082  circuit * vs;
1083  // save all branch currents of voltage sources
1084  for (int r = 0; r < M; r++) {
1085  vs = findVoltageSource (r);
1086  vs->setJ (r, x->get (r + N));
1087  }
1088 }
1089 
1090 // The function saves the solution vector into each circuit.
1091 template <class nr_type_t>
1093  saveNodeVoltages ();
1094  saveBranchCurrents ();
1095 }
1096 
1097 // This function stores the solution (node voltages and branch currents).
1098 template <class nr_type_t>
1100  // cleanup solution previously
1101  solution.clear ();
1102  int r;
1103  int N = countNodes ();
1104  int M = countVoltageSources ();
1105  // store all nodes except reference node
1106  for (r = 0; r < N; r++) {
1107  struct nodelist_t * n = nlist->getNode (r);
1108  solution.add (n->name, x->get (r), 0);
1109  }
1110  // store all branch currents of voltage sources
1111  for (r = 0; r < M; r++) {
1112  circuit * vs = findVoltageSource (r);
1113  int vn = r - vs->getVoltageSource () + 1;
1114  solution.add (vs->getName (), x->get (r + N), vn);
1115  }
1116 }
1117 
1118 // This function recalls the solution (node voltages and branch currents).
1119 template <class nr_type_t>
1121  int r;
1122  int N = countNodes ();
1123  int M = countVoltageSources ();
1124  naentry<nr_type_t> * na;
1125  // store all nodes except reference node
1126  for (r = 0; r < N; r++) {
1127  struct nodelist_t * n = nlist->getNode (r);
1128  if ((na = solution.find (n->name, 0)) != NULL)
1129  x->set (r, na->value);
1130  }
1131  // store all branch currents of voltage sources
1132  for (r = 0; r < M; r++) {
1133  circuit * vs = findVoltageSource (r);
1134  int vn = r - vs->getVoltageSource () + 1;
1135  if ((na = solution.find (vs->getName (), vn)) != NULL)
1136  x->set (r + N, na->value);
1137  }
1138 }
1139 
1140 /* This function saves the results of a single solve() functionality
1141  into the output dataset. */
1142 template <class nr_type_t>
1143 void nasolver<nr_type_t>::saveResults (const char * volts, const char * amps,
1144  int saveOPs, vector * f) {
1145  int N = countNodes ();
1146  int M = countVoltageSources ();
1147  char * n;
1148 
1149  // add node voltage variables
1150  if (volts) {
1151  for (int r = 0; r < N; r++) {
1152  if ((n = createV (r, volts, saveOPs)) != NULL) {
1153  saveVariable (n, x->get (r), f);
1154  free (n);
1155  }
1156  }
1157  }
1158 
1159  // add branch current variables
1160  if (amps) {
1161  for (int r = 0; r < M; r++) {
1162  if ((n = createI (r, amps, saveOPs)) != NULL) {
1163  saveVariable (n, x->get (r + N), f);
1164  free (n);
1165  }
1166  }
1167  }
1168 
1169  // add voltage probe data
1170  if (volts) {
1171  circuit * root = subnet->getRoot ();
1172  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
1173  if (!c->isProbe ()) continue;
1174  if (c->getSubcircuit () && !(saveOPs & SAVE_ALL)) continue;
1175  if (strcmp (volts, "vn"))
1176  c->saveOperatingPoints ();
1177  n = createOP (c->getName (), volts);
1178  saveVariable (n, rect (c->getOperatingPoint ("Vr"),
1179  c->getOperatingPoint ("Vi")), f);
1180  free (n);
1181  }
1182  }
1183 
1184  // save operating points of non-linear circuits if requested
1185  if (saveOPs & SAVE_OPS) {
1186  circuit * root = subnet->getRoot ();
1187  for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
1188  if (!c->isNonLinear ()) continue;
1189  if (c->getSubcircuit () && !(saveOPs & SAVE_ALL)) continue;
1190  c->calcOperatingPoints ();
1191  valuelistiterator<operatingpoint> it (c->getOperatingPoints ());
1192  for (; *it; ++it) {
1193  operatingpoint * p = it.currentVal ();
1194  n = createOP (c->getName (), p->getName ());
1195  saveVariable (n, p->getValue (), f);
1196  free (n);
1197  }
1198  }
1199  }
1200 }
1201 
1202 /* Create an appropriate variable name for operating points. The
1203  caller is responsible to free() the returned string. */
1204 template <class nr_type_t>
1205 char * nasolver<nr_type_t>::createOP (const char * c, const char * n) {
1206  char * text = (char *) malloc (strlen (c) + strlen (n) + 2);
1207  sprintf (text, "%s.%s", c, n);
1208  return text;
1209 }
1210 
1211 /* Creates an appropriate variable name for voltages. The caller is
1212  responsible to free() the returned string. */
1213 template <class nr_type_t>
1214 char * nasolver<nr_type_t>::createV (int n, const char * volts, int saveOPs) {
1215  if (nlist->isInternal (n)) return NULL;
1216  char * node = nlist->get (n);
1217  if (strchr (node, '.') && !(saveOPs & SAVE_ALL)) return NULL;
1218  char * text = (char *) malloc (strlen (node) + 2 + strlen (volts));
1219  sprintf (text, "%s.%s", node, volts);
1220  return text;
1221 }
1222 
1223 /* Create an appropriate variable name for currents. The caller is
1224  responsible to free() the returned string. */
1225 template <class nr_type_t>
1226 char * nasolver<nr_type_t>::createI (int n, const char * amps, int saveOPs) {
1227  circuit * vs = findVoltageSource (n);
1228 
1229  // don't output internal (helper) voltage sources
1230  if (vs->isInternalVoltageSource ())
1231  return NULL;
1232 
1233  /* save only current through real voltage sources and explicit
1234  current probes */
1235  if (!vs->isVSource () && !(saveOPs & SAVE_OPS))
1236  return NULL;
1237 
1238  // don't output subcircuit components if not requested
1239  if (vs->getSubcircuit () && !(saveOPs & SAVE_ALL))
1240  return NULL;
1241 
1242  // create appropriate current name for single/multiple voltage sources
1243  char * name = vs->getName ();
1244  char * text = (char *) malloc (strlen (name) + 4 + strlen (amps));
1245  if (vs->getVoltageSources () > 1)
1246  sprintf (text, "%s.%s%d", name, amps, n - vs->getVoltageSource () + 1);
1247  else
1248  sprintf (text, "%s.%s", name, amps);
1249  return text;
1250 }