Demo5 CimSparql - statnett/Talk2PowerSystem GitHub Wiki

Intro

Task #131

The statnett/CimSparql project includes complex real-world queries that were implemented to provide data scientists with convenient access to CIM data.

This wiki page contains AI-generated analysis of each query. The analysis is automatically produced by ChatGPT 5 (using a custom GPT), instructed to provide:

  • a natural language question that bes summarises the query
  • a very brief explanation of what the query does (TLDR)
  • a detailed explanation of the way the query works

Each Original SPARQL is followed by a subsection Fixed SPARQL that:

  • Rationalizes/simplifies when possible
  • Adapts the query for Nordic+Telemark (which e.g. don't keep different profiles in different repos and don't use SPARQL Federation).
  • Replaces each external param (eg cim:IdentifiedObject.name '${rate}') with an uppercase variable (eg cim:IdentifiedObject.name '$RATE'). This is used during query development, then we will parameterize before adding to our Q&A dataset

TOC

ac_lines

Question

Which AC line segments (with electrical parameters, connectivity, status, limits, and loss assignment per terminal) exist in a given region, and what are their key properties?

TLDR;

Returns one row per cim:ACLineSegment in the selected ${region} with R/X/B/G, base voltage, length, connectivity nodes, terminal-connected flags, service status, optional active power limit named ${rate}, and assigns total active power losses to the terminal with the lower export (via SvPowerFlow.p). Filters out self-loops, incomplete terminals, disabled-for-analysis equipment, and lines with fewer than two terminals.

Explanation

Scope & prefixes. The query operates on IEC CIM v16 plus a Statnett extension (SN:) and XSD. It expects equipment data to be reachable via an ENTSO-E equipment graph IRI obtained from ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo and then accessed in a federated SERVICE ?eq_repo { ... } block.

Core extraction (SERVICE block).

  • Selects each ?acline a cim:ACLineSegment and reads its series/shunt parameters:
    • cim:ACLineSegment.r?r
    • cim:ACLineSegment.x?x
    • cim:ACLineSegment.bch?b
    • Optional cim:ACLineSegment.gch?g
  • Identifiers: cim:IdentifiedObject.mRID (?mrid) and cim:IdentifiedObject.name (?name).
  • Nominal voltage via the base voltage chain: cim:ConductingEquipment.BaseVoltage / cim:BaseVoltage.nominalVoltage?un.
  • Physical length: cim:Conductor.length?length.
  • Terminals & topology:
    • ?terminal cim:Terminal.ConductingEquipment ?acline
    • ?terminal cim:Terminal.ConnectivityNode ?con_node
    • Terminal sequence number ?nr from cim:Terminal.sequenceNumber or cim:ACDCTerminal.sequenceNumber (covers AC/DC terminal property alternatives).
  • Topology → Substation → Area:
    • ?con_node cim:ConnectivityNode.ConnectivityNodeContainer / cim:VoltageLevel.Substation ?substation
    • ?substation cim:Substation.Region / cim:SubGeographicalRegion.Region / cim:IdentifiedObject.name ?area
  • Optional equipment-level flag: SN:Equipment.networkAnalysisEnable ?analysis_enabled (used later to exclude equipment unless analysis is enabled; defaults to True if absent).
  • Optional active power limit: Finds an cim:ActivePowerLimit that belongs to a set attached to this ?acline, with a specific name ${rate}, and returns its cim:ActivePowerLimit.value as ?rate.

State & flows (outside SERVICE).

  • Optional steady-state service status: inverse path from cim:SvStatus: ?acline ^cim:SvStatus.ConductingEquipment / cim:SvStatus.inService ?in_service.
  • Terminal online flag and active power:
    • ?terminal cim:ACDCTerminal.connected ?connected
    • Optional flow ?p via ?terminal ^cim:SvPowerFlow.Terminal / cim:SvPowerFlow.p ?p.

Terminal-specific splitting (node 1 vs. node 2). Using BIND with the terminal sequence number ?nr:

  • For ?nr = 1, populate ?p_1 (as string), ?connected_1, and ?connectivity_node_1; otherwise set empty string ('') or False.
  • For ?nr = 2, similarly populate ?p_2, ?connected_2, and ?connectivity_node_2. This pattern lets later MAX() aggregation recover the single non-empty value among grouped rows.

Regional filter. FILTER (REGEX(?area, '${region}')) keeps only equipment whose area name matches the provided ${region regex.

Aggregation & computed outputs. Grouping key is ?acline, so results collapse to one row per line segment. The SELECT uses extensive MAX() aggregation to pick the single meaningful value from each group:

  • Scalar picks: ?mrid, ?name, ?r, ?x, ?b, ?g, ?un, ?length, ?rate, ?connectivity_node_1, ?connectivity_node_ 2.
  • Status: ?status is COALESCE(MAX(?in_service), MAX(?connected_1) && MAX(?connected_2), True):
    1. Prefer explicit SvStatus.inService;
    2. Else, both terminals must be connected;
    3. Else default True (assume in service if no info).
  • Loss allocation per terminal: Convert the (string-bounded) ?p_1/?p_2 to doubles via xsd:double(MAX(?p_1)), etc.
    • Compute total active power loss as MAX(?p_1) + MAX(?p_2).
    • If p_1 < p_2, assign this sum to ?ploss_1, else 0.
    • If p_1 > p_2, assign the sum to ?ploss_2, else 0. This ensures only one terminal receives the loss number (ties yield 0 on both sides).
  • Rationale for MAX() on strings/booleans:
    • For strings, the non-empty string is lexicographically greater than '', so MAX() selects the populated one.
    • For booleans, True > False, so MAX() returns True if any grouped row has it.

Post-aggregation filters (HAVING). The result keeps only “valid” line segments:

  1. Not a self-loop: MAX(?connectivity_node_1) != MAX(?connectivity_node_2)
  2. Both terminals present and non-empty.
  3. At least two grouped rows (two terminals): COUNT(*) > 1.
  4. Equipment is enabled for analysis if the flag exists: COALESCE(MAX(?analysis_enabled), True).

Parameters you supply:

  • ${region} — a regex to match area names (e.g., ^NO[12]$).
  • ${rate} — the name of the ActivePowerLimit to fetch (e.g., "NTC summer").

Overall, this produces a clean, one-row-per-ACLineSegment table with electrical characteristics, topology endpoints, operational status, optional limits, and a deterministic loss allocation to one of the two terminals.

Original SPARQL

# Name: AC Lines
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT
# Below a lot of max aggregation is performed. For variables without any suffix (_1 og _2) all values within the
# group are equal and we might as well just pick the maximum value. For variables with the suffix _1 og _2, the
# value we want to extract either exist as one entry in the group. The other entry is an empty string. Since
# all strings are greater than the empty string, max-aggregation will pick the value we want. For the boolan
# value ?connected_1 (or_2), the default value is False. By a similar argument True is larger than False,
# thus max-aggregation will pick True if it exists in the group
(MAX(?mrid) AS ?mrid) (MAX(?name) AS ?name)
# If SvPower at node 1 is smaller than SvPower at node 2, we assign the loss (sum of the two) to node_1. In case
# SvPower at node 1 is larger than on node 2, the loss is assigned to node_2
(IF(xsd:double(MAX(?p_1)) < xsd:double(MAX(?p_2)),xsd:double(MAX(?p_1)) + xsd:double(MAX(?p_2)),xsd:double(0.0)) AS ?ploss_1) (IF(xsd:double(MAX(?p_1)) > xsd:double(MAX(?p_2)),xsd:double(MAX(?p_1)) + xsd:double(MAX(?p_2)),xsd:double(0.0)) AS ?ploss_2) (MAX(?r) AS ?r) (MAX(?rate) AS ?rate) (COALESCE(MAX(?in_service), MAX(?connected_1) && MAX(?connected_2), True) AS ?status) (MAX(?un) AS ?un) (MAX(?x) AS ?x) (MAX(?b) AS ?b) (MAX(?g) AS ?g) (MAX(?length) AS ?length) (MAX(?connectivity_node_1) AS ?connectivity_node_1) (MAX(?connectivity_node_2) AS ?connectivity_node_2) WHERE {
    # TODO: Check if SN:Equipment.networkAnalysisEnable is on it's way into the standard
    # Assume π equivalent with at least r,x and b shunt.
    # Extract properties for ACLineSegments
    ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo
    SERVICE ?eq_repo {
        ?acline cim:ACLineSegment.r ?r ;
        cim:ACLineSegment.x ?x ;
        cim:ACLineSegment.bch ?b ;
        cim:IdentifiedObject.mRID ?mrid ;
        cim:IdentifiedObject.name ?name ;
        cim:ConductingEquipment.BaseVoltage/cim:BaseVoltage.nominalVoltage ?un ;
        cim:Conductor.length ?length .
        ?terminal cim:Terminal.ConductingEquipment ?acline ;
        cim:Terminal.ConnectivityNode ?con_node ;
        cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber ?nr .
        # Find substation of each connectivity node of the terminals above
        ?con_node cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation ?substation ;
        cim:IdentifiedObject.mRID ?connectivity_node .
        # Find area and optionally bidzone for each substation
        ?substation cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area .
        OPTIONAL {
            ?acline cim:ACLineSegment.gch ?g .
        }
        OPTIONAL {
            ?acline SN:Equipment.networkAnalysisEnable ?analysis_enabled .
        }
        # If exists, extract active power limit for acline
        OPTIONAL {
            ?_lim cim:OperationalLimit.OperationalLimitSet/cim:OperationalLimitSet.Equipment ?acline ;
            a cim:ActivePowerLimit ;
            cim:IdentifiedObject.name '${rate}' ;
            cim:ActivePowerLimit.value ?rate .
        }
    } .
    OPTIONAL {
        # Search for acline SV status will be combined with SSH connected
        ?acline ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service .
    }
    # Find properties in TP/SV/SSH profile for each terminal.
    ?terminal cim:ACDCTerminal.connected ?connected .
    OPTIONAL {
        ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p .
    }
    # Create variables for node 1 and node 2
    BIND (IF(?nr = 1,STR(?p),'') AS ?p_1)
    BIND (IF(?nr = 1,?connected,False) AS ?connected_1)
    BIND (IF(?nr = 2,STR(?p),'') AS ?p_2)
    BIND (IF(?nr = 2,?connected,False) AS ?connected_2)
    BIND (IF(?nr = 1,?connectivity_node,'') AS ?connectivity_node_1)
    BIND (IF(?nr = 2,?connectivity_node,'') AS ?connectivity_node_2)
    FILTER (REGEX(?area,'${region}'))
}
GROUP BY ?acline
# Filtration rules
# 1) We don't need lines connecting nodes to themselves
# 2) Only extract lines where at least two nodes exist
# 3) Only extract lines where SN:Equipment.networkAnalysisEnable is True (if the field exists)
HAVING ((MAX(?connectivity_node_1) != MAX(?connectivity_node_2)) && (MAX(?connectivity_node_1) != "") && (MAX(?connectivity_node_2) != "") && (COUNT(*) > 1) && COALESCE(MAX(?analysis_enabled), True))```

Query Rationalization

Posted as https://github.com/statnett/cimsparql/issues/369

The original query can be simplified significantly:

  • It selects both terminals of acline and their params in one set of variables (?terminal, ?p etc)
  • Then dispatches each to separate variables:

Terminal-specific splitting (node 1 vs. node 2). Using BIND with the terminal sequence number ?nr: For ?nr = 1, populate ?p_1 (as string), ?connected_1, and ?connectivity_node_1; otherwise set empty string ('') or False...

  • then does an ugly hack: MAX aggregation on strings and booleans

The simplified query below simply selects ?terminal1 with sequenceNumber=1 and ?terminal2 with sequenceNumber=2, and their characteristics in similarly numbered variables. No GROUP, HAVING, or MAX is needed.

  • Fixed prop name from SN:Equipment.networkAnalysisEnable to cim:Equipment.networkAnalysisEnabled

Fixed SPARQL

# Name: AC Lines
PREFIX cim: <https://cim.ucaiug.io/ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT
  # Rather than using max aggregation, we pick two terminals by sequenceNumber=1,2
  ?mrid ?name ?area
  # If SvPower at node 1 is smaller than SvPower at node 2, we assign the loss (sum of the two) to node1.
  # In case SvPower at node 1 is larger than on node 2, the loss is assigned to node 2
  (IF(?p1<?p2, ?p1+?p2, "0"^^xsd:float) AS ?ploss1)
  (IF(?p1>?p2, ?p1+?p2, "0"^^xsd:float) AS ?ploss2)
  ?r $RATE ?rate
  (COALESCE(?in_service, ?connected1 && ?connected2, True) AS ?status)
  ?un ?x ?b ?g ?length ?connectivity_node1 ?connectivity_node2
WHERE {
    # Assume π equivalent with at least r, x and b shunt.
    # Extract properties for ACLineSegments
    ?acline cim:ACLineSegment.r ?r ;
        cim:ACLineSegment.x ?x ;
        cim:ACLineSegment.bch ?b ;
        cim:IdentifiedObject.mRID ?mrid ;
        cim:IdentifiedObject.name ?name ;
        cim:ConductingEquipment.BaseVoltage/cim:BaseVoltage.nominalVoltage ?un ;
        cim:Conductor.length ?length .
    # Fetch Terminal1 with sequenceNumber=1 and Terminal2 with sequenceNumber=2
    ?terminal1 cim:Terminal.ConductingEquipment ?acline ;
        cim:Terminal.ConnectivityNode ?con_node1 ;
        cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber 1 .
    ?terminal2 cim:Terminal.ConductingEquipment ?acline ;
        cim:Terminal.ConnectivityNode ?con_node2 ;
        cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber 2 .
    # Find substation of the connectivity node of terminal1.
    # terminal2 might be connected to different substation, but it will be in the same area, so we use only terminal1
    ?con_node1 cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation ?substation ;
        cim:IdentifiedObject.mRID ?connectivity_node1 .
    ?con_node2
        cim:IdentifiedObject.mRID ?connectivity_node2 .
    # Find area for the substation (TODO: and optionally bidzone)
    ?substation cim:Substation.Region/cim:IdentifiedObject.name ?area .
    OPTIONAL {?acline cim:ACLineSegment.gch ?g .}
    OPTIONAL {?acline cim:Equipment.networkAnalysisEnabled ?analysis_enabled .}
    # If exists, extract active power limit for acline. In our dataset, there are limits only on Terminal1
    OPTIONAL {
        ?_lim cim:OperationalLimit.OperationalLimitSet/cim:OperationalLimitSet.Terminal ?terminal1 ;
          a cim:ActivePowerLimit ;
          cim:IdentifiedObject.name $RATE ;
          cim:ActivePowerLimit.value ?rate .
    }
    # Search for acline SV status will be combined with SSH connected
    OPTIONAL {?acline ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service .}
    # Find properties in TP/SV/SSH profile for each terminal.
    ?terminal1 cim:ACDCTerminal.connected ?connected1 .
    ?terminal2 cim:ACDCTerminal.connected ?connected2 .
    OPTIONAL {?terminal1 ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p1 .}
    OPTIONAL {?terminal2 ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p2 .}

    # Filtration rules
    # 0) Only lines in desired $REGION
    # FILTER (REGEX(?area,$REGION))
    # 1) We don't need lines connecting nodes to themselves
    FILTER(?connectivity_node1 != ?connectivity_node2)
    # 2) Only extract lines where at least two nodes exist: guaranteed
    # 3) Only extract lines where Equipment.networkAnalysisEnabled is True (if the field exists)
    FILTER(COALESCE(?analysis_enabled, True))
}

associated_switches

Question

For each non-switch piece of conducting equipment, which nearby switches share a connectivity node with it (i.e., are associated at the same node), and what are their mRIDs and names?

TLDR;

Finds, per non-switch ConductingEquipment, all connected switches that have at least two terminals (so they can actually connect/disconnect), then aggregates the switches’ mRIDs and names into comma‑separated lists.

Explanation

  • Core idea: Start from a piece of cim:ConductingEquipment (?equipment), walk to its terminals and the shared cim:ConnectivityNode, then back out to other conducting equipment on that same node. Among those neighbors, keep only those that are cim:Switch instances.
  • Graph path: ?equipment ^cim:Terminal.ConductingEquipment / cim:Terminal.ConnectivityNode / ^cim:Terminal.ConnectivityNode / cim:Terminal.ConductingEquipment ?switch This means: from ?equipment ←(has Terminal)— go to its ConnectivityNode — then to all other Terminals on that node — and finally to their ConductingEquipment (?switch). This finds equipment that is co‑incident at the same node as ?equipment.
  • Ensure the neighbor is a switch: ?switch cim:Switch.normalOpen ?_normalOpen leverages the Switch-specific property to both type‑narrow to switches and capture their normal open state.
  • Require a usable (two-terminal) switch: ?switch ^cim:Terminal.ConductingEquipment / cim:ACDCTerminal.sequenceNumber 2 ensures the switch has a terminal numbered 2, excluding single‑terminal switches that cannot actually connect/disconnect circuits.
  • Exclude the starting equipment if it’s a switch: MINUS { ?equipment cim:Switch.normalOpen ?_open } removes any ?equipment that is itself a switch; the query is about switches associated to other equipment.
  • Outputs & aggregation:
    • SAMPLE(?equipment_mrid) and SAMPLE(?name) pick a representative mRID and name for the grouped ?equipment (since we GROUP BY ?equipment these are stable per group).
    • GROUP_CONCAT(?switch_mrid) and GROUP_CONCAT(?switch_name) aggregate all associated switches’ identifiers and names into comma‑separated strings.
  • Result: One row per non-switch conducting equipment, listing all co‑located (same node) operational switches with at least two terminals.

Original SPARQL

# Name: Associated switches
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT (SAMPLE(?equipment_mrid) AS ?mrid) (SAMPLE(?name) AS ?name) (GROUP_CONCAT(?switch_mrid; SEPARATOR=',') AS ?switch_mrids) (GROUP_CONCAT(?switch_name; SEPARATOR=',') AS ?switch_names) WHERE {
    {
        ?equipment ^cim:Terminal.ConductingEquipment/cim:Terminal.ConnectivityNode/^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment ?switch ;
        cim:IdentifiedObject.name ?name ;
        cim:IdentifiedObject.mRID ?equipment_mrid .
        ?switch cim:Switch.normalOpen ?_normalOpen ;
        cim:IdentifiedObject.mRID ?switch_mrid ;
        cim:IdentifiedObject.name ?switch_name ;
        # Only included switches that has a "second" terminal. There are switches
        # that only have one terminal. These switches can not connect/disconnect anything
        ^cim:Terminal.ConductingEquipment/cim:ACDCTerminal.sequenceNumber 2 .
    }
    MINUS {
        ?equipment cim:Switch.normalOpen ?_open
    }
}
GROUP BY ?equipment

Query Rationalization

https://github.com/statnett/cimsparql/issues/370

  • It's good to pose the requested type at the beginning
  • Checking for cim:Switch.normalOpen (then not using the value) is a weird way to find switches. Better use the type (we have subclass reasoning, else use rdf:type/rdfs:subClassOf*)
  • The MINUS is a weird way to say "The equipment should not be a switch": changed to FILTER NOT EXISTS
  • We can use the derived relation cimr:connectedTo
  • Rather than SAMPLE it's better to add these attributes to GROUP BY

Fixed SPARQL

# Name: Associated switches
PREFIX cim: <https://cim.ucaiug.io/ns#>
SELECT ?mrid ?name
  (GROUP_CONCAT(?switch_mrid; SEPARATOR=',') AS ?switch_mrids)
  (GROUP_CONCAT(?switch_name; SEPARATOR=',') AS ?switch_names)
WHERE {
  ?equipment a cim:ConductingEquipment; cimr:connectedTo ?switch ;
    cim:IdentifiedObject.name ?name ;
    cim:IdentifiedObject.mRID ?mrid .
  filter not exists {?equipment a cim:Switch}
  ?switch a cim:Switch ;
    cim:IdentifiedObject.mRID ?switch_mrid ;
    cim:IdentifiedObject.name ?switch_name ;
    # Only included switches that has a "second" terminal. There are switches
    # that only have one terminal. These switches can not connect/disconnect anything
    cim:ConductingEquipment.Terminals/cim:ACDCTerminal.sequenceNumber 2 .
}
GROUP BY ?mrid ?name

base_voltage

Question

What is the average operating voltage observed on each BaseVoltage and what are the corresponding BaseVoltage mRID and nominal voltage (Un)?

TLDR;

For each cim:BaseVoltage, compute the average of positive SvVoltage.v values on its topological nodes, then return that average alongside the BaseVoltage’s mRID and nominal voltage Un via a federated lookup.

Explanation

The query is structured in two parts:

  1. Aggregation subquery (operating voltage per BaseVoltage)

    • It starts from each TopologicalNode (?top_node) and walks backwards along the property path ^cim:SvVoltage.TopologicalNode to find all cim:SvVoltage state variables that reference that node.
    • For each such SvVoltage, it reads the measured/estimated voltage cim:SvVoltage.v as ?voltage and the node’s associated cim:TopologicalNode.BaseVoltage as ?base_voltage.
    • It filters out non‑positive readings: FILTER (xsd:double(STR(?voltage)) > 0.0). The STR(...) then xsd:double(...) casts ensure a numeric comparison regardless of the literal’s original datatype.
    • It groups by ?base_voltage and computes AVG(...) across all included SvVoltage.v values to produce ?operating_voltage. The result is one row per BaseVoltage with its average observed operating voltage.
  2. Federated enrichment (nominal Un and identifiers)

    • The triple ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo provides the endpoint IRI of an equipment repository in ?eq_repo.
    • A SERVICE ?eq_repo { ... } block then dereferences each ?base_voltage to:
      • fetch its nominal voltage cim:BaseVoltage.nominalVoltage as ?un, and
      • fetch its identifier cim:IdentifiedObject.mRID as ?mrid.
    • The outer SELECT returns ?mrid (BaseVoltage ID), ?un (nominal), and the aggregated ?operating_voltage side by side, letting you compare actual average operating voltage against the BaseVoltage’s nominal Un.

Result semantics: Each result row corresponds to a single cim:BaseVoltage resource: its unique mRID, its nominal voltage Un, and the average of positive SvVoltage.v values observed on all topological nodes that reference that BaseVoltage.

Original SPARQL

# Name: Base voltage
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?mrid ?un ?operating_voltage {
    {
        SELECT ?base_voltage (AVG(xsd:double(STR(?voltage))) AS ?operating_voltage) {
            ?top_node ^cim:SvVoltage.TopologicalNode/cim:SvVoltage.v ?voltage ;
            cim:TopologicalNode.BaseVoltage ?base_voltage .
            FILTER (xsd:double(STR(?voltage)) > 0.0)
        }
        GROUP BY ?base_voltage
    }
    ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
    SERVICE ?eq_repo {
        # Find the nominal voltage from the base voltage associated with the topological node
        ?base_voltage cim:BaseVoltage.nominalVoltage ?un ;
        cim:IdentifiedObject.mRID ?mrid
    }
}

Query Rationalization

  • Don't need to use xsd:double(STR(..)) shenanigans since our literals have proper datatypes
  • We have all inverses, so don't need to use inverse prop paths (^) and the field names read more natural (notice the parallelism of the two ?top_node fields below)

Fixed SPARQL

PREFIX cim: <https://cim.ucaiug.io/ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?mrid ?un ?operating_voltage {
    # Find nominal voltages of all base voltages
    ?base_voltage cim:BaseVoltage.nominalVoltage ?un ;
        cim:IdentifiedObject.mRID ?mrid
    {
        SELECT ?base_voltage (AVG(?voltage) AS ?operating_voltage) {
            ?top_node
              cim:TopologicalNode.SvVoltage/cim:SvVoltage.v ?voltage ;
              cim:TopologicalNode.BaseVoltage ?base_voltage .
            FILTER (?voltage > 0.0)
        }
        GROUP BY ?base_voltage
    }
}

borders

Question

Which AC lines connect two different geographical regions (borders), and what are their terminal and market details—limited to lines touching a given ${region}?

TLDR;

Find ACLineSegments whose two terminals lie in different top-level regions, optionally return their market code, include terminal mRIDs, and restrict results to lines where either end matches ${region}. Excludes HVDC and only includes equipment enabled for network analysis (or missing that flag).

Explanation

  • Target equipment The query selects cim:ACLineSegment instances and extracts their identifiers and names:

    • ?acline a cim:ACLineSegment ; cim:IdentifiedObject.mRID ?mrid ; cim:IdentifiedObject.name ?name .
  • Optional metadata Two optional lookups enrich the lines:

    • ?market_code: via the equipment container to a SN:Line extension: cim:Equipment.EquipmentContainer / SN:Line.marketCode ?market_code
    • ?_analysis_enabled: Statnett extension flag on the equipment: SN:Equipment.networkAnalysisEnable ?_analysis_enabled
  • Terminals and area derivation Each AC line should have two terminals. The query explicitly fetches both ends by sequence number:

    • For terminal 1 (?terminal1): (cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber) 1
    • For terminal 2 (?terminal2): (cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber) 2 The union | in the property path covers models where the sequence number may be stated on either cim:Terminal or cim:ACDCTerminal.

    From each terminal it climbs the network containment hierarchy to get the top-level region name:

    Terminal
      → cim:Terminal.ConnectivityNode
      → cim:ConnectivityNode.ConnectivityNodeContainer
      → cim:VoltageLevel.Substation
      → cim:Substation.Region
      → cim:SubGeographicalRegion.Region
      → cim:IdentifiedObject.name  → ?area_1 / ?area_2
    

    It also keeps each terminal’s mRID as ?t_mrid_1 and ?t_mrid_2.

  • Border logic (different regions) FILTER (?area_1 != ?area_2) ensures the two terminal ends sit in different top-level regions—i.e., the line crosses a regional border.

  • Region scoping FILTER (REGEX(?area_1,'${region}') || REGEX(?area_2,'${region}')) limits results to lines that touch the user-supplied ${region} on either end. (Regex is case-sensitive unless your engine defaults otherwise.)

  • Exclude HVDC lines FILTER (!REGEX(?name,'HVDC')) removes lines whose name contains “HVDC”, focusing the result set on AC interconnectors. (This relies on naming convention.)

  • Network-analysis enablement (default permissive) The flag is made truthy-by-default using:

    BIND (COALESCE(?_analysis_enabled, True) AS ?analysis_enabled)
    FILTER (?analysis_enabled)
    

    If the SN:Equipment.networkAnalysisEnable property is missing, the line is treated as enabled; if present and false, the line is excluded.

  • Output columns The SELECT projects:

    • Line identity: ?mrid, ?name
    • Optional market: ?market_code
    • Areas at each end: ?area_1, ?area_2
    • Terminal identities: ?t_mrid_1, ?t_mrid_2

Overall, the query returns AC inter-regional tie-lines (borders) relevant to a given ${region}, with helpful terminal and market context.

Original SPARQL

# Name: Borders
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?mrid ?name ?market_code ?area_1 ?area_2 ?t_mrid_1 ?t_mrid_2 WHERE {
    # Extract mRID, name and optionally market_code for aclines
    ?acline a cim:ACLineSegment ;
    cim:IdentifiedObject.mRID ?mrid ;
    cim:IdentifiedObject.name ?name .
    OPTIONAL {
        ?acline cim:Equipment.EquipmentContainer/SN:Line.marketCode ?market_code
    } .
    OPTIONAL {
        ?acline SN:Equipment.networkAnalysisEnable ?_analysis_enabled .
    }
    # Extract properties for the terminal connected to one end of the acline (sequenceNumber 1)
    ?terminal1 a cim:Terminal ;
    cim:Terminal.ConductingEquipment ?acline ;
    cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber 1 ;
    cim:Terminal.ConnectivityNode/cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area_1 ;
    cim:IdentifiedObject.mRID ?t_mrid_1 .
    # Extract properties for the terminal connected to the other end of the acline (sequenceNumber 2)
    ?terminal2 a cim:Terminal ;
    cim:Terminal.ConductingEquipment ?acline ;
    cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber 2 ;
    cim:Terminal.ConnectivityNode/cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area_2 ;
    cim:IdentifiedObject.mRID ?t_mrid_2 .
    FILTER (?area_1 != ?area_2)
    FILTER (REGEX(?area_1,'${region}') || REGEX(?area_2,'${region}'))
    FILTER (!REGEX(?name,'HVDC')) # Ignore HVDC
    BIND (COALESCE(?_analysis_enabled, True) AS ?analysis_enabled)
    FILTER (?analysis_enabled)
}

Fixed SPARQL

PREFIX cim: <https://cim.ucaiug.io/ns#>
SELECT ?mrid ?name ?market_code ?area_1 ?area_2 ?t_mrid_1 ?t_mrid_2 WHERE {
    # Extract mRID, name and optionally market_code for aclines
    ?acline a cim:ACLineSegment ;
      cim:IdentifiedObject.mRID ?mrid ;
      cim:IdentifiedObject.name ?name .
    # We don't have any prop "marketCode"
    OPTIONAL {?acline cim:Equipment.EquipmentContainer/cim:Line.marketCode ?market_code}
    OPTIONAL {?acline cim:Equipment.networkAnalysisEnabled ?_analysis_enabled}
    # Extract properties for the terminal connected to one end of the acline (sequenceNumber 1)
    ?terminal1 a cim:Terminal ;
      cim:Terminal.ConductingEquipment ?acline ;
      cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber 1 ;
      cim:Terminal.ConnectivityNode/cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:IdentifiedObject.name ?area_1 ;
      cim:IdentifiedObject.mRID ?t_mrid_1 .
    # Extract properties for the terminal connected to the other end of the acline (sequenceNumber 2)
    ?terminal2 a cim:Terminal ;
      cim:Terminal.ConductingEquipment ?acline ;
      cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber 2 ;
      cim:Terminal.ConnectivityNode/cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:IdentifiedObject.name ?area_2 ;
      cim:IdentifiedObject.mRID ?t_mrid_2 .
    FILTER (?area_1 != ?area_2)
    #FILTER (REGEX(?area_1,'${region}') || REGEX(?area_2,'${region}'))
    FILTER (!REGEX(?name,'HVDC')) # Ignore HVDC
    FILTER (COALESCE(?_analysis_enabled, True))
}

branch_node_withdraw

Question

Which connected terminals of AC line segments, series compensators, and power transformers within a given region are associated with which topological nodes, and what are their instantaneous active/reactive power flows (p, q) and connectivity-node mRIDs—considering only equipment enabled for network analysis?

TLDR;

Filters to ACLineSegment / SeriesCompensator / PowerTransformer terminals in a target region, pulls their SvPowerFlow (p, q), resolves the terminal’s topological node via either the connectivity node or the terminal, defaults networkAnalysisEnable to true, and returns only connected, analysis-enabled results.

Explanation

  1. Equipment-scope subquery (inside SERVICE ?eq_repo)

    • ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo . locates the Equipment profile repository/graph IRI and binds it to ?eq_repo.
    • SERVICE ?eq_repo { ... } runs the inner pattern specifically against that Equipment graph.
    • VALUES ?rdf_type { cim:ACLineSegment cim:SeriesCompensator cim:PowerTransformer } restricts the query to the three branch-like equipment classes.
    • ?component a ?rdf_type . picks each equipment instance of those classes.
    • OPTIONAL { ?component SN:Equipment.networkAnalysisEnable ?_analysis_enabled . } captures a vendor/extension flag that can disable an equipment from analysis.
    • Each equipment’s terminal is retrieved:
      • ?terminal cim:Terminal.ConductingEquipment ?component ;
      • cim:IdentifiedObject.mRID ?mrid ; — note: here ?mrid is the terminal’s mRID (not the equipment’s).
      • cim:Terminal.ConnectivityNode ?con_node .
    • The terminal’s connectivity node mRID is exposed: ?con_node cim:IdentifiedObject.mRID ?connectivity_node .
    • Regional filter: Navigates from the connectivity node up the container hierarchy to an area name:
      ?con_node
        cim:ConnectivityNode.ConnectivityNodeContainer/
        cim:VoltageLevel.Substation/
        cim:Substation.Region/
        cim:SubGeographicalRegion.Region/
        cim:IdentifiedObject.name ?area .
      
      Then FILTER (REGEX(?area,'${region}')) restricts results to the requested region (placeholder ${region} is meant to be substituted by the caller).
  2. Status and state variables (outside the SERVICE block)

    • ?terminal cim:ACDCTerminal.connected ?connected . keeps only terminals currently in the connected state (later enforced by a filter).
    • Joins to the SV (state variables) profile to bring in the terminal’s power flow:
      ?_sv_t cim:SvPowerFlow.Terminal ?terminal ;
             cim:SvPowerFlow.p ?p ;
             cim:SvPowerFlow.q ?q .
      
  3. Resolving the topological node (two paths with UNION)

    • Preferred path via connectivity node: ?con_node cim:ConnectivityNode.TopologicalNode ?topological_node .
    • Fallback when CN→TN mapping is absent:
      • FILTER NOT EXISTS { ?con_node cim:ConnectivityNode.TopologicalNode ?topological_node }
      • Use terminal’s mapping instead: ?terminal cim:Terminal.TopologicalNode ?topological_node .
    • Expose the topological node identifier: ?topological_node cim:IdentifiedObject.mRID ?node .
  4. Analysis-enable defaulting and final filters

    • BIND (COALESCE(?_analysis_enabled, True) AS ?analysis_enabled) treats missing networkAnalysisEnable as True.
    • FILTER (?analysis_enabled) and FILTER (?connected) yield only analysis-enabled and currently connected terminals.
  5. Outputs

    • SELECT ?mrid ?node ?p ?q ?connectivity_node
    • For each qualifying terminal:
      • ?mrid: terminal mRID
      • ?node: associated topological node mRID
      • ?p, ?q: active/reactive power flow from SvPowerFlow
      • ?connectivity_node: the CN mRID the terminal is attached to

Net effect: The query “withdraws”/extracts branch-end (terminal-level) injections for specified branch equipment in a region, mapped to the operative topological nodes, constrained to equipment that is both analysis-enabled (or unspecified) and electrically connected.

Original SPARQL

# Name: Branch node withdraw
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?mrid ?node ?p ?q ?connectivity_node WHERE {
  {
    SELECT * WHERE {
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      SERVICE ?eq_repo {
        # Specify components wer are interested in
        VALUES ?rdf_type { cim:ACLineSegment cim:SeriesCompensator cim:PowerTransformer } .
        ?component a ?rdf_type .
        OPTIONAL {
          ?component SN:Equipment.networkAnalysisEnable ?_analysis_enabled .
        }
        ?terminal cim:Terminal.ConductingEquipment ?component ;
                  cim:IdentifiedObject.mRID ?mrid ;
                  cim:Terminal.ConnectivityNode ?con_node .
        ?con_node cim:IdentifiedObject.mRID ?connectivity_node .
        # Find area for the connectivity node of the terminal
        ?con_node cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area .
        FILTER (REGEX(?area,'${region}'))
      }
    }
  }
  # Extract properties of each terminal from the SV/TP/SSH profile.
  # If exists, the mRID of the topological node associated with the terminal is etracted
  ?terminal cim:ACDCTerminal.connected ?connected .
  ?_sv_t cim:SvPowerFlow.Terminal ?terminal ;
         cim:SvPowerFlow.p ?p ;
         cim:SvPowerFlow.q ?q .
  {
    ?con_node cim:ConnectivityNode.TopologicalNode ?topological_node .
  }
  UNION {
    FILTER NOT EXISTS {
      ?con_node cim:ConnectivityNode.TopologicalNode ?topological_node
    }
    ?terminal cim:Terminal.TopologicalNode ?topological_node .
  }
  ?topological_node cim:IdentifiedObject.mRID ?node .
  # Assign analysis enabled (all True if not exist)
  BIND (COALESCE(?_analysis_enabled, True) AS ?analysis_enabled)
  FILTER (?analysis_enabled)
  FILTER (?connected)
}

Query Rationalization

https://github.com/statnett/cimsparql/issues/371

  • Cosmetic: put single-triple OPTIONAL patterns on one line
  • Cosmetic: remove superfluous . (before and after }, before FILTER and OPTIONAL)
  • Instead of UNION ... FILTER NOT EXISTS to select amongst two alternatives, we use two consecutive OPTIONAL. This is a beautiful trick that's very logical when you grok it:
    • If the first OPTIONAL succeeds then it will bind ?topological_node
    • Which will preclude the second OPTIONAL from selecting another value for ?topological_node
  • (Note: I've moved ?topological_node cim:IdentifiedObject.mRID inside the OPTIONAL to match the comment "If exists, the mRID of the topological node...")

Fixed SPARQL

PREFIX cim: <https://cim.ucaiug.io/ns#>
SELECT ?mrid ?node ?p ?q ?connectivity_node WHERE {
  # Specify components we are interested in
  VALUES ?rdf_type { cim:ACLineSegment cim:SeriesCompensator cim:PowerTransformer }
  ?component a ?rdf_type .
  OPTIONAL {?component cim:Equipment.networkAnalysisEnabled ?_analysis_enabled}
  FILTER (COALESCE(?_analysis_enabled, True))
  ?terminal cim:Terminal.ConductingEquipment ?component ;
            cim:IdentifiedObject.mRID ?mrid ;
            cim:Terminal.ConnectivityNode ?con_node .
  ?con_node cim:IdentifiedObject.mRID ?connectivity_node .
  # Find area for the connectivity node of the terminal
  ?con_node cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:IdentifiedObject.name ?area .
  # FILTER (REGEX(?area,$REGION))
  # Extract properties of each terminal from the SV/TP/SSH profile.
  ?terminal cim:ACDCTerminal.connected ?connected .
  FILTER (?connected)
  ?_sv_t cim:SvPowerFlow.Terminal ?terminal ;
         cim:SvPowerFlow.p ?p ;
         cim:SvPowerFlow.q ?q .
  # If exists, the mRID of the topological node associated with the terminal is extracted
  optional {?con_node cim:ConnectivityNode.TopologicalNode ?topological_node. ?topological_node cim:IdentifiedObject.mRID ?node}
  optional {?terminal cim:Terminal.TopologicalNode         ?topological_node. ?topological_node cim:IdentifiedObject.mRID ?node}
}

bus

Question

Which topological buses (nodes) exist in a given region, what are their names, voltages, substations, bidding zones, and state‑variable voltages, and which of them act as swing buses?

TLDR;

Return one row per TopologicalNode (bus) filtered by region, enriched with equipment (EQ) data for nominal/base voltage and substation context, plus real‑time SvVoltage, island membership, bidding zone (if any), and a boolean flag indicating whether the bus is the island angle reference (swing bus).

Explanation

This SPARQL query targets a Common Information Model (CIM) dataset with multiple profiles (TP/SV/EQ/SSH) and some profile extensions. It composes information primarily from the Topology Processing (TP) and State Variables (SV) profiles, and federates to the Equipment (EQ) repository to fetch nominal voltages and substation/region context.

Prefixes

  • cim: Core CIM 16 schema.
  • SN: Statnett CIM extension.
  • entsoeSecretariat: ENTSO‑E profile extension used for EIC codes.

Core node selection (TP/SV)

?top_node
  cim:TopologicalNode.BaseVoltage ?base_voltage ;
  cim:TopologicalNode.ConnectivityNodeContainer ?container ;
  cim:IdentifiedObject.mRID ?mrid ;
  cim:IdentifiedObject.name ?busname ;
  ^cim:SvVoltage.TopologicalNode/cim:SvVoltage.v ?sv_voltage .
  • Selects each TopologicalNode (a bus).
  • Captures its:
    • BaseVoltage link (?base_voltage).
    • Owning container (?container), typically a VoltageLevel.
    • CIM identifiers/name (?mrid, ?busname).
    • Current state variable voltage magnitude (?sv_voltage) via a reverse property path: ^cim:SvVoltage.TopologicalNode finds the SvVoltage tied to this node, then cim:SvVoltage.v gets the value.

Optional island context (TP)

OPTIONAL {
  ?top_node ^cim:TopologicalIsland.TopologicalNodes/cim:IdentifiedObject.name ?island_name .
}
OPTIONAL {
  ?top_node ^cim:TopologicalIsland.AngleRefTopoligicalNode ?angle_ref .
}
  • First block: gets the topological island name that contains the node (reverse path from island to its nodes).
  • Second block: checks whether this node is the angle reference of its island (i.e., the swing bus). If the triple is present, ?angle_ref is bound.

Dynamic federation to the EQ repository

?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
SERVICE ?eq_repo { ... }
  • The dataset contains a triple that binds ?eq_repo to the SPARQL endpoint (or service graph IRI) for the Equipment profile.
  • SERVICE ?eq_repo { ... } uses this dynamically discovered endpoint/graph to pull equipment‑side metadata:
    • From the node’s BaseVoltage:
      ?base_voltage cim:BaseVoltage.nominalVoltage ?un ;
                    cim:IdentifiedObject.mRID ?base_voltage_mrid .
      
      Retrieves nominal voltage (?un) and the BaseVoltage mRID.
    • From the node’s container (VoltageLevel) up to its Substation:
      ?container cim:VoltageLevel.Substation ?_substation .
      ?_substation
        cim:IdentifiedObject.mRID ?substation_mrid ;
        cim:IdentifiedObject.name ?substation_name ;
        cim:Substation.Region/
          cim:SubGeographicalRegion.Region/
          cim:IdentifiedObject.name ?area .
      
      • Finds the hosting Substation, its mRID/name, and the enclosing Geographical/SubGeographical Region name (?area).
    • Optional bidding area via Statnett + ENTSO‑E extensions:
      OPTIONAL {
        ?_substation SN:Substation.MarketDeliveryPoint/
                     SN:MarketDeliveryPoint.BiddingArea/
                     entsoeSecretariat:IdentifiedObject.energyIdentCodeEIC ?bidzone
      }
      
      • If present, returns the EIC code of the bidding zone (?bidzone).

Regional filter

FILTER REGEX(?area,'${region}')
  • Keeps only rows whose ?area matches the supplied region parameter (string/regex). This parameter is intended to be injected by the client at runtime.

Derived flags and defaults

BIND (IF(BOUND(?angle_ref),True,False) AS ?is_swing_bus)
BIND (COALESCE(?island_name, "Unknown") AS ?island)
  • ?is_swing_bus: True if the optional AngleRefTopoligicalNode triple existed for the node (node is the island’s angle reference), else False.
  • ?island: uses the island name if found; otherwise the literal "Unknown".

Output columns (SELECT)

  • ?node — the TopologicalNode mRID (?mrid as ?node).
  • ?busname — Topological node (bus) name.
  • ?substation — Substation name.
  • ?un — Nominal voltage (from BaseVoltage.nominalVoltage).
  • ?base_voltage_mrid — BaseVoltage mRID.
  • ?substation_mrid — Substation mRID.
  • ?bidzone — Optional EIC code of the bidding area for the substation.
  • ?sv_voltage — Measured/estimated SV voltage magnitude at the bus.
  • ?island — Topological island name (or "Unknown").
  • ?is_swing_bus — Boolean flag indicating swing bus status.

Notes & assumptions

  • The reverse paths (^...) are used to traverse from TopologicalNode back to related resources defined in other classes.
  • The federation pattern assumes your dataset exposes a triple that maps to a valid EQ SERVICE target; without it, the SERVICE ?eq_repo { ... } block would not execute.
  • The filter uses a regex over ?area; tune it (e.g., anchors / case‑insensitive flag) as needed for your region strings.

Original SPARQL

# Name: Bus
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX entsoeSecretariat: <http://entsoe.eu/Secretariat/ProfileExtension/1#>
SELECT (?mrid AS ?node) ?busname (?substation_name AS ?substation) ?un ?base_voltage_mrid ?substation_mrid ?bidzone ?sv_voltage ?island ?is_swing_bus WHERE {
  # Extract propertoes from the TP/SV/SSH profile for each topological node
  ?top_node cim:TopologicalNode.BaseVoltage ?base_voltage ;
            cim:TopologicalNode.ConnectivityNodeContainer ?container ;
            cim:IdentifiedObject.mRID ?mrid ;
            cim:IdentifiedObject.name ?busname ;
            ^cim:SvVoltage.TopologicalNode/cim:SvVoltage.v ?sv_voltage .
  OPTIONAL {
    ?top_node ^cim:TopologicalIsland.TopologicalNodes/cim:IdentifiedObject.name ?island_name .
  }
  OPTIONAL {
    ?top_node ^cim:TopologicalIsland.AngleRefTopoligicalNode ?angle_ref .
  }
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    # Find the nominal voltage from the base voltage associated with the topological node
    ?base_voltage cim:BaseVoltage.nominalVoltage ?un ;
                  cim:IdentifiedObject.mRID ?base_voltage_mrid .
    # Extract properties from the connectivity node container associated with each topoligical node
    ?container cim:VoltageLevel.Substation ?_substation .
    # Extract information about the substation associated with the connectivity node container
    ?_substation cim:IdentifiedObject.mRID ?substation_mrid ;
                 cim:IdentifiedObject.name ?substation_name ;
                 cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area .
    # Extract the bidzone of each substation if it exists
    OPTIONAL {
      ?_substation SN:Substation.MarketDeliveryPoint/SN:MarketDeliveryPoint.BiddingArea/entsoeSecretariat:IdentifiedObject.energyIdentCodeEIC ?bidzone
    }
  } .
  FILTER REGEX(?area,'${region}')
  BIND (IF(BOUND(?angle_ref),True,False) AS ?is_swing_bus)
  BIND (COALESCE(?island_name, "Unknown") AS ?island)
}

Query Rationalization

  • AngleRefTopologicalNode was misspelled, fixed
  • Fix namespace and casing of eu:IdentifiedObject.energyIdentCodeEic
    • However, neither the latest CIM/NC nor Nordic44/Telemark120 have such data: SN:Substation.MarketDeliveryPoint/SN:MarketDeliveryPoint.BiddingArea
  • Reverse the direction of several props so we don't need to use inverse prop path
  • Simplify IF(...., True, False) to simply ...
  • Note: on Nordic44, the query appears to return duplicate results (having same names and attributes") until you notice that there are Buses (TopologicalNodes) with the same name but different mRID, eg
47eb7c33-d0f6-11e7-9f7b-b46d83638f70 KRISTIANSAND
47eb7c30-d0f6-11e7-9f7b-b46d83638f70 KRISTIANSAND

Fixed SPARQL

PREFIX eu: <https://cim.ucaiug.io/ns/eu#>
PREFIX cim: <https://cim.ucaiug.io/ns#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>

SELECT ?node ?busname ?substation ?un ?base_voltage_mrid ?substation_mrid ?bidzone ?sv_voltage ?island ?is_swing_bus
WHERE {
  # Extract properties from the TP/SV/SSH profile for each topological node
  ?top_node cim:TopologicalNode.BaseVoltage ?base_voltage ;
            cim:TopologicalNode.ConnectivityNodeContainer ?container ;
            cim:IdentifiedObject.mRID ?node ;
            cim:IdentifiedObject.name ?busname ;
            cim:TopologicalNode.SvVoltage/cim:SvVoltage.v ?sv_voltage .
  OPTIONAL {?top_node cim:TopologicalNode.TopologicalIsland/cim:IdentifiedObject.name ?island_name}
  OPTIONAL {?top_node ^cim:TopologicalIsland.AngleRefTopologicalNode ?angle_ref}
  # Find the nominal voltage from the base voltage associated with the topological node
  ?base_voltage cim:BaseVoltage.nominalVoltage ?un ;
                cim:IdentifiedObject.mRID ?base_voltage_mrid .
  # Extract properties from the connectivity node container associated with each topoligical node
  ?container cim:VoltageLevel.Substation ?_substation .
  # Extract information about the substation associated with the connectivity node container
  ?_substation cim:IdentifiedObject.mRID ?substation_mrid ;
               cim:IdentifiedObject.name ?substation ;
               cim:Substation.Region/cim:IdentifiedObject.name ?area .
  # Extract the bidzone of each substation if it exists.
  # NOTE: no such props in latest CIM/NC, and no such data in Nordic44/Telemark120
  OPTIONAL {?_substation SN:Substation.MarketDeliveryPoint/SN:MarketDeliveryPoint.BiddingArea/eu:IdentifiedObject.energyIdentCodeEic ?bidzone}
  #FILTER REGEX(?area,$REGION)
  BIND (BOUND(?angle_ref) AS ?is_swing_bus)
  BIND (COALESCE(?island_name, "Unknown") AS ?island)
}

connectivity_nodes

Question

Which active connectivity nodes exist in the network, along with their container (substation or line), voltage level, bidding zone, and related identifiers?

TLDR;

This query retrieves all active connectivity nodes and details about their associated container (either a substation or a line), including voltage level, bidding zone, container name, and IDs.

Explanation

The query identifies active connectivity nodes in the CIM model and collects metadata about their context within the network.

  1. Active Connectivity Nodes

    • The inner subquery checks if each connectivity node is linked to any conducting equipment with SN:Equipment.networkAnalysisEnable set to True (supports both typed and string "true" values).
    • Only nodes with at least one such active link (?num_active > 0) are included.
  2. Connectivity Node Container

    • Every connectivity node belongs to a ConnectivityNodeContainer, which can be either:
      • A Substation (via a VoltageLevel)
      • A Line (directly)
  3. Substation branch (first block)

    • For substations:
      • Retrieves the BaseVoltage and its nominal voltage (?un) and mRID.
      • Retrieves substation metadata: mRID, name, and the geographical region (?area).
      • Attempts to find the bidding zone:
        • First tries to get it directly from the substation's MarketDeliveryPoint.
        • If none exists, runs another subquery to find it from nearby substations connected via conducting equipment.
      • Assigns container type "Substation".
  4. Line branch (second block)

    • For lines:
      • Retrieves line metadata: name, mRID, and area.
      • Since lines have no direct BaseVoltage, it searches for voltage via connected equipment and picks the highest nominal voltage found (by ordering in descending numeric value and limiting to 1).
      • Assigns container type "Line".
  5. Common output fields

    • ?mrid: Connectivity node ID
    • ?container: ID of the containing substation or line
    • ?container_name: Name of the container
    • ?un: Nominal voltage (kV)
    • ?base_voltage_mrid: Voltage level ID
    • ?bidzone: Bidding zone EIC code
    • ?container_type: Either "Substation" or "Line"
  6. Filtering

    • The FILTER REGEX(?area,'.*') ensures that ?area is bound, effectively requiring a geographical area to be associated with the node.

Overall, this query is useful for network analysis and market integration, as it links the physical network topology (connectivity nodes) with market and voltage information.

Original SPARQL

# Name: Connectivity nodes
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX entsoeSecretariat: <http://entsoe.eu/Secretariat/ProfileExtension/1#>
SELECT ?mrid (?container_mrid AS ?container) ?container_name ?un ?base_voltage_mrid ?bidzone ?container_type WHERE {
  ?con_node cim:ConnectivityNode.ConnectivityNodeContainer ?con_node_container ;
            cim:IdentifiedObject.mRID ?mrid .
  {
    SELECT ?con_node (COUNT(*) AS ?num_active) {
      # Support both typed and not typed networkAnalysisEnable flag
      VALUES ?true { True "true" }
      ?con_node ^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment/SN:Equipment.networkAnalysisEnable ?true
    }
    GROUP BY ?con_node
  }
  FILTER (?num_active > 0)
  # Extract properties from the connectivity node container associated with each topoligical node
  {
    ?con_node_container cim:VoltageLevel.BaseVoltage ?base_voltage ;
                        cim:VoltageLevel.Substation ?_substation .
    ?base_voltage cim:BaseVoltage.nominalVoltage ?un ;
                  cim:IdentifiedObject.mRID ?base_voltage_mrid .
    # Extract information about the substation associated with the connectivity node container
    ?_substation cim:IdentifiedObject.mRID ?container_mrid ;
                 cim:IdentifiedObject.name ?container_name ;
                 cim:Substation.Region/cim:IdentifiedObject.name ?area .
    # Extract the bidzone of each substation if it exists
    OPTIONAL {
      ?_substation SN:Substation.MarketDeliveryPoint/SN:MarketDeliveryPoint.BiddingArea/entsoeSecretariat:IdentifiedObject.energyIdentCodeEIC ?direct_bidzone
    } .
    {
      FILTER (!BOUND(?bidzone))
      # When an EIC code does not exist, look for an EIC code in neighbouring nodes
      {
        SELECT (MAX(?related_bidzone) AS ?nearby_bidzone) WHERE {
          # Collect bidzones from substations connected to the current substation via a ConductingEquipment
          ?con_node ^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment/^cim:Terminal.ConductingEquipment/cim:Terminal.ConnectivityNode/cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/SN:Substation.MarketDeliveryPoint/SN:MarketDeliveryPoint.BiddingArea/entsoeSecretariat:IdentifiedObject.energyIdentCodeEIC ?related_bidzone .
        }
      }
    }
    BIND ("Substation" AS ?container_type)
    BIND (COALESCE(?direct_bidzone, ?nearby_bidzone) AS ?bidzone)
  }
  UNION {
    ?con_node_container cim:Line.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area ;
                        cim:IdentifiedObject.name ?container_name ;
                        cim:IdentifiedObject.mRID ?container_mrid
    # Extract voltage levels connectivity nodes related via a conducting equipment (cim:Line has no cim:VoltageLeve.BaseVoltage)
    {
      SELECT ?base_voltage_mrid ?un {
        ?con_node ^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment/^cim:Terminal.ConductingEquipment/cim:Terminal.ConnectivityNode/cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.BaseVoltage ?base_voltage .
        ?base_voltage cim:IdentifiedObject.mRID ?base_voltage_mrid ;
                      cim:BaseVoltage.nominalVoltage ?un
      }
      ORDER BY DESC(xsd:double(STR(?un)))
      LIMIT 1
    }
    BIND ("Line" AS ?container_type)
  }
  FILTER REGEX(?area,'.*')
}

Query Rationalization

  • Replaced GROUP BY and FILTER (?num_active > 0) with FILTER EXISTS
  • Fixed prop name from SN:Equipment.networkAnalysisEnable to cim:Equipment.networkAnalysisEnabled and filter out false only (treat missing as true)
  • Removed the selection of ?area (which is not returned) and the vacuous filter FILTER REGEX(?area,'.*')
  • Nordic44 doesn't have SN:Substation.MarketDeliveryPoint/SN:MarketDeliveryPoint.BiddingArea so I removed these optional branches
  • Nordic44 doesn't have any cim:ConnectivityNodeContainer.ConnectivityNodes relations for cim:Line (see Nordic44#77, CIM4NoUtility#406) so the second branch "Line" of the query finds nothing. The first issue suggests how the missing relations can be added with a CONSTRUCT query: I added a similar clause below
  • Please note that some Telemark-120 voltages have values that look a bit strange: e.g. "2.3E-1"^^xsd:float means 230V.

Fixed SPARQL

PREFIX cim: <https://cim.ucaiug.io/ns#>

SELECT ?mrid ?container ?container_name ?un ?base_voltage_mrid ?container_type
WHERE {
  ?con_node a cim:ConnectivityNode; cim:IdentifiedObject.mRID ?mrid .
  filter exists {
    ?con_node cim:ConnectivityNode.Terminals/cim:Terminal.ConductingEquipment ?equipment
    filter not exists {?equipment cim:Equipment.networkAnalysisEnabled false}
  }
  {
    # Extract properties of Substation, which is connectivity node container associated with the connectivity node
    ?con_node cim:ConnectivityNode.ConnectivityNodeContainer ?con_node_container.
    ?con_node_container cim:VoltageLevel.BaseVoltage ?base_voltage ;
                        cim:VoltageLevel.Substation ?_substation .
    ?base_voltage cim:BaseVoltage.nominalVoltage ?un ;
                  cim:IdentifiedObject.mRID ?base_voltage_mrid .
    # Extract information about the substation associated with the connectivity node container
    ?_substation cim:IdentifiedObject.mRID ?container ;
                 cim:IdentifiedObject.name ?container_name .
    BIND ("Substation" AS ?container_type)
  }
  UNION {
    # Find ConnectivityNodes belonging to Line through ACLineSegment
    ?con_node_container a cim:Line;
        cim:IdentifiedObject.name ?container_name ; cim:IdentifiedObject.mRID ?container ;
        cim:EquipmentContainer.Equipments ?segment.
    # Extract voltage levels from ACLineSegment
    ?segment a cim:ACLineSegment; cim:ConductingEquipment.Terminals/cim:Terminal.ConnectivityNode ?con_node;
        cim:ConductingEquipment.BaseVoltage ?base_voltage.
    ?base_voltage cim:IdentifiedObject.mRID ?base_voltage_mrid ;
      cim:BaseVoltage.nominalVoltage ?un
    BIND ("Line" AS ?container_type)
  }
}

converter_hvdc_bidzones

Question

For each HVDC converter, which two bidding zones (market codes) are connected by the DC line that starts at the converter’s associated market delivery point?

TLDR;

Find each converter’s pole, get its substation’s market delivery point and bidding zone (bidzone_1), then follow the DC line that departs from that delivery point to the opposite end’s bidding zone (bidzone_2), and return pairs where the zones differ.

Explanation

This query maps HVDC converters to the pair of bidding zones they bridge, using Statnett (SN) and Alstom (ALG) CIM extensions alongside the CIM core namespace (cim).

1) Namespaces

  • cim: — core IEC CIM (e.g., IdentifiedObject, Equipment, EquipmentContainer).
  • SN: — Statnett CIM extension (e.g., Substation.MarketDeliveryPoint, BiddingArea.marketCode, Line.FromMarketDeliveryPoint).
  • ALG: — Alstom CIM extension for DC converters (e.g., DCConverter.DCPole, VoltageSourceConverter.DCPole).

2) Selecting converters and their poles

?converter ALG:DCConverter.DCPole | ALG:VoltageSourceConverter.DCPole ?pole ;
           cim:IdentifiedObject.mRID ?mrid .
  • Uses a property path union (|) to cover two model variants: classical DC converters and voltage‑source converters, both linking a converter to its ?pole.
  • Retrieves the converter’s external identifier ?mrid.

3) From the pole to its market delivery point and bidding zone

?pole cim:Equipment.EquipmentContainer/SN:Substation.MarketDeliveryPoint ?delivery_point .
?delivery_point SN:MarketDeliveryPoint.BiddingArea/SN:BiddingArea.marketCode ?bidzone_1 .
  • Uses a chained property path (/) to traverse:

    • cim:Equipment.EquipmentContainer → the container (typically a Substation) that holds the pole.
    • SN:Substation.MarketDeliveryPoint → the substation’s MarketDeliveryPoint (?delivery_point).
  • From that delivery point, it reaches its BiddingArea and extracts the market code as ?bidzone_1.

4) Following the outgoing line to the opposite bidding zone

?line SN:Line.FromMarketDeliveryPoint ?delivery_point ;
      SN:Line.ToMarketDeliveryPoint/SN:MarketDeliveryPoint.BiddingArea/SN:BiddingArea.marketCode ?bidzone_2
  • Finds a Line where the FromMarketDeliveryPoint equals the converter’s ?delivery_point.
  • Traverses the ToMarketDeliveryPoint end to its BiddingArea and captures that end’s market code as ?bidzone_2.

5) Ensuring cross-zone connections and uniqueness

FILTER (?bidzone_1 != ?bidzone_2)
  • Keeps only inter‑zone connections (i.e., the two ends are in different bidding zones).
  • SELECT DISTINCT removes duplicates that could arise from multiple modelling paths, multiple poles per converter, or multiple lines starting from the same delivery point.

6) Output

SELECT DISTINCT ?mrid ?bidzone_1 ?bidzone_2
  • Returns the converter’s identifier and the pair of market codes representing the two bidding zones bridged by the line originating at that converter’s market delivery point.

Notes & assumptions

  • The pole’s EquipmentContainer is a Substation that has exactly one SN:Substation.MarketDeliveryPoint; if several exist, DISTINCT will deduplicate results.
  • Directionality is taken from SN:Line.FromMarketDeliveryPointSN:Line.ToMarketDeliveryPoint. If the dataset also models the reverse direction or multiple lines per delivery point, you may see multiple ?bidzone_2 values (still deduplicated).
  • The union on ALG properties makes the query resilient to different converter modelling styles (LCC vs VSC).

Original SPARQL

# Name: Converter HVDC bidzones
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX ALG: <http://www.alstom.com/grid/CIM-schema-cim15-extension#>
SELECT DISTINCT ?mrid ?bidzone_1 ?bidzone_2 WHERE {
  ?converter ALG:DCConverter.DCPole | ALG:VoltageSourceConverter.DCPole ?pole ;
             cim:IdentifiedObject.mRID ?mrid .
  ?pole cim:Equipment.EquipmentContainer/SN:Substation.MarketDeliveryPoint ?delivery_point .
  ?delivery_point SN:MarketDeliveryPoint.BiddingArea/SN:BiddingArea.marketCode ?bidzone_1 .
  # Extract the destination point from the line that has one end matching bidzone_1
  ?line SN:Line.FromMarketDeliveryPoint ?delivery_point ;
        SN:Line.ToMarketDeliveryPoint/SN:MarketDeliveryPoint.BiddingArea/SN:BiddingArea.marketCode ?bidzone_2
  FILTER (?bidzone_1 != ?bidzone_2)
}
  • ALG:DCConverter.DCPole is now standardized as nc:DCConverterUnit.DCPole
  • ALG:VoltageSourceConverter.DCPole does not exist
  • Cannot refactor this query because we don't have any info about MarketDeliveryPoint

converters

Question

Which converters exist in the system, along with their key electrical parameters, connectivity details, substation associations, control factors, and loss characteristics?

TLDR;

This SPARQL query retrieves detailed information about all converters in the network, including identifiers, names, alias names, power values (P, Q), substation details, status, connectivity nodes, controllers and participation factors, pole and converter losses, and voltage ratings.

Explanation

The query is structured to gather comprehensive converter-related data by combining multiple nested queries, optional patterns, and bindings. Here’s how it works step-by-step:

  1. Repository Setup It starts by locating the relevant CIM equipment repository via:
    ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
    

Then it uses a SERVICE call to query that repository for the actual converter data.

  1. Core Converter Details Inside the service, it identifies converters by matching:

    • cim:IdentifiedObject.mRID → unique ID (?mrid)
    • cim:IdentifiedObject.name → human-readable name (?name)
    • Associated poles via either ALG:VoltageSourceConverter.DCPole or ALG:DCConverter.DCPole.
  2. Controller & Participation Factor It fetches the pole’s controller mRID and calculates the average participation factor:

    MAX(xsd:float(STR(?controller_factor))) / COUNT(*) AS ?controller_factor
    

    grouped per pole.

  3. Alias Name (Optional) If available, cim:IdentifiedObject.aliasName is retrieved.

  4. Terminal & Connectivity It finds the converter’s terminal with sequenceNumber = 1 and traces its connectivity node:

    • Node mRID (?connectivity_node)
    • Container → VoltageLevel → Substation → Region (?substation_mrid, ?un voltage level, ?area region name).
  5. Optional Network Analysis Enable Flag From SN:Equipment.networkAnalysisEnable, defaults to True if missing.

  6. Loss Parameters (Optional) Retrieves converter loss coefficients (loss0, loss1, loss2) and DC nominal voltage (vdcn) from poles, defaulting to 0.0 when absent.

  7. Filtering Only converters with analysis_enabled = True are kept.

  8. Active & Reactive Power Retrieves cim:ACDCConverter.p (active power) and cim:ACDCConverter.q (reactive power).

  9. Pole Loss & Status Optional poleLossP is retrieved. Status is determined by coalescing SvStatus.inService with terminal connection status.

  10. Final Output Variables Returns:

    • IDs: mrid, substation_mrid, connectivity_node, controller
    • Names: name, alias
    • Electrical: p, q, un, vdcn
    • Losses: pole_loss, loss0, loss1, loss2
    • Control: controller_factor
    • Status: status

Original SPARQL

# Name: Converters
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX ALG: <http://www.alstom.com/grid/CIM-schema-cim15-extension#>
SELECT ?mrid ?name ?alias ?p ?q ?substation_mrid ?status ?connectivity_node ?controller ?controller_factor ?pole_loss ?loss0 ?loss1 ?loss2 ?vdcn ?un WHERE {
  {
    SELECT * WHERE {
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      SERVICE ?eq_repo {
        # Extract mrid, name, substation and optionally aliasName of the converter
        ?converter cim:IdentifiedObject.mRID ?mrid ;
                   cim:IdentifiedObject.name ?name ;
                   ALG:VoltageSourceConverter.DCPole | ALG:DCConverter.DCPole ?pole .
        ?pole ALG:DCPole.DCController/cim:IdentifiedObject.mRID ?controller .
        {
          SELECT ?pole (MAX(xsd:float(STR(?controller_factor))) / COUNT(*) AS ?controller_factor) {
            ?converter ALG:VoltageSourceConverter.DCPole | ALG:DCConverter.DCPole ?pole .
            ?pole ALG:DCPole.participationFactor ?controller_factor .
          }
          GROUP BY ?pole
        }
        OPTIONAL {
          ?converter cim:IdentifiedObject.aliasName ?alias .
        }
        # Extract properties for the terminals for the converter
        ?terminal cim:Terminal.ConductingEquipment ?converter ;
                  cim:Terminal.ConnectivityNode ?con_node ;
                  cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber 1 .
        ?con_node cim:IdentifiedObject.mRID ?connectivity_node ;
                  cim:ConnectivityNode.ConnectivityNodeContainer ?con_node_container .
        ?con_node_container cim:VoltageLevel.Substation ?substation ;
                            cim:VoltageLevel.BaseVoltage/cim:BaseVoltage.nominalVoltage ?un .
        ?substation cim:IdentifiedObject.mRID ?substation_mrid ;
                    cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area .
        OPTIONAL {
          ?converter SN:Equipment.networkAnalysisEnable ?_analysis_enabled .
        }
        OPTIONAL {
          ?converter ALG:VoltageSourceConverter.loss0 ?_loss0 ;
                     ALG:VoltageSourceConverter.loss1 ?_loss1 ;
                     ALG:VoltageSourceConverter.loss2 ?_loss2 .
          ?pole cim:ConductingEquipment.BaseVoltage/cim:BaseVoltage.nominalVoltage ?_vdcn ;
        }
        BIND (COALESCE(?_analysis_enabled, True) AS ?analysis_enabled)
        BIND (COALESCE(?_loss0, 0.0) AS ?loss0)
        BIND (COALESCE(?_loss1, 0.0) AS ?loss1)
        BIND (COALESCE(?_loss2, 0.0) AS ?loss2)
        BIND (COALESCE(?_vdcn, 0.0) AS ?vdcn)
        FILTER (?analysis_enabled)
      }
    }
  }
  # Extract active and reactive power for the converter
  ?converter cim:ACDCConverter.p ?p ;
             cim:ACDCConverter.q ?q .
  OPTIONAL {
    ?converter cim:ACDCConverter.poleLossP ?_pole_loss .
  }
  OPTIONAL {
    ?converter ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service .
  }
  # Extract connected and optionally the mrid for the topological node associated with the terminal
  ?terminal cim:ACDCTerminal.connected ?connected .
  BIND (COALESCE(?in_service, ?connected) AS ?status)
  BIND (COALESCE(?_pole_loss, 0.0) AS ?pole_loss)
}
  • Some of the terms are now standardized as nc:DirectCurrentPoleController.DCPole, nc:DCConverterUnit.DCPole etc
  • We don't have any data about DC Convertors, so cannot refactor the query

coordinates

Question

For each substation and AC line segment, what are the (x, y) coordinates of their position points, which EPSG code are those coordinates in, and what is the resource’s mRID and RDF type?

TLDR;

Returns every PositionPoint (x/y) tied to a Location that belongs to either a cim:Substation or a cim:ACLineSegment, along with the resource mRID, the parsed EPSG code from the coordinate system URN, and the string form of the resource’s RDF class.

Explanation

  • Core pattern (position points):
    ?s cim:PositionPoint.Location ?location ;
       cim:PositionPoint.sequenceNumber ?nr ;
       cim:PositionPoint.xPosition ?x ;
       cim:PositionPoint.yPosition ?y .
    

This finds each cim:PositionPoint (?s) with its parent ?location, its ordinal ?nr, and its coordinates ?x/?y.

  • From location to the owning resource(s):

    ?location cim:Location.PowerSystemResources ?resource .
    VALUES ?resource_type { cim:Substation cim:ACLineSegment }
    ?resource a ?resource_type ;
              cim:IdentifiedObject.mRID ?mrid .
    

    Each Location may be shared by multiple resources. The VALUES clause restricts results to resources whose RDF type is either cim:Substation or cim:ACLineSegment. For the chosen resource, the query retrieves its mRID.

  • Coordinate reference system → EPSG:

    ?location cim:Location.CoordinateSystem/cim:CoordinateSystem.crsUrn ?co .
    BIND (STRAFTER(?co,'EPSG::') AS ?epsg)
    

    The path cim:Location.CoordinateSystem/cim:CoordinateSystem.crsUrn gets a CRS URN like urn:ogc:def:crs:EPSG::4326. STRAFTER extracts the EPSG code ("4326") as ?epsg.

  • Readable RDF type column:

    (STR(?resource_type) AS ?rdf_type)
    

    Converts the class IRI (cim:Substation or cim:ACLineSegment) to its lexical string (typically the full IRI) for display.

  • Intended sequencing:

    BIND (CONCAT(STR(?mrid), STR(?nr)) AS ?sequence)
    ORDER BY (?sequece)
    

    The query appears to create a sort key combining the resource mRID with the position point’s sequence number, so points for the same resource sort by ?nr. However, there are two issues:

    1. Typo in ORDER BY — it uses ?sequece instead of ?sequence, so ordering won’t apply.
    2. ?sequence not selected — it’s only used for ordering; that’s fine, but if you want to inspect it you’d need to add it to SELECT.

    Better approach: sort by the actual variables without concatenation:

    ORDER BY ?mrid xsd:integer(?nr)
    

    (Cast ?nr if it’s a string to ensure numeric ordering.)

  • What the result rows look like: Each row corresponds to one PositionPoint of a qualifying resource and contains:

    • ?mrid — the mRID of the Substation or ACLineSegment
    • ?x, ?y — the point’s coordinates
    • ?epsg — EPSG code parsed from the CRS URN
    • ?rdf_type — string form of the resource’s RDF class

Quick fixed version (minimal changes):

PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?mrid ?x ?y ?epsg (STR(?resource_type) AS ?rdf_type) WHERE {
  ?s cim:PositionPoint.Location ?location ;
     cim:PositionPoint.sequenceNumber ?nr ;
     cim:PositionPoint.xPosition ?x ;
     cim:PositionPoint.yPosition ?y .
  ?location cim:Location.PowerSystemResources ?resource ;
            cim:Location.CoordinateSystem/cim:CoordinateSystem.crsUrn ?co .
  VALUES ?resource_type { cim:Substation cim:ACLineSegment }
  ?resource a ?resource_type ;
            cim:IdentifiedObject.mRID ?mrid .
  BIND (STRAFTER(?co,'EPSG::') AS ?epsg)
}
ORDER BY ?mrid xsd:integer(?nr)

This preserves the intent, avoids the typo, and ensures a stable, numeric ordering of points per resource.

Original SPARQL

# Name: Coordinates
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?mrid ?x ?y ?epsg (STR(?resource_type) AS ?rdf_type) WHERE {
  ?s cim:PositionPoint.Location ?location ;
     cim:PositionPoint.sequenceNumber ?nr ;
     cim:PositionPoint.xPosition ?x ;
     cim:PositionPoint.yPosition ?y .
  ?location cim:Location.PowerSystemResources ?resource ;
            cim:Location.CoordinateSystem/cim:CoordinateSystem.crsUrn ?co .
  VALUES ?resource_type { cim:Substation cim:ACLineSegment }
  ?resource a ?resource_type ;
            cim:IdentifiedObject.mRID ?mrid .
  BIND (STRAFTER(?co,'EPSG::') AS ?epsg)
  BIND (CONCAT(STR(?mrid), STR(?nr)) AS ?sequence)
}
ORDER BY (?sequece)

Query Rationalization

  • We've converted coordinates to GeoSPARQL WKT:
    • Every cim:Location is also geo:Geometry with geo:asWKT (POINT or LINESTRING) that carries all point coordinates in the appropriate order
    • Individual cim:PositionPoint are deleted since they are not needed anymore
  • I added strafter to return only the localname of ?rdf_type; else I don't see why str(?rdf_type) is needed
  • Note: the query hard-codes that only cim:Substation cim:ACLineSegment have coordinates. An easy way to generalize this is to replace the VALUES clause with this:
  ?resource sesame:directType ?resource_type; cim:IdentifiedObject.mRID ?mrid .
  filter(strstarts(str(?resource_type),str(cim:)))

Where:

  • sesame:directType doesn't return inferred types (abstract supertypes)
  • filter(strstarts()) ensures that only CIM types are returned.
    • Following the GeoSPARQL specification, we've added a secondary type geo:Feature to every resource that has a Geometry

Fixed SPARQL

PREFIX sesame: <http://www.openrdf.org/schema/sesame#>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX cim: <https://cim.ucaiug.io/ns#>

SELECT ?mrid ?wkt ?epsg ?rdf_type WHERE {
  ?location cim:Location.PowerSystemResources ?resource ;
    geo:asWKT ?wkt;
    cim:Location.CoordinateSystem/cim:CoordinateSystem.crsUrn ?co .
  VALUES ?resource_type { cim:Substation cim:ACLineSegment }
  ?resource a ?resource_type; cim:IdentifiedObject.mRID ?mrid .
  bind(strafter(str(?resource_type),str(cim:)) as ?rdf_type)
  BIND (STRAFTER(?co,'EPSG::') AS ?epsg)
} ORDER BY ?mrid

dc_active_power_flow

Question

For each line segment, series compensator, and transformer end in a chosen region, what is the terminal active power (p) (from the state variables) and the implied flow direction based on the terminal’s sequence number?

TLDR;

Looks up terminals of selected equipment in a target region, joins them to SvPowerFlow.p, and returns the equipment mRID, the active power (p) at each terminal, and a direction flag +1 for terminal 1 and -1 for the opposite terminal.

Explanation

  1. Prefixes & focus

    • Uses the IEC CIM namespace (cim:) and XML Schema datatypes (xsd:).
    • The query ultimately returns three bindings per row:
      • ?mrid – the mRID of the equipment component,
      • ?p – the active power at the terminal from SvPowerFlow,
      • ?direction – a sign (+1/-1) derived from the terminal sequence number.
  2. Join to state variables (SvPowerFlow)

    • Outside the SERVICE block:
      ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p .
      
      The reverse path ^cim:SvPowerFlow.Terminal finds the SvPowerFlow row whose Terminal is ?terminal, then extracts its p value. This ensures the power is per terminal (not per equipment).
  3. Choosing the equipment repository

    • The line:
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      
      binds ?eq_repo to the dataset/endpoint IRI that hosts the EquipmentCore model. This is then used in:
      SERVICE ?eq_repo { ... }
      
      to run the equipment/topology part of the query against that repository.
  4. Selecting components & their terminals (two cases) Inside the SERVICE:

    • Lines & series compensators

      VALUES ?rdf_type { cim:ACLineSegment cim:SeriesCompensator } .
      ?component a ?rdf_type ;
                 cim:Equipment.EquipmentContainer/(cim:Line.Region
                    | cim:VoltageLevel.Substation/cim:Substation.Region) ?region .
      ?terminal cim:Terminal.ConductingEquipment ?component ;
                cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber ?nr .
      
      • Restricts to ACLineSegment and SeriesCompensator.
      • Resolves the geographical region via two alternative paths:
        • directly from a line’s region (Line.Region), or
        • via the VoltageLevel → Substation → Region chain for equipment contained in a substation.
      • Grabs the terminal linked to the component and its sequence number (?nr). The alternation Terminal.sequenceNumber | ACDCTerminal.sequenceNumber allows either AC or DC-style terminals.
    • Transformers (by ends)

      ?p_transformer cim:Equipment.EquipmentContainer/cim:Substation.Region ?region .
      ?component cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
                 cim:TransformerEnd.Terminal ?terminal .
      BIND (1 AS ?nr)
      
      • First determine the region at the power transformer level (via its substation).
      • Then treat each PowerTransformerEnd as the ?component, retrieving its associated ?terminal.
      • Since transformer ends might not carry a meaningful sequenceNumber in the same way, it forces ?nr = 1 for consistent handling downstream.

    These two branches are combined with UNION, so the result set contains terminals from line segments, series compensators, and transformer ends.

  5. Region naming & filtering

    ?region cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?regionName .
    FILTER (REGEX(?regionName,'${region}'))
    
  • Obtains a human-readable ?regionName by traversing SubGeographicalRegion.Region → IdentifiedObject.name.
  • Applies a parameterized regex filter using the template variable ${region} (to be substituted by the caller) to restrict results to the desired region(s). The match is case-sensitive by default unless the template adds flags.
  1. Equipment identity

    ?component cim:IdentifiedObject.mRID ?mrid .
    
    • Extracts the canonical identifier for the component tied to the terminal.
  2. Direction sign from terminal ordering

    BIND (IF(?nr = 1, 1, -1) AS ?direction)
    
    • Interprets terminal sequence to produce a direction convention:

      • +1 for terminal 1,
      • -1 for any other terminal (commonly terminal 2 for two-terminal devices).
    • For transformer ends (where ?nr was bound to 1), the sign becomes +1 by construction.

  3. Result semantics

    • Each row corresponds to a specific terminal of a selected component in the chosen region.
    • ?p is the active power injection/flow at that terminal, as computed by the state variables (SvPowerFlow).
    • ?direction provides a simple convention to orient flows consistently using terminal order; consumers can multiply p * direction if they wish to align all flows to the “from-terminal-1” direction.
    • Multiple rows per equipment are expected (e.g., one per terminal on a line), allowing downstream aggregation if needed.

In summary, the query ties equipment geometry (region & container) to network terminals, joins those terminals to the state variable active power, and emits a signed terminal power based on a consistent terminal-order convention, restricted to a caller-provided region.

Original SPARQL

# Name: DC Active Power Flow
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?mrid ?p ?direction WHERE {
  ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p .
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    {
      {
        # Declare components we are interested in
        VALUES ?rdf_type { cim:ACLineSegment cim:SeriesCompensator } .
        ?component a ?rdf_type ;
                   cim:Equipment.EquipmentContainer/(cim:Line.Region | cim:VoltageLevel.Substation/cim:Substation.Region) ?region .
        # Extract properties for the terminal associated with the component
        ?terminal cim:Terminal.ConductingEquipment ?component ;
                  cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber ?nr .
      }
      UNION {
        # Extract the region for the transformer
        ?p_transformer cim:Equipment.EquipmentContainer/cim:Substation.Region ?region .
        # For each power transformer extract all windngs and terminal
        ?component cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
                   cim:TransformerEnd.Terminal ?terminal .
        BIND (1 AS ?nr)
      }
    } .
    ?region cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?regionName .
    # Extract the mRID for the component
    ?component cim:IdentifiedObject.mRID ?mrid .
    FILTER (REGEX(?regionName,'${region}'))
    BIND (IF(?nr = 1,1,-1) AS ?direction)
  }
}

Query Rationalization

Not many changes here:

  • Removed the federated SERVICE call
  • Removed cim:SubGeographicalRegion.Region since in our data all these are "NO"

Fixed SPARQL

PREFIX cim: <https://cim.ucaiug.io/ns#>
SELECT ?mrid ?p ?direction WHERE {
  ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p .
  {
    # Declare components we are interested in
    VALUES ?rdf_type { cim:ACLineSegment cim:SeriesCompensator } .
    ?component a ?rdf_type ;
               cim:Equipment.EquipmentContainer/(cim:Line.Region | cim:VoltageLevel.Substation/cim:Substation.Region) ?region .
    # Extract properties for the terminal associated with the component
    ?terminal cim:Terminal.ConductingEquipment ?component ;
              cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber ?nr .
  }
  UNION {
    # Extract the region for the transformer
    ?p_transformer cim:Equipment.EquipmentContainer/cim:Substation.Region ?region .
    # For each power transformer extract all windings and terminal
    ?component cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
               cim:TransformerEnd.Terminal ?terminal .
    BIND (1 AS ?nr)
  }
  ?region cim:IdentifiedObject.name ?regionName .
  # Extract the mRID for the component
  ?component cim:IdentifiedObject.mRID ?mrid .
  # FILTER (REGEX(?regionName,$REGION)
  BIND (IF(?nr = 1,1,-1) AS ?direction)
}

disconnected

Question

Which disconnectors are open or terminals are disconnected in the CIM network?

TLDR;

This query retrieves the identifiers (?mrid) of all disconnectors that are currently open and all terminals that are not connected.

Explanation

The query uses the CIM ontology to check for two different disconnection scenarios and combines them with a UNION so that results from either scenario are included:

  1. First block
    ?mrid a cim:Disconnector ;
          cim:Switch.open True .
    

This matches all resources of type cim:Disconnector where the property cim:Switch.open has the value True. In CIM terms, this means the disconnector is physically in an open state, preventing current flow.

  1. Second block

    ?mrid a cim:Terminal ;
          cim:Terminal.connected | cim:ACDCTerminal.connected False .
    

    This matches all resources of type cim:Terminal where either cim:Terminal.connected or cim:ACDCTerminal.connected is False. The vertical bar (|) acts as a property path “OR”, meaning it will check both possible connection properties in case the terminal is AC/DC specific. A False value means the terminal is not currently connected to the network.

The use of UNION means the final result set will include:

  • All open disconnectors (from the first block)
  • All disconnected terminals (from the second block)

This provides a combined view of components that break network continuity—either by open switching devices or by unconnected terminals.

Original SPARQL

# Name: Disconnected
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?mrid WHERE {
  {
    ?mrid a cim:Disconnector ;
          cim:Switch.open True .
  }
  UNION {
    ?mrid a cim:Terminal ;
          cim:Terminal.connected | cim:ACDCTerminal.connected False .
  }
}

Fixed SPARQL

PREFIX cim: <https://cim.ucaiug.io/ns#>
SELECT ?mrid WHERE {
  {
    ?mrid a cim:Disconnector ;
          cim:Switch.open True .
  }
  UNION {
    ?mrid a cim:Terminal ;
          cim:Terminal.connected | cim:ACDCTerminal.connected False .
  }
}

exchange

Question

Which AC line segments export power from a chosen region to outside that region, and what are their names, terminal-1 active power flow (P), topological node mRID, equipment mRID, market code, and overall in-service/connected status?

TLDR;

Finds non‑HVDC ACLineSegments whose two terminals sit in different regions (one matching ${region}, the other not), then reports terminal‑1 power flow p, node ID, market code, and a computed status (prefer inService; otherwise both terminals must be connected).

Explanation

This query stitches together state variables, equipment data (via a federated SERVICE), and topology to list AC transmission lines that represent exchanges between a specified region and its neighbors.

  1. State & connectivity at terminal level

    • ?terminal1 ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p ; cim:ACDCTerminal.connected ?connected_1 . Uses a reverse property path to pull the active power flow ?p from the SvPowerFlow state tied to ?terminal1 and reads its connected flag.
    • ?terminal2 cim:ACDCTerminal.connected ?connected_2 . Reads the connected flag for the opposite terminal.
  2. Topological node resolution (robust to model variants)

    • Either take the ConnectivityNode → TopologicalNode mapping:
      ?con_node cim:ConnectivityNode.TopologicalNode ?topological_node .
      
      OR, if that mapping is absent, fall back to the Terminal → TopologicalNode link:
      FILTER NOT EXISTS { ?con_node cim:ConnectivityNode.TopologicalNode ?topological_node }
      ?terminal1 cim:Terminal.TopologicalNode ?topological_node .
      
    • Then fetch the node identifier: ?topological_node cim:IdentifiedObject.mRID ?node .
  3. Optional computed equipment status

    • OPTIONAL { ?acline ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service } If an SvStatus.inService exists for the ?acline, capture it.
  4. Federated access to the equipment repository

    • ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo . Supplies the endpoint/graph IRI for equipment data.
    • SERVICE ?eq_repo { ... } wraps all equipment‑side lookups:
      • Identify non‑HVDC AC lines and basic IDs:
        ?acline a cim:ACLineSegment ;
                cim:IdentifiedObject.name ?name ;
                cim:IdentifiedObject.mRID ?mrid .
        FILTER (!REGEX(?name,'HVDC')) .
        
      • Optionally pull a market code via the equipment container chain:
        OPTIONAL {
          ?acline cim:Equipment.EquipmentContainer/SN:Line.marketCode ?market_code .
        }
        
      • Bind each terminal to the line and its sequence numbers:
        • ?terminal1 … sequenceNumber 1 ; Terminal.ConnectivityNode ?con_node .
        • ?terminal2 … sequenceNumber 2 ; Terminal.ConnectivityNode/(…)/IdentifiedObject.name ?area_2 .
      • Resolve region names for each terminal by traversing:
        ConnectivityNode
          → ConnectivityNodeContainer
          → VoltageLevel
          → Substation
          → Region
          → SubGeographicalRegion.Region
          → IdentifiedObject.name
        
        producing ?area_1 (via ?con_node) and ?area_2 (via ?terminal2’s node).
      • Region boundary filter (intent): keep only lines where exactly one terminal is in ${region}:
        (REGEX(?area_1,'${region}') && !REGEX(?area_2,'${region}')) ||
        (REGEX(?area_2,'${region}') && !REGEX(?area_1,'${region}'))
        

        Note: the code’s second disjunct repeats ?area_1; per the comment it should symmetrically test ?area_2—adjust if needed.

      • Optionally read an analysis toggle and default it:
        OPTIONAL { ?acline SN:Equipment.networkAnalysisEnable ?_analysis_enabled . }
        BIND (COALESCE(?_analysis_enabled, True) AS ?analysis_enabled)
        FILTER (?analysis_enabled)
        
        So equipment lacking the flag is included; explicitly false is excluded.
  5. Compute an overall status

    • BIND (COALESCE(?in_service, ?connected_1 && ?connected_2) AS ?status) Prefer the equipment’s inService; if absent, require both terminals connected.
  6. Projection

    • Returns:
      • ?name — AC line name
      • ?node — mRID of the associated TopologicalNode
      • ?mrid — ACLineSegment mRID
      • ?status — overall availability (computed)
      • ?p — active power flow at terminal 1
      • ?market_code — optional market classification

Net effect: for a template variable ${region}, the query lists cross‑boundary AC lines (non‑HVDC) that connect the region to elsewhere, including their flow, IDs, market code, and a status derived from either explicit inService or both‑ends connectivity.

Original SPARQL

# Name: Exchange
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?name ?node ?mrid ?status ?p ?market_code WHERE {
  ?terminal1 ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p ;
             cim:ACDCTerminal.connected ?connected_1 .
  ?terminal2 cim:ACDCTerminal.connected ?connected_2 .
  {
    ?con_node cim:ConnectivityNode.TopologicalNode ?topological_node .
  }
  UNION {
    FILTER NOT EXISTS {
      ?con_node cim:ConnectivityNode.TopologicalNode ?topological_node
    }
    ?terminal1 cim:Terminal.TopologicalNode ?topological_node .
  }
  ?topological_node cim:IdentifiedObject.mRID ?node .
  # Extract a connection flag that is composed of the inService flag for all conducting equipment connected
  # to an acline.
  OPTIONAL {
    ?acline ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service
  } .
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    # Extract name and mrid for aclines
    ?acline a cim:ACLineSegment ;
            cim:IdentifiedObject.name ?name ;
            cim:IdentifiedObject.mRID ?mrid .
    FILTER (!REGEX(?name,'HVDC')) .
    OPTIONAL {
      ?acline cim:Equipment.EquipmentContainer/SN:Line.marketCode ?market_code .
    }
    # Extract properties for the terminal at sequenceNumber 1 for the acline
    ?terminal1 cim:Terminal.ConductingEquipment ?acline ;
               cim:ACDCTerminal.sequenceNumber 1 ;
               cim:Terminal.ConnectivityNode ?con_node .
    # Extract from the connectivity node of terminal1
    ?con_node cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area_1 .
    # Extract propertoes for the terminal at squenceNumber 2 for the acline
    ?terminal2 cim:Terminal.ConductingEquipment ?acline ;
               cim:ACDCTerminal.sequenceNumber 2 ;
               cim:Terminal.ConnectivityNode/cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area_2 .
    # When a region is provided: only keep results that satisfy either
    # 1) area_1 = region and area_2 != region
    # 2) area_2 = region and area_1 != region
    FILTER ((REGEX(?area_1,'${region}') && !REGEX(?area_2,'${region}')) || (REGEX(?area_1,'${region}') && !REGEX(?area_2,'${region}')))
    OPTIONAL {
      ?acline SN:Equipment.networkAnalysisEnable ?_analysis_enabled .
    }
  }
  BIND (COALESCE(?_analysis_enabled, True) AS ?analysis_enabled)
  FILTER (?analysis_enabled)
  # Assign an overall status. The status is derived from the first existing entry of
  # 1) The computed in_service flag for each acline
  # 2) The connected status for the terminals. If one of the terminals is off, the status is False
  BIND (COALESCE(?in_service, ?connected_1 && ?connected_2) AS ?status)
}

Query Rationalization

  • This query is "written backwards": first some terminals and connectivityNodes are picked, then they are related to ACLineSegments. That makes it hard to read, and likely inefficient. Perhaps this was dictated by the need for federated SERVICE; we don't have such a requirement so we write it in a more logical order
  • We simplify FILTER (?analysis_enabled) to FILTER NOT EXISTS {?acline cim:Equipment.networkAnalysisEnabled false} taking into account that the default value is true
  • Use the Double OPTIONAL trick to find ?topological_node

Fixed SPARQL

PREFIX cim: <https://cim.ucaiug.io/ns#>
SELECT ?name ?node ?mrid ?status ?p ?area_1 ?area_2 WHERE {
  # Extract name and mrid for aclines
  ?acline a cim:ACLineSegment ;
          cim:IdentifiedObject.name ?name ;
          cim:IdentifiedObject.mRID ?mrid .
  FILTER (!REGEX(?name,'HVDC'))
  FILTER NOT EXISTS {?acline cim:Equipment.networkAnalysisEnabled false}
  # Extract a connection flag that is composed of the inService flag for all conducting equipment connected to an acline.
  OPTIONAL {?acline ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service}

  # Extract properties for the terminal at sequenceNumber 1 for the acline
  ?terminal1 cim:Terminal.ConductingEquipment ?acline ;
    cim:ACDCTerminal.sequenceNumber 1 ;
    cim:Terminal.ConnectivityNode ?con_node ;
    ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p ;
    cim:ACDCTerminal.connected ?connected_1 .
  # Extract from the connectivity node of terminal1
  ?con_node cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:IdentifiedObject.name ?area_1 .

  # Extract properties for the terminal at sequenceNumber 2 for the acline
  ?terminal2 cim:Terminal.ConductingEquipment ?acline ;
    cim:ACDCTerminal.sequenceNumber 2 ;
    cim:ACDCTerminal.connected ?connected_2 ;
    cim:Terminal.ConnectivityNode/cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation/cim:Substation.Region/cim:IdentifiedObject.name ?area_2 .

  # Only keep results that are across areas
  FILTER(?area_1 != ?area_2)
  # Only keep results in the REGION of interest
  # FILTER (REGEX(?area_1,$REGION) || REGEX(?area_2,$REGION))

  # Find topological node from terminal1
  OPTIONAL {?con_node cim:ConnectivityNode.TopologicalNode ?topological_node. ?topological_node cim:IdentifiedObject.mRID ?node}
  OPTIONAL {?terminal1 cim:Terminal.TopologicalNode        ?topological_node. ?topological_node cim:IdentifiedObject.mRID ?node}

  # Assign an overall status. The status is derived from the first existing entry of
  # 1) The computed in_service flag for each acline
  # 2) The connected status for the terminals. If one of the terminals is off, the status is False
  BIND (COALESCE(?in_service, ?connected_1 && ?connected_2) AS ?status)
}

full_model

Question

Which models are ready to use (i.e., all their dependencies are available), and if not locally, which equipment-profile models can we list directly from the equipment repository—showing each model’s time, profile, version, and description?

TLDR;

Return a unified list of models with their key metadata.

  • From the local catalog: include only models whose md:Model.DependentOn models can be found (locally or in the equipment repo).
  • From the external equipment repo: include all equipment models, filling in missing profile/version with defaults.

Explanation

The query builds a single result set of ?model ?time ?profile ?version ?description using a UNION of two branches:

Branch A — Local models whose dependencies exist

{
  ?model md:Model.profile ?profile ;
         md:Model.scenarioTime ?time ;
         md:Model.version ?version ;
         md:Model.description ?description .
  {
    SELECT ?model (MIN(?dependency_exists) AS ?all_dependencies_exist) {
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      ?model md:Model.DependentOn ?depend_on .

      OPTIONAL {
        { ?depend_on md:Model.created ?created }                # dependency found locally
        UNION
        { SERVICE ?eq_repo { ?depend_on md:Model.created ?created } }  # or in the equipment repo
      }
      BIND (BOUND(?created) AS ?dependency_exists)
    }
    GROUP BY ?model
  }
  FILTER (?all_dependencies_exist)
}

What it does:

  • It first binds the equipment repository endpoint/graph IRI to ?eq_repo using the mapping triple ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo. This is a pattern used in this dataset to look up the service for the ENTSO-E Equipment profile.

  • For each local ?model, it enumerates its dependencies via md:Model.DependentOn ?depend_on.

  • It then tries to resolve each dependency’s creation timestamp (md:Model.created ?created) either:

    • locally, or
    • remotely via SERVICE ?eq_repo { ... } to the equipment repository.
  • BOUND(?created) yields true if that dependency can be found, false otherwise.

  • The subselect aggregates with MIN(?dependency_exists):

    • In SPARQL, MIN(false,true,...) is false if any dependency is missing; only if all are found does it become true.
  • The outer FILTER (?all_dependencies_exist) keeps only models whose every dependency was resolvable (locally or via the equipment repo).

  • For such models, the metadata come from the local graph:

    • ?time = md:Model.scenarioTime
    • ?profile, ?version, ?description from the model itself.

Branch B — Equipment repository fallback (complete equipment models)

UNION {
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    ?model md:Model.description ?description ;
           md:Model.created ?time .
    OPTIONAL { ?model md:Model.profile ?nullable_profile }
    OPTIONAL { ?model md:Model.version ?nullable_version }
    BIND (COALESCE(?nullable_profile, "http://entsoe.eu/CIM/EquipmentCore/3/1") AS ?profile)
    BIND (COALESCE(?nullable_version, "") AS ?version)
  }
}

What it does:

  • Queries the external equipment repository directly (again located via the mapping triple).

  • Pulls ?model along with:

    • ?description (required)

    • ?time from md:Model.created (note: this differs from Branch A’s scenarioTime)

    • Optional profile and version. If absent, they are defaulted:

      • profile → the Equipment profile IRI (http://entsoe.eu/CIM/EquipmentCore/3/1)
      • version → empty string ""
  • This branch ensures you still get equipment models even if you don’t have a complete local roll-up with resolved dependencies.

Combined behavior

  • The UNION merges:

    • Locally curated, dependency-complete models (with scenarioTime)
    • Raw equipment models from the equipment repository (with created as the time)
  • Output columns are harmonized as ?model ?time ?profile ?version ?description.

  • Practical effect: you get a “full model list” view that prioritizes dependency integrity for local models, while still surfacing equipment-layer models directly from their source when needed.

Original SPARQL

# Name: Full model
PREFIX md: <http://iec.ch/TC57/61970-552/ModelDescription/1#>
SELECT ?model ?time ?profile ?version ?description WHERE {
  {
    ?model md:Model.profile ?profile ;
           md:Model.scenarioTime ?time ;
           md:Model.version ?version ;
           md:Model.description ?description .
    {
      SELECT ?model (MIN(?dependency_exists) AS ?all_dependencies_exist) {
        ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
        ?model md:Model.DependentOn ?depend_on .
        # Extract created time from both tp/sv/ssh profile and eq.
        OPTIONAL {
          {
            ?depend_on md:Model.created ?created
          }
          UNION {
            SERVICE ?eq_repo {
              ?depend_on md:Model.created ?created
            }
          }
        }
        BIND (BOUND(?created) AS ?dependency_exists)
      }
      GROUP BY ?model
    }
    FILTER (?all_dependencies_exist)
  }
  UNION {
    ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
    SERVICE ?eq_repo {
      ?model md:Model.description ?description ;
             md:Model.created ?time .
      OPTIONAL {
        ?model md:Model.profile ?nullable_profile
      }
      OPTIONAL {
        ?model md:Model.version ?nullable_version
      }
      BIND (COALESCE(?nullable_profile, "http://entsoe.eu/CIM/EquipmentCore/3/1") AS ?profile)
      BIND (COALESCE(?nullable_version, "") AS ?version)
    }
  }
}

Query Rationalization

We use a new representation of models:

  • The old CIM represents models using custom namespaces md, dm
  • The new CIM represents models using DCAT: specifically namespaces dct, dcat, dcat-cim, prov
  • Inst4CIM-KG # Fix Model Representation describes the new representation in detail
  • The SPARQL Update fix-model.ru implements the transformation

In addition to refactoring the query to the new representation, we do this:

  • Simplify the complex ?all_dependencies_exist with a double filter not exists: the first one checks for a link to ?depend_on, the second one checks for appropriate type of that node.
    • In other words, we check there are no dangling dct:requires links that have no triples
  • Remove the second clause that looks in the ?eq_repo "model registry" since we don't have such
  • Use DISTINCT on dct:temporal/dcat:startDate: due to bug Talk2PowerSystem_PM#257, there are double blank nodes for dct:temporal
  • Only a quarter of models have ?version, so use OPTIONAL
  • A couple models have bilingual descriptions, so use filter(lang(?description)!="no")
  • Note: some models have several values for ?profile, but that's fine

Fixed SPARQL

PREFIX dct: <http://purl.org/dc/terms/>
PREFIX dcat: <http://www.w3.org/ns/dcat#>
SELECT ?model ?time ?profile ?version ?description WHERE {
  ?model a dcat:Dataset; dct:conformsTo ?profile.
  ?model dct:description ?description filter(lang(?description)!="no")
  optional {?model dcat:version ?version}
  {select distinct * {?model a dcat:Dataset; dct:temporal/dcat:startDate ?time}}
  filter not exists {
      ?model dct:requires ?depend_on.
      filter not exists {?depend_on a dcat:Dataset}
  }
}

gen_unit_and_sync_machine_mapping

Question

How can I retrieve the mapping between a generating unit's mRID and its corresponding synchronous machine's mRID?

TLDR;

This query returns pairs of mRIDs linking each synchronous machine to the generating unit it belongs to.

Explanation

The query uses the CIM ontology to connect a GeneratingUnit to its physical representation, a SynchronousMachine, via their mRID values.

  1. Prefixes The PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#> declares the CIM namespace, so CIM classes and properties can be referenced without the full URI.

  2. Variables Selected

    • ?gen_unit_mrid: the mRID (unique identifier) of the generating unit.
    • ?sync_machine_mrid: the mRID of the synchronous machine (physical equipment) associated with that generating unit.
  3. Triple Patterns

    • ?s cim:SynchronousMachine.GeneratingUnit/cim:IdentifiedObject.mRID ?gen_unit_mrid This uses property path notation (/) to traverse from a synchronous machine (?s), through its GeneratingUnit association, and then to the generating unit's mRID.
    • ?s cim:IdentifiedObject.mRID ?sync_machine_mrid Retrieves the mRID of the synchronous machine itself.
  4. How It Works The variable ?s represents a synchronous machine.

    • First path: follow cim:SynchronousMachine.GeneratingUnit to find the generating unit linked to ?s, then get that generating unit's mRID.
    • Second triple: directly retrieve the synchronous machine's own mRID. The result is a table where each row shows a generating unit mRID and the mRID of its associated synchronous machine.

Effectively, this query creates a logical link between the conceptual generating unit and the physical synchronous machine it is implemented by, using their CIM-defined relationships.

Original SPARQL

# Name: GeneratingUnit mrid to physical equipment mrid
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?gen_unit_mrid ?sync_machine_mrid WHERE {
  ?s cim:SynchronousMachine.GeneratingUnit/cim:IdentifiedObject.mRID ?gen_unit_mrid ;
     cim:IdentifiedObject.mRID ?sync_machine_mrid
}

Fixed SPARQL

PREFIX cim: <https://cim.ucaiug.io/ns#>
SELECT ?gen_unit_mrid ?sync_machine_mrid WHERE {
  ?s a cim:SynchronousMachine; cim:IdentifiedObject.mRID ?sync_machine_mrid;
    cim:RotatingMachine.GeneratingUnit/cim:IdentifiedObject.mRID ?gen_unit_mrid
}

hvdc

Question

Which pairs of Voltage Source Converters (VSCs) are connected by a DC line made of multiple segments, and what are the segment names and the total line resistance between them?

TLDR;

Finds VSC–VSC pairs linked by the same DC line segment(s) at terminal sequence ends 1 and 2, keeps only pairs with more than one matching segment, concatenates segment names, and sums their resistances.

Explanation

Prefixes

  • cim: and ALG: provide access to CIM and an extension namespace (Alstom) used for DC equipment.
  • xsd: is used to coerce numeric values.

Core resources

  • ALG:VoltageSourceConverter — the two end converters, bound as ?converter_1 and ?converter_2.
  • ALG:DCLineSegment — individual DC line segments forming the link between the converters, bound as ?line.

SELECT clause

  • ?converter_mrid_1, ?converter_mrid_2 — the mRIDs of the two VSCs.
  • (GROUP_CONCAT(?name; SEPARATOR="--") AS ?name) — concatenates the names of all matching DC line segments between the pair, joined by --.
  • (SUM(xsd:float(STR(?r))) AS ?r) — sums the (string-to-float coerced) resistances of those segments to get the total resistance.

How the topology match works

  1. Two converters are identified:

    ?converter_1 a ALG:VoltageSourceConverter ; cim:IdentifiedObject.mRID ?converter_mrid_1 .
    ?converter_2 a ALG:VoltageSourceConverter ; cim:IdentifiedObject.mRID ?converter_mrid_2 .
    
  2. Two terminals on the same DC line segment ?line are matched, each at a specific end by sequence number:

    • ?t_1 is a terminal on ?line with cim:ACDCTerminal.sequenceNumber 1.
    • ?t_2 is a terminal on the same ?line with cim:ACDCTerminal.sequenceNumber 2.
  3. Each terminal is required to share a ConnectivityNode with a terminal of one of the converters:

    ?t_1 cim:Terminal.ConnectivityNode/^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment ?converter_1 ;
         cim:Terminal.ConductingEquipment ?line ;
         cim:ACDCTerminal.sequenceNumber 1 .
    
    ?t_2 cim:Terminal.ConnectivityNode/^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment ?converter_2 ;
         cim:Terminal.ConductingEquipment ?line ;
         cim:ACDCTerminal.sequenceNumber 2 .
    

    The path cim:Terminal.ConnectivityNode/^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment ?converter_X means:

    • From the line terminal (?t_1 / ?t_2), go to its ConnectivityNode.
    • Inverse (^) back out to any terminal at that same node.
    • From that terminal, go to its ConductingEquipment and require that to be the respective converter. This ensures the line terminal is electrically connected to the converter via the same node (i.e., they meet at the node).
  4. The DC line segment resource itself is constrained and annotated:

    ?line a ALG:DCLineSegment ;
          ALG:DCLineSegment.dcSegmentResistance ?r ;
          cim:IdentifiedObject.name ?name .
    

Aggregation and filtering

  • GROUP BY ?converter_mrid_1 ?converter_mrid_2 groups all segments that connect the same ordered converter pair (end 1 → end 2).
  • HAVING (COUNT(*) > 1) keeps only those converter pairs that are connected by more than one matching line segment — i.e., multi‑segment DC connections.
  • Within each group, GROUP_CONCAT builds a readable list of segment names and SUM accumulates the segment resistances to obtain the total line resistance between the two converters.

Result semantics Each output row represents a pair of VSC endpoints (?converter_mrid_1, ?converter_mrid_2) connected through a DC line composed of at least two segments. The ?name field lists those segment names (joined by --), and ?r is the numeric sum of their resistances.

Original SPARQL

# Name: HVDC
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX ALG: <http://www.alstom.com/grid/CIM-schema-cim15-extension#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?converter_mrid_1 ?converter_mrid_2 (GROUP_CONCAT(?name; SEPARATOR="--") AS ?name) (SUM(xsd:float(STR(?r))) AS ?r) WHERE {
  ?converter_1 a ALG:VoltageSourceConverter ;
               cim:IdentifiedObject.mRID ?converter_mrid_1 .
  ?converter_2 a ALG:VoltageSourceConverter ;
               cim:IdentifiedObject.mRID ?converter_mrid_2 .
  ?t_1 cim:Terminal.ConnectivityNode/^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment ?converter_1 ;
       cim:Terminal.ConductingEquipment ?line ;
       cim:ACDCTerminal.sequenceNumber 1 .
  ?t_2 cim:Terminal.ConnectivityNode/^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment ?converter_2 ;
       cim:Terminal.ConductingEquipment ?line ;
       cim:ACDCTerminal.sequenceNumber 2 .
  ?line a ALG:DCLineSegment ;
        ALG:DCLineSegment.dcSegmentResistance ?r ;
        cim:IdentifiedObject.name ?name .
}
GROUP BY ?converter_mrid_1 ?converter_mrid_2
HAVING (COUNT(*) > 1)

We have no such data

loads

Question

Which loads (Conform, NonConform, and generic EnergyConsumers) in a given region are enabled for network analysis, what are their identifiers, names, substations, connectivity nodes, current status (in-service/connected), and their latest active/reactive power flows, along with any market station group?

TLDR;

Fetch all enabled loads in the specified ${region}, get their mRID, name, substation mRID, connectivity node mRID, status (in-service or, if missing, terminal-connected), real/reactive power (p/q) from SvPowerFlow, and optional market/station group.

Explanation

This query assembles per-load operational info by joining equipment, topology, status, and state-variable data—scoped to a specific region—while default-enforcing that loads must be network-analysis enabled.

1) Federated equipment lookup (subquery + SERVICE)

  • The outer pattern wraps an inner SELECT * subquery to speed execution.
  • It first finds an equipment repository graph IRI via:
?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1 ?eq_repo .

and then issues a federated SERVICE ?eq_repo { ... } call so that all equipment lookups run against that repository endpoint/graph.

2) Select load types and core load attributes

  • Restricts to three load classes:
VALUES ?load_type { cim:ConformLoad cim:NonConformLoad cim:EnergyConsumer }
  • For each ?load a ?load_type, it extracts:
  • cim:IdentifiedObject.mRID?mrid
  • cim:IdentifiedObject.name?name
  • Its container substation via cim:Equipment.EquipmentContainer / cim:VoltageLevel.Substation?substation

3) Region scoping and substation identity

  • From each ?substation, it walks up the geography:
cim\:Substation.Region / cim\:SubGeographicalRegion.Region / cim\:IdentifiedObject.name ?area
  • Keeps only substations whose ?area matches the ${region} regex:
FILTER REGEX(?area,'\${region}')
  • Captures the substation’s mRID as ?substation_mrid.

4) Optional market/station grouping

  • If present, pulls a market “station group” from a statnett extension path:
?load cim\:NonConformLoad.LoadGroup /
SN\:NonConformLoadGroup.ScheduleResource /
SN\:ScheduleResource.marketCode ?station\_group

(Optional: not all loads or load types will have it.)

5) Network-analysis enable flag with defaulting

  • Optionally reads SN:Equipment.networkAnalysisEnable into ?_network_analysis.
  • Uses BIND (COALESCE(?_network_analysis, True) AS ?network_analysis) so the default is True when the flag is absent.
  • Filters to only enabled loads: FILTER (?network_analysis).

6) Terminal and connectivity mapping

  • Still inside the SERVICE block, it maps each load to its terminals and connectivity nodes:

?terminal cim\:Terminal.ConductingEquipment ?load ;
cim\:Terminal.ConnectivityNode ?con\_node .
?con\_node cim\:IdentifiedObject.mRID ?connectivity\_node .

  • The query returns one row per (load × terminal), preserving ?terminal for later joins.

7) Outside SERVICE: operational status and power flows

  • Gets the terminal’s switch-level connection flag:

?terminal cim\:ACDCTerminal.connected ?connected .

  • Optionally retrieves equipment-level service status via SvStatus:

OPTIONAL { ?load ^cim\:SvStatus.ConductingEquipment / cim\:SvStatus.inService ?in\_service }

  • Pulls steady-state power flow linked to the terminal:

?sv\_power\_flow cim\:SvPowerFlow\.Terminal ?terminal ;
cim\:SvPowerFlow\.p ?p ;
cim\:SvPowerFlow\.q ?q .

8) Status resolution (in-service vs connected)

  • Final status is chosen by precedence:

BIND (COALESCE(?in\_service, ?connected) AS ?status)

meaning: use in_service if available; otherwise fall back to the terminal’s connected flag.

9) Result set

  • The SELECT projects:
  • ?mrid, ?name: load identity
  • ?substation_mrid: parent substation identifier
  • ?status: resolved operational status
  • ?p, ?q: active/reactive power from SvPowerFlow
  • ?station_group: optional market grouping
  • ?connectivity_node: terminal’s connectivity node mRID
  • Due to terminal joins, loads with multiple terminals will yield multiple rows (one per terminal).

Key behaviors & nuances

  • Region filter: ${region} is a parameter placeholder; the regex allows partial or case-sensitive matches depending on the engine’s defaults. Adjust the pattern for anchoring or case-insensitivity if needed (e.g., "(?i)^Nordics$").
  • Enablement default: Missing SN:Equipment.networkAnalysisEnable evaluates as enabled (True), ensuring broad inclusion unless explicitly disabled.
  • Status precedence: in_service overrides connected when both exist, offering a higher-level operational view if available.

Original SPARQL

# Name: Loads
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?mrid ?name ?substation_mrid ?status ?p ?q ?station_group ?connectivity_node WHERE {
  {
    # Appears to be significantly faster to run the query if the EQ part is executed within its own sub-query
    SELECT * WHERE {
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      SERVICE ?eq_repo {
        # Extract mRID, name and substation for each load
        VALUES ?load_type { cim:ConformLoad cim:NonConformLoad cim:EnergyConsumer }
        ?load a ?load_type ;
              cim:IdentifiedObject.mRID ?mrid ;
              cim:IdentifiedObject.name ?name ;
              cim:Equipment.EquipmentContainer/cim:VoltageLevel.Substation ?substation .
        # Extract area and mRID (referred to as 'station') for the substation of each load
        ?substation cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area ;
                    cim:IdentifiedObject.mRID ?substation_mrid .
        FILTER REGEX(?area,'${region}')
        OPTIONAL {
          ?load cim:NonConformLoad.LoadGroup/SN:NonConformLoadGroup.ScheduleResource/SN:ScheduleResource.marketCode ?station_group
        } .
        OPTIONAL {
          ?load SN:Equipment.networkAnalysisEnable ?_network_analysis
        } .
        BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
        FILTER (?network_analysis)
        # Extract mRID and connectivity node for each terminal associated with a load
        ?terminal cim:Terminal.ConductingEquipment ?load ;
                  cim:Terminal.ConnectivityNode ?con_node .
        ?con_node cim:IdentifiedObject.mRID ?connectivity_node .
      } .
    }
  }
  # Extrcact connected flag and optionally the mRID of the topological node associated with the terminal
  ?terminal cim:ACDCTerminal.connected ?connected .
  # Optionally extract in_service flag and active and reactive energy consumption for each load
  OPTIONAL {
    ?load ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service
  } .
  ?sv_power_flow cim:SvPowerFlow.Terminal ?terminal ;
                 cim:SvPowerFlow.p ?p ;
                 cim:SvPowerFlow.q ?q .
  # Assign status flag for load. The status flag is set to the first existing of
  # 1) in_service flag
  # 2) connected flag the terminal
  BIND (COALESCE(?in_service, ?connected) AS ?status)
}

Query Rationalization

  • remove cim:SubGeographicalRegion.Region since all are "NO"
  • simplified the filter cim:Equipment.networkAnalysisEnabled false
  • there is prop cim:NonConformLoadGroup.NonConformLoadSchedules but we don't have such data, and it doesn't have markeCode

Fixed SPARQL

PREFIX cim: <https://cim.ucaiug.io/ns#>
SELECT ?area ?mrid ?name ?substation_mrid ?status ?p ?q ?connectivity_node ?schedule WHERE {
  # Extract mRID, name and substation for each load
  VALUES ?load_type { cim:ConformLoad cim:NonConformLoad cim:EnergyConsumer }
  ?load a ?load_type ;
        cim:IdentifiedObject.mRID ?mrid ;
        cim:IdentifiedObject.name ?name ;
        cim:Equipment.EquipmentContainer/cim:VoltageLevel.Substation ?substation .
  filter not exists {?load cim:Equipment.networkAnalysisEnabled false}
  # Extract area and mRID (referred to as 'station') for the substation of each load
  ?substation cim:Substation.Region/cim:IdentifiedObject.name ?area ;
              cim:IdentifiedObject.mRID ?substation_mrid .
  # FILTER REGEX(?area,$REGION)
  OPTIONAL {?load cim:NonConformLoad.LoadGroup/cim:NonConformLoadGroup.NonConformLoadSchedules ?schedule}
  # Extract mRID and connectivity node for each terminal associated with a load
  ?terminal cim:Terminal.ConductingEquipment ?load ;
            cim:Terminal.ConnectivityNode ?con_node .
  ?con_node cim:IdentifiedObject.mRID ?connectivity_node .
  # Extract connected flag and optionally the mRID of the topological node associated with the terminal
  ?terminal cim:ACDCTerminal.connected ?connected .
  # Optionally extract in_service flag and active and reactive energy consumption for each load
  OPTIONAL {?load ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service}
  ?sv_power_flow cim:SvPowerFlow.Terminal ?terminal ;
                 cim:SvPowerFlow.p ?p ;
                 cim:SvPowerFlow.q ?q .
  # Assign status flag for load. The status flag is set to the first existing of
  # 1) in_service flag
  # 2) connected flag the terminal
  BIND (COALESCE(?in_service, ?connected) AS ?status)
}

market_dates

Question

What are the IDs, names, and activation dates of all market definition sets?

TLDR;

This query retrieves each market definition set’s unique identifier (mRID), its name, and the date it was activated.

Explanation

The query is written in SPARQL and uses two namespaces:

  • cim: for the standard CIM (Common Information Model) schema.
  • SN: for Statnett’s CIM extension schema.

The WHERE clause specifies:

  1. ?_set a SN:MarketDefinitionSet — Finds all resources that are of type SN:MarketDefinitionSet. This means the subject represents a market definition set entity.
  2. cim:IdentifiedObject.mRID ?mrid — Retrieves the unique identifier (mRID) for each market definition set.
  3. cim:IdentifiedObject.name ?name — Retrieves the human-readable name of the set.
  4. SN:MarketDefinitionSet.activationDate ?activation_date — Retrieves the date the set was activated.

The SELECT clause returns the three variables:

  • ?mrid — unique identifier.
  • ?name — descriptive name.
  • ?activation_date — the activation date.

Effectively, this query lists all market definition sets along with their ID, name, and when they took effect.

Original SPARQL

# Name: Market dates
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?mrid ?name ?activation_date WHERE {
  ?_set a SN:MarketDefinitionSet ;
        cim:IdentifiedObject.mRID ?mrid ;
        cim:IdentifiedObject.name ?name ;
        SN:MarketDefinitionSet.activationDate ?activation_date
}

phase_tap_changer

Question

Which phase tap changers (linear) are in the model, and for each, what are its phase‑shift increment, step limits (low/high/neutral), regulating control mode and target value, the monitored winding mRID, and whether the overall control is enabled?

TLDR;

Federates to the equipment repository, finds each PhaseTapChangerLinear, pulls its step settings and associated RegulatingControl, resolves the monitored winding, normalizes the control mode label, and reports a single enabled flag that is true only if both the tap changer and its regulating control are enabled.

Explanation

  • Setup & inputs The query uses PREFIX cim: for the IEC CIM namespace. It expects a link to an equipment repository endpoint via:

?\_eq\_subject [http://entsoe.eu/CIM/EquipmentCore/3/1](http://entsoe.eu/CIM/EquipmentCore/3/1) ?eq\_repo .

The discovered IRI in ?eq_repo is then used as a SPARQL federation endpoint inside a SERVICE ?eq_repo { ... } clause.

  • Top‑level control flags (outside SERVICE) Before federating, two booleans are read:
  • ?tap_changer cim:TapChanger.controlEnabled ?enabled_1 .
  • ?tap_changer_control cim:RegulatingControl.enabled ?enabled_2 ; cim:RegulatingControl.targetValue ?target_value .

At the end, BIND (?enabled_1 && ?enabled_2 AS ?enabled) combines them so ?enabled is true only when both the physical tap changer and the regulating control are enabled.

  • Federated retrieval of device details (inside SERVICE) Within SERVICE ?eq_repo { ... }, the query gathers the phase tap changer’s characteristics and relationships:

  • ?tap_changer cim:PhaseTapChangerLinear.stepPhaseShiftIncrement ?phase_shift_increment ;

    • Using the PhaseTapChangerLinear.stepPhaseShiftIncrement property effectively scopes results to linear phase tap changers.
  • cim:TapChanger.TapChangerControl ?tap_changer_control ;

    • Associates the device with its RegulatingControl.
  • Step limits:

    • cim:TapChanger.neutralStep ?neutral_step ;
    • cim:TapChanger.highStep ?high_step ;
    • cim:TapChanger.lowStep ?low_step ;
  • Related winding via inverse path:

    • ^cim:TransformerEnd.PhaseTapChanger ?winding .
    • This walks from a TransformerEnd that has this phase tap changer to the tap changer itself (the caret ^ means inverse), yielding the transformer end (?winding) the changer acts on.
  • The winding’s identifier:

    • ?winding cim:IdentifiedObject.mRID ?mrid .
  • Regulating control metadata and the monitored winding:

    • ?tap_changer_control cim:RegulatingControl.mode ?control_mode ;
    • cim:RegulatingControl.Terminal/^cim:TransformerEnd.Terminal/cim:IdentifiedObject.mRID ?monitored_winding .
      • This path goes from the control’s Terminal to the TransformerEnd attached to that terminal (again using ^ for the inverse of TransformerEnd.Terminal), then reads that end’s mRID. This is the winding being monitored by the control loop (which may differ from the winding the tap changer is physically on).
  • Post‑processing / normalization

  • BIND (replace(STR(?control_mode), STR(cim:), "") AS ?mode) strips the CIM namespace from the RegulatingControl.mode IRI, leaving just the local token (e.g., voltage, activePower, etc.) in ?mode.

  • Projection The SELECT returns, per tap changer:

  • ?mrid — mRID of the transformer end the changer is on

  • ?phase_shift_increment — linear phase shift per step

  • ?enabled — combined enablement of device and control

  • ?neutral_step, ?high_step, ?low_step — step configuration

  • ?mode — normalized control mode (local name)

  • ?target_value — setpoint from RegulatingControl.targetValue

  • ?monitored_winding — mRID of the transformer end the control monitors

In short, the query cross‑links equipment and control domains to present a compact, operational snapshot of each PhaseTapChangerLinear: how it steps, what it targets, which winding it monitors, and whether it’s actually active.

Original SPARQL

# Name: Phase tap changer
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?mrid ?phase_shift_increment ?enabled ?neutral_step ?high_step ?low_step ?mode ?target_value ?monitored_winding WHERE {
  ?tap_changer cim:TapChanger.controlEnabled ?enabled_1 .
  ?tap_changer_control cim:RegulatingControl.enabled ?enabled_2 ;
                       cim:RegulatingControl.targetValue ?target_value .
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    ?tap_changer cim:PhaseTapChangerLinear.stepPhaseShiftIncrement ?phase_shift_increment ;
                 cim:TapChanger.TapChangerControl ?tap_changer_control ;
                 cim:TapChanger.neutralStep ?neutral_step ;
                 cim:TapChanger.highStep ?high_step ;
                 cim:TapChanger.lowStep ?low_step ;
                 ^cim:TransformerEnd.PhaseTapChanger ?winding .
    ?winding cim:IdentifiedObject.mRID ?mrid .
    ?tap_changer_control cim:RegulatingControl.mode ?control_mode ;
                         cim:RegulatingControl.Terminal/^cim:TransformerEnd.Terminal/cim:IdentifiedObject.mRID ?monitored_winding .
  }
  BIND (replace(STR(?control_mode), STR(cim:), "") AS ?mode)
  BIND (?enabled_1 && ?enabled_2 AS ?enabled)
}

power_flow

Question

What are the active (P) and reactive (Q) power flows at each terminal, along with the terminal’s mRID and whether its associated equipment is considered in service?

TLDR;

Pulls P and Q from SvPowerFlow per Terminal, fetches the terminal’s mRID from the equipment repository via a SERVICE call, and reports inService status (defaulting to true when missing).

Explanation

This query joins state variables with equipment metadata to present power flow by terminal, annotated with identity and service status.

1) Select state-variable power flows

?_s a cim:SvPowerFlow ;
    cim:SvPowerFlow.Terminal ?terminal ;
    cim:SvPowerFlow.p ?p ;
    cim:SvPowerFlow.q ?q .
  • Restricts to resources of type cim:SvPowerFlow.

  • From each SvPowerFlow, it extracts:

    • the associated ?terminal,
    • the active power ?p (typically MW),
    • the reactive power ?q (typically MVAr).

This frames one row per terminal state-variable entry.

2) Discover the equipment repository and federate to get the terminal mRID

?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
SERVICE ?eq_repo {
  ?terminal cim:IdentifiedObject.mRID ?mrid .
}
  • The triple with predicate <http://entsoe.eu/CIM/EquipmentCore/3/1> provides (or points to) the IRI of the equipment model endpoint in ?eq_repo.

  • SERVICE ?eq_repo { … } federates a subquery to that endpoint, retrieving the cim:IdentifiedObject.mRID of each ?terminal.

    • This decouples state (Sv* graphs) from equipment (EQ/core metadata), a common deployment pattern for CIM-based platforms.

3) Resolve the in-service status, defaulting to true

OPTIONAL {
  ?terminal
    cim:Terminal.ConductingEquipment
      /^cim:SvStatus.ConductingEquipment
      /cim:SvStatus.inService ?_in_service
}
BIND (COALESCE(?_in_service, True) AS ?in_service)
  • The OPTIONAL pattern walks a path to find a corresponding cim:SvStatus boolean:

    • From the ?terminal to its cim:Terminal.ConductingEquipment.
    • Then inverse of cim:SvStatus.ConductingEquipment (^…) to reach any cim:SvStatus linked to that equipment.
    • Finally reads cim:SvStatus.inService into ?_in_service.
  • If such a status isn’t present, COALESCE sets ?in_service to True. This ensures every row reports a service flag without needing a separate filter or post-processing.

4) Output

SELECT ?mrid ?p ?q ?in_service
  • For each terminal with a state-variable power flow, returns:

    • ?mrid — the terminal’s identifier from the equipment repository,
    • ?p, ?q — active/reactive power from SvPowerFlow,
    • ?in_service — boolean service state (true if missing).

Notes & implications

  • If multiple SvStatus instances exist for the same equipment, the OPTIONAL path could yield multiple rows per terminal unless upstream data is constrained. The query intentionally doesn’t de-duplicate.
  • Sign conventions for p/q follow the CIM SvPowerFlow definition (typically positive from the terminal into the equipment/network, depending on the implementation).
  • The use of SERVICE means execution depends on the availability and authorization of the remote equipment endpoint (?eq_repo).

Original SPARQL

# Name: Power flow
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?mrid ?p ?q ?in_service WHERE {
  ?_s a cim:SvPowerFlow ;
      cim:SvPowerFlow.Terminal ?terminal ;
      cim:SvPowerFlow.p ?p ;
      cim:SvPowerFlow.q ?q .
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    ?terminal cim:IdentifiedObject.mRID ?mrid .
  }
  OPTIONAL {
    ?terminal cim:Terminal.ConductingEquipment/^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?_in_service
  }
  BIND (COALESCE(?_in_service, True) AS ?in_service)
}

ras_equipment

Question

Which RAS protective actions reference which pieces of equipment, and what are the mRIDs and names of those protective actions and the mRIDs of the referenced equipment?

TLDR;

For every RAS protective action (?rpact), follow any of three ALG extension properties to a referenced piece of equipment and return: the protective action’s mRID and name, plus the referenced equipment’s mRID.

Explanation

The query inspects relationships defined in the Alstom (ALG) CIM extension to find which equipment a RAS protective action is tied to. It does this with a SPARQL property path that unions three predicates and then drills down to the equipment’s identifier:

  • alg:ProtectiveActionEquipment.Equipment
  • alg:ProtectiveActionAdjustmentAC.ConductingEquipment
  • alg:ProtectiveActionAdjustmentDCController.DCController

The core of the pattern is:


?rpact (P1 | P2 | P3) / cim\:IdentifiedObject.mRID ?equipment\_mrid .

Where P1|P2|P3 is the union of the three ALG predicates above. This means:

  1. Start at a node ?rpact (a protective action instance, implicitly—because only such nodes will have these ALG properties in the data).
  2. Follow any one of the three ALG properties to reach a target equipment resource (which may be a generic Equipment, a ConductingEquipment, or a DCController).
  3. From that equipment resource, follow cim:IdentifiedObject.mRID to extract the equipment’s identifier into ?equipment_mrid.

In parallel, the query also binds two attributes directly on the protective action node:


?rpact cim\:IdentifiedObject.mRID ?mrid ;
cim\:IdentifiedObject.name ?name .

So each solution row contains:

  • ?mrid: the protective action’s mRID
  • ?name: the protective action’s human-readable name
  • ?equipment_mrid: the mRID of the equipment referenced by that protective action via any of the three allowed relationships

Behavioral notes:

  • Because of the union, a single protective action that references equipment via multiple of these properties will produce multiple rows (one per referenced equipment).
  • The query does not constrain ?rpact by explicit RDF type; the use of the ALG properties effectively scopes matches to protective action-like nodes, assuming predicate domains are respected in the data.
  • No DISTINCT is used; if the data contains duplicate links or repeated mRID values, duplicates may appear in the results.

Original SPARQL

# Name: RAS Equipment
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX alg: <http://www.alstom.com/grid/CIM-schema-cim15-extension#>
SELECT ?mrid ?equipment_mrid ?name WHERE {
  ?rpact (alg:ProtectiveActionEquipment.Equipment | alg:ProtectiveActionAdjustmentAC.ConductingEquipment | alg:ProtectiveActionAdjustmentDCController.DCController)/cim:IdentifiedObject.mRID ?equipment_mrid ;
         cim:IdentifiedObject.mRID ?mrid ;
         cim:IdentifiedObject.name ?name .
}

regions

Question

Which SubGeographicalRegions exist, and what are their identifiers and names, along with (optionally) their short names and aliases, plus the name and alias of their parent GeographicalRegion?

TLDR;

Lists every cim:SubGeographicalRegion with its mRID, own name, optional short/alias names, and the name (and optional alias) of the cim:GeographicalRegion it belongs to.

Explanation

The query targets CIM geography classes using the cim and SN prefixes.

  • Core pattern (required):

    • ?subGeoRegion a cim:SubGeographicalRegion ; ... Binds ?subGeoRegion to each instance of cim:SubGeographicalRegion.
    • cim:IdentifiedObject.mRID ?mrid Retrieves the unique identifier (mRID) for each sub‑region.
    • cim:IdentifiedObject.name ?name Retrieves the human‑readable name of the sub‑region.
    • cim:SubGeographicalRegion.Region ?subgeoreg Follows the object property linking a SubGeographicalRegion to its parent GeographicalRegion (bound to ?subgeoreg).
    • ?subgeoreg cim:IdentifiedObject.name ?region Gets the parent region’s name (required).
  • Optional patterns (may be absent without filtering out the row):

    • OPTIONAL { ?subGeoRegion SN:IdentifiedObject.shortName ?short_name } If present in the Statnett extension, binds a short name for the sub‑region to ?short_name.
    • OPTIONAL { ?subGeoRegion cim:IdentifiedObject.aliasName ?alias_name } If the sub‑region has an alias, binds it to ?alias_name.
    • OPTIONAL { ?subgeoreg cim:IdentifiedObject.aliasName ?region_name } If the parent region has an alias, binds it to ?region_name.
  • Projection: The SELECT clause outputs:

    • ?mrid — the sub‑region’s unique identifier,
    • ?region — the parent region’s name,
    • ?short_name — the sub‑region’s optional short name (from the SN extension),
    • ?name — the sub‑region’s official name,
    • ?alias_name — the sub‑region’s optional alias,
    • ?region_name — the parent region’s optional alias.

Because the optional blocks are non‑binding when data is missing, each row is guaranteed to at least include the sub‑region’s mRID, its name, and its parent region’s name. Where alias/short names exist, the corresponding columns are filled; otherwise they remain unbound (typically shown as empty/null in results).

Original SPARQL

# Name: Regions
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?mrid ?region ?short_name ?name ?alias_name ?region_name WHERE {
  ?subGeoRegion a cim:SubGeographicalRegion ;
                cim:IdentifiedObject.mRID ?mrid ;
                cim:IdentifiedObject.name ?name ;
                cim:SubGeographicalRegion.Region ?subgeoreg .
  OPTIONAL {
    ?subGeoRegion SN:IdentifiedObject.shortName ?short_name
  } .
  OPTIONAL {
    ?subGeoRegion cim:IdentifiedObject.aliasName ?alias_name
  } .
  ?subgeoreg cim:IdentifiedObject.name ?region .
  OPTIONAL {
    ?subgeoreg cim:IdentifiedObject.aliasName ?region_name
  }
}

series_compensators

Question

Which SeriesCompensator devices exist in the selected region, what are their key electrical parameters and connectivity, what (converted) current limits do they have, and what are their estimated active power losses at each terminal based on state-estimated terminal powers?

TLDR;

For each two‑terminal cim:SeriesCompensator in the chosen sub‑geographical region, the query gathers IDs, names, base voltage, R/X, terminal connectivity nodes, a boolean status, and an optional MVA rating converted from a named cim:CurrentLimit. It also derives per‑end “loss” values from terminal SvPowerFlow.p. Results are filtered to devices with two distinct connectivity nodes and (intended) network‑analysis enabled.

Explanation

This query is written against a CIM dataset and uses a federated SERVICE to the equipment repository for core equipment/terminal topology, while pulling state variables (Sv*) and limits from the outer graph.

1) Scope & federation

?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
SERVICE ?eq_repo { ... }

The SERVICE block points to the equipment model endpoint ?eq_repo. Inside it, we fetch series‑compensator master data and topology; outside it, we join state variables (status, power flows) and limits.

2) Selecting series compensators and core attributes

?compensator a cim:SeriesCompensator ;
             cim:SeriesCompensator.r ?r ;
             cim:SeriesCompensator.x ?x ;
             cim:IdentifiedObject.mRID ?mrid ;
             cim:IdentifiedObject.name ?name ;
             cim:ConductingEquipment.BaseVoltage/cim:BaseVoltage.nominalVoltage ?un ;
             ^cim:Terminal.ConductingEquipment ?terminal .

This binds each cim:SeriesCompensator and its electrical parameters (r, x), identifiers (mRID, name), base voltage (un), and its terminals.

3) Terminals, connectivity, area filter

?terminal cim:Terminal.ConnectivityNode ?con_node ;
          cim:ACDCTerminal.sequenceNumber ?nr .

?con_node cim:ConnectivityNode.ConnectivityNodeContainer/
           cim:VoltageLevel.Substation ?substation ;
          cim:IdentifiedObject.mRID ?connectivity_node .

?substation cim:Substation.Region/
             cim:SubGeographicalRegion.Region/
             cim:IdentifiedObject.name ?area .

FILTER (REGEX(?area,'${region}'))

Each terminal is tied to a ConnectivityNode and has a sequence number (nr) expected to be 1 or 2. The query walks up to the parent substation/region and keeps only compensators whose region name matches the ${region} regex parameter.

4) Optional “network analysis enabled”

OPTIONAL { ?compensator SN:Equipment.networkAnalysisEnable ?network_analysis }

This captures a vendor extension flag indicating if the device is enabled for network analysis. Note: The HAVING clause later references ?analysis_enabled, which doesn’t exist. It should probably use MAX(?network_analysis) instead. As written, the COALESCE(MAX(?analysis_enabled), True) part will always fall back to True, unintentionally ignoring the flag.

5) Optional current limit → converted apparent power rating

OPTIONAL {
  ?_lim cim:OperationalLimit.OperationalLimitSet/cim:OperationalLimitSet.Equipment ?compensator ;
        a cim:CurrentLimit ;
        cim:IdentifiedObject.name '${rate}' ;
        cim:CurrentLimit.value ?_rate .
  BIND (1.7320508075688772 * xsd:double(xsd:string(?un))
        * xsd:double(xsd:string(?_rate)) / 1000.0 AS ?rate)
}

If a cim:CurrentLimit exists on the device with the given name ${rate}, it is converted to an MVA‑style rating using √3·U[kV]·I[A]/1000.

6) In‑service status and connectivity

Outside the SERVICE, the query joins state variables and connectivity:

OPTIONAL { ?compensator ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service } .
?terminal cim:ACDCTerminal.connected ?connected .

Later, status is computed as COALESCE(MAX(?in_service), MAX(?connected_1) && MAX(?connected_2)), i.e., use SvStatus.inService if available; otherwise require both terminals to be connected.

7) Terminal power flows and per‑end loss proxies

OPTIONAL { ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p . }
BIND (IF(?nr = 1, STR(?p), '') AS ?p_1)
BIND (IF(?nr = 2, STR(?p), '') AS ?p_2)

Per terminal, the active power SvPowerFlow.p (in MW) is captured and routed into p_1 or p_2. In the SELECT, the query computes:

(IF(xsd:double(MAX(?p_1)) < xsd:double(MAX(?p_2)),
    xsd:double(MAX(?p_1)) + xsd:double(MAX(?p_2)), 0.0) AS ?ploss_1)

(IF(xsd:double(MAX(?p_1)) > xsd:double(MAX(?p_2)),
    xsd:double(MAX(?p_1)) + xsd:double(MAX(?p_2)), 0.0) AS ?ploss_2)

Idea: if power magnitudes imply flow from 2→1 (or 1→2), the sum p1 + p2 is treated as a positive “loss” on the receiving end, otherwise 0. This assumes p is signed by terminal orientation and that p1 + p2 ≈ losses. Caveats:

  • p_1/p_2 are bound as strings (STR(?p)) and aggregated with MAX before casting, which is lexicographic and can be brittle if multiple p values exist or empty strings appear. A safer pattern is to cast to xsd:double before aggregating and use SUM/SAMPLE.
  • If either terminal lacks SvPowerFlow.p, the casts can error; the current OPTIONAL + empty string plus MAX may mask this but is fragile.

8) Per‑terminal routing of other fields

BIND (IF(?nr = 1, ?connected, False) AS ?connected_1)
BIND (IF(?nr = 2, ?connected, False) AS ?connected_2)
BIND (IF(?nr = 1, ?connectivity_node, '') AS ?connectivity_node_1)
BIND (IF(?nr = 2, ?connectivity_node, '') AS ?connectivity_node_2)

These create per‑end variables (connectivity flags and node mRIDs) so they can be aggregated later.

9) Aggregation & output columns

The SELECT uses MAX(...) to collapse per‑device fields:

  • ?mrid, ?name, ?r, ?x, ?un, ?rate — single‑valued by design, MAX just picks one.
  • ?connectivity_node_1, ?connectivity_node_2 — per‑end node IDs.
  • ?status — as described above.
  • ?ploss_1, ?ploss_2 — derived “loss” proxies per end.

Grouping is by ?compensator:

GROUP BY ?compensator

10) Quality filters in HAVING

HAVING (
  (COUNT(*) > 1)                                  # at least two terminal rows
  && (MAX(?connectivity_node_1) != MAX(?connectivity_node_2))  # two distinct nodes
  && COALESCE(MAX(?analysis_enabled), True)       # intended: network analysis enabled
)
  • Ensures we only keep two‑terminal devices with distinct nodes.
  • Bug alert: ?analysis_enabled isn’t defined; should be ?network_analysis. As written, this condition always evaluates to True, so the flag is effectively ignored.

11) Practical notes / potential fixes

  • Fix the flag variable: change ?analysis_enabled?network_analysis in HAVING.

  • Safer power handling: bind numeric values directly, e.g.

    OPTIONAL { ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p . }
    BIND (IF(?nr=1, xsd:double(?p), 0.0) AS ?p1_num)
    BIND (IF(?nr=2, xsd:double(?p), 0.0) AS ?p2_num)
    

    then use SUM(?p1_num)/SUM(?p2_num) or MAX on numeric variables, avoiding string casts and empty strings.

  • Ratings by name: the limit is selected by cim:IdentifiedObject.name '${rate}'. Ensure the ${rate} parameter matches the naming convention in your data (e.g., “Continuous”, “Summer”, etc.).

Original SPARQL

# Name: Series compensators
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT (MAX(?mrid) AS ?mrid) (MAX(?name) AS ?name) (IF(xsd:double(MAX(?p_1)) < xsd:double(MAX(?p_2)),xsd:double(MAX(?p_1)) + xsd:double(MAX(?p_2)),xsd:double(0.0)) AS ?ploss_1) (IF(xsd:double(MAX(?p_1)) > xsd:double(MAX(?p_2)),xsd:double(MAX(?p_1)) + xsd:double(MAX(?p_2)),xsd:double(0.0)) AS ?ploss_2) (MAX(?r) AS ?r) (MAX(?rate) AS ?rate) (COALESCE(MAX(?in_service), MAX(?connected_1) && MAX(?connected_2)) AS ?status) (MAX(?un) AS ?un) (MAX(?x) AS ?x) (MAX(?connectivity_node_1) AS ?connectivity_node_1) (MAX(?connectivity_node_2) AS ?connectivity_node_2) WHERE {
  # Assume π equivalent with at least r,x and b shunt.
  # Extract properties for series compensator
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    ?compensator cim:SeriesCompensator.r ?r ;
                 cim:SeriesCompensator.x ?x ;
                 cim:IdentifiedObject.mRID ?mrid ;
                 cim:IdentifiedObject.name ?name ;
                 cim:ConductingEquipment.BaseVoltage/cim:BaseVoltage.nominalVoltage ?un ;
                 ^cim:Terminal.ConductingEquipment ?terminal .
    # Extract terminal properties
    ?terminal cim:Terminal.ConnectivityNode ?con_node ;
              cim:ACDCTerminal.sequenceNumber ?nr .
    ?con_node cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.Substation ?substation ;
              cim:IdentifiedObject.mRID ?connectivity_node .
    ?substation cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area .
    OPTIONAL {
      ?compensator SN:Equipment.networkAnalysisEnable ?network_analysis
    }
    FILTER (REGEX(?area,'${region}'))
    # Optionally extract current limits
    OPTIONAL {
      ?_lim cim:OperationalLimit.OperationalLimitSet/cim:OperationalLimitSet.Equipment ?compensator ;
            a cim:CurrentLimit ;
            cim:IdentifiedObject.name '${rate}' ;
            cim:CurrentLimit.value ?_rate .
      # Convert rate from current to power using √3 * U [kV] * I [A] / 1000.0 [MVA]
      BIND (1.7320508075688772 * xsd:double(xsd:string(?un)) * xsd:double(xsd:string(?_rate)) / 1000.0 AS ?rate)
    } .
  }
  # Optionally extract in_service associated with compensator
  OPTIONAL {
    ?compensator ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service
  } .
  ?terminal cim:ACDCTerminal.connected ?connected .
  OPTIONAL {
    ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?p .
  }
  # Create variables for node 1 and node 2
  BIND (IF(?nr = 1,STR(?p),'') AS ?p_1)
  BIND (IF(?nr = 1,?connected,False) AS ?connected_1)
  BIND (IF(?nr = 2,STR(?p),'') AS ?p_2)
  BIND (IF(?nr = 2,?connected,False) AS ?connected_2)
  BIND (IF(?nr = 1,?connectivity_node,'') AS ?connectivity_node_1)
  BIND (IF(?nr = 2,?connectivity_node,'') AS ?connectivity_node_2)
}
GROUP BY ?compensator
HAVING ((COUNT(*) > 1) && (MAX(?connectivity_node_1) != MAX(?connectivity_node_2)) && COALESCE(MAX(?analysis_enabled), True))

station_group_code_and_names

Question

Which ScheduleResource entries (in the Statnett CIM extension) exist in the dataset, and for each one, what are its marketCode (station group), name, and—if present—alias name?

TLDR;

Return all resources that have SN:ScheduleResource.marketCode and cim:IdentifiedObject.name; include cim:IdentifiedObject.aliasName when available.

Explanation

The query uses two prefixes:

  • cim: → the IEC CIM namespace, to access common attributes like IdentifiedObject.name and IdentifiedObject.aliasName.
  • SN: → the Statnett CIM extension namespace, to access ScheduleResource.marketCode.

Projection

It selects three variables:

  • ?station_group — bound from SN:ScheduleResource.marketCode
  • ?name — bound from cim:IdentifiedObject.name
  • ?alias_name — optionally bound from cim:IdentifiedObject.aliasName

Pattern logic


?s SN\:ScheduleResource.marketCode ?station\_group ;
cim\:IdentifiedObject.name ?name .
OPTIONAL { ?s cim\:IdentifiedObject.aliasName ?alias\_name }

  • The subject ?s is any resource that has a SN:ScheduleResource.marketCode property and a cim:IdentifiedObject.name. Because these two triples are not in an OPTIONAL, they act as a filter: only subjects with both properties are returned.
  • The OPTIONAL block tries to bind ?alias_name from cim:IdentifiedObject.aliasName. If that property is missing for ?s, the row is still returned with ?alias_name unbound.

Notes & implications

  • Namespace-specific property: The query keys on SN:ScheduleResource.marketCode. This effectively scopes results to the Statnett extension model; if the same concept exists under a different namespace in your data, those entries will not match. (This is why the inline comment asks whether ScheduleResource might exist in another namespace.)
  • No explicit rdf:type check: There’s no ?s a SN:ScheduleResource triple. Instead, the presence of the marketCode property is used to identify schedule resources. This is often sufficient and slightly more flexible, but if you want stricter typing, add:

?s a SN\:ScheduleResource .

  • Cardinality effects:
  • If a subject has multiple aliasName values, you’ll get one row per alias (standard SPARQL multivalued join behavior).
  • Similarly, multiple marketCode or name values would create a row for each combination.
  • Semicolon syntax: The semicolons after ?s simply chain predicates with the same subject, making the query more concise:

?s P1 O1 ;
P2 O2 .

is shorthand for


?s P1 O1 .
?s P2 O2 .

Overall, the query lists schedule-related resources defined via the Statnett extension, reporting their market grouping (station_group), canonical name, and an optional alias if present.

Original SPARQL

# Name: Station group code and names
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?station_group ?name ?alias_name WHERE {
  # SN is not made optional here because then there is no filtering left.
  # TODO: Is ScheduleResource available in a another namespace?
  ?s SN:ScheduleResource.marketCode ?station_group ;
     cim:IdentifiedObject.name ?name ;
  OPTIONAL {
    ?s cim:IdentifiedObject.aliasName ?alias_name
  }
}

station_group_for_power_unit

Question

For each scheduling resource, which power system element (a SynchronousMachine tied to a GeneratingUnit or a NonConformLoad tied to a LoadGroup) is associated to it, and what are the identifiers, names, and market codes involved?

TLDR;

Finds the physical CIM object (either a SynchronousMachine’s GeneratingUnit or a NonConformLoad’s LoadGroup parent) connected to a given scheduling resource and returns the physical object’s mRID, the unit’s mRID and name, and the resource’s market code.

Explanation

Prefixes

  • SN: points to the Statnett CIM extension namespace, used here for SN:ScheduleResource.marketCode.
  • cim: points to the IEC CIM v16 namespace for standard CIM classes and properties.

Selected outputs

  • ?power_system_model_mrid — the mRID of the physical CIM object (either a cim:SynchronousMachine or a cim:NonConformLoad).
  • ?market_unit_mrid — the mRID of the unit associated with the resource (a cim:GeneratingUnit or a cim:LoadGroup).
  • ?resource_name — the name of that unit.
  • ?market_code — the market code of the scheduling resource.

Graph pattern walkthrough

  1. ?resource SN:ScheduleResource.marketCode ?market_code . Finds each scheduling resource and its market code in the Statnett extension.

  2. ?unit ?p ?resource ;
          cim:IdentifiedObject.name ?resource_name ;
          cim:IdentifiedObject.mRID ?market_unit_mrid ;
    

Locates a unit (?unit) that links to the ?resource via some predicate ?p (left intentionally generic). From that unit, the query also retrieves the unit’s name and mRID. In practice, ?unit will be a cim:GeneratingUnit or a cim:LoadGroup.

  1. VALUES ?physical_model_predicate {
      cim:SynchronousMachine.GeneratingUnit
      cim:NonConformLoad.LoadGroup
    }
    

    Restricts the relationship used to connect a physical object to the unit. Only two CIM properties are allowed:

    • cim:SynchronousMachine.GeneratingUnit (domain: SynchronousMachine, range: GeneratingUnit)
    • cim:NonConformLoad.LoadGroup (domain: NonConformLoad, range: LoadGroup)
  2. ?physical_object ?physical_model_predicate ?unit ;
                     cim:IdentifiedObject.mRID ?power_system_model_mrid .
    

    Uses the chosen predicate to find the physical object (?physical_object) that owns/points to the ?unit.

    • If ?physical_model_predicate is cim:SynchronousMachine.GeneratingUnit, then ?physical_object is a cim:SynchronousMachine that references the ?unit (a GeneratingUnit).
    • If it is cim:NonConformLoad.LoadGroup, then ?physical_object is a cim:NonConformLoad that references the ?unit (a LoadGroup). The query then extracts that physical object’s mRID as ?power_system_model_mrid.

What this effectively returns For each scheduling resource with a market code, the query:

  • Finds a related unit (either GeneratingUnit or LoadGroup) that points to the resource.
  • Traverses back from that unit to the associated physical network element (SynchronousMachine or NonConformLoad) via the appropriate CIM property.
  • Reports the physical object’s mRID (as the “power system model mRID”), plus the unit’s mRID and name, and the resource’s market code.

Notes & assumptions

  • The generic ?unit ?p ?resource allows any property to link a unit to the scheduling resource. In your data model, this should align with how resources are attached to their units; otherwise, consider constraining ?p to a specific predicate.
  • The query uses only mandatory patterns. If a unit lacks a link to a resource or a physical object via the specified predicates, that record will not appear.
  • Multiple physical objects tied to the same unit/resource will yield multiple rows (one per physical object).

Original SPARQL

# Name: station group for power resource
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?power_system_model_mrid ?market_unit_mrid ?resource_name ?market_code WHERE {
  ?resource SN:ScheduleResource.marketCode ?market_code .
  ?unit ?p ?resource ;
        cim:IdentifiedObject.name ?resource_name ;
        cim:IdentifiedObject.mRID ?market_unit_mrid ;
  VALUES ?physical_model_predicate { cim:SynchronousMachine.GeneratingUnit cim:NonConformLoad.LoadGroup }
  ?physical_object ?physical_model_predicate ?unit ;
                   cim:IdentifiedObject.mRID ?power_system_model_mrid .
}

substation_voltage_level

Question

For each substation, what is the highest nominal voltage among its voltage levels, and which voltage level(s) achieve that value (returning their mRIDs alongside the substation mRID)?

TLDR;

Compute the maximum BaseVoltage.nominalVoltage per substation, then return the substation mRID, the max voltage value, and the mRID(s) of the VoltageLevel(s) in that substation that have that maximum.

Explanation

The query works in two layers:

  1. Inner subquery (aggregation per substation)
    {
      SELECT (MAX(?volt) AS ?v) ?_substation WHERE {
        ?volt_level a cim:VoltageLevel ;
                    cim:VoltageLevel.BaseVoltage/cim:BaseVoltage.nominalVoltage ?volt ;
                    cim:VoltageLevel.Substation ?_substation .
      }
      GROUP BY ?_substation
    }
    
  • Scans all cim:VoltageLevel instances.
  • Via a property path, it retrieves each level’s nominal voltage: cim:VoltageLevel.BaseVoltage / cim:BaseVoltage.nominalVoltage.
  • Groups by the containing substation (?_substation) and computes MAX(?volt) for each group, binding the result to ?v.
  • The subquery thus returns one row per substation with the maximum nominal voltage in that substation.
  1. Outer query (join back to concrete voltage levels and IDs)

    ?volt_level a cim:VoltageLevel ;
                cim:IdentifiedObject.mRID ?container ;
                cim:VoltageLevel.BaseVoltage/cim:BaseVoltage.nominalVoltage ?v ;
                cim:VoltageLevel.Substation ?_substation .
    ?_substation cim:IdentifiedObject.mRID ?substation .
    
    • Matches VoltageLevel resources again, binding:

      • ?container to the mRID of the voltage level (i.e., the voltage level’s own identifier).
      • ?v to the level’s nominal voltage (same property path as above).
      • ?_substation to the level’s parent substation resource.
    • Joins with the subquery on both ?_substation and ?v. Because ?v from the subquery is the maximum per substation, this join filters the outer solutions to only those voltage levels whose nominal voltage equals the per‑substation maximum.

    • Finally, it resolves the substation’s external identifier by binding ?substation to the substation’s cim:IdentifiedObject.mRID.

Result shape SELECT ?container ?substation ?v yields:

  • ?container: mRID of the voltage level(s) that have the maximum nominal voltage within their substation.
  • ?substation: mRID of the corresponding substation.
  • ?v: the maximum nominal voltage value for that substation.

Notes & behavior

  • If multiple voltage levels in the same substation share the same maximum voltage, all of them are returned (one row per level).
  • The use of the property path cim:VoltageLevel.BaseVoltage/cim:BaseVoltage.nominalVoltage traverses from a VoltageLevel to its associated BaseVoltage resource and then to the numeric nominalVoltage literal.
  • Variable scoping ensures that ?v from the inner subquery is the aggregated maximum, and the outer pattern’s ?v (the actual level’s voltage) must match it to survive the join.

Original SPARQL

# Name: Substation voltage level
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?container ?substation ?v WHERE {
  ?volt_level a cim:VoltageLevel ;
              cim:IdentifiedObject.mRID ?container ;
              cim:VoltageLevel.BaseVoltage/cim:BaseVoltage.nominalVoltage ?v ;
              cim:VoltageLevel.Substation ?_substation .
  {
    SELECT (MAX(?volt) AS ?v) ?_substation WHERE {
      ?volt_level a cim:VoltageLevel ;
                  cim:VoltageLevel.BaseVoltage/cim:BaseVoltage.nominalVoltage ?volt ;
                  cim:VoltageLevel.Substation ?_substation .
    }
    GROUP BY ?_substation
  } .
  ?_substation cim:IdentifiedObject.mRID ?substation
}

sv_branch

Question

Which ACLineSegments (branches) have their equipment parameters, terminal connectivity, and state‑variable results (status, P/Q flows, voltages, angles) so we can verify a state estimator where both current and voltage are state variables?

TLDR;

Pulls one row per cim:ACLineSegment with r/x/bch/gch, IDs and base‑voltage, terminal‑1/2 connectivity, in‑service status, SvPowerFlow (p/q) per terminal, and SvVoltage (v/angle) per end’s topological node; status defaults to inService if present, otherwise to connected_1 && connected_2. Intended to include only equipment with SN:Equipment.networkAnalysisEnable true.

Explanation

The query stitches together data from the EQ (equipment), TP (topology), and SV (state variable) models around each cim:ACLineSegment (“branch”) to produce a verification‑ready view for state estimation results.

1) Scope to the Equipment repository and fetch static line data

?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
SERVICE ?eq_repo {
  ?acline cim:ACLineSegment.r ?r ;
          cim:ACLineSegment.x ?x ;
          cim:ACLineSegment.bch ?bch ;
          cim:ACLineSegment.gch ?gch ;
          cim:IdentifiedObject.mRID ?mrid ;
          cim:IdentifiedObject.name ?name ;
          cim:ConductingEquipment.BaseVoltage/cim:BaseVoltage.nominalVoltage ?un .
  ...
}
  • The SERVICE ?eq_repo block isolates the EQ graph. For each ?acline, it returns series/shunt parameters (r, x, bch, gch), identifiers (mRID, name), and the nominal base voltage ?un via the attached BaseVoltage.

  • Inside the same SERVICE, the two physical terminals are identified and ordered using sequence numbers:

    ?terminal_1 a cim:Terminal ; ... ; (cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber) 1 .
    ?terminal_2 a cim:Terminal ; ... ; (cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber) 2 .
    

    This gives a consistent “end 1” and “end 2” for later joining.

2) Bring in SV status for the conducting equipment

?acline ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service .
  • Reads the state‑variable flag SvStatus.inService for the line (if present).

3) Walk from ConnectivityNodes to TopologicalNodes, then attach SV node voltages

?con_node_1 cim:ConnectivityNode.TopologicalNode ?node_1 .
OPTIONAL {
  ?_sv_n_1 cim:SvVoltage.TopologicalNode ?node_1 ;
           cim:SvVoltage.v ?v_1 ;
           cim:SvVoltage.angle ?angle_1 .
}
?con_node_2 cim:ConnectivityNode.TopologicalNode ?node_2 .
OPTIONAL {
  ?_sv_n_2 cim:SvVoltage.TopologicalNode ?node_2 ;
           cim:SvVoltage.v ?v_2 ;
           cim:SvVoltage.angle ?angle_2 .
}
  • Each terminal’s ConnectivityNode is mapped to its TopologicalNode (TP layer).
  • SvVoltage on each TopologicalNode provides per‑end voltage magnitude v and angle.

4) Terminal connectivity and SV power flows per terminal

?terminal_1 (cim:Terminal.connected | cim:ACDCTerminal.connected) ?connected_1 .
OPTIONAL {
  ?_sv_t_1 cim:SvPowerFlow.Terminal ?terminal_1 ;
           cim:SvPowerFlow.p ?p_1 ;
           cim:SvPowerFlow.q ?q_1 .
}
?terminal_2 (cim:Terminal.connected | cim:ACDCTerminal.connected) ?connected_2 .
OPTIONAL {
  ?_sv_t_2 cim:SvPowerFlow.Terminal ?terminal_2 ;
           cim:SvPowerFlow.p ?p_2 ;
           cim:SvPowerFlow.q ?q_2 .
}
  • Reads the equipment‑model connectivity flags for both terminals (handles either Terminal or ACDCTerminal property).
  • Optionally attaches SvPowerFlow active/reactive power for each terminal, giving p_1/q_1 and p_2/q_2.

5) Network‑analysis enable flag and intended filtering

OPTIONAL { ?acline SN:Equipment.networkAnalysisEnable ?_network_analysis }
FILTER (?network_analysis)
BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
  • Goal: return only equipment where networkAnalysisEnable is true, defaulting to True when the flag is missing.

  • Important note on evaluation order: As written, FILTER (?network_analysis) occurs before BIND defines ?network_analysis, so when the optional flag is absent the variable is unbound and the filter evaluates to error → the row is dropped. To implement the “default true” behavior, move the filter after the BIND, or fold the default into the filter:

    • Fix A (reorder):

      OPTIONAL { ?acline SN:Equipment.networkAnalysisEnable ?_network_analysis }
      BIND (COALESCE(?_network_analysis, true) AS ?network_analysis)
      FILTER (?network_analysis)
      
    • Fix B (inline):

      FILTER ( COALESCE(?_network_analysis, true) )
      

6) Derive the branch status

BIND (COALESCE(?in_service, ?connected_1 && ?connected_2) AS ?status) .
  • Prioritizes SvStatus.inService when available; otherwise uses terminal connectivity and declares the branch “up” only if both ends are connected.

7) Output columns

SELECT
  ?mrid ?name ?r ?x ?bch ?gch ?status ?length ?un
  ?p_1 ?q_1 ?p_2 ?q_2
  ?v_1 ?angle_1 ?v_2 ?angle_2
  • Static parameters and identifiers from EQ (mrid, name, r, x, bch, gch, un).

  • Derived status, per‑end P/Q (p_1/q_1, p_2/q_2), and per‑node volt/angle (v_1/angle_1, v_2/angle_2).

  • Note: ?length appears in SELECT but is never bound. If needed, add (likely inside the SERVICE block):

    ?acline cim:Conductor.length ?length .
    

    Otherwise ?length will be unbound (NULL) for all rows.

8) Optional patterns and data sparsity

  • All SvVoltage/SvPowerFlow parts are OPTIONAL, so the query still returns the line with whatever SV data is available.
  • Using explicit terminal sequence numbers ensures *_1 and *_2 fields are consistently associated with the same physical ends, which is critical when comparing state‑estimator results.

What you get per line segment

  • Equipment: mRID, name, r/x, bch/gch, base voltage un (and length if added).
  • Topology/SV per end: v/angle on each end’s TopologicalNode.
  • Power flow per terminal: p/q at end 1 and end 2.
  • Availability: status derived from SvStatus.inService or terminal connectivity.
  • (Intended) network‑analysis gating via SN:Equipment.networkAnalysisEnable.

This combined view is suitable for side‑by‑side checks of SE outputs (voltages/angles and terminal P/Q) against the modeled branch parameters and connectivity/state status.

Original SPARQL

# Name: SV branch
#
# Extract data from both EQ/TP/SV for ACLineSegment used to verify
# results from state estimator where both current and voltage has been
# defined as state variables.
#
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?mrid ?name ?r ?x ?bch ?gch ?status ?length ?un ?p_1 ?q_1 ?p_2 ?q_2 ?v_1 ?angle_1 ?v_2 ?angle_2 WHERE {
  # Extract key properties for ACLineSegment
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    ?acline cim:ACLineSegment.r ?r ;
            cim:ACLineSegment.x ?x ;
            cim:ACLineSegment.bch ?bch ;
            cim:ACLineSegment.gch ?gch ;
            cim:IdentifiedObject.mRID ?mrid ;
            cim:IdentifiedObject.name ?name ;
            cim:ConductingEquipment.BaseVoltage/cim:BaseVoltage.nominalVoltage ?un .
    ?terminal_1 a cim:Terminal ;
                cim:Terminal.ConductingEquipment ?acline ;
                cim:Terminal.ConnectivityNode ?con_node_1 ;
                cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber 1 .
    ?terminal_2 a cim:Terminal ;
                cim:Terminal.ConductingEquipment ?acline ;
                cim:Terminal.ConnectivityNode ?con_node_2 ;
                cim:Terminal.sequenceNumber | cim:ACDCTerminal.sequenceNumber 2 .
  }
  # Extract in_service flag for each acline
  ?acline ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service .
  # Extract connectivity node, topological node, voltage ang angle
  ?con_node_1 cim:ConnectivityNode.TopologicalNode ?node_1 .
  OPTIONAL {
    ?_sv_n_1 cim:SvVoltage.TopologicalNode ?node_1 ;
             cim:SvVoltage.v ?v_1 ;
             cim:SvVoltage.angle ?angle_1 .
  }
  # Extract conencted flag and active and reactive power for each terminal
  ?terminal_1 cim:Terminal.connected | cim:ACDCTerminal.connected ?connected_1 .
  OPTIONAL {
    ?_sv_t_1 cim:SvPowerFlow.Terminal ?terminal_1 ;
             cim:SvPowerFlow.p ?p_1 ;
             cim:SvPowerFlow.q ?q_1 .
  } .
  ?con_node_2 cim:ConnectivityNode.TopologicalNode ?node_2 .
  OPTIONAL {
    ?_sv_n_2 cim:SvVoltage.TopologicalNode ?node_2 ;
             cim:SvVoltage.v ?v_2 ;
             cim:SvVoltage.angle ?angle_2 .
  }
  ?terminal_2 cim:Terminal.connected | cim:ACDCTerminal.connected ?connected_2 .
  OPTIONAL {
    ?_sv_t_2 cim:SvPowerFlow.Terminal ?terminal_2 ;
             cim:SvPowerFlow.p ?p_2 ;
             cim:SvPowerFlow.q ?q_2 .
  }
  OPTIONAL {
    ?acline SN:Equipment.networkAnalysisEnable ?_network_analysis
  }
  FILTER (?network_analysis)
  BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
  # Assign status flag for each branch. It is set to the first that exist of
  # 1) in_service flag for each acline
  # 2) Connected flags of the two terminals. Status is set to False if at least one of the terminals are disconnected
  BIND (COALESCE(?in_service, ?connected_1 && ?connected_2) AS ?status) .
}

sv_injection

Question

Which topological nodes have steady-state real and reactive power injections, and what are their values?

TLDR;

The query retrieves the IDs of topological nodes along with their real (P) and reactive (Q) power injection values from SvInjection measurements.

Explanation

This SPARQL query is designed to extract information from the SvInjection class in the CIM model. The SvInjection class represents the steady-state injection of real and reactive power into the network at a given topological node.

Here's how the query works step-by-step:

  1. PREFIX declaration The prefix cim: is defined to point to the CIM RDF namespace: http://iec.ch/TC57/2013/CIM-schema-cim16# This makes it easier to refer to CIM classes and properties without typing the full URI.

  2. Triple pattern for node identification


?sv\_injection cim\:SvInjection.TopologicalNode/cim\:IdentifiedObject.mRID ?node ;

This line does two things:

  • Finds instances of SvInjection and follows the cim:SvInjection.TopologicalNode property to get the related topological node.
  • From the topological node, it navigates via cim:IdentifiedObject.mRID to retrieve the node's unique identifier (?node).
  1. Real power injection value

cim\:SvInjection.pInjection ?p ;

This fetches the real power (P) injection value associated with the SvInjection record, storing it in ?p.

  1. Reactive power injection value

cim\:SvInjection.qInjection ?q .

This fetches the reactive power (Q) injection value from the SvInjection record, storing it in ?q.

  1. Result The output will be a table with columns:
  • ?node → The mRID of the topological node.
  • ?p → The real power injection value.
  • ?q → The reactive power injection value.

Effectively, the query links steady-state injection records to their topological node IDs and retrieves both P and Q injection values for each.

Original SPARQL

# Name: SvInjection
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?node ?p ?q WHERE {
  ?sv_injection cim:SvInjection.TopologicalNode/cim:IdentifiedObject.mRID ?node ;
                cim:SvInjection.pInjection ?p ;
                cim:SvInjection.qInjection ?q .
}

sv_power_deviation

Question

For each topological node, what is the summed active power from connected terminals (from SvPowerFlow or converter nameplate) and how does it compare to the reported SvInjection.pInjection at that node? Also, which connectivity nodes and terminals contribute to it?

TLDR;

Groups by TopologicalNode and:

  • Sums terminal active power p (prefer SvPowerFlow; fallback to ACDCConverter.p if no state value).
  • Retrieves the node’s reported SvInjection.pInjection (takes the max if multiple).
  • Lists involved ConnectivityNode mRIDs and terminal names.

Explanation

This query rolls up power information from terminals to their TopologicalNode and aligns it with the node-level state variable injection. Key parts:

  1. Equipment/terminal context via a federated service
    {
      SELECT * {
        ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
        SERVICE ?eq_repo {
          ?terminal cim:Terminal.ConnectivityNode ?con_node ;
                    cim:Terminal.ConductingEquipment ?equipment ;
                    cim:IdentifiedObject.name ?terminal_name .
          ?con_node cim:IdentifiedObject.mRID ?con_node_mrid
        }
      }
    }
    
  • Finds an equipment repository endpoint (?eq_repo) and uses SERVICE to federate into it.
  • Pulls terminals, their conducting equipment, and their connectivity nodes (plus IDs and terminal names).
  • This establishes the list of terminals and how they attach to the network.
  1. Terminal active power (p) with fallback

    {
      ?sv_power_flow cim:SvPowerFlow.Terminal ?terminal ;
                     cim:SvPowerFlow.p ?p
    }
    UNION {
      ?equipment cim:ACDCConverter.p ?p
      FILTER NOT EXISTS {
        ?sv_power_flow cim:SvPowerFlow.Terminal ?terminal
      }
    }
    
    • Primary source: SvPowerFlow bound to the ?terminal, giving ?p.
    • Fallback: If there is no SvPowerFlow for that terminal, use the equipment’s ACDCConverter.p as ?p.
    • This ensures each terminal contributes a power value even when state variables are missing.
  2. Map terminals to a TopologicalNode (two routes)

    {
      ?con_node cim:ConnectivityNode.TopologicalNode ?tp_node
    }
    UNION {
      FILTER NOT EXISTS {
        ?con_node cim:ConnectivityNode.TopologicalNode ?tp_node
      }
      ?terminal cim:Terminal.TopologicalNode ?tp_node
    }
    
    • Preferred mapping: from the ConnectivityNode to its TopologicalNode.
    • Fallback mapping: if the connectivity node lacks a topo link, use the terminal’s direct TopologicalNode.
    • This normalizes everything to the topology level.
  3. Node identity and node-level injection

    ?tp_node cim:IdentifiedObject.mRID ?node ;
             cim:IdentifiedObject.name ?name ;
             ^cim:SvInjection.TopologicalNode/cim:SvInjection.pInjection ?p_inj
    
    • Captures the TopologicalNode’s mRID (?node) and name.
    • Via an inverse path, finds any SvInjection on that node and binds its pInjection as ?p_inj.
  4. Aggregation by node

    GROUP BY ?node
    

    With the following outputs:

    • (SUM(xsd:double(STR(?p))) AS ?sum_terminal_flow) Sums all terminal power contributions on the node. STR() then xsd:double() is used to robustly cast different literal forms of ?p to a numeric for summation.
    • (MAX(xsd:double(STR(?p_inj))) AS ?reported_sv_injection) If multiple SvInjection values exist for the node, choose the maximum as a representative reported value.
    • (GROUP_CONCAT(DISTINCT (?con_node_mrid); SEPARATOR=",") AS ?connectivity_nodes) Lists unique contributing ConnectivityNode mRIDs for traceability.
    • (GROUP_CONCAT(?terminal_name; SEPARATOR=",") AS ?terminal_names) Lists the terminal names aggregated into the node.

What the result means

Each result row corresponds to a TopologicalNode:

  • ?sum_terminal_flow: The net active power from all terminals tied (directly or via connectivity nodes) to that node, using state values when available, otherwise converter power.
  • ?reported_sv_injection: The node-level injection reported in SvInjection.
  • Comparing the two highlights power deviations between bottom-up terminal sums and top-down node injection, useful for validation, estimation errors, or data gaps.
  • ?connectivity_nodes/?terminal_names give provenance for auditing which parts of the network and which terminals contributed to the aggregate.

Original SPARQL

# Name: Sv power deviation
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?node (SUM(xsd:double(STR(?p))) AS ?sum_terminal_flow) (MAX(xsd:double(STR(?p_inj))) AS ?reported_sv_injection) (GROUP_CONCAT(DISTINCT (?con_node_mrid); SEPARATOR=",") AS ?connectivity_nodes) (GROUP_CONCAT(?terminal_name; SEPARATOR=",") AS ?terminal_names) WHERE {
  {
    SELECT * {
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      SERVICE ?eq_repo {
        ?terminal cim:Terminal.ConnectivityNode ?con_node ;
                  cim:Terminal.ConductingEquipment ?equipment ;
                  cim:IdentifiedObject.name ?terminal_name .
        ?con_node cim:IdentifiedObject.mRID ?con_node_mrid
      }
    }
  }
  {
    ?sv_power_flow cim:SvPowerFlow.Terminal ?terminal ;
                   cim:SvPowerFlow.p ?p
  }
  UNION {
    ?equipment cim:ACDCConverter.p ?p
    FILTER NOT EXISTS {
      ?sv_power_flow cim:SvPowerFlow.Terminal ?terminal
    }
  }
  {
    ?con_node cim:ConnectivityNode.TopologicalNode ?tp_node
  }
  UNION {
    FILTER NOT EXISTS {
      ?con_node cim:ConnectivityNode.TopologicalNode ?tp_node
    }
    ?terminal cim:Terminal.TopologicalNode ?tp_node
  }
  ?tp_node cim:IdentifiedObject.mRID ?node ;
           cim:IdentifiedObject.name ?name ;
           ^cim:SvInjection.TopologicalNode/cim:SvInjection.pInjection ?p_inj
}
GROUP BY ?node

switches

Question

Which switches (breaker, disconnector, etc.) exist in the model, what is each switch’s effective open/closed state, and which two connectivity nodes (by mRID) does each switch connect between?

TLDR;

Returns one row per switch (from a set of switch-like equipment types) with: mRID, effective state (is_open, preferring SSH Switch.open over EQ normalOpen), equipment type (local name), terminal‑1 and terminal‑2 connectivity node mRIDs, and the switch name. If a switch has only one terminal defined, terminal‑2 collapses to terminal‑1.

Explanation

  • Repository selection & SERVICE scope

    • The inner SELECT * retrieves an equipment repository IRI ?eq_repo via the triple:
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      
      The subsequent SERVICE ?eq_repo { ... } block executes the main pattern against that repository/graph.
  • Handling switches with variable terminal counts

    • A subquery inside the SERVICE finds, for each switch, the maximum terminal sequence number it has:
      SELECT (?internal_switch AS ?switch) (MAX(?nr) AS ?max_terminal_number) {
        ?internal_switch cim:Switch.normalOpen ?_unused_normal_open ;
                         ^cim:Terminal.ConductingEquipment/cim:ACDCTerminal.sequenceNumber ?nr
      }
      GROUP BY ?internal_switch
      
    • This yields ?max_terminal_number (usually 2). If it’s 1 (a degenerate case), the query later binds both terminals using sequence number 1 so terminal‑2 effectively equals terminal‑1, representing a switch that cannot connect two distinct nodes.
  • Limiting to switch-like equipment types

    • VALUES ?type { cim:Breaker cim:Disconnector cim:GroundDisconnector cim:LoadBreakSwitch cim:Recloser cim:MktSwitch cim:Fuse cim:Sectionaliser cim:Cut cim:Jumper } restricts results to common switch classes.
  • Core switch attributes

    • The main switch pattern requires:
      • cim:IdentifiedObject.mRID?mrid
      • RDF a ?type → later turned into a readable local name with STRAFTER(STR(?type),"#") AS ?equipment_type
      • cim:Switch.normalOpen?normal_open (the EQ-profile default state)
      • Two terminals linked by inverse of cim:Terminal.ConductingEquipment:
        • ?terminal1 with cim:ACDCTerminal.sequenceNumber 1
        • ?terminal2 with cim:ACDCTerminal.sequenceNumber ?max_terminal_number
      • Each terminal is followed to its connectivity node mRID:
        • Terminal‑1 → ?connectivity_node_1
        • Terminal‑2 → ?connectivity_node_2
      • cim:IdentifiedObject.name?name
  • Effective state resolution (SSH overrides EQ)

    • The operational state may be provided dynamically in SSH as cim:Switch.open:
      OPTIONAL { ?switch cim:Switch.open ?ssh_is_open }
      BIND (COALESCE(?ssh_is_open, ?normal_open) AS ?is_open)
      
    • COALESCE ensures ?is_open prefers the SSH state when present; otherwise it falls back to the equipment’s normalOpen.
  • Final projection

    • The outermost SELECT returns:
      • ?mrid
      • ?is_open (effective state)
      • ?equipment_type (local class name after #)
      • ?connectivity_node_1, ?connectivity_node_2
      • ?name
  • Practical outcome

    • Each row fully identifies a switch, its class, its readable name, where it sits in the network (via the two endpoint connectivity node mRIDs), and whether it is open or closed according to the most authoritative available profile (SSH > EQ). For single‑terminal anomalies, both endpoints resolve to the same node, reflecting no connective capability.

Original SPARQL

# Name: Switches
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?mrid ?is_open (STRAFTER(STR(?type),"#") AS ?equipment_type) ?connectivity_node_1 ?connectivity_node_2 ?name WHERE {
  {
    SELECT * {
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      SERVICE ?eq_repo {
        {
          # Extract the maximum terminal number per switch. This number is used to deduce the second terminal
          # Normally this equals 2, but occasionally it can be one. In that case it means that the switch
          # has only one termainal. A switch with only one terminal can not connect or disconnect anything
          # which is equivalent of setting terminal 2 equal to terminal 1
          SELECT (?internal_switch AS ?switch) (MAX(?nr) AS ?max_terminal_number) {
            ?internal_switch cim:Switch.normalOpen ?_unused_normal_open ;
                             ^cim:Terminal.ConductingEquipment/cim:ACDCTerminal.sequenceNumber ?nr
          }
          GROUP BY ?internal_switch
        }
        VALUES ?type { cim:Breaker cim:Disconnector cim:GroundDisconnector cim:LoadBreakSwitch cim:Recloser cim:MktSwitch cim:Fuse cim:Sectionaliser cim:Cut cim:Jumper }
        ?switch cim:IdentifiedObject.mRID ?mrid ;
                a ?type ;
                cim:Switch.normalOpen ?normal_open ;
                ^cim:Terminal.ConductingEquipment ?terminal1 ;
                ^cim:Terminal.ConductingEquipment ?terminal2 ;
                cim:IdentifiedObject.name ?name .
        ?terminal1 cim:ACDCTerminal.sequenceNumber 1 ;
                   cim:Terminal.ConnectivityNode/cim:IdentifiedObject.mRID ?connectivity_node_1 .
        ?terminal2 cim:ACDCTerminal.sequenceNumber ?max_terminal_number ;
                   cim:Terminal.ConnectivityNode/cim:IdentifiedObject.mRID ?connectivity_node_2 .
      }
    }
  }
  OPTIONAL {
    ?switch cim:Switch.open ?ssh_is_open
  }
  BIND (COALESCE(?ssh_is_open, ?normal_open) AS ?is_open)
}

synchronous_machines

Question

Which synchronous machines (and their associated generating units) are in a given region, what are their statuses and electrical values, and what market/scheduling metadata (limits, codes, grouping, type, and aFRR prequalification) are associated with them?

TLDR;

Federated query that lists synchronous machines in ${region}, deriving status and P/Q from state variables, defaulting and inferring limits/type when missing, and enriching with Statnett extension fields (market code, merit order, schedule resource, station group) plus an aFRR-prequalification flag.

Explanation

This query returns one row per synchronous machine (or its generating unit) that satisfies:

  • It belongs to a substation within an area whose name matches ${region}.
  • It is enabled for network analysis (defaulting to true when not explicitly disabled).

Federated source selection

  • ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo . followed by SERVICE ?eq_repo { ... } performs a federated subquery against the equipment repository endpoint discovered via that predicate.

Core equipment & topology

  • Selects each ?machine a cim:SynchronousMachine and its basic identifiers:
    • cim:IdentifiedObject.mRID ?machine_mrid
    • cim:IdentifiedObject.name ?name
    • Apparent power rating cim:RotatingMachine.ratedS ?sn
  • Topology binding:
    • Inverse from Terminal to the machine: ^cim:Terminal.ConductingEquipment ?terminal
    • From terminal to connectivity node and its mRID: ?terminal cim:Terminal.ConnectivityNode ?con_node ; ?con_node cim:IdentifiedObject.mRID ?connectivity_node
  • Container → substation chain:
    • cim:Equipment.EquipmentContainer / cim:VoltageLevel.Substation ?substation
    • Region/area lookup: ?substation cim:Substation.Region / cim:SubGeographicalRegion.Region / cim:IdentifiedObject.name ?area
    • Substation identifier: ?substation cim:IdentifiedObject.mRID ?substation_mrid
  • Region filter: FILTER REGEX(?area,'${region}')

Generating Unit association & type

  • Because of CIM15/16 modeling differences, it tries both properties:
    • cim:SynchronousMachine.GeneratingUnit | cim:RotatingMachine.GeneratingUnit ?nullable_gen_unit
  • If a generating unit exists, its rdf:type is captured via ?nullable_gen_unit a ?gen_type against a whitelist of unit classes:
    • { cim:NuclearGeneratingUnit cim:MktGeneratingUnit cim:HydroGeneratingUnit cim:WindGeneratingUnit cim:ThermalGeneratingUnit cim:SolarGeneratingUnit }
  • A non-null ?gen_unit is guaranteed for later use:
    • BIND (COALESCE(?nullable_gen_unit, URI("http://no-gen-unit")) AS ?gen_unit)
  • Human-friendly generator type string:
    • BIND (IF(BOUND(?gen_type), replace(STR(?gen_type), STR(cim:), ""), "unknown") AS ?generator_type)

Operating limits & market metadata (SN extensions)

  • If a generating unit exists, optional properties are pulled:
    • Identifiers & limits:
      • cim:IdentifiedObject.mRID ?gen_unit_mrid
      • cim:GeneratingUnit.minOperatingP ?nullable_minP
      • cim:GeneratingUnit.maxOperatingP ?nullable_maxP
    • Statnett market fields:
      • SN:GeneratingUnit.marketCode ?market_code
      • SN:GeneratingUnit.groupAllocationWeight ?merit_order
      • SN:GeneratingUnit.ScheduleResource ?ScheduleResource
        • From the schedule resource:
          • SN:ScheduleResource.marketCode ?station_group
          • cim:IdentifiedObject.mRID ?schedule_resource
          • cim:IdentifiedObject.name ?station_group_name
  • Primary identifier preference:
    • BIND (COALESCE(?gen_unit_mrid, ?machine_mrid) AS ?mrid) — use the GU mRID when available, otherwise the machine’s.

aFRR prequalification

  • Boolean flag computed via an existence check on the GU:
    • BIND (EXISTS { ?gen_unit SN:GeneratingUnit.ScheduleFRRResource [] } AS ?afrr_prequalified)

Status & network-analysis enablement

  • Connectivity status from the terminal: ?terminal cim:ACDCTerminal.connected ?connected
  • Optional state variable status (if present):
    • ?machine ^cim:SvStatus.ConductingEquipment / cim:SvStatus.inService ?in_service
  • Effective status:
    • BIND (COALESCE(?in_service, ?connected) AS ?status) — prefer state variable; fallback to connectivity.
  • Network-analysis toggle (SN extension), defaulting to true:
    • OPTIONAL { ?machine SN:Equipment.networkAnalysisEnable ?_network_analysis }
    • BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
    • Final filter: FILTER (?network_analysis)

Electrical state variables (Sv) & derived limits

  • From SvPowerFlow tied to the terminal:
    • cim:SvPowerFlow.p ?p (active power)
    • cim:SvPowerFlow.q ?q (reactive power)
  • Computed limits when GU limits are missing:
    • ?max_p defaults to:
      • ?nullable_maxP if present, else
      • ?p when p ≥ 0, else 0.0
      • BIND (COALESCE(?nullable_maxP, IF(xsd:double(STR(?p)) >= 0.0, ?p, 0.0), 0.0) AS ?max_p)
    • ?min_p defaults to:
      • ?nullable_minP if present, else
      • ?p when p < 0, else 0.0
      • BIND (COALESCE(?nullable_minP, IF(xsd:double(STR(?p)) < 0.0, ?p, 0.0), 0.0) AS ?min_p)
    • Note the explicit cast xsd:double(STR(?p)) to ensure numeric comparison robustness.

Result columns

  • The outer SELECT exposes:
    • Identifiers & names: ?mrid, ?name, ?substation_mrid, ?connectivity_node, ?schedule_resource
    • Status & configuration: ?status, ?generator_type, ?afrr_prequalified
    • Ratings & state variables: ?sn, ?p, ?q, ?max_p, ?min_p
    • Market metadata: ?market_code, ?merit_order, ?station_group, ?station_group_name

Practical notes

  • The query is resilient to partial data: pervasive OPTIONAL, COALESCE, and EXISTS ensure outputs even when GU links or limits are missing.
  • It accommodates CIM15/16 differences by trying both SynchronousMachine.GeneratingUnit and RotatingMachine.GeneratingUnit.
  • ${region} is a template parameter inserted into a regex filter against the substation’s area name.

Original SPARQL

# Name: Synchronous machines
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?mrid ?name ?market_code ?status ?station_group ?station_group_name ?substation_mrid ?max_p ?min_p ?merit_order ?sn ?p ?q ?connectivity_node ?generator_type ?schedule_resource ?afrr_prequalified WHERE {
  # Extract properties for synchronous machines.
  {
    SELECT * WHERE {
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      SERVICE ?eq_repo {
        ?machine a cim:SynchronousMachine ;
                 cim:IdentifiedObject.mRID ?machine_mrid ;
                 cim:IdentifiedObject.name ?name ;
                 cim:RotatingMachine.ratedS ?sn ;
                 ^cim:Terminal.ConductingEquipment ?terminal ;
                 cim:Equipment.EquipmentContainer/cim:VoltageLevel.Substation ?substation .
        # TODO: Seems like cim15 uses SynchronousMachine while cim16 uses RotatingMachine. Will fix this when we have a proper cim16 model
        OPTIONAL {
          VALUES ?gen_type { cim:NuclearGeneratingUnit cim:MktGeneratingUnit cim:HydroGeneratingUnit cim:WindGeneratingUnit cim:ThermalGeneratingUnit cim:SolarGeneratingUnit }
          ?machine cim:SynchronousMachine.GeneratingUnit | cim:RotatingMachine.GeneratingUnit ?nullable_gen_unit .
          ?nullable_gen_unit a ?gen_type
        }
        BIND (COALESCE(?nullable_gen_unit, URI("http://no-gen-unit")) AS ?gen_unit)
        ?terminal cim:Terminal.ConnectivityNode ?con_node .
        ?con_node cim:IdentifiedObject.mRID ?connectivity_node .
        # Extract area and mrid for the substation associated with a sync machine
        ?substation cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area ;
                    cim:IdentifiedObject.mRID ?substation_mrid .
        # Optionally extract min/max operating power for each machin
        OPTIONAL {
          ?gen_unit cim:IdentifiedObject.mRID ?gen_unit_mrid ;
                    cim:GeneratingUnit.minOperatingP ?nullable_minP ;
                    cim:GeneratingUnit.maxOperatingP ?nullable_maxP .
          OPTIONAL {
            ?gen_unit SN:GeneratingUnit.marketCode ?market_code ;
                      SN:GeneratingUnit.groupAllocationWeight ?merit_order ;
                      SN:GeneratingUnit.ScheduleResource ?ScheduleResource .
            ?ScheduleResource SN:ScheduleResource.marketCode ?station_group ;
                              cim:IdentifiedObject.mRID ?schedule_resource ;
                              cim:IdentifiedObject.name ?station_group_name .
          }
        }
        FILTER REGEX(?area,'${region}')
        # Opionally extract non-CIM standard properties generating units
        OPTIONAL {
          ?machine SN:Equipment.networkAnalysisEnable ?_network_analysis
        }
        BIND (IF(BOUND(?gen_type),replace(STR(?gen_type), STR(cim:), ""),"unknown") AS ?generator_type)
        BIND (COALESCE(?gen_unit_mrid, ?machine_mrid) AS ?mrid)
        BIND (EXISTS {
          ?gen_unit SN:GeneratingUnit.ScheduleFRRResource []
        } AS ?afrr_prequalified)
      }
    }
  }
  ?terminal cim:ACDCTerminal.connected ?connected .
  OPTIONAL {
    ?machine ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service
  } .
  ?sv_power_flow cim:SvPowerFlow.Terminal ?terminal ;
                 cim:SvPowerFlow.p ?p ;
                 cim:SvPowerFlow.q ?q .
  BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
  BIND (COALESCE(?in_service, ?connected) AS ?status)
  BIND (COALESCE(?nullable_maxP, IF(xsd:double(STR(?p)) >= 0.0,?p,0.0), 0.0) AS ?max_p)
  BIND (COALESCE(?nullable_minP, IF(xsd:double(STR(?p)) < 0.0,?p,0.0), 0.0) AS ?min_p)
  FILTER (?network_analysis)
}

transformer_branches

Question

How can I list transformer-end “branches” (one per winding) in a given region, including their electrical parameters, connection status, tap-influenced ratio and angle, and the two “nodes” they connect?

TLDR;

For each transformer end in substations whose area name matches ${region}, this query returns a branch-like view: impedance/admittance (r, x, b, g), nominal voltage, an active-power limit named ${rate} (if any), service/connected status, and the two nodes—(1) the real connectivity node and (2) a dummy node equal to the transformer’s mRID. Tap-changer settings scale the voltage ratio and set the phase angle from the phase-angle clock.

Explanation

High-level idea. Power transformer ends are treated as branch elements that connect a real network node to a “dummy” node representing the transformer unit. This makes each end look like a branch for network export/analysis. The inner SERVICE block gathers all equipment facts; the outer block adds status flags and derives tap-dependent quantities before selecting final columns.

1) Scoping to the equipment graph & region

  • ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo . Looks up the endpoint/graph for equipment data and binds it to ?eq_repo.
  • SERVICE ?eq_repo { ... } All core equipment facts are retrieved from that repository.
  • Region filter:

?p\_transformer cim\:Equipment.EquipmentContainer ?Substation .
?Substation cim\:Substation.Region/
cim\:SubGeographicalRegion.Region/
cim\:IdentifiedObject.name ?area .
FILTER (REGEX(?area,'\${region}'))

Keeps only transformers inside substations whose area name matches the ${region} regex.

2) Modeling a “branch” per transformer end

  • “Dummy” node: ?p_transformer cim:IdentifiedObject.mRID ?node_2 . The transformer’s mRID is reused as a pseudo node (?node_2). In the final SELECT it’s exposed as ?connectivity_node_2.
  • Select the transformer end (winding):

?winding cim\:TransformerEnd.Terminal ?terminal ;
cim\:IdentifiedObject.mRID ?mrid ;
cim\:IdentifiedObject.name ?name ;
cim\:PowerTransformerEnd.PowerTransformer ?p\_transformer ;
cim\:PowerTransformerEnd.ratedU ?ubase ;
cim\:PowerTransformerEnd.r ?r ;
cim\:PowerTransformerEnd.x ?x ;
cim\:PowerTransformerEnd.b ?b ;
cim\:PowerTransformerEnd.g ?g .

Each end contributes its series impedance (r,x) and shunt b,g, plus its rated voltage ?ubase.

3) Clock angle and real network node

  • Phase-angle clock (vector group position) is optional:

OPTIONAL { ?winding cim\:PowerTransformerEnd.phaseAngleClock ?aclock . }
BIND (COALESCE(?aclock, 0.0) AS ?angleclock)

If absent, default is 0.

  • Real connectivity node and nominal voltage:

?terminal cim\:Terminal.ConnectivityNode ?con\_node .
?con\_node cim\:ConnectivityNode.ConnectivityNodeContainer/
cim\:VoltageLevel.BaseVoltage/
cim\:BaseVoltage.nominalVoltage ?un ;
cim\:IdentifiedObject.mRID ?connectivity\_node\_1 .

This gives the actual node id (?connectivity_node_1) and its nominal voltage ?un.

4) Optional active power limit (rate)

  • If there’s an ActivePowerLimit on the same terminal with name exactly ${rate}, its value is returned as ?rate:

OPTIONAL {
?\_lim a cim\:ActivePowerLimit ;
cim\:OperationalLimit.OperationalLimitSet/cim\:OperationalLimitSet.Terminal ?terminal ;
cim\:IdentifiedObject.name '\${rate}' ;
cim\:ActivePowerLimit.value ?rate .
}

5) Network-analysis enable flag

  • Some datasets gate equipment by SN:Equipment.networkAnalysisEnable:

OPTIONAL { ?p\_transformer SN\:Equipment.networkAnalysisEnable ?\_network\_analysis } .
BIND (COALESCE(?\_network\_analysis, True) AS ?network\_analysis)
FILTER (?network\_analysis)

Only ends from equipment enabled for network analysis are kept (defaulting to enabled if the flag is missing).

6) Tap-changer handling and voltage ratio

  • Optional ratio tap-changer facts per end:

OPTIONAL {
?tap\_changer cim\:RatioTapChanger.TransformerEnd ?winding ;
cim\:RatioTapChanger.stepVoltageIncrement ?inc ;
cim\:TapChanger.neutralStep ?neutral\_step ;
cim\:TapChanger.normalStep ?normal\_step
} .
BIND (COALESCE(?tap\_changer, [http://non-existing-tap-changer](http://non-existing-tap-changer)) AS ?ratio\_tap\_changer)

  • Operating step:

OPTIONAL { ?ratio\_tap\_changer cim\:TapChanger.step ?ssh\_step } .
BIND (COALESCE(?ssh\_step, ?normal\_step) AS ?step)

Prefers an SSH (state) step; falls back to equipment normalStep.

  • Tap ratio multiplier ?tau:

BIND (IF(BOUND(?inc),
1 + (xsd\:integer(STR(?step)) - xsd\:integer(STR(?neutral\_step)))
\* xsd\:double(STR(?inc)) / 100.0,
1.0) AS ?tau)

If stepVoltageIncrement (?inc, in %) exists, scale around neutralStep. Otherwise no scaling.

  • Final per-end ratio (base adjustment by nominal/rated U):

BIND (xsd\:double(STR(?un)) \* ?tau / xsd\:double(STR(?ubase)) AS ?ratio)

Casts to numbers robustly via STR(...) then xsd:double(...).

7) Phase angle from clock


BIND (xsd\:double(30.0) \* ?angleclock AS ?angle)

Each clock position corresponds to 30 electrical degrees (e.g., clock 1 → 30°, 6 → 180°).

8) Status resolution (service vs. connected) Outside the SERVICE block the query consults operational state:


?terminal cim\:ACDCTerminal.connected ?connected .
OPTIONAL { ?winding ^cim\:SvStatus.ConductingEquipment/cim\:SvStatus.inService ?in\_service } .
BIND (COALESCE(?in\_service, ?connected) AS ?status)

If a state variable (SvStatus.inService) exists, it wins; otherwise the terminal’s connected flag is used.

9) Final projection The top-level SELECT returns:

  • ?status — resolved in-service/connected flag.
  • Identification: ?name, ?mrid.
  • Electrical: ?un, ?r, ?x, ?b, ?g, ?rate (optional).
  • Tap-derived: ?angle, ?ratio.
  • Topology: ?connectivity_node_1 (real node) and ?connectivity_node_2 (the dummy node, i.e., the transformer’s mRID via (?node_2 AS ?connectivity_node_2)).

Result interpretation. Each row describes one transformer end as a branch from the real connectivity node to a per-transformer dummy node, carrying per-end impedance/admittance, status, and tap-influenced ratio/angle. This shape is convenient for building branch lists in power-flow tools, especially when transformers are modeled by their individual ends.

Original SPARQL

# Name: Transformer branches
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
# Name: Transformer branches
SELECT ?status ?name ?mrid ?un ?r ?x ?b ?g ?rate ?angle ?ratio ?connectivity_node_1 (?node_2 AS ?connectivity_node_2) WHERE {
  {
    SELECT * WHERE {
      ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
      SERVICE ?eq_repo {
        # Use mrid of transformer as dummy node
        ?p_transformer cim:IdentifiedObject.mRID ?node_2 ;
                       cim:Equipment.EquipmentContainer ?Substation .
        ?Substation cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area .
        FILTER (REGEX(?area,'${region}'))
        # Extract properties for the windings associated with p_transformer
        ?winding cim:TransformerEnd.Terminal ?terminal ;
                 cim:IdentifiedObject.mRID ?mrid ;
                 cim:IdentifiedObject.name ?name ;
                 cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
                 cim:PowerTransformerEnd.ratedU ?ubase ;
                 cim:PowerTransformerEnd.r ?r ;
                 cim:PowerTransformerEnd.x ?x ;
                 cim:PowerTransformerEnd.b ?b ;
                 cim:PowerTransformerEnd.g ?g .
        OPTIONAL {
          ?winding cim:PowerTransformerEnd.phaseAngleClock ?aclock .
        }
        BIND (COALESCE(?aclock, 0.0) AS ?angleclock)
        # Extract the connectivity node for each termina, and find the nominal voltage from the connectivity node
        ?terminal cim:Terminal.ConnectivityNode ?con_node .
        ?con_node cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.BaseVoltage/cim:BaseVoltage.nominalVoltage ?un ;
                  cim:IdentifiedObject.mRID ?connectivity_node_1 .
        # Optionally extract active power limits
        OPTIONAL {
          ?_lim a cim:ActivePowerLimit ;
                cim:OperationalLimit.OperationalLimitSet/cim:OperationalLimitSet.Terminal ?terminal ;
                cim:IdentifiedObject.name '${rate}' ;
                cim:ActivePowerLimit.value ?rate .
        }
        OPTIONAL {
          ?p_transformer SN:Equipment.networkAnalysisEnable ?_network_analysis
        } .
        OPTIONAL {
          ?tap_changer cim:RatioTapChanger.TransformerEnd ?winding ;
                       cim:RatioTapChanger.stepVoltageIncrement ?inc ;
                       cim:TapChanger.neutralStep ?neutral_step ;
                       cim:TapChanger.normalStep ?normal_step
        } .
        BIND (COALESCE(?tap_changer, <http://non-existing-tap-changer>) AS ?ratio_tap_changer)
      }
    }
  }
  ?terminal cim:ACDCTerminal.connected ?connected .
  OPTIONAL {
    ?winding ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?in_service
  } .
  OPTIONAL {
    ?ratio_tap_changer cim:TapChanger.step ?ssh_step
  } .
  BIND (COALESCE(?ssh_step, ?normal_step) AS ?step)
  BIND (IF(BOUND(?inc),1 + (xsd:integer(STR(?step)) - xsd:integer(STR(?neutral_step))) * xsd:double(STR(?inc)) / 100.0,1.0) AS ?tau)
  BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
  FILTER (?network_analysis)
  # Assign status flag to the transformer. The first that exists is used
  # 1) in_service flag on the transformer
  # 2) Connected flag for the terminal
  BIND (COALESCE(?in_service, ?connected) AS ?status)
  BIND (xsd:double(STR(?un)) * ?tau / xsd:double(STR(?ubase)) AS ?ratio)
  BIND (xsd:double(30.0) * ?angleclock AS ?angle)
}

transformer_center_nodes

Question

Which connectivity nodes (and their substations, voltages, bidding zones, and islands) are associated with the “end 1” side of power transformers that are enabled for network analysis?

TLDR;

Fetches the connectivity node on terminal end number 1 of each power transformer (from a remote equipment repository), along with substation info, base voltage, bidding zone EIC, and topological island name; defaults missing “networkAnalysisEnable” to true and excludes equipment where it’s false. (Note: as written, the FILTER(?network_analysis) occurs before the BIND that defines it, which can eliminate all results depending on the SPARQL engine.)

Explanation

This query stitches together equipment data from a remote repository with local/topological context to list “bus-like” nodes tied to the first end of each power transformer.

Prefixes

  • cim:: IEC CIM v16 core.
  • SN:: Statnett CIM extensions.
  • entsoeSecretariat:: ENTSO-E Secretariat profile extensions.

High-level flow

  1. Discover the remote equipment repository

?\_eq\_subject [http://entsoe.eu/CIM/EquipmentCore/3/1](http://entsoe.eu/CIM/EquipmentCore/3/1) ?eq\_repo .

The triple encodes a pointer to an equipment dataset/endpoint. That value is used in a federated SERVICE.

  1. (OPTIONAL) Island lookup from topology

OPTIONAL {
?con\_node cim\:ConnectivityNode.TopologicalNode ?top\_node .
?top\_node ^cim\:TopologicalIsland.TopologicalNodes/cim\:IdentifiedObject.name ?island\_name
} .

  • From a ConnectivityNode reach its TopologicalNode.
  • Use a reverse path ^cim:TopologicalIsland.TopologicalNodes to find the containing TopologicalIsland and get its name.
  • This entire step is optional; if no island is found, a default will be applied later.
  1. Federated query to the equipment repository

SERVICE ?eq\_repo {
?p\_transformer cim\:IdentifiedObject.mRID ?node ;
cim\:IdentifiedObject.name ?busname ;
cim\:Equipment.EquipmentContainer ?container .

?winding cim:TransformerEnd.endNumber 1 ; cim:PowerTransformerEnd.PowerTransformer ?p_transformer ; cim:TransformerEnd.Terminal ?terminal .

?terminal cim:Terminal.ConnectivityNode ?con_node .

?con_node cim:IdentifiedObject.mRID ?connectivity_node ; cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.BaseVoltage ?base_voltage .

?container cim:IdentifiedObject.mRID ?container_mrid ; cim:IdentifiedObject.name ?container_name . ?container SN:Substation.MarketDeliveryPoint/ SN:MarketDeliveryPoint.BiddingArea/ entsoeSecretariat:IdentifiedObject.energyIdentCodeEIC ?bidzone .

OPTIONAL { ?p_transformer SN:Equipment.networkAnalysisEnable ?_network_analysis }

?base_voltage cim:IdentifiedObject.mRID ?base_voltage_mrid ; cim:BaseVoltage.nominalVoltage ?un


}

  • Transformer selection: grabs each PowerTransformer (?p_transformer) and its mRID (?node) and name (?busname), plus its equipment container (a VoltageLevel’s Substation).
  • End 1 only: picks the TransformerEnd with endNumber 1, then follows to its Terminal and on to the ConnectivityNode (?con_node). This yields the “bus” on the transformer’s end 1 side.
  • Voltage & IDs: from the ConnectivityNode, navigate to its VoltageLevel.BaseVoltage and pull base-voltage mRID and nominal voltage (?un).
  • Substation & bidding zone: from the container, get both ID/name and the market delivery point → bidding area → EIC code (?bidzone).
  • Network-analysis flag: SN:Equipment.networkAnalysisEnable is optional; if missing, we will treat it as enabled.
  1. Enable/disable logic via COALESCE

BIND (COALESCE(?\_network\_analysis, True) AS ?network\_analysis)

  • If the transformer lacks the networkAnalysisEnable flag, assume True.
  1. Filter to enabled equipment

FILTER (?network\_analysis)

  • Keeps only equipment where the computed ?network_analysis is true.

Important caveat: In standard SPARQL evaluation, variables used in FILTER must be in scope at that point. Here, FILTER(?network_analysis) appears before the BIND that creates ?network_analysis. On many engines this makes the filter evaluate with ?network_analysis unbound (EBV error), effectively discarding all results. To ensure the intended behavior, move the FILTER after the BIND, or filter directly on COALESCE(?_network_analysis, true):

  • Option A (reorder):
    BIND(COALESCE(?_network_analysis, true) AS ?network_analysis)
    FILTER(?network_analysis)
    
  • Option B (inline):
    FILTER(COALESCE(?_network_analysis, true))
    
  1. Island defaulting

BIND (COALESCE(?island\_name, "Unknown") AS ?island)

  • If the optional island lookup failed, set island to "Unknown".
  1. Projected variables / aliases
  • ?node: transformer mRID (id).
  • ?busname: transformer name (used here as a “bus” label).
  • ?substation: alias of ?container_name.
  • ?un: nominal base voltage.
  • ?base_voltage_mrid: base voltage ID.
  • ?substation_mrid: alias of ?container_mrid.
  • ?bidzone: EIC of the bidding area.
  • ?island: island name (or "Unknown").
  • ?is_swing_bus: hard-coded False.
  • ?connectivity_node: the node mRID on the end-1 terminal.
  • ?sv_voltage: duplicate of ?un (projected again for downstream compatibility).

What it returns conceptually One row per power transformer end-1 connectivity node that is “network-analysis enabled,” enriched with substation identifiers/names, base-voltage info, the market bidding zone, and the topological island label.

Suggested robustness tweaks

  • Move the FILTER(?network_analysis) after the BIND, or inline the COALESCE in the FILTER.
  • Consider guarding the federated SERVICE with error handling (engine-specific) if ?eq_repo can be missing.
  • Ensure the island OPTIONAL either runs after ?con_node is guaranteed bound (or rely on the engine’s join semantics as-is).

Original SPARQL

# Name: Transformer center nodes
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX entsoeSecretariat: <http://entsoe.eu/Secretariat/ProfileExtension/1#>
SELECT ?node ?busname (?container_name AS ?substation) ?un ?base_voltage_mrid (?container_mrid AS ?substation_mrid) ?bidzone ?island ( False as ?is_swing_bus) ?connectivity_node (?un AS ?sv_voltage) WHERE {
  OPTIONAL {
    ?con_node cim:ConnectivityNode.TopologicalNode ?top_node .
    ?top_node ^cim:TopologicalIsland.TopologicalNodes/cim:IdentifiedObject.name ?island_name
  } .
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    ?p_transformer cim:IdentifiedObject.mRID ?node ;
                   cim:IdentifiedObject.name ?busname ;
                   cim:Equipment.EquipmentContainer ?container .
    ?winding cim:TransformerEnd.endNumber 1 ;
             cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
             cim:TransformerEnd.Terminal ?terminal .
    ?terminal cim:Terminal.ConnectivityNode ?con_node .
    ?con_node cim:IdentifiedObject.mRID ?connectivity_node ;
              cim:ConnectivityNode.ConnectivityNodeContainer/cim:VoltageLevel.BaseVoltage ?base_voltage .
    ?container cim:IdentifiedObject.mRID ?container_mrid ;
               cim:IdentifiedObject.name ?container_name .
    ?container SN:Substation.MarketDeliveryPoint/SN:MarketDeliveryPoint.BiddingArea/entsoeSecretariat:IdentifiedObject.energyIdentCodeEIC ?bidzone
    OPTIONAL {
      ?p_transformer SN:Equipment.networkAnalysisEnable ?_network_analysis
    }
    ?base_voltage cim:IdentifiedObject.mRID ?base_voltage_mrid ;
                  cim:BaseVoltage.nominalVoltage ?un
  }
  FILTER (?network_analysis)
  BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
  BIND (COALESCE(?island_name, "Unknown") AS ?island)
}

transformer_winding_angle

Question

What is the present phase-shift angle (in degrees) of each phase-shifting transformer winding, computed from its tap position relative to neutral, and what is the associated equipment mRID?

TLDR;

For each phase tap changer, pick the current tap position (prefer state variable SvTapStep if available, otherwise TapChanger.step from SSH), subtract the neutral step, multiply by the phase-shift increment per step, and report the resulting angle with the winding’s mRID.

Explanation

1) Inputs & namespaces

  • cim: is the IEC CIM v16 namespace; xsd: provides numeric casts.
  • The query works across profiles: SSH/SV for operational state, and EQ for static equipment data.

2) Getting a tap changer and its “best-known” position

?tap_changer cim:TapChanger.step ?ssh_position .
OPTIONAL {
  ?tap_changer ^cim:SvTapStep.TapChanger/cim:SvTapStep.position ?sv_position
}
BIND (COALESCE(?sv_position, ?ssh_position) AS ?position)
  • cim:TapChanger.step is the steady‑state hypothesis (SSH) configured position.
  • The OPTIONAL block tries to read the live state variable cim:SvTapStep.position (SV).
  • COALESCE prefers ?sv_position (if present) and falls back to ?ssh_position, giving a single “effective” ?position.

3) Federated hop into the Equipment profile (EQ)

?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
SERVICE ?eq_repo { ... }
  • A data triple encodes (or references) the EQ SPARQL endpoint as ?eq_repo.
  • SERVICE ?eq_repo { ... } runs a federated subquery against the EQ dataset to fetch static design data for each tap changer.

4) Equipment data required for angle computation and identification Inside the SERVICE block:

?tap_changer
  cim:PhaseTapChangerLinear.stepPhaseShiftIncrement ?inc ;
  cim:TapChanger.neutralStep ?neutralstep ;
  ^cim:TransformerEnd.PhaseTapChanger/cim:IdentifiedObject.mRID ?mrid .
  • cim:PhaseTapChangerLinear.stepPhaseShiftIncrement (?inc) is the per‑step phase shift (typically degrees/step).
  • cim:TapChanger.neutralStep (?neutralstep) is the position that yields zero phase shift.
  • ^cim:TransformerEnd.PhaseTapChanger walks from the tap changer back to its TransformerEnd; from there, cim:IdentifiedObject.mRID gives the stable identifier ?mrid for that end (the winding terminal in EQ).

5) Final numeric computation

BIND ((xsd:double(STR(?position)) - xsd:double(STR(?neutralstep))) * xsd:double(STR(?inc)) AS ?angle)
  • Ensures numeric arithmetic by string‑casting then xsd:double‑casting each term (guards against mixed literal types).
  • Formula: angle = (position − neutralStep) × stepPhaseShiftIncrement
  • This yields the current phase‑shift angle in degrees for the winding associated with ?mrid.

6) Output

SELECT ?mrid ?angle
  • For each applicable tap changer / transformer end, the query returns the equipment mRID and the computed angle.

Key SPARQL techniques used

  • Inverse property paths (^prop) to navigate from a tap changer to its transformer end.
  • Federated queries (SERVICE ?eq_repo) to join runtime state with equipment design data stored in a separate endpoint.
  • OPTIONAL + COALESCE to prefer live state (SV) over planned state (SSH) seamlessly.
  • Explicit numeric casting to ensure reliable arithmetic across differently typed literals.

Original SPARQL

# Name: Winding transformer angle
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?mrid ?angle WHERE {
  ?tap_changer cim:TapChanger.step ?ssh_position .
  OPTIONAL {
    ?tap_changer ^cim:SvTapStep.TapChanger/cim:SvTapStep.position ?sv_position
  } .
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    ?tap_changer cim:PhaseTapChangerLinear.stepPhaseShiftIncrement ?inc ;
                 cim:TapChanger.neutralStep ?neutralstep ;
                 ^cim:TransformerEnd.PhaseTapChanger/cim:IdentifiedObject.mRID ?mrid .
  }
  BIND (COALESCE(?sv_position, ?ssh_position) AS ?position)
  BIND ((xsd:double(STR(?position)) - xsd:double(STR(?neutralstep))) * xsd:double(STR(?inc)) AS ?angle)
}

transformer_windings

Question

What are the windings of each power transformer, including the transformer's ID, the winding's ID, and the winding's end number?

TLDR;

This query lists all power transformers and their associated windings, showing the transformer's unique ID, each winding's unique ID, and the winding's connection end number.

Explanation

The query is structured to extract details about power transformers and their windings from a CIM dataset.

  1. Prefixes

    • The cim: prefix points to the IEC CIM 16 schema, which defines all the classes and properties used.
  2. Main transformer selection

    • ?power_transformer a cim:PowerTransformer ; cim:IdentifiedObject.mRID ?mrid . This line finds all instances of cim:PowerTransformer and retrieves their unique identifier (mRID).
  3. Winding association

    • ?winding cim:IdentifiedObject.mRID ?w_mrid ; cim:PowerTransformerEnd.PowerTransformer ?power_transformer ; cim:TransformerEnd.endNumber ?end_number This line:
      • Finds winding objects (?winding).
      • Gets their unique identifier (?w_mrid).
      • Links them back to their parent transformer through cim:PowerTransformerEnd.PowerTransformer.
      • Retrieves the endNumber property, which indicates the winding's connection point in the transformer (e.g., primary, secondary).
  4. Result columns

    • The query outputs three pieces of information:
      • ?mrid → Power transformer’s ID.
      • ?end_number → Winding end number.
      • ?w_mrid → Winding’s ID.

Essentially, this query gives a mapping of each transformer to each winding, with enough data to identify both uniquely and understand their connection points.

Original SPARQL

# Name: Transformer Windings
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
SELECT ?mrid ?end_number ?w_mrid WHERE {
  ?power_transformer a cim:PowerTransformer ;
                     cim:IdentifiedObject.mRID ?mrid .
  ?winding cim:IdentifiedObject.mRID ?w_mrid ;
           cim:PowerTransformerEnd.PowerTransformer ?power_transformer ;
           cim:TransformerEnd.endNumber ?end_number
}

transformers

Question

Which power transformers and their windings are in a sub-geographical region matching ${region}, what are their key electrical parameters (name, mRIDs, end number, rated voltage, r, x, terminal), and—if present—what is the value of the active power limit named ${rate}—but only for transformers that have network analysis enabled (or default to enabled when not specified)?

TLDR;

Return one row per transformer winding within the selected region, including winding/transformer identifiers and electrical parameters, optionally joining a named active power limit, and filtering to only equipment with networkAnalysisEnable true (defaulting to true when the flag is absent).

Explanation

  1. Prefixes

    • Uses the CIM 16 namespace (cim:) and a Statnett-specific extension namespace (SN:).
  2. Core transformer selection

    • ?p_transformer a cim:PowerTransformer ; cim:IdentifiedObject.mRID ?p_mrid ; ...
    • From each PowerTransformer we extract its mRID (?p_mrid) and navigate up its containment to derive the area name:
      cim:Equipment.EquipmentContainer
        / cim:Substation.Region
        / cim:SubGeographicalRegion.Region
        / cim:IdentifiedObject.name ?area
      
      This property path walks: Transformer → its EquipmentContainer (e.g., substation) → the substation’s Region → the parent SubGeographicalRegion’s Region → that region’s name.
  3. Winding join and electrical parameters

    • Each PowerTransformerEnd (winding) linked to the transformer is bound as ?winding:
      ?winding cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
               cim:PowerTransformerEnd.ratedU ?un ;
               cim:PowerTransformerEnd.r ?r ;
               cim:PowerTransformerEnd.x ?x ;
               cim:IdentifiedObject.mRID ?w_mrid ;
               cim:IdentifiedObject.name ?name ;
               cim:TransformerEnd.endNumber ?end_number ;
               cim:TransformerEnd.Terminal/cim:IdentifiedObject.mRID ?t_mrid .
      
    • This yields the winding’s rated voltage (?un), series resistance/reactance (?r, ?x), winding mRID (?w_mrid), winding name (?name), end number (?end_number), and the associated terminal mRID (?t_mrid).
  4. Region filtering

    • FILTER REGEX(?area,'${region}')
    • Keeps only transformers whose derived area name matches the (injected) regular expression ${region}. This allows partial matches; it’s case-sensitive by default unless your SPARQL endpoint treats it otherwise. If you need exact or case-insensitive matching, you’d typically adjust the pattern or use flags (e.g., '(?i)^...$').
  5. Optional active power limit lookup

    • Optional block:
      ?p_lim a cim:ActivePowerLimit ;
             cim:OperationalLimit.OperationalLimitSet
               / cim:OperationalLimitSet.Equipment ?winding ;
             cim:IdentifiedObject.name '${rate}' ;
             cim:ActivePowerLimit.value ?rate .
      
    • If there exists an ActivePowerLimit whose OperationalLimitSet applies to this winding and whose name equals the injected literal ${rate}, its value is returned as ?rate. If no such limit exists, ?rate is unbound for that row.
    • Note: If multiple limits match (e.g., multiple sets or duplicate names), the query may produce duplicate rows—one per matching limit.
  6. Network analysis enable flag (Statnett extension)

    • Optional read of a Statnett extension flag on the transformer:
      OPTIONAL { ?p_transformer SN:Equipment.networkAnalysisEnable ?_analysis_enabled . }
      BIND (COALESCE(?_analysis_enabled, True) AS ?analysis_enabled)
      FILTER (?analysis_enabled)
      
    • Logic:
      • If the flag exists, use its value.
      • If it’s absent, default to True via COALESCE, treating the transformer as enabled.
      • The FILTER then keeps only rows where ?analysis_enabled is true.
    • Implementation note: In SPARQL, FILTER applies to the bindings available at its position in the pattern. To be strictly safe across engines, place the FILTER (?analysis_enabled) after the BIND (as shown above). Some engines reorder, but relying on that can be brittle.
  7. Projected variables

    • SELECT ?name ?p_mrid ?w_mrid ?end_number ?un ?t_mrid ?r ?x ?rate
    • Output includes key identifiers and parameters for each winding, plus the optional ?rate.
  8. Cardinality & duplicates

    • The query returns one row per winding (and per matching limit, if present). Because of optional joins (limits, terminals) and property paths, multiple rows can appear for the same winding if there are multiple terminals or multiple matching limits. Use DISTINCT or further constraints if you need unique rows.
  9. Parameters and templating

    • ${region} and ${rate} appear to be template placeholders injected by the calling application. Ensure ${region} is a safe regex (escape special characters when needed) and ${rate} matches the exact IdentifiedObject.name used for the active power limit you want.

Original SPARQL

# Name: Transformers
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?name ?p_mrid ?w_mrid ?end_number ?un ?t_mrid ?r ?x ?rate WHERE {
  # Extract mRID for each power transformer
  ?p_transformer a cim:PowerTransformer ;
                 cim:IdentifiedObject.mRID ?p_mrid ;
                 cim:Equipment.EquipmentContainer/cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area .
  ?winding cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
           cim:PowerTransformerEnd.ratedU ?un ;
           cim:PowerTransformerEnd.r ?r ;
           cim:PowerTransformerEnd.x ?x ;
           cim:IdentifiedObject.mRID ?w_mrid ;
           cim:IdentifiedObject.name ?name ;
           cim:TransformerEnd.endNumber ?end_number ;
           cim:TransformerEnd.Terminal/cim:IdentifiedObject.mRID ?t_mrid ;
  FILTER REGEX(?area,'${region}') .
  OPTIONAL {
    ?p_lim cim:OperationalLimit.OperationalLimitSet/cim:OperationalLimitSet.Equipment ?winding ;
           a cim:ActivePowerLimit ;
           cim:IdentifiedObject.name '${rate}' ;
           cim:ActivePowerLimit.value ?rate
  }
  OPTIONAL {
    ?p_transformer SN:Equipment.networkAnalysisEnable ?_analysis_enabled .
  }
  FILTER (?analysis_enabled)
  BIND (COALESCE(?_analysis_enabled, True) AS ?analysis_enabled)
}

transformers_connected_to_converter

Question

Which power transformers are connected to DC converters in a specified geographical region, and what are their associated mRIDs and terminal mRIDs?

TLDR;

The query finds all power transformers that are connected to certain types of DC converters (like voltage source or current source converters) within a specified region, retrieving their mRIDs, names, and the mRIDs of their first winding terminals, while ensuring both the transformer and converter are enabled for network analysis.

Explanation

This SPARQL query identifies power transformers connected to converters by:

  1. Selecting Converter Types The query limits ?converter to specific CIM and vendor-specific converter classes:

    • cim:VsConverter, cim:CsConverter, cim:DCConvertUnit (standard CIM types)
    • ALG:VoltageSourceConverter, ALG:DCConverter (vendor extensions)
  2. Retrieving Converter mRID Each converter must have a cim:IdentifiedObject.mRID value, which is stored as ?mrid.

  3. Finding Connected Power Transformers A cim:PowerTransformer is identified with:

    • Its own mRID as ?p_mrid
    • Its alias name as ?name
    • A region name (?area) determined through the equipment container → substation → sub-geographical region → geographical region hierarchy.
    • Connectivity is determined by traversing from the transformer’s terminals through cim:ConnectivityNode links back to the converter's terminals.
  4. Identifying the First Winding Terminal Only the terminal for winding number 1 (cim:TransformerEnd.endNumber 1) is retrieved, and its mRID is stored as ?t_mrid.

  5. Filtering by Region The FILTER REGEX(?area,'${region}') ensures results only come from the region of interest.

  6. Checking Network Analysis Enable Flags The query optionally retrieves SN:Equipment.networkAnalysisEnable for both transformers and converters. If the flag is missing, it defaults to True using COALESCE. Only equipment with the flag set to True is included.

  7. Ensuring Unique Results SELECT DISTINCT removes duplicates, which can occur if converters appear in both EQ and SSH profiles.

In essence, the query walks the network model to find transformers directly connected to specific types of DC converters, filters them by region and network-analysis-enabled status, and outputs both transformer and terminal identifiers.

Original SPARQL

# Name: Transforers connected to converter
PREFIX ALG: <http://www.alstom.com/grid/CIM-schema-cim15-extension#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
# TODO: We probably need distinct since we get a hit on cim:VsConverter in SSH-profile and ALG:VoltageSourceConverter
# in EQ-repo. This was caused by correcting the ALG-namespace which lacked # at the end
SELECT DISTINCT ?mrid ?name ?t_mrid ?p_mrid WHERE {
  VALUES ?converter_type { cim:VsConverter cim:CsConverter cim:DCConvertUnit ALG:VoltageSourceConverter ALG:DCConverter }
  # Extract mRID for the converters
  ?converter a ?converter_type ;
             cim:IdentifiedObject.mRID ?mrid .
  # Extract mRID name for the power transformer associated with the converter
  ?p_transformer a cim:PowerTransformer ;
                 cim:IdentifiedObject.mRID ?p_mrid ;
                 cim:IdentifiedObject.aliasName ?name ;
                 cim:Equipment.EquipmentContainer/cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area ;
                 ^cim:Terminal.ConductingEquipment/cim:Terminal.ConnectivityNode/^cim:Terminal.ConnectivityNode/cim:Terminal.ConductingEquipment ?converter .
  # Extract the mRID of the terminal associated with the first winding on each power transformer
  ?winding cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
           cim:TransformerEnd.endNumber 1 ;
           cim:TransformerEnd.Terminal/cim:IdentifiedObject.mRID ?t_mrid .
  FILTER REGEX(?area,'${region}')
  OPTIONAL {
    ?p_transformer SN:Equipment.networkAnalysisEnable ?_transformer_analysis_enabled .
  }
  OPTIONAL {
    ?converter SN:Equipment.networkAnalysisEnable ?_converter_analysis_enabled .
  }
  BIND (COALESCE(?_transformer_analysis_enabled, True) AS ?transformer_analysis_enabled)
  BIND (COALESCE(?_converter_analysis_enabled, True) AS ?converter_analysis_enabled)
  FILTER (?transformer_analysis_enabled)
  FILTER (?converter_analysis_enabled)
}

type_mapper

Question

For each CIM attribute (SPARQL property), what is the underlying literal datatype it should map to, whether it is a Primitive or a CIMDatatype with a value?

TLDR;

Builds a lookup table from CIM attribute properties to their effective literal ranges by handling two cases: direct Primitive datatypes and CIMDatatype wrappers whose value points to the underlying primitive type.

Explanation

This query produces pairs of (?sparql_type, ?range) that tell you which literal datatype a given CIM attribute (expressed as an RDF/OWL property) should use.

It does this with two UNIONed patterns:

  1. Direct Primitive datatype
    ?sparql_type ^rdfs:domain/cims:dataType ?range .
    ?range cims:stereotype "Primitive"
    
  • ^rdfs:domain walks backwards from a class to the property whose domain is that class. In other words, it finds properties ?sparql_type such that there exists some class X with ?sparql_type rdfs:domain X.
  • cims:dataType on that class X points to a datatype resource ?range.
  • The filter ?range cims:stereotype "Primitive" limits to plain primitive datatypes (e.g., String, Integer, Boolean), so the mapping is direct: the attribute’s range is that primitive.
  1. CIMDatatype wrapper with a value slot

    ?sparql_type ^rdfs:domain/cims:dataType ?_dtype .
    ?_dtype cims:stereotype "CIMDatatype" .
    ?_dtype_value rdfs:domain ?_dtype ;
                  rdfs:label "value"@en ;
                  cims:dataType ?range
    
    • Some attributes are typed not by a primitive directly, but by a CIMDatatype (a structured/value object such as DateTime, UnitMultiplier, etc.).

    • For such a datatype ?_dtype, the query finds its internal property (here bound as ?_dtype_value) that:

      • has rdfs:domain ?_dtype (i.e., belongs to that datatype),
      • is labeled "value"@en, and
      • itself declares cims:dataType ?range.
    • This extracts the underlying primitive carried by the CIMDatatype’s value field, yielding the effective literal range for the original attribute.

Finally, SELECT DISTINCT deduplicates results in case the same mapping is discoverable via multiple paths or model profiles.

Why this matters: CIM profiles often model attributes via metaclasses where rdfs:domain and cims:dataType sit on a “slot” class rather than directly on the property. This query normalizes both modeling styles—direct primitives and wrapper CIMDatatypes—into a single, usable mapping from SPARQL attribute properties to concrete literal types.

Original SPARQL

# Name: Type mapper
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX cims: <http://iec.ch/TC57/1999/rdf-schema-extensions-19990926#>
SELECT DISTINCT ?sparql_type ?range WHERE {
  {
    ?sparql_type ^rdfs:domain/cims:dataType ?range .
    ?range cims:stereotype "Primitive"
  }
  UNION {
    ?sparql_type ^rdfs:domain/cims:dataType ?_dtype .
    ?_dtype cims:stereotype "CIMDatatype" .
    ?_dtype_value rdfs:domain ?_dtype ;
                  rdfs:label "value"@en ;
                  cims:dataType ?range
  }
}

wind_generating_units

Question

Which wind generating units (that are enabled for network analysis) exist in the model, and what are their IDs, names, min/max operating power, market code, station group, and associated wind power plant mRID?

TLDR;

Return all cim:WindGeneratingUnit instances with their mRID, name, min/max operating P, and (optionally) their market code, station group, and parent wind power plant mRID. The query intends to include only units where SN:Equipment.networkAnalysisEnable is true (defaulting to true when the flag is missing).

Explanation

Prefixes

  • cim: points to the IEC CIM v16 namespace.
  • SN: points to Statnett’s CIM extension namespace.

Core pattern

?wind_gen_unit a cim:WindGeneratingUnit ;
               cim:IdentifiedObject.mRID ?mrid ;
               cim:IdentifiedObject.name ?name ;
               cim:GeneratingUnit.maxOperatingP ?max_p ;
               cim:GeneratingUnit.minOperatingP ?min_p .

This selects every resource typed as a cim:WindGeneratingUnit and extracts its:

  • unique identifier (?mrid)
  • human‑readable name (?name)
  • operating limits (?min_p, ?max_p)

Optional commercial/plant context

OPTIONAL {
  ?wind_gen_unit
      SN:WindGeneratingUnit.WindPowerPlant/cim:IdentifiedObject.mRID ?plant_mrid ;
      SN:GeneratingUnit.marketCode ?market_code ;
      SN:GeneratingUnit.ScheduleResource/SN:ScheduleResource.marketCode ?station_group .
}
  • SN:WindGeneratingUnit.WindPowerPlant/cim:IdentifiedObject.mRID is a property path: from the wind unit, follow the Statnett link to its WindPowerPlant, then read that plant’s mRID into ?plant_mrid.
  • SN:GeneratingUnit.marketCode reads a market code directly associated with the generating unit.
  • SN:GeneratingUnit.ScheduleResource/SN:ScheduleResource.marketCode is another path: from the unit to its ScheduleResource, then read that resource’s market code into ?station_group. All three are optional: if any link is missing, the respective variable is simply unbound (NULL in the result).

Optional network‑analysis flag & filtering

OPTIONAL { ?wind_gen_unit SN:Equipment.networkAnalysisEnable ?_network_analysis }
FILTER (?network_analysis)
BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
  • The model may store a boolean flag SN:Equipment.networkAnalysisEnable indicating whether the equipment should be considered in network analysis runs. It is read into the temporary variable ?_network_analysis.
  • BIND (COALESCE(..., True) AS ?network_analysis) is intended to default the flag to true when it is absent: if ?_network_analysis is unbound, COALESCE yields True.
  • FILTER (?network_analysis) then keeps only rows where the effective flag is true.

Important execution‑order note In SPARQL, solution modifiers are applied in the written order within the group pattern. As written, the FILTER (?network_analysis) appears before BIND (...), which means ?network_analysis is unbound at filter time. An unbound variable in a FILTER expression evaluates to an error, which filters the row out. This would unintentionally drop all rows where the flag is missing, defeating the “default to True” intent.

To achieve the intended behavior, put the BIND before the FILTER:

OPTIONAL { ?wind_gen_unit SN:Equipment.networkAnalysisEnable ?_network_analysis }
BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
FILTER (?network_analysis)

With this order:

  • If the flag is present and true, the row stays.
  • If the flag is present and false, the row is removed.
  • If the flag is absent, COALESCE sets it to true, and the row stays.

Result set The SELECT projects:

?mrid ?name ?market_code ?station_group ?min_p ?max_p ?plant_mrid
  • ?market_code, ?station_group, and ?plant_mrid may be unbound (NULL) when the optional data is not present.
  • If a unit is linked to multiple schedule resources (or other multi‑valued properties), the query can produce multiple rows per unit (cartesian expansion). Add SELECT DISTINCT ... if you want to de‑duplicate.

Practical tips

  • Ensure boolean literals are actual booleans in the store (e.g., "true"^^xsd:boolean) so FILTER (?network_analysis) works as expected.
  • If you need to exclude units that lack the flag rather than defaulting to true, drop the COALESCE and filter directly on ?_network_analysis.
OPTIONAL { ?wind_gen_unit SN:Equipment.networkAnalysisEnable ?_network_analysis }
FILTER (BOUND(?_network_analysis) && ?_network_analysis)

Original SPARQL

# Name: Wind generating units
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
SELECT ?mrid ?name ?market_code ?station_group ?min_p ?max_p ?plant_mrid WHERE {
  ?wind_gen_unit a cim:WindGeneratingUnit ;
                 cim:IdentifiedObject.mRID ?mrid ;
                 cim:IdentifiedObject.name ?name ;
                 cim:GeneratingUnit.maxOperatingP ?max_p ;
                 cim:GeneratingUnit.minOperatingP ?min_p .
  OPTIONAL {
    ?wind_gen_unit SN:WindGeneratingUnit.WindPowerPlant/cim:IdentifiedObject.mRID ?plant_mrid ;
                   SN:GeneratingUnit.marketCode ?market_code ;
                   SN:GeneratingUnit.ScheduleResource/SN:ScheduleResource.marketCode ?station_group .
  }
  OPTIONAL {
    ?wind_gen_unit SN:Equipment.networkAnalysisEnable ?_network_analysis
  }
  FILTER (?network_analysis)
  BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
}

winding

Question

For each power transformer, how many windings does it have, how many of those are “in service,” what’s the transformer’s name, and what is the summed active power flowing at the winding terminals?

TLDR;

Group transformer windings by their parent cim:PowerTransformer (via cim:PowerTransformerEnd.PowerTransformer). Count windings, sum a boolean “in‑service” flag into ?nr_on, and sum terminal active power ?sv_p into ?p. Fetch equipment model data through a federated SERVICE endpoint discovered at runtime.

Explanation

What the query returns (grouped per transformer ?p_transformer):

  • ?count: number of windings (one per cim:TransformerEnd).
  • ?name: the transformer's name (taken as MAX(?_name); acts like “any” when names are identical).
  • ?nr_on: how many windings are effectively considered “on/in service.”
  • ?p: sum of active power injections at the windings’ terminals.

How it finds and aggregates windings

  • Each row starts from a winding (a cim:TransformerEnd that is part of a cim:PowerTransformer):
    ?winding cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
             cim:TransformerEnd.Terminal ?terminal .
    ?p_transformer cim:IdentifiedObject.name ?_name .
    

* `?terminal` is the AC/DC terminal associated with that winding.

**Getting state & telemetry (optionally)**

* `SvStatus.inService` (boolean) per **conducting equipment** of the winding:

  ```sparql
  OPTIONAL { ?winding ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?_in_service . }
  • Topology connection state on the terminal:

    OPTIONAL { ?terminal cim:ACDCTerminal.connected ?_connected . }
    
  • Steady‑state active power at the terminal:

    OPTIONAL { ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?_sv_p . }
    

Federated access to the equipment repository

  • The line

    ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
    SERVICE ?eq_repo { ... }
    

    discovers the equipment model endpoint IRI at runtime (?eq_repo) and then runs the equipment‑side pattern inside that remote/service graph. This is a data‑driven federation: the local store holds a pointer to the remote equipment dataset.

Deriving the flags and numeric measures

  • Count each winding:

    BIND (1 AS ?_count)
    
  • Default terminal connection when missing, then derive an “in service” indicator:

    BIND (COALESCE(?_connected, 1) AS ?connected)
    BIND (IF(COALESCE(?_in_service, ?connected), 1, 0) AS ?in_service)
    

    The intention is:

    • If SvStatus.inService exists, use it.
    • Otherwise fall back to ACDCTerminal.connected.
    • If both are missing, default to “on”.

    Spec nuance: In SPARQL, IF() expects a boolean Effective Boolean Value (EBV). Using 1 (an integer) as a fallback in COALESCE can yield an EBV error on some engines. A safer, standards‑compliant variant is:

    BIND (COALESCE(?_connected, true) AS ?_is_connected)              # boolean default
    BIND (COALESCE(?_in_service, ?_is_connected) AS ?_is_on)          # boolean
    BIND (IF(?_is_on, 1, 0) AS ?in_service)                           # numeric for SUM()
    
  • Power defaulting and summation:

    BIND (COALESCE(?_sv_p, xsd:double(0.0)) AS ?sv_p)
    

    Then

    (SUM(xsd:double(STR(?sv_p))) AS ?p)
    

    converts to string and back to double before summing. If ?sv_p is already xsd:double, SUM(?sv_p) suffices and is cleaner.

Grouping

GROUP BY ?p_transformer

Aggregates all winding rows per transformer.

Why MAX(?_name) for name?

  • Within a group, ?p_transformer should have a single name. Using MAX is a common trick to pick that single value without adding it to GROUP BY. If names could differ (data quality issue), you’d instead GROUP_CONCAT(DISTINCT ?_name; separator=", ") or return the transformer name from a separate query.

Potential data‑shape pitfalls & tips

  • Duplicate rows: If the model contains multiple SvPowerFlow entries per terminal/time or multiple terminals/endings per winding, you might over‑count. Use SELECT DISTINCT inside the SERVICE block or aggregate earlier to avoid duplication.
  • Type safety: Prefer boolean fallbacks (true/false) when building EBV conditions; avoid numeric literals (1/0) inside boolean expressions.
  • Units & sign convention: SvPowerFlow.p often uses MW and a sign convention (positive for injection, negative for consumption). Ensure your downstream interpretation matches your CIM profile.
  • Null‑tolerant sums: The COALESCE on ?sv_p ensures missing flows don’t nullify the group sum.

Minimal, standards‑friendly rewrite (same output, safer booleans):

PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN:  <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

SELECT ?p_transformer
       (SUM(?_count) AS ?count)
       (MAX(?_name)  AS ?name)
       (SUM(?in_service) AS ?nr_on)
       (SUM(?sv_p) AS ?p)
WHERE {
  OPTIONAL { ?winding ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?_in_service . }
  OPTIONAL { ?terminal cim:ACDCTerminal.connected ?_connected . }
  OPTIONAL { ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?_sv_p . }

  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    ?winding cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
             cim:TransformerEnd.Terminal ?terminal .
    ?p_transformer cim:IdentifiedObject.name ?_name .
  }

  BIND (COALESCE(?_connected, true) AS ?_is_connected)
  BIND (COALESCE(?_in_service, ?_is_connected) AS ?_is_on)
  BIND (IF(?_is_on, 1, 0) AS ?in_service)

  BIND (COALESCE(xsd:double(?_sv_p), 0.0) AS ?sv_p)
  BIND (1 AS ?_count)
}
GROUP BY ?p_transformer

