All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.powsybl.cgmes.conversion.SubstationIdMapping Maven / Gradle / Ivy

There is a newer version: 6.6.0
Show newest version
/**
 * Copyright (c) 2017-2018, RTE (http://www.rte-france.com)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

package com.powsybl.cgmes.conversion;

import com.powsybl.cgmes.model.CgmesNames;
import com.powsybl.cgmes.model.CgmesTerminal;
import com.powsybl.commons.PowsyblException;
import com.powsybl.triplestore.api.PropertyBag;
import com.powsybl.triplestore.api.PropertyBags;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

/**
 * CGMES standard: 
* A PowerTransformer is contained in one Substation, but it can connect a Terminal to another different Substation
* A Switch can connect to different voltageLevels *

* IIDM Model:
* Ends of transformers need to be in the same substation
* Ends of switches need to be in the same voltageLevel *

* Solution:
* CGMES substations that are connected by transformers will be mapped to a single IIDM substation
* CGMES voltageLevels that are connected by switches will be mapped to a single IIDM voltageLevel *

* Example:
* We suppose that VL1, VL2, VL3, VL4, VL5, VL6 and VL7 are CGMES voltageLevels,
* Sw23 is a switch connecting voltageLevels VL2 and VL3,
* Sw34 is a switch connecting voltageLevels VL3 and VL4 and
* Sw67 is a switch connecting voltageLevels VL6 and VL7 *

* Steps:
* Fill voltageLevelAdjacency Map
* Two voltageLevels are adjacent if they are connected by a switch
* The voltageLevelAdjacency Map will include the following records
* (VL1, [])
* (VL2, [VL2, VL3])
* (VL3, [VL2, VL3, VL4])
* (VL4, [VL3, VL4])
* (VL5, [])
* (VL6, [VL6, VL7])
* (VL7, [VL6, VL7])
*

* For each non-visited VoltageLevel-key of the voltageLevelAdjacency Map all connected voltageLevels will be calculated
* Two voltageLevels are connected if they are adjacent
* (allConnected method)
* All connected VoltageLevels to VL1 will be [VL1]
* All connected VoltageLevels to VL2 will be [VL2, VL3, VL4]
* All connected VoltageLevels to VL5 will be [VL5]
* All connected VoltageLevels to VL6 will be [VL6, VL7] *

* So the following voltageLevels should be merged
* [VL2, VL3, VL4] and the representative (IIDM voltageLevel) will be VL2
* [VL6, VL7] and the representative (IIDM voltageLevel) will be VL6 *

* And finally previous data is recorded in the voltageLevelMapping Map as
* (For each merged voltageLevel a record (merged voltageLevel, representative voltageLevel) is added)
* (VL3, VL2)
* (VL4, VL2)
* (VL7, VL6)
*

* The voltageLevelMapping Map will be used to assign the IIDM voltageLevel during the conversion process *

* The same algorithm is used to identify the substations that should be merged but:
* Two substations are adjacent if there is a transformer between them.
* The two substations associated with two adjacent voltageLevels, are adjacent if they are different substations. *

* @author Luma Zamarreño * @author José Antonio Marqués */ public class SubstationIdMapping { public SubstationIdMapping(Context context) { this.context = context; this.substationMapping = new HashMap<>(); this.voltageLevelMapping = new HashMap<>(); } public boolean substationIsMapped(String cgmesIdentifier) { String sid = context.namingStrategy().getIidmId(CgmesNames.SUBSTATION, cgmesIdentifier); return substationMapping.containsKey(sid); } public String substationIidm(String cgmesIdentifier) { String sid = context.namingStrategy().getIidmId(CgmesNames.SUBSTATION, cgmesIdentifier); if (substationMapping.containsKey(sid)) { return substationMapping.get(sid); } return sid; } // All the keys for a given value, all the merged substations that have cgmesIdentifier as representative public Set mergedSubstations(String cgmesIdentifier) { String sid = context.namingStrategy().getIidmId(CgmesNames.SUBSTATION, cgmesIdentifier); return substationMapping.entrySet().stream().filter(record -> record.getValue().equals(sid)) .map(Map.Entry::getKey).collect(Collectors.toSet()); } public boolean voltageLevelIsMapped(String cgmesIdentifier) { String vlid = context.namingStrategy().getIidmId(CgmesNames.VOLTAGE_LEVEL, cgmesIdentifier); return voltageLevelMapping.containsKey(vlid); } public String voltageLevelIidm(String cgmesIdentifier) { String vlid = context.namingStrategy().getIidmId(CgmesNames.VOLTAGE_LEVEL, cgmesIdentifier); if (voltageLevelMapping.containsKey(vlid)) { return voltageLevelMapping.get(vlid); } return vlid; } // All the keys for a given value, all the merged voltageLevels that have cgmesIdentifier as representative public Set mergedVoltageLevels(String cgmesIdentifier) { String vlid = context.namingStrategy().getIidmId(CgmesNames.VOLTAGE_LEVEL, cgmesIdentifier); return voltageLevelMapping.entrySet().stream().filter(record -> record.getValue().equals(vlid)) .map(Map.Entry::getKey).collect(Collectors.toSet()); } public void build() { Map> voltageLevelAdjacency = new HashMap<>(); Map> substationAdjacency = new HashMap<>(); buildAdjacency(voltageLevelAdjacency, substationAdjacency); buildVoltageLevel(voltageLevelAdjacency); buildSubstation(substationAdjacency); } private void buildAdjacency(Map> voltageLevelAdjacency, Map> substationAdjacency) { context.cgmes().voltageLevels().forEach(vl -> addVoltageLevel(voltageLevelAdjacency, vl)); context.cgmes().substations().forEach(st -> addSubstation(substationAdjacency, st)); context.cgmes().switches().forEach(sw -> addSwitch(voltageLevelAdjacency, substationAdjacency, sw)); context.cgmes().groupedTransformerEnds().forEach((t, tends) -> addEnds(substationAdjacency, tends)); } private void addVoltageLevel(Map> voltageLevelAdjacency, PropertyBag vl) { String voltageLevelId = vl.getId(CgmesNames.VOLTAGE_LEVEL); String vId = context.namingStrategy().getIidmId(CgmesNames.VOLTAGE_LEVEL, voltageLevelId); voltageLevelAdjacency.put(vId, new HashSet<>()); } private void addSubstation(Map> substationAdjacency, PropertyBag sub) { String substationlId = sub.getId(CgmesNames.SUBSTATION); String subId = context.namingStrategy().getIidmId(CgmesNames.SUBSTATION, substationlId); substationAdjacency.put(subId, new HashSet<>()); } // Two different voltageLevels are adjacent if they are connected by a switch // If the corresponding substations are different they are also adjacent private void addSwitch(Map> voltageLevelAdjacency, Map> substationAdjacency, PropertyBag sw) { CgmesTerminal t1 = context.cgmes().terminal(sw.getId(CgmesNames.TERMINAL + 1)); CgmesTerminal t2 = context.cgmes().terminal(sw.getId(CgmesNames.TERMINAL + 2)); String voltageLevelId1 = context.cgmes().voltageLevel(t1, context.nodeBreaker()); String voltageLevelId2 = context.cgmes().voltageLevel(t2, context.nodeBreaker()); // Null could be received as voltageLevel at the boundary if (voltageLevelId1 == null || voltageLevelId2 == null || voltageLevelId1.equals(voltageLevelId2)) { return; } addAdjacency(voltageLevelAdjacency, voltageLevelId1, voltageLevelId2); String substationId1 = context.cgmes().substation(t1, context.nodeBreaker()); String substationId2 = context.cgmes().substation(t2, context.nodeBreaker()); // Null could be received as substation at the boundary if (substationId1 == null || substationId2 == null || substationId1.equals(substationId2)) { return; } addAdjacency(substationAdjacency, substationId1, substationId2); } // Two different substations are adjacent if they are connected by a transformer private void addEnds(Map> substationAdjacency, PropertyBags tends) { List substationsIds = substationsIds(tends); if (substationsIds.size() <= 1) { return; } String sub0 = substationsIds.get(0); for (int i = 1; i < substationsIds.size(); i++) { String subi = substationsIds.get(i); if (sub0.contentEquals(subi)) { continue; } addAdjacency(substationAdjacency, sub0, subi); } } // Record in the adjacency Map that "id1 is adjacent to id2" and "id2 is adjacent to id1" private static void addAdjacency(Map> adjacency, String id1, String id2) { Set ad1 = adjacency.get(id1); if (ad1 == null) { throw new PowsyblException("Unexpected reference to Substation or voltageLevel " + id1 + ". It has not been defined in CGMES substations / voltageLevels."); } Set ad2 = adjacency.get(id2); if (ad2 == null) { throw new PowsyblException("Unexpected reference to Substation or voltageLevel " + id2 + ". It has not been defined in CGMES substations / voltageLevels."); } ad1.add(id2); ad2.add(id1); } private void buildVoltageLevel(Map> voltageLevelAdjacency) { Set visitedVoltageLevels = new HashSet<>(); for (String vl : voltageLevelAdjacency.keySet()) { if (!visitedVoltageLevels.contains(vl)) { Set vlAds = allConnected(voltageLevelAdjacency, visitedVoltageLevels, vl); String selectedVoltageLevelId = representativeVoltageLevelId(vlAds); recordMergedIds(voltageLevelMapping, vlAds, selectedVoltageLevelId); } } if (!voltageLevelMapping.isEmpty()) { LOG.warn("VoltageLevel id mapping needed for {} voltageLevels: {}", voltageLevelMapping.size(), voltageLevelMapping); } } private void buildSubstation(Map> substationAdjacency) { Set visitedSubstations = new HashSet<>(); for (String sub : substationAdjacency.keySet()) { if (!visitedSubstations.contains(sub)) { Set subAds = allConnected(substationAdjacency, visitedSubstations, sub); String selectedSubstationId = representativeSubstationId(subAds); recordMergedIds(substationMapping, subAds, selectedSubstationId); } } if (!substationMapping.isEmpty()) { LOG.warn("Substation id mapping needed for {} substations: {}", substationMapping.size(), substationMapping); } } // Given an id (substation / voltageLevel) returns all connected ids (substations / voltageLevels) // Two ids are connected if they are adjacent in the adjacency Map private static Set allConnected(Map> adjacency, Set visited, String id) { ArrayList allConnected = new ArrayList<>(); // Insert id in the allConnected list and record it as visited allConnected.add(id); visited.add(id); // Expand, adding in each step all non-visited adjacent ids" int k = 0; while (k < allConnected.size()) { String vl0 = allConnected.get(k); if (adjacency.containsKey(vl0)) { adjacency.get(vl0).forEach(ad -> { if (visited.contains(ad)) { return; } allConnected.add(ad); visited.add(ad); }); } k++; } return new HashSet<>(allConnected); } private static String representativeVoltageLevelId(Collection voltageLevelIds) { return voltageLevelIds.stream() .sorted() .findFirst() .orElseThrow(() -> new IllegalStateException("Unexpected: voltageLevelIds list is empty")); } private String representativeSubstationId(Collection substationIds) { return substationIds.stream() .filter(substationId -> context.config().substationIdsExcludedFromMapping() .stream() .noneMatch(substationId::matches)) .sorted() .findFirst() .orElse(substationIds.iterator().next()); } // For each merged substation a record (merged substation, representative substation) is added // For each merged voltageLevel a record (merged voltageLevel, representative voltageLevel) is added private static void recordMergedIds(Map mapping, Collection mergedIds, String representativeId) { for (String id : mergedIds) { if (!id.equals(representativeId)) { mapping.put(id, representativeId); } } } private List substationsIds(PropertyBags tends) { List substationsIds = new ArrayList<>(); for (PropertyBag end : tends) { CgmesTerminal t = context.cgmes().terminal(end.getId(CgmesNames.TERMINAL)); String node = context.nodeBreaker() ? t.connectivityNode() : t.topologicalNode(); if (node != null && !context.boundary().containsNode(node)) { String sid = context.cgmes().substation(t, context.nodeBreaker()); if (sid != null) { substationsIds.add(context.namingStrategy().getIidmId(CgmesNames.SUBSTATION, sid)); } } } return substationsIds; } private final Context context; private final Map substationMapping; private final Map voltageLevelMapping; private static final Logger LOG = LoggerFactory.getLogger(SubstationIdMapping.class); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy