Add CGMES exporter (pp2cim): pandapower → CGMES - #3031
Conversation
New pp2cim package with to_cim(), the inverse of from_cim: rebuilds the CIM data structure from the net's preserved origin_id/terminal/topology metadata and serializes it to RDF/XML (EQ/SSH/TP/SV) via cim_writer.py. Supports CGMES 2.4.15 and 3.0, with round-trip tests and docs.
…onlinear shunts Adds DL/GL coordinate export, EnergySource->sgen/ext_grid, EquivalentBranch->impedance, line/transformer CurrentLimits, NonlinearShuntCompensator and 2-winding vector groups, with tests. All three reference grids now round-trip 100% of their elements.
vk_percent carries the sign of x, so use abs() for the impedance magnitude and copysign to restore it. Found by round-tripping a real ENTSO-E network.
Behaviour-preserving cleanup from a code-quality review: shared transformer math and coordinate-scan helpers, consistent defaultdict accumulators, and small consistency fixes.
Node-breaker buses get their voltage via ConnectivityNode -> VoltageLevel -> BaseVoltage, but the containers were only rebuilt in the GL (geo) path, so node-breaker grids without coordinates lost all bus voltages on round-trip. Rebuild them in the main bus conversion instead; the GL path reuses them. Adds a regression test for node-breaker bus voltage.
Rebuild PhaseTapChangerTabular + PhaseTapChangerTable + per-step PhaseTapChangerTablePoint from the flattened per-step characteristic (net['trafo_characteristic_table']) so the importer recovers the same per-step voltage ratio, hence the transformer ratio at the operating tap. Previously these tap changers were dropped, which left the ratio at 1.0 and shifted the power-flow solution on grids that use tabular tap changers. On a real ~21k-bus grid, an import -> solve -> export -> re-import -> solve loop now reproduces the operating point to ~0.17 deg mean bus-angle (was ~4.75 deg), max ~0.67 deg (was ~29.6 deg). The per-step impedance deviation is not reconstructed (base impedance treated as tap-independent); table-based RatioTapChangers are still exported as linear. Adds a synthetic round-trip test for a tabular tap changer.
A table-based RatioTapChanger carries a per-step ratio characteristic (net['trafo_characteristic_table']) in addition to its linear stepVoltageIncrement. These were exported as purely linear, dropping the non-linear per-step ratio. Rebuild the RatioTapChangerTable + per-step RatioTapChangerTablePoint (and keep stepVoltageIncrement) so the per-step ratio survives. On the real ~21k-bus import -> solve -> export -> re-import -> solve loop this cuts the bus-voltage drift ~10x (max ~0.0024 pu) and the characteristic table now round-trips exactly. Adds a synthetic round-trip test.
The table-based tap changers were exported with a tap-independent impedance (table points r=x=0). Reconstruct the per-step impedance deviation for two-winding transformers from the characteristic's per-step vk/vkr (percent space), shared by both the tabular phase and table-based ratio changers. On the real import -> solve -> export -> re-import -> solve loop this lowers the mean bus-angle drift from ~0.17 deg to ~0.05 deg. Three-winding per-step impedance is left tap-independent (documented).
Export the SubGeographicalRegion / GeographicalRegion hierarchy and a minimal Line container for line EquipmentContainers, so the Substation.Region and ACLineSegment.EquipmentContainer references resolve. The exported files are now referentially complete: a well-formedness + reference-resolution check over the reference grids and a real ~21k-bus grid (327k internal references) reports zero dangling references.
A 2-winding transformer's impedance is exported entirely on the HV winding (the LV end is left ideal). pandapower stores only the total impedance, so a source model that splits impedance across both windings cannot be reproduced per-winding. Add an xfail test documenting this for discussion; the total impedance still round-trips.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #3031 +/- ##
===========================================
+ Coverage 71.67% 72.28% +0.61%
===========================================
Files 355 359 +4
Lines 39348 40237 +889
===========================================
+ Hits 28204 29087 +883
- Misses 11144 11150 +6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Behavior-preserving fixes for Codacy and SonarCloud: f-strings, lazy logging, docstrings, __all__, implicit booleaness, extracted constants for duplicated literals, reduced cognitive complexity of two converters, and removed a dead branch in the bus-voltage test.
5009c49 to
5415a27
Compare
|
|
Hey @simepavlic, thank you very much for your contribution and for the effort you put into implementing a pandapower2CGMES converter. During our review and testing, we encountered several issues, in particular when converting SimBench networks. Some elements and model details are currently not converted in a way that allows the resulting CGMES models to be used reliably. In parallel, we have been working on our own pp2cim converter, which addresses these cases and follows the requirements we need for our workflows. We are planning to release this converter as open-source in the near future. For this reason, we have decided not to proceed with merging this PR. Nevertheless, we sincerely appreciate your work and your contribution to the pandapower ecosystem. |


Summary
pandapower can already import CGMES (
cim2pp), but there was no way to write a network back out. This PR adds the inverse — ato_cim(net, ...)exporter that converts a pandapower net into CGMES RDF/XML files, closing the loop so a grid can be imported, worked on in pandapower, and exported back into a CGMES workflow.How it works
The exporter mirrors the importer in reverse as a 3-step pipeline
net → CIM data structure → RDF/XML:pp2cim/build_cim_net.py(PpToCimConverter) reads each pandapower table and rebuilds the corresponding CGMES objects into an in-memory structure, reusing the importer's existing class/column blueprint so the two directions stay consistent.cim_writer.py(CimWriter) serialises that structure to one XML file per profile, driven by the existing serialized CGMES schema.It leans on the fact that the importer preserves each element's original CGMES identifiers (
origin_id, terminals, topology) on the net, so for a net that came from CGMES the export is a faithful reconstruction rather than a guess.What it covers
TopologicalNode) and node-breaker (ConnectivityNode) modelsThe full supported-class list and limitations are in
doc/converter/cgmes.rst.Validation
Validated by round-trip (
import → export → re-import) and value comparison:import → solve → export → re-import → solvereproduces the operating point to ~0.05 deg mean bus-angle (~0.5 deg max) and ~0.0003 pu voltage.Limitations (documented)
The export targets round-trip fidelity, not byte-identity to the source files, and has not been validated against third-party CGMES tools or formal CGMES conformance checks. Known approximations and not-yet-exported data (short-circuit parameters, generator metadata, tap/voltage controls, DC equipment, motors, measurements, etc.) are listed in the
Limitationssection of the docs, with anxfailtest documenting the 2-winding HV/LV impedance-split case for discussion.Tests
Adds ~62 exporter tests in
test_to_cim.py/test_to_cim_net.py(round-trip against the sample grids + synthetic-net tests for classes absent from the samples, plus onexfail). The existing 96 import tests are unchanged and still pass.