gridoxide ships as a pip-installable package exposing the solver to Python:
pip install gridoxidePrebuilt wheels are published for Linux (x86_64), Windows, and macOS (arm64).
import gridoxide
model = gridoxide.PowerFlowModel.from_pgm_json("grid.json", backend="klu_native")
model.solve()
print(model.voltage_mag()) # per-unit magnitude, one entry per node
print(model.voltage_ang()) # angle in radians, one entry per nodeGrids are loaded from power-grid-model (PGM)
JSON input files. python/README.md in the repository is the package's own PyPI landing page and
carries a full worked example of that input format.
PowerFlowModel.from_pgm_json(path, backend="scalar", tol=1e-6, max_iter=20, s_base_va=1e6, freq_hz=50.0)— loads a PGM JSON file and builds the Y-bus admittance matrix.model.n_nodes— number of buses, including one virtual slack bus per activesource.model.solve()— runs Newton-Raphson from a flat/linear-initial-guess start; raisesRuntimeErrorif it doesn't converge withinmax_iteriterations.model.reset()— discards the cached symbolic factorization; call before the nextsolve()if the topology has changed.model.voltage_mag()/model.voltage_ang()— per-bus results in node order.
PowerFlowModel wraps the Rust solver::PersistentSolver — it is that API, not a reimplementation
of it — so repeated .solve() calls on one model reuse the cached symbolic factorization exactly as
described in Backends and Factorization Reuse. Construct one model per
topology, then solve as many times as needed:
model = gridoxide.PowerFlowModel.from_pgm_json("grid.json", backend="scalar")
for scenario in scenarios:
apply_scenario(model, scenario) # changes p/q values only
model.solve()
results.append(model.voltage_mag())Call model.reset() if the topology itself changes between solves, not just bus values.
This is what lets the benchmark suite run its whole comparison in pure Python
(scripts/bench/bench_gridoxide_native.py), timing gridoxide with the same
time.perf_counter()-around-a-persistent-solve-object methodology every other tool there already
uses (PGM's PowerGridModel, lightsim2grid's GridModel, pandapower's net), rather than shelling
out to a compiled Rust binary and parsing its stdout.
| Backend | Notes |
|---|---|
"scalar" (default) |
Sparse LU via faer, no special build requirements. |
"block" |
Block-structured variant (one 2×2 block per bus); faster on some topologies. |
"klu_native" |
From-scratch Rust translation of SuiteSparse KLU, always available in the wheel. |
Two further backends exist in the source tree but are not in the published wheel, since they need extra system dependencies at build time — build from source with the matching Cargo feature:
"klu"— links vendored SuiteSparse C directly (--features python,klu)."pardiso"— Intel oneMKL's PARDISO solver (--features python,pardiso, needsMKLROOTset).
See Backends and Factorization Reuse for what each one actually does and how they compare.
Two helpers produce or convert PGM JSON, so a working grid doesn't require hand-writing one. Both
ship in the pip package itself (they used to live only under scripts/bench/) and are installed as
console scripts:
-
gridoxide.generate_grid— synthetic radial MV/LV distribution grid generator at any scale, pure stdlib, no extra dependencies:from gridoxide.generate_grid import generate generate(target_nodes=2200, seed=42, out_path="grid.json") # ~2,600 nodes
or
gridoxide-generate-grid grid.json --target-nodes 2200 --seed 42. -
gridoxide.matpower(needspip install gridoxide[matpower]) — converts a raw MATPOWER.m/.matcase into PGM JSON:from gridoxide.matpower import convert convert("case14.m", "case14.json")
or
gridoxide-matpower case14.m case14.json.
scripts/bench/generate_grid.py and matpower_to_pgm.py are thin CLI wrappers delegating to these
same package modules, so the conversion logic only lives in one place.
There is deliberately no pandapower-based converter: it would pull in the full pandapower +
power-grid-model-io dependency chain, and gridoxide.matpower already covers the same real-world
test-case grids straight from their original MATPOWER sources. If you already have a
pandapower.pandapowerNet and pandapower installed, scripts/bench/convert_pandapower_case.py in
the main repo is a standalone (not packaged) converter.
src/python.rs exposes PersistentSolver and PGM-JSON loading as gridoxide._gridoxide, a private
compiled extension module built with maturin:
maturin develop --release --features python,kluIt is gated entirely behind the opt-in python Cargo feature and compiled by nothing else, so a
plain cargo build/cargo test never touches it — the feature must never be combined with a plain
cargo invocation.
This is a mixed Rust/Python maturin project (pyproject.toml's python-source = "python" plus
module-name = "gridoxide._gridoxide"): pure-Python code lives in python/gridoxide/ and ships in
the same wheel as the compiled extension, re-exported through python/gridoxide/__init__.py so
callers only ever write import gridoxide.
python/tests/ holds a pytest suite (scalar/block only — no klu, matching what's published)
checked against this project's own committed PGM reference fixtures, run by
.github/workflows/python.yml on every push/PR. .github/workflows/pypi.yml builds wheels
(Linux/Windows/macOS) plus an sdist and publishes to PyPI via
trusted publishing on v* tags. The published wheel
deliberately omits the klu backend — LGPL-2.1-or-later vendored SuiteSparse source, plus a C
compiler and libclang needed on every target platform. See
Provenance and Licensing.