Original SPARQL

# Name: Windings
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?p_transformer (SUM(?_count) AS ?count) (MAX(?_name) AS ?name) (SUM(?in_service) AS ?nr_on) (SUM(xsd:double(STR(?sv_p))) AS ?p) WHERE {
  OPTIONAL {
    ?winding ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?_in_service .
  } .
  OPTIONAL {
    ?terminal cim:ACDCTerminal.connected ?_connected .
  }
  OPTIONAL {
    ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?_sv_p .
  }
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    ?winding cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
             cim:TransformerEnd.Terminal ?terminal .
    ?p_transformer cim:IdentifiedObject.name ?_name .
  }
  BIND (COALESCE(?_connected, 1) AS ?connected)
  BIND (IF(COALESCE(?_in_service, ?connected),1,0) AS ?in_service) .
  BIND (COALESCE(?_sv_p, xsd:double(0.0)) AS ?sv_p)
  BIND (1 AS ?_count)
}
GROUP BY ?p_transformer

winding_loss

Question

For each power transformer, what is the per‑branch (per in‑service winding/terminal) active‑power loss based on steady‑state terminal measurements, considering only transformers enabled for network analysis?

TLDR;

Group terminals by their parent transformer, sum terminal active power SvPowerFlow.p across the transformer's windings (proxy for transformer total losses), then divide by the number of in‑service windings to get a per‑branch loss value.

Explanation

What it returns (per transformer ?mrid):

  • ?ploss_2 — computed as SUM(sv_p) / SUM(in_service) where:
    • sv_p is terminal active power (defaults to 0.0 if missing).
    • in_service is 1 if the winding is effectively on, else 0.

Core data paths

  • Start from each terminal that has a connection state:
  ?terminal cim:ACDCTerminal.connected ?connected .
  • Optionally pull steady‑state power and equipment service flag:

    OPTIONAL { ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?_sv_p . }
    OPTIONAL { ?winding  ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?_in_service . }
    
  • Federate into the equipment repository to relate windings/terminals to their transformer and fetch the transformer mRID (and area):

    ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
    SERVICE ?eq_repo {
      ?winding cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
               cim:TransformerEnd.Terminal ?terminal .
      ?p_transformer cim:IdentifiedObject.mRID ?mrid ;
                     cim:Equipment.EquipmentContainer/cim:Substation.Region/
                     cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area .
      OPTIONAL { ?p_transformer SN:Equipment.networkAnalysisEnable ?_network_analysis }
      FILTER (REGEX(?area, '.*'))  # logically redundant; always true
    }
    

Flags and numeric bindings

  • Effective in‑service indicator uses SvStatus.inService if present, else falls back to terminal connectivity:

    BIND (IF(COALESCE(?_in_service, ?connected), 1, 0) AS ?in_service)
    
  • Default missing power to zero and coerce to numeric:

    BIND (COALESCE(?_sv_p, xsd:double(0.0)) AS ?sv_p)
    
  • Only keep transformers enabled for network analysis, defaulting missing flag to true:

    BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
    FILTER (?network_analysis)
    

Aggregation

SELECT ?mrid
       (SUM(xsd:double(STR(?sv_p))) / SUM(?in_service) AS ?ploss_2)
...
GROUP BY ?mrid
  • SUM(?sv_p) across all the transformer's terminals estimates total transformer losses (sum of terminal injections equals losses by convention).
  • Dividing by SUM(?in_service) yields an average per in‑service branch loss (i.e., per winding/terminal).

Notes & caveats

  • The REGEX(?area, '.*') filter is a no‑op; it can be removed unless it’s deliberately used to force ?area binding or exploit an index hint on some engines.
  • Casting via xsd:double(STR(?sv_p)) is defensive; if ?sv_p is already numeric you can simply use SUM(?sv_p).
  • Beware division by zero if all windings are out of service for a transformer (SUM(?in_service) = 0); consider guarding with FILTER(SUM(?in_service) > 0) in a subquery or computing ?ploss_2 after aggregation.
  • Sign conventions for SvPowerFlow.p matter (positive injection vs. consumption). The sum should reflect the chosen convention where the transformer's net terminal sum approximates losses.
  • The mandatory ?terminal cim:ACDCTerminal.connected ?connected triple excludes terminals lacking an explicit connectivity value; if you want to include them as connected by default, make it OPTIONAL and default with COALESCE.

