Skip to content

v0.2.0 — signal-domain enforcement, scripting blocks, measurements, streaming output

Choose a tag to compare

@Elvis-codeur Elvis-codeur released this 04 Sep 07:30
· 46 commits to main since this release

Second checkpoint release — 47 commits since v0.1.0. Educational Rust simulator for
piecewise-linear circuits and mixed circuit/block-diagram systems continues to grow its
signal-domain block vocabulary and output tooling, while fixing a real memory-exhaustion crash
found and closed out this same cycle. See README.md for the architecture rationale and
book/dev-guide/src/design-decisions.md for the reasoning behind each of these.

Highlights

  • Enforced physical/signal-domain converters, the breaking change v0.1.0 was tagged
    immediately before: kind=sig2phys domain=voltage|current (unified from separate
    sig2voltage/sig2current kinds) is the only legal way a signal-domain block drives a
    source's magnitude or an ideal switch's gate; kind=phys2sig node=<n>|branch=<n> (renamed
    from probe) is the only way a circuit quantity re-enters the signal domain — a real,
    netlist-level physical/signal boundary, not just a naming convention.
  • Two new scripting block families: kind=pyfunc/kind=pyblock (embedded Python via
    PyO3, gated behind an optional python feature) and kind=octfunc/kind=octblock
    (GNU Octave via subprocess — deliberately never linked, since Octave is GPLv3 and this
    project is MIT — "mere aggregation" only, empirically validated for performance: a
    persistent session vs. a fresh process per call is ~4500x faster).
  • kind=cscript gained a real state contract: optional continuous state (xc), an
    optional update()/next_sample_hit() split for block-controlled discrete sample times, a
    to= phase offset — the same contract pyblock/octblock share.
  • Vector-signal support (SignalValue::Vector) — kind=statespace is now genuinely MIMO,
    not scalar-only.
  • New block kinds: discrete-time state-space/transfer-function/PID, logic gates, SR
    latches, flip-flops, a counter, and signal-domain SIN/PULSE/EXP/SFFM sources.
  • kind=measure: ngspice/Xyce-style post-processing measurements (MAX/MIN/RMS/
    FOUR/TRIG-TARG/ERROR/...) over a completed transient trace, backed by the
    newly-extracted gs-waveform-measurements crate (now its own sibling repo, independent
    release cadence). Results write to a <netlist>.log file, matching real SPICE tools' own
    .measure convention — not stdout, not stderr.
  • --format raw: writes a genuine binary SPICE rawfile, cross-validated against two real
    Python readers (PySpice, spicelib) reading a file this CLI actually wrote.
  • Adaptive stepping now lands exactly on discrete/PWM edges instead of silently missing
    them once dt_max approaches a switching period (earliest_next_event_dt) — closes a real
    bug demonstrated on a genuine MATLAB-ported phase-shift-PWM modulator (0/100 → 100/100 edges
    correctly resolved at the previously-failing step sizes).
  • Streaming CSV/raw output: the transient engine no longer buffers an entire run in memory
    before printing/writing anything — fixes a real incident where a large, stiff circuit under
    adaptive stepping drove a development machine to ~11GB RSS/24GB swap before being killed; the
    same run now holds ~23MB RSS regardless of length. A new --max-steps flag caps runaway
    adaptive runs as a hard safety net.
  • Generated Component Reference: 36+ kind=/BlockKind components fully documented, each
    backed by a committed doc-verify/ proof folder (a real .cir fixture + a Python test that
    actually runs the CLI and checks the documented claim).
  • Renamed Mosfet/Diode PWL models to IdealSwitch/IdealDiode; pyblock-ffi/pyo3 gated
    behind an optional python Cargo feature for a python-free build to stay possible.

Breaking changes since v0.1.0: kind=sig2voltage/kind=sig2current/kind=probe/
kind=sig2gate/kind=mosfet/kind=diode are all gone, no compatibility aliases — see the
items above for their replacements.