From 1b0ea5393f1a5b260a06cfc47f5bc314bb85824e Mon Sep 17 00:00:00 2001 From: esaruoho Date: Tue, 3 Mar 2026 14:21:23 +0200 Subject: [PATCH 1/2] Fix SCR convergence failure with series resistor (#851) Move the SCR on/off state evaluation from doStep() to startIteration() so it is evaluated once per timestep rather than on every Newton-Raphson sub-iteration. The previous code switched the anode resistance between 0.0105 ohms (on) and 100K ohms (off) inside doStep(), causing the resistance to flip-flop on every iteration when the SCR was near its trigger threshold with an external series resistor, preventing convergence. This matches the pattern used by TriacElm and DiacElm, which both evaluate their state in startIteration(). Co-Authored-By: Claude Opus 4.6 --- .../circuitjs1/client/SCRElm.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/com/lushprojects/circuitjs1/client/SCRElm.java b/src/com/lushprojects/circuitjs1/client/SCRElm.java index 62fb41359..438f5405d 100644 --- a/src/com/lushprojects/circuitjs1/client/SCRElm.java +++ b/src/com/lushprojects/circuitjs1/client/SCRElm.java @@ -97,6 +97,7 @@ void undumpXml(XMLDeserializer xml) { double ia, ic, ig, curcount_a, curcount_c, curcount_g; double lastvac, lastvag; double gresistance, triggerI, holdingI; + boolean state; final int hs = 8; Polygon poly; @@ -217,6 +218,19 @@ void stamp() { diode.stamp(nodes[inode], nodes[cnode]); } + // Evaluate the on/off state once per timestep, not during Newton-Raphson + // iterations. Previously the state switching was inside doStep(), causing + // the anode resistance to flip-flop between on (0.0105) and off (10e5) on + // every sub-iteration when an external series resistance was present, + // preventing convergence (issue #851). This matches the pattern used by + // TriacElm and DiacElm. + void startIteration() { + double icmult = 1/triggerI; + double iamult = 1/holdingI - icmult; + state = (-icmult*ic + ia*iamult > 1); + aresistance = state ? .0105 : 10e5; + } + void doStep() { double vac = volts[anode]-volts[cnode]; // typically negative double vag = volts[anode]-volts[gnode]; // typically positive @@ -226,11 +240,6 @@ void doStep() { lastvac = vac; lastvag = vag; diode.doStep(volts[inode]-volts[cnode]); - double icmult = 1/triggerI; - double iamult = 1/holdingI - icmult; - //System.out.println(icmult + " " + iamult); - aresistance = (-icmult*ic + ia*iamult > 1) ? .0105 : 10e5; - //System.out.println(vac + " " + vag + " " + sim.converged + " " + ic + " " + ia + " " + aresistance + " " + volts[inode] + " " + volts[gnode] + " " + volts[anode]); sim.stampResistor(nodes[anode], nodes[inode], aresistance); } void getInfo(String arr[]) { From e9b7c75b511f746b017f4b2bf8cda35640628f3b Mon Sep 17 00:00:00 2001 From: esaruoho Date: Thu, 12 Mar 2026 14:27:33 +0200 Subject: [PATCH 2/2] Fix SCR oscillation: use latching trigger/hold logic like TriacElm The previous combined formula (ig/triggerI + ia/holdingI > 1) re-evaluated the on/off state every timestep without hysteresis. With a series resistor, the anode current would hover near the threshold: the SCR would turn on, current would rise, then the voltage drop across the series resistor would reduce anode current below the threshold, turning it off again -- causing oscillation between two states. Replace with separate trigger and hold conditions: - Gate current exceeding triggerI latches the SCR on (state = true) - Anode current dropping below holdingI turns it off (state = false) This matches the latching behavior of a real SCR and the pattern already used by TriacElm. An SCR with a series resistor should latch on and remain on once triggered, which is what LTspice shows for the same circuit. Also reset state to false in reset() for proper initialization. Co-Authored-By: Claude Opus 4.6 --- .../lushprojects/circuitjs1/client/SCRElm.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/com/lushprojects/circuitjs1/client/SCRElm.java b/src/com/lushprojects/circuitjs1/client/SCRElm.java index 438f5405d..1ba7c3824 100644 --- a/src/com/lushprojects/circuitjs1/client/SCRElm.java +++ b/src/com/lushprojects/circuitjs1/client/SCRElm.java @@ -76,6 +76,7 @@ void reset() { volts[anode] = volts[cnode] = volts[gnode] = 0; diode.reset(); lastvag = lastvac = curcount_a = curcount_c = curcount_g = 0; + state = false; } int getDumpType() { return 177; } @@ -222,12 +223,17 @@ void stamp() { // iterations. Previously the state switching was inside doStep(), causing // the anode resistance to flip-flop between on (0.0105) and off (10e5) on // every sub-iteration when an external series resistance was present, - // preventing convergence (issue #851). This matches the pattern used by - // TriacElm and DiacElm. + // preventing convergence (issue #851). + // + // Use separate trigger/hold conditions with latching, matching the pattern + // used by TriacElm. Once the gate current exceeds triggerI the SCR latches + // on, and stays on until the anode current drops below holdingI. The gate + // only triggers; it does not need to remain driven to keep the SCR on. void startIteration() { - double icmult = 1/triggerI; - double iamult = 1/holdingI - icmult; - state = (-icmult*ic + ia*iamult > 1); + if (ia < holdingI) + state = false; + if (ig > triggerI) + state = true; aresistance = state ? .0105 : 10e5; }