velxio/test/test_circuit/src/solver/linalg.js

42 lines
1.1 KiB
JavaScript
Raw Normal View History

feat: electrical simulation via ngspice-WASM (eecircuit-engine) Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy- loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate with real analog behaviour: correct voltages on wires, real I–V curves on LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM driving RC filters, transistors, op-amps, diodes, MOSFETs, etc. Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false. Frontend additions: - simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with UnionFind over wires, componentToSpice mapping (24 metadataIds incl. real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148, 1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with debounced coalescing, AVRSpiceBridge for quasi-static co-simulation. - store/useElectricalStore: Zustand slice, feature-flag aware. - components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay. - components/components-instruments/: Voltmeter, Ammeter probes. - 62 tests (spice-*, netlist-builder, component-to-spice, instruments). Sandbox (test/test_circuit/): 47-test validation sandbox that proved the approach (hand-rolled MNA baseline + ngspice pipeline) before porting to the app. Kept as reference. Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering architecture, solvers, components, AVR bridge, gotchas, performance, integration plan, API reference, appendix) + electrical-simulation- user-guide.md (end-user facing). Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
// Gaussian elimination with partial pivoting for small dense systems.
// Ax = b where A is n×n and b is length n. Returns x (length n).
// Throws on singular matrices.
export function solveLinear(A, b) {
const n = b.length;
const M = A.map((row, i) => [...row, b[i]]);
for (let col = 0; col < n; col++) {
let pivot = col;
for (let r = col + 1; r < n; r++) {
if (Math.abs(M[r][col]) > Math.abs(M[pivot][col])) pivot = r;
}
if (Math.abs(M[pivot][col]) < 1e-14) {
throw new Error(`Singular matrix at column ${col}`);
}
if (pivot !== col) {
[M[col], M[pivot]] = [M[pivot], M[col]];
}
for (let r = col + 1; r < n; r++) {
const factor = M[r][col] / M[col][col];
for (let c = col; c <= n; c++) {
M[r][c] -= factor * M[col][c];
}
}
}
const x = new Array(n).fill(0);
for (let r = n - 1; r >= 0; r--) {
let sum = M[r][n];
for (let c = r + 1; c < n; c++) sum -= M[r][c] * x[c];
x[r] = sum / M[r][r];
}
return x;
}
export function zeros(n, m) {
const out = new Array(n);
for (let i = 0; i < n; i++) out[i] = new Array(m).fill(0);
return out;
}