* hbsolver.cpp - harmonic balance solver class implementation
*
* Copyright (C) 2005, 2006, 2007, 2008 Stefan Jahn <stefan@lkcc.org>
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this package; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* $Id$
*
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include<algorithm>
#include <stdio.h>
#include "object.h"
#include "logging.h"
#include "complex.h"
#include "vector.h"
#include "node.h"
#include "circuit.h"
#include "component_id.h"
#include "net.h"
#include "netdefs.h"
#include "strlist.h"
#include "ptrlist.h"
#include "tvector.h"
#include "tmatrix.h"
#include "eqnsys.h"
#include "analysis.h"
#include "dataset.h"
#include "fourier.h"
#include "hbsolver.h"
#define HB_DEBUG 0
namespace qucs {
using namespace fourier;
hbsolver::hbsolver () : analysis () {
type = ANALYSIS_HBALANCE;
frequency = 0;
nlnodes = lnnodes = banodes = nanodes = NULL;
Y = Z = A = NULL;
NA = YV = JQ = JG = JF = NULL;
OM = IR = QR = RH = IG = FQ = VS = VP = FV = IL = IN = IC = IS = NULL;
vs = x = NULL;
runs = 0;
ndfreqs = NULL;
}
hbsolver::hbsolver (char * n) : analysis (n) {
type = ANALYSIS_HBALANCE;
frequency = 0;
nlnodes = lnnodes = banodes = nanodes = NULL;
Y = Z = A = NULL;
NA = YV = JQ = JG = JF = NULL;
OM = IR = QR = RH = IG = FQ = VS = VP = FV = IL = IN = IC = IS = NULL;
vs = x = NULL;
runs = 0;
ndfreqs = NULL;
}
hbsolver::~hbsolver () {
delete nlnodes;
delete lnnodes;
delete banodes;
delete nanodes;
delete A;
delete Z;
delete Y;
delete NA;
delete YV;
delete JQ;
delete JG;
delete JF;
delete IC;
delete IS;
delete FV;
delete IL;
delete IN;
delete IG;
delete FQ;
delete VS;
delete VP;
delete vs;
delete OM;
delete IR;
delete QR;
delete RH;
delete x;
delete[] ndfreqs;
}
based on the given hbsolver object. */
hbsolver::hbsolver (hbsolver & o) : analysis (o) {
frequency = o.frequency;
negfreqs = o.negfreqs;
posfreqs = o.posfreqs;
nlnodes = o.nlnodes;
lnnodes = o.lnnodes;
banodes = o.banodes;
nanodes = o.nanodes;
Y = Z = A = NULL;
NA = YV = JQ = JG = JF = NULL;
OM = IR = QR = RH = IG = FQ = VS = VP = FV = IL = IN = IC = IS = NULL;
vs = x = NULL;
runs = o.runs;
ndfreqs = NULL;
}
#define VS_(r) (*VS) (r)
#define OM_(r) (*OM) (r)
solves it then. */
int hbsolver::solve (void) {
int iterations = 0, done = 0;
int MaxIterations = getPropertyInteger ("MaxIter");
splitCircuits ();
collectFrequencies ();
getNodeLists ();
prepareLinear ();
runs++;
logprint (LOG_STATUS, "NOTIFY: %s: solving for %d frequencies\n",
getName (), lnfreqs);
if (nbanodes > 0) {
logprint (LOG_STATUS, "NOTIFY: %s: balancing at %d nodes\n", getName (),
nbanodes);
prepareNonLinear ();
#if HB_DEBUG
fprintf (stderr, "YV -- transY in f:\n"); YV->print ();
fprintf (stderr, "IC -- constant current in f:\n"); IC->print ();
#endif
do {
iterations++;
#if HB_DEBUG
fprintf (stderr, "\n -- iteration step: %d\n", iterations);
fprintf (stderr, "vs -- voltage in t:\n"); vs->print ();
#endif
loadMatrices ();
#if HB_DEBUG
fprintf (stderr, "FQ -- charge in t:\n"); FQ->print ();
fprintf (stderr, "IG -- current in t:\n"); IG->print ();
#endif
VectorFFT (IG);
VectorFFT (FQ);
VectorFFT (IR);
VectorFFT (QR);
#if HB_DEBUG
fprintf (stderr, "VS -- voltage in f:\n"); VS->print ();
fprintf (stderr, "FQ -- charge in f:\n"); FQ->print ();
fprintf (stderr, "IG -- current in f:\n"); IG->print ();
fprintf (stderr, "IR -- corrected Jacobi current in f:\n"); IR->print ();
#endif
solveHB ();
#if HB_DEBUG
fprintf (stderr, "FV -- error vector F(V) in f:\n"); FV->print ();
fprintf (stderr, "IL -- linear currents in f:\n"); IL->print ();
fprintf (stderr, "IN -- non-linear currents in f:\n"); IN->print ();
fprintf (stderr, "RH -- right-hand side currents in f:\n"); RH->print ();
#endif
if (iterations > 1 && checkBalance ()) {
done = 1;
break;
}
#if HB_DEBUG
fprintf (stderr, "JG -- G-Jacobian in t:\n"); JG->print ();
fprintf (stderr, "JQ -- C-Jacobian in t:\n"); JQ->print ();
#endif
MatrixFFT (JG);
MatrixFFT (JQ);
#if HB_DEBUG
fprintf (stderr, "JQ -- dQ/dV C-Jacobian in f:\n"); JQ->print ();
fprintf (stderr, "JG -- dI/dV G-Jacobian in f:\n"); JG->print ();
#endif
calcJacobian ();
#if HB_DEBUG
fprintf (stderr, "JF -- full Jacobian in f:\n"); JF->print ();
#endif
solveVoltages ();
#if HB_DEBUG
fprintf (stderr, "VS -- next voltage in f:\n"); VS->print ();
#endif
VectorIFFT (vs);
}
while (!done && iterations < MaxIterations);
if (iterations >= MaxIterations) {
qucs::exception * e = new qucs::exception (EXCEPTION_NO_CONVERGENCE);
e->setText ("no convergence in %s analysis after %d iterations",
getName (), iterations);
throw_exception (e);
logprint (LOG_ERROR, "%s: no convergence after %d iterations\n",
getName (), iterations);
}
else {
logprint (LOG_STATUS, "%s: convergence reached after %d iterations\n",
getName (), iterations);
}
}
else {
logprint (LOG_STATUS, "NOTIFY: %s: no balancing necessary\n", getName ());
}
estack.print ();
finalSolution ();
saveResults ();
return 0;
}
function. */
void hbsolver::calc (hbsolver * self) {
circuit * root = self->getNet()->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
c->calcHB (self->frequency);
}
}
function. */
void hbsolver::initHB (void) {
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
c->initHB ();
}
}
function. */
void hbsolver::initDC (void) {
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
c->initDC ();
}
}
bool hbsolver::isExcitation (circuit * c) {
int type = c->getType ();
if (type == CIR_PAC || type == CIR_VAC || type == CIR_VDC)
return true;
return false;
}
void hbsolver::expandFrequencies (nr_double_t f, int n) {
auto nfreqs = negfreqs;
auto pfreqs = posfreqs;
int i, k, len = nfreqs.size ();
negfreqs.clear ();
posfreqs.clear ();
if (len > 0) {
for (i = 0; i <= n + 1; i++) {
for (k = 0; k < len; k++) {
negfreqs.push_back (i * f + nfreqs[k]);
}
}
for (i = -n; i < 0; i++) {
for (k = 0; k < len; k++) {
negfreqs.push_back (i * f + nfreqs[k]);
}
}
for (i = 0; i <= 2 * n + 1; i++) {
for (k = 0; k < len; k++) {
posfreqs.push_back (i * f + pfreqs[k]);
}
}
}
else {
for (i = 0; i <= n + 1; i++) negfreqs.push_back (i * f);
for (i = -n; i < 0; i++) negfreqs.push_back (i * f);
for (i = 0; i <= 2 * n + 1; i++) posfreqs.push_back (i * f);
}
}
int hbsolver::calcOrder (int n) {
int o, order = n * 2;
for (o = 1; o < order; o <<= 1) ;
return o / 2 - 1;
}
circuit list depending on the maximum number of harmonics per
excitation and saves its results into the 'negfreqs' vector. */
void hbsolver::collectFrequencies (void) {
negfreqs.clear ();
posfreqs.clear ();
rfreqs.clear ();
dfreqs.clear ();
delete[] ndfreqs;
int i, n = calcOrder (getPropertyInteger ("n"));
nr_double_t f;
for (auto * c : excitations) {
if (c->getType () != CIR_VDC) {
if ((f = c->getPropertyDouble ("f")) != 0.0) {
const auto epsilon = std::numeric_limits<nr_double_t>::epsilon();
auto found =
std::find_if(dfreqs.cbegin(),dfreqs.cend(),
[f,epsilon](decltype(*dfreqs.cbegin()) x) {
return (std::abs(x-f) < epsilon);
})
;
if (found == dfreqs.cend()) {
dfreqs.push_back (f);
expandFrequencies (f, n);
}
}
}
}
if (negfreqs.size () == 0) {
f = getPropertyDouble ("f");
dfreqs.push_back (f);
expandFrequencies (f, n);
}
ndfreqs = new int[dfreqs.size ()];
for (i = 0; i < dfreqs.size (); i++) {
ndfreqs[i] = (n + 1) * 2;
}
#if HB_DEBUG
fprintf (stderr, "%d frequencies: [ ", negfreqs.getSize ());
for (i = 0; i < negfreqs.getSize (); i++) {
fprintf (stderr, "%g ", (double) real (negfreqs.get (i)));
}
fprintf (stderr, "]\n");
#endif
for (n = 0; n < negfreqs.size (); n++) {
if ((f = negfreqs[n]) < 0.0) continue;
rfreqs.push_back (f);
}
lnfreqs = rfreqs.size ();
nlfreqs = negfreqs.size ();
OM = new tvector<nr_complex_t> (nlfreqs);
for (n = i = 0; n < nlfreqs; n++, i++)
OM_(n) = nr_complex_t (0, 2 * pi * negfreqs[i]);
}
void hbsolver::splitCircuits (void) {
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ()) {
if (c->isNonLinear ()) {
nolcircuits.push_front(c);
}
else if (isExcitation (c)) {
excitations.push_front(c);
}
else if (c->getType () != CIR_GROUND) {
lincircuits.push_front (c);
}
}
}
strlist * hbsolver::circuitNodes (ptrlist<circuit> circuits) {
strlist * nodes = new strlist ();
for (auto * c : circuits) {
for (int i = 0; i < c->getSize (); i++) {
const char * n = c->getNode(i)->getName ();
if (strcmp (n, "gnd")) {
if (!nodes->contains (n)) nodes->add (n);
}
}
}
return nodes;
}
void hbsolver::getNodeLists (void) {
nlnodes = circuitNodes (nolcircuits);
lnnodes = circuitNodes (lincircuits);
exnodes = circuitNodes (excitations);
#if DEBUG && 0
fprintf (stderr, "nonlinear nodes: [ %s ]\n", nlnodes->toString ());
fprintf (stderr, " linear nodes: [ %s ]\n", lnnodes->toString ());
#endif
nanodes = new strlist (*nlnodes);
strlistiterator it;
for (it = strlistiterator (exnodes); *it; ++it)
nanodes->append (*it);
for (it = strlistiterator (lnnodes); *it; ++it) {
if (!nanodes->contains (*it))
nanodes->append (*it);
}
banodes = new strlist (*nlnodes);
#if DEBUG && 0
fprintf (stderr, " balanced nodes: [ %s ]\n", banodes->toString ());
fprintf (stderr, " exnodes nodes: [ %s ]\n", exnodes->toString ());
fprintf (stderr, " nanodes nodes: [ %s ]\n", nanodes->toString ());
#endif
}
built in internal voltage source in the list of circuits. */
int hbsolver::assignVoltageSources (ptrlist<circuit> circuits) {
int sources = 0;
for (auto *c: circuits) {
if (c->getVoltageSources () > 0) {
c->setVoltageSource (sources);
sources += c->getVoltageSources ();
}
}
return sources;
}
int hbsolver::assignNodes (ptrlist<circuit> circuits, strlist * nodes,
int offset) {
for (int nr = 0; nr < nodes->length (); nr++) {
char * nn = nodes->get (nr);
for (auto *c : circuits) {
for (int i = 0; i < c->getSize (); i++) {
node * n = c->getNode (i);
if (!strcmp (n->getName (), nn)) n->setNode (offset + nr + 1);
}
}
}
return nodes->length ();
}
void hbsolver::prepareLinear (void) {
for (auto *lc : lincircuits)
lc->initHB ();
nlnvsrcs = assignVoltageSources (lincircuits);
nnlvsrcs = excitations.size ();
nnanodes = nanodes->length ();
nexnodes = exnodes->length ();
nbanodes = banodes->length ();
assignNodes (lincircuits, nanodes);
assignNodes (excitations, nanodes);
createMatrixLinearA ();
createMatrixLinearY ();
calcConstantCurrent ();
}
contains the MNA entries for all linear components for each
requested frequency. */
void hbsolver::createMatrixLinearA (void) {
int M = nlnvsrcs;
int N = nnanodes;
int f = 0;
nr_double_t freq;
A = new tmatrix<nr_complex_t> ((N + M) * lnfreqs);
for (int i = 0; i < rfreqs.size (); i++) {
freq = rfreqs[i];
for (auto *lc : lincircuits)
lc->calcHB (freq);
fillMatrixLinearA (A, f++);
}
NA = new tmatrix<nr_complex_t> (*A);
}
#undef A_
#undef B_
#define A_(r,c) (*A) ((r)*lnfreqs+f,(c)*lnfreqs+f)
#define G_(r,c) A_(r,c)
#define B_(r,c) A_(r,c+N)
#define C_(r,c) A_(r+N,c)
#define D_(r,c) A_(r+N,c+N)
a given frequency index. */
void hbsolver::fillMatrixLinearA (tmatrix<nr_complex_t> * A, int f) {
int N = nnanodes;
for (auto *cir : lincircuits) {
int s = cir->getSize ();
int nr, nc, r, c, v;
for (r = 0; r < s; r++) {
if ((nr = cir->getNode(r)->getNode () - 1) < 0) continue;
for (c = 0; c < s; c++) {
if ((nc = cir->getNode(c)->getNode () - 1) < 0) continue;
G_(nr, nc) += cir->getY (r, c);
}
}
if ((v = cir->getVoltageSources ()) > 0) {
for (r = 0; r < s; r++) {
if ((nr = cir->getNode(r)->getNode () - 1) < 0) continue;
for (c = 0; c < v; c++) {
nc = cir->getVoltageSource () + c;
B_(nr, nc) += cir->getB (r, nc);
}
}
for (r = 0; r < v; r++) {
nr = cir->getVoltageSource () + r;
for (c = 0; c < s; c++) {
if ((nc = cir->getNode(c)->getNode () - 1) < 0) continue;
C_(nr, nc) += cir->getC (nr, c);
}
}
for (r = 0; r < v; r++) {
nr = cir->getVoltageSource () + r;
for (c = 0; c < v; c++) {
nc = cir->getVoltageSource () + c;
D_(nr, nc) += cir->getD (nr, nc);
}
}
}
}
}
void hbsolver::invertMatrix (tmatrix<nr_complex_t> * A,
tmatrix<nr_complex_t> * H) {
eqnsys<nr_complex_t> eqns;
int N = A->getCols ();
tvector<nr_complex_t> * x = new tvector<nr_complex_t> (N);
tvector<nr_complex_t> * z = new tvector<nr_complex_t> (N);
try_running () {
eqns.setAlgo (ALGO_LU_FACTORIZATION_CROUT);
eqns.passEquationSys (A, x, z);
eqns.solve ();
}
catch_exception () {
case EXCEPTION_PIVOT:
default:
logprint (LOG_ERROR, "WARNING: %s: during TI inversion\n", getName ());
estack.print ();
}
eqns.setAlgo (ALGO_LU_SUBSTITUTION_CROUT);
for (int c = 0; c < N; c++) {
z->set (0.0);
z->set (c, 1.0);
eqns.passEquationSys (A, x, z);
eqns.solve ();
for (int r = 0; r < N; r++) H->set (r, c, x->get (r));
}
delete x;
delete z;
}
#define V_(r) (*V) (r)
#define I_(r) (*I) (r)
#undef A_
#define A_(r,c) (*A) (r,c)
#define Z_(r,c) (*Z) (r,c)
#define Y_(r,c) (*Y) (r,c)
#define ZVU_(r,c) Z_(r,c)
#define ZVL_(r,c) Z_((r)*lnfreqs+f+sn,c)
#define ZCU_(r,c) Z_(r,(c)*lnfreqs+f+sn)
#define ZCL_(r,c) Z_((r)*lnfreqs+f+sn,(c)*lnfreqs+f+sn)
#define YV_(r,c) (*YV) (r,c)
#define NA_(r,c) (*NA) (r,c)
#define JF_(r,c) (*JF) (r,c)
1. form the MNA matrix A including all nodes (linear, non-linear and
excitations)
2. compute the variable transimpedance matrix entries for the nodes
to be balanced
3. compute the constant transimpedance matrix entries for the constant
current vector caused by the excitations
4. invert this overall transimpedance matrix
5. extract the variable transadmittance matrix entries
*/
void hbsolver::createMatrixLinearY (void) {
int M = nlnvsrcs;
int N = nnanodes;
int c, r, f;
int sa = (N + M) * lnfreqs;
int sv = nbanodes;
int se = nnlvsrcs;
int sy = sv + se;
Z = new tmatrix<nr_complex_t> (sy * lnfreqs);
eqnsys<nr_complex_t> eqns;
tvector<nr_complex_t> * V;
tvector<nr_complex_t> * I;
int sn = sv * lnfreqs;
V = new tvector<nr_complex_t> (sa);
I = new tvector<nr_complex_t> (sa);
for (c = 0; c < sv * lnfreqs; c++) A_(c, c) += 0.01;
for (auto *vs : excitations) {
int pnode = vs->getNode(NODE_1)->getNode ();
int nnode = vs->getNode(NODE_2)->getNode ();
for (f = 0; f < lnfreqs; f++) {
int pn = (pnode - 1) * lnfreqs + f;
int nn = (nnode - 1) * lnfreqs + f;
if (pnode) A_(pn, pn) += 0.01;
if (nnode) A_(nn, nn) += 0.01;
if (pnode && nnode) {
A_(pn, nn) -= 0.01;
A_(nn, pn) -= 0.01;
}
}
}
try_running () {
eqns.setAlgo (ALGO_LU_FACTORIZATION_CROUT);
eqns.passEquationSys (A, V, I);
eqns.solve ();
}
catch_exception () {
case EXCEPTION_PIVOT:
default:
logprint (LOG_ERROR, "WARNING: %s: during A factorization\n", getName ());
estack.print ();
}
eqns.setAlgo (ALGO_LU_SUBSTITUTION_CROUT);
for (c = 0; c < sn; c++) {
I->set (0.0);
I_(c) = 1.0;
eqns.passEquationSys (A, V, I);
eqns.solve ();
for (r = 0; r < sn; r++) ZVU_(r, c) = V_(r);
r = 0;
for (auto ite = excitations.begin(); ite != excitations.end(); ++ite, r++) {
for (f = 0; f < lnfreqs; f++) {
ZVL_(r, c) = excitationZ (V, *ite, f);
}
}
}
int vsrc = 0;
for (auto it = excitations.begin(); it != excitations.end(); ++it, vsrc++) {
circuit * vs = *it;
int pnode = vs->getNode(NODE_1)->getNode ();
int nnode = vs->getNode(NODE_2)->getNode ();
for (f = 0; f < lnfreqs; f++) {
int pn = (pnode - 1) * lnfreqs + f;
int nn = (nnode - 1) * lnfreqs + f;
I->set (0.0);
if (pnode) I_(pn) = +1.0;
if (nnode) I_(nn) = -1.0;
eqns.passEquationSys (A, V, I);
eqns.solve ();
for (r = 0; r < sn; r++) {
ZCU_(r, vsrc) = V_(r);
}
r = 0;
for (auto ite = excitations.begin(); ite != excitations.end(); ++ite, r++) {
ZCL_(r, vsrc) = excitationZ (V, *ite, f);
}
}
}
delete I;
delete V;
Y = new tmatrix<nr_complex_t> (sy * lnfreqs);
invertMatrix (Z, Y);
for (c = 0; c < sy * lnfreqs; c++) Y_(c, c) -= 0.01;
YV = new tmatrix<nr_complex_t> (sv * nlfreqs);
*YV = expandMatrix (*Y, sv);
delete A; A = NULL;
delete Z; Z = NULL;
}
given voltage source (excitation) and for a given frequency
index. */
nr_complex_t hbsolver::excitationZ (tvector<nr_complex_t> * V, circuit * vs,
int f) {
int pnode = vs->getNode(NODE_1)->getNode ();
int nnode = vs->getNode(NODE_2)->getNode ();
nr_complex_t z = 0.0;
if (pnode) z += V_((pnode - 1) * lnfreqs + f);
if (nnode) z -= V_((nnode - 1) * lnfreqs + f);
return z;
}
voltage of the excitations and the transadmittance matrix
entries. */
void hbsolver::calcConstantCurrent (void) {
int se = nnlvsrcs * lnfreqs;
int sn = nbanodes * lnfreqs;
int r, c, vsrc = 0;
tvector<nr_complex_t> VC (se);
for (auto it = excitations.begin(); it != excitations.end(); ++it, vsrc++) {
circuit * vs = *it;
vs->initHB ();
vs->setVoltageSource (0);
for (int f = 0; f < rfreqs.size (); f++) {
nr_double_t freq = rfreqs[f];
vs->calcHB (freq);
VC (vsrc * lnfreqs + f) = vs->getE (VSRC_1);
}
}
IC = new tvector<nr_complex_t> (sn);
for (r = 0; r < sn; r++) {
nr_complex_t i = 0.0;
for (c = 0; c < se; c++) {
i += Y_(r, c + sn) * VC (c);
}
int f = r % lnfreqs;
if (f != 0 && f != lnfreqs - 1) i /= 2;
IC->set (r, i);
}
*IC = expandVector (*IC, nbanodes);
IS = new tvector<nr_complex_t> (se);
for (r = 0; r < se; r++) {
nr_complex_t i = 0.0;
for (c = 0; c < se; c++) {
i += Y_(r + sn, c + sn) * VC (c);
}
IS->set (r, i);
}
delete Y; Y = NULL;
}
non-linear subcircuit (in the frequency domain) are equal. */
int hbsolver::checkBalance (void) {
nr_double_t iabstol = getPropertyDouble ("iabstol");
nr_double_t vabstol = getPropertyDouble ("vabstol");
nr_double_t reltol = getPropertyDouble ("reltol");
int n, len = FV->size ();
for (n = 0; n < len; n++) {
nr_double_t v_abs = abs (VS->get (n) - VP->get (n));
nr_double_t v_rel = abs (VS->get (n));
if (v_abs >= vabstol + reltol * v_rel) return 0;
nr_complex_t il = IL->get (n);
nr_complex_t in = IN->get (n);
if (il != in) {
nr_double_t i_abs = abs (il + in);
nr_double_t i_rel = abs ((il + in) / (il - in));
if (i_abs >= iabstol && 2.0 * i_rel >= reltol) return 0;
}
}
return 1;
}
#undef G_
#undef C_
#define G_(r,c) (*jg) ((r)*nlfreqs+f,(c)*nlfreqs+f)
#define C_(r,c) (*jq) ((r)*nlfreqs+f,(c)*nlfreqs+f)
#undef FI_
#undef FQ_
#define FI_(r) (*ig) ((r)*nlfreqs+f)
#define FQ_(r) (*fq) ((r)*nlfreqs+f)
#define IR_(r) (*ir) ((r)*nlfreqs+f)
#define QR_(r) (*qr) ((r)*nlfreqs+f)
non-linear HB equations for a given frequency index. */
void hbsolver::fillMatrixNonLinear (tmatrix<nr_complex_t> * jg,
tmatrix<nr_complex_t> * jq,
tvector<nr_complex_t> * ig,
tvector<nr_complex_t> * fq,
tvector<nr_complex_t> * ir,
tvector<nr_complex_t> * qr,
int f) {
for (auto *cir: nolcircuits) {
int s = cir->getSize ();
int nr, nc, r, c;
for (r = 0; r < s; r++) {
if ((nr = cir->getNode(r)->getNode () - 1) < 0) continue;
for (c = 0; c < s; c++) {
if ((nc = cir->getNode(c)->getNode () - 1) < 0) continue;
G_(nr, nc) += cir->getY (r, c);
C_(nr, nc) += cir->getQV (r, c);
}
FI_(nr) -= cir->getI (r);
FQ_(nr) -= cir->getQ (r);
IR_(nr) += cir->getGV (r) + cir->getI (r);
QR_(nr) += cir->getCV (r) + cir->getQ (r);
}
}
}
void hbsolver::prepareNonLinear (void) {
int N = nbanodes;
if (FQ == NULL) {
FQ = new tvector<nr_complex_t> (N * nlfreqs);
}
if (IG == NULL) {
IG = new tvector<nr_complex_t> (N * nlfreqs);
}
if (IR == NULL) {
IR = new tvector<nr_complex_t> (N * nlfreqs);
}
if (QR == NULL) {
QR = new tvector<nr_complex_t> (N * nlfreqs);
}
if (JG == NULL) {
JG = new tmatrix<nr_complex_t> (N * nlfreqs);
}
if (JQ == NULL) {
JQ = new tmatrix<nr_complex_t> (N * nlfreqs);
}
if (JF == NULL) {
JF = new tmatrix<nr_complex_t> (N * nlfreqs);
}
if (VS == NULL) {
VS = new tvector<nr_complex_t> (N * nlfreqs);
}
if (vs == NULL) {
vs = new tvector<nr_complex_t> (N * nlfreqs);
}
if (VP == NULL) {
VP = new tvector<nr_complex_t> (N * nlfreqs);
}
if (FV == NULL) {
FV = new tvector<nr_complex_t> (N * nlfreqs);
}
if (RH == NULL) {
RH = new tvector<nr_complex_t> (N * nlfreqs);
}
if (IL == NULL) {
IL = new tvector<nr_complex_t> (N * nlfreqs);
}
if (IN == NULL) {
IN = new tvector<nr_complex_t> (N * nlfreqs);
}
assignNodes (nolcircuits, nanodes);
for (auto *cir : nolcircuits) {
cir->initHB (nlfreqs);
}
}
frequency entry into the circuit voltage vector. */
void hbsolver::saveNodeVoltages (circuit * cir, int f) {
int r, nr, s = cir->getSize ();
for (r = 0; r < s; r++) {
if ((nr = cir->getNode(r)->getNode () - 1) < 0) continue;
cir->setV (r, real (vs->get (nr * nlfreqs + f)));
}
}
non-linear components' HB calculator for each frequency and applies
the matrix and vector entries appropriately. */
void hbsolver::loadMatrices (void) {
IG->set (0.0);
FQ->set (0.0);
IR->set (0.0);
QR->set (0.0);
JG->set (0.0);
JQ->set (0.0);
for (int f = 0; f < nlfreqs; f++) {
for (auto *cir : nolcircuits) {
saveNodeVoltages (cir, f);
cir->calcHB (f);
}
fillMatrixNonLinear (JG, JQ, IG, FQ, IR, QR, f);
}
}
Transformation from the time domain to the frequency domain.
\todo rewrite ugly should die
*/
void hbsolver::VectorFFT (tvector<nr_complex_t> * V, int isign) {
int i, k, r;
int n = nlfreqs;
int nd = dfreqs.size ();
int nodes = V->size () / n;
nr_double_t * d = (double *)V->getData ();
if (nd == 1) {
for (k = i = 0; i < nodes; i++, k += 2 * n) {
nr_double_t * dst = &d[k];
_fft_1d (dst, n, isign);
if (isign > 0) for (r = 0; r < 2 * n; r++) *dst++ /= n;
}
}
else {
for (k = i = 0; i < nodes; i++, k += 2 * n) {
nr_double_t * dst = &d[k];
_fft_nd (dst, ndfreqs, nd, isign);
if (isign > 0) for (r = 0; r < 2 * n; r++) *dst++ /= ndfreqs[0];
}
}
}
Fourier Transformation from the frequency domain to the domain
time. */
void hbsolver::VectorIFFT (tvector<nr_complex_t> * V, int isign) {
VectorFFT (V, -isign);
}
Transformation from the time domain to the frequency domain. */
void hbsolver::MatrixFFT (tmatrix<nr_complex_t> * M) {
#if THE_SLO_ALGO
for (int c = 0; c < M->getCols (); c++) {
tvector<nr_complex_t> V = M->getCol (c);
VectorFFT (&V);
M->setCol (c, V);
}
for (int r = 0; r < M->getRows (); r++) {
tvector<nr_complex_t> V = M->getRow (r);
VectorIFFT (&V);
M->setRow (r, V);
}
#else
int c, r, nc, nr;
for (nc = c = 0; c < nbanodes; c++, nc += nlfreqs) {
for (nr = r = 0; r < nbanodes; r++, nr += nlfreqs) {
tvector<nr_complex_t> V (nlfreqs);
int fr, fc, fi;
for (fc = 0; fc < nlfreqs; fc++) V (fc) = M->get (nr + fc, nc + fc);
VectorFFT (&V);
for (fc = 0; fc < nlfreqs; fc++) {
for (fi = nlfreqs - 1 - fc, fr = 0; fr < nlfreqs; fr++) {
if (++fi >= nlfreqs) fi = 0;
M->set (nr + fr, nc + fc, V (fi));
}
}
}
}
#endif
}
F(V) = IC + [YV] * VS + j[O] * FQ + IG -> 0
Care must be taken with indexing here: In the frequency domain only
real positive frequencies are used and computed, but in the time
domain we have more values because of the periodic continuation in
the frequency domain.
RHS = j[O] * CV + GV - (IC + j[O] * FQ + IG)
Also the right hand side of the equation system for the new voltage
vector is computed here. */
void hbsolver::solveHB (void) {
for (int r = 0; r < nbanodes * nlfreqs; ) {
for (int f = 0; f < nlfreqs; f++, r++) {
nr_complex_t il = 0.0, in = 0.0, ir = 0.0;
il += IC->get (r);
ir -= il;
for (int c = 0; c < nbanodes * nlfreqs; c++) {
il += YV_(r, c) * VS_(c);
}
in += OM_(f) * FQ->get (r);
in += IG->get (r);
ir += IR->get (r);
ir += OM_(f) * QR->get (r);
RH->set (r, ir);
FV->set (r, il + in);
IL->set (r, il);
IN->set (r, in);
}
}
}
void hbsolver::calcJacobian (void) {
int c, r, fc, fr, rt, ct;
admittances matrix JG into complete Jacobian JF */
for (c = 0; c < nbanodes; c++) {
ct = c * nlfreqs;
for (fc = 0; fc < nlfreqs; fc++, ct++) {
for (r = 0; r < nbanodes; r++) {
rt = r * nlfreqs;
for (fr = 0; fr < nlfreqs; fr++, rt++) {
JF_(rt, ct) = JG->get (rt, ct) + JQ->get (rt, ct) * OM_(fr);
}
}
}
}
*JF += *YV;
}
make it a real valued signal in the time domain. */
tvector<nr_complex_t> hbsolver::expandVector (tvector<nr_complex_t> V,
int nodes) {
tvector<nr_complex_t> res (nodes * nlfreqs);
int r, ff, rf, rt;
for (r = 0; r < nodes; r++) {
rt = r * nlfreqs;
rf = r * lnfreqs;
for (ff = 0; ff < lnfreqs; ff++, rf++, rt++) {
res (rt) = V (rf);
}
for (rf -= 2; ff < nlfreqs; ff++, rf--, rt++) {
res (rt) = conj (V (rf));
}
}
return res;
}
make it a real valued signal in the time domain. */
tmatrix<nr_complex_t> hbsolver::expandMatrix (tmatrix<nr_complex_t> M,
int nodes) {
tmatrix<nr_complex_t> res (nodes * nlfreqs);
int r, c, rf, rt, cf, ct, ff;
for (r = 0; r < nodes; r++) {
for (c = 0; c < nodes; c++) {
rf = r * lnfreqs;
rt = r * nlfreqs;
cf = c * lnfreqs;
ct = c * nlfreqs;
for (ff = 0; ff < lnfreqs; ff++, cf++, ct++, rf++, rt++) {
res (rt, ct) = M (rf, cf);
}
for (cf -= 2, rf -= 2; ff < nlfreqs; ff++, cf--, ct++, rf--, rt++) {
res (rt, ct) = conj (M (rf, cf));
}
}
}
return res;
}
JF * VS(n+1) = JF * VS(n) - FV
in order to obtains a new voltage vector in the frequency domain. */
void hbsolver::solveVoltages (void) {
*VP = *VS;
eqnsys<nr_complex_t> eqns;
try_running () {
eqns.setAlgo (ALGO_LU_DECOMPOSITION);
eqns.passEquationSys (JF, VS, RH);
eqns.solve ();
}
catch_exception () {
case EXCEPTION_PIVOT:
default:
logprint (LOG_ERROR, "WARNING: %s: during NR iteration\n", getName ());
estack.print ();
}
*vs = *VS;
}
contain the additional rows and columns for the excitation voltage
sources. */
tmatrix<nr_complex_t> hbsolver::extendMatrixLinear (tmatrix<nr_complex_t> M,
int nodes) {
int no = M.getCols ();
tmatrix<nr_complex_t> res (no + nodes * lnfreqs);
for (int r = 0; r < no; r++) {
for (int c = 0; c < no; c++) {
res (r, c) = M (r, c);
}
}
return res;
}
voltage sources into the extended rows and columns as well as the
actual voltage values into the right hand side vector. */
void hbsolver::fillMatrixLinearExtended (tmatrix<nr_complex_t> * Y,
tvector<nr_complex_t> * I) {
int of = lnfreqs * (nlnvsrcs + nnanodes);
int sc = of;
for (auto *vs : excitations) {
int pnode = vs->getNode(NODE_1)->getNode ();
int nnode = vs->getNode(NODE_2)->getNode ();
for (int f = 0; f < lnfreqs; f++, sc++) {
nr_double_t freq = rfreqs[f];
vs->calcHB (freq);
I_(sc) = vs->getE (VSRC_1);
int pn = (pnode - 1) * lnfreqs + f;
int nn = (nnode - 1) * lnfreqs + f;
if (pnode) {
Y_(pn, sc) = +1.0;
Y_(sc, pn) = +1.0;
}
if (nnode) {
Y_(nn, sc) = -1.0;
Y_(sc, nn) = -1.0;
}
}
}
}
void hbsolver::finalSolution (void) {
*NA = extendMatrixLinear (*NA, nnlvsrcs);
int S = NA->getCols ();
int N = nnanodes * lnfreqs;
tvector<nr_complex_t> * I = new tvector<nr_complex_t> (S);
tvector<nr_complex_t> * V = new tvector<nr_complex_t> (S);
x = new tvector<nr_complex_t> (N);
fillMatrixLinearExtended (NA, I);
for (int n = 0; n < nbanodes; n++) {
for (int f = 0; f < lnfreqs; f++) {
nr_complex_t i = IL->get (n * nlfreqs + f);
if (f != 0 && f != lnfreqs - 1) i *= 2;
I_(n * lnfreqs + f) = i;
}
}
try_running () {
eqnsys<nr_complex_t> eqns;
eqns.setAlgo (ALGO_LU_DECOMPOSITION);
eqns.passEquationSys (NA, V, I);
eqns.solve ();
}
catch_exception () {
case EXCEPTION_PIVOT:
default:
logprint (LOG_ERROR, "WARNING: %s: during final AC analysis\n",
getName ());
estack.print ();
}
for (int i = 0; i < N; i++) x->set (i, V_(i));
}
void hbsolver::saveResults (void) {
vector * f;
if ((f = data->findDependency ("hbfrequency")) == NULL) {
f = new vector ("hbfrequency");
data->addDependency (f);
}
if (runs == 1) {
for (int i = 0; i < lnfreqs; i++) f->add (rfreqs[i]);
}
int nanode = 0;
for (strlistiterator it (nanodes); *it; ++it, nanode++) {
int l = strlen (*it);
char * n = (char *) malloc (l + 4);
sprintf (n, "%s.Vb", *it);
for (int i = 0; i < lnfreqs; i++) {
saveVariable (n, x->get (i + nanode * lnfreqs), f);
}
}
}
PROP_REQ [] = {
{ "n", PROP_INT, { 1, PROP_NO_STR }, PROP_MIN_VAL (1) },
PROP_NO_PROP };
PROP_OPT [] = {
{ "f", PROP_REAL, { 1e9, PROP_NO_STR }, PROP_POS_RANGEX },
{ "iabstol", PROP_REAL, { 1e-12, PROP_NO_STR }, PROP_RNG_X01I },
{ "vabstol", PROP_REAL, { 1e-6, PROP_NO_STR }, PROP_RNG_X01I },
{ "reltol", PROP_REAL, { 1e-3, PROP_NO_STR }, PROP_RNG_X01I },
{ "MaxIter", PROP_INT, { 150, PROP_NO_STR }, PROP_RNGII (2, 10000) },
PROP_NO_PROP };
struct define_t hbsolver::anadef =
{ "HB", 0, PROP_ACTION, PROP_NO_SUBSTRATE, PROP_LINEAR, PROP_DEF };
}
- Take care about nodes with non-linear components only.
- AC Power Sources (extra Z and open loop voltage).
- Current sources.
- Balancing of multi-dimensional non-linear networks.
- Sources directly connected to non-linear components and no other
linear component (insert short).
- Bug: With capacitors at hand there is voltage convergence but no
current balancing.
- Output currents through voltage sources.
*/