This release is a pre-release and may not be stable for production use.
cimhub_raw
cimhub_raw
RAW v34 ↔ CIM converter for the cimhub ecosystem.
Supports reading, writing, and bidirectional conversion between RAW v34 files and CIM (IEC 61970) network models using cimgraph.
Installation
uv sync
# or
pip install -e .
Set the CIM profile before importing (required by cimgraph):
import os
os.environ.setdefault("CIMG_CIM_PROFILE", "cim17v40")
Usage
1. Read a RAW file (no CIM conversion)
The source is bound at construction and read_all() returns a
cimhub_core.GraphModel — the same graph[RecordClass][identifier] container
used across the cimhub ecosystem. Records are the LinkML-generated dataclasses
in raw34_schema.
from cimhub_raw.importer.raw34_reader import Raw34Reader
import cimhub_raw.schema.raw_v34.raw34_schema as raw
raw_network = Raw34Reader("network.raw").read_all()
# Counts and iteration go through the GraphModel API:
print(f"Buses: {raw_network.count(raw.BusRecord)}")
print(f"Loads: {raw_network.count(raw.LoadRecord)}")
print(f"Generators: {raw_network.count(raw.GeneratorRecord)}")
for bus in raw_network.list_by_class(raw.BusRecord):
print(f" Bus {bus.bus_number}: {bus.name}, {bus.base_voltage} kV")
Note: the reader used to return a
RawModelcontainer with.bus_records/.load_recordslist attributes. That container is gone — usecount()/list_by_class()/find_by_attribute()instead.
1a. Basic editing style
Records live at raw_network.graph[RecordClass][identifier]. The identifier is
the composite key the reader assigns per record type (see the table below), so
you can look a record up directly and mutate its fields in place:
import cimhub_raw.schema.raw_v34.raw34_schema as raw
# Direct access by composite identifier — generator id "3" on bus 10111:
gen = raw_network.graph[raw.GeneratorRecord]['10111_3']
gen.p_output = 250.0 # set active power (MW)
gen.q_output = 40.0 # set reactive power (MVAr)
# Or find a record without knowing its exact key:
(bus,) = raw_network.find_by_attribute(raw.BusRecord, 'bus_number', 10111)
bus.base_voltage = 138.0
# find_by_attribute returns a LIST (exact string match), so index or unpack it:
loads = raw_network.find_by_attribute(raw.LoadRecord, 'bus_number', 10111)
for load in loads:
load.status = 0 # take every load on the bus out of service
Composite identifier keys (what to put in graph[Cls][...]):
| Record | Identifier key |
|---|---|
BusRecord |
bus_number |
LoadRecord |
bus_number_loadid |
FixedShuntRecord |
bus_number_shuntid |
GeneratorRecord |
bus_number_genid |
BranchRecord / SwitchRecord |
from_to_ckt |
TransformerRecord |
i_j_k_ckt |
SwitchedShuntRecord |
bus_number |
VscDcRecord |
name_ibus |
If you don't know the exact key, find_by_attribute(cls, attr, value) is the
safe path — it matches on the string form of any field.
2. Write a RAW file
The writer's standard input is a GraphModel — pass the one you read/edited
straight to write():
from cimhub_raw.exporter.raw34_writer import Raw34Writer
writer = Raw34Writer()
writer.write(raw_network, "output.raw")
To render a single record without writing a file, use writer.serialize(obj).
3. RAW → RAW round-trip (read + write, no CIM)
from cimhub_raw.importer.raw34_reader import Raw34Reader
from cimhub_raw.exporter.raw34_writer import Raw34Writer
raw_network = Raw34Reader("network.raw").read_all()
Raw34Writer().write(raw_network, "output.raw")
4. RAW → CIM
Convert a RAW file to a CIM NodeBreakerModel (cimgraph):
import os
os.environ.setdefault("CIMG_CIM_PROFILE", "cim17v40")
from cimhub_raw.importer.raw34_to_cim import raw34_to_cim
import cimhub_raw.schema.data_profile as cim
network = raw34_to_cim("network.raw")
buses = network.list_by_class(cim.TopologicalNode)
loads = network.list_by_class(cim.EnergyConsumer)
gens = network.list_by_class(cim.SynchronousMachine)
print(f"Buses: {len(buses)}, Loads: {len(loads)}, Generators: {len(gens)}")
Pass include_extensions=True to attach raw_extension objects (e.g. RawBusRecord)
alongside each CIM object — useful for lossless round-trips:
network = raw34_to_cim("network.raw", include_extensions=True)
5. CIM → RAW
Export a CIM BusBranchModel back to a RAW file:
from cimhub_raw.exporter.cim_to_raw import cim_to_raw
cim_to_raw(network, "output.raw")
Or build the intermediate GraphModel first (e.g. to inspect before writing):
from cimhub_raw.exporter.cim_to_raw import Raw34Exporter
from cimhub_raw.exporter.raw34_writer import Raw34Writer
raw_model = Raw34Exporter().to_model(network, system_base_mva=100.0)
writer = Raw34Writer()
writer.write(raw_model, "output.raw")
6. Full RAW → CIM → RAW round-trip
import os
os.environ.setdefault("CIMG_CIM_PROFILE", "cim17v40")
from cimhub_raw.importer.raw34_to_cim import raw34_to_cim
from cimhub_raw.exporter.cim_to_raw import cim_to_raw
network = raw34_to_cim("network.raw", include_extensions=True)
cim_to_raw(network, "output.raw")
7. Serialize CIM to XML / JSON-LD
Once you have a CIM network model, serialize it to CIM XML or JSON-LD:
from cimgraph.utils import write_xml, write_json_ld
write_xml(network, "network.xml")
write_json_ld(network, "network.jsonld")
Equipment Coverage
Fully converted (RAW ↔ CIM)
| RAW Record | CIM Class(es) |
|---|---|
| BusRecord | TopologicalNode, ConnectivityNode |
| LoadRecord | EnergyConsumer |
| GeneratorRecord | SynchronousMachine |
| FixedShuntRecord | LinearShuntCompensator (single-section) |
| SwitchedShuntRecord | LinearShuntCompensator (multi-section) |
| BranchRecord | ACLineSegment, SeriesCompensator, Breaker |
| TransformerRecord | PowerTransformer (2-winding) |
| TwoTerminalDCRecord | DCLineSegment + CsConverter x2 |
| VscDcRecord | DCLineSegment + VsConverter x2 |
| FactsDeviceRecord | StaticVarCompensator, VsConverter |
| InductionMachineRecord | AsynchronousMachine |
| AreaInterchangeRecord | ControlArea / SubControlArea |
| ZoneRecord | SubGeographicalRegion |
| OwnerRecord | AssetOwner |
| MultiSectionLineRecord | Line (container grouping) |
| SwitchRecord | Switch / Breaker |
Passthrough only (read + write, no CIM conversion)
These record types are parsed and written verbatim but have no CIM mapping:
| RAW Record | Reason |
|---|---|
| InterareaTransferRecord | No standard CIM equivalent |
| ImpedanceCorrectionRecord | No CIM class in the CIM 17 profile |
| MultiTerminalDCRecord | Sub-records (converters, bus links) not fully parsed |
| GneDeviceRecord | Generic extensible model; depends on model field |
Running Tests
# From the repo root
uv run pytest cimhub_raw/tests/
# Key test files
uv run pytest cimhub_raw/tests/test_raw34_roundtrip.py # RAW → RAW (no CIM)
uv run pytest cimhub_raw/tests/test_ieee14_roundtrip.py # Full RAW → CIM → RAW
uv run pytest cimhub_raw/tests/test_production_roundtrip.py # 169-bus production model
TODO
CIM mappings needed for passthrough record types
These record types are currently read and written verbatim (no CIM conversion). Implementing them requires either finding an appropriate CIM class or extending the data profile.
1. InterareaTransferRecord
- Represents scheduled power transfers between control areas.
- No direct CIM equivalent in CIM 17. Closest candidates:
TieFlowor a custom extension. - Work needed: define CIM mapping, implement importer + exporter, add tests.
2. ImpedanceCorrectionRecord
- Tap-dependent impedance correction tables for transformers.
- No CIM class in the CIM 17 profile. Closest:
TransformerMeshImpedanceor a raw_extension. - Work needed: define CIM mapping or raw_extension class, implement importer + exporter, add tests.
3. MultiTerminalDCRecord
- Represents multi-terminal HVDC lines (3+ converter stations).
- CIM has
DCNode,DCTopologicalNode,DCConverterUnit— mapping is feasible but complex. - Current reader only parses the header line; converter/bus/link sub-records are skipped.
- Work needed: extend reader to parse all sub-record types, define CIM mapping, implement importer + exporter.
4. GneDeviceRecord
- Generic network element: a catch-all for vendor-specific dynamic models.
- No universal CIM mapping — depends on the
modelfield value. - Work needed: case-by-case assessment per model type; may require raw_extension approach.
Other known gaps
- 3-winding transformers:
TransformerRecordcurrently handles 2-winding only. 3-winding transformers are represented as a star (3 × 2-winding) in raw; CIM usesPowerTransformerEndwith 3 ends. Needs dedicated handling. - Multi-terminal DC importer registration:
MultiTerminalDCRecordis not inIMPORT_ORDERinraw34_to_cim.py.
Legacy PSS/E RAW extension layer (cimgraph_raw_extension). Provides raw-extension dataclasses that can be attached alongside CIM objects during a PSS/E import to preserve fields that have no direct CIM mapping.
Usage
from cimgraph_raw_extension.converters.rawToCim import raw_to_cim
from cimgraph_raw_extension.converters.cimToRaw import cim_to_raw
# Import RAW → CIM with extensions attached
network = raw_to_cim("network.raw")
# Export CIM → RAW
cim_to_raw(network, "output.raw")
Structure
src/cimgraph_raw_extension/
data_profile/raw_extension.py # Extension dataclasses (RawBusRecord, etc.)
models/rawModel.py # RawModel container (legacy)
converters/rawToCim.py # RAW → CIM conversion
converters/cimToRaw.py # CIM → RAW conversion
Metadata
Release files for cimhub-raw 2.0.0a3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cimhub_raw-2.0.0a3.tar.gz | 213.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cimhub_raw-2.0.0a3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 406.0 kB
Release files / cimhub_raw-2.0.0a3.tar.gz
| Download URL | cimhub_raw-2.0.0a3.tar.gz |
|---|---|
| Size | 213.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8433bbeeee4d4eb09cfea67d8229ca905d25e2fe849d20f199d57200dbedffe1
|
|
BLAKE2b-256 checksum How to use checksums |
94306799abdce099ff23fd968dd94537fb287875eb82130dadb9d8dfc79f6a64
|
| 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 Aug 20, 2026.
Transparency logRelease files / cimhub_raw-2.0.0a3-py3-none-any.whl
| Download URL | cimhub_raw-2.0.0a3-py3-none-any.whl |
|---|---|
| Size | 192.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
55708dd2041a6a2a90b856431b700d050e830f5227921b08ae35ed5051e99661
|
|
BLAKE2b-256 checksum How to use checksums |
59bb6f1ce909bc13679e9c1796bef5d14bccf3a1768043bc08e0f00f39d4f53c
|
| 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 Aug 20, 2026.
Transparency log