Skip to content

About

Pure-Python tooling for general-simulator: ergonomic schematic building over gsim_py, result parsing, and waveform measurement.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

gsim-tools

Build circuit and control schematics in Python, compile them to netlists, run them, and measure the waveforms that come back.

from gsim_tools.schematic import Schematic

sch = Schematic("rc", "RC low-pass")
vin = sch.add("source.voltage.dc", id="Vin", at=(0, 0), V=5.0)
r1 = sch.add("passive.resistor", id="R1", at=(60, 0), R=1000.0)
c1 = sch.add("passive.capacitor", id="C1", at=(120, 0), C=1e-6)
gnd = sch.add("routing.ground", id="gnd", at=(60, 80))
sch.connect(vin.p, r1.p1)
sch.connect(r1.p2, c1.p1)
sch.connect(c1.p2, gnd.p)
sch.connect(vin.n, gnd.p)

print(sch.compile())
* RC low-pass -- generated by gsim-core 0.1.0 from model 'rc'
Vin n_R1_p1 0 5
R1 n_R1_p1 n_C1_p1 1000
C1 n_C1_p1 0 1e-6

A schematic is an object you construct, wire up and validate, not a file you draw. Validation happens as you build: a wire to a port that does not exist, a signal port fed by two sources, a connection across incompatible paradigms or dimensions — each raises its own exception, at the line that caused it, rather than producing a netlist that fails later for a reason you have to work backwards from.

The modules

  • gsim_tools.schematic — build, validate and compile schematics. The main surface, and the reason this package exists.
  • gsim_tools.results — read simulation output back, from either format that exists: general-simulator's own CSV (including its streaming transient output) and kernel-sidecar's SIMULATE_RESULT manifest directories.
  • gsim_tools.measure — RMS, THD, average power, peak, mean, frequency and duty cycle, threshold crossings, error norms — over non-uniformly sampled waveforms, which is what a variable-step solver actually produces. A port of the gs-waveform-measurements Rust crate that general-simulator's own kind=measure blocks are built on, and cross-validated against it in tests/test_measure_parity.py.
  • gsim_tools.logs — structured parsing of general-simulator's error: diagnostics and its kind=measure log files.

Install

pip install gsim-tools              # results + measure + logs: stdlib only, no toolchain
pip install 'gsim-tools[sidecar]'   # adds numpy/h5py for kernel-sidecar manifests

Getting gsim_py, which schematic needs

The document model, the validation rules and the compile registry live in gsim-core — one Rust implementation behind the schematic editor, this library and the engine, instead of three copies that drift apart. gsim_tools.schematic is the Python layer over it, so it needs the gsim_py extension from the moment you construct a Schematic, not only to compile.

Until gsim-py publishes wheels, build it once into the same environment:

git clone https://github.com/GLODI-Engineering/gsim-core
cd gsim-core/crates/gsim-py
pip install maturin
maturin build --release
pip install ../../target/wheels/gsim_py-*.whl

Without it, every entry point in schematic raises GsimPyNotAvailable carrying these instructions. The other three modules are unaffected and need no toolchain at all.

Example

Build a schematic, run it through the real simulator, measure the result:

from gsim_tools import measure, results
from gsim_tools.schematic import Schematic

sch = Schematic("rc", "RC low-pass")
vin = sch.add("source.voltage.dc", id="Vin", at=(0, 0), V=5.0)
r1 = sch.add("passive.resistor", id="R1", at=(60, 0), R=1000.0)
c1 = sch.add("passive.capacitor", id="C1", at=(120, 0), C=1e-6)
gnd = sch.add("routing.ground", id="gnd", at=(60, 80))
sch.connect(vin.p, r1.p1)
sch.connect(r1.p2, c1.p1)
sch.connect(c1.p2, gnd.p)
sch.connect(vin.n, gnd.p)

run = results.run_netlist_text(sch.compile(), mode="transient", tfinal=0.02, dt=1e-5)
print(measure.find_at(run.trace.samples("V(out)"), 1e-3))

Layout

Coordinates need not be hand-picked. Schematic wraps all three of gsim_py's layout entry points:

sch.auto_arrange()                  # layer the whole model over *all* its wires, in place
pos = sch.layered_layout(           # the same layering, but it hands the positions back
    bboxes={"IM": (80, 88)},        # real sizes where you know them
    skip_wires=[feedback.id],       # feedback makes cycles; layer the forward path only
    column_gap=90, row_gap=70,
)
sch.place_without_overlap("VSI", (900, 200))    # drop one block in near a point

layered_layout is a longest-path (Sugiyama-style) layering via Kahn's algorithm: columns run left-to-right by signal-flow depth, blocks stack down within a column. It guarantees reading order and no overlap — it does not minimise wire crossings, and cycle members stay wherever the layering stalled, so a feedback loop is usually worth excluding via skip_wires.

Or, with no gsim_py anywhere, just read and measure an existing run:

trace = results.read_csv_file("run.csv")
v = trace.samples("V(a)")
print(measure.rms(v), measure.thd(v, fundamental_freq=50.0, num_harmonics=5))

Tests

python -m pytest tests -q     # pure-Python suite; needs neither gsim_py nor the simulator

Tests marked simulator additionally need the general-simulator binary (on PATH or at $GSIM_SIMULATOR); tests marked gsim_py need the extension built as above. Both are skipped when unavailable. examples/rc_lowpass_end_to_end.py runs the whole pipeline and asserts a known-correct numeric answer; see its own header for how to run it.

Licence

MIT — see LICENSE.

gsim-core, whose gsim_py extension gsim_tools.schematic is built on, is MIT too. The schematic editor and the UI-driving library in the same family are AGPL-3.0; this package and the core are deliberately permissive so they can be embedded.

About

Pure-Python tooling for general-simulator: ergonomic schematic building over gsim_py, result parsing, and waveform measurement.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages