* e_trsolver.cpp - external transient solver interface class implementation
*
* Copyright (C) 2004, 2005, 2006, 2007, 2009 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$
*
*/
* \brief The externally controlled transient solver implementation file.
*
*/
* \ingroup QucsInterface
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <cmath>
#include <float.h>
#include "compat.h"
#include "object.h"
#include "logging.h"
#include "complex.h"
#include "circuit.h"
#include "sweep.h"
#include "net.h"
#include "netdefs.h"
#include "analysis.h"
#include "nasolver.h"
#include "history.h"
#include "transient.h"
#include "exception.h"
#include "exceptionstack.h"
#include "environment.h"
#include "e_trsolver.h"
#include "component_id.h"
#include "ecvs.h"
#define STEPDEBUG 0
#define BREAKPOINTS 0
#ifndef dState
#define dState 0
#endif
#ifndef sState
#define sState 1
#endif
#ifndef SOL
#define SOL(state) (solution[(int) getState (sState, (state))])
#endif
namespace qucs {
using namespace transient;
e_trsolver::e_trsolver ()
: trsolver ()
{
type = ANALYSIS_E_TRANSIENT;
messagefcn = &logprint;
#if DEBUG
#endif
}
e_trsolver::e_trsolver (char * n)
: trsolver (n)
{
type = ANALYSIS_E_TRANSIENT;
messagefcn = &logprint;
}
e_trsolver::~e_trsolver ()
{
solve_post ();
if (progress) logprogressclear (40);
deinitTR ();
delete swp;
for (int i = 0; i < 8; i++)
{
if (solution[i] != NULL)
{
delete solution[i];
}
if (lastsolution[i] != NULL)
{
delete lastsolution[i];
}
}
delete tHistory;
}
based on the given e_trsolver object. */
e_trsolver::e_trsolver (e_trsolver & o)
: trsolver (o)
{
swp = o.swp ? new sweep (*o.swp) : NULL;
for (int i = 0; i < 8; i++) solution[i] = NULL;
tHistory = o.tHistory ? new history (*o.tHistory) : NULL;
relaxTSR = o.relaxTSR;
initialDC = o.initialDC;
}
void e_trsolver::debug()
{
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ())
{
messagefcn (0, c->getName() );
if (!c->getSubcircuit ().empty()) {
printf ("subcircuit Name %s\n", c->getSubcircuit ().c_str());
}
}
}
netlist solver. It prepares the circuit for simulation. */
int e_trsolver::init (nr_double_t start, nr_double_t firstdelta, int mode)
{
this->getEnv()->runSolver();
int error = 0;
const char * const solver = getPropertyString ("Solver");
relaxTSR = !strcmp (getPropertyString ("relaxTSR"), "yes") ? true : false;
initialDC = !strcmp (getPropertyString ("initialDC"), "yes") ? true : false;
MaxIterations = getPropertyInteger ("MaxIter");
reltol = getPropertyDouble ("reltol");
abstol = getPropertyDouble ("abstol");
vntol = getPropertyDouble ("vntol");
runs++;
saveCurrent = current = 0;
stepDelta = -1;
converged = 0;
fixpoint = 0;
lastsynctime = 0.0;
statRejected = statSteps = statIterations = statConvergence = 0;
if (!strcmp (solver, "CroutLU"))
eqnAlgo = ALGO_LU_DECOMPOSITION;
else if (!strcmp (solver, "DoolittleLU"))
eqnAlgo = ALGO_LU_DECOMPOSITION_DOOLITTLE;
else if (!strcmp (solver, "HouseholderQR"))
eqnAlgo = ALGO_QR_DECOMPOSITION;
else if (!strcmp (solver, "HouseholderLQ"))
eqnAlgo = ALGO_QR_DECOMPOSITION_LS;
else if (!strcmp (solver, "GolubSVD"))
eqnAlgo = ALGO_SV_DECOMPOSITION;
if (initialDC)
{
error = dcAnalysis ();
if (error)
return -1;
}
setDescription ("transient");
initETR (start, firstdelta, mode);
setCalculation ((calculate_func_t) &calcTR);
solve_pre ();
recallSolution ();
applyNodeset (false);
fillSolution (x);
fillLastSolution (x);
setMode (MODE_INIT);
running = 0;
rejected = 0;
if (mode == ETR_MODE_ASYNC)
{
delta /= 10;
}
else if (mode == ETR_MODE_SYNC)
{
}
else
{
qucs::exception * e = new qucs::exception (EXCEPTION_UNKNOWN_ETR_MODE);
e->setText ("Unknown ETR mode.");
throw_exception (e);
return -2;
}
fillState (dState, delta);
adjustOrder (1);
storeHistoryAges ();
return 0;
}
elements for later use (to make sure we don't set the histories
to be less than these initial requested values) */
void e_trsolver::storeHistoryAges (void)
{
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ())
{
if (c->hasHistory ())
{
initialhistages.push_back (c->getHistoryAge ());
}
}
}
void e_trsolver::fillLastSolution (tvector<nr_double_t> * s)
{
for (int i = 0; i < 8; i++) * lastsolution[(int) getState (sState, (i))] = *s;
}
function. */
void e_trsolver::initETR (nr_double_t start, nr_double_t firstdelta, int mode)
{
const char * const IMethod = getPropertyString ("IntegrationMethod");
corrMaxOrder = getPropertyInteger ("Order");
corrType = CMethod = correctorType (IMethod, corrMaxOrder);
predType = PMethod = predictorType (CMethod, corrMaxOrder, predMaxOrder);
corrOrder = corrMaxOrder;
predOrder = predMaxOrder;
if (mode == ETR_MODE_ASYNC){
delta = getPropertyDouble ("InitialStep");
deltaMin = getPropertyDouble ("MinStep");
deltaMax = getPropertyDouble ("MaxStep");
if (deltaMax == 0.0)
deltaMax = firstdelta;
if (deltaMin == 0.0)
deltaMin = NR_TINY * 10 * deltaMax;
if (delta == 0.0)
delta = firstdelta;
if (delta < deltaMin) delta = deltaMin;
if (delta > deltaMax) delta = deltaMax;
}
else if (mode == ETR_MODE_SYNC)
{
delta = firstdelta;
deltaMin = NR_TINY * 10;
deltaMax = std::numeric_limits<nr_double_t>::max() / 10;
}
setStates (2);
initStates ();
fillState (dState, delta);
saveState (dState, deltas);
setDelta ();
calcCorrectorCoeff (corrType, corrOrder, corrCoeff, deltas);
calcPredictorCoeff (predType, predOrder, predCoeff, deltas);
for (int i = 0; i < 8; i++)
{
solution[i] = new tvector<nr_double_t>;
setState (sState, (nr_double_t) i, i);
lastsolution[i] = new tvector<nr_double_t>;
}
lastasynctime = start;
saveState (dState, lastdeltas);
lastdelta = delta;
circuit *c, * root = subnet->getRoot ();
for (c = root; c != NULL; c = (circuit *) c->getNext ())
initCircuitTR (c);
for (c = root; c != NULL; c = (circuit *) c->getPrev ())
initCircuitTR (c);
}
void e_trsolver::printx()
{
char buf [1024];
for (int r = 0; r < x->size(); r++) {
buf[0] = '\0';
if (r == 2)
{
sprintf (buf, "%f\t%18.17f\t%6.5f %6.5f %6.5f %6.5f %6.5f %6.5f %6.5f %6.5f",
current,
(double) real (x->get (r)),
solution[0]->get(r) ,
solution[1]->get(r) ,
solution[2]->get(r) ,
solution[3]->get(r) ,
solution[4]->get(r) ,
solution[5]->get(r) ,
solution[6]->get(r) ,
solution[7]->get(r) );
messagefcn(0, buf);
}
}
}
*
* This function solves the circuit for a single time delta provided
* by an external source. Convergence issues etc. are expected to
* be handled by the external solver, as it is in full control of the
* time stepping.
*/
int e_trsolver::stepsolve_sync(nr_double_t synctime)
{
int error = 0;
convError = 0;
time = synctime;
updateExternalInterpTime(time);
delta = time - lastsynctime;
updateCoefficients (delta);
error += predictor ();
restartNR ();
try_running ()
{
error += corrector ();
}
catch_exception ()
{
case EXCEPTION_NO_CONVERGENCE:
pop_exception ();
this->convHelper = CONV_SteepestDescent;
convError = 2;
#if DEBUG
messagefcn (LOG_ERROR, "WARNING: delta rejected at t = %.3e, h = %.3e "
"(no convergence)\n", (double) saveCurrent, (double) delta);
#endif
try_running ()
{
error += solve_nonlinear ();
}
catch_exception ()
{
case EXCEPTION_NO_CONVERGENCE:
pop_exception ();
statRejected++;
statConvergence++;
rejected++;
converged = 0;
error = 0;
break;
default:
estack.print ();
error++;
break;
}
break;
default:
estack.print ();
error++;
break;
}
if (error) return -1;
if (!A->isFinite ())
{
return -1;
}
return 0;
}
void e_trsolver::acceptstep_sync()
{
statIterations += iterations;
if (--convError < 0) convHelper = 0;
if (running > 1)
{
adjustDelta_sync (current);
adjustOrder ();
}
else
{
fillStates ();
nextStates ();
rejected = 0;
}
saveCurrent = current;
current += delta;
running++;
converged++;
setMode (MODE_NONE);
if (running > 1)
{
updateHistory (current);
}
else
{
initHistory (current);
}
lastsynctime = current;
}
global truncation error. */
void e_trsolver::adjustDelta_sync (nr_double_t t)
{
deltaOld = delta;
if (delta > deltaMax)
{
delta = deltaMax;
}
if (delta < deltaMin)
{
delta = deltaMin;
}
int good = 0;
stepDelta = -1;
good = 1;
if (delta > 0.9 * deltaOld || good)
{
nextStates ();
rejected = 0;
#if STEPDEBUG
logprint (LOG_STATUS,
"DEBUG: delta accepted at t = %.3e, h = %.3e\n",
(double) current, (double) delta);
#endif
}
else if (deltaOld > delta)
{
rejected++;
statRejected++;
#if STEPDEBUG
logprint (LOG_STATUS,
"DEBUG: delta rejected at t = %.3e, h = %.3e\n",
(double) current, (double) delta);
#endif
if (current > 0) current -= deltaOld;
}
else
{
nextStates ();
rejected = 0;
}
}
int e_trsolver::stepsolve_async(nr_double_t steptime)
{
int error = 0;
convError = 0;
time = steptime;
updateExternalInterpTime(time);
updateHistoryAges (time - lastasynctime);
#if DEBUG && 0
messagefcn (LOG_STATUS, "NOTIFY: %s: solving netlist for t = %e\n",
getName (), (double) time);
#endif
do
{
#if STEPDEBUG
if (delta == deltaMin)
{
messagefcn (LOG_ERROR,
"WARNING: %s: minimum delta h = %.3e at t = %.3e\n",
getName (), (double) delta, (double) current);
}
#endif
updateCoefficients (delta);
error += predictor ();
if (rejected)
{
restartNR ();
rejected = 0;
}
try_running ()
{
error += corrector ();
}
catch_exception ()
{
case EXCEPTION_NO_CONVERGENCE:
pop_exception ();
if (current > 0) current -= delta;
delta /= 2;
if (delta <= deltaMin)
{
delta = deltaMin;
adjustOrder (1);
}
if (current > 0) current += delta;
statRejected++;
statConvergence++;
rejected++;
converged = 0;
error = 0;
convHelper = CONV_SteepestDescent;
convError = 2;
#if DEBUG
messagefcn (LOG_ERROR, "WARNING: delta rejected at t = %.3e, h = %.3e "
"(no convergence)\n", (double) saveCurrent, (double) delta);
#endif
break;
default:
estack.print ();
error++;
break;
}
if (error) return -1;
if (rejected) continue;
if (!A->isFinite ())
{
messagefcn (LOG_ERROR, "ERROR: %s: Jacobian singular at t = %.3e, "
"aborting %s analysis\n", getName (), (double) current,
getDescription ().c_str());
return -1;
}
statIterations += iterations;
if (--convError < 0) convHelper = 0;
if (running > 1)
{
adjustDelta (time);
adjustOrder ();
}
else
{
fillStates ();
nextStates ();
rejected = 0;
}
saveCurrent = current;
current += delta;
running++;
converged++;
setMode (MODE_NONE);
if (running > 1)
{
updateHistory (saveCurrent);
}
else
{
initHistory (saveCurrent);
}
}
while (saveCurrent < time);
return 0;
}
void e_trsolver::acceptstep_async(void)
{
copySolution (solution, lastsolution);
lastasynctime = time;
saveState (dState, lastdeltas);
lastdelta = delta;
}
void e_trsolver::rejectstep_async(void)
{
copySolution (lastsolution, solution);
truncateHistory (lastasynctime);
inputState (dState, lastdeltas);
for (int i = 0; i < 8; i++)
{
deltas[i] = lastdeltas[i];
}
delta = lastdelta;
setDelta ();
calcCorrectorCoeff (corrType, corrOrder, corrCoeff, deltas);
calcPredictorCoeff (predType, predOrder, predCoeff, deltas);
}
void e_trsolver::copySolution (tvector<nr_double_t> * src[8], tvector<nr_double_t> * dest[8])
{
for (int i = 0; i < 8; i++)
{
assert (src[i]->size () == dest[i]->size ());
for (int j = 0; j < src[i]->size (); j++)
{
dest[i]->set (j, src[i]->get (j));
}
}
}
void e_trsolver::updateHistoryAges (nr_double_t newage)
{
int i = 0;
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ())
{
if (c->hasHistory ())
{
c->setHistoryAge (std::max (initialhistages[i], newage));
i++;
}
}
}
void e_trsolver::getsolution (double * lastsol)
{
int N = countNodes ();
int M = countVoltageSources ();
for (int r = 0; r < N + M; r++)
{
lastsol[r] = real(x->get(r));
}
}
given name. returns -1 if the node name was not found */
int e_trsolver::getNodeV (char * label, nr_double_t& nodeV)
{
int r = nlist->getNodeNr (label);
if (r == -1)
{
return r;
}
else
{
nodeV = x->get(r);
return 0;
}
}
int e_trsolver::getVProbeV (char * probename, nr_double_t& probeV)
{
std::string fullname;
if (probename)
{
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ())
{
if (c->getType () == CIR_VPROBE) {
fullname.clear ();
if (!c->getSubcircuit ().empty())
{
fullname.append (c->getSubcircuit ());
fullname.append (".");
}
fullname.append (probename);
if (strcmp (fullname.c_str(), c->getName ()) == 0)
{
c->saveOperatingPoints ();
probeV = c->getOperatingPoint ("Vr");
return 0;
}
}
}
}
return -1;
}
int e_trsolver::getIProbeI (char * probename, nr_double_t& probeI)
{
std::string fullname;
if (probename)
{
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ())
{
if (c->getType () == CIR_IPROBE) {
fullname.clear ();
if (!c->getSubcircuit ().empty())
{
fullname.append (c->getSubcircuit ());
fullname.append (".");
}
fullname.append (probename);
if (strcmp (fullname.c_str(), c->getName ()) == 0)
{
probeI = real (x->get (c->getVoltageSource () + getN ()));
return 0;
}
}
}
}
return -1;
}
int e_trsolver::setECVSVoltage(char * ecvsname, nr_double_t V)
{
std::string fullname;
if (ecvsname)
{
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ())
{
if (c->getType () == CIR_ECVS) {
fullname.clear ();
if (!c->getSubcircuit ().empty())
{
fullname.append (c->getSubcircuit ());
fullname.append (".");
}
fullname.append (ecvsname);
if (strcmp (fullname.c_str(), c->getName ()) == 0)
{
c->setProperty("U", V);
return 0;
}
}
}
}
return -1;
}
void e_trsolver::updateExternalInterpTime(nr_double_t t)
{
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ())
{
if (c->getType () == CIR_ECVS) {
c->setProperty ("Tnext", t);
if (tHistory != NULL && tHistory->size () > 0)
{
c->setHistoryAge ( t - tHistory->last () + 0.1 * (t - tHistory->last ()) );
}
}
}
}
from all the circuit element histories */
void e_trsolver::truncateHistory (nr_double_t t)
{
circuit * root = subnet->getRoot ();
for (circuit * c = root; c != NULL; c = (circuit *) c->getNext ())
{
if (c->hasHistory ()) c->truncateHistory (t);
}
}
int e_trsolver::getJacRows()
{
return A->getRows();
}
int e_trsolver::getJacCols()
{
return A->getCols();
}
void e_trsolver::getJacData(int r, int c, nr_double_t& data)
{
data = A->get(r,c);
}
PROP_REQ [] =
{
PROP_NO_PROP
};
PROP_OPT [] =
{
{
"IntegrationMethod", PROP_STR, { PROP_NO_VAL, "Trapezoidal" },
PROP_RNG_STR4 ("Euler", "Trapezoidal", "Gear", "AdamsMoulton")
},
{ "Order", PROP_INT, { 2, PROP_NO_STR }, PROP_RNGII (1, 6) },
{ "InitialStep", PROP_REAL, { 1e-9, PROP_NO_STR }, PROP_POS_RANGE },
{ "MinStep", PROP_REAL, { 1e-16, PROP_NO_STR }, PROP_POS_RANGE },
{ "MaxStep", PROP_REAL, { 0, PROP_NO_STR }, PROP_POS_RANGE },
{ "MaxIter", PROP_INT, { 150, PROP_NO_STR }, PROP_RNGII (2, 10000) },
{ "abstol", PROP_REAL, { 1e-12, PROP_NO_STR }, PROP_RNG_X01I },
{ "vntol", PROP_REAL, { 1e-6, PROP_NO_STR }, PROP_RNG_X01I },
{ "reltol", PROP_REAL, { 1e-3, PROP_NO_STR }, PROP_RNG_X01I },
{ "LTEabstol", PROP_REAL, { 1e-6, PROP_NO_STR }, PROP_RNG_X01I },
{ "LTEreltol", PROP_REAL, { 1e-3, PROP_NO_STR }, PROP_RNG_X01I },
{ "LTEfactor", PROP_REAL, { 1, PROP_NO_STR }, PROP_RNGII (1, 16) },
{ "Temp", PROP_REAL, { 26.85, PROP_NO_STR }, PROP_MIN_VAL (K) },
{ "Solver", PROP_STR, { PROP_NO_VAL, "CroutLU" }, PROP_RNG_SOL },
{ "relaxTSR", PROP_STR, { PROP_NO_VAL, "no" }, PROP_RNG_YESNO },
{ "initialDC", PROP_STR, { PROP_NO_VAL, "yes" }, PROP_RNG_YESNO },
PROP_NO_PROP
};
struct define_t e_trsolver::anadef =
{ "ETR", 0, PROP_ACTION, PROP_NO_SUBSTRATE, PROP_LINEAR, PROP_DEF };
}