* eqndefined.cpp - equation defined device class implementation
*
* Copyright (C) 2007 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 "component.h"
#include "equation.h"
#include "environment.h"
#include "device.h"
#include "eqndefined.h"
using namespace qucs;
using namespace qucs::eqn;
eqndefined::eqndefined () : circuit () {
type = CIR_EQNDEFINED;
setVariableSized (true);
veqn = NULL;
ieqn = NULL;
qeqn = NULL;
geqn = NULL;
ceqn = NULL;
_jstat = NULL;
_jdyna = NULL;
_charges = NULL;
}
eqndefined::~eqndefined () {
free (veqn);
free (ieqn);
free (geqn);
free (qeqn);
free (ceqn);
free (_jstat);
free (_jdyna);
free (_charges);
}
void eqndefined::initDC (void) {
allocMatrixMNA ();
if (ieqn == NULL) initModel ();
doHB = false;
}
#define A(a) ((assignment *) (a))
#define C(c) ((constant *) (c))
#define BP(n) real (getV (n * 2 + 0) - getV (n * 2 + 1))
char * eqndefined::createVariable (const char * base, int n, bool pfx) {
const char * str = strchr (getName (), '.');
if (str != NULL)
str = strrchr (str, '.') + 1;
else
str = getName ();
char * txt = (char *) malloc (strlen (str) + strlen (base) + 3);
if (pfx)
sprintf (txt, "%s.%s%d", str, base, n);
else
sprintf (txt, "%s%d", base, n);
return txt;
}
char * eqndefined::createVariable (const char * base, int r, int c, bool pfx) {
const char * str = strchr (getName (), '.');
if (str != NULL)
str = strrchr (str, '.') + 1;
else
str = getName ();
char * txt = (char *) malloc (strlen (str) + strlen (base) + 4);
if (pfx)
sprintf (txt, "%s.%s%d%d", str, base, r, c);
else
sprintf (txt, "%s%d%d", base, r, c);
return txt;
}
void eqndefined::setResult (void * eqn, nr_double_t val) {
A(eqn)->evaluate ();
constant * c = A(eqn)->getResult ();
c->d = val;
}
nr_double_t eqndefined::getResult (void * eqn) {
A(eqn)->evaluate ();
return A(eqn)->getResultDouble ();
}
void eqndefined::initModel (void) {
int i, j, k, branches = getSize () / 2;
char * in, * qn, * vn, * gn, * cn, * vnold, * inold;
eqn::node * ivalue, * qvalue, * diff;
veqn = (void **) malloc (sizeof (assignment *) * branches);
ieqn = (void **) malloc (sizeof (assignment *) * branches);
geqn = (void **) malloc (sizeof (assignment *) * branches * branches);
qeqn = (void **) malloc (sizeof (assignment *) * branches);
ceqn = (void **) malloc (sizeof (assignment *) * branches * branches);
_jstat = (nr_double_t *) malloc (sizeof (nr_double_t) * branches * branches);
_jdyna = (nr_double_t *) malloc (sizeof (nr_double_t) * branches * branches);
_charges = (nr_double_t *) malloc (sizeof (nr_double_t) * branches);
for (i = 0; i < branches; i++) {
vn = createVariable ("V", i + 1);
if ((veqn[i] = getEnv()->getChecker()->findEquation (vn)) == NULL) {
veqn[i] = getEnv()->getChecker()->addDouble ("#voltage", vn, 0);
A(veqn[i])->evalType ();
A(veqn[i])->skip = 1;
}
free (vn);
}
for (i = 0; i < branches; i++) {
in = createVariable ("I", i + 1);
ivalue = getEnv()->getChecker()->findEquation (in);
if (!ivalue) {
logprint (LOG_ERROR, "ERROR: current equation `%s' not found for "
"EDD `%s'\n", in, getName ());
}
qn = createVariable ("Q", i + 1);
qvalue = getEnv()->getChecker()->findEquation (qn);
if (!qvalue) {
logprint (LOG_ERROR, "ERROR: charge equation `%s' not found for "
"EDD `%s'\n", qn, getName ());
}
free (in);
free (qn);
for (j = 0; j < branches; j++) {
in = createVariable ("I", j + 1);
inold = createVariable ("I", j + 1, false);
vn = createVariable ("V", j + 1);
vnold = createVariable ("V", j + 1, false);
if (ivalue) {
ivalue->replace (vnold, vn);
ivalue->replace (inold, in);
}
if (qvalue) {
qvalue->replace (vnold, vn);
qvalue->replace (inold, in);
}
free (vnold); free (vn);
free (inold); free (in);
}
ieqn[i] = ivalue;
qeqn[i] = qvalue;
}
for (i = 0; i < branches; i++) {
if (ieqn[i]) { A(ieqn[i])->evalType (); A(ieqn[i])->skip = 1; }
if (qeqn[i]) { A(qeqn[i])->evalType (); A(qeqn[i])->skip = 1; }
}
for (k = 0, i = 0; i < branches; i++) {
ivalue = A(ieqn[i]);
for (j = 0; j < branches; j++, k++) {
vn = createVariable ("V", j + 1);
if (ivalue) {
gn = createVariable ("G", i + 1, j + 1);
if ((geqn[k] = getEnv()->getChecker()->findEquation (gn)) == NULL) {
diff = ivalue->differentiate (vn);
getEnv()->getChecker()->addEquation (diff);
diff->evalType ();
diff->skip = 1;
geqn[k] = diff;
A(diff)->rename (gn);
}
free (gn);
#if DEBUG
logprint (LOG_STATUS, "DEBUG: %s\n", A(geqn[k])->toString ());
#endif
}
else geqn[k] = NULL;
free (vn);
}
}
for (k = 0, i = 0; i < branches; i++) {
qvalue = A(qeqn[i]);
for (j = 0; j < branches; j++, k++) {
vn = createVariable ("V", j + 1);
if (qvalue) {
cn = createVariable ("C", i + 1, j + 1);
if ((ceqn[k] = getEnv()->getChecker()->findEquation (cn)) == NULL) {
diff = qvalue->differentiate (vn);
getEnv()->getChecker()->addEquation (diff);
diff->evalType ();
ceqn[k] = diff;
A(diff)->rename (cn);
for (int l = 0; l < branches; l++) {
in = createVariable ("I", l + 1);
diff = qvalue->differentiate (in);
A(diff)->mul (A(geqn[l * branches + j]));
A(ceqn[k])->add (A(diff));
delete diff;
free (in);
}
A(ceqn[k])->evalType ();
A(ceqn[k])->skip = 1;
}
free (cn);
#if DEBUG
logprint (LOG_STATUS, "DEBUG: %s\n", A(ceqn[k])->toString ());
#endif
}
else ceqn[k] = NULL;
free (vn);
}
}
}
void eqndefined::updateLocals (void) {
int i, branches = getSize () / 2;
for (i = 0; i < branches; i++) {
setResult (veqn[i], BP (i));
}
getEnv()->passConstants ();
getEnv()->equationSolver ();
}
void eqndefined::calcDC (void) {
int i, j, k, branches = getSize () / 2;
updateLocals ();
for (i = 0; i < branches; i++) {
nr_double_t c = getResult (ieqn[i]);
setI (i * 2 + 0, -c);
setI (i * 2 + 1, +c);
}
for (k = 0, i = 0; i < branches; i++) {
nr_double_t gv = 0;
for (j = 0; j < branches; j++, k++) {
nr_double_t g = getResult (geqn[k]);
setY (i * 2 + 0, j * 2 + 0, +g);
setY (i * 2 + 1, j * 2 + 1, +g);
setY (i * 2 + 0, j * 2 + 1, -g);
setY (i * 2 + 1, j * 2 + 0, -g);
gv += g * BP (j);
}
if (doHB) {
setGV (i * 2 + 0, +gv);
setGV (i * 2 + 1, -gv);
}
else {
addI (i * 2 + 0, +gv);
addI (i * 2 + 1, -gv);
}
}
}
void eqndefined::evalOperatingPoints (void) {
int i, j, k, branches = getSize () / 2;
for (k = 0, i = 0; i < branches; i++) {
nr_double_t q = getResult (qeqn[i]);
_charges[i] = q;
for (j = 0; j < branches; j++, k++) {
nr_double_t g = getResult (geqn[k]);
_jstat[k] = g;
nr_double_t c = getResult (ceqn[k]);
_jdyna[k] = c;
}
}
}
void eqndefined::saveOperatingPoints (void) {
updateLocals ();
evalOperatingPoints ();
}
void eqndefined::initAC (void) {
allocMatrixMNA ();
doHB = false;
}
void eqndefined::calcAC (nr_double_t frequency) {
setMatrixY (calcMatrixY (frequency));
}
matrix eqndefined::calcMatrixY (nr_double_t frequency) {
int i, j, k, branches = getSize () / 2;
matrix y (2 * branches);
nr_double_t o = 2 * pi * frequency;
for (k = 0, i = 0; i < branches; i++) {
for (j = 0; j < branches; j++, k++) {
int r = i * 2;
int c = j * 2;
nr_complex_t val = nr_complex_t (_jstat[k], o * _jdyna[k]);
y (r + 0, c + 0) = +val;
y (r + 1, c + 1) = +val;
y (r + 0, c + 1) = -val;
y (r + 1, c + 0) = -val;
}
}
return y;
}
void eqndefined::initTR (void) {
int branches = getSize () / 2;
setStates (2 * branches);
initDC ();
}
void eqndefined::calcTR (nr_double_t) {
int state, i, j, k, branches = getSize () / 2;
calcDC ();
evalOperatingPoints ();
for (i = 0; i < branches; i++) {
int r = i * 2;
state = 2 * i;
transientCapacitanceQ (state, r + 0, r + 1, _charges[i]);
}
for (k = 0, i = 0; i < branches; i++) {
for (j = 0; j < branches; j++, k++) {
int r = i * 2;
int c = j * 2;
nr_double_t v = BP (j);
transientCapacitanceC (r + 0, r + 1, c + 0, c + 1, _jdyna[k], v);
}
}
}
void eqndefined::initSP (void) {
allocMatrixS ();
doHB = false;
}
void eqndefined::calcSP (nr_double_t frequency) {
setMatrixS (ytos (calcMatrixY (frequency)));
}
void eqndefined::initHB (int) {
allocMatrixHB ();
if (ieqn == NULL) initModel ();
doHB = true;
}
void eqndefined::calcHB (int) {
int i, j, k, branches = getSize () / 2;
calcDC ();
evalOperatingPoints ();
for (i = 0; i < branches; i++) {
setQ (i * 2 + 0, -_charges[i]);
setQ (i * 2 + 1, +_charges[i]);
}
for (k = 0, i = 0; i < branches; i++) {
nr_double_t cv = 0;
for (j = 0; j < branches; j++, k++) {
int r = i * 2;
int c = j * 2;
nr_double_t val = _jdyna[k];
setQV (r + 0, c + 0, +val);
setQV (r + 1, c + 1, +val);
setQV (r + 0, c + 1, -val);
setQV (r + 1, c + 0, -val);
cv += val * BP (j);
}
setCV (i * 2 + 0, +cv);
setCV (i * 2 + 1, -cv);
}
}
PROP_REQ [] = {
{ "I1", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q1", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
PROP_NO_PROP };
PROP_OPT [] = {
{ "I2", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q2", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I3", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q3", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I4", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q4", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I5", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q5", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I6", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q6", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I7", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q7", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I8", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q8", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I9", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q9", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I10", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q10", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I11", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q11", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I12", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q12", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I13", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q13", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I14", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q14", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I15", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q15", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I16", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q16", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I17", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q17", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I18", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q18", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I19", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q19", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "I20", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
{ "Q20", PROP_REAL, { 0, PROP_NO_STR }, PROP_NO_RANGE },
PROP_NO_PROP };
struct define_t eqndefined::cirdef =
{ "EDD",
PROP_NODES, PROP_COMPONENT, PROP_NO_SUBSTRATE, PROP_NONLINEAR, PROP_DEF };