/********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Thomas L. Quarles Modified: 2001 AlansFixes **********/ /* * NIiter(ckt,maxIter) * * This subroutine performs the actual numerical iteration. * It uses the sparse matrix stored in the circuit struct * along with the matrix loading program, the load data, the * convergence test function, and the convergence parameters */ #include "ngspice/ngspice.h" #include "ngspice/trandefs.h" #include "ngspice/cktdefs.h" #include "ngspice/smpdefs.h" #include "ngspice/sperror.h" #include "ngspice/fteext.h" /* Limit the number of 'singular matrix' warnings */ static int msgcount = 0; /* NIiter() - return value is non-zero for convergence failure */ int NIiter(CKTcircuit *ckt, int maxIter) { double startTime, *OldCKTstate0 = NULL; int error, i, j; int iterno = 0; int ipass = 0; /* some convergence issues that get resolved by increasing max iter */ if (maxIter < 100) maxIter = 100; if ((ckt->CKTmode & MODETRANOP) && (ckt->CKTmode & MODEUIC)) { SWAP(double *, ckt->CKTrhs, ckt->CKTrhsOld); error = CKTload(ckt); if (error) return(error); return(OK); } #ifdef WANT_SENSE2 if (ckt->CKTsenInfo) { error = NIsenReinit(ckt); if (error) return(error); } #endif if (ckt->CKTniState & NIUNINITIALIZED) { error = NIreinit(ckt); /* always returns 0 */ if (error) { #ifdef STEPDEBUG printf("re-init returned error \n"); #endif return(error); } } /* OldCKTstate0 = TMALLOC(double, ckt->CKTnumStates + 1); */ for (;;) { ckt->CKTnoncon = 0; #ifdef NEWPRED if (!(ckt->CKTmode & MODEINITPRED)) #endif { error = CKTload(ckt); /* printf("loaded, noncon is %d\n", ckt->CKTnoncon); */ /* fflush(stdout); */ iterno++; if (error) { ckt->CKTstat->STATnumIter += iterno; #ifdef STEPDEBUG printf("load returned error \n"); #endif FREE(OldCKTstate0); return (error); } /* printf("after loading, before solving\n"); */ /* CKTdump(ckt); */ if (!(ckt->CKTniState & NIDIDPREORDER)) { error = SMPpreOrder(ckt->CKTmatrix); if (error) { ckt->CKTstat->STATnumIter += iterno; #ifdef STEPDEBUG printf("pre-order returned error \n"); #endif FREE(OldCKTstate0); return(error); /* badly formed matrix */ } ckt->CKTniState |= NIDIDPREORDER; } if ((ckt->CKTmode & MODEINITJCT) || ((ckt->CKTmode & MODEINITTRAN) && (iterno == 1))) { ckt->CKTniState |= NISHOULDREORDER; } if (ckt->CKTniState & NISHOULDREORDER) { startTime = SPfrontEnd->IFseconds(); #ifdef KLU if (ckt->CKTkluMODE) { ckt->CKTmatrix->SMPkluMatrix->KLUloadDiagGmin = 1 ; } #endif error = SMPreorder(ckt->CKTmatrix, ckt->CKTpivotAbsTol, ckt->CKTpivotRelTol, ckt->CKTdiagGmin); ckt->CKTstat->STATreorderTime += SPfrontEnd->IFseconds() - startTime; if (error) { /* new feature - we can now find out something about what is * wrong - so we ask for the troublesome entry * Limit the number of messages to 6, if not 'set ngdebug'. */ if (ft_ngdebug || msgcount < 6) { SMPgetError(ckt->CKTmatrix, &i, &j); if(eq(NODENAME(ckt, i), NODENAME(ckt, j))) SPfrontEnd->IFerrorf(ERR_WARNING, "singular matrix: check node %s\n", NODENAME(ckt, i)); else SPfrontEnd->IFerrorf(ERR_WARNING, "singular matrix: check nodes %s and %s\n", NODENAME(ckt, i), NODENAME(ckt, j)); msgcount += 1; } ckt->CKTstat->STATnumIter += iterno; #ifdef STEPDEBUG printf("reorder returned error \n"); #endif FREE(OldCKTstate0); return(error); /* can't handle these errors - pass up! */ } ckt->CKTniState &= ~NISHOULDREORDER; } else { startTime = SPfrontEnd->IFseconds(); #ifdef KLU if (ckt->CKTkluMODE) { ckt->CKTmatrix->SMPkluMatrix->KLUloadDiagGmin = 1 ; } #endif error = SMPluFac(ckt->CKTmatrix, ckt->CKTpivotAbsTol, ckt->CKTdiagGmin); ckt->CKTstat->STATdecompTime += SPfrontEnd->IFseconds() - startTime; #ifdef KLU if ((ckt->CKTkluMODE) && (error == E_SINGULAR)) { /* Francesco Lannutti - 25 Aug 2020 * If the matrix is numerically singular during ReFactorization, take the same matrix and factor it from scratch in the same iteration. * This is my mod with KLU. It saves run-time, but also the system at the next iteration may be different. * How do we guarantee that the system is the same at the next iteration? So, the original SPARSE version below sounds like a bug. */ if (ft_ngdebug) fprintf (stderr, "Warning: KLU ReFactor failed. Factoring again...\n") ; ckt->CKTniState |= NISHOULDREORDER; ckt->CKTmatrix->SMPkluMatrix->KLUloadDiagGmin = 0 ; error = SMPreorder(ckt->CKTmatrix, ckt->CKTpivotAbsTol, ckt->CKTpivotRelTol, ckt->CKTdiagGmin); ckt->CKTstat->STATreorderTime += SPfrontEnd->IFseconds() - startTime; if (error) { SMPgetError(ckt->CKTmatrix, &i, &j); if (ft_ngdebug || msgcount < 6) { SMPgetError(ckt->CKTmatrix, &i, &j); if (eq(NODENAME(ckt, i), NODENAME(ckt, j))) SPfrontEnd->IFerrorf(ERR_WARNING, "singular matrix: check node %s\n", NODENAME(ckt, i)); else SPfrontEnd->IFerrorf(ERR_WARNING, "singular matrix: check nodes %s and %s\n", NODENAME(ckt, i), NODENAME(ckt, j)); msgcount += 1; } /* CKTload(ckt); */ /* SMPprint(ckt->CKTmatrix, stdout); */ /* seems to be singular - pass the bad news up */ ckt->CKTstat->STATnumIter += iterno; #ifdef STEPDEBUG printf("lufac returned error \n"); #endif FREE(OldCKTstate0); return(error); } } else if (error) { if (!(ckt->CKTkluMODE) && (error == E_SINGULAR)) { /* Francesco Lannutti - 25 Aug 2020 * If the matrix is numerically singular during ReFactorization, factor it from scratch at the next iteration. * This is the original SPICE3F5 code and uses SPARSE. */ ckt->CKTniState |= NISHOULDREORDER; DEBUGMSG(" forced reordering....\n"); continue; } /* CKTload(ckt); */ /* SMPprint(ckt->CKTmatrix, stdout); */ /* seems to be singular - pass the bad news up */ ckt->CKTstat->STATnumIter += iterno; #ifdef STEPDEBUG printf("lufac returned error \n"); #endif FREE(OldCKTstate0); return(error); } #else if (error) { if (error == E_SINGULAR) { /* Francesco Lannutti - 25 Aug 2020 * If the matrix is numerically singular during ReFactorization, factor it from scratch at the next iteration. * This is the original SPICE3F5 code and uses SPARSE. */ ckt->CKTniState |= NISHOULDREORDER; DEBUGMSG(" forced reordering....\n"); continue; } /* CKTload(ckt); */ /* SMPprint(ckt->CKTmatrix, stdout); */ /* seems to be singular - pass the bad news up */ ckt->CKTstat->STATnumIter += iterno; #ifdef STEPDEBUG printf("lufac returned error \n"); #endif FREE(OldCKTstate0); return(error); } #endif } /* moved it to here as if xspice is included then CKTload changes CKTnumStates the first time it is run */ if (!OldCKTstate0) OldCKTstate0 = TMALLOC(double, ckt->CKTnumStates + 1); if (ckt->CKTstate0) memcpy(OldCKTstate0, ckt->CKTstate0, (size_t) ckt->CKTnumStates * sizeof(double)); startTime = SPfrontEnd->IFseconds(); SMPsolve(ckt->CKTmatrix, ckt->CKTrhs, ckt->CKTrhsSpare); ckt->CKTstat->STATsolveTime += SPfrontEnd->IFseconds() - startTime; #ifdef STEPDEBUG /*XXXX*/ if (ckt->CKTrhs[0] != 0.0) printf("NIiter: CKTrhs[0] = %g\n", ckt->CKTrhs[0]); if (ckt->CKTrhsSpare[0] != 0.0) printf("NIiter: CKTrhsSpare[0] = %g\n", ckt->CKTrhsSpare[0]); if (ckt->CKTrhsOld[0] != 0.0) printf("NIiter: CKTrhsOld[0] = %g\n", ckt->CKTrhsOld[0]); /*XXXX*/ #endif ckt->CKTrhs[0] = 0; ckt->CKTrhsSpare[0] = 0; ckt->CKTrhsOld[0] = 0; if (iterno > maxIter) { ckt->CKTstat->STATnumIter += iterno; /* we don't use this info during transient analysis */ if (ckt->CKTcurrentAnalysis != DOING_TRAN) { FREE(errMsg); errMsg = copy("Too many iterations without convergence"); #ifdef STEPDEBUG fprintf(stderr, "too many iterations without convergence: %d iter's (max iter == %d)\n", iterno, maxIter); #endif FREE(errMsg); } FREE(OldCKTstate0); return(E_ITERLIM); } if ((ckt->CKTnoncon == 0) && (iterno != 1)) ckt->CKTnoncon = NIconvTest(ckt); else ckt->CKTnoncon = 1; #ifdef STEPDEBUG printf("noncon is %d\n", ckt->CKTnoncon); #endif } if ((ckt->CKTnodeDamping != 0) && (ckt->CKTnoncon != 0) && ((ckt->CKTmode & MODETRANOP) || (ckt->CKTmode & MODEDCOP)) && (iterno > 1)) { CKTnode *node; double diff, maxdiff = 0; for (node = ckt->CKTnodes->next; node; node = node->next) if (node->type == SP_VOLTAGE) { diff = fabs(ckt->CKTrhs[node->number] - ckt->CKTrhsOld[node->number]); if (maxdiff < diff) maxdiff = diff; } if (maxdiff > 10) { double damp_factor = 10 / maxdiff; if (damp_factor < 0.1) damp_factor = 0.1; for (node = ckt->CKTnodes->next; node; node = node->next) { diff = ckt->CKTrhs[node->number] - ckt->CKTrhsOld[node->number]; ckt->CKTrhs[node->number] = ckt->CKTrhsOld[node->number] + (damp_factor * diff); } for (i = 0; i < ckt->CKTnumStates; i++) { diff = ckt->CKTstate0[i] - OldCKTstate0[i]; ckt->CKTstate0[i] = OldCKTstate0[i] + (damp_factor * diff); } } } if (ckt->CKTmode & MODEINITFLOAT) { if ((ckt->CKTmode & MODEDC) && ckt->CKThadNodeset) { if (ipass) ckt->CKTnoncon = ipass; ipass = 0; } if (ckt->CKTnoncon == 0) { ckt->CKTstat->STATnumIter += iterno; FREE(OldCKTstate0); return(OK); } } else if (ckt->CKTmode & MODEINITJCT) { ckt->CKTmode = (ckt->CKTmode & ~INITF) | MODEINITFIX; ckt->CKTniState |= NISHOULDREORDER; } else if (ckt->CKTmode & MODEINITFIX) { if (ckt->CKTnoncon == 0) ckt->CKTmode = (ckt->CKTmode & ~INITF) | MODEINITFLOAT; ipass = 1; } else if (ckt->CKTmode & MODEINITSMSIG) { ckt->CKTmode = (ckt->CKTmode & ~INITF) | MODEINITFLOAT; } else if (ckt->CKTmode & MODEINITTRAN) { if (iterno <= 1) ckt->CKTniState |= NISHOULDREORDER; ckt->CKTmode = (ckt->CKTmode & ~INITF) | MODEINITFLOAT; } else if (ckt->CKTmode & MODEINITPRED) { ckt->CKTmode = (ckt->CKTmode & ~INITF) | MODEINITFLOAT; } else { ckt->CKTstat->STATnumIter += iterno; #ifdef STEPDEBUG printf("bad initf state \n"); #endif FREE(OldCKTstate0); return(E_INTERN); /* impossible - no such INITF flag! */ } /* build up the lvnim1 array from the lvn array */ SWAP(double *, ckt->CKTrhs, ckt->CKTrhsOld); /* printf("after loading, after solving\n"); */ /* CKTdump(ckt); */ } /*NOTREACHED*/ } void NIresetwarnmsg(void) { msgcount = 0; }