DC Operating Point
The DC operating point is the answer to the most basic question a circuit simulator can ask: if nothing is changing, what are all the voltages and currents?
Turn off every time-varying source. Remove every signal. Let the circuit settle into its steady state. The voltages and currents you find there are the DC operating point — and nearly everything else SPICE does (transient analysis, AC analysis, noise analysis) starts from it.
For a circuit with only resistors and voltage sources, finding the operating point is straightforward: assemble the MNA matrix from Chapter 2, solve it, done. One linear system, one solution.
But real circuits have diodes and transistors. These devices are nonlinear — their relationship between voltage and current isn't a straight line. The matrix coefficients depend on the very voltages we're trying to find, which means we can't just solve the system in one step. We need to iterate.
This chapter introduces the algorithm that makes it work: Newton-Raphson iteration. It is the beating heart of every SPICE simulator.
Linear circuits
Consider a resistive voltage divider — three resistors and a voltage source. For a circuit with only linear components, the DC operating point is trivial: stamp once, factor once, solve once.
Four nodes: a, b, c, gnd. The voltage at a is fixed at
This is a standard linear system:
The key property is linearity: the conductance of every component is fixed, independent of the voltages across it. A 2k resistor is always a 2k resistor, whether it has 1V across it or 100V. The matrix can be assembled once and solved once. No iteration, no guessing.
In spice-rs, the ni_iter loop (the Newton-Raphson loop in solver.rs) still runs, but it converges in a single iteration — the first solution is exact, so the convergence check immediately passes.
Nonlinear circuits
Now add a diode to the circuit:
The diode D1 connects node b to ground. Its current is given by the Shockley equation (Chapter 4 covers this in detail):
where
The MNA equation at node b is KCL — current in through R1 equals current out through D1:
This is no longer a linear equation. The right-hand side contains
The circular dependency
For the resistive divider, we could fill in every matrix entry before solving. Not anymore. The diode's contribution to the matrix — its equivalent conductance — is:
This is the slope of the I-V curve at the operating point. But to compute
We're stuck in a loop:
- To build the matrix, we need
- To find
, we need - To find
, we need to solve the matrix
This is the fundamental challenge of nonlinear circuit simulation. Every nonlinear device — diodes, MOSFETs, BJTs — creates the same circular dependency.
The solution: don't try to get it right in one step. Iterate.
The Nonlinear Transfer Curve
Before we see how Newton-Raphson solves this, let's visualize what the nonlinear equation looks like. Sweep
The transfer curve reveals the nonlinearity. At low
The sharp bend in the curve is the nonlinearity that Newton-Raphson must handle. A linear solver would predict the dashed line. The iterative solver finds the copper curve by repeatedly linearizing the exponential at each operating point until the solution converges.
Try increasing R — the transition becomes slightly sharper because a larger resistor means less current at the same
Newton-Raphson iteration
Newton-Raphson is the algorithm that turns a nonlinear problem into a sequence of linear ones. The idea: at each step, linearize every nonlinear device around the current guess, solve the resulting linear system, and use the solution as the next guess. Repeat until the guess stops changing.
The algorithm
- Start with a guess for all node voltages (typically 0V, or a smarter initial estimate)
- Linearize every nonlinear device at the current guess — replace each device with a conductance and current source that match the device's behavior at that operating point
- Stamp the linearized models into the MNA matrix
- Solve the linear system to get new node voltages
- Check convergence — if the new voltages are close enough to the old ones, stop
- Otherwise, go back to step 2 with the new voltages as the guess
Each pass through this loop is called a Newton-Raphson iteration (or just an "NR iteration"). Typical circuits converge in 5-15 iterations.
Linearization: the companion model
At each iteration, the diode is replaced by its companion model — a linear circuit that behaves identically to the diode at the current operating point.
Given a guess
This is the tangent-line approximation to the I-V curve. It stamps into the matrix exactly like a resistor (conductance
Interactive NR convergence
Adjust the initial guess below to see how Newton-Raphson converges for the diode-resistor circuit. The table shows the iteration-by-iteration convergence from your chosen starting point.
Try different initial guesses. Notice how starting at 0V takes a big first step (the diode is essentially open), but voltage limiting prevents the exponential from overflowing. Starting near the true solution (~0.65V) converges in just 2-3 iterations — that's the quadratic convergence kicking in.
Quadratic convergence
Newton-Raphson has a remarkable property: once you're close to the answer, the number of correct digits roughly doubles with each iteration. If iteration
This is called quadratic convergence, and it's why NR is so effective — once the iterates enter the "convergence basin" around the true solution, they converge extremely fast. The challenge is getting close enough for this rapid convergence to kick in.
How spice-rs implements this
The NR loop lives in solver.rs, in the function ni_iter — a faithful port of ngspice's NIiter. Here is the core structure:
loop {
// 1. Clear matrix and RHS
sim.mna.clear();
// 2. Load all devices — each one linearizes at current guess
// and stamps its companion model into the matrix
for device in &mut circuit.devices {
device.load(&mut sim.mna, ...)?;
}
// 3. Add diagonal gmin for numerical stability
sim.mna.add_diag_gmin(sim.diag_gmin);
// 4. Factor and solve the linear system
sim.mna.solve()?;
// 5. Check convergence — compare new solution to old
if sim.noncon == 0 && sim.iter_count > 1 {
sim.noncon = ni_conv_test(sim, circuit, config);
}
// 6. If converged, return; otherwise swap and iterate
if sim.noncon == 0 {
return Ok(sim.iter_count);
}
sim.mna.swap_rhs();
}
Each device's load() function is where linearization happens. The convergence test in ni_conv_test compares every node voltage and branch current between the current and previous solution:
where reltol = vntol =
Convergence
Newton-Raphson doesn't always work. The quadratic convergence guarantee holds only when you start close enough to the solution.
What can go wrong
Oscillation. If the I-V curve has regions of very high curvature, Newton-Raphson can overshoot on one iteration and undershoot on the next, bouncing back and forth without settling. The tangent-line approximation is only good locally. Voltage limiting (Chapter 4) directly addresses this.
Slow convergence. Some circuits converge, but painfully slowly — this typically means the circuit has near-singular behavior, with nodes weakly connected to anything.
No solution found. In rare cases, the NR iteration hits the iteration limit (typically 100 or 150 iterations for DC operating point) and gives up.
The convergence test
In spice-rs (ported from ngspice's NIconvTest), convergence is declared when every unknown in the system has stabilized. For each node voltage:
For each branch current:
where the default tolerances are:
reltol=(0.1% relative change) vntol=V (1 microvolt absolute) abstol=A (1 picoamp absolute)
The absolute tolerance matters for signals near zero — without it, a node voltage that bounced between
In addition to the node-level test, spice-rs (like ngspice) runs a per-device convergence test. Each nonlinear device checks that its terminal voltages haven't changed by more than its own tolerance.
The noncon flag
Inside the NR loop, spice-rs tracks a flag called noncon (short for "non-convergence"). Each device's load() function can set this flag if it applied voltage limiting. The convergence test only runs when noncon is already 0. If any device flagged non-convergence during the load step, the solver skips the test and goes directly to the next iteration.
Gmin and source stepping
When direct Newton-Raphson fails to converge, SPICE doesn't give up. It has fallback strategies that modify the circuit to make it easier to solve, then gradually remove the modifications until the original circuit is recovered.
In spice-rs (ported from ngspice's CKTop), the sequence is:
- Try direct Newton-Raphson
- If that fails, try dynamic gmin stepping
- If that fails, try true gmin stepping
- If that fails, try Gillespie source stepping
- If everything fails, report "no convergence"
Most circuits converge at step 1. Steps 2-4 are safety nets.
Gmin stepping
Some circuits have nodes that are connected only through nonlinear devices. At the initial guess (all voltages zero), those devices might have nearly zero conductance — meaning the node is effectively floating. A floating node makes the matrix singular or near-singular.
The solution: add a small conductance from every node to ground. This is called gmin — a minimum conductance that ensures no node is ever truly floating.
The trick is to start with a large gmin (around
- Set
S. Solve. (Easy -- heavy damping.) - Reduce
by a factor (typically 10). Solve, using the previous solution as the initial guess. - Repeat until
reaches its target value ( S by default). - Do one final solve with gmin at its target. This is the real answer.
Each step only changes the circuit slightly, so Newton-Raphson converges quickly from the previous solution.
Dynamic gmin adds gmin to the matrix diagonal only. The factor between steps adapts based on how many NR iterations the previous step required. True gmin adds gmin as a per-device parameter inside each semiconductor's equations, which is more physically meaningful.
Source stepping
Source stepping takes a different approach: instead of modifying the circuit's connectivity, it modifies the excitation. All independent voltage and current sources are scaled by a factor that ramps from 0 to 1.
With all sources at zero, every node is at 0V and every device is in a well-defined (if boring) state. Then the sources are gradually increased, with the solver tracking the operating point smoothly at each step.
Source stepping is most effective for circuits where the difficulty comes from the magnitude of the excitation rather than the topology.
The big picture
These convergence aids are what make SPICE practical for real circuits. The hierarchy of fallbacks reflects a design philosophy: try the simplest (and fastest) approach first, then progressively deploy heavier tools. Direct NR is fast but fragile. Gmin stepping is slower but more robust. Source stepping is slowest but handles the widest range of circuits.