pa-111
This commit is contained in:
parent
cea4b67ed4
commit
13210bbf42
|
|
@ -270,6 +270,11 @@ struct CKTcircuit {
|
||||||
unsigned int CKTkeepOpInfo:1; /* flag for small signal analyses */
|
unsigned int CKTkeepOpInfo:1; /* flag for small signal analyses */
|
||||||
unsigned int CKTcopyNodesets:1; /* NodesetFIX */
|
unsigned int CKTcopyNodesets:1; /* NodesetFIX */
|
||||||
unsigned int CKTnodeDamping:1; /* flag for node damping fix */
|
unsigned int CKTnodeDamping:1; /* flag for node damping fix */
|
||||||
|
unsigned int CKTlinesearch:1; /* Enhancement-111: adaptive damped-Newton line search */
|
||||||
|
double CKTlsMerit; /* E-111: this iteration's residual merit ||F|| = ||G*x-b|| */
|
||||||
|
double *CKTlsXk; /* E-111: line-search scratch (saved x_k) */
|
||||||
|
double *CKTlsD; /* E-111: line-search scratch (Newton step d) */
|
||||||
|
int CKTlsBufSz; /* E-111: allocated size of the LS scratch buffers */
|
||||||
unsigned int CKTcheckpoint:1; /* Enhancement-131: DCtran should continue from a restored
|
unsigned int CKTcheckpoint:1; /* Enhancement-131: DCtran should continue from a restored
|
||||||
checkpoint (keep loaded state, build a fresh output plot) */
|
checkpoint (keep loaded state, build a fresh output plot) */
|
||||||
double CKTabsDv; /* abs limit for iter-iter voltage change */
|
double CKTabsDv; /* abs limit for iter-iter voltage change */
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ enum {
|
||||||
OPT_LTEABSTOL,
|
OPT_LTEABSTOL,
|
||||||
OPT_LTETRTOL,
|
OPT_LTETRTOL,
|
||||||
OPT_NEWTRUNC,
|
OPT_NEWTRUNC,
|
||||||
|
OPT_LINESEARCH, /* Enhancement-111: adaptive damped-Newton line search */
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef XSPICE
|
#ifdef XSPICE
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ struct TSKtask {
|
||||||
unsigned int TSKkeepOpInfo:1; /* flag for small signal analyses */
|
unsigned int TSKkeepOpInfo:1; /* flag for small signal analyses */
|
||||||
unsigned int TSKcopyNodesets:1; /* flag for nodeset copy */
|
unsigned int TSKcopyNodesets:1; /* flag for nodeset copy */
|
||||||
unsigned int TSKnodeDamping:1; /* flag for node damping */
|
unsigned int TSKnodeDamping:1; /* flag for node damping */
|
||||||
|
unsigned int TSKlinesearch:1; /* Enhancement-111: adaptive damped-Newton line search */
|
||||||
unsigned int TSKnoopac:1; /* flag for no OP calculation before AC */
|
unsigned int TSKnoopac:1; /* flag for no OP calculation before AC */
|
||||||
double TSKabsDv; /* abs limit for iter-iter voltage change */
|
double TSKabsDv; /* abs limit for iter-iter voltage change */
|
||||||
double TSKrelDv; /* rel limit for iter-iter voltage change */
|
double TSKrelDv; /* rel limit for iter-iter voltage change */
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,29 @@ NIiter(CKTcircuit *ckt, int maxIter)
|
||||||
ckt->CKTniState |= NISHOULDREORDER;
|
ckt->CKTniState |= NISHOULDREORDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enhancement-111: residual merit ||F(x_k)|| = ||G*x_k - b|| for the
|
||||||
|
* globalized Newton line search. Computed here -- after the matrix
|
||||||
|
* is loaded and preordered but BEFORE it is LU-factored (spMultiply
|
||||||
|
* requires an unfactored matrix). G is the just-loaded Jacobian,
|
||||||
|
* x_k = CKTrhsOld, b = CKTrhs. F is the KCL residual (a current) --
|
||||||
|
* the merit function ngspice's iterate-based Newton otherwise lacks.
|
||||||
|
* CKTrhsSpare is scratch (free until the solve below). */
|
||||||
|
if (ckt->CKTlinesearch && ckt->CKTrhsSpare && (iterno > 1)) {
|
||||||
|
int sz = SMPmatSize(ckt->CKTmatrix);
|
||||||
|
int k;
|
||||||
|
double m = 0.0;
|
||||||
|
SMPmultiply(ckt->CKTmatrix, ckt->CKTrhsSpare, ckt->CKTrhsOld,
|
||||||
|
NULL, NULL);
|
||||||
|
for (k = 1; k <= sz; k++) {
|
||||||
|
double resid = ckt->CKTrhsSpare[k] - ckt->CKTrhs[k];
|
||||||
|
double w = fabs(resid) /
|
||||||
|
(ckt->CKTabstol + ckt->CKTreltol * fabs(ckt->CKTrhsSpare[k]));
|
||||||
|
if (w > m)
|
||||||
|
m = w;
|
||||||
|
}
|
||||||
|
ckt->CKTlsMerit = m;
|
||||||
|
}
|
||||||
|
|
||||||
if (ckt->CKTniState & NISHOULDREORDER) {
|
if (ckt->CKTniState & NISHOULDREORDER) {
|
||||||
startTime = SPfrontEnd->IFseconds();
|
startTime = SPfrontEnd->IFseconds();
|
||||||
|
|
||||||
|
|
@ -323,6 +346,83 @@ NIiter(CKTcircuit *ckt, int maxIter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enhancement-111: globalized (damped) Newton via Armijo backtracking
|
||||||
|
* line search (option `linesearch`, OFF by default). Runs only on the
|
||||||
|
* non-convergence path. Using the residual merit ||F|| = ||G*x - b||
|
||||||
|
* (the KCL current mismatch, computed above before factorization), it
|
||||||
|
* damps the full Newton step x_k -> x_full by the largest lambda in
|
||||||
|
* {1, 1/2, 1/4, ...} that gives a sufficient decrease of ||F|| (Armijo).
|
||||||
|
* Each trial RE-LOADS the devices at the trial point x_k + lambda*d and
|
||||||
|
* re-evaluates ||F|| on the (SMPclear-reset, unfactored) matrix. At/near
|
||||||
|
* a solution the full step already reduces ||F||, so lambda = 1 is
|
||||||
|
* accepted on the first trial (result-neutral); backtracking only kicks
|
||||||
|
* in on genuine overshoot. This gives ngspice the principled globalized
|
||||||
|
* Newton it lacks -- the merit is the real residual, not the iterate
|
||||||
|
* change. */
|
||||||
|
if (ckt->CKTlinesearch && (ckt->CKTnoncon != 0) &&
|
||||||
|
((ckt->CKTmode & MODETRANOP) || (ckt->CKTmode & MODEDCOP)) &&
|
||||||
|
(ckt->CKTmode & MODEINITFLOAT) && (iterno > 1))
|
||||||
|
{
|
||||||
|
int sz = SMPmatSize(ckt->CKTmatrix);
|
||||||
|
int k, saved_noncon = ckt->CKTnoncon;
|
||||||
|
double lambda = 1.0, merit_k = ckt->CKTlsMerit;
|
||||||
|
|
||||||
|
if (ckt->CKTlsBufSz < sz + 1) {
|
||||||
|
FREE(ckt->CKTlsXk);
|
||||||
|
FREE(ckt->CKTlsD);
|
||||||
|
ckt->CKTlsXk = TMALLOC(double, sz + 1);
|
||||||
|
ckt->CKTlsD = TMALLOC(double, sz + 1);
|
||||||
|
ckt->CKTlsBufSz = sz + 1;
|
||||||
|
}
|
||||||
|
/* save x_k and the full Newton step d = x_full - x_k */
|
||||||
|
for (k = 1; k <= sz; k++) {
|
||||||
|
ckt->CKTlsXk[k] = ckt->CKTrhsOld[k];
|
||||||
|
ckt->CKTlsD[k] = ckt->CKTrhs[k] - ckt->CKTrhsOld[k];
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
double trial_merit = 0.0;
|
||||||
|
for (k = 1; k <= sz; k++)
|
||||||
|
ckt->CKTrhsOld[k] = ckt->CKTlsXk[k] + lambda * ckt->CKTlsD[k];
|
||||||
|
/* Reset the device state (junction-voltage limiting reference)
|
||||||
|
* to x_k before every trial, so each trial load limits relative
|
||||||
|
* to the SAME point -- SPICE limiting is stateful and would
|
||||||
|
* otherwise drift across trials and corrupt the iteration. */
|
||||||
|
if (OldCKTstate0 && ckt->CKTstate0)
|
||||||
|
memcpy(ckt->CKTstate0, OldCKTstate0,
|
||||||
|
(size_t) ckt->CKTnumStates * sizeof(double));
|
||||||
|
if (CKTload(ckt)) /* trial load failed -> stop backtracking */
|
||||||
|
break;
|
||||||
|
SMPmultiply(ckt->CKTmatrix, ckt->CKTrhsSpare, ckt->CKTrhsOld,
|
||||||
|
NULL, NULL);
|
||||||
|
for (k = 1; k <= sz; k++) {
|
||||||
|
double resid = ckt->CKTrhsSpare[k] - ckt->CKTrhs[k];
|
||||||
|
double w = fabs(resid) /
|
||||||
|
(ckt->CKTabstol + ckt->CKTreltol * fabs(ckt->CKTrhsSpare[k]));
|
||||||
|
if (w > trial_merit)
|
||||||
|
trial_merit = w;
|
||||||
|
}
|
||||||
|
/* Armijo sufficient-decrease (c = 1e-4); floor lambda at 1/64 */
|
||||||
|
if (trial_merit <= (1.0 - 1.0e-4 * lambda) * merit_k ||
|
||||||
|
lambda <= 1.0 / 64.0)
|
||||||
|
break;
|
||||||
|
lambda *= 0.5;
|
||||||
|
}
|
||||||
|
/* Accept the (damped) step. Put x_trial into CKTrhs and restore
|
||||||
|
* CKTrhsOld = x_k so the SWAP below advances to x_trial. Roll the
|
||||||
|
* device state back to x_k so the trial loads leave NO state trace:
|
||||||
|
* the next iteration then loads at x_trial with the x_k limiting
|
||||||
|
* reference -- exactly the cadence a normal (un-line-searched)
|
||||||
|
* iteration would have. Only the chosen x_trial position persists. */
|
||||||
|
for (k = 1; k <= sz; k++) {
|
||||||
|
ckt->CKTrhs[k] = ckt->CKTrhsOld[k];
|
||||||
|
ckt->CKTrhsOld[k] = ckt->CKTlsXk[k];
|
||||||
|
}
|
||||||
|
if (OldCKTstate0 && ckt->CKTstate0)
|
||||||
|
memcpy(ckt->CKTstate0, OldCKTstate0,
|
||||||
|
(size_t) ckt->CKTnumStates * sizeof(double));
|
||||||
|
ckt->CKTnoncon = saved_noncon; /* trial loads dirtied it */
|
||||||
|
}
|
||||||
|
|
||||||
if (ckt->CKTmode & MODEINITFLOAT) {
|
if (ckt->CKTmode & MODEINITFLOAT) {
|
||||||
if ((ckt->CKTmode & MODEDC) && ckt->CKThadNodeset) {
|
if ((ckt->CKTmode & MODEDC) && ckt->CKThadNodeset) {
|
||||||
if (ipass)
|
if (ipass)
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,8 @@ CKTdestroy(CKTcircuit *ckt)
|
||||||
FREE(ckt->CKTrhs);
|
FREE(ckt->CKTrhs);
|
||||||
FREE(ckt->CKTrhsOld);
|
FREE(ckt->CKTrhsOld);
|
||||||
FREE(ckt->CKTrhsSpare);
|
FREE(ckt->CKTrhsSpare);
|
||||||
|
FREE(ckt->CKTlsXk); /* Enhancement-111 */
|
||||||
|
FREE(ckt->CKTlsD); /* Enhancement-111 */
|
||||||
FREE(ckt->CKTirhs);
|
FREE(ckt->CKTirhs);
|
||||||
FREE(ckt->CKTirhsOld);
|
FREE(ckt->CKTirhsOld);
|
||||||
FREE(ckt->CKTirhsSpare);
|
FREE(ckt->CKTirhsSpare);
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ CKTdoJob(CKTcircuit* ckt, int reset, TSKtask* task)
|
||||||
ckt->CKTkeepOpInfo = task->TSKkeepOpInfo;
|
ckt->CKTkeepOpInfo = task->TSKkeepOpInfo;
|
||||||
ckt->CKTcopyNodesets = task->TSKcopyNodesets;
|
ckt->CKTcopyNodesets = task->TSKcopyNodesets;
|
||||||
ckt->CKTnodeDamping = task->TSKnodeDamping;
|
ckt->CKTnodeDamping = task->TSKnodeDamping;
|
||||||
|
ckt->CKTlinesearch = task->TSKlinesearch; /* Enhancement-111 */
|
||||||
ckt->CKTabsDv = task->TSKabsDv;
|
ckt->CKTabsDv = task->TSKabsDv;
|
||||||
ckt->CKTrelDv = task->TSKrelDv;
|
ckt->CKTrelDv = task->TSKrelDv;
|
||||||
ckt->CKTtroubleNode = 0;
|
ckt->CKTtroubleNode = 0;
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ CKTnewTask(CKTcircuit *ckt, TSKtask **taskPtr, IFuid taskName, TSKtask **defPtr)
|
||||||
tsk->TSKkeepOpInfo = def->TSKkeepOpInfo;
|
tsk->TSKkeepOpInfo = def->TSKkeepOpInfo;
|
||||||
tsk->TSKcopyNodesets = def->TSKcopyNodesets;
|
tsk->TSKcopyNodesets = def->TSKcopyNodesets;
|
||||||
tsk->TSKnodeDamping = def->TSKnodeDamping;
|
tsk->TSKnodeDamping = def->TSKnodeDamping;
|
||||||
|
tsk->TSKlinesearch = def->TSKlinesearch; /* Enhancement-111 */
|
||||||
tsk->TSKabsDv = def->TSKabsDv;
|
tsk->TSKabsDv = def->TSKabsDv;
|
||||||
tsk->TSKrelDv = def->TSKrelDv;
|
tsk->TSKrelDv = def->TSKrelDv;
|
||||||
tsk->TSKnoopac = def->TSKnoopac;
|
tsk->TSKnoopac = def->TSKnoopac;
|
||||||
|
|
@ -135,6 +136,7 @@ CKTnewTask(CKTcircuit *ckt, TSKtask **taskPtr, IFuid taskName, TSKtask **defPtr)
|
||||||
tsk->TSKkeepOpInfo = 0;
|
tsk->TSKkeepOpInfo = 0;
|
||||||
tsk->TSKcopyNodesets = 0;
|
tsk->TSKcopyNodesets = 0;
|
||||||
tsk->TSKnodeDamping = 0;
|
tsk->TSKnodeDamping = 0;
|
||||||
|
tsk->TSKlinesearch = 0; /* Enhancement-111: off by default */
|
||||||
tsk->TSKabsDv = 0.5;
|
tsk->TSKabsDv = 0.5;
|
||||||
tsk->TSKrelDv = 2.0;
|
tsk->TSKrelDv = 2.0;
|
||||||
tsk->TSKepsmin = 1e-28;
|
tsk->TSKepsmin = 1e-28;
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,9 @@ CKTsetOpt(CKTcircuit *ckt, JOB *anal, int opt, IFvalue *val)
|
||||||
case OPT_NODEDAMPING:
|
case OPT_NODEDAMPING:
|
||||||
task->TSKnodeDamping = (val->iValue != 0);
|
task->TSKnodeDamping = (val->iValue != 0);
|
||||||
break;
|
break;
|
||||||
|
case OPT_LINESEARCH: /* Enhancement-111 */
|
||||||
|
task->TSKlinesearch = (val->iValue != 0);
|
||||||
|
break;
|
||||||
case OPT_ABSDV:
|
case OPT_ABSDV:
|
||||||
task->TSKabsDv = val->rValue;
|
task->TSKabsDv = val->rValue;
|
||||||
break;
|
break;
|
||||||
|
|
@ -359,6 +362,8 @@ static IFparm OPTtbl[] = {
|
||||||
"Copy nodesets from device terminals to internal nodes" },
|
"Copy nodesets from device terminals to internal nodes" },
|
||||||
{ "nodedamping", OPT_NODEDAMPING, IF_SET|IF_FLAG,
|
{ "nodedamping", OPT_NODEDAMPING, IF_SET|IF_FLAG,
|
||||||
"Limit iteration to iteration node voltage change" },
|
"Limit iteration to iteration node voltage change" },
|
||||||
|
{ "linesearch", OPT_LINESEARCH, IF_SET|IF_FLAG,
|
||||||
|
"Adaptive damped-Newton line search on the weighted step norm" },
|
||||||
{ "absdv", OPT_ABSDV, IF_SET|IF_REAL,
|
{ "absdv", OPT_ABSDV, IF_SET|IF_REAL,
|
||||||
"Maximum absolute iter-iter node voltage change" },
|
"Maximum absolute iter-iter node voltage change" },
|
||||||
{ "reldv", OPT_RELDV, IF_SET|IF_REAL,
|
{ "reldv", OPT_RELDV, IF_SET|IF_REAL,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue