Transient op calculation added to the standard operating point code.

When standard iteration, gmin stepping and source stepping fail,
try a transient simulation (like tran with uic option) to
determine the op. Implemented for AC and Tran simulation.
This commit is contained in:
Holger Vogt 2019-10-12 16:24:14 +02:00
parent a08e6a0676
commit df0792818f
4 changed files with 685 additions and 0 deletions

View File

@ -405,6 +405,8 @@ extern int PZpost(CKTcircuit *);
extern int PZaskQuest(CKTcircuit *, JOB *, int , IFvalue *);
extern int PZsetParm(CKTcircuit *, JOB *, int , IFvalue *);
extern int OPtran(CKTcircuit *);
#ifdef WANT_SENSE2
extern int SENaskQuest(CKTcircuit *, JOB *, int , IFvalue *);
extern void SENdestroy(SENstruct *);

View File

@ -84,6 +84,7 @@ libckt_la_SOURCES = \
ninteg.c \
noisean.c \
nsetparm.c \
optran.c \
pzan.c \
pzaskq.c \
pzsetp.c \

View File

@ -85,8 +85,12 @@ CKTop (CKTcircuit *ckt, long int firstmode, long int continuemode,
converged = gillespie_src(ckt, firstmode, continuemode, iterlim);
else
converged = spice3_src(ckt, firstmode, continuemode, iterlim);
if (converged == 0) /* If gmin-stepping worked... move out */
return converged;
}
converged = OPtran(ckt);
#ifdef XSPICE
/* gtri - wbk - add convergence problem reporting flags */
ckt->enh->conv_debug.last_NIiter_call = MIF_FALSE;

View File

