Skip to content

Commit 24fecc4

Browse files
committed
Widen voltage-limit knee, raise gmin for convergence
Paul's pfalstad#241 test circuit with multiple open switches was still failing. Root cause: vt was 5% of maxVoltage (0.25V for a 5V limit), giving a near-discontinuous Jacobian. g jumps by ~1e7 across a few tens of mV near the compliance threshold, so Newton pings between the linear and saturated regimes. Changes: - vt = max(0.2 * maxVoltage, 0.1V) spreads the knee so sech^2 varies smoothly across ~1V instead of ~0.25V. - Gmin floor raised from 1e-9 to 1e-6 to keep the Norton resistor well below the matrix stiffness scale in open-circuit topologies. - Removed the explicit converged=false trigger; the solver's own convergence criteria are sufficient once the Jacobian is well-shaped.
1 parent f7156f2 commit 24fecc4

File tree

1 file changed

+13
-9
lines changed

1 file changed

+13
-9
lines changed

src/com/lushprojects/circuitjs1/client/CurrentElm.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,23 @@ void stamp() {
121121
}
122122

123123
// Smooth voltage compliance via tanh-shaped saturation. Newton-Raphson
124-
// gets an exact Jacobian so it converges even when the load is open
125-
// (all switches off): as |vd| approaches maxVoltage, the source rolls off
126-
// to zero current rather than slamming between full-on and full-off.
124+
// gets an exact Jacobian so it converges even when the load is open:
125+
// as |vd| approaches maxVoltage, the source rolls off to zero current
126+
// rather than slamming between full-on and full-off.
127+
//
128+
// vt (transition width) is 20% of maxVoltage so the Jacobian slope
129+
// varies gently across the knee; a tighter knee (e.g. 5%) causes
130+
// Newton to ping-pong between linear and saturated iterations when
131+
// vd hovers near the compliance voltage.
127132
void doStep() {
128133
if (broken || !isVoltageLimited())
129134
return;
130135
double vd = volts[1] - volts[0];
131-
if (Math.abs(lastVoltDiff - vd) > 0.01)
132-
sim.converged = false;
133136
lastVoltDiff = vd;
134137

135138
double absVd = Math.abs(vd);
136139
double signVd = (vd >= 0) ? 1.0 : -1.0;
137-
double vt = Math.max(maxVoltage * 0.05, 1e-6);
140+
double vt = Math.max(maxVoltage * 0.2, 0.1);
138141

139142
// i(vd) = currentValue * 0.5 * (1 - tanh((|vd| - maxVoltage)/vt))
140143
double arg = (absVd - maxVoltage) / vt;
@@ -145,9 +148,10 @@ void doStep() {
145148
double g = -currentValue * 0.5 * sech2 / vt * signVd;
146149

147150
// Norton companion: parallel resistor (1/|g|) + adjusted current source.
148-
// Add gmin so a singular matrix is avoided when sech^2 is near zero
149-
// (well inside or well outside compliance).
150-
double absG = Math.abs(g) + 1e-9;
151+
// Gmin floor keeps the Norton resistance finite when sech^2 is
152+
// vanishing (well inside or well outside compliance) so the matrix
153+
// stays non-singular and Newton steps remain bounded.
154+
double absG = Math.abs(g) + 1e-6;
151155
sim.stampResistor(nodes[0], nodes[1], 1.0 / absG);
152156
sim.stampCurrentSource(nodes[0], nodes[1], i - g * vd);
153157
current = i;

0 commit comments

Comments
 (0)