cimoxide
Python bindings for cimoxide, a Rust toolkit for ENTSO-E CGMES (Common Grid Model Exchange Standard) power system data. This package wraps the Rust decoder and SHACL/SPARQL validator (via PyO3) to give you fast RDF/XML parsing and CGMES conformance validation from Python, with no Rust toolchain required at install time.
Install
pip install cimoxide
Prebuilt wheels are published for common platforms; if none match your environment, pip
will need a Rust toolchain and maturin to build from source.
Quick start
import cimoxide
# Parse one or more CGMES RDF/XML files into a single merged dataset.
ds = cimoxide.decode_files([
"RealGrid_EQ.xml",
"RealGrid_SSH.xml",
"RealGrid_TP.xml",
"RealGrid_SV.xml",
])
len(ds) # total number of elements
ds.by_type() # {"ACLineSegment": [mrid, ...], ...} — copies the whole index
ds.count_type("ACLineSegment") # 7561 — O(1), copies nothing
ds.get_type("ACLineSegment") # [{"_type": "ACLineSegment", "r": 0.12, ...}, ...]
for mrid in ds:
obj = ds[mrid] # dict, e.g. {"_type": "BusbarSection", "name": "...", ...}
Each element is a plain Python dict with a "_type" key (the CIM class name) plus one
key per populated attribute, snake_case, matching the JSON serialization of the underlying
Rust structs. Reference fields (MRID associations) are plain MRID strings.
Modify and re-encode
CimDataset supports dict-style assignment and deletion, so you can edit elements in place
and write the result back out as CGMES profile XML:
# Edit an existing element (read, mutate the dict, assign it back).
line = ds["ACLineSegment.1"]
line["r"] = 0.15
ds["ACLineSegment.1"] = line
# Add a brand-new element the same way — the "_type" key selects the CIM class.
ds["BaseVoltage.NEW"] = {"_type": "BaseVoltage", "id": "BaseVoltage.NEW", "nominal_voltage": 110.0}
# Remove one.
del ds["ACLineSegment.2"]
# Encode a single profile as an RDF/XML string.
eq_xml = ds.to_xml_for_profile("EQ")
# Or write a full profile set straight to a directory: dir/EQ.xml, dir/SSH.xml, ...
ds.write_xml_files("out/", ["EQ", "SSH", "TP", "SV"])
to_xml_for_profile/write_xml_files only emit elements and fields whose CIM schema
origin includes the requested profile. If the dataset still has the decoded FullModel
header for that profile (from the original source file), it's reused verbatim
(scenarioTime, modelingAuthoritySet, version, DependentOn, ...); otherwise a minimal
header is synthesized.
Validation
violations = cimoxide.validate_files(
["RealGrid_EQ.xml", "RealGrid_SSH.xml"],
profiles=["EQ", "SSH"], # optional; auto-detected if omitted
)
for v in violations:
print(v.severity, v.rule_id, v.message, v.object_id)
validate_files runs two-phase validation: per-profile SHACL/SPARQL checks against each
file individually, then cross-profile checks on the merged dataset. See the
validate_files docstring for the full parameter list
(solved, common, quality, silence).
API surface
| Function / method | Description |
|---|---|
cimoxide.decode_file(path) |
Parse a single RDF/XML file. |
cimoxide.decode_files(paths) |
Parse and merge multiple RDF/XML files. |
cimoxide.decode_str(content) |
Parse RDF/XML from a string. |
cimoxide.validate_files(paths, ...) |
Two-phase SHACL/SPARQL validation, returns list[Violation]. |
CimDataset.merge(other) |
Merge another dataset into this one (other becomes empty). |
CimDataset.drop_blocks() |
Free internal parse buffers after the final merge. |
CimDataset[mrid] / .get(mrid) |
Fetch one element as a dict (KeyError / None if missing). |
CimDataset[mrid] = {...} |
Insert or replace the element at mrid. |
del CimDataset[mrid] |
Remove the element at mrid (KeyError if missing). |
CimDataset.mrids() / iter(ds) / len(ds) |
Enumerate or count MRIDs. |
CimDataset.by_type() |
dict[str, list[mrid]] type index; copies one str per MRID. |
CimDataset.count_type(name) |
Number of elements of one CIM class. O(1), copies nothing. |
CimDataset.get_type(name) |
All element dicts for one CIM class. |
CimDataset.entries() |
All entries as dict[mrid, dict] (deserializes everything). |
CimDataset.query(sparql) |
Run SPARQL 1.1 over the dataset. Builds an RDF graph on first call, then caches it. |
CimDataset.drop_sparql_store() |
Release that cached graph (it roughly doubles resident memory). |
CimDataset.to_xml_for_profile(profile) |
Encode one CGMES profile (e.g. "EQ") as an RDF/XML string. |
CimDataset.write_xml_files(dir, profiles) |
Write one RDF/XML file per profile into dir. |
query() materialises the dataset into an in-memory RDF graph the first time it
is called and reuses it afterwards, so the first query costs far more than the
rest — on a ~150k-element dataset, roughly 1.1 s then ~4 ms. The cache is dropped
automatically whenever the dataset is mutated, and drop_sparql_store() releases
it explicitly. Call query() before drop_blocks() if you need both: freeing the
parse buffers first forces a lossy fallback when the graph is built.
Full type stubs with per-method docstrings are in
python/cimoxide/__init__.pyi and
python/cimoxide/types.pyi (generated TypedDict per CIM
class, for editor autocomplete on the returned dicts).
Examples
examples/example_counts.py decodes the RealGrid test
configuration and prints an element count per CIM type:
python examples/example_counts.py
examples/example_encode.py decodes RealGrid, encodes it
back to EQ/SSH/TP/SV profile files (in a temp directory by default, or the directory
given as an argument), then re-decodes the output to confirm the round-trip is lossless:
python examples/example_encode.py [output-dir]
Both require the CGMES-Test-Configurations submodule checked out at the repo root — see
"Development" below.
Benchmark
examples/benchmark_realgrid.py times decode_files,
write_xml_files, and validate_files against the full RealGrid dataset and reports
best/mean wall time plus MB/s throughput for each:
python examples/benchmark_realgrid.py [iterations] # default: 3
Also requires the CGMES-Test-Configurations submodule.
Tests
The test suite decodes and validates the CGMES fixture files checked into the parent
repository's testdata/ directory:
pip install pytest
pytest tests/
tests/test_decode.py— round-trip decode tests (decode_file/decode_str/decode_files, indexing, iteration).tests/test_api.py— dataset API contract tests (merge,drop_blocks, mutation via__setitem__/__delitem__, error handling).tests/test_encode.py—to_xml_for_profile/write_xml_filesbehavior, includingFullModelheader reuse against a real CGMES fixture.tests/test_validate.py—validate_filesbehavior (profile filtering,silence,quality/commonflags,Violationfields).
Development
This package is built from the cimoxide monorepo,
where cimoxide-py lives alongside the Rust crates it binds (cimdecoder, cimstructs,
cimvalidation, cimconvert). To build it from source:
# for ubuntu
git clone --recurse-submodules https://github.com/m-mirz/cimoxide.git
cd cimoxide
python3 -m venv .venv
source .venv/bin/activate
pip3 install maturin
cd cimoxide-py
maturin develop --release # editable install into the active virtualenv
pip3 install pytest
pytest tests/
See the repository README for the full project layout, the code generator, and the Rust CLI.
License
Apache-2.0
Metadata
Release files for cimoxide 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cimoxide-0.3.3.tar.gz | 904.4 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cimoxide-0.3.3-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| cimoxide-0.3.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| cimoxide-0.3.3-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
Total release size: 28.2 MB
Release files / cimoxide-0.3.3.tar.gz
| Download URL | cimoxide-0.3.3.tar.gz |
|---|---|
| Size | 904.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9f0e97be4a440caf83bed25f1b27abcd8994dc930008726db5c143c3865132d5
|
|
BLAKE2b-256 checksum How to use checksums |
77df5506c1580aa375a5848f47b59545bc471f564de6c8cf537730e477a9431e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / cimoxide-0.3.3-cp39-abi3-win_amd64.whl
| Download URL | cimoxide-0.3.3-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 10.1 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
84d90d80eba55a92caf7bc9419c4a27dd6caa5eb3fb81c96f14bba84bcea96e2
|
|
BLAKE2b-256 checksum How to use checksums |
baa0db35540c01c7955f42ab60d52eee43e75fb8ca733ac3c949e8d234ca055d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / cimoxide-0.3.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | cimoxide-0.3.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 8.9 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
758c4cda602a620a15c86879c1631eadae25595c26fd667bd5e0078d96c1e661
|
|
BLAKE2b-256 checksum How to use checksums |
a43925ac8fe74925d6104fd56086d3f80b83358bb35954e0dd280280e26d4f78
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / cimoxide-0.3.3-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | cimoxide-0.3.3-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 8.3 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
16ff0438db003272af07cfef942371a31dad89a20d9a41fb3367f05c6f43ede6
|
|
BLAKE2b-256 checksum How to use checksums |
0ed3a610a2eb109bf440a33d1ba55d3eb12cfaf66a934429fd04ab644e1a3472
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency log