PowerIO: A Fast Parser and Converter for Power System Case Files

I'm releasing PowerIO, a fast parser and converter for power system case files. If you've ever had to move a case from MATPOWER into PSS/E, or pull a PowerModels.jl network into pandapower, you've probably written the same kind of brittle, lossy script I have. Every tool speaks its own dialect, and the glue between them is almost always a one-off.

PowerIO is meant to be the pandoc of power system formats: read a case into one typed Network, write it back out to whatever format you need, and build the sparse matrices and graph views your solver wants along the way. The core is written in Rust, and that same engine drives the Python package, the command-line tool, a C ABI for C/C++/Julia and other FFI consumers, and PowerIO.jl. It's dual-licensed MIT/Apache-2.0.

Docs: LinkCode: Link

PowerIO parses case files into a typed network model, then converts between formats and builds matrices.

Why PowerIO?

The design is hub-and-spoke. Every format is one reader (text → Network) and one writer (Network → text), and a conversion is just NetworkNetwork rendered into the target schema. Adding a format touches one module instead of an N-by-N grid of pairwise converters, and everything downstream–-matrices, graphs, solvers–-sees one model.

Right now it reads and writes MATPOWER .m, PSS/E .raw (revisions 33–35), PowerWorld .aux, PowerModels.jl, egret, and pandapower JSON, PyPSA CSV folders, and GridFM Parquet. PowerWorld .pwb binary cases and GE PSLF .epc are currently read-only. Distribution networks live in a companion crate, powerio-dist, in wire coordinates: OpenDSS .dss, PowerModelsDistribution JSON, and the IEEE benchmark format.

Features

Quick example

import powerio as pio

case = pio.parse_file("case9.m")
print(case.n_buses, case.base_mva)        # 9 100.0

# Same-format write returns the retained original text, byte for byte
text = case.to_matpower()

# Convert, and see exactly what the target format can't represent
raw, warnings = pio.convert_file("case9.m", "psse")
for w in warnings:
    print("warning:", w)

# Matrices and graphs (with the [matrix]/[graph] extras installed)
B = case.bprime()        # scipy.sparse (also ybus, ptdf, lodf)
G = case.to_networkx()   # networkx graph

From the command line

# MATPOWER -> PSS/E
powerio convert case14.m --to psse -o case14.raw

# GE PSLF -> MATPOWER
powerio convert case.epc --from pslf --to matpower -o case.m

# Build a DC OPF bundle, or PTDF/LODF sensitivities
powerio dcopf       case30.m -o out
powerio sensitivities case30.m -o out

Performance

The priorities were correctness and predictable failure, but the Rust core is also fast: on large synthetic cases (~100k–200k buses), PowerIO parses at the same speed as ExaPowerIO.jl, whose performance work set the bar for MATPOWER parsing. Benchmarks with methodology, along with the validation suite that checks PowerIO against several reference implementations, are in the repository.

Install

cargo add powerio          # Rust

pip install powerio        # Python (parsing/conversion, no runtime deps)
pip install 'powerio[all]' # add SciPy/NumPy matrices and NetworkX graphs

Julia bindings are at PowerIO.jl.

PowerIO is a team effort in the Eigenergy group, and it leans on a lot of careful prior work documenting these formats. If you hit a format quirk or an edge case, please open an issue on GitHub.