File: spSolve.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Barth, November 12, 1986 3:55:21 pm PST
Pradeep Sindhu March 19, 1986 11:45:02 pm PST
Sweetsun Chen, November 24, 1985 4:02:56 pm PST
DIRECTORY
BasicTime USING [Now],
IO USING [char, int, PutF, PutFR, PutRope, real, rope],
Labels USING [Set],
Real USING [Fix, FixC, FixI, Float, RoundLI, SmallestNormalizedNumber],
RealFns USING [SqRt],
RefText USING [Equal],
Rope USING [Equal, FromRefText, Length, ROPE],
spGlobals USING [Aborted, argList, argNodes, branchLinkPtr, branchPtr, Canned, CheckPoint, dumpAll, error, findNodeOrBranch, getSignedNumber, Handle, initPlot, InitPP, initPrint, makeStringNB, maxCountsPerStep, maxLog, modBrPtr, modFuncPtr, next, NextStage, nodePtr, plotFromList, printFromList, PutMsgLine, RefFuncBody, RefModBody, RefC, RefI, RefL, RefR, RefV, ResetCheckPoint, ShowDetails, UpdatePP];
spSolve: CEDAR PROGRAM
IMPORTS BasicTime, IO, Labels, Real, RealFns, RefText, spGlobals
EXPORTS spGlobals=
BEGIN OPEN spGlobals;
Retreat: PUBLIC SIGNAL[cause: Rope.ROPE]= CODE;
Failure: PUBLIC SIGNAL[errorNum: NAT]= CODE;
solveError: SIGNAL[s: Rope.ROPE]= CODE;
solveTest: SIGNAL[s: Rope.ROPE]= CODE;
updateFunctions: PROC[handle: Handle, f: modFuncPtr, firstTime: BOOLFALSE]= {
mb: modBrPtr;
source: argNodes;
args, res: argList;
allInactive: BOOL;
handle.vars.modelsSkipped← 0;
UNTIL f=NIL DO
source← f.arguments;
args← f.argVector;
allInactive← TRUE;
IF source # NIL THEN
FOR i: NAT IN [0..source.size) DO
args[i]← source[i].nHist.y;
allInactive← ((NOT firstTime) AND allInactive AND source[i].marked);
ENDLOOP;
WITH f.body SELECT FROM
fcn: RefFuncBody => {
fcn.branch.comVal← fcn.functionProc[handle.vars.t, args, f.parmVector];
};
mod: RefModBody =>
IF ~allInactive THEN {
since ~allInactive, from above we know that source is not Nil
oldArgs: argList← mod.oldArgVector;
FOR i: NAT IN [0..source.size) DO
oldArgs[i]← source[i].nHist.oldy;
ENDLOOP;
res← mod.modelResults;
mod.modelProc[args, oldArgs, f.parmVector, res];
FOR i: NAT IN [0..source.size) DO -- what the hell is this doing here?
source[i].nHist.y← args[i];
ENDLOOP;
mb← mod.modelBranches;
UNTIL mb=NIL DO
mb.b.comVal← res[mb.b.modelIndex];
mb← mb.nextBranch;
ENDLOOP;
}
ELSE handle.vars.modelsSkipped← handle.vars.modelsSkipped + 1;
ENDCASE;
f← f.nextMFPtr;
ENDLOOP;
}; -- updateFunctions
GSiteration: PROC[handle: Handle, dT: REAL, print, firstTime, mark: BOOLFALSE]
RETURNS[nIter: INT← 0]= {
E, newE, dvdt, I, newI, cond, csum, otherV, maxI, denom, tempDvDt: REAL;
n: nodePtr;
b: branchPtr;
vCur: branchPtr;
vCurBody: RefV;
bLink: branchLinkPtr;
plus, nodeOK, OK: BOOL;
realMaxIter: INT = IF handle.vars.maxIter >= LAST[INT]/2 THEN LAST[INT]
ELSE handle.vars.maxIter + handle.vars.maxIter;
updateFunctions[handle, handle.vars.intNodeModFunc, firstTime];
IF print THEN printNodeEqn[handle];
UNTIL nIter > realMaxIter DO
OKTRUE;
IF print THEN handle.msgStream.PutF["t= %10.4f, iteration %2d --\n",
IO.real[handle.vars.t], IO.int[nIter]];
IF handle.vars.otherModFunc # NIL THEN
updateFunctions[handle, handle.vars.otherModFunc];
IF firstTime THEN updateFunctions[handle, handle.vars.intNodeModFunc];
FOR n← handle.vars.nodeList, n.nextNode UNTIL n=NIL DO
IF n=handle.vars.gndNode THEN LOOP;
IF nIter > 1 AND n.converged THEN LOOP;
IF print THEN handle.msgStream.PutF["Node %g: ",
IO.rope[n.nodeName.name]];
bLink← n.branches;
IF n.integrate THEN {
maxI← csum← I← 0.0;
UNTIL bLink=NIL DO
b ← bLink.branch;
plus ← bLink.pos;
WITH b.body SELECT FROM
r: RefR => newI← (bLink.otherNode.nHist.y - n.nHist.y)/b.comVal;
c: RefC => {
csum← csum + b.comVal;
newI← b.comVal*bLink.otherNode.nHist.f0;
};
l: RefL => newI← (IF plus THEN -l.iHist.y ELSE l.iHist.y)/b.comVal;
v: RefV => newI← IF plus THEN -v.vsCurrent ELSE v.vsCurrent;
i: RefI => newI← IF plus THEN -b.comVal ELSE b.comVal
ENDCASE;
maxI← MAX[maxI, ABS[newI]];
II + newI;
bLink← bLink.nextLink;
ENDLOOP;
E← n.nHist.f0;
tempDvDt← I/csum;
dvdt← n.nHist.f0← IF ABS[tempDvDt] < handle.vars.ignorDvDtBelow THEN 0.0 ELSE tempDvDt;
IF mark THEN n.marked← (ABS[I] <= handle.vars.dvdtTrunc*maxI);
denom← MAX[ABS[E], ABS[dvdt]];
nodeOK← IF denom <= Real.SmallestNormalizedNumber THEN TRUE
ELSE (handle.vars.tol >= ABS[(dvdt - E)/denom]);
IF print THEN handle.msgStream.PutF["Old v'= %9.2f New v'= %9.2f C= %9.2f.\n",
IO.real[E], IO.real[dvdt], IO.real[csum]];
}
ELSE {
E← n.nHist.y;
IF n.curPtr # NIL THEN {
vCur← n.curPtr;
vCurBody← NARROW[vCur.body];
newE← IF vCur.posNode=n THEN vCur.negNode.nHist.y + vCur.comVal
ELSE vCur.posNode.nHist.y - vCur.comVal;
I← vCurBody.vsCurrent;
newI← 0.0;
UNTIL bLink=NIL DO
b ← bLink.branch;
otherV← bLink.otherNode.nHist.y;
plus ← bLink.pos;
WITH b.body SELECT FROM
r: RefR => newI← newI + (otherV - newE)/b.comVal;
c: RefC => newI← newI + b.comVal*(bLink.otherNode.nHist.f0 - n.nHist.f0);
l: RefL => newI← newI + (IF plus THEN -l.iHist.y ELSE l.iHist.y)/b.comVal;
v: RefV => newI← IF plus THEN newI - v.vsCurrent ELSE newI + v.vsCurrent;
i: RefI => newI← IF plus THEN newI - b.comVal ELSE newI + b.comVal
ENDCASE;
bLink← bLink.nextLink
ENDLOOP;
newI← IF n=vCur.posNode THEN I + newI ELSE I - newI;
denom← MAX[ABS[I], ABS[newI]];
nodeOK← IF denom <= Real.SmallestNormalizedNumber THEN TRUE
ELSE (handle.vars.tol >= ABS[(newI - I)/denom]);
n.nHist.f0← IF firstTime THEN 0.0 ELSE
handle.vars.squish*n.nHist.f1 +
(1.0-handle.vars.squish)*(newE-n.nHist.oldy)/dT +
(IF vCur.posNode=n THEN vCur.negNode.nHist.f0
ELSE vCur.posNode.nHist.f0);
n.nHist.f0← 0.0;
IF NOT firstTime THEN {
tempDvDt ← handle.vars.squish*n.nHist.f1 +
(1.0-handle.vars.squish)*(newE-n.nHist.oldy)/dT +
(IF vCur.posNode=n THEN vCur.negNode.nHist.f0 ELSE vCur.posNode.nHist.f0);
IF ABS[tempDvDt] > handle.vars.ignorDvDtBelow THEN n.nHist.f0← tempDvDt;
};
vCurBody.vsCurrent← newI;
IF print THEN handle.msgStream.PutF["Old I= %9.2f New I= %9.2f",
IO.real[I], IO.real[newI]];
}
ELSE {
cond← newE← 0.0;
UNTIL bLink=NIL DO
b ← bLink.branch;
plus ← bLink.pos;
WITH b.body SELECT FROM
r: RefR => {
cond← cond + 1.0/b.comVal;
newE← newE + bLink.otherNode.nHist.y/b.comVal;
};
l: RefL => newE← newE + (IF plus THEN -l.iHist.y ELSE l.iHist.y)/b.comVal;
v: RefV => newE← IF plus THEN newE - v.vsCurrent
ELSE newE + v.vsCurrent;
i: RefI => newE← IF plus THEN newE - b.comVal
ELSE newE + b.comVal
ENDCASE;
bLink← bLink.nextLink
ENDLOOP;
newE← newE/cond;
denom← MAX[ABS[E], ABS[newE]];
nodeOK← IF denom <= Real.SmallestNormalizedNumber THEN TRUE
ELSE (handle.vars.tol >= ABS[(newE - E)/denom]);
};
n.nHist.y← newE;
IF print THEN handle.msgStream.PutF["Old E= %9.2f New E= %9.2f.\n",
IO.real[E], IO.real[newE]];
};
OKOK AND nodeOK;
n.converged← nodeOK;
IF ~nodeOK THEN
IF nIter = realMaxIter THEN
SIGNAL solveError[IO.PutFR["Node %g GS did not converge.",
IO.rope[spGlobals.makeStringNB[handle, n, NIL]]]];
FOR bLink← n.branches, bLink.nextLink UNTIL bLink=NIL DO
bLink.otherNode.converged← FALSE;
ENDLOOP;
ENDLOOP; -- n
nIter← nIter + 1;
IF handle.vars.canned AND NOT handle.vars.forcedDump THEN SIGNAL Aborted;
IF nIter=handle.vars.maxIter THEN
handle.msgStream.PutF["t= %9.3f, iteration count exceeded",
IO.real[handle.vars.t]];
IF OK THEN EXIT;
ENDLOOP; -- nIter
}; -- GSiteration
printBranches: PROC [handle: Handle, n: nodePtr] = {
FOR bLink: branchLinkPtr ← n.branches, bLink.nextLink UNTIL bLink=NIL DO
branch: branchPtr ← bLink.branch;
WITH branch.body SELECT FROM
r: RefR => handle.msgStream.PutRope["resistor"];
c: RefC => handle.msgStream.PutRope["capacitor"];
l: RefL => handle.msgStream.PutRope["inductor"];
v: RefV => handle.msgStream.PutRope["voltage source"];
i: RefI => handle.msgStream.PutRope["current source"];
ENDCASE;
handle.msgStream.PutF[", %g, %g\n",
IO.real[branch.comVal],
IO.rope[bLink.otherNode.nodeName.name]];
ENDLOOP;
};
printNodeEqn: PROC[handle: Handle]= {
n: nodePtr;
bLink: branchLinkPtr;
branch: branchPtr;
plus: BOOL;
FOR n← handle.vars.intNodeList, n.nextIntNode UNTIL n=NIL DO
handle.msgStream.PutF["\n%g -- %13.6f %13.6f\n",
IO.rope[n.nodeName.name],
IO.real[n.nHist.y],
IO.real[n.nHist.oldy]];
FOR bLink← n.branches, bLink.nextLink UNTIL bLink=NIL DO
branch← bLink.branch;
plus ← bLink.pos;
WITH branch.body SELECT FROM
r: RefR => handle.msgStream.PutF["(%13.6f - %13.6f)/%13.6f",
IO.real[bLink.otherNode.nHist.y],
IO.real[n.nHist.y],
IO.real[branch.comVal] ];
c: RefC => handle.msgStream.PutF["%13.6fx%13.6f",
IO.real[branch.comVal],
IO.real[bLink.otherNode.nHist.f0] ];
l: RefL =>
IF plus THEN handle.msgStream.PutF["-1x%13.6f/%13.6f",
IO.real[l.iHist.y],
IO.real[branch.comVal]]
ELSE handle.msgStream.PutF["%13.6f/%13.6f",
IO.real[l.iHist.y],
IO.real[branch.comVal]];
v: RefV =>
IF plus THEN handle.msgStream.PutF["-1x%13.6f",
IO.real[v.vsCurrent]]
ELSE handle.msgStream.PutF["%13.6f", IO.real[v.vsCurrent]];
i: RefI =>
IF plus THEN handle.msgStream.PutF["-1x%13.6f",
IO.real[branch.comVal]]
ELSE handle.msgStream.PutF["%13.6f",IO.real[branch.comVal]];
ENDCASE;
handle.msgStream.PutF[" %s(%13.6f, %13.6f)\n",
IO.rope[bLink.otherNode.nodeName.name],
IO.real[bLink.otherNode.nHist.y],
IO.real[bLink.otherNode.nHist.f0]];
ENDLOOP;
ENDLOOP;
}; -- printNodeEqn
runParms: PROC[handle: Handle]= {
OPEN handle.vars;
parmValue: PROC[booleanOrNot: {boolean, notBoolean}]
RETURNS[r: REAL, b: BOOL, n: NAT]= {
IF booleanOrNot=boolean THEN
IF item=name THEN {
IF RefText.Equal[newString, "FALSE", FALSE] THEN b← FALSE
ELSE
IF RefText.Equal[newString, "TRUE", FALSE] THEN b← TRUE
ELSE error[handle, 719];
next[handle]
}
ELSE error[handle, 704]
ELSE {
r← getSignedNumber[handle];
IF r >= 0.0 AND r < Real.Float[LAST[NAT]] THEN n← Real.FixC[r]; -- else?
};
}; -- parmValue
nParms: NAT= 36;
parmNames: ARRAY[0..nParms) OF REF TEXT= ["maxiter", "printiter", "initstep", "squish", "tol", "dt", "tmax", "printstep", "rungekutta", "tmin", "printall", "amiter", "amup", "inttol", "amdown", "vmin", "vmax", "dvdtmax", "dvdtfac", "tbreak", "rkerr", "amerr", "amdelay", "floor", "minratio", "rkfac", "retratio", "ignorDvDtBelow", "showRetreat", "SaveAllData", "tUnit", "iUnit", "vUnit", "ymin", "ymax", ""];
nameIndex: NAT;
tempR: REAL;
WHILE item=name DO
FOR i: NAT IN [0..nParms) DO
nameIndex← i;
IF RefText.Equal[newString, parmNames[nameIndex], FALSE] THEN EXIT;
ENDLOOP;
next[handle];
IF item # leftArrow THEN error[handle, 703] ELSE next[handle];
SELECT nameIndex FROM
0=> {
limit: INTLAST[INT];
[tempR, , ]← parmValue[notBoolean];
IF tempR >= Real.Float[limit] THEN maxIter← limit
ELSE IF tempR > 0 THEN maxIter← Real.Fix[tempR];
};
1=> [ , printIter, ]← parmValue[boolean];
2=> [initStep, , ]← parmValue[notBoolean];
3=> [squish, , ]← parmValue[notBoolean];
4=> [tol, , ]← parmValue[notBoolean];
5=> [printdT, , ]← parmValue[notBoolean];
6=> [tMax, , ]← parmValue[notBoolean];
7=> [ , printStep, ]← parmValue[boolean];
8=> [ , doRungeKutta, ]← parmValue[boolean];
9=> [tMin, , ]← parmValue[notBoolean];
10=> [ , printAll, ]← parmValue[boolean];
11=> [ , , AMiter]← parmValue[notBoolean];
12=> [AMup, , ]← parmValue[notBoolean];
13=> [intTol, , ]← parmValue[notBoolean];
14=> [AMdown, , ]← parmValue[notBoolean];
15=> [Vmin, , ]← parmValue[notBoolean];
16=> [Vmax, , ]← parmValue[notBoolean];
17=> [dvdtmax, , ]← parmValue[notBoolean];
18=> [dvdtFac, , ]← parmValue[notBoolean];
19=> [tBreak, , ]← parmValue[notBoolean];
20=> [RKerr, , ]← parmValue[notBoolean];
21=> [AMerr, , ]← parmValue[notBoolean];
22=> [ , , AMdelay]← parmValue[notBoolean];
23=> [floor, , ]← parmValue[notBoolean];
24=> [minRatio, , ]← parmValue[notBoolean];
25=> [RKfac, , ]← parmValue[notBoolean];
26=> [retRatio, , ]← parmValue[notBoolean];
27=> [ignorDvDtBelow, , ]← parmValue[notBoolean];
28=> [ , showRetreat, ]← parmValue[boolean];
29=> [ , saveAllData, ]← parmValue[boolean];
30=> [tUnit, , ]← parmValue[notBoolean];
31=> [iUnit, , ]← parmValue[notBoolean];
32=> [vUnit, , ]← parmValue[notBoolean];
33=> [yMin, , ]← parmValue[notBoolean];
34 => [yMax, , ]← parmValue[notBoolean];
ENDCASE=> error[handle, 790];
IF item=comma THEN next[handle];
ENDLOOP;
}; -- runParms
predict: PROC[handle: Handle, h: REAL]= {
nodes: nodePtr← handle.vars.intNodeList;
inds: branchPtr← handle.vars.inductorList;
incr: REAL;
h← h/24.0;
UNTIL nodes=NIL DO
IF ~nodes.marked THEN {
OPEN nodes.nHist;
incr← h*(55.0*f1 - 59.0*f2 + 37.0*f3 - 9.0*f4);
y← oldy + incr;
};
nodes← nodes.nextIntNode;
ENDLOOP;
UNTIL inds=NIL DO
indsBody: RefL← NARROW[inds.body];
{OPEN indsBody.iHist;
y← oldy + h*(55.0*f1 - 59.0*f2 + 37.0*f3 - 9.0*f4)};
inds← indsBody.nextInductor;
ENDLOOP;
}; -- predict
firstCorrector: PROC[handle: Handle, h: REAL]= {
nodes: nodePtr← handle.vars.intNodeList;
inds: branchPtr← handle.vars.inductorList;
f: REAL;
h← h/24.0;
UNTIL nodes=NIL DO
IF ~nodes.marked THEN {
OPEN nodes.nHist;
y← oldy + h*(9.0*f0 + 19.0*f1 - 5.0*f2 + f3);
oldf← f0;
};
nodes← nodes.nextIntNode;
ENDLOOP;
UNTIL inds=NIL DO
indsBody: RefL← NARROW[inds.body];
{OPEN indsBody.iHist;
f← inds.posNode.nHist.y - inds.negNode.nHist.y;
y← oldy + h*(9.0*f + 19.0*f1 - 5.0*f2 + f3);
oldf← f};
inds← indsBody.nextInductor;
ENDLOOP;
}; -- firstCorrector
iteratingCorrector: PROC[handle: Handle, h: REAL] RETURNS[newRatio: REAL← 4.0,
OK: BOOLTRUE]= {
nodes: nodePtr← handle.vars.intNodeList;
inds: branchPtr← handle.vars.inductorList;
f, deltay, newy, err, denom: REAL;
h← 3.0*h/8.0;
UNTIL nodes=NIL DO
IF ~nodes.marked THEN {
OPEN nodes.nHist;
deltay← h*(f0 - oldf);
newy← y + deltay;
denom← MAX[ABS[newy], ABS[y]];
IF denom > handle.vars.floor AND deltay > 1.0E-30 THEN {
err← handle.vars.AMerr*ABS[deltay/denom];
IF handle.vars.intTol < err THEN {
OKFALSE;
newRatio← MIN[newRatio, handle.vars.intTol/err];
handle.vars.worstNode← nodes;
};
};
y← newy;
oldf← f0;
};
nodes← nodes.nextIntNode;
ENDLOOP;
UNTIL inds=NIL DO
indsBody: RefL← NARROW[inds.body];
{ OPEN indsBody.iHist;
f← inds.posNode.nHist.y - inds.negNode.nHist.y;
deltay← h*(f - oldf);
newy← y + deltay;
denom← MAX[ABS[newy], ABS[y]];
IF denom > handle.vars.floor AND deltay > 1.0E-30 THEN {
err← handle.vars.AMerr*ABS[deltay/denom];
IF handle.vars.intTol < err THEN {
OKFALSE;
newRatio← MIN[newRatio, handle.vars.intTol/err];
};
};
y← newy;
oldf← f;
};
inds← indsBody.nextInductor;
ENDLOOP;
}; -- iteratingCorrector
AdamsMoulton: PROC[handle: Handle, dT: REAL]
RETURNS[newRatio: REAL, nGS: INT, ok: BOOL]= {
nAM: NAT;
predict[handle, dT];
nGS← GSiteration[handle, dT, handle.vars.printAll];
firstCorrector[handle, dT];
nGS← GSiteration[handle, dT, handle.vars.printAll] + nGS;
FOR nAM← 2, nAM + 1 UNTIL nAM=handle.vars.AMiter DO
[]← iteratingCorrector[handle, dT];
nGS← GSiteration[handle, dT, handle.vars.printAll] + nGS
ENDLOOP;
[newRatio, ok]← iteratingCorrector[handle, dT];
}; -- AdamsMoulton
RKupdate: PROC[handle: Handle, c, h: REAL, last: BOOL]
RETURNS[newRatio: REAL← 1.0, ok: BOOLTRUE]= {
nodes: nodePtr← handle.vars.intNodeList;
inds: branchPtr← handle.vars.inductorList;
k, incr, y, err, denom: REAL;
UNTIL nodes=NIL DO
IF ~nodes.marked THEN {
nodes.nHist.sumk← c*nodes.nHist.f0 + nodes.nHist.sumk;
IF last THEN {
incr← h*nodes.nHist.sumk;
y← nodes.nHist.oldy + incr;
denom← MAX[ABS[y], ABS[nodes.nHist.y]];
IF denom > handle.vars.floor THEN {
err← handle.vars.RKerr*ABS[(y - nodes.nHist.y)/denom];
IF handle.vars.intTol < err THEN {
ok← FALSE;
IF handle.vars.countsPerStep=1 THEN
SIGNAL solveError[IO.PutFR["\nNode %g convergence problems.",
IO.rope[spGlobals.makeStringNB[handle, nodes, NIL]]]];
newRatio← MIN[newRatio, handle.vars.intTol/err]
};
};
nodes.nHist.y← y;
nodes.nHist.sumk← 0.0;
}
ELSE {
incr← h*nodes.nHist.f0;
nodes.nHist.y← nodes.nHist.oldy + incr;
};
};
nodes← nodes.nextIntNode;
ENDLOOP;
UNTIL inds=NIL DO
indsBody: RefL← NARROW[inds.body];
k← inds.posNode.nHist.y - inds.negNode.nHist.y;
indsBody.iHist.sumk← c*k + indsBody.iHist.sumk;
IF last THEN {
indsBody.iHist.y← indsBody.iHist.oldy + h*indsBody.iHist.sumk;
indsBody.iHist.sumk← 0.0
}
ELSE indsBody.iHist.y← indsBody.iHist.oldy + h*k;
inds← indsBody.nextInductor;
ENDLOOP;
}; -- RKupdate
RungeKutta: PROC[handle: Handle, dT: REAL]
RETURNS[newRatio: REAL, nGS: NAT, ok: BOOL]= {
time: REAL;
time← handle.vars.t;
handle.vars.t← handle.vars.t - 0.5*dT;
[] ← RKupdate[handle, 1.0, 0.5*dT, FALSE];
nGS← GSiteration[handle, 0.5*dT, handle.vars.printAll];
[] ← RKupdate[handle, 2.0, 0.5*dT, FALSE];
nGS← GSiteration[handle, 0.5*dT, handle.vars.printAll] + nGS;
handle.vars.t← time;
[] ← RKupdate[handle, 2.0, dT, FALSE];
nGS← GSiteration[handle, dT, handle.vars.printAll] + nGS;
[newRatio, ok]← RKupdate[handle, 1.0, 0.1666667*dT, TRUE]
}; -- RungeKutta
changeStepSize: PROC[handle: Handle, ratio: REAL, RK: BOOL]= {
int: CHAR;
IF ratio < 1.0 OR handle.vars.countsPerStep < maxCountsPerStep THEN {
handle.vars.numGoodSteps← 1;
handle.vars.worstNodeLog[handle.vars.curLog]←
IF handle.vars.worstNode # NIL THEN
[visited: FALSE,
node: handle.vars.worstNode,
t: handle.vars.t,
v: handle.vars.worstNode.nHist.y,
dvdt: handle.vars.worstNode.nHist.f0]
ELSE [FALSE, NIL, 0.0, 0.0, 0.0];
handle.vars.curLog← (handle.vars.curLog + 1) MOD maxLog;
IF handle.vars.countsPerStep=1 THEN SIGNAL solveError["Step too small"];
handle.vars.countsPerStep←
IF ratio > Real.Float[maxCountsPerStep/handle.vars.countsPerStep] THEN
maxCountsPerStep
ELSE MAX[1, Real.RoundLI[handle.vars.countsPerStep*ratio]];
int← IF RK THEN 'R ELSE 'A;
IF ShowDetails[handle] THEN {
Labels.Set[handle.time,
IO.PutFR["%12.6e", IO.real[handle.vars.t]]];
Labels.Set[handle.step,
IO.PutFR["%10d %g", IO.int[handle.vars.countsPerStep], IO.char[int]]];
};
};
}; -- changeStepSize
saveState: PROC[handle: Handle]= {
inds: branchPtr← handle.vars.inductorList;
volts: branchPtr← handle.vars.vSourceList;
indsBody: RefL;
voltsBody: RefV;
FOR nodes: nodePtr← handle.vars.nodeList, nodes.nextNode UNTIL nodes=NIL DO
OPEN nodes.nHist;
SolveTrouble: PROC [explanation: Rope.ROPE] = {
SIGNAL solveTest[IO.PutFR["Node %g %g", IO.rope[spGlobals.makeStringNB[handle, nodes, NIL]], IO.rope[explanation]]];
};
oldy← y;
f4← f3; f3← f2; f2← f1; f1← f0;
SELECT TRUE FROM
y < handle.vars.Vmin => SolveTrouble["below Vmin"];
y > handle.vars.Vmax => SolveTrouble["above Vmax"];
ABS[f0] > handle.vars.dvdtmax => SolveTrouble["change rate too large"];
ENDCASE => NULL;
ENDLOOP;
UNTIL inds=NIL DO
indsBody← NARROW[inds.body];
{ OPEN indsBody.iHist;
oldy← y;
f4← f3; f3← f2; f2← f1;
f1← inds.posNode.nHist.y - inds.negNode.nHist.y;
};
inds← indsBody.nextInductor;
ENDLOOP;
UNTIL volts=NIL DO
voltsBody← NARROW[volts.body];
voltsBody.oldCurrent← voltsBody.vsCurrent;
volts← voltsBody.nextvSource;
ENDLOOP;
}; -- saveState
restoreState: PROC[handle: Handle]= {
nodes: nodePtr← handle.vars.nodeList;
inds: branchPtr← handle.vars.inductorList;
volts: branchPtr← handle.vars.vSourceList;
indsBody: RefL;
voltsBody: RefV;
UNTIL nodes=NIL DO
nodes.nHist.y← nodes.nHist.oldy;
nodes.nHist.f0← nodes.nHist.f1;
IF ABS[nodes.nHist.f0] < handle.vars.ignorDvDtBelow THEN nodes.nHist.f0← 0.0;
nodes← nodes.nextNode;
ENDLOOP;
UNTIL inds=NIL DO
indsBody← NARROW[inds.body];
indsBody.iHist.y← indsBody.iHist.oldy;
inds← indsBody.nextInductor;
ENDLOOP;
UNTIL volts=NIL DO
voltsBody← NARROW[volts.body];
voltsBody.vsCurrent← voltsBody.oldCurrent;
volts← voltsBody.nextvSource;
ENDLOOP;
}; -- restoreState
integration: PROC[handle: Handle, RK: BOOL]= {
nGS: INT;
out, ok: BOOL← FALSE;
ratio, oldTime, DT: REAL;
oldLowStepcount, oldHighStepCount: INT;
saveState[handle];
oldTime← handle.vars.t;
oldLowStepcount← handle.vars.lowStepCount;
oldHighStepCount← handle.vars.highStepCount;
DO
handle.vars.lowStepCount← handle.vars.lowStepCount + handle.vars.countsPerStep;
IF handle.vars.lowStepCount > maxCountsPerStep THEN {
handle.vars.highStepCount← handle.vars.highStepCount + 1;
handle.vars.lowStepCount← handle.vars.lowStepCount - maxCountsPerStep;
out← TRUE;
}
ELSE out← handle.vars.printStep;
handle.vars.t← handle.vars.tMin + handle.vars.dT*handle.vars.lowStepCount +
(handle.vars.dT*maxCountsPerStep)*handle.vars.highStepCount;
DT← handle.vars.dT*handle.vars.countsPerStep;
{ ENABLE Retreat=> {
IF ~Rope.Equal[cause, handle.vars.lastRetreatCause] THEN {
IF Rope.Length[cause] > 0 AND handle.vars.showRetreat THEN
handle.msgStream.PutF["%g at t= %12.6e.\n",
IO.rope[cause], IO.real[handle.vars.t]];
handle.vars.lastRetreatCause← cause;
IF handle.vars.countsPerStep = 1 THEN REJECT;
};
ok← FALSE;
ratio← handle.vars.retRatio;
GOTO FlushIt;
};
IF handle.vars.numGoodSteps < 4 THEN {
[ratio, nGS, ok]← RungeKutta[handle, DT];
IF ok THEN nGS← nGS +
GSiteration[handle, DT, handle.vars.printAll,, TRUE];
ratio← RealFns.SqRt[ratio]*handle.vars.RKfac;
IF ok AND ~RK THEN
handle.vars.numGoodSteps← 1 + handle.vars.numGoodSteps;
}
ELSE {
handle.vars.AMcount← 1 + handle.vars.AMcount;
[ratio, nGS, ok]← AdamsMoulton[handle, DT];
IF ok THEN {
nGS← nGS + GSiteration[handle, DT, handle.vars.printAll,, TRUE];
IF ratio > handle.vars.AMup AND
handle.vars.AMcount >= handle.vars.AMdelay THEN {
ratio← RealFns.SqRt[RealFns.SqRt[ratio]];
changeStepSize[handle, ratio, FALSE];
handle.vars.AMcount← 0;
};
}
ELSE ratio← RealFns.SqRt[ratio];
};
EXITS
FlushIt=> NULL;
};
IF ok THEN EXIT;
restoreState[handle];
handle.vars.t← oldTime;
handle.vars.highStepCount← oldHighStepCount;
handle.vars.lowStepCount← oldLowStepcount;
changeStepSize[handle, MIN[ratio, handle.vars.minRatio],
(handle.vars.numGoodSteps < 4)];
handle.vars.AMcount← 0
ENDLOOP;
NextStage[handle];
IF out THEN {
printFromList[handle, nGS, handle.vars.t, handle.vars.printIter];
plotFromList[handle, handle.vars.t];
IF handle.saveAllData THEN UpdatePP[handle, handle.vars.t];
};
WHILE handle.vars.tBreak > 0.0 AND handle.vars.t >= handle.vars.tBreak DO
oldTime← handle.vars.t ENDLOOP;
}; -- integration
zeroVars: PROC[handle: Handle]= {
n: nodePtr← handle.vars.nodeList;
inds: branchPtr← handle.vars.inductorList;
vSs: branchPtr← handle.vars.vSourceList;
indsBody: RefL;
vSsBody: RefV;
handle.vars.gndNode.marked← TRUE;
UNTIL n=NIL DO
n.nHist← [];
n.marked← FALSE;
n← n.nextNode;
ENDLOOP;
UNTIL inds=NIL DO
indsBody← NARROW[inds.body];
indsBody.iHist← [];
inds← indsBody.nextInductor;
ENDLOOP;
UNTIL vSs=NIL DO
vSsBody← NARROW[vSs.body];
vSsBody.vsCurrent← vSsBody.oldCurrent← 0.0;
IF vSs.controller=NIL THEN
IF vSs.posNode=handle.vars.gndNode THEN vSs.negNode.marked← TRUE
ELSE
IF vSs.negNode=handle.vars.gndNode THEN vSs.posNode.marked← TRUE;
vSs← vSsBody.nextvSource;
ENDLOOP;
}; -- zeroVars
getStepSize: PROC[handle: Handle] RETURNS[stepOK: BOOL]= {
vMax, dvdtMax, frac: REAL← 0.0;
inds: branchPtr← handle.vars.inductorList;
nodes: nodePtr← handle.vars.nodeList;
handle.vars.lowStepCount← 0;
handle.vars.highStepCount← 0;
handle.vars.numberOfSteps← Real.FixI[handle.vars.tMax/handle.vars.printdT];
handle.vars.worstNode← NIL;
UNTIL nodes=NIL DO
vMax← MAX[ABS[nodes.nHist.y], vMax];
IF ABS[nodes.nHist.f0] > dvdtMax THEN {
dvdtMax← ABS[nodes.nHist.f0];
handle.vars.worstNode← nodes;
};
nodes← nodes.nextNode;
ENDLOOP;
frac← IF dvdtMax > 0.0 AND vMax > 0.0 THEN
handle.vars.intTol*vMax/(dvdtMax*handle.vars.printdT)
ELSE handle.vars.initStep;
handle.vars.dT← handle.vars.printdT/maxCountsPerStep;
handle.vars.dvdtTrunc← handle.vars.dvdtFac*handle.vars.intTol;
handle.vars.countsPerStep←
Real.RoundLI[maxCountsPerStep*MIN[frac, handle.vars.initStep]];
handle.vars.countsPerStep←
MAX[handle.vars.countsPerStep, 1];
IF handle.vars.countsPerStep=1 THEN {
stepOK← FALSE;
PutMsgLine[handle, "Initial step is too small!"];
}
ELSE {
stepOK← TRUE;
handle.msgStream.PutF["Initial step is %d.\n",
IO.int[handle.vars.countsPerStep]];
};
}; -- getStepSize
setICs: PUBLIC PROC[handle: Handle]= {
n: nodePtr;
b: branchPtr;
v: REAL;
allNodes: BOOL;
zeroVars[handle];
IF handle.vars.item # leftB THEN error[handle, 700] ELSE next[handle];
IF handle.vars.item=number THEN {
handle.vars.tMin← handle.vars.value;
next[handle];
IF handle.vars.item=comma THEN next[handle] ELSE error[handle, 702];
};
DO
allNodes← (handle.vars.item=star);
IF allNodes THEN next[handle] ELSE [n, b]← findNodeOrBranch[handle];
IF handle.vars.item=leftArrow THEN next[handle] ELSE error[handle, 703];
v← getSignedNumber[handle];
IF allNodes THEN
FOR n← handle.vars.nodeList, n.nextNode UNTIL n=NIL DO
n.nHist.y← v;
ENDLOOP
ELSE
IF n # NIL THEN n.nHist.y← v
ELSE
IF b # NIL THEN
WITH b.body SELECT FROM
l: RefL => l.iHist.y← v;
ENDCASE=> error[handle, 721]
ELSE error[handle, 720];
IF handle.vars.item=comma THEN next[handle] ELSE EXIT;
ENDLOOP;
IF handle.vars.item # rightB THEN error[handle, 701, TRUE] ELSE next[handle];
handle.vars.icsSet← TRUE;
}; -- setICs
runIt: PUBLIC PROC[handle: Handle] RETURNS[REAL]= {
n: INT;
IF handle.vars.item # leftB THEN error[handle, 700] ELSE next[handle];
handle.vars.title ← "Post-Processing Graph";
IF handle.vars.item = string THEN {
handle.vars.title ← Rope.FromRefText[handle.vars.newString];
next[handle];
IF handle.vars.item = comma THEN next[handle] ELSE error[handle, 603]
};
runParms[handle];
IF handle.vars.item # rightB THEN error[handle, 701] ELSE next[handle];
IF handle.vars.tMax <= handle.vars.tMin THEN error[handle, 731];
IF handle.vars.errCount <= 0 THEN {
ENABLE Failure => {error[handle, errorNum]; GOTO quit};
IF ~handle.vars.icsSet THEN zeroVars[handle];
handle.vars.simTime← BasicTime.Now[];
initPrint[handle, handle.vars.simTime];
initPlot[handle, handle.vars.tMin, handle.vars.tMin + handle.vars.tMax];
IF handle.saveAllData THEN
InitPP[handle, handle.vars.tMin, handle.vars.tMin + handle.vars.tMax];
handle.vars.t← handle.vars.tMin;
IF handle.vars.printdT <= 0.0 THEN
handle.vars.printdT← handle.vars.tMax/handle.vars.dfltInts;
handle.vars.dT← handle.vars.printdT;
n← GSiteration[handle, 0.0, handle.vars.printAll, TRUE, TRUE];
printFromList[handle, n, handle.vars.t, handle.vars.printIter];
plotFromList[handle, handle.vars.t];
IF handle.saveAllData THEN UpdatePP[handle, handle.vars.t];
IF ~getStepSize[handle] THEN RETURN[handle.vars.tMin];
handle.vars.numGoodSteps← 1;
WHILE handle.vars.highStepCount <= handle.vars.numberOfSteps DO
integration[handle, handle.vars.doRungeKutta];
IF Canned[handle] THEN EXIT;
IF CheckPoint[handle] THEN {
ResetCheckPoint[handle];
dumpAll[handle, handle.vars.t];
};
ENDLOOP;
handle.vars.icsSet← FALSE;
RETURN[handle.vars.t];
EXITS
quit=> RETURN[handle.vars.t];
}
ELSE RETURN[handle.vars.tMin]
}; -- runIt
END.
CHANGE LOG
Wilhelm, April 27, 1982 4:04 PM
Barth, 7-May-82 10:32:52 PDT
Chen, April 22, 1983 10:56 AM. Changed dvdtmax from 1E13 to 1E15.
Barth, July 11, 1983 1:42 PM
Chen, February 12, 1984 8:13 PM modified to support oldArgVector.
Chen, June 12, 1984 6:10:21 pm PDT, cedarized.
Barth, February 8, 1985 3:17:34 pm PST
McCreight, April 4, 1985 1:57:23 pm PST
Chen, May 9, 1985 6:40:28 pm PDT:
1. RealOps.SqRt => RealFns.SqRt;
2. moved lastRetreatCause from a global variable to be one on the handle.vars.
3. in order to get around the concurrency problem in floating point software, added a new run-parameter ignorDvDtBelow to ignor dvdt when its magnitude is smaller than ignorDvDtBelow.
4. added another new run-parameter, showRetreat, so that if one really wants to see the reasons of retreats during "integration", he may do so by setting it true in the run statement.
Chen, July 22, 1985 8:09:51 pm PDT, => Cedar6.0.
Chen, November 24, 1985 3:21:27 pm PST, added post-processing capability.
Barth, November 12, 1986 3:53:53 pm PST, changed GSIteration to pass firstTime to updateFunctions and changed updateFunctions to always evaluate the models the first time. Controlled branches were not being initialized if both of the nodes to which they were connected were constant nodes.