snp2le: S-Parameter To Lumped Element Netlist Converter
(c) 2026 Simon Dorrer
Institute for Integrated Circuits and Quantum Computing (IICQC), Johannes Kepler University (JKU), Linz, Austria
[!IMPORTANT] The converter (GUI and CLI) runs anywhere with Python ≥ 3.10, see Install below. Running the exported netlists in a testbench additionally needs Xschem plus Ngspice and/or VACASK. The easiest way to get all of them is the IIC-OSIC-TOOLS container. Since tag
2026.07,snp2lehas been installed directly in the IIC-OSIC-TOOLS container.
Description
snp2le turns a Touchstone .sNp S-parameter file (for example from an AWS Palace EM simulation) into an equivalent lumped-element netlist for Ngspice (Berkeley SPICE3) and VACASK (Spectre syntax). An EM-extracted structure can then be co-simulated at circuit level, without re-running the field solve.
It offers two conversion philosophies:
- Universal (any N-port). Vector-fits the S-parameters with scikit-rf
VectorFitting, optionally enforces passivity, and synthesises a passive macromodel of R, C and controlled sources. It works for any structure and port count, and is electrically exact but not physically interpretable. - Structure-specific. Fits a known physical topology, so every component maps to reality (series L, shunt C, coupling k, and so on) at a chosen extraction frequency. See Available structures.
A single dialect-agnostic Circuit IR drives both netlist backends and the on-screen schematic, so the outputs always agree. The code is split into a pure-Python, Qt-free snp2le.core (fully unit-tested) and a thin snp2le.gui on PySide6-Essentials, both driven by one entry point, engine.convert(state, net).
The snp2le GUI converting a band-pass filter (BPF) S-parameter file into a lumped-element netlist.
Plot view: loaded data (grey) vs extracted model (blue) vs imported testbench simulation (red).
Directory Structure
📁 snp2le/
├─ 📁 doc/ architecture notes and screenshots
│ ├─ 📁 fig/ GUI and plot screenshots
│ └─ architecture.md data flow, internals, how to extend
├─ 📁 netlist/ exported lumped-element netlists
│ ├─ 📁 spectre/ VACASK (.inc) + syntax_cheatsheet.inc
│ └─ 📁 spice/ Ngspice (.spice)
├─ 📁 schematic/
│ └─ 📁 xschem/ DUT symbols (*.sym) and xschemrc
├─ 📁 snp2le/ the application package (pip-installable)
│ ├─ 📁 core/ pure Python, Qt-free, all the maths
│ │ ├─ 📁 structures/ physical extractors, one per topology
│ │ │ ├─ __init__.py registry (GUI dropdown + CLI find it)
│ │ │ ├─ base.py
│ │ │ ├─ balun.py
│ │ │ ├─ branchline.py
│ │ │ ├─ inductor_pi.py
│ │ │ ├─ mim_cap.py
│ │ │ ├─ tline.py
│ │ │ └─ wilkinson.py
│ │ ├─ __init__.py
│ │ ├─ dc.py DC operating-point (singularity) check
│ │ ├─ engine.py convert(state, net) -> Results, the entry point
│ │ ├─ io.py load Touchstone, parse Ngspice tables
│ │ ├─ ir.py dialect-agnostic Circuit IR
│ │ ├─ mna.py rebuild N-port S-parameters from an RLC IR
│ │ ├─ netlist.py render the IR to Ngspice and VACASK
│ │ ├─ state.py ConverterState and Results dataclasses
│ │ ├─ units.py engineering-notation parse and format
│ │ ├─ universal.py vector-fit passive macromodel
│ │ └─ xschem.py headless Xschem netlist and simulate
│ ├─ 📁 examples/ Touchstone .sNp samples (BPF, ind, balun, ...)
│ ├─ 📁 gui/ PySide6-Essentials, no maths
│ │ ├─ 📁 assets/ logos (svg and png), snp2le.ico
│ │ ├─ __init__.py
│ │ ├─ design_view.py results, values, tolerances, schematic
│ │ ├─ main_window.py the controller
│ │ ├─ plot_view.py four S-parameter / extracted-param plots
│ │ ├─ top_bar.py load, mode, structure, options, run
│ │ └─ ... help_dialog.py, style.py, widgets.py, and more
│ ├─ __init__.py package version
│ ├─ __main__.py single entry point (GUI, or -b for the CLI)
│ ├─ app.py the GUI launcher (__main__ starts it)
│ └─ cli.py the batch CLI behind -b
├─ 📁 testbenches/
│ └─ 📁 xschem/ N-port testbenches (Ngspice and VACASK)
│ ├─ 📁 plot_simulations/ plot scripts (plot_*.py, sparam_plot.py, ngspice2python.py)
│ │ ├─ 📁 data/ simulation result tables, overlaid on the plots
│ │ └─ 📁 figures/ PNG figures written by the plot scripts
│ └─ 📁 simulations/ generated netlists and raw output (not tracked)
├─ 📁 tests/ pytest suite
│ ├─ test_core.py
│ ├─ test_gui_sim_flow.py headless GUI run/poll/import regressions
│ ├─ test_qt_essentials.py guards the Essentials-only dependency
│ └─ test_xschem.py
├─ 📁 LICENSES/ license texts the REUSE check resolves against
│ └─ Apache-2.0.txt
├─ 📁 .github/workflows/ CI
│ └─ license-check.yml reuse lint: every file carries copyright + license
├─ CITATION.cff
├─ LICENSE Apache-2.0
├─ MANIFEST.in sdist manifest (bundles examples and assets)
├─ pyproject.toml packaging, dependencies, snp2le entry point
├─ README.md
├─ REUSE.toml licensing of files that cannot carry an SPDX header
└─ requirements.txt runtime dependencies (mirrors pyproject.toml)
How to Use
Install
From PyPI:
pip install snp2le
# or, for an isolated install with its own command on PATH:
pipx install snp2le
From source (for development), an editable install pulls in every dependency:
git clone https://github.com/iic-jku/snp2le.git
cd snp2le
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS / Linux: source .venv/bin/activate
pip install -e .
Run the GUI
snp2le # after installing (pip / pipx)
python -m snp2le # from the repo root of a source checkout, no install needed
A bundled example is preloaded on first run. More live in snp2le/examples/.
[!NOTE] Start it as a module (
python -m snp2le), notpython snp2le/app.py. The launcher imports thesnp2lepackage, which Python only finds when it is run as a module from the repo root (or afterpip install).
Typical workflow
- Load a Touchstone
.sNpfile from the top bar. The header shows the port count and frequency range. - Choose a mode. Universal (set Max order and Enforce passivity) or Structure-specific (pick a structure and set the extraction frequency). Some structures expose an extra option such as Stages, Isolation R or Resistive loss.
- Inspect the result, element values, per-element tolerances at the extraction frequency, the drawn schematic, and the generated netlist in the Design & Schematic view.
- Compare the loaded data (grey) against the extracted model (blue) in the Plot view (up to four traces, magnitude and phase).
- Export the netlist. Export Ngspice writes a
.spicefile and Export VACASK writes an.incfile. The.SUBCKTis named after the file, so a testbench that instantiates it resolves the include.
[!TIP] The Help button in the top bar opens a full in-app guide to every control.
Run a testbench (simulate)
Drop the exported subcircuit into an Xschem testbench, then run it from the GUI:
- Load .sch. Pick the testbench. The Simulator auto-selects from the file name (a name containing
vacaskselects VACASK, any other name selects Ngspice) and can be overridden. - Run Simulation. Both simulators netlist and simulate through Xschem and write their result table to
plot_simulations/data/, which is imported and overlaid on the plots automatically. The button turns green on success or red on failure. On failure the dialog shows the simulator log. - Show output. Tick it to show the simulator's console and plot windows. Leave it unticked to run quietly. The result is imported either way.
[!NOTE] A simulator (Xschem plus Ngspice and/or VACASK) is only needed for this step. The conversion and export themselves are pure Python.
View testbench results
The testbenches follow the plot_simulations structure of the ihp-sg13g2-ams-chip-template: every testbench exports its result table to testbenches/xschem/plot_simulations/data/, and the plot scripts next to it write their PNG figures to testbenches/xschem/plot_simulations/figures/.
plot_n_port_tb_acsp_vacask.pyruns automatically as the VACASK postprocess step of every*_tb_acsp_vacask.schrun: it writes both the result table (data/<testbench>.txt, the same column naming the Ngspice testbenches use) and the figure (figures/<testbench>.png).plot_n_port_tb_acsp_ngspice.pyreproduces the.controlblocks' plots from the exported Ngspicewrdatatables with matplotlib, magnitude and phase over frequency, one figure per testbench. Run it after a quiet Ngspice run (where theplotcommands are suppressed).ngspice2python.pyis the helper module that loads thewrdatacolumns (the same helper the ihp-sg13g2-ams-chip-template plotting scripts use).sparam_plot.pyholds the figure layout both plot scripts draw through, so the Ngspice and VACASK results are directly comparable. An N-port testbench has N x N S-parameters, which is unreadable in a single pair of axes, so the figure is split by excitation port: one column of axes per driven port j, magnitude on top and phase below, leaving only N traces per panel. The color encodes the receiving port i and is the same in every panel, so one legend serves the whole figure.
One script serves every port count: it discovers the exported vectors from the table header (Ngspice) or the s(i,j) vector names (VACASK). Without an argument every *_tb_acsp_ngspice table in data/ is plotted; with a testbench name only that one:
python3 testbenches/xschem/plot_simulations/plot_n_port_tb_acsp_ngspice.py
python3 testbenches/xschem/plot_simulations/plot_n_port_tb_acsp_ngspice.py two_port_tb_acsp_ngspice
The plot windows open when a display is available; headless, only the PNGs are written.
Run the tests
pytest # from the repo root
CLI Overview
The same engine is available headlessly for Makefiles and batch use, through the -b (batch) flag:
snp2le -b list-structures
snp2le -b convert <file.sNp> [options]
From a source checkout without installing, use python -m snp2le -b ... in place of snp2le -b.
convert options
| Option | Scope | Description |
|---|---|---|
inputs |
all | one or more .sNp files or globs |
--mode universal|structure |
both | conversion philosophy (default universal) |
--structure KEY |
structure | structure key (see list-structures) |
--order N |
universal | maximum model order (poles) |
--passive / --no-passive |
universal | enforce passivity (default on) |
--fext FREQ |
structure | extraction frequency, e.g. 7GHz |
--stages N |
structure | RLGC ladder cells (transmission line) |
--iso-r / --no-iso-r |
structure | Wilkinson isolation R or branch-line arm loss |
--format ngspice|vacask|both |
both | output dialect(s). VACASK writes .inc |
-o, --output PATH |
both | output path (single input), names the .SUBCKT |
--values |
structure | print the extracted element values |
--tolerances |
structure | print per-element tolerances at f_ext |
--simulate SCH |
sim | run an Xschem testbench after converting |
--simulator ngspice|vacask |
sim | simulator for --simulate (default: auto from .sch name) |
--show-output |
sim | show the simulator's console and plot windows |
--timeout S |
sim | seconds to wait for a --simulate result (default 180) |
--plot [SPARAMS] |
both | display data-vs-model plots, plus the sim overlay after --simulate (e.g. S11,S21) |
--quiet |
both | suppress the per-file status line |
Examples
# universal macromodel to an Ngspice netlist
snp2le -b convert coupler.s4p --mode universal --order 12 -o coupler.spice
# structure extraction at 7 GHz, both dialects, print values and tolerances
snp2le -b convert ind.s2p --mode structure --structure inductor-pi \
--fext 7GHz --format both --values --tolerances
# convert the BPF, run the 2-port Xschem testbench, and show data vs model vs sim plots
snp2le -b convert snp2le/examples/bpf_ihp-sg13g2.s2p \
--mode universal --order 13 -o netlist/spice/two_port.spice \
--simulate testbenches/xschem/two_port_tb_acsp_ngspice.sch --plot
[!NOTE]
--simulateneeds Xschem and--plotneeds a display. If Xschem is not onPATH,--simulateprints a clear message and the run exits non-zero.
Available structures
| Key | Model | Ports | Notes |
|---|---|---|---|
inductor-pi |
Inductor | 2 | series R-L plus shunt C/R per port |
mim-cap |
MIM capacitor | 2 | series C with parasitic L/R plus shunt C (use it for MOM caps too) |
tline-rlgc |
Tline (RLGC) | 2 | transmission line as an N-cell ladder of L-cells (--stages) |
wilkinson-inphase |
Wilkinson (in-phase) | 3 | optional isolation resistor (--iso-r) |
wilkinson |
Wilkinson (quadrature) | 3 | quadrature (90 deg) outputs |
balun |
Balun (transformer) | 4 | coupled inductors (k, M, n), Qp and Qs |
branchline |
Branch-line coupler | 4 | optional fitted arm loss (--iso-r) |
New structures plug in by subclassing snp2le.core.structures.base.Structure and registering them in snp2le/core/structures/__init__.py. They then appear in the GUI dropdown and the CLI automatically.
Cite This Work
@misc{2026_snp2le,
author = {Dorrer, Simon},
month = july,
year = {2026},
title = {{GitHub Repository for snp2le: A S-Parameter To Lumped Element Netlist Converter}},
url = {https://github.com/iic-jku/snp2le},
doi = {10.5281/zenodo.21189545}
}
Acknowledgements
- The structure-specific extractors (inductor, MIM capacitor, RLGC line) were inspired by Volker Mühlhaus' lumpedmodel.
- The passivity-enforcement strategy for the universal macromodel was adapted from the COBRA project.
- Vector fitting is provided by scikit-rf.
License
Licensed under the Apache License 2.0, see LICENSE.
The repository is REUSE compliant: every file carries SPDX-FileCopyrightText and SPDX-License-Identifier tags, either inline (source files) or through REUSE.toml for files that cannot hold a header (configs, schematics and symbols, generated netlists, example Touchstone data, figures and result tables). The License Check workflow runs reuse lint on every push and pull request to main, so a new file without licensing information fails CI. Check it locally with:
pip install 'reuse[charset-normalizer]'
reuse lint
When adding a source file, start it with:
# SPDX-FileCopyrightText: 2026 Simon Dorrer
# SPDX-License-Identifier: Apache-2.0
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file snp2le-0.1.7.tar.gz.
File metadata
- Download URL: snp2le-0.1.7.tar.gz
- Upload date:
- Size: 342.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
26c89562b06c95cdfae82882f317c46625f96f0fb78861dddb768e10ad92df44
|
|
| MD5 |
1db6e3420467ba59e9fbfb121d63769b
|
|
| BLAKE2b-256 |
d549700d69a5e680df8363e359198374a2b9023db525ecb7a16c3feecf2efea4
|
File details
Details for the file snp2le-0.1.7-py3-none-any.whl.
File metadata
- Download URL: snp2le-0.1.7-py3-none-any.whl
- Upload date:
- Size: 345.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4836cff9f89b61b36ed039d7a10f6fb13202a72acfcb99af13ecbbc7d494b437
|
|
| MD5 |
05e17904a30de04f211bcb532817ac87
|
|
| BLAKE2b-256 |
17660acdd9becb56c0e049ff98c48393c43e942c1084f02e34b5e0e98e4288de
|