@ -0,0 +1,678 @@
/**********
Copyright 2019 Holger Vogt All rights reserved.
Author: 2019 Holger Vogt
Modified BSD license
**********/
/* subroutine to generate OP by TRANSIENT analysis */
#include "ngspice/ngspice.h"
#include "ngspice/cktdefs.h"
#include "cktaccept.h"
#include "ngspice/trandefs.h"
#include "ngspice/sperror.h"
#include "ngspice/fteext.h"
#include "ngspice/missing_math.h"
/* for setting breakpoints required by dbs data base */
extern struct dbcomm *dbs;
#include "ngspice/ftedebug.h"
#ifdef XSPICE
/* gtri - add - wbk - Add headers */
#include "ngspice/miftypes.h"
#include "ngspice/evt.h"
#include "ngspice/enh.h"
#include "ngspice/mif.h"
#include "ngspice/evtproto.h"
#include "ngspice/ipctiein.h"
/* gtri - end - wbk - Add headers */
#endif
#ifdef SHARED_MODULE
extern int add_bkpt(void);
extern int sharedsync(double*, double*, double, double, double, int, int*, int);
extern int ng_ident; /* for debugging */
#endif
static double *opbreaks;
static int OPbreakSize;
static double opfinaltime = 1e-6;
int OPclrBreak(CKTcircuit *ckt)
{
double *tmp;
int j;
NG_IGNORE(ckt);
if (OPbreakSize > 2) {
tmp = TMALLOC(double, OPbreakSize - 1);
if (tmp == NULL)
return (E_NOMEM);
for (j = 1; j < OPbreakSize; j++) {
tmp[j - 1] = opbreaks[j];
}
FREE(opbreaks);
OPbreakSize--;
opbreaks = tmp;
}
else {
opbreaks[0] = opbreaks[1];
opbreaks[1] = opfinaltime;
}
return (OK);
}
int OPsetBreak(CKTcircuit *ckt, double time)
{
double *tmp;
int i, j;
for (i = 0; i < OPbreakSize; i++) {
if (opbreaks[i] > time) { /* passed */
if ((opbreaks[i] - time) <= ckt->CKTminBreak) {
/* very close together - take earlier point */
#ifdef TRACE_BREAKPOINT
printf("[t:%e] \t %e replaces %e\n", ckt->CKTtime, time,
ckt->CKTbreaks[i]);
CKTbreakDump(ckt);
#endif
opbreaks[i] = time;
return (OK);
}
if (i > 0 && time - opbreaks[i - 1] <= ckt->CKTminBreak) {
/* very close together, but after, so skip */
#ifdef TRACE_BREAKPOINT
printf("[t:%e] \t %e skipped\n", ckt->CKTtime, time);
CKTbreakDump(ckt);
#endif
return (OK);
}
/* fits in middle - new array & insert */
tmp = TMALLOC(double, OPbreakSize + 1);
if (tmp == NULL)
return (E_NOMEM);
for (j = 0; j < i; j++) {
tmp[j] = opbreaks[j];
}
tmp[i] = time;
#ifdef TRACE_BREAKPOINT
printf("[t:%e] \t %e added\n", ckt->CKTtime, time);
CKTbreakDump(ckt);
#endif
for (j = i; j < OPbreakSize; j++) {
tmp[j + 1] = opbreaks[j];
}
FREE(opbreaks);
OPbreakSize++;
opbreaks = tmp;
return (OK);
}
}
/* never found it - beyond end of time - extend out idea of time */
if (time - opbreaks[OPbreakSize - 1] <= ckt->CKTminBreak) {
/* very close tegether - keep earlier, throw out new point */
#ifdef TRACE_BREAKPOINT
printf("[t:%e] \t %e skipped (at the end)\n", ckt->CKTtime, time);
CKTbreakDump(ckt);
#endif
return (OK);
}
/* fits at end - grow array & add on */
opbreaks = TREALLOC(double, opbreaks, OPbreakSize + 1);
OPbreakSize++;
opbreaks[OPbreakSize - 1] = time;
#ifdef TRACE_BREAKPOINT
printf("[t:%e] \t %e added at end\n", ckt->CKTtime, time);
CKTbreakDump(ckt);
#endif
return (OK);
}
/* Do a simple transient simulation, starting with time 0 until opfinaltime.
No output vectors are generated, actual times and breakpoints are kept local.
When returning, the matrix is left in its current state.
Thus this algorithm creates an operating point to start other simulations.
The code is derived from dctran.c by removing all un-needed parts.*/
int
OPtran(CKTcircuit *ckt)
{
int i;
double olddelta;
double delta;
double newdelta;
double *temp;
double optime = 0;
int converged;
int firsttime;
int error;
int save_order;
long save_mode;
double maxstepsize = 0.0;
int ltra_num;
#if defined SHARED_MODULE
int redostep;
#endif
ACAN *acjob = (ACAN *) ckt->CKTcurJob;
TRANan *trjob = (TRANan *) ckt->CKTcurJob;
NOISEAN *nojob = (NOISEAN *) ckt->CKTcurJob;
if(optime == 0) {
int type = ckt->CKTcurJob->JOBtype;
SPfrontEnd->IFerrorf(ERR_INFO, "Transient op started");
/* Set the final time */
/* If we are in transient simulation */
if (type == 4 && ckt->CKTstep > 0.)
opfinaltime = 100. * ckt->CKTstep;
/* We are in AC mode */
else if (type == 1 && acjob->ACstartFreq > 0) {
opfinaltime = 0.1 / acjob->ACstartFreq;
ckt->CKTstep = opfinaltime / 1000.;
}
/* Noise analysis */
else if (type == 8 && nojob->NstartFreq > 0) {
opfinaltime = 0.1 / nojob->NstartFreq;
ckt->CKTstep = opfinaltime / 1000.;
}
delta=MIN(opfinaltime/100,ckt->CKTstep)/10;
/* begin LTRA code addition */
if (ckt->CKTtimePoints != NULL)
FREE(ckt->CKTtimePoints);
if (ckt->CKTstep >= ckt->CKTmaxStep)
maxstepsize = ckt->CKTstep;
else
maxstepsize = ckt->CKTmaxStep;
ckt->CKTsizeIncr = 10;
ckt->CKTtimeIndex = -1; /* before the DC soln has been stored */
ckt->CKTtimeListSize = (int) ceil( opfinaltime / maxstepsize );
ltra_num = CKTtypelook("LTRA");
if (ltra_num >= 0 && ckt->CKThead[ltra_num] != NULL)
ckt->CKTtimePoints = TMALLOC(double, ckt->CKTtimeListSize);
/* end LTRA code addition */
opbreaks = TMALLOC(double, 2);
if(opbreaks == NULL) return(E_NOMEM);
opbreaks[0] = 0;
opbreaks[1] = opfinaltime;
OPbreakSize = 2;
#ifdef SHARED_MODULE
add_bkpt();
#endif
firsttime = 1;
save_mode = (ckt->CKTmode&MODEUIC) | MODETRANOP | MODEINITJCT;
save_order = ckt->CKTorder;
#ifdef XSPICE
/* gtri - begin - wbk - Add Breakpoint stuff */
/* Initialize the temporary breakpoint variables to infinity */
g_mif_info.breakpoint.current = 1.0e30;
g_mif_info.breakpoint.last = 1.0e30;
/* gtri - end - wbk - Add Breakpoint stuff */
#endif
ckt->CKTorder = 1;
for(i=0;i<7;i++) {
ckt->CKTdeltaOld[i]=ckt->CKTmaxStep;
}
ckt->CKTdelta = delta;
#ifdef STEPDEBUG
(void)printf("delta initialized to %g\n",ckt->CKTdelta);
#endif
ckt->CKTsaveDelta = opfinaltime/50;
ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITTRAN;
/* modeinittran set here */
ckt->CKTag[0]=ckt->CKTag[1]=0;
memcpy(ckt->CKTstate1, ckt->CKTstate0,
(size_t) ckt->CKTnumStates * sizeof(double));
} else {
/* saj As traninit resets CKTmode */
ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITPRED;
if(ckt->CKTminBreak==0) ckt->CKTminBreak=ckt->CKTmaxStep*5e-5;
firsttime=0;
goto resume;
}
/* 650 */
nextTime:
/* begin LTRA code addition */
if (ckt->CKTtimePoints) {
ckt->CKTtimeIndex++;
if (ckt->CKTtimeIndex >= ckt->CKTtimeListSize) {
/* need more space */
int need;
need = (int) ceil( (opfinaltime - optime) / maxstepsize );
if (need < ckt->CKTsizeIncr)
need = ckt->CKTsizeIncr;
ckt->CKTtimeListSize += need;
ckt->CKTtimePoints = TREALLOC(double, ckt->CKTtimePoints, ckt->CKTtimeListSize);
ckt->CKTsizeIncr = (int) ceil(1.4 * ckt->CKTsizeIncr);
}
ckt->CKTtimePoints[ckt->CKTtimeIndex] = optime;
}
/* end LTRA code addition */
error = CKTaccept(ckt);
/* check if current breakpoint is outdated; if so, clear */
if (optime > opbreaks[0])
OPclrBreak(ckt);
/*
* Breakpoint handling scheme:
* When a timepoint t is accepted (by CKTaccept), clear all previous
* breakpoints, because they will never be needed again.
*
* t may itself be a breakpoint, or indistinguishably close. DON'T
* clear t itself; recognise it as a breakpoint and act accordingly
*
* if t is not a breakpoint, limit the timestep so that the next
* breakpoint is not crossed
*/
ckt->CKTbreak = 0;
/* XXX Error will cause single process to bail. */
if(error) {
tfree(opbreaks);
return(error);
}
#ifdef XSPICE
/* gtri - begin - wbk - Update event queues/data for accepted timepoint */
/* Note: this must be done AFTER sending results to SI so it can't */
/* go next to CKTaccept() above */
if(ckt->evt->counts.num_insts > 0)
EVTaccept(ckt, optime);
/* gtri - end - wbk - Update event queues/data for accepted timepoint */
#endif
/* We are finished */
if(AlmostEqualUlps( optime, opfinaltime, 100 ) ) {
tfree(opbreaks);
SPfrontEnd->IFerrorf(ERR_INFO, "Transient op finished successfully");
return(OK);
}
resume:
#ifdef HAS_PROGREP
if (optime == 0.)
SetAnalyse( "optran init", 0);
else
SetAnalyse( "optran", (int)((optime * 1000.) / opfinaltime + 0.5));
#endif
ckt->CKTdelta =
MIN(ckt->CKTdelta,ckt->CKTmaxStep);
#ifdef XSPICE
/* gtri - begin - wbk - Cut integration order if first timepoint after breakpoint */
/* if(optime == g_mif_info.breakpoint.last) */
if ( AlmostEqualUlps( optime, g_mif_info.breakpoint.last, 100 ) )
ckt->CKTorder = 1;
/* gtri - end - wbk - Cut integration order if first timepoint after breakpoint */
#endif
/* are we at a breakpoint, or indistinguishably close? */
/* if ((optime == opbreaks[0]) || (opbreaks[0] - */
if ( AlmostEqualUlps( optime, opbreaks[0], 100 ) ||
opbreaks[0] - optime <= ckt->CKTdelmin) {
/* first timepoint after a breakpoint - cut integration order */
/* and limit timestep to .1 times minimum of time to next breakpoint,
* and previous timestep
*/
ckt->CKTorder = 1;
ckt->CKTdelta = MIN(ckt->CKTdelta, .1 * MIN(ckt->CKTsaveDelta,
opbreaks[1] - opbreaks[0]));
if(firsttime) {
/* set a breakpoint to reduce ringing of current in devices */
if (ckt->CKTmode & MODEUIC)
OPsetBreak(ckt, ckt->CKTstep);
ckt->CKTdelta /= 10;
#ifdef STEPDEBUG
(void)printf("delta cut for initial timepoint\n");
#endif
}
#ifndef XSPICE
/* don't want to get below delmin for no reason */
ckt->CKTdelta = MAX(ckt->CKTdelta, ckt->CKTdelmin*2.0);
#endif
}
#ifndef XSPICE
else if(optime + ckt->CKTdelta >= opbreaks[0]) {
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTdelta = opbreaks[0] - optime;
ckt->CKTbreak = 1; /* why? the current pt. is not a bkpt. */
}
#endif /* !XSPICE */
#ifdef XSPICE
/* gtri - begin - wbk - Add Breakpoint stuff */
if(optime + ckt->CKTdelta >= g_mif_info.breakpoint.current) {
/* If next time > temporary breakpoint, force it to the breakpoint */
/* And mark that timestep was set by temporary breakpoint */
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTdelta = g_mif_info.breakpoint.current - optime;
g_mif_info.breakpoint.last = optime + ckt->CKTdelta;
} else {
/* Else, mark that timestep was not set by temporary breakpoint */
g_mif_info.breakpoint.last = 1.0e30;
}
/* gtri - end - wbk - Add Breakpoint stuff */
/* gtri - begin - wbk - Modify Breakpoint stuff */
/* Throw out any permanent breakpoint times <= current time */
for (;;) {
#ifdef STEPDEBUG
printf(" brk_pt: %g ckt_time: %g ckt_min_break: %g\n",opbreaks[0], optime, ckt->CKTminBreak);
#endif
if(AlmostEqualUlps(opbreaks[0], optime, 100) ||
opbreaks[0] <= optime + ckt->CKTminBreak) {
#ifdef STEPDEBUG
printf("throwing out permanent breakpoint times <= current time (brk pt: %g)\n",opbreaks[0]);
printf(" ckt_time: %g ckt_min_break: %g\n",optime, ckt->CKTminBreak);
#endif
OPclrBreak(ckt);
} else {
break;
}
}
/* Force the breakpoint if appropriate */
if(optime + ckt->CKTdelta > opbreaks[0]) {
ckt->CKTbreak = 1;
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTdelta = opbreaks[0] - optime;
}
/* gtri - end - wbk - Modify Breakpoint stuff */
#ifdef SHARED_MODULE
/* Either directly go to next time step, or modify ckt->CKTdelta depending on
synchronization requirements. sharedsync() returns 0. */
sharedsync(&optime, &ckt->CKTdelta, 0, opfinaltime,
ckt->CKTdelmin, 0, &ckt->CKTstat->STATrejected, 0);
#endif
/* gtri - begin - wbk - Do event solution */
if(ckt->evt->counts.num_insts > 0) {
/* if time = 0 and op_alternate was specified as false during */
/* dcop analysis, call any changed instances to let them */
/* post their outputs with their associated delays */
if((optime == 0.0) && (! ckt->evt->options.op_alternate))
EVTiter(ckt);
/* while there are events on the queue with event time <= next */
/* projected analog time, process them */
while((g_mif_info.circuit.evt_step = EVTnext_time(ckt))
<= (optime + ckt->CKTdelta)) {
/* Initialize temp analog bkpt to infinity */
g_mif_info.breakpoint.current = 1e30;
/* Pull items off queue and process them */
EVTdequeue(ckt, g_mif_info.circuit.evt_step);
EVTiter(ckt);
/* If any instances have forced an earlier */
/* next analog time, cut the delta */
if(opbreaks[0] < g_mif_info.breakpoint.current)
if(opbreaks[0] > optime + ckt->CKTminBreak)
g_mif_info.breakpoint.current = opbreaks[0];
if(g_mif_info.breakpoint.current < optime + ckt->CKTdelta) {
/* Breakpoint must be > last accepted timepoint */
/* and >= current event time */
if(g_mif_info.breakpoint.current > optime + ckt->CKTminBreak &&
g_mif_info.breakpoint.current >= g_mif_info.circuit.evt_step) {
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTdelta = g_mif_info.breakpoint.current - optime;
g_mif_info.breakpoint.last = optime + ckt->CKTdelta;
}
}
} /* end while next event time <= next analog time */
} /* end if there are event instances */
/* gtri - end - wbk - Do event solution */
#else
#ifdef SHARED_MODULE
/* Either directly go to next time step, or modify ckt->CKTdelta depending on
synchronization requirements. sharedsync() returns 0.
*/
sharedsync(&optime, &ckt->CKTdelta, 0, opfinaltime,
ckt->CKTdelmin, 0, &ckt->CKTstat->STATrejected, 0);
#endif
#endif
for(i=5; i>=0; i--)
ckt->CKTdeltaOld[i+1] = ckt->CKTdeltaOld[i];
ckt->CKTdeltaOld[0] = ckt->CKTdelta;
temp = ckt->CKTstates[ckt->CKTmaxOrder+1];
for(i=ckt->CKTmaxOrder;i>=0;i--) {
ckt->CKTstates[i+1] = ckt->CKTstates[i];
}
ckt->CKTstates[0] = temp;
/* 600 */
for (;;) {
#if defined SHARED_MODULE
redostep = 1;
#endif
olddelta=ckt->CKTdelta;
/* time abort? */
optime += ckt->CKTdelta;
ckt->CKTdeltaOld[0]=ckt->CKTdelta;
NIcomCof(ckt);
save_mode = ckt->CKTmode;
save_order = ckt->CKTorder;
#ifdef XSPICE
/* gtri - begin - wbk - Add Breakpoint stuff */
/* Initialize temporary breakpoint to infinity */
g_mif_info.breakpoint.current = 1.0e30;
/* gtri - end - wbk - Add Breakpoint stuff */
/* gtri - begin - wbk - add convergence problem reporting flags */
/* delta is forced to equal delmin on last attempt near line 650 */
if(ckt->CKTdelta <= ckt->CKTdelmin)
ckt->enh->conv_debug.last_NIiter_call = MIF_TRUE;
else
ckt->enh->conv_debug.last_NIiter_call = MIF_FALSE;
/* gtri - begin - wbk - add convergence problem reporting flags */
/* gtri - begin - wbk - Call all hybrids */
/* gtri - begin - wbk - Set evt_step */
if(ckt->evt->counts.num_insts > 0) {
g_mif_info.circuit.evt_step = optime;
}
/* gtri - end - wbk - Set evt_step */
#endif
converged = NIiter(ckt,ckt->CKTtranMaxIter);
#ifdef XSPICE
if(ckt->evt->counts.num_insts > 0) {
g_mif_info.circuit.evt_step = optime;
EVTcall_hybrids(ckt);
}
/* gtri - end - wbk - Call all hybrids */
#endif
ckt->CKTmode = (ckt->CKTmode&MODEUIC)|MODETRAN | MODEINITPRED;
if(firsttime) {
memcpy(ckt->CKTstate2, ckt->CKTstate1,
(size_t) ckt->CKTnumStates * sizeof(double));
memcpy(ckt->CKTstate3, ckt->CKTstate1,
(size_t) ckt->CKTnumStates * sizeof(double));
}
/* txl, cpl addition */
if (converged == 1111) {
tfree(opbreaks);
return(converged);
}
if(converged != 0) {
#ifndef SHARED_MODULE
optime = optime -ckt->CKTdelta;
#else
redostep = 1;
#endif
ckt->CKTdelta = ckt->CKTdelta/8;
if(firsttime) {
ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITTRAN;
}
ckt->CKTorder = 1;
#ifdef XSPICE
/* gtri - begin - wbk - Add Breakpoint stuff */
/* Force backup if temporary breakpoint is < current time */
} else if(g_mif_info.breakpoint.current < optime) {
ckt->CKTsaveDelta = ckt->CKTdelta;
optime -= ckt->CKTdelta;
ckt->CKTdelta = g_mif_info.breakpoint.current - optime;
g_mif_info.breakpoint.last = optime + ckt->CKTdelta;
if(firsttime) {
ckt->CKTmode = (ckt->CKTmode&MODEUIC)|MODETRAN | MODEINITTRAN;
}
ckt->CKTorder = 1;
/* gtri - end - wbk - Add Breakpoint stuff */
#endif
} else {
if (firsttime) {
firsttime = 0;
#if !defined SHARED_MODULE
goto nextTime; /* no check on
* first time point
*/
#else
redostep = 0;
goto chkStep;
#endif
}
newdelta = ckt->CKTdelta;
error = CKTtrunc(ckt,&newdelta);
if(error) {
return(error);
tfree(opbreaks);
}
if (newdelta > .9 * ckt->CKTdelta) {
if ((ckt->CKTorder == 1) && (ckt->CKTmaxOrder > 1)) { /* don't rise the order for backward Euler */
newdelta = ckt->CKTdelta;
ckt->CKTorder = 2;
error = CKTtrunc(ckt, &newdelta);
if (error) {
tfree(opbreaks);
return(error);
}
if (newdelta <= 1.05 * ckt->CKTdelta) {
ckt->CKTorder = 1;
}
}
/* time point OK - 630 */
ckt->CKTdelta = newdelta;
#if !defined SHARED_MODULE
/* go to 650 - trapezoidal */
goto nextTime;
#else
redostep = 0;
goto chkStep;
#endif
} else {
#ifndef SHARED_MODULE
optime = optime -ckt->CKTdelta;
ckt->CKTstat->STATrejected ++;
#else
redostep = 1;
#endif
ckt->CKTdelta = newdelta;
}
}
if (ckt->CKTdelta <= ckt->CKTdelmin) {
if (olddelta > ckt->CKTdelmin) {
ckt->CKTdelta = ckt->CKTdelmin;
} else {
errMsg = CKTtrouble(ckt, "Timestep too small");
tfree(opbreaks);
return(E_TIMESTEP);
}
}
#ifdef XSPICE
/* gtri - begin - wbk - Do event backup */
if(ckt->evt->counts.num_insts > 0)
EVTbackup(ckt, optime + ckt->CKTdelta);
/* gtri - end - wbk - Do event backup */
#endif
#ifdef SHARED_MODULE
/* redostep == 0:
Either directly go to next time step, or modify ckt->CKTdelta depending on
synchronization requirements. sharedsync() returns 0.
redostep == 1:
No convergence, or too large truncation error.
Redo the last time step by subtracting olddelta, and modify ckt->CKTdelta
depending on synchronization requirements. sharedsync() returns 1.
User-supplied redo request:
sharedsync() may return 1 if the user has decided to do so in the callback
function.
*/
chkStep:
if(sharedsync(&optime, &ckt->CKTdelta, olddelta, opfinaltime,
ckt->CKTdelmin, redostep, &ckt->CKTstat->STATrejected, 1) == 0)
goto nextTime;
#endif
}
/* NOTREACHED */
}