diff --git a/src/include/ngspice/cktdefs.h b/src/include/ngspice/cktdefs.h index 8cc4376f7..aa68ee129 100644 --- a/src/include/ngspice/cktdefs.h +++ b/src/include/ngspice/cktdefs.h @@ -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 *); diff --git a/src/spicelib/analysis/Makefile.am b/src/spicelib/analysis/Makefile.am index 86d6b7371..1ab48abbc 100644 --- a/src/spicelib/analysis/Makefile.am +++ b/src/spicelib/analysis/Makefile.am @@ -84,6 +84,7 @@ libckt_la_SOURCES = \ ninteg.c \ noisean.c \ nsetparm.c \ + optran.c \ pzan.c \ pzaskq.c \ pzsetp.c \ diff --git a/src/spicelib/analysis/cktop.c b/src/spicelib/analysis/cktop.c index 3c199b657..a9d2e8e08 100644 --- a/src/spicelib/analysis/cktop.c +++ b/src/spicelib/analysis/cktop.c @@ -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; diff --git a/src/spicelib/analysis/optran.c b/src/spicelib/analysis/optran.c new file mode 100644 index 000000000..9c67f216b --- /dev/null +++ b/src/spicelib/analysis/optran.c @@ -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 */ +}