Skip to content

Add CGMES exporter (pp2cim): pandapower → CGMES - #3031

Open
simepavlic wants to merge 14 commits into
e2nIEE:developfrom
simepavlic:feature/cgmes-exporter
Open

Add CGMES exporter (pp2cim): pandapower → CGMES#3031
simepavlic wants to merge 14 commits into
e2nIEE:developfrom
simepavlic:feature/cgmes-exporter

Conversation

@simepavlic

Copy link
Copy Markdown
Contributor

Summary

pandapower can already import CGMES (cim2pp), but there was no way to write a network back out. This PR adds the inverse — a to_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.

from pandapower.converter.cim import to_cim

to_cim(net, file_path="grid.zip", cgmes_version="2.4.15")   # one zip, one XML per profile

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

  • Profiles: EQ, SSH, TP, SV (when solved), DL and GL (when coordinates present)
  • Versions: CGMES 2.4.15 and 3.0
  • Topologies: both bus-branch (TopologicalNode) and node-breaker (ConnectivityNode) models
  • Elements: buses/terminals, lines, loads, ExternalNetworkInjections, SynchronousMachines (gen/sgen) + GeneratingUnit + RegulatingControl, EnergySource, switches, shunts (linear/nonlinear/SVC), SeriesCompensator/EquivalentBranch, ward/xward, 2- and 3-winding transformers, ratio/phase/tabular tap changers, current limits, and the Substation/VoltageLevel/region container hierarchy.

The full supported-class list and limitations are in doc/converter/cgmes.rst.

Validation

Validated by round-trip (import → export → re-import) and value comparison:

grid model elements completeness worst value delta
FullGrid / MicroGrid / MiniGrid (reference) bus-branch + node-breaker small 100% float noise
real ENTSO-E network bus-branch 76,962 100% 7e-12
real ENTSO-E network node-breaker 57,240 100% 7e-15
  • Load-flow survival: on a solved real ~21k-bus grid, import → solve → export → re-import → solve reproduces the operating point to ~0.05 deg mean bus-angle (~0.5 deg max) and ~0.0003 pu voltage.
  • Referential integrity: a well-formedness + reference-resolution check reports 0 dangling references (up to ~327k internal references on the real grid).

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 Limitations section of the docs, with an xfail test 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 one xfail). The existing 96 import tests are unchanged and still pass.

Šime Pavlić added 12 commits June 8, 2026 15:55
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

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.62542% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.28%. Comparing base (1576fe6) to head (c05d282).
⚠️ Report is 6 commits behind head on develop.

Files with missing lines Patch % Lines
pandapower/converter/cim/pp2cim/build_cim_net.py 96.82% 22 Missing ⚠️
pandapower/converter/cim/cim_writer.py 96.40% 6 Missing ⚠️
pandapower/converter/cim/pp2cim/to_cim.py 91.66% 2 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.
@simepavlic
simepavlic force-pushed the feature/cgmes-exporter branch from 5009c49 to 5415a27 Compare June 15, 2026 08:27
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
17 Security Hotspots

See analysis details on SonarQube Cloud

@mrifraunhofer

Copy link
Copy Markdown
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants