Repository navigation
Releases: javaNoviceProgrammer/Ngspice_OpenVAF_Enhancements
Release list
Enhancement-888: ordfix says it runs without truncation control, and a diverging run under it stops — Adams at ordfix=3 ended a stiff network at 3e201 V with exit 0
Enhancement-888: ordfix says it runs without truncation control, and a diverging run under it stops — Adams at ordfix=3 ended a stiff network at 3e201 V with exit 0
Scope: N6 of the
transient stress campaign of 2026-10-12.
ngspice: spicelib/analysis/dctran.c: a note at the start of an ordfix run, and at each
accepted point of one a node past 1e15 V ends the run.
examples/transnotes_examples/ (new, shared with E-885,
E-886 and E-887); examples/transopts_examples/
check [5] reads E-882's note by its own text. ngspice only.
Suites:
transnotes_examples: [9] to [11] are this enhancement's,
11 of 11 per solver. On the E-883 binaries [9] and [10] fail; [11] is a control.transopts(7 of 7),corenum,integmethod,maxordnoteanddynorderpass.- The full sweep: 587 of 587.
What was wrong
ordfix (Enhancement-181) is a verification pin. It fixes the
integration order and accepts every converged step: the step grows to tmax, and no truncation
estimate checks it. A run said nothing of this. At an order that is not A-stable, Gear or Adams
at 3 and above, a stiff network grew without bound. One of Family A's networks (seed 1017, nine
nodes, four capacitors, nine resistors) under method=adams maxord=3 ordfix=3:
- ended at −3e201 V at its stop time, with exit 0 and no message;
- run ten times longer, reached 5e305 V after 730 529 iterations, still with exit 0.
Over Family A the error reached 5e206 times trap's.
The change
An ordfix run says what it is:
Note: ordfix=3 accepts every converged step: the step grows to tmax, and no truncation-error estimate checks it.
Note: order 3 of adams is not A-stable: on a stiff circuit the result can grow without bound at a fixed order. A run whose node passes 1e+15 V stops.
The second note is printed only for Gear and Adams pinned at 3 and above.
At each accepted point of an ordfix run, a node voltage past 1e15 V, or not a number, ends the
run. This is E-679's bound for a node held only by gmin:
TRAN: time = 1.26652e-05: the solution is diverging -- node '5' has reached -1.21601e+15 V.
ordfix=3 accepts every converged step, with no truncation-error control. Run without ordfix, or pin an order that is A-stable (gear or adams at 2 at most).
The same network at gear ordfix=2 runs to the end, at −0.917 V, with the first note only.
transopts [5] (E-882) checked that ordfix=4 at maxord=4 printed no
ordfix= note; it now reads E-882's note by its own text, since this note is printed either way.
The checks
transnotes_examples:
- [9] An
ordfixrun says every converged step is accepted with no truncation-error estimate;
adams ordfix=3also says the order is not A-stable, andgear ordfix=2does not. - [10] The stiff network at
adams ordfix=3stops when a node passes 1e15 V. - [11] (control) At
gear ordfix=2the same network runs to the end.
Limits
The bound catches a divergence that reaches 1e15 V. A run whose error grows but stays below it,
such as trbdf2 ordfix=2 73 % off on one of Family A's networks at a tmax too coarse for it,
ends normally: that is what ordfix without truncation control does, and the first note says so.
Write-up: enhancements_doc/Enhancement-888.md
Enhancement-887: an option given while a run is paused is applied or held, and said — option maxord=3 or option klu before resume crashed a run started by run, and a run started by tran ignored every option without a word
Enhancement-887: an option given while a run is paused is applied or held, and said — option maxord=3 or option klu before resume crashed a run started by run, and a run started by tran ignored every option without a word
Scope: N5 of the
transient stress campaign of 2026-10-12.
ngspice:
frontend/spiceif.c,if_run: the options a run starts with are kept; onresumethe ones
changed since are applied or held, with a note.include/ngspice/ftedefs.h:ci_runopts.frontend/runcoms2.c: freed with the circuit.
examples/transnotes_examples/ (new, shared with E-885,
E-886 and E-888). ngspice only.
Suites:
transnotes_examples: [5] to [8] are this enhancement's,
11 of 11 per solver. On the E-883 binaries [5] to [8] fail.checkpoint,ckorder,resumebreak,stagedslots,autosave,idtassertandwritemc
pass.- The full sweep: 587 of 587.
What was wrong
option writes the default task's options. What a paused run did with them depended on how the
run started.
-
Started by
run(the deck's own.tran): the run's task IS the default task, and
CKTdoJobcopies every option from it again at a resume. A new method ormaxordmet history
slots the run had not allocated, andoption klumet a matrix built for Sparse:option maxord=3,option method=gear maxord=4 dynorder: a segmentation fault inCAPload;option klu: one inNIiter.
option method=gearswitched the method mid-run, andoption reltol=1e-6was taken. None of
it was said. -
Started by
tran(from.control): the run has its own copy of the task, made at the
command, and nothing given during the pause reached it. Method,maxord,dynorder, the
solver andreltolall gave the uninterrupted run's 1 346 iterations, without a word. This is
what the campaign saw: its sessions started their runs withtran.
The change
Both routes now resume alike. When a run starts, if_run keeps a copy of its options; at
resume it compares the default task, where option writes, with that copy. So an option set
before the run is not reported, and only what changed during the pause is.
- Taken by the resumed run: the options read afresh at every step. These are
reltol,
abstol,vntol,chgtol,trtol,xmu,trgamma,itl4,gmin,gshunt,pivtol,
pivrel,absdv,reldv,bypass, and the Newton aidsnodedamping,linesearchand
trustregion.Note: the resumed run takes reltol = 1e-06 and trtol = 0.1, given during the pause. - Held at the run's values: the options the run was built on. These are the method,
maxord,ordfix,dynorder,temp,tnom,cshuntand the solver with itsklu_*
settings. They take effect from the next analysis.On theNote: method = gear, maxord = 4 and dynorder on, given during the pause, take effect from the next analysis; the resumed run keeps method = trap, maxord = 2 and dynorder off, which it was built on.runroute the default task gets the values given back after the resumed run, for the
next analysis. A held option is named once, however many pauses follow.
A checkpoint's continuation (loadstate) starts from the options set at that moment, which
E-879 built its circuit on. It compares nothing, and is the start a later
pause compares against.
Measured
On an RC with an inductor, stopped at 1 µs of 4 µs, with delete all before the resume:
| given during the pause | E-883, run |
E-883, tran |
E-887, both |
|---|---|---|---|
maxord=3 |
segmentation fault | kept, silently | kept, named; 4.44486e-7, the uninterrupted value |
klu |
segmentation fault | kept, silently | kept, named |
method=gear maxord=4 dynorder |
segmentation fault | kept, silently | kept, named |
method=gear |
switched mid-run, silently | kept, silently | kept, named |
reltol=1e-6 trtol=0.1 |
taken (3 385 iterations), silently | ignored (887) | taken on both, named; 4.51376e-7 |
The next analysis after the resumed run, with method=gear maxord=3 klu given during the pause,
ends at 4.212264e-7 on both routes. That is the value of a fresh run with those options.
The checks
transnotes_examples:
- [5] A run started by
run, givenmaxord=3, the other solver, and
method=gear maxord=4 dynorderduring a pause, resumes without a crash. It names each with the
value it keeps, and ends where the uninterrupted run ends. - [6] A run started by
trannames and keeps them the same way. - [7]
reltolandtrtolgiven during the pause are taken on both routes, which say so and
end at the same value. - [8] The options held for the resumed run take effect at the next analysis, on both routes.
Write-up: enhancements_doc/Enhancement-887.md
Enhancement-886: newtrunc and its three tolerances say they have no effect, and what is used instead — ltereltol, lteabstol and ltetrtol were taken without a word
Enhancement-886: newtrunc and its three tolerances say they have no effect, and what is used instead — ltereltol, lteabstol and ltetrtol were taken without a word
Scope: N4 of the
transient stress campaign of 2026-10-12.
ngspice: spicelib/analysis/cktsopt.c, CKTsetOpt: OPT_LTERELTOL, OPT_LTEABSTOL and
OPT_LTETRTOL warn in a build without PREDICTOR, and OPT_NEWTRUNC's warning says what runs
instead. Stock options.
examples/transnotes_examples/ (new, shared with E-885,
E-887 and E-888). ngspice only.
Suites:
transnotes_examples: [4] is this enhancement's, 11 of 11
per solver. On the E-883 binaries [4] fails.inputguardpasses.- The full sweep: 587 of 587.
What was wrong
.option newtrunc selects a truncation estimate from node voltages: the difference between a
predictor and the corrector. The predictor is compiled in only by --enable-predictor
(PREDICTOR), which also changes every device's Newton start, and this build, like stock
ngspice's default, does not have it. newtrunc said so:
Warning: Option 'newtrunc' ignored,
compilation with preprocessor flag 'PREDICTOR' is required.
Its three tolerances, ltereltol, lteabstol and ltetrtol, were stored and read by nothing,
and said nothing. Family A's newtrunc rows were trap rows.
The change
Warning -- Option newtrunc is ignored: the voltage-based truncation estimate needs the predictor, which this build does not have (ngspice configured with --enable-predictor has it). The estimate from the charges, set by reltol, abstol, chgtol and trtol, is used.
Warning -- Option ltereltol has no effect: it is a tolerance of newtrunc's voltage-based truncation estimate, which this build does not have (see newtrunc). The estimate from the charges, set by reltol, abstol, chgtol and trtol, is used.
lteabstol and ltetrtol say the same. newtrunc=0 and a deck without them say nothing. In a
build with PREDICTOR nothing changes.
The checks
transnotes_examples:
- [4]
newtruncsays it is ignored and that the charge estimate is used;ltereltol,
lteabstolandltetrtoleach say they have no effect; a deck without them prints neither.
Write-up: enhancements_doc/Enhancement-886.md
Enhancement-885: the truncation estimate sees the first-order error of xmu below 0.5 — xmu=0 ran at trap's points with ten times trap's error
Enhancement-885: the truncation estimate sees the first-order error of xmu below 0.5 — xmu=0 ran at trap's points with ten times trap's error
Scope: N2 of the
transient stress campaign of 2026-10-12.
ngspice: spicelib/analysis/cktterr.c, terr: at trap's order 2 with xmu other than 0.5, the
first-order term of the local error is estimated as well. Stock behaviour.
examples/transnotes_examples/ (new, shared with E-886,
E-887 and E-888). ngspice only.
Suites:
transnotes_examples: [1] to [3] are this enhancement's,
11 of 11 per solver. On the E-883 binaries [1] and [2] fail; [3] is a control.transopts,corenumandintegmethodpass.- The full sweep: 587 of 587.
What was wrong
xmu mixes the trapezoidal rule (0.5) with backward Euler (0). The step is the θ-method
(1 − xmu)·i(n) + xmu·i(n−1) = (q(n) − q(n−1))/h,
whose local error starts at (xmu − ½)·h²·q″. Below 0.5 it is a first-order method. The
truncation estimate kept trap's: its constant on the third divided difference, the h³ term, and a
square root. It sized the steps as if the method were second order, and since the damping lowers
the third difference, it took fewer points than trap.
On an RC with an inductor and a second capacitor, at a 100 ns output step, against a tight
reference:
| xmu | points | rms error (of the range) |
|---|---|---|
| 0.5 (trap) | 177 | 2.76e-3 |
| 0.45 | 165 | 3.70e-3 |
| 0.3 | 153 | 1.13e-2 |
| 0.2 | 150 | 1.71e-2 |
| 0 | 147 | 2.94e-2 |
method=gear maxord=1 |
592 | 1.04e-2 |
xmu=0 is backward Euler, and backward Euler's own control took four times its points. Over the
campaign's Family A, xmu=0 was 106 times trap's error at the same points (median), and
xmu=0.2 up to 764 times.
The change
At trap's order 2 with xmu other than 0.5, the step is also limited by the first-order term,
estimated as backward Euler's is: the constant |0.5 − xmu| on the second divided difference, at
the first power. At xmu=0 that IS backward Euler's estimate. The step is the smaller of the two.
xmu=0.5 takes the trap path as before.
| xmu | points | rms error |
|---|---|---|
| 0.5 | 177 | 2.76e-3 |
| 0.45 | 196 | 3.59e-3 |
| 0.3 | 313 | 6.86e-3 |
| 0.2 | 426 | 8.13e-3 |
| 0 | 592 | 1.04e-2 — backward Euler's steps |
The error now follows the method's order at the tolerance asked for. A damped trap is still a
first-order method, and costs what one costs.
The checks
transnotes_examples:
- [1]
xmu=0takes backward Euler's steps: the points and the value ofmethod=gear maxord=1,
to rounding. The two are the same method computed two ways, trap's (q(n) − q(n−1))/h and
Gear's ag0·q(n) + ag1·q(n−1). They agree to the bit on arm64; on x86-64 under Sparse they are
one point apart in 589, and 4e-12 apart in value. - [2]
xmu=0.2takes more points than trap, and its error is below 1 % of the range. - [3] (control)
xmu=0.5is the default run.
Limits
TR-BDF2's first stage honours xmu too (E-419). Its estimate keeps
TR-BDF2's own second-order constant.
Write-up: enhancements_doc/Enhancement-885.md
Enhancement-884: built-in switches land on their crossing — a switch without hysteresis ended a charge pump with "Timestep too small" at reltol 1e-6, and a switch at a 100 ns step closed 11 ns late
Enhancement-884: built-in switches land on their crossing — a switch without hysteresis ended a charge pump with "Timestep too small" at reltol 1e-6, and a switch at a 100 ns step closed 11 ns late
Scope: N1 of the
transient stress campaign of 2026-10-12,
and the switching half of its N3.
ngspice:
spicelib/devices/sw/swtrunc.candcsw/cswtrunc.c: a switch that turned over in the point
under way asks for the point to be redone at its crossing.sw/swload.candcsw/cswload.c: the switch keeps its accepted state on that landing.
cswload.calso stores the controlling current, which stock ngspice left out.spicelib/analysis/cktterr.c:CKTlandRequest, the landing request any device makes.spicelib/analysis/dctran.c: the retry takes the earliest request; a switch's landing is
redone from the accepted solution, and the step after it starts at order 1.osdi/osditrunc.c: Enhancement-759's Verilog-A landing goes through the
same request.include/ngspice/cktdefs.h,sw/swdefs.h,csw/cswdefs.h.
examples/swland_examples/ (new). ngspice only.
Suites:
swland_examples: 6 of 6 per solver. On the E-883 binaries [1]
to [5] fail; [6] is a control.argguard,ctrlnode,dcpath,gatecheck,sensrestore,solvercoreandwarnphysics
(the suites with built-in switches) pass.evtedge(24 of 24),simctrl,discontinuityandfinalstep(E-759's landing) pass.- The full sweep: 587 of 587.
What was wrong
A charge pump, two switches with vh=0 driven by complementary 1 ns ramps, ran at
reltol=1e-3 and 1e-5 and ended "Timestep too small" at 1e-6 and 1e-7, under both
solvers. Family C of the campaign saw it in 34 of 40 charge-pump references (one more ran past
120 s) and 5 of 40 boost references. Any hysteresis seemed to cure it.
Nothing placed a point at a switch's crossing. The step that crossed was judged by the
truncation estimate, which at a crossing measures the jump in current the switching brings. The
point was rejected, and the retry, sized by that estimate, fell short of the crossing and was
accepted. The next point crossed again. The accepted points crept up on the crossing, each
closer than the last: 4.9984, 4.99994, 4.99998 … × 10⁻¹⁰ s. At reltol=1e-6 the steps reached
1e-16 s, where the rounding in Δq/h is larger than Newton's tolerance, and every retry failed to
converge. Hysteresis did not cure it: a vh=0.01 switch crept the same way, in 1e-14 s steps,
and got across.
At looser tolerance the crossing step was accepted on the grid. A built-in sw driven by a 1 µs
ramp, at a 100 ns step, closed 11.2 ns late and opened 19.7 ns early; E-759's table has the same
lag, 31.2 ns, on its own deck ("plain if, built-in sw: unchanged").
Trap integrated the crossing step with the new state at its end, and rang. The seed-9 charge
pump of Family C (N3) put its switch node at 2.66 V on a 2.49 V supply, and its output 1.1 %
off.
The change
A switch whose state changed between the accepted point and the attempt finds where its control
crossed the band edge that turned it over. The control is interpolated linearly between the two
points; the edge is vt + vh or vt − vh, by SWload's own rule, for either sign of vh. The
switch then asks for the attempt to be redone at the crossing, through E-759's landing protocol.
On that landing it keeps its accepted state. The circuit is integrated with the old state up to
the crossing and with the new one from it, and no gap is left to creep across. The landing is
redone from the accepted solution, not the rejected attempt's: a switch with hysteresis keeps
the state its first iterate took while the control stays in the band.
The step after the landing is the one the switch turns over in. It is taken at order 1, and at
a tenth of the landing step at most, as after a source's corner. No switch asks for a landing
from the landing itself, from the step after one, or for a crossing in the first thousandth of
the step.
CKTlandRequest takes the earliest request, and dctran uses it as the retry's step,
whatever a device type later in the table estimated. The switch comes before the OSDI devices
and after the capacitors in that table. E-759's OSDI request goes through it too; with OSDI
devices last, their landings are unchanged. Under ordfix the request replaces the pinned
step, which would have redone the point at twice the step and crossed again.
The current-controlled switch had no landing to make. CSWload did not store its controlling
current ("without this statement truncation is not used, as with SW. But test are needed"), so
CSWtrunc saw no change. It is stored now, for the landing. The approach limit that read it, which
never ran, is left out.
Measured
| E-883 | E-884 | |
|---|---|---|
charge pump, vh=0, reltol=1e-6 / 1e-7 |
"Timestep too small" | 4.243345 V / 4.243344 V, 80 landings (one per crossing) |
the same, iterations at 1e-3 / 1e-5 |
42 042 / 50 004 | 44 616 / 46 474 |
sw, vh=0, 100 ns step: closes / opens (exact 500.00 / 1500.69 ns) |
511.22 / 1480.98 ns | 500.07 / 1500.78 ns |
vh=0.1 (600.00 / 1600.69 ns) |
591.22 / 1600.98 ns | 600.22 / 1600.74 ns |
vh=−0.1 (400.00 / 1400.69 ns) |
391.22 / 1400.98 ns | 400.22 / 1400.74 ns |
csw (500.00 / 1500.69 ns) |
511.22 / 1480.98 ns | 500.07 / 1500.78 ns |
| seed-9 pump, default trap (tight: 1.183447 V) | 1.196523 V | 1.183490 V |
| its switch node's peak (supply 2.4932 V) | 2.661 V | 2.502 V |
its reltol=1e-6 run, iterations |
57 515 | 35 503 |
its method=trbdf2 run |
1.198507 V | 1.184133 V |
Family C, rerun on both binaries (the three templates with switches):
- Charge pump and boost references (trap,
reltol=1e-6): all 80 end. On E-883, 39 ended
"Timestep too small" and one ran past 120 s. - The 160 runs under random settings: 4 fail (were 11). Each of the four pins an order that
is not A-stable withordfix, as N6 describes (E-888). - The Schmitt trigger: all 120 runs end, on both binaries.
The waveforms were checked against each binary's tight reference, by the rms error over time.
On the charge pump the median error ratio, E-884 over E-883, is 0.33 (21 of 35 runs better by
more than 2×, 2 worse). On the Schmitt trigger it is 1.0 (16 better, 3 worse), and on the boost
0.99 (9 better, 3 worse). The runs that are worse are:
ordfix=6;- boost converters at loose tolerance whose result is set by trap's phase error on a ringing
switch node. On seed 343 the two binaries differ by 18 % and 3 % at default tolerance, the
other way round atreltol=1e-2, and agree to 0.01 V at1e-4and1e-5.
The checks
swland_examples:
- [1] The charge pump with
vh=0runs to the end atreltol=1e-6and1e-7. - [2] A
vh=0switch at a 100 ns step closes at 500 ns and opens at 1500 ns, within 0.3 ns. - [3] With
vh=0.1andvh=−0.1it turns over at the band edge that applies, within 0.5 ns. - [4] A current-controlled switch lands on its crossing, within 0.3 ns.
- [5] The seed-9 pump under default trap ends within 0.05 % of
reltol=1e-6, and its switch
node peaks within 1 % of the supply. - [6] (control) With
vh=0.01the pump runs atreltol=1e-6and ends at 4.24334 V.
Limits
- The crossing is found by linear interpolation of the control. A curved control lands near,
not on, its crossing; the switch keeps its state there either way, and turns over in the next
step. - The step after a landing takes the new state from its start. Its truncation estimate follows
a long landing step, and a fast transient inside it (a picosecond time constant behind a 1 Ω
switch) is crossed in one backward-Euler step rather than resolved; the point after it is
where the transient ends. trbdf2andsdirkkeep their own order across the landing; on the seed-9 pumptrbdf2's
switch node still peaks at 2.57 V.- An XSPICE digital or
aswitchcode model is not a built-in switch and does not land.
Write-up: enhancements_doc/Enhancement-884.md
Enhancement-883: an unknown integration method is named, and so is the method that runs instead — method=trbdf ran trap after "unsupported integration method"
Enhancement-883: an unknown integration method is named, and so is the method that runs instead — method=trbdf ran trap after "unsupported integration method"
Scope: D3 of the
transient stress campaign of 2026-10-12.
ngspice: spicelib/analysis/cktsopt.c, CKTsetOpt: OPT_METHOD with a name it does not know
warns and keeps the current method, instead of returning E_METHOD.
examples/transopts_examples/ (new, shared with E-881 and
E-882). ngspice only.
Suites:
transopts_examples: [6] is this enhancement's, 7 of 7 per
solver. On the E-875 binaries [6] fails.- The full sweep: 585 of 585.
What was wrong
.option method=trbdf, a typo for trbdf2, printed
setAnalysisParm(options) ci_curOpt: unsupported integration method
and the run went on with trap. The message named neither the value nor the method that ran.
method=bdf and method=gear2 did the same.
The change
An unknown name is reported with the value, the names that exist and the method that is kept:
Warning -- Option method = trbdf is unknown (use trap, gear, trbdf2, sdirk or adams); ignored, keeping trap
After an option method=gear, an unknown name says keeping gear. The generic line is no longer
printed.
The checks
transopts_examples:
- [6]
method=trbdfnames the value and the valid ones, and says trap is kept. After
option method=gear,method=foosays gear is kept. The generic "unsupported integration
method" line is gone.
Write-up: enhancements_doc/Enhancement-883.md
Enhancement-882: ordfix and reltol say when a value has no reading — ordfix=-1 and ordfix=100 were taken without a word, and so was reltol=10
Enhancement-882: ordfix and reltol say when a value has no reading — ordfix=-1 and ordfix=100 were taken without a word, and so was reltol=10
Scope: D2 of the
transient stress campaign of 2026-10-12.
ngspice:
spicelib/analysis/cktsopt.c,CKTsetOpt:OPT_ORDFIXrefuses a negative value and caps one
above 6, with a warning;OPT_RELTOLrefuses 1 and more.spicelib/analysis/dctran.c: a note whenordfixis above the order the method andmaxord
allow.
examples/transopts_examples/ (new, shared with E-881 and
E-883). ngspice only.
Suites:
transopts_examples: [3], [4], [5] and [7] are this
enhancement's, 7 of 7 per solver. On the E-875 binaries [3] to [5] fail; [7] is a control.inputguard(107 of 107) andcorenumpass.- The full sweep: 585 of 585.
What was wrong
ordfix(Enhancement-181) pins the integration order: 0 is off, 1 to
6 an order.ordfix=-1was taken as off.ordfix=7,100and1e9were cut to the order
ceiling. Neither said anything.- The ceiling (Enhancement-853) is
maxord, or 2 under trap. So
method=gear ordfix=4at the defaultmaxord=2ran at order 2, also without a word. reltol:reltol=1andreltol=10were taken. A relative tolerance of 100 % or more has no
reading. Enhancement-426 refused onlyreltol <= 0.
The change
-
ordfixbelow 0 is refused. Above 6 it is set to 6, asmaxordis:Warning -- Option ordfix = 7 is above 6, the highest integration order; 6 is used -
At the start of a transient, an
ordfixabove the ceiling says so:Note: ordfix=4 is above the highest order allowed here (2, from maxord); the order is pinned at 2. -
reltolof 1 or more is refused and the run keeps its value:Warning -- Option reltol = 10 must be less than 1; ignored, keeping 0.001reltol <= 0keeps its "must be greater than zero".
The checks
transopts_examples:
- [3]
ordfix=-1is refused;ordfix=7andordfix=1e9warn that 6 is used. - [4]
reltol=1andreltol=10are refused, and the run is the default run. - [5] Gear with
ordfix=4at the defaultmaxord=2says the order is pinned at 2; at
maxord=4it says nothing. - [7] (control)
reltol=0is refused as "must be greater than zero", as before.
Update (E-888). Every ordfix run now prints a note of its own, so
[5]'s "at maxord=4 it says nothing" reads: no note that the order is pinned at a ceiling. The
check looks for E-882's note by its text.
Write-up: enhancements_doc/Enhancement-882.md
Enhancement-881: xmu outside [0, 0.5] is refused — xmu=2 turned an answer of 3.757e-7 into −2.53e-7 without a word
Enhancement-881: xmu outside [0, 0.5] is refused — xmu=2 turned an answer of 3.757e-7 into −2.53e-7 without a word
Scope: D1 of the
transient stress campaign of 2026-10-12.
ngspice: spicelib/analysis/cktsopt.c, CKTsetOpt: OPT_XMU is range-checked. Stock option.
examples/transopts_examples/ (new, shared with E-882 and
E-883). ngspice only.
Suites:
transopts_examples: [1] and [2] are this enhancement's,
7 of 7 per solver. On the E-875 binaries [1] fails; [2] is a control.- The full sweep: 585 of 585.
What was wrong
xmu weights the trapezoidal rule (0.5) against backward Euler (0). On an RC with an inductor
whose answer at 4 µs is 3.757e-7:
.option xmu=-1gave 2.58e-6;.option xmu=2gave −2.53e-7.
Neither printed anything. Outside [0, 0.5] the formula is neither method. Every sibling
tolerance has been range-checked since Enhancement-426 and
Enhancement-447.
The change
A value outside [0, 0.5] is refused, and the run keeps the value it had:
Warning -- Option xmu = 2 must lie in [0, 0.5]; ignored, keeping 0.5
Both decks now give 3.757e-7. xmu=0 and xmu=0.3 are taken as before.
The checks
transopts_examples:
- [1]
xmu=-1andxmu=2are refused with a warning, and the run is the default run. - [2] (control)
xmu=0andxmu=0.3are taken, and move the answer.
Write-up: enhancements_doc/Enhancement-881.md
Enhancement-880: a resumed transient keeps the breakpoint spacing it ran with — a resume, in memory or from a checkpoint, took a minbreak 5e6 times larger and stepped over edges the run had landed on
Enhancement-880: a resumed transient keeps the breakpoint spacing it ran with — a resume, in memory or from a checkpoint, took a minbreak 5e6 times larger and stepped over edges the run had landed on
Scope: F7 of the
transient stress campaign of 2026-10-12.
ngspice: spicelib/analysis/dctran.c: tran_minbreak, the run's rule for a minbreak that is not
given, used at the start, on a resume and on a checkpoint continuation. Stock ngspice-46
behaviour for the in-memory resume; the checkpoint branch of
Enhancement-131 copied it.
examples/resumebreak_examples/ (new). ngspice only.
Suites:
resumebreak_examples: 5 of 5 per solver. On the E-875
binaries [1] to [3] fail; [4] and [5] are controls.checkpoint,ckorderandstagedslotspass.- The campaign's Family D, rerun: no resumed run and no checkpoint continuation is more than 10×
less accurate than the uninterrupted run, except one underordfix=6. Before, there were 5
and 4. Every one of those nine now matches the uninterrupted run's error. - The full sweep: 584 of 584.
What was wrong
A checkpoint continuation did not land where the uninterrupted run landed. On a network of the
campaign, driven by a pulse with an 82 fs rise, it was 0.19 off against the exact solution where
the uninterrupted run was 0.011. On an RC driven by a pulse with 10 fs edges, the uninterrupted run
landed on 17 of the 18 corners after the stop, and the continuation on 9. Every rise's end was
stepped over.
minbreak is the smallest spacing kept between breakpoints; a source skips a corner closer than
that. With no .option minbreak, a run starts with ten times delmin in a build with XSPICE, as
ours is, and with tmax/20 000 in a build without it. CKTdoJob puts the option's 0 back for every
job, a resume included. The resume branch of DCtran then re-derived it with tmax/20 000 in
either build, and the checkpoint branch was written the same way. On these decks that is 1e-12 s
against 2e-18 s, 5e6 times larger. The 10 fs edges fell under it, and the continuation skipped
the corners the run had landed on.
The checkpoint continuation is the in-memory resume, point for point, on both the E-875 and the
current binaries. The defect was the resume's, and F7's checkpoint cases were two of its faces.
Enhancement-877 fixed three of the five Family D resumes it explained (a
current source's chain). The other two, under ordfix and trbdf2, were this.
The change
tran_minbreak returns the run's rule: ten times delmin with XSPICE, tmax/20 000 without. A run's
start, a resume and a checkpoint continuation all take it when minbreak is not given. A given
minbreak is used as given, as before.
The RC continuation now lands on the same 17 corners as the run, and the campaign network's
continuation follows the uninterrupted run to 1e-6 of the range; it was 0.16.
A continuation is still not the uninterrupted run point for point. stop when time > t sets a
breakpoint at t, so the stopped run lands there and restarts its step. The continuation equals a
run with the same stop.
The checks
resumebreak_examples, every deck under .option sparse (checkpoints need it):
- [1] An RC driven by a pulse with 10 fs edges: after the stop, a checkpoint continuation
lands on every corner the uninterrupted run lands on. - [2] The in-memory resume: the same.
- [3] The campaign network: the continuation follows the uninterrupted run to 1e-4 of the
range. - [4] (control) The checkpoint continuation and the in-memory resume are the same run, point
for point. - [5] (control) With
.option minbreak=1p, the 10 fs edges' ends are skipped by the run and by
the continuation alike.
Write-up: enhancements_doc/Enhancement-880.md
Enhancement-879: loadstate restores a checkpoint taken with any maxord, and a mismatch names the order — every checkpoint above maxord=2 was refused as "does not match this circuit (equations 4 vs 4, states 2 vs 2)"
Enhancement-879: loadstate restores a checkpoint taken with any maxord, and a mismatch names the order — every checkpoint above maxord=2 was refused as "does not match this circuit (equations 4 vs 4, states 2 vs 2)"
Scope: F6 of the
transient stress campaign of 2026-10-12.
ngspice: frontend/com_checkpoint.c, loadstate:
- the integration method and
maxordare copied from the task before the circuit is set up,
with Enhancement-874's pin for the staged methods; - the mismatch message names the maximum order.
examples/ckorder_examples/ (new). ngspice only.
Suites:
ckorder_examples: 5 of 5 per solver. On the E-875 binaries
[1] to [4] fail; [5] is a control.checkpoint(22 of 22) andstagedslotspass.- The full sweep: 583 of 583.
What was wrong
* run part of a transient and save it; then, in a fresh process, load it
v1 a 0 pulse(0 1 0 1n 1n 1u 2u)
r1 a b 1k
c1 b 0 1n
.option sparse method=gear maxord=3
.tran 10n 4u
.control
stop when time > 1.5u
run
savestate ck.bin ; the second deck has `loadstate ck.bin` here
.endc
.end
Error: loadstate: checkpoint does not match this circuit (equations 4 vs 4, states 2 vs 2).
The same happened with maxord=3 alone, method=adams maxord=4, gear maxord=6 dynorder,
method=trbdf2 maxord=3, and method=sdirk, whose order is 3. Only maxord=2 restored. In the
campaign's Family D, 34 of 96 checkpoint trials stopped here.
loadstate sets the circuit up itself when it is not set up yet, as it is in a fresh process.
CKTdoJob copies the task's options into the circuit only when the analysis is dispatched, at the
resume, after that setup. Enhancement-180 copied the KLU flags across that
gap. The integration options were not copied, so the circuit had the default maxord of 2. The
signature check compares the saved maxord with it, and the message printed the two counts that
matched, not the order that did not.
The change
Before the setup, loadstate copies the method and maxord from the task. A staged method's order
is pinned as CKTdoJob pins it: 2 for trbdf2, 3 for sdirk. The checkpoint then meets the
circuit it was taken from. A real mismatch names the order:
Error: loadstate: checkpoint does not match this circuit (equations 6 vs 6, states 4 vs 4, maximum order 3 vs 2).
Every setting above now restores, and the continuation ends where the uninterrupted run does.
It does not follow the uninterrupted run's time grid (F7 of the campaign), so the two agree to
about 3e-8, not to the bit.
The checks
ckorder_examples, every deck under .option sparse (checkpoints need it:
Enhancement-131):
- [1]
gear maxord=3: the checkpoint restores in a fresh process, and v(b) at 2.5 µs is the
uninterrupted run's to 1e-6. - [2]
adams maxord=4, andgear maxord=6withdynorder: the same. - [3]
sdirk: the same. - [4] A checkpoint taken at
maxord=3and loaded into a deck atmaxord=2is refused with
"maximum order 3 vs 2" in the message. - [5] (control)
maxord=2restores, as before.
Write-up: enhancements_doc/Enhancement-879.md