Original SPARQL

# Name: Transformer branches loss
PREFIX cim: <http://iec.ch/TC57/2013/CIM-schema-cim16#>
PREFIX SN: <http://www.statnett.no/CIM-schema-cim15-extension#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?mrid (SUM(xsd:double(STR(?sv_p))) / SUM(?in_service) AS ?ploss_2) WHERE {
  ?terminal cim:ACDCTerminal.connected ?connected .
  OPTIONAL {
    ?terminal ^cim:SvPowerFlow.Terminal/cim:SvPowerFlow.p ?_sv_p .
  }
  OPTIONAL {
    ?winding ^cim:SvStatus.ConductingEquipment/cim:SvStatus.inService ?_in_service
  } .
  ?_eq_subject <http://entsoe.eu/CIM/EquipmentCore/3/1> ?eq_repo .
  SERVICE ?eq_repo {
    # Extract mRID and area for each power transformer
    ?winding cim:PowerTransformerEnd.PowerTransformer ?p_transformer ;
             cim:TransformerEnd.Terminal ?terminal .
    ?p_transformer cim:IdentifiedObject.mRID ?mrid ;
                   cim:Equipment.EquipmentContainer/cim:Substation.Region/cim:SubGeographicalRegion.Region/cim:IdentifiedObject.name ?area .
    FILTER (REGEX(?area,'.*'))
    OPTIONAL {
      ?p_transformer SN:Equipment.networkAnalysisEnable ?_network_analysis
    }
  }
  BIND (IF(COALESCE(?_in_service, ?connected),1,0) AS ?in_service) .
  BIND (COALESCE(?_sv_p, xsd:double(0.0)) AS ?sv_p)
  BIND (COALESCE(?_network_analysis, True) AS ?network_analysis)
  FILTER (?network_analysis)
}
GROUP BY ?mrid