feat(core): mutual inductance K, ic= ICs, .tran uic + Chua & Lin showcase#49
feat(core): mutual inductance K, ic= ICs, .tran uic + Chua & Lin showcase#49mfiumara wants to merge 1 commit into
Conversation
Adds the SPICE K-element for magnetically-coupled inductors, ic= initial
conditions on capacitors and inductors, and the .tran ... uic flag that
seeds the transient from those ICs instead of a DC operating point. Lets
spice-ts run the Chua & Lin 8-7 (page 343) benchmark — a classic SPICE
torture test where every simulator gives a slightly different answer
because it stresses K-coupling, IC handling, and stiff multi-time-scale
dynamics simultaneously. Wired into the showcase under "Benchmarks".
UIC seed builds a substituted DC system (caps with ic become V sources,
inductors with ic become I sources, K-elements drop) and maps the result
back to the original circuit's branch layout. Inductor → I-source flips
node order to match the inductor branch-current sign convention. The
seeded solution does not satisfy the trapezoidal companion's history, so
the driver bootstraps with Backward Euler on step 1 — same pattern used
after waveform breakpoints.
Tokenizer now ignores trailing unit letters after numeric values
("2H", "100Ohm", "1uF"); previously these threw ParseError.
Issue #48.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #49 +/- ##
==========================================
+ Coverage 88.92% 89.09% +0.17%
==========================================
Files 74 76 +2
Lines 5713 5798 +85
Branches 1290 1310 +20
==========================================
+ Hits 5080 5166 +86
+ Misses 633 632 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| const seed = analysis.uic | ||
| ? computeUICInitialSolution(circuit, compiled, opts) | ||
| : solveDCOperatingPoint(compiled, opts).assembler.solution; | ||
| result.transient = solveTransient(compiled, analysis, opts, seed, analysis.uic); |
There was a problem hiding this comment.
🔴 UIC handling not implemented in simulateStream, simulateStepStream, and solveStep paths
The PR adds UIC (Use Initial Conditions) support to simulate() at packages/core/src/simulate.ts:91-94, correctly computing a UIC seed and passing seedIsUIC to the transient driver. However, the three other public/internal simulation entry points that run .tran analyses were not updated:
simulateStream()(packages/core/src/simulate.ts:157-160) always callssolveDCOperatingPoint, ignoringanalysis.uic.simulateStepStream()→streamWithSteps()(packages/core/src/simulate.ts:259-265) same issue.solveStep()(packages/core/src/analysis/step.ts:115-118) same issue.
Additionally, the internal streamTransient() helper (packages/core/src/simulate.ts:300-310) never passes seedIsUIC to createDriverFromCompiled, so even if the correct seed were provided, the driver wouldn't use the required Backward-Euler bootstrap for the first step.
When a user calls simulateStream or simulateStepStream with a .tran ... uic netlist, the uic flag is parsed and stored in the TransientAnalysis object, but these paths silently ignore it, seeding the transient from a DC operating point instead of the declared ic= values. This produces incorrect simulation results.
Prompt for agents
The UIC feature was implemented in simulate() but not in the parallel code paths. The fix requires changes in multiple locations:
1. In simulateStream() (simulate.ts ~line 155-161): When analysis.uic is true, compute the UIC seed via computeUICInitialSolution(circuit, compiled, opts) instead of solveDCOperatingPoint. Pass the seed and seedIsUIC flag to streamTransient.
2. In streamTransient() (simulate.ts ~line 294-320): Add a seedIsUIC parameter and pass it through to createDriverFromCompiled in the config object.
3. In simulateStepStream() no-step path (simulate.ts ~line 207-215): Same pattern as simulateStream - check analysis.uic and compute UIC seed when needed.
4. In streamWithSteps() (simulate.ts ~line 257-266): This function only receives compiled (not circuit), so it needs circuit passed in as well to call computeUICInitialSolution. Alternatively, the UIC seed computation could be done in the callers.
5. In solveStep() (analysis/step.ts ~line 115-120): Same issue - needs access to circuit, needs to check analysis.uic, and needs to pass seedIsUIC to solveTransient.
The key challenge is that streamWithSteps and solveStep don't currently have access to the Circuit object, which is needed by computeUICInitialSolution. The function signatures need to be extended to accept the circuit parameter.
Was this helpful? React with 👍 or 👎 to provide feedback.
Closes #48.
Summary
K1 La Lb k, stamps-M = -k·√(L_a·L_b)into off-diagonals of the C matrix at the two inductor branch rows.ic=on capacitors and inductors..tran ... uic: builds a substituted DC system (caps-with-ic → V sources, inductors-with-ic → I sources, K dropped), solves it, and maps node voltages and branch currents back to the original layout. Inductor → I-source flips node order to match the inductor branch-current sign convention.2H,100Ohm,1uF) no longer throwParseError.Why this circuit is interesting
It's a deliberate cross-section of the parts of SPICE that diverge between simulators:
Mdepends on dot convention; a sign flip reverses the secondary's response.uicsemantics — without explicitic=, some simulators force V/I to 0, others let topology decide. With C2 (no ic) onx, C12 (ic=2) onx–y, and C3 (ic=5) ony, the constraints aren't simultaneously satisfiable, so different policies pick different starts.Test plan
pnpm -r lintcleanpnpm -r test— 405/405 core, 206/206 uimutual-inductor.test.ts: K parsing, K coupling alters dynamics, RC/RL UIC seed🤖 Generated with Claude Code