CGMES Export

writeCGMESFiles writes a Net as a CGMES 2.4.15 (RDF/XML) delivery with four profiles:

  • EQ + TP: TopologicalNodes with BaseVoltage/VoltageLevel containers, ACLineSegments, PowerTransformers with two or three PowerTransformerEnds, EnergyConsumers, SynchronousMachines with GeneratingUnit and, for voltage-regulating units, RegulatingControl, external network injections, EquivalentInjections for converter units, asynchronous machines, static VAr compensators, LinearShuntCompensators and bus links (closed Breakers), each with terminals and topology associations; a branch the importer built from an EquivalentBranch goes out as EquivalentBranch again (in one EquivalentNetwork), as long as it carries no charging and no ratio and its shunt arms fit the phase stamp of an asymmetric one (Asymmetric EquivalentBranch); r, x, r21 and x21 leave as delivered (Net.equivalent_branch_metadata), also where the branch model uses the geometric mean of $|z_{12}|$ and $|z_{21}|$; one notice counts those objects, and the re-import builds the same branch. A branch changed since the import leaves with the values of its model instead, $z_{12} = z_s e^{-j\varphi}$ and $z_{21} = z_s e^{j\varphi}$; zero-sequence line attributes and harvested short-circuit attributes in the EquipmentShortCircuit part of EQ.
  • SSH: load and machine p/q, the referencePriority of every machine and network injection (its stored reference priority, 1 for a slack unit that states none; a re-import takes the strongest priority as its slack, so a non-slack unit with a priority as strong as the slack's is named in the notices), voltage-regulation targets, tap steps and shunt sections.
  • SV: SvVoltage per bus and SvPowerFlow per terminal: after a solve the solution, so a re-import can start from it (cgmes_import.start_values: sv) and validate against it; right after an import the start state.

A re-import rebuilds the same electrical model, and its power flow matches the original to solver precision, phase-shifting transformers included. The P limits of machines and network injections come back as written. Q limits come back as their minQ/maxQ hull, so an asymmetric pair that binds can move the solution. Any Net exports, whatever its source. Every file starts with Generated by Sparlectra.jl v<version> on <date>, repeated as md:Model.description in the profile header; the date is the created timestamp.

using Sparlectra

net = Net(name = "demo", baseMVA = 100.0)
addBus!(net = net, busName = "B1", vn_kV = 110.0, vm_pu = 1.0, va_deg = 0.0)
addBus!(net = net, busName = "B2", vn_kV = 110.0, vm_pu = 1.0, va_deg = 0.0)
addACLinePerKm!(net = net, fromBus = "B1", toBus = "B2", length_km = 25.0, r_ohm_per_km = 0.2, x_ohm_per_km = 0.39, c_nf_per_km = 9.5)

files = writeCGMESFiles(net; path = "out")
# -> ["out/demo_EQ.xml", "out/demo_TP.xml", "out/demo_SSH.xml", "out/demo_SV.xml"]

A two-winding transformer exports the full impedance on end 2 (the to-side base of the branch model) and the effective turns ratio in the end ratedU values, so the importer reproduces the solved ratio exactly. A phase shift becomes a single-step linear phase tap changer whose increment is the shift angle. A winding with ratio-tap machinery (PowerTransformerTaps) exports a RatioTapChanger with range, neutral data and current step; the end ratedU absorbs the residual. Three-winding transformers imported from CGMES are reassembled into one PowerTransformer with three ends and the original mRIDs when the reconstruction is exact (a leg with a tap changer included, its tap flattened into the end ratedU), otherwise the star group stays two-winding with a notice. A leg with a negative r or x is written as it is. The end g, b is the sum of both arms of its leg; the importer puts it at the star point again. A leg open at its side bus writes its end terminal with connected = false.

Keyword arguments:

  • sc_line_data::Dict{Int,CGMESLineShortCircuit}: zero-sequence and short-circuit data per line index (order of net.linesAC) for the EQ profile; cgmesLineShortCircuitData(result) builds it from a CGMES import's harvest.
  • sc_source::CGMESShortCircuitData: the short-circuit harvest of a CGMES import (result.shortcircuit); machine, injection and motor attributes (x''d, feeder current limits, locked-rotor data) are written back by mRID, so a short-circuit run on the re-import reproduces the original.
  • zip::Bool: also pack the profiles into <name>_CGMES.zip, which the importer and the Web UI upload read directly; its path is appended to the returned list.
  • created::DateTime: the md:Model.scenarioTime / md:Model.created stamp (default: current time); a fixed value makes the output byte-reproducible.
  • notices::Vector{String}: one line per model element the export could not carry exactly; without a sink the lines are emitted as warnings.

Examples: examples/cgmes/cgmes_export_demo.jl, and examples/cgmes/cgmes_export_physical.jl for a net built from line data in ohm/km and nF/km, whose delivery carries the same physical values.

Identity and mRIDs on export

Each object's mRID is resolved through a structural key stored in net.cgmes_ids; a key describes what an object is, not what it is called:

KeyObject
TN|<busname>TopologicalNode of a bus
BV|<vn_kV> / VL|<vn_kV>BaseVoltage / VoltageLevel per nominal voltage
ACL|<busA>|<busB>|<k>ACLineSegment; the bus pair is sorted lexicographically, <k> numbers parallel lines on the same pair in first-seen order
LNC|<busA>|<busB>|<k>cim:Line container of that segment
PT|<busA>|<busB>|<k>PowerTransformer (same pair and <k> rules, counted over transformer branches)
EQB|<busA>|<busB>|<k>EquivalentBranch built as a line, recorded by the importer; <k> continues the ACL counter of its pair
EQBT|<busA>|<busB>|<k>EquivalentBranch built as a transformer branch (across two levels, or phase-stamped), recorded by the importer; <k> continues the PT counter of its pair
PT3|<busA>|<busB>|<busC>|<k>three-winding PowerTransformer over its sorted side buses
…|E1 / …|E2 / …|E3PowerTransformerEnd by end number
…|PTCthe phase-shift-carrying linear phase tap changer
…|RTCthe exported ratio tap changer of a tap-carrying winding
SVC|<bus>|<k>StaticVarCompensator (also with …|RC)
LNK|<busA>|<busB>|<k>bus link (Breaker)
EC|<bus>|<k>EnergyConsumer; <k> numbers the bus's units of one class in first-seen order
SM|<bus>|<k>SynchronousMachine, with …|GU (GeneratingUnit) and …|RC (RegulatingControl) children
ENI|<bus>|<k>ExternalNetworkInjection (also with …|RC)
EI|<bus>|<k>EquivalentInjection written for a converter unit
ASM|<bus>|<k>AsynchronousMachine
SH|<bus>|<k>LinearShuntCompensator
…|T1 / …|T2Terminals by equipment sequence number (T1 = from side)
MODEL|EQ / MODEL|TP / MODEL|SSH / MODEL|SVmd:FullModel header ids

An imported network keeps its original ids: the importer records the source mRIDs under the same keys. A key without a recorded id (programmatic nets, MATPOWER/DTF imports) gets a uuid5 minted from the key string and recorded on the net, so the same structure produces the same ids across runs and machines. Renaming a component never changes its mRID; renaming a bus does, because its TopologicalNode key and the line keys built from it change. Two keys resolving to the same mRID (a hand-edited net.cgmes_ids) abort the export with an error naming both keys before any file is written.

Export from the Web UI

The run option Write MATPOWER export artifact works the same way for every case format: the network of the run is written as a new file <case>_calc_<YYYYMMDD>.m next to the other artifacts, with a header that names the source case, the write time, the Sparlectra version and whether the bus and branch columns carry the calculated state. The input file is never written.

Export case as CGMES delivery on the run form writes, for every case format, the combined <case>_CGMES.zip with all four profiles right after the network is built; a run that does not converge exports too (the SV then carries the start state). The zip is listed with the run's artifacts and re-imports directly; a CGMES case carries its harvested zero-sequence line attributes along. The result page shows a CGMES export row and the export notices as a warning badge. Service callers set export_cgmes: true in the start_powerflow_run request.

Tap-changer classes

A winding with a typed phase model in the CGMES regulating-vector convention exports the model's class with its step: :symmetrical as PhaseTapChangerSymmetrical, :asymmetrical as PhaseTapChangerAsymmetrical (with windingConnectionAngle), :tabular as PhaseTapChangerTabular with its PhaseTapChangerTable and points; a model that came in as PhaseTapChangerLinear goes out as a table. ratedU1 then carries the neutral ratio and the importer applies the model on top, so the round trip keeps the model. A branch without a model, or with a model in the from-side reciprocal convention (DTF, hand-built), keeps the single-step PhaseTapChangerLinear that carries the solved shift exactly. Ratio tap changers export as RatioTapChanger without a table.

Converter units

CGMES 2.4.15 has no PowerElectronicsConnection. A prosumer of that component type (a CGMES 3.0 import with cgmes_import.map_power_electronics) goes out as an EquivalentInjection, not as a SynchronousMachine, which would give it a machine's short-circuit contribution on a re-import. The EQ carries minP, maxP, minQ, maxQ and regulationCapability, the SSH p, q, regulationStatus and regulationTarget (kV): a voltage-regulating unit regulates its own bus at its set point, the others state false and a target of 0. One notice counts the units written this way.

A re-import reads an EquivalentInjection, so the CIM class changes. The export writes IdentifiedObject.description = PowerElectronicsConnection on it, and the importer gives such an injection the converter component type again. The re-imported unit has the same P, Q and regulation; its Q limits come back as the minQ/maxQ hull like a machine's (equal for a symmetric pair), its P limits are not read. EquivalentInjection has no converter fault current (maxIFault) and no ratedS, so in the short circuit the unit has no record, feeds nothing and flags its island: Ik''max there is a lower bound, also with short_circuit.converter_current_factor set. A tool that ignores the description reads a plain EquivalentInjection without a short-circuit contribution.

Short-circuit data

The export writes the short-circuit data the network carries: the harvest of a CGMES import, the generators of a PowSyBl import with their reactances and ratings, the feeder of a case file. A record belongs to the unit with its mRID, else to the unit of its bus that carries its name, else to the unit of its class on a bus with one record of that class. A bus whose reference unit is typed as a generator but carries a feeder record gets that unit written as the ExternalNetworkInjection the record describes. A network without source data gives a delivery without it, and the short-circuit run on that delivery is refused (Short-Circuit Analysis).

A machine on a wind or solar unit keeps its class: a record that names WindGeneratingUnit or SolarGeneratingUnit (converter_unit, from a CGMES delivery or an IIDM generator with energySource WIND or SOLAR) writes the unit of the SynchronousMachine as that class with the same mRID. Every other unit goes out as a plain GeneratingUnit. A WindGeneratingUnit carries windGenUnitType as delivered; where the source has none (an IIDM import, a delivery without the attribute) the export writes onshore, since CGMES 2.4.15 requires the attribute. A re-import finds the class again, and the short circuit takes the machine as a converter current source again.

Scope

Not produced: tap and machine controller wiring (tap changers carry range and position, not the controller logic), ratio tap tables, and the DC modeling of a source delivery. An ACLineSegment carries one charging value, which reads back in two halves; a line whose two terminal shunt arms differ (the network-side admittance of a PowSyBl dangling line) gets the symmetric part on the segment and the excess of each terminal as a LinearShuntCompensator at that bus, like the MATPOWER export. The delivery cannot say that the compensator belongs to the line, so an outage of the line leaves it in place; the notices name every such line. A two-winding transformer follows the same rule, since the importer reads its end admittances back as one total, half on each terminal: unequal arms (PowSyBl) write the smaller arm on both ends and the excess as a compensator, the from excess seen through the tap. Import: CGMES Import.