Skip to main content

A fast Rust-backed reader for Cadence binary PSF files, with a clean numpy API

Project description

psfox

A fast Rust-backed reader for Cadence binary PSF files, with a clean numpy API.

The Cadence psf-parser (pure Python) is slow and memory-hungry — it OOM-crashes on large files because it stores every sample as an individual Python object. psfox is a Rust core (PyO3 + maturin) that reads the same files much faster and with low RAM, returning numpy arrays directly.

Performance

Measured against the pinned pure-Python psf-parser (each parser in a fresh subprocess, median of repeated runs):

Dataset Size psfox psf-parser speedup psfox peak RSS
noise (struct-typed) ~634 MB ~1.2 s ~60 s ~49× ~1.3 GB
AC (complex) ~133 MB ~0.34 s ~17 s ~51× ~0.27 GB

Small files (S-parameter, DC) parse in single-digit milliseconds (tens of times faster). The output is bit-identical to psf-parser (exact, NaN/Inf-aware) on every tested file, and the memory ceiling is low enough to parse a 600 MB+ file on a 4 GB machine without OOM.

What it reads

psfbin 1.1, big-endian: swept and non-swept analyses; real and complex vectors; and struct/composite types → numpy structured arrays. Common analysis types (noise, AC, DC, S-parameter) are supported. Out of scope (for now): PSF ASCII, PSF-XL/transient, and multi-sweep / family / corner files.

Requirements

  • Python ≥ 3.9
  • numpy ≥ 1.23 — a runtime dependency (the reader returns numpy arrays). It is declared in the package metadata, so installing psfox pulls numpy in automatically.

Install

Not on PyPI yet. Once published, the install is simply:

uv add psfox      # or: pip install psfox

Until then, install from a prebuilt wheel (no Rust toolchain needed). Build it once with maturin:

maturin build --release
# -> target/wheels/psfox-0.1.0-cp39-abi3-<platform>.whl

Then install that wheel into any Python ≥ 3.9 environment (numpy is pulled in automatically):

pip install psfox-0.1.0-cp39-abi3-<platform>.whl

Notes:

  • The wheel is abi3 (cp39-abi3): one wheel works for Python 3.9, 3.10, … 3.13+ — no rebuild per Python version.
  • A wheel is platform-specific (Linux / macOS / Windows × architecture). A wheel built on one OS will not install on another; build it on the target platform, cross-build a Linux wheel from any host (see below), or wait for a public cross-platform release.

To build from source instead (needs the Rust toolchain), see CONTRIBUTING.md in the repository.

Cross-building a Linux wheel

psfox has no system C dependencies — the only native code is the Rust core, built as an abi3 extension — so a manylinux wheel can be cross-compiled from any host (Windows, macOS, Linux) using zig as the linker. No Docker or virtual machine required:

rustup target add x86_64-unknown-linux-gnu   # one-time: add the Linux target
pip install ziglang                           # the zig toolchain, shipped as a Python package
maturin build --release --target x86_64-unknown-linux-gnu --zig --compatibility manylinux2014
# -> target/wheels/psfox-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

The manylinux2014 tag links against an old glibc, so the wheel installs on essentially any modern Linux distribution.

On Windows, maturin may not auto-detect the zig that ships inside the ziglang package (it fails with Failed to find zig). If so, put that package's folder on PATH for the build:

$env:PATH = "$PWD\.venv\Lib\site-packages\ziglang;$env:PATH"

Because the wheel is cross-compiled, smoke-test import psfox on an actual Linux host (or WSL) — it cannot run on the machine that built it.

Usage

import psfox

psf = psfox.read_psf("path/to/data.sp")
print(psf)                         # <Psf sweeps=1 traces=16 values=0>
print(psf.header["PSFversion"])    # header is a plain dict

# Sweep axes and traces are numpy arrays
for sw in psf.sweeps:
    print(sw.name, sw.data.dtype, sw.data.shape)   # e.g. freq float64 (700,)

t = psf.traces[0]
print(t.name, t.data.dtype, t.data.shape)          # e.g. s11 complex128 (700,)

# Non-swept DC -> .values (Python scalars), no traces/sweeps
dc = psfox.read_psf("path/to/data.dc")
print(len(dc.values), dc.values[0].name, dc.values[0].data)   # e.g. 5349 'net1' -7.7e-04

# Struct traces -> numpy structured array, one float64 field per contribution
noise = psfox.read_psf("path/to/data.noise")
tr = noise.traces[0]
if tr.data.dtype.names is not None:                # struct vs flat discriminator
    print(tr.data.dtype.names[:3])                 # e.g. ('<dev>.thermal', '<dev>.shot', ...)
    print(tr.data[tr.data.dtype.names[0]])         # float64[N] for that contribution

Run a script with the project environment: uv run python your_script.py (or activate .venv).

API

Object Attributes
read_psf(path) -> Psf parse a psfbin file
Psf .header (dict), .sweeps (list[Sweep]), .traces (list[Trace]), .values (list[Value])
Sweep, Trace .name (str), .data (numpy array)
Value .name (str), .data (Python scalar)
psfox.__version__ version string
  • flat vs struct trace: trace.data.dtype.names is None → flat (float64[N] / complex128[N]); not None → struct (one named float64 field per contribution).
  • swept vs non-swept: swept analyses populate .traces (+ .sweeps); non-swept DC populates .values.
  • real vs complex is inferred per trace from the file, not assumed file-wide.

Type stubs and a py.typed marker ship with the package, so editors give autocomplete and type checking; help(psfox.read_psf) shows the inline docstrings.

Distribution

psfox is built with maturin + PyO3 — the same stack as polars and pydantic-core — and is distributed as a Python wheel (.whl). The remaining step toward a pip install psfox that "just works" everywhere is a CI-built wheel matrix + a PyPI publish. Thanks to abi3, that matrix is one wheel per platform rather than one per platform × Python version.

Contributing

Build, test, and benchmark instructions live in CONTRIBUTING.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

psfox-0.1.0.tar.gz (51.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

psfox-0.1.0-cp39-abi3-win_amd64.whl (166.7 kB view details)

Uploaded CPython 3.9+Windows x86-64

psfox-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (280.8 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

File details

Details for the file psfox-0.1.0.tar.gz.

File metadata

  • Download URL: psfox-0.1.0.tar.gz
  • Upload date:
  • Size: 51.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for psfox-0.1.0.tar.gz
Algorithm Hash digest
SHA256 51a969bf8fad368ffe12a19b0c2b704e29c8b996818d1b4433efa20e15aaf434
MD5 f45653a1f6a6ca8fc3eed5f4d645d784
BLAKE2b-256 c8c379d4d3945c887c87bd008418f6516f5a71ee478f068878ac733cf3e5b006

See more details on using hashes here.

Provenance

The following attestation bundles were made for psfox-0.1.0.tar.gz:

Publisher: publish.yml on Zwelckovich/psfox

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file psfox-0.1.0-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: psfox-0.1.0-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 166.7 kB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for psfox-0.1.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 698e2f2bda8cf03782c431bb0d7d036df198df08e40a7b8a4445008077d4929a
MD5 bb5bcb97634c62a7ec43ad0bb9d8220f
BLAKE2b-256 42f53e85d078324680428edb546ec0612ad190099710ee61a1fa8a7d2ccbc882

See more details on using hashes here.

Provenance

The following attestation bundles were made for psfox-0.1.0-cp39-abi3-win_amd64.whl:

Publisher: publish.yml on Zwelckovich/psfox

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file psfox-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for psfox-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 bc279df8a124adf55fa6eb1f517e6fc3eecac6338b680f9f7630b2625a47c8d4
MD5 7bd8b6dd8bf49f107d946dab74a05324
BLAKE2b-256 f8952d500617687727fabc68a17a15cdb6fa08ace3dfb3f17979e71c3750c008

See more details on using hashes here.

Provenance

The following attestation bundles were made for psfox-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on Zwelckovich/psfox

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page