Skip to content

Cannot load ReliCapGrid CGMES v3.0 multi-TSO CGM — multiple bugs in pypowsybl 1.15.0 #1245

Description

@MahirSarfati

Describe the current behavior

Loading the ReliCapGrid CGMES v3.0 multi-TSO dataset (https://github.com/entsoe/relicapgrid) via pypowsybl 1.15.0 fails with multiple distinct errors depending on the loading approach. ReliCapGrid is the ENTSO-E open-source synthetic CGMES v3.0 test dataset recently integrated into powsybl-core Java tests via PR #3592.
Attempt 1 — Full CGM (all 8 TSOs in one zip):

Fails with duplicate triplestore key error because all 5 boundary files share the same md:Model.modelingAuthoritySet URI http://ninerealms.nr/boundary:
Duplicate key http://ninerealms.nr/boundary (attempted merging values [Boundary_Border-Svedala-Belgovia.xml] and [Boundary_Border-Galia-Britheim.xml])
Attempt 2 — Full CGM with patched boundary URIs (unique URI per boundary file):

Fails with VoltageLevel not created during node/breaker → IIDM conversion, even though the VoltageLevel is correctly defined in the included EQ file:
BusbarSection 4b62e70e-41fd-3615-d8b0-36d0adf2e35b voltage level b7630cf0-3bd1-4813-8be3-404a1c4cfc7a has not been created in IIDM
Attempt 3 — Individual IGMs (each TSO loaded separately):

6 out of 8 IGMs load and converge. Two fail:

Espheim: Same VoltageLevel not created error as Attempt 2
Nordheim: Loads without exception but produces 0 buses and 1 substation — network is empty and unusable

Describe the expected behavior

All 8 ReliCapGrid IGMs should load successfully and produce non-empty networks with correct bus counts, consistent with the Java tests in powsybl-core PR #3592. The full 8-TSO CGM should be mergeable into a single network for AC load flow and sensitivity analysis via pypowsybl.

Describe the steps

Describe the steps to reproduce

  1. Clone the ReliCapGrid dataset:
    git clone https://github.com/entsoe/relicapgrid data/relicapgrid

  2. Install pypowsybl:
    pip install pypowsybl==1.15.0

  3. Run the following Python script:
    pythonimport pypowsybl as pp
    import zipfile, tempfile, os
    from pathlib import Path

ROOT = Path('data/relicapgrid/Instance')
TSOs = ['Belgovia','Britheim','DC-Espheim-Svedala','DC-Nordheim-Galia',
'Espheim','Galia','Nordheim','Svedala']

Collect all grid + common + boundary files (excluding Portheim)

files = []
for tso in TSOs:
d = ROOT / tso / 'Grid' / 'cimxml'
if d.exists():
files.extend(list(d.glob('.xml')))
files.extend(list((ROOT / 'commonData' / 'Grid' / 'cimxml').glob('
.xml')))
for f in (ROOT / 'boundaryData' / 'Grid' / 'cimxml').glob('*.xml'):
if 'Portheim' not in f.name:
files.append(f)

Pack into a zip and load

with tempfile.TemporaryDirectory() as tmp:
zippath = os.path.join(tmp, 'cgm.zip')
with zipfile.ZipFile(zippath, 'w') as zf:
for f in files:
zf.write(f, Path(f).name)

# Step 3a: Try loading as-is -> triggers Bug 1
net = pp.network.load(zippath)
  1. Observe Bug 1: Duplicate key error for http://ninerealms.nr/boundary

  2. Apply workaround for Bug 1 — patch boundary URIs to be unique:
    pythonimport io
    import xml.etree.ElementTree as ET

def patch_boundary_uri(src: Path, dst: Path):
content = src.read_text(encoding='utf-8')
content = content.replace(
'http://ninerealms.nr/boundary',
f'http://ninerealms.nr/boundary/{src.stem}'
)
dst.write_text(content, encoding='utf-8')

boundary_dir = ROOT / 'boundaryData' / 'Grid' / 'cimxml'

with tempfile.TemporaryDirectory() as tmp:
zippath = os.path.join(tmp, 'cgm_patched.zip')
with zipfile.ZipFile(zippath, 'w') as zf:
for f in files:
if f.parent == boundary_dir:
patched = Path(tmp) / f'patched_{f.name}'
patch_boundary_uri(f, patched)
zf.write(patched, f.name)
else:
zf.write(f, Path(f).name)

# Step 5a: Try loading with patched boundary URIs -> triggers Bug 2
net = pp.network.load(zippath, parameters={
    'iidm.import.cgmes.cgm-with-subnetworks': 'false',
})
  1. Observe Bug 2: VoltageLevel b7630cf0 not created in IIDM for Espheim

  2. To reproduce Bug 3 (Nordheim empty network), load Nordheim IGM individually:
    python# Apply data fix from relicapgrid issue Add features #3 — remove duplicate ConnectivityNode

from Boundary_Border-Galia-Nordheim.xml, then load Nordheim alone

DUPLICATE_IDS = {
'892ef502-162b-469f-b93f-266aae828227',
'e61f8ecf-8129-4702-a3a2-826bdf5ad13d',
'c39c3eb8-9cce-4778-9069-6952a117ba2b',
'77467625-a8fe-8792-7cf2-06c5dfd5fba9',
'9356077b-f716-0d6c-0cac-32c074bd8fa3',
}

def patch_nordheim_boundary(src: Path, dst: Path):
content = src.read_text(encoding='utf-8')
content = content.replace(
'http://ninerealms.nr/boundary',
f'http://ninerealms.nr/boundary/{src.stem}'
)
tree = ET.parse(io.StringIO(content))
root = tree.getroot()
to_remove = [
elem for elem in root
if any(
dup_id.replace('-','') in v.replace('-','').strip('#').replace('urn:uuid:','')
for v in elem.attrib.values()
for dup_id in DUPLICATE_IDS
)
]
for elem in to_remove:
root.remove(elem)
tree.write(dst, encoding='UTF-8', xml_declaration=True)

nordheim_files = list((ROOT / 'Nordheim' / 'Grid' / 'cimxml').glob('.xml'))
common_files = list((ROOT / 'commonData' / 'Grid' / 'cimxml').glob('
.xml'))
boundary_src = ROOT / 'boundaryData' / 'Grid' / 'cimxml' / 'Boundary_Border-Galia-Nordheim.xml'

with tempfile.TemporaryDirectory() as tmp:
patched_b = Path(tmp) / boundary_src.name
patch_nordheim_boundary(boundary_src, patched_b)

zippath = os.path.join(tmp, 'Nordheim.zip')
with zipfile.ZipFile(zippath, 'w') as zf:
    for f in nordheim_files + common_files:
        zf.write(f, Path(f).name)
    zf.write(patched_b, boundary_src.name)

net = pp.network.load(zippath, parameters={
    'iidm.import.cgmes.source-for-iidm-id': 'mRID',
    'iidm.import.cgmes.cgm-with-subnetworks': 'false',
})
print('Buses:', len(net.get_buses()))        # prints 0
print('Substations:', len(net.get_substations()))  # prints 1
  1. Observe Bug 3: Nordheim loads without error but produces 0 buses and 1 substation.
    The network is empty and cannot be used for load flow or sensitivity analysis.

Environment

pypowsybl version: 1.15.0
Python version: 3.11
OS: Linux Ubuntu 24 (GitHub Codespaces)
Dataset: ReliCapGrid v1.0 — https://github.com/entsoe/relicapgrid
Dataset format: CGMES v3.0, node/breaker topology
Reference Java test: powsybl-core PR #3592

Relevant Log Output

Attempt 1 — Full CGM, all boundary files included

Duplicate key http://ninerealms.nr/boundary (attempted merging values
[Boundary_Border-Svedala-Belgovia.xml] and [Boundary_Border-Galia-Britheim.xml])

Attempt 2 — Full CGM, boundary URIs patched to be unique

BusbarSection 4b62e70e-41fd-3615-d8b0-36d0adf2e35b voltage level
b7630cf0-3bd1-4813-8be3-404a1c4cfc7a has not been created in IIDM

Attempt 3 — Individual IGMs, result per TSO

OK Belgovia buses=15 LF=CONVERGED
OK Britheim buses=6 LF=CONVERGED
FAIL Espheim BusbarSection 4b62e70e... voltage level b7630cf0... has not been created in IIDM
OK Galia buses=11 LF=CONVERGED
FAIL Nordheim buses=0 substations=1 (loads without error but empty network)
OK Svedala buses=112 LF=CONVERGED
OK DC-Espheim-Svedala buses=6 LF=NO_CALCULATION
OK DC-Nordheim-Galia buses=7 LF=CONVERGED

Extra Information

Three distinct bugs identified:
Bug 1 — Duplicate modelingAuthoritySet URI (Attempt 1)

All 5 ReliCapGrid boundary files share md:Model.modelingAuthoritySet = http://ninerealms.nr/boundary. The pypowsybl triplestore rejects duplicate keys when multiple boundary files are loaded together. Workaround: patch each boundary file to use a unique URI (e.g. http://ninerealms.nr/boundary/Boundary_Border-Svedala-Belgovia). This resolves Bug 1 but exposes Bug 2.
Bug 2 — VoltageLevel not created in IIDM for Espheim (Attempt 2 and 3)

VoltageLevel b7630cf0-3bd1-4813-8be3-404a1c4cfc7a is correctly defined in 20220615T2230Z__Espheim_EQ_1.xml with rdf:ID:
xml<cim:VoltageLevel rdf:ID="_b7630cf0-3bd1-4813-8be3-404a1c4cfc7a">
cim:IdentifiedObject.mRIDb7630cf0-3bd1-4813-8be3-404a1c4cfc7a</cim:IdentifiedObject.mRID>
...
</cim:VoltageLevel>
It does not appear in any boundary file — it is purely internal to Espheim. pypowsybl fails to create it during CGMES v3.0 node/breaker → IIDM conversion. This bug persists with both cgm-with-subnetworks=true and cgm-with-subnetworks=false. No workaround found.
Bug 3 — Nordheim IGM loads with 0 buses (Attempt 3)

After applying the data fix from relicapgrid issue #3 (removing duplicate ConnectivityNode 892ef502 and its hierarchy from Boundary_Border-Galia-Nordheim.xml), Nordheim loads without exception but produces an empty network (0 buses, 0 lines, 1 substation). The substation shell is created but no topology is built. No workaround found.
Additional notes:

VeraGrid (GridCal/eRoots) successfully loads all 8 ReliCapGrid TSOs and runs AC load flow, confirming the dataset itself is valid CGMES v3.0
The duplicate boundary URI issue (Bug 1) may affect other multi-TSO CGMES v3.0 datasets where multiple boundary files share a modelingAuthoritySet
Cross-reference: relicapgrid issue #3 documents the Nordheim duplicate ConnectivityNode data issue
Cross-reference: powsybl-core PR #3592 — Java tests load ReliCapGrid successfully, but the Python API (pypowsybl) does not yet expose equivalent functionality for this dataset

Minimal reproduction code:
pythonimport pypowsybl as pp
import zipfile, tempfile, os
from pathlib import Path

ROOT = Path('data/relicapgrid/Instance') # git clone https://github.com/entsoe/relicapgrid
TSOs = ['Belgovia','Britheim','DC-Espheim-Svedala','DC-Nordheim-Galia',
'Espheim','Galia','Nordheim','Svedala']

files = []
for tso in TSOs:
d = ROOT / tso / 'Grid' / 'cimxml'
if d.exists():
files.extend(list(d.glob('.xml')))
files.extend(list((ROOT / 'commonData' / 'Grid' / 'cimxml').glob('
.xml')))
for f in (ROOT / 'boundaryData' / 'Grid' / 'cimxml').glob('*.xml'):
if 'Portheim' not in f.name:
files.append(f)

with tempfile.TemporaryDirectory() as tmp:
zippath = os.path.join(tmp, 'cgm.zip')
with zipfile.ZipFile(zippath, 'w') as zf:
for f in files:
zf.write(f, Path(f).name)
net = pp.network.load(zippath) # Fails with Bug 1

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions