Skip to main content
Pre-release

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 RawModel container with .bus_records / .load_records list attributes. That container is gone — use count() / 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: TieFlow or 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: TransformerMeshImpedance or 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 model field value.
  • Work needed: case-by-case assessment per model type; may require raw_extension approach.

Other known gaps

  • 3-winding transformers: TransformerRecord currently handles 2-winding only. 3-winding transformers are represented as a star (3 × 2-winding) in raw; CIM uses PowerTransformerEnd with 3 ends. Needs dedicated handling.
  • Multi-terminal DC importer registration: MultiTerminalDCRecord is not in IMPORT_ORDER in raw34_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)

Source distribution for cimhub-raw 2.0.0a3
File Size Uploaded
cimhub_raw-2.0.0a3.tar.gz 213.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cimhub-raw 2.0.0a3
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

2.0.0a3 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page