Branch data Line data Source code
1 : : /*
2 : : * hbsolver.cpp - harmonic balance solver class implementation
3 : : *
4 : : * Copyright (C) 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$
22 : : *
23 : : */
24 : :
25 : : #if HAVE_CONFIG_H
26 : : # include <config.h>
27 : : #endif
28 : :
29 : : #include <stdio.h>
30 : :
31 : : #include "object.h"
32 : : #include "logging.h"
33 : : #include "complex.h"
34 : : #include "vector.h"
35 : : #include "node.h"
36 : : #include "circuit.h"
37 : : #include "component_id.h"
38 : : #include "net.h"
39 : : #include "netdefs.h"
40 : : #include "strlist.h"
41 : : #include "ptrlist.h"
42 : : #include "tvector.h"
43 : : #include "tmatrix.h"
44 : : #include "eqnsys.h"
45 : : #include "analysis.h"
46 : : #include "dataset.h"
47 : : #include "fourier.h"
48 : : #include "hbsolver.h"
49 : :
50 : : #define HB_DEBUG 0
51 : :
52 : : namespace qucs {
53 : :
54 : : using namespace fourier;
55 : :
56 : : // Constructor creates an unnamed instance of the hbsolver class.
57 [ + - ][ + - ]: 1 : hbsolver::hbsolver () : analysis () {
[ + - ][ # # ]
[ # # ][ # # ]
58 : 1 : type = ANALYSIS_HBALANCE;
59 : 1 : frequency = 0;
60 : 1 : nlnodes = lnnodes = banodes = nanodes = NULL;
61 : 1 : Y = Z = A = NULL;
62 : 1 : NA = YV = JQ = JG = JF = NULL;
63 : 1 : OM = IR = QR = RH = IG = FQ = VS = VP = FV = IL = IN = IC = IS = NULL;
64 : 1 : vs = x = NULL;
65 : 1 : runs = 0;
66 : 1 : ndfreqs = NULL;
67 : 1 : }
68 : :
69 : : // Constructor creates a named instance of the hbsolver class.
70 [ # # ][ # # ]: 0 : hbsolver::hbsolver (char * n) : analysis (n) {
[ # # ][ # # ]
[ # # ][ # # ]
71 : 0 : type = ANALYSIS_HBALANCE;
72 : 0 : frequency = 0;
73 : 0 : nlnodes = lnnodes = banodes = nanodes = NULL;
74 : 0 : Y = Z = A = NULL;
75 : 0 : NA = YV = JQ = JG = JF = NULL;
76 : 0 : OM = IR = QR = RH = IG = FQ = VS = VP = FV = IL = IN = IC = IS = NULL;
77 : 0 : vs = x = NULL;
78 : 0 : runs = 0;
79 : 0 : ndfreqs = NULL;
80 : 0 : }
81 : :
82 : : // Destructor deletes the hbsolver class object.
83 : 1 : hbsolver::~hbsolver () {
84 : : // delete nodelists
85 [ + - ][ + - ]: 1 : if (nlnodes) delete nlnodes;
[ + - ][ # # ]
[ # # ][ # # ]
86 [ + - ][ + - ]: 1 : if (lnnodes) delete lnnodes;
[ + - ][ # # ]
[ # # ][ # # ]
87 [ + - ][ + - ]: 1 : if (banodes) delete banodes;
[ + - ][ # # ]
[ # # ][ # # ]
88 [ + - ][ + - ]: 1 : if (nanodes) delete nanodes;
[ + - ][ # # ]
[ # # ][ # # ]
89 : :
90 : : // delete temporary matrices
91 [ - + ][ # # ]: 1 : if (A) delete A;
[ # # ][ # # ]
92 [ - + ][ # # ]: 1 : if (Z) delete Z;
[ # # ][ # # ]
93 [ - + ][ # # ]: 1 : if (Y) delete Y;
[ # # ][ # # ]
94 : :
95 : : // delete matrices
96 [ + - ][ + - ]: 1 : if (NA) delete NA;
[ # # ][ # # ]
97 [ + - ][ + - ]: 1 : if (YV) delete YV;
[ # # ][ # # ]
98 [ + - ][ + - ]: 1 : if (JQ) delete JQ;
[ # # ][ # # ]
99 [ + - ][ + - ]: 1 : if (JG) delete JG;
[ # # ][ # # ]
100 [ + - ][ + - ]: 1 : if (JF) delete JF;
[ # # ][ # # ]
101 : :
102 : : // delete vectors
103 [ + - ][ + - ]: 1 : if (IC) delete IC;
[ # # ][ # # ]
104 [ + - ][ + - ]: 1 : if (IS) delete IS;
[ # # ][ # # ]
105 [ + - ][ + - ]: 1 : if (FV) delete FV;
[ # # ][ # # ]
106 [ + - ][ + - ]: 1 : if (IL) delete IL;
[ # # ][ # # ]
107 [ + - ][ + - ]: 1 : if (IN) delete IN;
[ # # ][ # # ]
108 [ + - ][ + - ]: 1 : if (IG) delete IG;
[ # # ][ # # ]
109 [ + - ][ + - ]: 1 : if (FQ) delete FQ;
[ # # ][ # # ]
110 [ + - ][ + - ]: 1 : if (VS) delete VS;
[ # # ][ # # ]
111 [ + - ][ + - ]: 1 : if (VP) delete VP;
[ # # ][ # # ]
112 [ + - ][ + - ]: 1 : if (vs) delete vs;
[ # # ][ # # ]
113 [ + - ][ + - ]: 1 : if (OM) delete OM;
[ # # ][ # # ]
114 [ + - ][ + - ]: 1 : if (IR) delete IR;
[ # # ][ # # ]
115 [ + - ][ + - ]: 1 : if (QR) delete QR;
[ # # ][ # # ]
116 [ + - ][ + - ]: 1 : if (RH) delete RH;
[ # # ][ # # ]
117 : :
118 [ + - ][ + - ]: 1 : if (x) delete x;
[ # # ][ # # ]
119 [ + - ][ + - ]: 1 : if (ndfreqs) delete[] ndfreqs;
[ # # ][ # # ]
120 [ - + ][ # # ]: 2 : }
121 : :
122 : : /* The copy constructor creates a new instance of the hbsolver class
123 : : based on the given hbsolver object. */
124 [ # # ][ # # ]: 0 : hbsolver::hbsolver (hbsolver & o) : analysis (o) {
[ # # ][ # # ]
[ # # ][ # # ]
125 : 0 : frequency = o.frequency;
126 [ # # ][ # # ]: 0 : negfreqs = o.negfreqs;
127 [ # # ][ # # ]: 0 : posfreqs = o.posfreqs;
128 : 0 : nlnodes = o.nlnodes;
129 : 0 : lnnodes = o.lnnodes;
130 : 0 : banodes = o.banodes;
131 : 0 : nanodes = o.nanodes;
132 : 0 : Y = Z = A = NULL;
133 : 0 : NA = YV = JQ = JG = JF = NULL;
134 : 0 : OM = IR = QR = RH = IG = FQ = VS = VP = FV = IL = IN = IC = IS = NULL;
135 : 0 : vs = x = NULL;
136 : 0 : runs = o.runs;
137 : 0 : ndfreqs = NULL;
138 : 0 : }
139 : :
140 : : #define VS_(r) (*VS) (r)
141 : : #define OM_(r) (*OM) (r)
142 : :
143 : : /* This is the HB netlist solver. It prepares the circuit list and
144 : : solves it then. */
145 : 1 : int hbsolver::solve (void) {
146 : :
147 : 1 : int iterations = 0, done = 0;
148 : 1 : int MaxIterations = getPropertyInteger ("MaxIter");
149 : :
150 : : // collect different parts of the circuit
151 : 1 : splitCircuits ();
152 : :
153 : : // create frequency array
154 : 1 : collectFrequencies ();
155 : :
156 : : // find interconnects between the linear and non-linear subcircuit
157 : 1 : getNodeLists ();
158 : :
159 : : // prepares the linear part --> 0 = IC + [YV] * VS
160 : 1 : prepareLinear ();
161 : :
162 : 1 : runs++;
163 : : logprint (LOG_STATUS, "NOTIFY: %s: solving for %d frequencies\n",
164 : 1 : getName (), lnfreqs);
165 : :
166 [ + - ]: 1 : if (nbanodes > 0) {
167 : :
168 : : // start balancing
169 : : logprint (LOG_STATUS, "NOTIFY: %s: balancing at %d nodes\n", getName (),
170 : 1 : nbanodes);
171 : :
172 : : // prepares the non-linear part
173 : 1 : prepareNonLinear ();
174 : :
175 : : #if HB_DEBUG
176 : : fprintf (stderr, "YV -- transY in f:\n"); YV->print ();
177 : : fprintf (stderr, "IC -- constant current in f:\n"); IC->print ();
178 : : #endif
179 : :
180 : : // start iteration
181 [ + - ][ + - ]: 10 : do {
[ + - ]
182 : 11 : iterations++;
183 : :
184 : : #if HB_DEBUG
185 : : fprintf (stderr, "\n -- iteration step: %d\n", iterations);
186 : : fprintf (stderr, "vs -- voltage in t:\n"); vs->print ();
187 : : #endif
188 : :
189 : : // evaluate component functionality and fill matrices and vectors
190 : 11 : loadMatrices ();
191 : :
192 : : #if HB_DEBUG
193 : : fprintf (stderr, "FQ -- charge in t:\n"); FQ->print ();
194 : : fprintf (stderr, "IG -- current in t:\n"); IG->print ();
195 : : #endif
196 : :
197 : : // currents into frequency domain
198 : 11 : VectorFFT (IG);
199 : :
200 : : // charges into frequency domain
201 : 11 : VectorFFT (FQ);
202 : :
203 : : // right hand side currents and charges into the frequency domain
204 : 11 : VectorFFT (IR);
205 : 11 : VectorFFT (QR);
206 : :
207 : : #if HB_DEBUG
208 : : fprintf (stderr, "VS -- voltage in f:\n"); VS->print ();
209 : : fprintf (stderr, "FQ -- charge in f:\n"); FQ->print ();
210 : : fprintf (stderr, "IG -- current in f:\n"); IG->print ();
211 : : fprintf (stderr, "IR -- corrected Jacobi current in f:\n"); IR->print ();
212 : : #endif
213 : :
214 : : // solve HB equation --> FV = IC + [YV] * VS + j[O] * FQ + IG
215 : 11 : solveHB ();
216 : :
217 : : #if HB_DEBUG
218 : : fprintf (stderr, "FV -- error vector F(V) in f:\n"); FV->print ();
219 : : fprintf (stderr, "IL -- linear currents in f:\n"); IL->print ();
220 : : fprintf (stderr, "IN -- non-linear currents in f:\n"); IN->print ();
221 : : fprintf (stderr, "RH -- right-hand side currents in f:\n"); RH->print ();
222 : : #endif
223 : :
224 : : // termination criteria met
225 [ + + ][ + + ]: 11 : if (iterations > 1 && checkBalance ()) {
[ + + ]
226 : 1 : done = 1;
227 : 1 : break;
228 : : }
229 : :
230 : : #if HB_DEBUG
231 : : fprintf (stderr, "JG -- G-Jacobian in t:\n"); JG->print ();
232 : : fprintf (stderr, "JQ -- C-Jacobian in t:\n"); JQ->print ();
233 : : #endif
234 : :
235 : : // G-Jacobian into frequency domain
236 : 10 : MatrixFFT (JG);
237 : :
238 : : // C-Jacobian into frequency domain
239 : 10 : MatrixFFT (JQ);
240 : :
241 : : #if HB_DEBUG
242 : : fprintf (stderr, "JQ -- dQ/dV C-Jacobian in f:\n"); JQ->print ();
243 : : fprintf (stderr, "JG -- dI/dV G-Jacobian in f:\n"); JG->print ();
244 : : #endif
245 : :
246 : : // calculate Jacobian --> JF = [YV] + j[O] * JQ + JG
247 : 10 : calcJacobian ();
248 : :
249 : : #if HB_DEBUG
250 : : fprintf (stderr, "JF -- full Jacobian in f:\n"); JF->print ();
251 : : #endif
252 : :
253 : : // solve equation system --> JF * VS(n+1) = JF * VS(n) - FV
254 : 10 : solveVoltages ();
255 : :
256 : : #if HB_DEBUG
257 : : fprintf (stderr, "VS -- next voltage in f:\n"); VS->print ();
258 : : #endif
259 : :
260 : : // inverse FFT of frequency domain voltage vector VS(n+1)
261 : 10 : VectorIFFT (vs);
262 : : }
263 : : // check termination criteria (balanced frequency domain currents)
264 : : while (!done && iterations < MaxIterations);
265 : :
266 [ - + ]: 1 : if (iterations >= MaxIterations) {
267 [ # # ]: 0 : qucs::exception * e = new qucs::exception (EXCEPTION_NO_CONVERGENCE);
268 : : e->setText ("no convergence in %s analysis after %d iterations",
269 : 0 : getName (), iterations);
270 : 0 : throw_exception (e);
271 : : logprint (LOG_ERROR, "%s: no convergence after %d iterations\n",
272 : 0 : getName (), iterations);
273 : : }
274 : : else {
275 : : logprint (LOG_STATUS, "%s: convergence reached after %d iterations\n",
276 : 1 : getName (), iterations);
277 : : }
278 : : }
279 : : else {
280 : : // no balancing necessary
281 : 0 : logprint (LOG_STATUS, "NOTIFY: %s: no balancing necessary\n", getName ());
282 : : }
283 : :
284 : : // print exception stack
285 : 1 : estack.print ();
286 : :
287 : : // apply AC analysis to the complete network in order to obtain the
288 : : // final results
289 : 1 : finalSolution ();
290 : :
291 : : // save results into dataset
292 : 1 : saveResults ();
293 : 1 : return 0;
294 : : }
295 : :
296 : : /* Goes through the list of circuit objects and runs its calcHB()
297 : : function. */
298 : 0 : void hbsolver::calc (hbsolver * self) {
299 : 0 : circuit * root = self->getNet()->getRoot ();
300 [ # # ]: 0 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
301 : 0 : c->calcHB (self->frequency);
302 : : }
303 : 0 : }
304 : :
305 : : /* Goes through the list of circuit objects and runs its initHB()
306 : : function. */
307 : 0 : void hbsolver::initHB (void) {
308 : 0 : circuit * root = subnet->getRoot ();
309 [ # # ]: 0 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
310 : 0 : c->initHB ();
311 : : }
312 : 0 : }
313 : :
314 : : /* Goes through the list of circuit objects and runs its initDC()
315 : : function. */
316 : 0 : void hbsolver::initDC (void) {
317 : 0 : circuit * root = subnet->getRoot ();
318 [ # # ]: 0 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
319 : 0 : c->initDC ();
320 : : }
321 : 0 : }
322 : :
323 : : // Returns true if circuit is a HB source.
324 : 4 : bool hbsolver::isExcitation (circuit * c) {
325 : 4 : int type = c->getType ();
326 [ + - ][ + + ]: 4 : if (type == CIR_PAC || type == CIR_VAC || type == CIR_VDC)
[ - + ]
327 : 1 : return true;
328 : 4 : return false;
329 : : }
330 : :
331 : : // Expands the frequency array using the given frequency and the order.
332 : 1 : void hbsolver::expandFrequencies (nr_double_t f, int n) {
333 [ + - ]: 1 : tvector<nr_double_t> nfreqs = negfreqs;
334 [ + - ]: 1 : tvector<nr_double_t> pfreqs = posfreqs;
335 : 1 : int i, k, len = nfreqs.getSize ();
336 : 1 : negfreqs.clear ();
337 : 1 : posfreqs.clear ();
338 [ - + ]: 1 : if (len > 0) {
339 : : // frequency expansion for full frequency sets
340 [ # # ]: 0 : for (i = 0; i <= n + 1; i++) {
341 [ # # ]: 0 : for (k = 0; k < len; k++) {
342 [ # # ]: 0 : negfreqs.add (i * f + nfreqs.get (k));
343 : : }
344 : : }
345 [ # # ]: 0 : for (i = -n; i < 0; i++) {
346 [ # # ]: 0 : for (k = 0; k < len; k++) {
347 [ # # ]: 0 : negfreqs.add (i * f + nfreqs.get (k));
348 : : }
349 : : }
350 [ # # ]: 0 : for (i = 0; i <= 2 * n + 1; i++) {
351 [ # # ]: 0 : for (k = 0; k < len; k++) {
352 [ # # ]: 0 : posfreqs.add (i * f + pfreqs.get (k));
353 : : }
354 : : }
355 : : }
356 : : else {
357 : : // first frequency
358 [ + + ]: 10 : for (i = 0; i <= n + 1; i++) negfreqs.add (i * f);
359 [ + + ]: 8 : for (i = -n; i < 0; i++) negfreqs.add (i * f);
360 [ + + ]: 17 : for (i = 0; i <= 2 * n + 1; i++) posfreqs.add (i * f);
361 : 1 : }
362 : 1 : }
363 : :
364 : : // Calculates an order fulfilling the "power of two" requirement.
365 : 1 : int hbsolver::calcOrder (int n) {
366 : 1 : int o, order = n * 2; // current order + DC + negative freqencies
367 [ + + ]: 5 : for (o = 1; o < order; o <<= 1) ; // a power of 2
368 : 1 : return o / 2 - 1;
369 : : }
370 : :
371 : : /* The function computes the harmonic frequencies excited in the
372 : : circuit list depending on the maximum number of harmonics per
373 : : exitation and saves its results into the 'negfreqs' vector. */
374 : 1 : void hbsolver::collectFrequencies (void) {
375 : :
376 : : // initialization
377 : 1 : negfreqs.clear ();
378 : 1 : posfreqs.clear ();
379 : 1 : rfreqs.clear ();
380 : 1 : dfreqs.clear ();
381 [ - + ][ # # ]: 1 : if (ndfreqs) delete[] ndfreqs;
382 : :
383 : : // obtain order
384 : 1 : int i, n = calcOrder (getPropertyInteger ("n"));
385 : :
386 : : // expand frequencies for each exitation
387 : : nr_double_t f;
388 [ + + ]: 2 : for (auto * c : excitations) {
389 [ + - ]: 1 : if (c->getType () != CIR_VDC) { // no extra DC sources
390 [ + - ][ + - ]: 1 : if ((f = c->getPropertyDouble ("f")) != 0.0) {
391 [ + - ]: 1 : if (!dfreqs.contains (f)) { // no double frequencies
392 : 1 : dfreqs.add (f);
393 [ + - ]: 1 : expandFrequencies (f, n);
394 : : }
395 : : }
396 : : }
397 : : }
398 : :
399 : : // no excitations
400 [ - + ]: 1 : if (negfreqs.getSize () == 0) {
401 : : // use specified frequency
402 : 0 : f = getPropertyDouble ("f");
403 : 0 : dfreqs.add (f);
404 : 0 : expandFrequencies (f, n);
405 : : }
406 : :
407 : : // build frequency dimension lengths
408 : 1 : ndfreqs = new int[dfreqs.getSize ()];
409 [ + + ]: 2 : for (i = 0; i < dfreqs.getSize (); i++) {
410 : 1 : ndfreqs[i] = (n + 1) * 2;
411 : : }
412 : :
413 : : #if HB_DEBUG
414 : : fprintf (stderr, "%d frequencies: [ ", negfreqs.getSize ());
415 : : for (i = 0; i < negfreqs.getSize (); i++) {
416 : : fprintf (stderr, "%g ", (double) real (negfreqs.get (i)));
417 : : }
418 : : fprintf (stderr, "]\n");
419 : : #endif /* HB_DEBUG */
420 : :
421 : : // build list of positive frequencies including DC
422 [ + + ]: 17 : for (n = 0; n < negfreqs.getSize (); n++) {
423 [ + + ]: 16 : if ((f = negfreqs (n)) < 0.0) continue;
424 : 9 : rfreqs.add (f);
425 : : }
426 : 1 : lnfreqs = rfreqs.getSize ();
427 : 1 : nlfreqs = negfreqs.getSize ();
428 : :
429 : : // pre-calculate the j[O] vector
430 [ + - ]: 1 : OM = new tvector<nr_complex_t> (nlfreqs);
431 [ + + ]: 17 : for (n = i = 0; n < nlfreqs; n++, i++)
432 : 16 : OM_(n) = nr_complex_t (0, 2 * M_PI * negfreqs (i));
433 : 1 : }
434 : :
435 : : // Split netlist into excitation, linear and non-linear part.
436 : 1 : void hbsolver::splitCircuits (void) {
437 : 1 : circuit * root = subnet->getRoot ();
438 [ + + ]: 6 : for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
439 [ + + ]: 5 : if (c->isNonLinear ()) {
440 : : // non-linear part
441 [ + - ]: 1 : nolcircuits.push_front(c);
442 : : }
443 [ + + ]: 4 : else if (isExcitation (c)) {
444 : : // get sinusoidal sources
445 [ + - ]: 1 : excitations.push_front(c);
446 : : }
447 [ + + ]: 3 : else if (c->getType () != CIR_GROUND) {
448 : : // linear part
449 [ + - ]: 2 : lincircuits.push_front (c);
450 : : }
451 : : }
452 : 1 : }
453 : :
454 : : // Creates a nodelist for the given circuit list.
455 : 3 : strlist * hbsolver::circuitNodes (ptrlist<circuit> circuits) {
456 [ + - ]: 3 : strlist * nodes = new strlist ();
457 [ + + ]: 7 : for (auto * c : circuits) {
458 [ + + ]: 12 : for (int i = 0; i < c->getSize (); i++) {
459 [ + - ]: 8 : char * n = c->getNode(i)->getName ();
460 [ + + ]: 8 : if (strcmp (n, "gnd")) {
461 [ + - ][ + + ]: 6 : if (!nodes->contains (n)) nodes->add (n);
[ + - ]
462 : : }
463 : : }
464 : : }
465 : 3 : return nodes;
466 : : }
467 : :
468 : : // Obtain node lists for linear and non-linear part.
469 : 1 : void hbsolver::getNodeLists (void) {
470 : : // non-linear nodes
471 [ + - ][ + - ]: 1 : nlnodes = circuitNodes (nolcircuits);
472 : : // linear nodes
473 [ + - ][ + - ]: 1 : lnnodes = circuitNodes (lincircuits);
474 : : // excitation nodes
475 [ + - ][ + - ]: 1 : exnodes = circuitNodes (excitations);
476 : :
477 : : #if DEBUG && 0
478 : : fprintf (stderr, "nonlinear nodes: [ %s ]\n", nlnodes->toString ());
479 : : fprintf (stderr, " linear nodes: [ %s ]\n", lnnodes->toString ());
480 : : #endif
481 : :
482 : : // organization of the nodes for the MNA:
483 : : // --------------------------------------
484 : : // 1.) balanced nodes: all connected to at least one non-linear device
485 : : // 2.a) the excitation nodes
486 : : // 2.b) all linear nodes not contained in 1. and 2.a
487 : : // 2.c) additional gyrators of linear nodes (built-in voltage sources)
488 : : // please note: excitation nodes also in 2.b; 1. and 2.a are 'ports'
489 : :
490 [ + - ][ + - ]: 1 : nanodes = new strlist (*nlnodes); // list 1.
491 [ + - ]: 1 : strlistiterator it;
492 : : // add excitation nodes; list 2.a
493 [ + - ][ + - ]: 2 : for (it = strlistiterator (exnodes); *it; ++it)
[ + - ][ + - ]
[ + + ]
494 [ + - ][ + - ]: 1 : nanodes->append (*it);
495 : : // add linear nodes; list 2.b
496 [ + - ][ + - ]: 4 : for (it = strlistiterator (lnnodes); *it; ++it) {
[ + - ][ + - ]
[ + + ]
497 [ + - ][ + - ]: 3 : if (!nanodes->contains (*it))
[ + + ]
498 [ + - ][ + - ]: 1 : nanodes->append (*it);
499 : : }
500 : :
501 [ + - ][ + - ]: 1 : banodes = new strlist (*nlnodes);
[ + - ]
502 : : #if DEBUG && 0
503 : : fprintf (stderr, " balanced nodes: [ %s ]\n", banodes->toString ());
504 : : fprintf (stderr, " exnodes nodes: [ %s ]\n", exnodes->toString ());
505 : : fprintf (stderr, " nanodes nodes: [ %s ]\n", nanodes->toString ());
506 : : #endif
507 : 1 : }
508 : :
509 : : /* The function applies unique voltage source identifiers to each
510 : : built in internal voltage source in the list of circuits. */
511 : 1 : int hbsolver::assignVoltageSources (ptrlist<circuit> circuits) {
512 : 1 : int sources = 0;
513 [ + + ]: 3 : for (auto *c: circuits) {
514 [ + - ][ + - ]: 2 : if (c->getVoltageSources () > 0) {
515 : 2 : c->setVoltageSource (sources);
516 [ + - ]: 2 : sources += c->getVoltageSources ();
517 : : }
518 : : }
519 : 1 : return sources;
520 : : }
521 : :
522 : : /* The function assigns unique node identifiers to each circuit node. */
523 : 3 : int hbsolver::assignNodes (ptrlist<circuit> circuits, strlist * nodes,
524 : : int offset) {
525 : : // through all collected nodes
526 [ + + ]: 12 : for (int nr = 0; nr < nodes->length (); nr++) {
527 : 9 : char * nn = nodes->get (nr);
528 : : // through all circuits
529 [ + + ]: 21 : for (auto *c : circuits) {
530 : : // assign current identifier if any of the circuit nodes equals
531 : : // the current one
532 [ + + ]: 36 : for (int i = 0; i < c->getSize (); i++) {
533 [ + - ]: 24 : node * n = c->getNode (i);
534 [ + + ]: 24 : if (!strcmp (n->getName (), nn)) n->setNode (offset + nr + 1);
535 : : }
536 : : }
537 : : }
538 : 3 : return nodes->length ();
539 : : }
540 : :
541 : : // Prepares the linear operations.
542 : 1 : void hbsolver::prepareLinear (void) {
543 [ + + ]: 3 : for (auto *lc : lincircuits)
544 [ + - ]: 2 : lc->initHB ();
545 [ + - ]: 1 : nlnvsrcs = assignVoltageSources (lincircuits);
546 : 1 : nnlvsrcs = excitations.size ();
547 : 1 : nnanodes = nanodes->length ();
548 : 1 : nexnodes = exnodes->length ();
549 : 1 : nbanodes = banodes->length ();
550 [ + - ]: 1 : assignNodes (lincircuits, nanodes);
551 [ + - ]: 1 : assignNodes (excitations, nanodes);
552 : 1 : createMatrixLinearA ();
553 : 1 : createMatrixLinearY ();
554 : 1 : calcConstantCurrent ();
555 : 1 : }
556 : :
557 : : /* The function creates the complex linear network MNA matrix. It
558 : : contains the MNA entries for all linear components for each
559 : : requested frequency. */
560 : 1 : void hbsolver::createMatrixLinearA (void) {
561 : 1 : int M = nlnvsrcs;
562 : 1 : int N = nnanodes;
563 : 1 : int f = 0;
564 : : nr_double_t freq;
565 : :
566 : : // create new MNA matrix
567 [ + - ]: 1 : A = new tmatrix<nr_complex_t> ((N + M) * lnfreqs);
568 : :
569 : : // through each frequency
570 [ + + ]: 10 : for (int i = 0; i < rfreqs.getSize (); i++) {
571 : 9 : freq = rfreqs (i);
572 : : // calculate components' MNA matrix for the given frequency
573 [ + + ]: 27 : for (auto *lc : lincircuits)
574 [ + - ]: 18 : lc->calcHB (freq);
575 : : // fill in all matrix entries for the given frequency
576 : 9 : fillMatrixLinearA (A, f++);
577 : : }
578 : :
579 : : // save a copy of the original MNA matrix
580 [ + - ]: 1 : NA = new tmatrix<nr_complex_t> (*A);
581 : 1 : }
582 : :
583 : : // some definitions for the linear matrix filler
584 : : #undef A_
585 : : #undef B_
586 : : #define A_(r,c) (*A) ((r)*lnfreqs+f,(c)*lnfreqs+f)
587 : : #define G_(r,c) A_(r,c)
588 : : #define B_(r,c) A_(r,c+N)
589 : : #define C_(r,c) A_(r+N,c)
590 : : #define D_(r,c) A_(r+N,c+N)
591 : :
592 : : /* This function fills in the MNA matrix entries into the A matrix for
593 : : a given frequency index. */
594 : 9 : void hbsolver::fillMatrixLinearA (tmatrix<nr_complex_t> * A, int f) {
595 : 9 : int N = nnanodes;
596 : :
597 : : // through each linear circuit
598 [ + + ]: 27 : for (auto *cir : lincircuits) {
599 : 18 : int s = cir->getSize ();
600 : : int nr, nc, r, c, v;
601 : :
602 : : // apply G-matrix entries
603 [ + + ]: 54 : for (r = 0; r < s; r++) {
604 [ + - ][ - + ]: 36 : if ((nr = cir->getNode(r)->getNode () - 1) < 0) continue;
605 [ + + ]: 108 : for (c = 0; c < s; c++) {
606 [ + - ][ - + ]: 72 : if ((nc = cir->getNode(c)->getNode () - 1) < 0) continue;
607 [ + - ][ + - ]: 72 : G_(nr, nc) += cir->getY (r, c);
608 : : }
609 : : }
610 : :
611 : : // augmented part -- built in voltage sources
612 [ + - ][ + - ]: 18 : if ((v = cir->getVoltageSources ()) > 0) {
613 : :
614 : : // apply B-matrix entries
615 [ + + ]: 54 : for (r = 0; r < s; r++) {
616 [ + - ][ - + ]: 36 : if ((nr = cir->getNode(r)->getNode () - 1) < 0) continue;
617 [ + + ]: 72 : for (c = 0; c < v; c++) {
618 : 36 : nc = cir->getVoltageSource () + c;
619 [ + - ][ + - ]: 36 : B_(nr, nc) += cir->getB (r, nc);
620 : : }
621 : : }
622 : :
623 : : // apply C-matrix entries
624 [ + + ]: 36 : for (r = 0; r < v; r++) {
625 : 18 : nr = cir->getVoltageSource () + r;
626 [ + + ]: 54 : for (c = 0; c < s; c++) {
627 [ + - ][ - + ]: 36 : if ((nc = cir->getNode(c)->getNode () - 1) < 0) continue;
628 [ + - ][ + - ]: 36 : C_(nr, nc) += cir->getC (nr, c);
629 : : }
630 : : }
631 : :
632 : : // apply D-matrix entries
633 [ + + ]: 36 : for (r = 0; r < v; r++) {
634 : 18 : nr = cir->getVoltageSource () + r;
635 [ + + ]: 36 : for (c = 0; c < v; c++) {
636 : 18 : nc = cir->getVoltageSource () + c;
637 [ + - ][ + - ]: 18 : D_(nr, nc) += cir->getD (nr, nc);
638 : : }
639 : : }
640 : : }
641 : : }
642 : 9 : }
643 : :
644 : : // The function inverts the given matrix A into the matrix H.
645 : 1 : void hbsolver::invertMatrix (tmatrix<nr_complex_t> * A,
646 : : tmatrix<nr_complex_t> * H) {
647 : 1 : eqnsys<nr_complex_t> eqns;
648 : 1 : int N = A->getCols ();
649 [ + - ][ + - ]: 1 : tvector<nr_complex_t> * x = new tvector<nr_complex_t> (N);
650 [ + - ][ + - ]: 1 : tvector<nr_complex_t> * z = new tvector<nr_complex_t> (N);
651 : :
652 : : try_running () {
653 : : // create LU decomposition of the A matrix
654 : 1 : eqns.setAlgo (ALGO_LU_FACTORIZATION_CROUT);
655 [ + - ]: 1 : eqns.passEquationSys (A, x, z);
656 [ + - ]: 1 : eqns.solve ();
657 : : }
658 : : // appropriate exception handling
659 [ + - ][ - + ]: 1 : catch_exception () {
[ # # ]
660 : : case EXCEPTION_PIVOT:
661 : : default:
662 [ # # ]: 0 : logprint (LOG_ERROR, "WARNING: %s: during TI inversion\n", getName ());
663 [ # # ]: 0 : estack.print ();
664 : : }
665 : :
666 : : // use the LU decomposition to obtain the inverse H
667 : 1 : eqns.setAlgo (ALGO_LU_SUBSTITUTION_CROUT);
668 [ + + ]: 19 : for (int c = 0; c < N; c++) {
669 : 18 : z->set (0.0);
670 [ + - ]: 18 : z->set (c, 1.0);
671 [ + - ]: 18 : eqns.passEquationSys (A, x, z);
672 [ + - ]: 18 : eqns.solve ();
673 [ + - ][ + - ]: 342 : for (int r = 0; r < N; r++) H->set (r, c, x->get (r));
[ + + ]
674 : : }
675 [ + - ]: 1 : delete x;
676 [ + - ]: 1 : delete z;
677 : 1 : }
678 : :
679 : : // Some defines for matrix element access.
680 : : #define V_(r) (*V) (r)
681 : : #define I_(r) (*I) (r)
682 : : #undef A_
683 : : #define A_(r,c) (*A) (r,c)
684 : :
685 : : #define Z_(r,c) (*Z) (r,c)
686 : : #define Y_(r,c) (*Y) (r,c)
687 : :
688 : : #define ZVU_(r,c) Z_(r,c)
689 : : #define ZVL_(r,c) Z_((r)*lnfreqs+f+sn,c)
690 : : #define ZCU_(r,c) Z_(r,(c)*lnfreqs+f+sn)
691 : : #define ZCL_(r,c) Z_((r)*lnfreqs+f+sn,(c)*lnfreqs+f+sn)
692 : :
693 : : #define YV_(r,c) (*YV) (r,c)
694 : : #define NA_(r,c) (*NA) (r,c)
695 : : #define JF_(r,c) (*JF) (r,c)
696 : :
697 : : /* The following function performs the following steps:
698 : : 1. form the MNA matrix A including all nodes (linear, non-linear and
699 : : excitations)
700 : : 2. compute the variable transimpedance matrix entries for the nodes
701 : : to be balanced
702 : : 3. compute the constant transimpedance matrix entries for the constant
703 : : current vector caused by the excitations
704 : : 4. invert this overall transimpedance matrix
705 : : 5. extract the variable transadmittance matrix entries
706 : : */
707 : 1 : void hbsolver::createMatrixLinearY (void) {
708 : 1 : int M = nlnvsrcs;
709 : 1 : int N = nnanodes;
710 : : int c, r, f;
711 : :
712 : : // size of overall MNA matrix
713 : 1 : int sa = (N + M) * lnfreqs;
714 : 1 : int sv = nbanodes;
715 : 1 : int se = nnlvsrcs;
716 : 1 : int sy = sv + se;
717 : :
718 : : // allocate new transimpedance matrix
719 [ + - ][ + - ]: 1 : Z = new tmatrix<nr_complex_t> (sy * lnfreqs);
720 : :
721 : : // prepare equation system
722 : 1 : eqnsys<nr_complex_t> eqns;
723 : : tvector<nr_complex_t> * V;
724 : : tvector<nr_complex_t> * I;
725 : :
726 : : // 1. create variable transimpedance matrix entries relating
727 : : // voltages at the balanced nodes to the currents through these
728 : : // nodes into the non-linear part
729 : 1 : int sn = sv * lnfreqs;
730 [ + - ][ + - ]: 1 : V = new tvector<nr_complex_t> (sa);
731 [ + - ][ + - ]: 1 : I = new tvector<nr_complex_t> (sa);
732 : :
733 : : // connect a 100 Ohm resistor (to ground) to balanced node in the MNA matrix
734 [ + - ][ + + ]: 10 : for (c = 0; c < sv * lnfreqs; c++) A_(c, c) += 0.01;
735 : :
736 : : // connect a 100 Ohm resistor (in parallel) to each excitation
737 [ + + ]: 2 : for (auto *vs : excitations) {
738 : : // get positive and negative node
739 [ + - ]: 1 : int pnode = vs->getNode(NODE_1)->getNode ();
740 [ + - ]: 1 : int nnode = vs->getNode(NODE_2)->getNode ();
741 [ + + ]: 10 : for (f = 0; f < lnfreqs; f++) { // for each frequency
742 : 9 : int pn = (pnode - 1) * lnfreqs + f;
743 : 9 : int nn = (nnode - 1) * lnfreqs + f;
744 [ + - ][ + - ]: 9 : if (pnode) A_(pn, pn) += 0.01;
745 [ - + ][ # # ]: 9 : if (nnode) A_(nn, nn) += 0.01;
746 [ + - ][ - + ]: 9 : if (pnode && nnode) {
747 [ # # ]: 0 : A_(pn, nn) -= 0.01;
748 [ # # ]: 0 : A_(nn, pn) -= 0.01;
749 : : }
750 : : }
751 : : }
752 : :
753 : : // LU decompose the MNA matrix
754 : : try_running () {
755 : 1 : eqns.setAlgo (ALGO_LU_FACTORIZATION_CROUT);
756 [ + - ]: 1 : eqns.passEquationSys (A, V, I);
757 [ + - ]: 1 : eqns.solve ();
758 : : }
759 : : // appropriate exception handling
760 [ + - ][ - + ]: 1 : catch_exception () {
[ # # ]
761 : : case EXCEPTION_PIVOT:
762 : : default:
763 [ # # ]: 0 : logprint (LOG_ERROR, "WARNING: %s: during A factorization\n", getName ());
764 [ # # ]: 0 : estack.print ();
765 : : }
766 : :
767 : : // aquire variable transimpedance matrix entries
768 : 1 : eqns.setAlgo (ALGO_LU_SUBSTITUTION_CROUT);
769 [ + + ]: 10 : for (c = 0; c < sn; c++) {
770 : 9 : I->set (0.0);
771 [ + - ]: 9 : I_(c) = 1.0;
772 [ + - ]: 9 : eqns.passEquationSys (A, V, I);
773 [ + - ]: 9 : eqns.solve ();
774 : : // ZV | ..
775 : : // ---+---
776 : : // .. | ..
777 [ + - ][ + - ]: 90 : for (r = 0; r < sn; r++) ZVU_(r, c) = V_(r);
[ + + ]
778 : : // .. | ..
779 : : // ---+---
780 : : // ZV | ..
781 : 9 : r = 0;
782 [ + + ]: 18 : for (auto *e : excitations) {
783 : : // lower part entries
784 [ + + ]: 90 : for (f = 0; f < lnfreqs; f++) {
785 [ + - ][ + - ]: 81 : ZVL_(r, c) = excitationZ (V, e, f);
786 : : }
787 : : }
788 : : }
789 : :
790 : : // create constant transimpedance matrix entries relating the
791 : : // source voltages to the interconnection currents
792 : 1 : int vsrc = 0;
793 : : // aquire constant transadmittance matrix entries
794 [ + + ]: 2 : for (auto it = excitations.begin(); it != excitations.end(); ++it, vsrc++) {
795 : 1 : circuit * vs = *it;
796 : : // get positive and negative node
797 [ + - ]: 1 : int pnode = vs->getNode(NODE_1)->getNode ();
798 [ + - ]: 1 : int nnode = vs->getNode(NODE_2)->getNode ();
799 [ + + ]: 10 : for (f = 0; f < lnfreqs; f++) { // for each frequency
800 : 9 : int pn = (pnode - 1) * lnfreqs + f;
801 : 9 : int nn = (nnode - 1) * lnfreqs + f;
802 : 9 : I->set (0.0);
803 [ + - ][ + - ]: 9 : if (pnode) I_(pn) = +1.0;
804 [ - + ][ # # ]: 9 : if (nnode) I_(nn) = -1.0;
805 [ + - ]: 9 : eqns.passEquationSys (A, V, I);
806 [ + - ]: 9 : eqns.solve ();
807 : : // .. | ZC
808 : : // ---+---
809 : : // .. | ..
810 [ + + ]: 90 : for (r = 0; r < sn; r++) {
811 : : // upper part of the entries
812 [ + - ][ + - ]: 81 : ZCU_(r, vsrc) = V_(r);
813 : : }
814 : : // .. | ..
815 : : // ---+---
816 : : // .. | ZC
817 : 9 : r = 0;
818 [ + + ]: 18 : for (auto ite = excitations.begin(); ite != excitations.end(); ++ite, r++) {
819 : : // lower part entries
820 [ + - ][ + - ]: 9 : ZCL_(r, vsrc) = excitationZ (V, *ite, f);
821 : : }
822 : : }
823 : : }
824 [ + - ]: 1 : delete I;
825 [ + - ]: 1 : delete V;
826 : :
827 : : // allocate new transadmittance matrix
828 [ + - ][ + - ]: 1 : Y = new tmatrix<nr_complex_t> (sy * lnfreqs);
829 : :
830 : : // invert the Z matrix to a Y matrix
831 [ + - ]: 1 : invertMatrix (Z, Y);
832 : :
833 : : // substract the 100 Ohm resistor
834 [ + - ][ + + ]: 19 : for (c = 0; c < sy * lnfreqs; c++) Y_(c, c) -= 0.01;
835 : :
836 : : // extract the variable transadmittance matrix
837 [ + - ][ + - ]: 1 : YV = new tmatrix<nr_complex_t> (sv * nlfreqs);
838 : :
839 : : // variable transadmittance matrix must be continued conjugately
840 [ + - ][ + - ]: 1 : *YV = expandMatrix (*Y, sv);
[ + - ]
841 : :
842 : : // delete overall temporary MNA matrix
843 [ + - ]: 1 : delete A; A = NULL;
844 : : // delete transimpedance matrix
845 [ + - ]: 1 : delete Z; Z = NULL;
846 : 1 : }
847 : :
848 : : /* Little helper function obtaining a transimpedance value for the
849 : : given voltage source (excitation) and for a given frequency
850 : : index. */
851 : 90 : nr_complex_t hbsolver::excitationZ (tvector<nr_complex_t> * V, circuit * vs,
852 : : int f) {
853 : : // get positive and negative node
854 [ + - ]: 90 : int pnode = vs->getNode(NODE_1)->getNode ();
855 [ + - ]: 90 : int nnode = vs->getNode(NODE_2)->getNode ();
856 : 90 : nr_complex_t z = 0.0;
857 [ + - ][ + - ]: 90 : if (pnode) z += V_((pnode - 1) * lnfreqs + f);
858 [ - + ][ # # ]: 90 : if (nnode) z -= V_((nnode - 1) * lnfreqs + f);
859 : 90 : return z;
860 : : }
861 : :
862 : : /* This function computes the constant current vectors using the
863 : : voltage of the excitations and the transadmittance matrix
864 : : entries. */
865 : 1 : void hbsolver::calcConstantCurrent (void) {
866 : 1 : int se = nnlvsrcs * lnfreqs;
867 : 1 : int sn = nbanodes * lnfreqs;
868 : 1 : int r, c, vsrc = 0;
869 : :
870 : : // collect excitation voltages
871 [ + - ]: 1 : tvector<nr_complex_t> VC (se);
872 [ + + ]: 2 : for (auto it = excitations.begin(); it != excitations.end(); ++it, vsrc++) {
873 : 1 : circuit * vs = *it;
874 [ + - ]: 1 : vs->initHB ();
875 : 1 : vs->setVoltageSource (0);
876 [ + + ]: 10 : for (int f = 0; f < rfreqs.getSize (); f++) { // for each frequency
877 [ + - ]: 9 : nr_double_t freq = rfreqs (f);
878 [ + - ]: 9 : vs->calcHB (freq);
879 [ + - ][ + - ]: 9 : VC (vsrc * lnfreqs + f) = vs->getE (VSRC_1);
880 : : }
881 : : }
882 : :
883 : : // compute constant current vector for balanced nodes
884 [ + - ][ + - ]: 1 : IC = new tvector<nr_complex_t> (sn);
885 : : // .. | YC * VC
886 : : // ---+---
887 : : // .. | ..
888 [ + + ]: 10 : for (r = 0; r < sn; r++) {
889 : 9 : nr_complex_t i = 0.0;
890 [ + + ]: 90 : for (c = 0; c < se; c++) {
891 [ + - ][ + - ]: 81 : i += Y_(r, c + sn) * VC (c);
[ + - ]
892 : : }
893 : 9 : int f = r % lnfreqs;
894 [ + + ][ + + ]: 9 : if (f != 0 && f != lnfreqs - 1) i /= 2;
895 [ + - ]: 9 : IC->set (r, i);
896 : : }
897 : : // expand the constant current conjugate
898 [ + - ][ + - ]: 1 : *IC = expandVector (*IC, nbanodes);
[ + - ]
899 : :
900 : : // compute constant current vector for sources itself
901 [ + - ][ + - ]: 1 : IS = new tvector<nr_complex_t> (se);
902 : : // .. | ..
903 : : // ---+---
904 : : // .. | YC * VC
905 [ + + ]: 10 : for (r = 0; r < se; r++) {
906 : 9 : nr_complex_t i = 0.0;
907 [ + + ]: 90 : for (c = 0; c < se; c++) {
908 [ + - ][ + - ]: 81 : i += Y_(r + sn, c + sn) * VC (c);
[ + - ]
909 : : }
910 [ + - ]: 9 : IS->set (r, i);
911 : : }
912 : :
913 : : // delete overall transadmittance matrix
914 [ + - ]: 1 : delete Y; Y = NULL;
915 : 1 : }
916 : :
917 : : /* Checks whether currents through the interconnects of the linear and
918 : : non-linear subcircuit (in the frequency domain) are equal. */
919 : 10 : int hbsolver::checkBalance (void) {
920 : 10 : nr_double_t iabstol = getPropertyDouble ("iabstol");
921 : 10 : nr_double_t vabstol = getPropertyDouble ("vabstol");
922 : 10 : nr_double_t reltol = getPropertyDouble ("reltol");
923 : 10 : int n, len = FV->getSize ();
924 [ + + ]: 35 : for (n = 0; n < len; n++) {
925 : : // check iteration voltages
926 [ + - ][ + - ]: 25 : nr_double_t v_abs = abs (VS->get (n) - VP->get (n));
927 [ + - ]: 25 : nr_double_t v_rel = abs (VS->get (n));
928 [ + + ]: 25 : if (v_abs >= vabstol + reltol * v_rel) return 0;
929 : : // check balanced currents
930 [ + - ]: 18 : nr_complex_t il = IL->get (n);
931 [ + - ]: 18 : nr_complex_t in = IN->get (n);
932 [ + - ][ + - ]: 18 : if (il != in) {
933 : 18 : nr_double_t i_abs = abs (il + in);
934 [ + - ]: 18 : nr_double_t i_rel = abs ((il + in) / (il - in));
935 [ + + ][ + + ]: 18 : if (i_abs >= iabstol && 2.0 * i_rel >= reltol) return 0;
936 : : }
937 : : }
938 : 10 : return 1;
939 : : }
940 : :
941 : : // some definitions for the non-linear matrix filler
942 : : #undef G_
943 : : #undef C_
944 : : #define G_(r,c) (*jg) ((r)*nlfreqs+f,(c)*nlfreqs+f)
945 : : #define C_(r,c) (*jq) ((r)*nlfreqs+f,(c)*nlfreqs+f)
946 : : #undef FI_
947 : : #undef FQ_
948 : : #define FI_(r) (*ig) ((r)*nlfreqs+f)
949 : : #define FQ_(r) (*fq) ((r)*nlfreqs+f)
950 : : #define IR_(r) (*ir) ((r)*nlfreqs+f)
951 : : #define QR_(r) (*qr) ((r)*nlfreqs+f)
952 : :
953 : : /* This function fills in the matrix and vector entries for the
954 : : non-linear HB equations for a given frequency index. */
955 : 176 : void hbsolver::fillMatrixNonLinear (tmatrix<nr_complex_t> * jg,
956 : : tmatrix<nr_complex_t> * jq,
957 : : tvector<nr_complex_t> * ig,
958 : : tvector<nr_complex_t> * fq,
959 : : tvector<nr_complex_t> * ir,
960 : : tvector<nr_complex_t> * qr,
961 : : int f) {
962 : : // through each linear circuit
963 [ + + ]: 352 : for (auto *cir: nolcircuits) {
964 : 176 : int s = cir->getSize ();
965 : : int nr, nc, r, c;
966 : :
967 [ + + ]: 528 : for (r = 0; r < s; r++) {
968 [ + - ][ + + ]: 352 : if ((nr = cir->getNode(r)->getNode () - 1) < 0) continue;
969 : : // apply G- and C-matrix entries
970 [ + + ]: 528 : for (c = 0; c < s; c++) {
971 [ + - ][ + + ]: 352 : if ((nc = cir->getNode(c)->getNode () - 1) < 0) continue;
972 [ + - ][ + - ]: 176 : G_(nr, nc) += cir->getY (r, c);
973 [ + - ][ + - ]: 176 : C_(nr, nc) += cir->getQV (r, c);
974 : : }
975 : : // apply I- and Q-vector entries
976 [ + - ][ + - ]: 176 : FI_(nr) -= cir->getI (r);
977 [ + - ][ + - ]: 176 : FQ_(nr) -= cir->getQ (r);
978 : : // ThinkME: positive or negative?
979 [ + - ][ + - ]: 176 : IR_(nr) += cir->getGV (r) + cir->getI (r);
[ + - ]
980 [ + - ][ + - ]: 176 : QR_(nr) += cir->getCV (r) + cir->getQ (r);
[ + - ]
981 : : }
982 : : }
983 : 176 : }
984 : :
985 : : /* The function initializes the non-linear part of the HB. */
986 : 1 : void hbsolver::prepareNonLinear (void) {
987 : 1 : int N = nbanodes;
988 : :
989 : : // allocate matrices and vectors
990 [ + - ]: 1 : if (FQ == NULL) {
991 [ + - ]: 1 : FQ = new tvector<nr_complex_t> (N * nlfreqs);
992 : : }
993 [ + - ]: 1 : if (IG == NULL) {
994 [ + - ]: 1 : IG = new tvector<nr_complex_t> (N * nlfreqs);
995 : : }
996 [ + - ]: 1 : if (IR == NULL) {
997 [ + - ]: 1 : IR = new tvector<nr_complex_t> (N * nlfreqs);
998 : : }
999 [ + - ]: 1 : if (QR == NULL) {
1000 [ + - ]: 1 : QR = new tvector<nr_complex_t> (N * nlfreqs);
1001 : : }
1002 [ + - ]: 1 : if (JG == NULL) {
1003 [ + - ]: 1 : JG = new tmatrix<nr_complex_t> (N * nlfreqs);
1004 : : }
1005 [ + - ]: 1 : if (JQ == NULL) {
1006 [ + - ]: 1 : JQ = new tmatrix<nr_complex_t> (N * nlfreqs);
1007 : : }
1008 [ + - ]: 1 : if (JF == NULL) {
1009 [ + - ]: 1 : JF = new tmatrix<nr_complex_t> (N * nlfreqs);
1010 : : }
1011 : :
1012 : : // voltage vector in frequency and time domain
1013 [ + - ]: 1 : if (VS == NULL) {
1014 [ + - ]: 1 : VS = new tvector<nr_complex_t> (N * nlfreqs);
1015 : : }
1016 [ + - ]: 1 : if (vs == NULL) {
1017 [ + - ]: 1 : vs = new tvector<nr_complex_t> (N * nlfreqs);
1018 : : }
1019 [ + - ]: 1 : if (VP == NULL) {
1020 [ + - ]: 1 : VP = new tvector<nr_complex_t> (N * nlfreqs);
1021 : : }
1022 : :
1023 : : // error vector
1024 [ + - ]: 1 : if (FV == NULL) {
1025 [ + - ]: 1 : FV = new tvector<nr_complex_t> (N * nlfreqs);
1026 : : }
1027 : : // right hand side vector
1028 [ + - ]: 1 : if (RH == NULL) {
1029 [ + - ]: 1 : RH = new tvector<nr_complex_t> (N * nlfreqs);
1030 : : }
1031 : :
1032 : : // linear and non-linear current vector
1033 [ + - ]: 1 : if (IL == NULL) {
1034 [ + - ]: 1 : IL = new tvector<nr_complex_t> (N * nlfreqs);
1035 : : }
1036 [ + - ]: 1 : if (IN == NULL) {
1037 [ + - ]: 1 : IN = new tvector<nr_complex_t> (N * nlfreqs);
1038 : : }
1039 : :
1040 : : // assign nodes
1041 [ + - ]: 1 : assignNodes (nolcircuits, nanodes);
1042 : :
1043 : : // initialize circuits
1044 [ + + ]: 2 : for (auto *cir : nolcircuits) {
1045 [ + - ]: 1 : cir->initHB (nlfreqs);
1046 : : }
1047 : 1 : }
1048 : :
1049 : : /* Saves the node voltages of the given circuit and for the given
1050 : : frequency entry into the circuit voltage vector. */
1051 : 176 : void hbsolver::saveNodeVoltages (circuit * cir, int f) {
1052 : 176 : int r, nr, s = cir->getSize ();
1053 [ + + ]: 528 : for (r = 0; r < s; r++) {
1054 [ + + ]: 352 : if ((nr = cir->getNode(r)->getNode () - 1) < 0) continue;
1055 : : // apply V-vector entries
1056 [ + - ]: 176 : cir->setV (r, real (vs->get (nr * nlfreqs + f)));
1057 : : }
1058 : 176 : }
1059 : :
1060 : : /* The function saves voltages into non-linear circuits, runs each
1061 : : non-linear components' HB calculator for each frequency and applies
1062 : : the matrix and vector entries appropriately. */
1063 : 11 : void hbsolver::loadMatrices (void) {
1064 : : // clear matrices and vectors before
1065 : 11 : IG->set (0.0);
1066 : 11 : FQ->set (0.0);
1067 : 11 : IR->set (0.0);
1068 : 11 : QR->set (0.0);
1069 : 11 : JG->set (0.0);
1070 : 11 : JQ->set (0.0);
1071 : : // through each frequency
1072 [ + + ]: 187 : for (int f = 0; f < nlfreqs; f++) {
1073 : : // calculate components' HB matrices and vector for the given frequency
1074 [ + + ]: 352 : for (auto *cir : nolcircuits) {
1075 [ + - ]: 176 : saveNodeVoltages (cir, f); // node voltages
1076 [ + - ]: 176 : cir->calcHB (f); // HB calculator
1077 : : }
1078 : : // fill in all matrix entries for the given frequency
1079 : 176 : fillMatrixNonLinear (JG, JQ, IG, FQ, IR, QR, f);
1080 : : }
1081 : 11 : }
1082 : :
1083 : : /* The following function transforms a vector using a Fast Fourier
1084 : : Transformation from the time domain to the frequency domain. */
1085 : 74 : void hbsolver::VectorFFT (tvector<nr_complex_t> * V, int isign) {
1086 : : int i, k, r;
1087 : 74 : int n = nlfreqs;
1088 : 74 : int nd = dfreqs.getSize ();
1089 : 74 : int nodes = V->getSize () / n;
1090 : 74 : nr_double_t * d = (nr_double_t *) V->getData ();
1091 : :
1092 [ + - ]: 74 : if (nd == 1) {
1093 : : // for each node a single 1d-FFT
1094 [ + + ]: 148 : for (k = i = 0; i < nodes; i++, k += 2 * n) {
1095 : 74 : nr_double_t * dst = &d[k];
1096 : 74 : _fft_1d (dst, n, isign);
1097 [ + + ][ + + ]: 2122 : if (isign > 0) for (r = 0; r < 2 * n; r++) *dst++ /= n;
1098 : : }
1099 : : }
1100 : : else {
1101 : : // for each node a single nd-FFT
1102 [ # # ]: 0 : for (k = i = 0; i < nodes; i++, k += 2 * n) {
1103 : 0 : nr_double_t * dst = &d[k];
1104 : 0 : _fft_nd (dst, ndfreqs, nd, isign);
1105 [ # # ][ # # ]: 0 : if (isign > 0) for (r = 0; r < 2 * n; r++) *dst++ /= ndfreqs[0];
1106 : : }
1107 : : }
1108 : 74 : }
1109 : :
1110 : : /* The following function transforms a vector using an Inverse Fast
1111 : : Fourier Transformation from the frequency domain to the domain
1112 : : time. */
1113 : 10 : void hbsolver::VectorIFFT (tvector<nr_complex_t> * V, int isign) {
1114 : 10 : VectorFFT (V, -isign);
1115 : 10 : }
1116 : :
1117 : : /* The following function transforms a matrix using a Fast Fourier
1118 : : Transformation from the time domain to the frequency domain. */
1119 : 20 : void hbsolver::MatrixFFT (tmatrix<nr_complex_t> * M) {
1120 : :
1121 : : #if THE_SLO_ALGO
1122 : : // each column FFT
1123 : : for (int c = 0; c < M->getCols (); c++) {
1124 : : tvector<nr_complex_t> V = M->getCol (c);
1125 : : VectorFFT (&V);
1126 : : M->setCol (c, V);
1127 : : }
1128 : :
1129 : : // each row IFFT
1130 : : for (int r = 0; r < M->getRows (); r++) {
1131 : : tvector<nr_complex_t> V = M->getRow (r);
1132 : : VectorIFFT (&V);
1133 : : M->setRow (r, V);
1134 : : }
1135 : : #else
1136 : : int c, r, nc, nr;
1137 : : // for each non-linear node block
1138 [ + + ]: 40 : for (nc = c = 0; c < nbanodes; c++, nc += nlfreqs) {
1139 [ + + ]: 40 : for (nr = r = 0; r < nbanodes; r++, nr += nlfreqs) {
1140 [ + - ]: 20 : tvector<nr_complex_t> V (nlfreqs);
1141 : : int fr, fc, fi;
1142 : : // transform the sub-diagonal only
1143 [ + - ][ + - ]: 340 : for (fc = 0; fc < nlfreqs; fc++) V (fc) = M->get (nr + fc, nc + fc);
[ + + ]
1144 [ + - ]: 20 : VectorFFT (&V);
1145 : : // fill in resulting sub-matrix for the node
1146 [ + + ]: 340 : for (fc = 0; fc < nlfreqs; fc++) {
1147 [ + + ]: 5440 : for (fi = nlfreqs - 1 - fc, fr = 0; fr < nlfreqs; fr++) {
1148 [ + + ]: 5120 : if (++fi >= nlfreqs) fi = 0;
1149 [ + - ][ + - ]: 5120 : M->set (nr + fr, nc + fc, V (fi));
1150 : : }
1151 : : }
1152 : 20 : }
1153 : : }
1154 : : #endif
1155 : 20 : }
1156 : :
1157 : : /* This function solves the actual HB equation in the frequency domain.
1158 : : F(V) = IC + [YV] * VS + j[O] * FQ + IG -> 0
1159 : : Care must be taken with indexing here: In the frequency domain only
1160 : : real positive frequencies are used and computed, but in the time
1161 : : domain we have more values because of the periodic continuation in
1162 : : the frequency domain.
1163 : : RHS = j[O] * CV + GV - (IC + j[O] * FQ + IG)
1164 : : Also the right hand side of the equation system for the new voltage
1165 : : vector is computed here. */
1166 : 11 : void hbsolver::solveHB (void) {
1167 : : // for each non-linear node
1168 [ + + ]: 22 : for (int r = 0; r < nbanodes * nlfreqs; ) {
1169 : : // for each frequency
1170 [ + + ]: 187 : for (int f = 0; f < nlfreqs; f++, r++) {
1171 : 176 : nr_complex_t il = 0.0, in = 0.0, ir = 0.0;
1172 : : // constant current vector due to sources
1173 [ + - ]: 176 : il += IC->get (r);
1174 : : // part 1 of right hand side vector
1175 : 176 : ir -= il;
1176 : : // transadmittance matrix multiplied by voltage vector
1177 [ + + ]: 2992 : for (int c = 0; c < nbanodes * nlfreqs; c++) {
1178 [ + - ][ + - ]: 2816 : il += YV_(r, c) * VS_(c);
[ + - ]
1179 : : }
1180 : : // charge vector
1181 [ + - ][ + - ]: 176 : in += OM_(f) * FQ->get (r);
[ + - ]
1182 : : // current vector
1183 [ + - ]: 176 : in += IG->get (r);
1184 : : // part 2, 3 and 4 of right hand side vector
1185 [ + - ]: 176 : ir += IR->get (r);
1186 [ + - ][ + - ]: 176 : ir += OM_(f) * QR->get (r);
[ + - ]
1187 : : // put values into result vectors
1188 [ + - ]: 176 : RH->set (r, ir);
1189 [ + - ]: 176 : FV->set (r, il + in);
1190 [ + - ]: 176 : IL->set (r, il);
1191 [ + - ]: 176 : IN->set (r, in);
1192 : : }
1193 : : }
1194 : 11 : }
1195 : :
1196 : : /* The function calculates the full Jacobian JF = [YV] + j[O] * JQ + JG */
1197 : 10 : void hbsolver::calcJacobian (void) {
1198 : : int c, r, fc, fr, rt, ct;
1199 : : /* add admittances of capacitance matrix JQ and non-linear
1200 : : admittances matrix JG into complete Jacobian JF */
1201 [ + + ]: 20 : for (c = 0; c < nbanodes; c++) {
1202 : 10 : ct = c * nlfreqs;
1203 [ + + ]: 170 : for (fc = 0; fc < nlfreqs; fc++, ct++) {
1204 [ + + ]: 320 : for (r = 0; r < nbanodes; r++) {
1205 : 160 : rt = r * nlfreqs;
1206 [ + + ]: 2720 : for (fr = 0; fr < nlfreqs; fr++, rt++) {
1207 [ + - ][ + - ]: 2560 : JF_(rt, ct) = JG->get (rt, ct) + JQ->get (rt, ct) * OM_(fr);
1208 : : }
1209 : : }
1210 : : }
1211 : : }
1212 [ + - ]: 10 : *JF += *YV; // add linear admittance matrix
1213 : 10 : }
1214 : :
1215 : : /* The function expands the given vector in the frequency domain to
1216 : : make it a real valued signal in the time domain. */
1217 : 1 : tvector<nr_complex_t> hbsolver::expandVector (tvector<nr_complex_t> V,
1218 : : int nodes) {
1219 : 1 : tvector<nr_complex_t> res (nodes * nlfreqs);
1220 : : int r, ff, rf, rt;
1221 [ + + ]: 2 : for (r = 0; r < nodes; r++) {
1222 : 1 : rt = r * nlfreqs;
1223 : 1 : rf = r * lnfreqs;
1224 : : // copy first part of vector
1225 [ + + ]: 10 : for (ff = 0; ff < lnfreqs; ff++, rf++, rt++) {
1226 [ + - ][ + - ]: 9 : res (rt) = V (rf);
1227 : : }
1228 : : // continue vector conjugated
1229 [ + + ]: 8 : for (rf -= 2; ff < nlfreqs; ff++, rf--, rt++) {
1230 [ + - ][ + - ]: 7 : res (rt) = conj (V (rf));
1231 : : }
1232 : : }
1233 : 1 : return res;
1234 : : }
1235 : :
1236 : : /* The function expands the given matrix in the frequency domain to
1237 : : make it a real valued signal in the time domain. */
1238 : 1 : tmatrix<nr_complex_t> hbsolver::expandMatrix (tmatrix<nr_complex_t> M,
1239 : : int nodes) {
1240 : 1 : tmatrix<nr_complex_t> res (nodes * nlfreqs);
1241 : : int r, c, rf, rt, cf, ct, ff;
1242 [ + + ]: 2 : for (r = 0; r < nodes; r++) {
1243 [ + + ]: 2 : for (c = 0; c < nodes; c++) {
1244 : 1 : rf = r * lnfreqs;
1245 : 1 : rt = r * nlfreqs;
1246 : 1 : cf = c * lnfreqs;
1247 : 1 : ct = c * nlfreqs;
1248 : : // copy first part of diagonal
1249 [ + + ]: 10 : for (ff = 0; ff < lnfreqs; ff++, cf++, ct++, rf++, rt++) {
1250 [ + - ][ + - ]: 9 : res (rt, ct) = M (rf, cf);
1251 : : }
1252 : : // continue diagonal conjugated
1253 [ + + ]: 8 : for (cf -= 2, rf -= 2; ff < nlfreqs; ff++, cf--, ct++, rf--, rt++) {
1254 [ + - ][ + - ]: 7 : res (rt, ct) = conj (M (rf, cf));
1255 : : }
1256 : : }
1257 : : }
1258 : 1 : return res;
1259 : : }
1260 : :
1261 : : /* This function solves the equation system
1262 : : JF * VS(n+1) = JF * VS(n) - FV
1263 : : in order to obtains a new voltage vector in the frequency domain. */
1264 : 10 : void hbsolver::solveVoltages (void) {
1265 : : // save previous iteration voltage
1266 [ + - ]: 10 : *VP = *VS;
1267 : :
1268 : : // setup equation system
1269 : 10 : eqnsys<nr_complex_t> eqns;
1270 : : try_running () {
1271 : : // use LU decomposition for solving
1272 : 10 : eqns.setAlgo (ALGO_LU_DECOMPOSITION);
1273 [ + - ]: 10 : eqns.passEquationSys (JF, VS, RH);
1274 [ + - ]: 10 : eqns.solve ();
1275 : : }
1276 : : // appropriate exception handling
1277 [ + - ][ - + ]: 10 : catch_exception () {
[ # # ]
1278 : : case EXCEPTION_PIVOT:
1279 : : default:
1280 [ # # ]: 0 : logprint (LOG_ERROR, "WARNING: %s: during NR iteration\n", getName ());
1281 [ # # ]: 0 : estack.print ();
1282 : : }
1283 : :
1284 : : // save new voltages in time domain vector
1285 [ + - ]: 10 : *vs = *VS;
1286 : 10 : }
1287 : :
1288 : : /* The following function extends the existing linear MNA matrix to
1289 : : contain the additional rows and columns for the excitation voltage
1290 : : sources. */
1291 : 1 : tmatrix<nr_complex_t> hbsolver::extendMatrixLinear (tmatrix<nr_complex_t> M,
1292 : : int nodes) {
1293 : 1 : int no = M.getCols ();
1294 : 1 : tmatrix<nr_complex_t> res (no + nodes * lnfreqs);
1295 : : // copy the existing part
1296 [ + + ]: 46 : for (int r = 0; r < no; r++) {
1297 [ + + ]: 2070 : for (int c = 0; c < no; c++) {
1298 [ + - ][ + - ]: 2025 : res (r, c) = M (r, c);
1299 : : }
1300 : : }
1301 : 1 : return res;
1302 : : }
1303 : :
1304 : : /* The function fills in the missing MNA entries for the excitation
1305 : : voltage sources into the extended rows and columns as well as the
1306 : : actual voltage values into the right hand side vector. */
1307 : 1 : void hbsolver::fillMatrixLinearExtended (tmatrix<nr_complex_t> * Y,
1308 : : tvector<nr_complex_t> * I) {
1309 : : // through each excitation source
1310 : 1 : int of = lnfreqs * (nlnvsrcs + nnanodes);
1311 : 1 : int sc = of;
1312 : :
1313 [ + + ]: 2 : for (auto *vs : excitations) {
1314 : : // get positive and negative node
1315 [ + - ]: 1 : int pnode = vs->getNode(NODE_1)->getNode ();
1316 [ + - ]: 1 : int nnode = vs->getNode(NODE_2)->getNode ();
1317 [ + + ]: 10 : for (int f = 0; f < lnfreqs; f++, sc++) { // for each frequency
1318 : : // fill right hand side vector
1319 [ + - ]: 9 : nr_double_t freq = rfreqs (f);
1320 [ + - ]: 9 : vs->calcHB (freq);
1321 [ + - ][ + - ]: 9 : I_(sc) = vs->getE (VSRC_1);
1322 : : // fill MNA entries
1323 : 9 : int pn = (pnode - 1) * lnfreqs + f;
1324 : 9 : int nn = (nnode - 1) * lnfreqs + f;
1325 [ + - ]: 9 : if (pnode) {
1326 [ + - ]: 9 : Y_(pn, sc) = +1.0;
1327 [ + - ]: 9 : Y_(sc, pn) = +1.0;
1328 : : }
1329 [ - + ]: 9 : if (nnode) {
1330 [ # # ]: 0 : Y_(nn, sc) = -1.0;
1331 [ # # ]: 0 : Y_(sc, nn) = -1.0;
1332 : : }
1333 : : }
1334 : : }
1335 : 1 : }
1336 : :
1337 : : /* The function calculates and saves the final solution. */
1338 : 1 : void hbsolver::finalSolution (void) {
1339 : :
1340 : : // extend the linear MNA matrix
1341 [ + - ][ + - ]: 1 : *NA = extendMatrixLinear (*NA, nnlvsrcs);
1342 : :
1343 : 1 : int S = NA->getCols ();
1344 : 1 : int N = nnanodes * lnfreqs;
1345 : :
1346 : : // right hand side vector
1347 [ + - ]: 1 : tvector<nr_complex_t> * I = new tvector<nr_complex_t> (S);
1348 : : // temporary solution
1349 [ + - ]: 1 : tvector<nr_complex_t> * V = new tvector<nr_complex_t> (S);
1350 : : // final solution
1351 [ + - ]: 1 : x = new tvector<nr_complex_t> (N);
1352 : :
1353 : : // fill in missing MNA entries
1354 : 1 : fillMatrixLinearExtended (NA, I);
1355 : :
1356 : : // put currents through balanced nodes into right hand side
1357 [ + + ]: 2 : for (int n = 0; n < nbanodes; n++) {
1358 [ + + ]: 10 : for (int f = 0; f < lnfreqs; f++) {
1359 [ + - ]: 9 : nr_complex_t i = IL->get (n * nlfreqs + f);
1360 [ + + ][ + + ]: 9 : if (f != 0 && f != lnfreqs - 1) i *= 2;
1361 [ + - ]: 9 : I_(n * lnfreqs + f) = i;
1362 : : }
1363 : : }
1364 : :
1365 : : // use QR decomposition for the final solution
1366 : : try_running () {
1367 : 1 : eqnsys<nr_complex_t> eqns;
1368 : 1 : eqns.setAlgo (ALGO_LU_DECOMPOSITION);
1369 [ + - ]: 1 : eqns.passEquationSys (NA, V, I);
1370 [ + - ]: 1 : eqns.solve ();
1371 : : }
1372 : : // appropriate exception handling
1373 [ - + ]: 1 : catch_exception () {
1374 : : case EXCEPTION_PIVOT:
1375 : : default:
1376 : : logprint (LOG_ERROR, "WARNING: %s: during final AC analysis\n",
1377 : 0 : getName ());
1378 : 0 : estack.print ();
1379 : : }
1380 [ + + ]: 28 : for (int i = 0; i < N; i++) x->set (i, V_(i));
1381 : 1 : }
1382 : :
1383 : : // Saves simulation results.
1384 : 1 : void hbsolver::saveResults (void) {
1385 : : vector * f;
1386 : : // add current frequency to the dependency of the output dataset
1387 [ + - ]: 1 : if ((f = data->findDependency ("hbfrequency")) == NULL) {
1388 [ + - ]: 1 : f = new vector ("hbfrequency");
1389 : 1 : data->addDependency (f);
1390 : : }
1391 : : // save frequency vector
1392 [ + - ]: 1 : if (runs == 1) {
1393 [ + - ][ + + ]: 10 : for (int i = 0; i < lnfreqs; i++) f->add (rfreqs (i));
1394 : : }
1395 : : // save node voltage vectors
1396 : 1 : int nanode = 0;
1397 [ + - ][ + - ]: 4 : for (strlistiterator it (nanodes); *it; ++it, nanode++) {
[ + - ][ + + ]
1398 [ + - ]: 3 : int l = strlen (*it);
1399 : 3 : char * n = (char *) malloc (l + 4);
1400 [ + - ]: 3 : sprintf (n, "%s.Vb", *it);
1401 [ + + ]: 30 : for (int i = 0; i < lnfreqs; i++) {
1402 [ + - ][ + - ]: 27 : saveVariable (n, x->get (i + nanode * lnfreqs), f);
1403 : : }
1404 [ + - ]: 1 : }
1405 : 1 : }
1406 : :
1407 : : // properties
1408 : : PROP_REQ [] = {
1409 : : { "n", PROP_INT, { 1, PROP_NO_STR }, PROP_MIN_VAL (1) },
1410 : : PROP_NO_PROP };
1411 : : PROP_OPT [] = {
1412 : : { "f", PROP_REAL, { 1e9, PROP_NO_STR }, PROP_POS_RANGEX },
1413 : : { "iabstol", PROP_REAL, { 1e-12, PROP_NO_STR }, PROP_RNG_X01I },
1414 : : { "vabstol", PROP_REAL, { 1e-6, PROP_NO_STR }, PROP_RNG_X01I },
1415 : : { "reltol", PROP_REAL, { 1e-3, PROP_NO_STR }, PROP_RNG_X01I },
1416 : : { "MaxIter", PROP_INT, { 150, PROP_NO_STR }, PROP_RNGII (2, 10000) },
1417 : : PROP_NO_PROP };
1418 : : struct define_t hbsolver::anadef =
1419 : : { "HB", 0, PROP_ACTION, PROP_NO_SUBSTRATE, PROP_LINEAR, PROP_DEF };
1420 : :
1421 : : } // namespace qucs
1422 : :
1423 : : /* TODO list for HB Solver:
1424 : : - Take care about nodes with non-linear components only.
1425 : : - AC Power Sources (extra Z and open loop voltage).
1426 : : - Current sources.
1427 : : - Balancing of multi-dimensional non-linear networks.
1428 : : - Sources directly connected to non-linear components and no other
1429 : : linear component (insert short).
1430 : : - Bug: With capacitors at hand there is voltage convergence but no
1431 : : current balancing.
1432 : : - Output currents through voltage sources.
1433 : : */
|