delayedBoundaryNodes) {
@@ -1107,6 +1042,15 @@ public boolean disconnectNetworkSideOfDanglingLinesIfBoundaryIsDisconnected() {
return disconnectNetworkSideOfDanglingLinesIfBoundaryIsDisconnected;
}
+ public boolean getCreateFictitiousVoltageLevelsForEveryNode() {
+ return createFictitiousVoltageLevelsForEveryNode;
+ }
+
+ public Config setCreateFictitiousVoltageLevelsForEveryNode(boolean b) {
+ createFictitiousVoltageLevelsForEveryNode = b;
+ return this;
+ }
+
private boolean convertBoundary = false;
private boolean createBusbarSectionForEveryConnectivityNode = false;
@@ -1135,6 +1079,7 @@ public boolean disconnectNetworkSideOfDanglingLinesIfBoundaryIsDisconnected() {
private Xfmr3StructuralRatioInterpretationAlternative xfmr3StructuralRatio = Xfmr3StructuralRatioInterpretationAlternative.STAR_BUS_SIDE;
private double missingPermanentLimitPercentage = 100;
+ private boolean createFictitiousVoltageLevelsForEveryNode = true;
}
private final CgmesModel cgmes;
diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/NodeContainerMapping.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/NodeContainerMapping.java
new file mode 100644
index 00000000000..4ddeaf4200f
--- /dev/null
+++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/NodeContainerMapping.java
@@ -0,0 +1,472 @@
+/**
+ * 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+
+package com.powsybl.cgmes.conversion;
+
+import com.powsybl.cgmes.model.CgmesContainer;
+import com.powsybl.cgmes.model.CgmesNames;
+import com.powsybl.cgmes.model.CgmesTerminal;
+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 {@literal }
+ * @author José Antonio Marqués {@literal }
+ */
+
+public class NodeContainerMapping {
+
+ public NodeContainerMapping(Context context) {
+ this.context = context;
+ this.substationMapping = new HashMap<>();
+ this.voltageLevelMapping = new HashMap<>();
+ this.fictitiousVoltageLevels = new HashMap<>();
+ this.referenceVoltageLevels = 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(r -> r.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(r -> r.getValue().equals(vlid))
+ .map(Map.Entry::getKey).collect(Collectors.toSet());
+ }
+
+ public void build() {
+ Map> voltageLevelAdjacency = new HashMap<>();
+ Map> substationAdjacency = new HashMap<>();
+ Map> fictitiousVoltageLevelAdjacency = new HashMap<>();
+
+ buildAdjacency(voltageLevelAdjacency, substationAdjacency, fictitiousVoltageLevelAdjacency);
+ buildVoltageLevelMapping(voltageLevelAdjacency);
+ buildSubstationMapping(substationAdjacency);
+ buildReferenceVoltageLevels(fictitiousVoltageLevelAdjacency);
+
+ // substation containers including connectivityNodes must be connected using switches to other containers (voltageLevel, Line)
+ // then there is no need for a new container
+ fictitiousVoltageLevels.keySet().stream()
+ .filter(this::isSubstationContainer)
+ .map(this::voltageLevelIidm) // map the representative voltageLevel
+ .filter(voltageLevelRepresentative -> fictitiousVoltageLevels.containsKey(voltageLevelRepresentative) && isSubstationContainer(voltageLevelRepresentative)) // representative must be fictitious
+ .findFirst().ifPresent(fictitiousVoltageLevelForSubstationContainer -> {
+ String containerId = getContainerId(fictitiousVoltageLevelForSubstationContainer).orElseThrow();
+ throw new ConversionException("Substation container directly associated with connectivity or topological nodes. It is not expected to create a fictitious voltage level: " + containerId);
+ });
+ }
+
+ private void buildAdjacency(Map> voltageLevelAdjacency, Map> substationAdjacency,
+ Map> fictitiousVoltageLevelAdjacency) {
+ boolean fictitiousVoltageLevelForEveryNode = context.config().getCreateFictitiousVoltageLevelsForEveryNode();
+
+ context.cgmes().switches().forEach(sw -> addAdjacencyThroughSwitch(voltageLevelAdjacency, substationAdjacency, sw, fictitiousVoltageLevelForEveryNode));
+ context.cgmes().groupedTransformerEnds().forEach((t, tends) -> addAdjacencyThroughTransformerEnds(substationAdjacency, tends));
+
+ context.cgmes().acLineSegments().forEach(ac -> addAdjacencyThroughBranch(fictitiousVoltageLevelAdjacency, ac, fictitiousVoltageLevelForEveryNode));
+ context.cgmes().seriesCompensators().forEach(sc -> addAdjacencyThroughBranch(fictitiousVoltageLevelAdjacency, sc, fictitiousVoltageLevelForEveryNode));
+ }
+
+ // Two different voltageLevels are adjacent if they are connected by a switch
+ // If the corresponding substations are different they are also adjacent
+ private void addAdjacencyThroughSwitch(Map> voltageLevelAdjacency,
+ Map> substationAdjacency,
+ PropertyBag sw, boolean fictitiousVoltageLevelForEveryNode) {
+
+ CgmesTerminal t1 = context.cgmes().terminal(sw.getId(CgmesNames.TERMINAL + 1));
+ CgmesTerminal t2 = context.cgmes().terminal(sw.getId(CgmesNames.TERMINAL + 2));
+
+ Optional nodeId1 = context.cgmes().node(t1, context.nodeBreaker());
+ Optional nodeId2 = context.cgmes().node(t2, context.nodeBreaker());
+ if (nodeId1.isPresent() && !context.boundary().containsNode(nodeId1.get()) && nodeId2.isPresent() && !context.boundary().containsNode(nodeId2.get())) {
+ Optional cgmesContainer1 = context.cgmes().nodeContainer(nodeId1.get());
+ Optional cgmesContainer2 = context.cgmes().nodeContainer(nodeId2.get());
+
+ if (cgmesContainer1.isPresent() && cgmesContainer2.isPresent()) {
+ String voltageLevelId1 = findVoltageLevelAndRecordItIfItIsFictitious(cgmesContainer1.get(), nodeId1.get(), fictitiousVoltageLevelForEveryNode);
+ String voltageLevelId2 = findVoltageLevelAndRecordItIfItIsFictitious(cgmesContainer2.get(), nodeId2.get(), fictitiousVoltageLevelForEveryNode);
+
+ addAdjacency(voltageLevelAdjacency, voltageLevelId1, voltageLevelId2);
+ addAdjacency(substationAdjacency, cgmesContainer1.get().substation(), cgmesContainer2.get().substation());
+ }
+ }
+ }
+
+ private String findVoltageLevelAndRecordItIfItIsFictitious(CgmesContainer cgmesContainer, String nodeId, boolean fictitiousVoltageLevelForEveryNode) {
+ if (cgmesContainer.isVoltageLevel()) {
+ return cgmesContainer.voltageLevel();
+ } else {
+ String fictitiousVoltageLevelId = getFictitiousVoltageLevelForContainer(cgmesContainer.id(), nodeId, fictitiousVoltageLevelForEveryNode);
+ recordFictitiousVoltageLevel(fictitiousVoltageLevels, fictitiousVoltageLevelId, cgmesContainer.id(), nodeId);
+ return fictitiousVoltageLevelId;
+ }
+ }
+
+ private static void recordFictitiousVoltageLevel(Map fictitiousVoltageLevels, String fictitiousVoltageLevelId, String cgmesContainerId, String nodeId) {
+ if (fictitiousVoltageLevels.containsKey(fictitiousVoltageLevelId)) {
+ if (fictitiousVoltageLevels.get(fictitiousVoltageLevelId).containerId().equals(cgmesContainerId)) {
+ fictitiousVoltageLevels.get(fictitiousVoltageLevelId).nodeIdSet().add(nodeId);
+ } else {
+ throw new ConversionException("Unexpected cgmesContainerId: " + cgmesContainerId);
+ }
+ } else {
+ Set nodeIdSet = new HashSet<>();
+ nodeIdSet.add(nodeId);
+ fictitiousVoltageLevels.put(fictitiousVoltageLevelId, new ContainerR(cgmesContainerId, nodeIdSet));
+ }
+ }
+
+ // Two different substations are adjacent if they are connected by a transformer
+ private void addAdjacencyThroughTransformerEnds(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++) {
+ addAdjacency(substationAdjacency, sub0, substationsIds.get(i));
+ }
+ }
+
+ private void addAdjacencyThroughBranch(Map> fictitiousVoltageLevelAdjacency,
+ PropertyBag equipment, boolean fictitiousVoltageLevelByNode) {
+
+ CgmesTerminal t1 = context.cgmes().terminal(equipment.getId(CgmesNames.TERMINAL + 1));
+ CgmesTerminal t2 = context.cgmes().terminal(equipment.getId(CgmesNames.TERMINAL + 2));
+
+ Optional nodeId1 = context.cgmes().node(t1, context.nodeBreaker());
+ Optional nodeId2 = context.cgmes().node(t2, context.nodeBreaker());
+ if (nodeId1.isPresent() && !context.boundary().containsNode(nodeId1.get()) && nodeId2.isPresent() && !context.boundary().containsNode(nodeId2.get())) {
+ Optional cgmesContainer1 = context.cgmes().nodeContainer(nodeId1.get());
+ Optional cgmesContainer2 = context.cgmes().nodeContainer(nodeId2.get());
+
+ if (cgmesContainer1.isPresent() && cgmesContainer2.isPresent() && isValidReference(cgmesContainer1.get(), cgmesContainer2.get())) {
+ String voltageLevelId1 = findVoltageLevelAndRecordItIfItIsFictitious(cgmesContainer1.get(), nodeId1.get(), fictitiousVoltageLevelByNode);
+ String voltageLevelId2 = findVoltageLevelAndRecordItIfItIsFictitious(cgmesContainer2.get(), nodeId2.get(), fictitiousVoltageLevelByNode);
+ addAdjacency(fictitiousVoltageLevelAdjacency, voltageLevelId1, voltageLevelId2);
+ }
+ }
+ }
+
+ // one or both of them must be LineContainers
+ private static boolean isValidReference(CgmesContainer cgmesContainer1, CgmesContainer cgmesContainer2) {
+ return !(cgmesContainer1.isVoltageLevel() && cgmesContainer2.isVoltageLevel());
+ }
+
+ private static void addAdjacency(Map> adjacency, String id1, String id2) {
+ if (isValidAdjacency(id1, id2)) {
+ adjacency.computeIfAbsent(id1, k -> new HashSet<>()).add(id2);
+ adjacency.computeIfAbsent(id2, k -> new HashSet<>()).add(id1);
+ }
+ }
+
+ private static boolean isValidAdjacency(String ad1, String ad2) {
+ return ad1 != null && ad2 != null && !ad1.equals(ad2);
+ }
+
+ private void buildVoltageLevelMapping(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()) {
+ CgmesReports.voltageLevelMappingReport(context.getReportNode(), voltageLevelMapping.size(), voltageLevelMapping.toString());
+ LOG.warn("Original {} VoltageLevel container(s) connected by switches have been merged in IIDM. Map of original VoltageLevel to IIDM: {}",
+ voltageLevelMapping.size(), voltageLevelMapping);
+ }
+ }
+
+ private void buildSubstationMapping(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()) {
+ CgmesReports.substationMappingReport(context.getReportNode(), substationMapping.size(), substationMapping.toString());
+ LOG.warn("Original {} Substation container(s) connected by transformers have been merged in IIDM. Map of original Substation to IIDM: {}",
+ substationMapping.size(), substationMapping);
+ }
+ }
+
+ private void buildReferenceVoltageLevels(Map> fictitiousVoltageLevelAdjacency) {
+ // Find a representative for fictitious voltage levels of substation containers
+ voltageLevelMapping.forEach((key, value) -> {
+ if (fictitiousVoltageLevels.containsKey(key) && !fictitiousVoltageLevels.containsKey(value)) {
+ referenceVoltageLevels.put(key, value);
+ }
+ });
+
+ // Find a representative for fictitious voltage levels of line containers
+ Set visited = new HashSet<>();
+ for (String fictitiousVoltageLevelId : fictitiousVoltageLevelAdjacency.keySet()) {
+ if (!visited.contains(fictitiousVoltageLevelId)) {
+ SetvlAds = allConnected(fictitiousVoltageLevelAdjacency, visited, fictitiousVoltageLevelId);
+ referenceVoltageLevel(vlAds).ifPresent(referenceId -> recordReferenceVoltageLevel(vlAds, referenceId));
+ }
+ }
+
+ // All fictitious voltage levels need a reference voltage level
+ fictitiousVoltageLevels.keySet().stream()
+ .filter(fictitiousVoltageLevel -> !referenceVoltageLevels.containsKey(fictitiousVoltageLevel))
+ .findFirst()
+ .ifPresent(fictitiousVoltageLevelIdWithoutReference -> {
+ throw new ConversionException("Fictitious voltage level without reference: " + fictitiousVoltageLevelIdWithoutReference);
+ });
+ }
+
+ private Optional referenceVoltageLevel(Set voltageLevelIds) {
+ return voltageLevelIds.stream()
+ .filter(vl -> !fictitiousVoltageLevels.containsKey(vl))
+ .min(Comparator.naturalOrder());
+ }
+
+ private void recordReferenceVoltageLevel(Set voltageLevelIds, String referenceId) {
+ voltageLevelIds.stream()
+ .filter(fictitiousVoltageLevels::containsKey) // only for the fictitious voltage levels
+ .forEach(vl -> referenceVoltageLevels.put(vl, referenceId));
+ }
+
+ // 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 String representativeVoltageLevelId(Collection voltageLevelIds) {
+ Optional existingVoltageLevelId = voltageLevelIds.stream()
+ .filter(voltageLevelId -> !fictitiousVoltageLevels.containsKey(voltageLevelId))
+ .min(Comparator.naturalOrder());
+ if (existingVoltageLevelId.isPresent()) {
+ return existingVoltageLevelId.get();
+ }
+ Optional fictitiousVoltageLevelIdForLineContainer = voltageLevelIds.stream()
+ .filter(voltageLevelId -> !isSubstationContainer(voltageLevelId)) // Fictitious for LineContainer
+ .min(Comparator.naturalOrder());
+ return fictitiousVoltageLevelIdForLineContainer.orElseGet(() -> voltageLevelIds.stream().min(Comparator.naturalOrder())
+ .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))
+ .min(Comparator.naturalOrder())
+ .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));
+
+ Optional nodeId = context.cgmes().node(t, context.nodeBreaker());
+ if (nodeId.isPresent() && !context.boundary().containsNode(nodeId.get())) {
+ Optional cgmesContainer = context.cgmes().nodeContainer(nodeId.get());
+
+ if (cgmesContainer.isPresent()) {
+ String sid = cgmesContainer.get().substation();
+ if (sid != null) {
+ substationsIds.add(context.namingStrategy().getIidmId(CgmesNames.SUBSTATION, sid));
+ }
+ }
+ }
+ }
+ return substationsIds;
+ }
+
+ // Only iidm voltage levels that are fictitious
+ Set getFictitiousVoltageLevelsForLineContainersToBeCreated() {
+ return fictitiousVoltageLevels.keySet().stream()
+ .filter(fictitiousVoltageLevelId -> !isSubstationContainer(fictitiousVoltageLevelId))
+ .map(this::voltageLevelIidm) // map the representative voltageLevel
+ .filter(fictitiousVoltageLevels::containsKey) // representative must be fictitious
+ .collect(Collectors.toSet());
+ }
+
+ Optional getContainerId(String fictitiousVoltageLevelId) {
+ return fictitiousVoltageLevels.containsKey(fictitiousVoltageLevelId) ? Optional.of(fictitiousVoltageLevels.get(fictitiousVoltageLevelId).containerId()) : Optional.empty();
+ }
+
+ Optional getContainerName(String fictitiousVoltageLevelId) {
+ Optional containerId = getContainerId(fictitiousVoltageLevelId);
+ if (containerId.isPresent()) {
+ CgmesContainer cgmesContainer = context.cgmes().container(containerId.get());
+ return cgmesContainer != null ? Optional.of(cgmesContainer.name()) : Optional.empty();
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ private boolean isSubstationContainer(String fictitiousVoltageLevelId) {
+ Optional containerId = getContainerId(fictitiousVoltageLevelId);
+ if (containerId.isPresent()) {
+ CgmesContainer cgmesContainer = context.cgmes().container(containerId.get());
+ return cgmesContainer != null && cgmesContainer.isSubstation();
+ } else {
+ return false;
+ }
+ }
+
+ Optional getReferenceVoltageLevelId(String fictitiousVoltageLevelId) {
+ if (referenceVoltageLevels.containsKey(fictitiousVoltageLevelId)) {
+ String referenceVoltageLevelId = referenceVoltageLevels.get(fictitiousVoltageLevelId);
+ return Optional.ofNullable(voltageLevelIidm(referenceVoltageLevelId));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public String getFictitiousVoltageLevelForContainer(String containerId, String nodeId) {
+ return getFictitiousVoltageLevelForContainer(containerId, nodeId, context.config().getCreateFictitiousVoltageLevelsForEveryNode());
+ }
+
+ private static String getFictitiousVoltageLevelForContainer(String containerId, String nodeId, boolean fictitiousVoltageLevelForEveryNode) {
+ // ? one voltage level for each node : one voltage level for each container
+ return fictitiousVoltageLevelForEveryNode ? nodeId + "_VL" : containerId + "_VL";
+ }
+
+ private final Context context;
+ private final Map substationMapping;
+ private final Map voltageLevelMapping;
+ private final Map fictitiousVoltageLevels;
+ private final Map referenceVoltageLevels;
+
+ private static final Logger LOG = LoggerFactory.getLogger(NodeContainerMapping.class);
+
+ private record ContainerR(String containerId, Set nodeIdSet) {
+ }
+}
diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/SubstationIdMapping.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/SubstationIdMapping.java
deleted file mode 100644
index e1b46bcc55c..00000000000
--- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/SubstationIdMapping.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/**
- * 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/.
- * SPDX-License-Identifier: 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 {@literal }
- * @author José Antonio Marqués {@literal }
- */
-
-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(r -> r.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(r -> r.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()) {
- CgmesReports.voltageLevelMappingReport(context.getReportNode(), voltageLevelMapping.size(), voltageLevelMapping.toString());
- LOG.warn("Original {} VoltageLevel container(s) connected by switches have been merged in IIDM. Map of original VoltageLevel to IIDM: {}",
- 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()) {
- CgmesReports.substationMappingReport(context.getReportNode(), substationMapping.size(), substationMapping.toString());
- LOG.warn("Original {} Substation container(s) connected by transformers have been merged in IIDM. Map of original Substation to IIDM: {}",
- 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);
-}
diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/AbstractConductingEquipmentConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/AbstractConductingEquipmentConversion.java
index 85ad9983d1d..a2dc767cfe9 100644
--- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/AbstractConductingEquipmentConversion.java
+++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/AbstractConductingEquipmentConversion.java
@@ -12,10 +12,7 @@
import com.powsybl.cgmes.conversion.Conversion;
import com.powsybl.cgmes.conversion.ConversionException;
import com.powsybl.cgmes.extensions.CgmesDanglingLineBoundaryNodeAdder;
-import com.powsybl.cgmes.model.CgmesModelException;
-import com.powsybl.cgmes.model.CgmesNames;
-import com.powsybl.cgmes.model.CgmesTerminal;
-import com.powsybl.cgmes.model.PowerFlow;
+import com.powsybl.cgmes.model.*;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.ThreeWindingsTransformerAdder.LegAdder;
@@ -31,7 +28,7 @@
* @author Luma Zamarreño {@literal }
*
* A ConductingEquipment has at least one Terminal. From the Terminal we
- * get either its ConnectivityNode or its TopologicalNode, depending of
+ * get either its ConnectivityNode or its TopologicalNode, depending on
* the conversion context
*/
public abstract class AbstractConductingEquipmentConversion extends AbstractIdentifiedObjectConversion {
@@ -517,24 +514,30 @@ static class TerminalData {
&& context.boundary().containsNode(nodeId)) {
cgmesVoltageLevelId = Context.boundaryVoltageLevelId(nodeId);
} else {
- // cgmesVoltageLevelId may be null if terminal is contained in a Line
- // (happens in boundaries)
- cgmesVoltageLevelId = context.cgmes().voltageLevel(t, context.nodeBreaker());
+ // If the terminal's node is contained in a Line (happens in boundaries) or in a Substation, a fictitious VoltageLevel is created
+ cgmesVoltageLevelId = findCgmesVoltageLevelIdForContainer(nodeId, context);
}
if (cgmesVoltageLevelId != null) {
String iidmVl = context.namingStrategy().getIidmId("VoltageLevel", cgmesVoltageLevelId);
- iidmVoltageLevelId = context.substationIdMapping().voltageLevelIidm(iidmVl);
+ iidmVoltageLevelId = context.nodeContainerMapping().voltageLevelIidm(iidmVl);
voltageLevel = context.network().getVoltageLevel(iidmVoltageLevelId);
} else {
- // if terminal is contained in a Line Container, a fictitious voltage level is created,
- // its ID is composed by its connectivity node ID + '_VL' sufix
- voltageLevel = context.network().getVoltageLevel(nodeId + "_VL");
- if (voltageLevel != null) {
- iidmVoltageLevelId = nodeId + "_VL";
- } else {
- iidmVoltageLevelId = null;
+ iidmVoltageLevelId = null;
+ voltageLevel = null;
+ }
+ }
+
+ // if nodeId is included in a Line Container, the fictitious voltage level must be considered
+ private static String findCgmesVoltageLevelIdForContainer(String nodeId, Context context) {
+ String cgmesVoltageLevelId = null;
+ Optional cgmesContainer = context.cgmes().nodeContainer(nodeId);
+ if (cgmesContainer.isPresent()) {
+ cgmesVoltageLevelId = cgmesContainer.get().voltageLevel();
+ if (cgmesVoltageLevelId == null) {
+ cgmesVoltageLevelId = context.nodeContainerMapping().getFictitiousVoltageLevelForContainer(cgmesContainer.get().id(), nodeId);
}
}
+ return cgmesVoltageLevelId;
}
}
diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/NodeConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/NodeConversion.java
index 4824d744357..846d04f2628 100644
--- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/NodeConversion.java
+++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/NodeConversion.java
@@ -9,7 +9,6 @@
package com.powsybl.cgmes.conversion.elements;
import com.powsybl.cgmes.conversion.Context;
-import com.powsybl.cgmes.conversion.Conversion;
import com.powsybl.cgmes.conversion.CountryConversion;
import com.powsybl.cgmes.model.CgmesNames;
import com.powsybl.iidm.network.*;
@@ -140,7 +139,7 @@ public void setVoltageAngleNodeBreaker() {
return;
}
// To obtain a bus for which we want to set voltage:
- // If there no Terminal at this IIDM node,
+ // If there is no Terminal at this IIDM node,
// then find from it the first connected node with a Terminal
Terminal t = topo.getOptionalTerminal(iidmNode)
.orElseGet(() -> Networks.getEquivalentTerminal(vl, iidmNode));
@@ -166,16 +165,16 @@ private VoltageLevel voltageLevel() {
if (insideBoundary() && context.config().convertBoundary()) {
return context.network().getVoltageLevel(Context.boundaryVoltageLevelId(this.id));
} else if (!insideBoundary()) {
- String containerId = p.getId("ConnectivityNodeContainer");
+ String containerId = p.getId(CgmesNames.CONNECTIVITY_NODE_CONTAINER);
String cgmesId = context.cgmes().container(containerId).voltageLevel();
if (cgmesId == null) {
// A CGMES Voltage Level can not be obtained from the connectivity node container
// The connectivity node container is a cim:Line, and
// the conversion has created a fictitious voltage level in IIDM
- cgmesId = Conversion.getFictitiousVoltageLevelForNodeInContainer(containerId, this.id);
+ cgmesId = context.nodeContainerMapping().getFictitiousVoltageLevelForContainer(containerId, this.id);
}
String iidm = context.namingStrategy().getIidmId(CgmesNames.VOLTAGE_LEVEL, cgmesId);
- String iidmId = context.substationIdMapping().voltageLevelIidm(iidm);
+ String iidmId = context.nodeContainerMapping().voltageLevelIidm(iidm);
return iidmId != null ? context.network().getVoltageLevel(iidmId) : null;
}
return null;
@@ -183,8 +182,8 @@ private VoltageLevel voltageLevel() {
private void newNode(VoltageLevel vl) {
VoltageLevel.NodeBreakerView nbv = vl.getNodeBreakerView();
- String connectivityNode = id;
- int iidmNode = context.nodeMapping().iidmNodeForConnectivityNode(connectivityNode, vl);
+ // id is the connectivityNode
+ int iidmNode = context.nodeMapping().iidmNodeForConnectivityNode(id, vl);
// Busbar sections are created for every connectivity node to be
// able to easily check the topology calculated by IIDM
@@ -218,14 +217,7 @@ private boolean checkValidVoltageAngle(Bus bus) {
}
boolean valid = valid(v, angle);
if (!valid) {
- Supplier reason = () -> String.format("v = %f, angle = %f. Node %s", v, angle, id);
- Supplier location = () -> bus == null
- ? "No bus"
- : String.format("Bus %s, %sVoltage level %s",
- bus.getId(),
- bus.getVoltageLevel().getSubstation().map(s -> "Substation " + s.getNameOrId() + ", ").orElse(""),
- bus.getVoltageLevel().getNameOrId());
- Supplier message = () -> reason.get() + ". " + location.get();
+ Supplier message = getStringSupplier(bus, v, angle);
context.invalid("SvVoltage", message);
if (bus != null) {
@@ -237,6 +229,17 @@ private boolean checkValidVoltageAngle(Bus bus) {
return valid;
}
+ private Supplier getStringSupplier(Bus bus, double v, double angle) {
+ Supplier reason = () -> String.format("v = %f, angle = %f. Node %s", v, angle, id);
+ Supplier location = () -> bus == null
+ ? "No bus"
+ : String.format("Bus %s, %sVoltage level %s",
+ bus.getId(),
+ bus.getVoltageLevel().getSubstation().map(s -> "Substation " + s.getNameOrId() + ", ").orElse(""),
+ bus.getVoltageLevel().getNameOrId());
+ return () -> reason.get() + ". " + location.get();
+ }
+
private void setVoltageAngle(Bus bus) {
Objects.requireNonNull(bus);
double v = p.asDouble(CgmesNames.VOLTAGE);
@@ -259,7 +262,7 @@ private boolean valid(double v, double angle) {
// and the right values returned.
// Another option could be to keep storing SV values (v=0, angle=0),
// but perform a when a switch is converted,
- // and ensure its both ends have the same values (v,angle).
+ // and ensure that both ends have the same values (v,angle).
// This is what HELM integration layer does when mapping from IIDM to HELM.
boolean valid = v > 0;
diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/SubstationConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/SubstationConversion.java
index 2d1a0d00760..c552543df37 100644
--- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/SubstationConversion.java
+++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/SubstationConversion.java
@@ -29,7 +29,7 @@ public SubstationConversion(PropertyBag s, Context context) {
@Override
public boolean valid() {
// Only create IIDM Substations for CGMES substations that are not mapped to others
- return !context.substationIdMapping().substationIsMapped(id);
+ return !context.nodeContainerMapping().substationIsMapped(id);
}
@Override
@@ -44,7 +44,7 @@ public void convert() {
// After applying naming strategy it is possible that two CGMES substations are mapped
// to the same Network substation, so we should check if corresponding substation has
// already been created
- String iidmSubstationId = context.substationIdMapping().substationIidm(id);
+ String iidmSubstationId = context.nodeContainerMapping().substationIidm(id);
Substation substation = context.network().getSubstation(iidmSubstationId);
if (substation != null) {
throw new IllegalStateException("Substation should be null");
@@ -63,7 +63,7 @@ public void convert() {
private void addAliasesAndProperties(Substation s, String subRegionId, String regionId, String regionName) {
int index = 0;
- for (String mergedSub : context.substationIdMapping().mergedSubstations(s.getId())) {
+ for (String mergedSub : context.nodeContainerMapping().mergedSubstations(s.getId())) {
index++;
s.addAlias(mergedSub, "MergedSubstation" + index, context.config().isEnsureIdAliasUnicity());
}
diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/VoltageLevelConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/VoltageLevelConversion.java
index 167f707eff6..3f9467aa213 100644
--- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/VoltageLevelConversion.java
+++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/VoltageLevelConversion.java
@@ -26,7 +26,7 @@ public class VoltageLevelConversion extends AbstractIdentifiedObjectConversion {
public VoltageLevelConversion(PropertyBag vl, Context context) {
super(CgmesNames.VOLTAGE_LEVEL, vl, context);
cgmesSubstationId = p.getId("Substation");
- iidmSubstationId = context.substationIdMapping().substationIidm(cgmesSubstationId);
+ iidmSubstationId = context.nodeContainerMapping().substationIidm(cgmesSubstationId);
substation = context.network().getSubstation(iidmSubstationId);
}
@@ -45,7 +45,7 @@ public boolean valid() {
iidmSubstationId));
return false;
}
- return !context.substationIdMapping().voltageLevelIsMapped(id);
+ return !context.nodeContainerMapping().voltageLevelIsMapped(id);
}
@Override
@@ -62,7 +62,7 @@ public void convert() {
throw new CgmesModelException(String.format("nominalVoltage not found for %s", bv));
}
- String iidmVoltageLevelId = context.substationIdMapping().voltageLevelIidm(id);
+ String iidmVoltageLevelId = context.nodeContainerMapping().voltageLevelIidm(id);
VoltageLevel voltageLevel = context.network().getVoltageLevel(iidmVoltageLevelId);
if (voltageLevel == null) {
VoltageLevelAdder adder = substation.newVoltageLevel()
@@ -81,7 +81,7 @@ public void convert() {
private void addAliases(VoltageLevel vl) {
int index = 0;
- for (String mergedVl : context.substationIdMapping().mergedVoltageLevels(vl.getId())) {
+ for (String mergedVl : context.nodeContainerMapping().mergedVoltageLevels(vl.getId())) {
index++;
vl.addAlias(mergedVl, "MergedVoltageLevel" + index, context.config().isEnsureIdAliasUnicity());
}
diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ConversionUtil.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ConversionUtil.java
index f326c005bb2..449e7f76f20 100644
--- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ConversionUtil.java
+++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ConversionUtil.java
@@ -7,12 +7,15 @@
*/
package com.powsybl.cgmes.conversion.test;
+import com.powsybl.cgmes.conversion.CgmesExport;
import com.powsybl.cgmes.conversion.CgmesImportPostProcessor;
import com.powsybl.cgmes.conversion.Conversion;
import com.powsybl.cgmes.model.CgmesModel;
import com.powsybl.cgmes.model.CgmesModelFactory;
import com.powsybl.cgmes.model.GridModelReference;
import com.powsybl.commons.datasource.ReadOnlyDataSource;
+import com.powsybl.commons.datasource.ResourceDataSource;
+import com.powsybl.commons.datasource.ResourceSet;
import com.powsybl.iidm.network.Network;
import com.powsybl.triplestore.api.TripleStoreFactory;
@@ -25,8 +28,9 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* @author Geoffroy Jamgotchian {@literal }
@@ -84,4 +88,40 @@ public static boolean xmlContains(InputStream is, String clazz, String ns, Strin
}
return false;
}
+
+ public static Network readCgmesResources(String dir, String... files) {
+ return readCgmesResources(new Properties(), dir, files);
+ }
+
+ public static Network readCgmesResources(Properties properties, String dir, String... files) {
+ ReadOnlyDataSource ds = new ResourceDataSource("CGMES input file(s)", new ResourceSet(dir, files));
+ return Network.read(ds, properties);
+ }
+
+ public static String writeCgmesProfile(Network network, String profile, Path outDir) throws IOException {
+ return writeCgmesProfile(network, profile, outDir, new Properties());
+ }
+
+ public static String writeCgmesProfile(Network network, String profile, Path outDir, Properties properties) throws IOException {
+ properties.put(CgmesExport.PROFILES, List.of(profile));
+ network.write("CGMES", properties, outDir.resolve("CgmesExport"));
+ return Files.readString(outDir.resolve("CgmesExport_" + profile + ".xml"));
+ }
+
+ public static String getFirstMatch(String text, Pattern pattern) {
+ Matcher matcher = pattern.matcher(text);
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ return null;
+ }
+
+ public static Set getUniqueMatches(String text, Pattern pattern) {
+ Set matches = new HashSet<>();
+ Matcher matcher = pattern.matcher(text);
+ while (matcher.find()) {
+ matches.add(matcher.group(1));
+ }
+ return matches;
+ }
}
diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/JoinVoltageLevelTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/JoinVoltageLevelTest.java
deleted file mode 100644
index 12401530e8d..00000000000
--- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/JoinVoltageLevelTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
- * Copyright (c) 2020, 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/.
- * SPDX-License-Identifier: MPL-2.0
- */
-
-package com.powsybl.cgmes.conversion.test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.IOException;
-
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.powsybl.cgmes.conformity.CgmesConformity1ModifiedCatalog;
-import com.powsybl.cgmes.conversion.Conversion;
-import com.powsybl.cgmes.model.GridModelReference;
-import com.powsybl.iidm.network.TwoSides;
-import com.powsybl.iidm.network.*;
-
-/**
- * @author Luma Zamarreño {@literal }
- * @author José Antonio Marqués {@literal }
- */
-class JoinVoltageLevelTest {
-
- @Test
- void smallNodeBreakerJoinVoltageLevelSwitch() throws IOException {
- Conversion.Config config = new Conversion.Config();
- Network n = networkModel(CgmesConformity1ModifiedCatalog.miniNodeBreakerJoinVoltageLevelSwitch(), config);
-
- Switch sw = n.getSwitch("5e9f0079-647e-46da-b0ee-f5f24e127602");
- VoltageLevel voltageLevel = sw.getVoltageLevel();
- Substation substation = voltageLevel.getSubstation().orElse(null);
-
- boolean ok = compareVoltageLevelSubstation("d6056127-34f1-43a9-b029-23fddb913bd5", "a43d15db-44a6-4fda-a525-2402ff43226f", substation.getId(), voltageLevel.getId());
- assertTrue(ok);
-
- VoltageLevel voltageLevelIidm = n.getVoltageLevel("a43d15db-44a6-4fda-a525-2402ff43226f");
- assertEquals("a43d15eb-44a6-4fda-a525-2402ff43226f", voltageLevelIidm.getAliasFromType("MergedVoltageLevel1").get());
- }
-
- @Test
- void miniNodeBreakerSwitchBetweenVoltageLevelsOpen() throws IOException {
- Conversion.Config config = new Conversion.Config();
- Network n = networkModel(CgmesConformity1ModifiedCatalog.miniNodeBreakerSwitchBetweenVoltageLevelsOpen(), config);
-
- Switch sw = n.getSwitch("5e9f0079-647e-46da-b0ee-f5f24e127602");
- assertNotNull(sw);
-
- boolean isOpen = sw.isOpen();
- assertTrue(isOpen);
-
- VoltageLevel voltageLevel = sw.getVoltageLevel();
- Substation substation = voltageLevel.getSubstation().orElse(null);
-
- boolean ok = compareVoltageLevelSubstation("d6056127-34f1-43a9-b029-23fddb913bd5", "a43d15db-44a6-4fda-a525-2402ff43226f", substation.getId(), voltageLevel.getId());
- assertTrue(ok);
- }
-
- @Test
- void smallNodeBreakerJoinVoltageLevelTx() throws IOException {
- Conversion.Config config = new Conversion.Config();
- Network n = networkModel(CgmesConformity1ModifiedCatalog.miniNodeBreakerJoinVoltageLevelTx(), config);
-
- TwoWindingsTransformer t2x = n.getTwoWindingsTransformer("ceb5d06a-a7ff-4102-a620-7f3ea5fb4a51");
- Terminal t1 = t2x.getTerminal(TwoSides.ONE);
- VoltageLevel voltageLevel1 = t1.getVoltageLevel();
- Terminal t2 = t2x.getTerminal(TwoSides.TWO);
- VoltageLevel voltageLevel2 = t2.getVoltageLevel();
-
- Substation substation = t2x.getSubstation().orElse(null);
-
- boolean ok = compareVoltageLevelSubstation("d6056127-34f1-43a9-b029-23fddb913bd5",
- "a43d15db-44a6-4fda-a525-2402ff43226f", "0d68ac81-124d-4d21-afa8-6c503feef5b8", substation.getId(),
- voltageLevel1.getId(), voltageLevel2.getId());
- assertTrue(ok);
-
- Substation substationIidm = n.getSubstation("d6056127-34f1-43a9-b029-23fddb913bd5");
- assertEquals("d6056137-34f1-43a9-b029-23fddb913bd5", substationIidm.getAliasFromType("MergedSubstation1").get());
- }
-
- private static Network networkModel(GridModelReference testGridModel, Conversion.Config config) throws IOException {
- config.setConvertSvInjections(true);
- return ConversionUtil.networkModel(testGridModel, config);
- }
-
- private static boolean compareVoltageLevelSubstation(String expectedSubstationId, String expectedVoltageLevelId,
- String actualSubstationId, String actualVoltageLevelId) {
- boolean ok = true;
- if (!expectedSubstationId.equals(actualSubstationId)) {
- ok = false;
- }
- if (!expectedVoltageLevelId.equals(actualVoltageLevelId)) {
- ok = false;
- }
- if (!ok) {
- LOG.info("Substation expected: {} actual {}", expectedSubstationId, actualSubstationId);
- LOG.info("VoltageLevel expected: {} actual {}", expectedVoltageLevelId, actualVoltageLevelId);
- }
- return ok;
- }
-
- private static boolean compareVoltageLevelSubstation(String expectedSubstationId, String expectedVoltageLevelId1,
- String expectedVoltageLevelId2, String actualSubstationId, String actualVoltageLevelId1,
- String actualVoltageLevelId2) {
- boolean ok = true;
- if (!expectedSubstationId.equals(actualSubstationId)) {
- ok = false;
- }
- if (!expectedVoltageLevelId1.equals(actualVoltageLevelId1)) {
- ok = false;
- }
- if (!expectedVoltageLevelId2.equals(actualVoltageLevelId2)) {
- ok = false;
- }
- if (!ok) {
- LOG.info("Substation expected: {} actual {}", expectedSubstationId, actualSubstationId);
- LOG.info("VoltageLevel1 expected: {} actual {}", expectedVoltageLevelId1, actualVoltageLevelId1);
- LOG.info("VoltageLevel2 expected: {} actual {}", expectedVoltageLevelId2, actualVoltageLevelId2);
- }
- return ok;
- }
-
- private static final Logger LOG = LoggerFactory.getLogger(JoinVoltageLevelTest.class);
-}
diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/LineContainersTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/LineContainersTest.java
deleted file mode 100644
index a9effa5c612..00000000000
--- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/LineContainersTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright (c) 2024, 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/.
- * SPDX-License-Identifier: MPL-2.0
- */
-
-package com.powsybl.cgmes.conversion.test;
-
-import com.powsybl.iidm.network.*;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-/**
- * @author Luma Zamarreño {@literal }
- */
-
-class LineContainersTest {
-
- @Test
- void testLineContainerWithOneACLSnotAssociated() {
- // This unit test reproduces a configuration observed in public data from a European TSO (NG)
- // where three ACLSs lie on a common connectivity node inside a cim:Line container
- // Two of the ACLSs have an association with the cim:Line container
- // The other does not any equipment container
-
- // A fictitious voltage level is created to hold the connectivity node inside the cim:Line container,
- // All three ACLSs must be imported
- // And they have to share a bus
-
- Network network = Network.read("line_container_3acls_EQ.xml", getClass().getResourceAsStream("/issues/line_container_3acls_EQ.xml"));
- assertNotNull(network);
-
- // Check all three ACLSs have been imported
- Line l1 = network.getLine("ACLS1-without-Line-Container");
- Line l2 = network.getLine("ACLS2");
- Line l3 = network.getLine("ACLS3");
- assertNotNull(l1);
- assertNotNull(l2);
- assertNotNull(l3);
-
- VoltageLevel vlNodeInsideLineContainer = l1.getTerminal2().getVoltageLevel();
- // Check that the fictitious voltage level created has a reference to the original Line container
- assertEquals("LineContainer", vlNodeInsideLineContainer.getProperty("CGMES.LineContainerId"));
- assertEquals("Line Container", vlNodeInsideLineContainer.getNameOrId());
- // Check that the only bus in the fictitious voltage level has a terminal for each line
- assertEquals(1, vlNodeInsideLineContainer.getBusBreakerView().getBusCount());
- Bus tbus = vlNodeInsideLineContainer.getBusBreakerView().getBuses().iterator().next();
- assertEquals(3, tbus.getConnectedTerminalCount());
- assertEquals(List.of("ACLS1-without-Line-Container", "ACLS2", "ACLS3"),
- tbus.getConnectedTerminalStream().map(Terminal::getConnectable).map(Connectable::getId).sorted().toList());
- }
-}
diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/NodeContainerMappingTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/NodeContainerMappingTest.java
new file mode 100644
index 00000000000..4563ba35e85
--- /dev/null
+++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/NodeContainerMappingTest.java
@@ -0,0 +1,146 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+
+package com.powsybl.cgmes.conversion.test;
+
+import com.powsybl.cgmes.conversion.CgmesImport;
+import com.powsybl.commons.test.AbstractSerDeTest;
+import com.powsybl.iidm.network.*;
+import org.junit.jupiter.api.Test;
+
+import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.powsybl.cgmes.conversion.test.ConversionUtil.readCgmesResources;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * @author Romain Courtier {@literal }
+ */
+
+class NodeContainerMappingTest extends AbstractSerDeTest {
+
+ private static final String DIR = "/issues/node-containers/";
+
+ @Test
+ void nodeContainersConnectedBySwitchesTest() {
+ // CGMES network:
+ // Switch SW_AB connects nodes in VoltageLevels VL_1A, VL_1B of the same Substation ST_1.
+ // Switch SW_12 connects nodes in VoltageLevels VL_1B, VL_2 of Substations ST_1, ST_2.
+ // Switch SW_23 connects node in VoltageLevel VL_2 of Substation ST_2 with node in Substation ST_3.
+ // Switch SW_3L connects node in Substation ST_3 with node in Line LN_L.
+ // There is a BusbarSection attached to the node in VL_2 and a Load attached to the node in ST_3
+ // IIDM network:
+ // Ends of switches must be in the same VoltageLevel. So none of the situation above is allowed.
+ // In such cases, a representative Substation and VoltageLevel are determined and gather all the elements.
+ Network network = readCgmesResources(DIR, "containers_connected_by_switches.xml");
+ assertNotNull(network);
+
+ // All Substations and VoltageLevels are adjacent. Only 1 of each is kept.
+ assertEquals(1, network.getSubstationCount());
+ assertEquals(1, network.getVoltageLevelCount());
+
+ // Representatives are ST_1 and VL_1A (min in alphabetical order).
+ assertNotNull(network.getSubstation("ST_1"));
+ assertNotNull(network.getVoltageLevel("VL_1A"));
+
+ // Merged Substations and VoltageLevels are saved in aliases.
+ assertEquals(Set.of("ST_2", "ST_3"), network.getSubstation("ST_1").getAliases());
+ assertEquals(Set.of("VL_1B", "VL_2", "CN_3_VL", "CN_L_VL"), network.getVoltageLevel("VL_1A").getAliases());
+
+ // Verify that the Load has been imported and has a Bus
+ assertNotNull(network.getLoad("LD"));
+ assertNotNull(network.getLoad("LD").getTerminal().getBusView().getBus());
+ }
+
+ @Test
+ void nodeOfTJunctionInLineContainerTest() {
+ // CGMES network:
+ // 3 VoltageLevels VL_1, VL_2, VL_3 are connected together through line segments in a T-junction.
+ // ACLineSegments ACL_1A, ACL_2A are in Line LN_A, but ACL_3A has no EquipmentContainer.
+ // Node CN_A of T-junction is directly in Line Container LN_A.
+ // IIDM network:
+ // Nodes must be within a VoltageLevel.
+ // If that is not the case and no real representative can be found, a fictitious one is created.
+ Network network = readCgmesResources(DIR, "line_with_t-junction.xml");
+ assertNotNull(network);
+
+ // There is no real representative VoltageLevel for node CN_A, so a fictitious one has been created.
+ VoltageLevel innerVL = network.getVoltageLevel("CN_A_VL");
+ assertNotNull(innerVL);
+ assertNull(innerVL.getNullableSubstation());
+ assertEquals("LN_A", innerVL.getProperty("CGMES.LineContainerId"));
+
+ // All line segments have been imported (even if not associated to an EquipmentContainer) and connected together
+ assertEquals(3, network.getLineCount());
+ assertEquals(3, innerVL.getBusBreakerView().getBuses().iterator().next().getConnectedTerminalCount());
+ }
+
+ @Test
+ void chainedLineSegmentsTest() {
+ // CGMES network:
+ // 2 Substations ST_1, ST_2 connected by 3 ACLineSegments in a row: ACL_1A, ACL_AB, ACL_B2.
+ // The 3 ACLineSegments are in the Line container LN_12.
+ // Extremity nodes CN_A and CN_B of ACL_AB are in LN_12, nodes CN_1, CN_2 are in VL_1, VL_2 of ST_1, ST_2.
+ // IIDM network:
+ // Nodes must be within a VoltageLevel. In case of multiple nodes in the same non-VoltageLevel container,
+ // a parameter allows to create a fictitious VoltageLevel for every node or for that container
+ Properties importParams = new Properties();
+ importParams.put(CgmesImport.CREATE_FICTITIOUS_VOLTAGE_LEVEL_FOR_EVERY_NODE, "false");
+ Network network = readCgmesResources(importParams, DIR, "chained_line_segments.xml");
+ assertNotNull(network);
+ assertEquals(Set.of("VL_1", "VL_2", "LN_12_VL"),
+ network.getVoltageLevelStream().map(Identifiable::getId).collect(Collectors.toSet()));
+
+ importParams.put(CgmesImport.CREATE_FICTITIOUS_VOLTAGE_LEVEL_FOR_EVERY_NODE, "true");
+ network = readCgmesResources(importParams, DIR, "chained_line_segments.xml");
+ assertNotNull(network);
+ assertEquals(Set.of("VL_1", "VL_2", "CN_A_VL", "CN_B_VL"),
+ network.getVoltageLevelStream().map(Identifiable::getId).collect(Collectors.toSet()));
+ }
+
+ @Test
+ void substationsConnectedByTransformerTest() {
+ // CGMES network:
+ // 3-winding transformer connects nodes in VoltageLevels VL_1, VL_2, VL_3 of Substations ST_1, ST_2, ST_3.
+ // IIDM network:
+ // Ends of transformers need to be in the same Substation. So the situation above is not allowed.
+ // In such a case, a representative Substation is determined and gathers all the elements.
+ Network network = readCgmesResources(DIR, "substations_connected_by_transformer.xml");
+ assertNotNull(network);
+
+ // All Substations are adjacent, only 1 is kept.
+ assertEquals(1, network.getSubstationCount());
+
+ // Representative is ST_1 (min in alphabetical order).
+ assertNotNull(network.getSubstation("ST_1"));
+
+ // Merged Substations are saved in aliases.
+ assertEquals(Set.of("ST_2", "ST_3"), network.getSubstation("ST_1").getAliases());
+ }
+
+ @Test
+ void voltageLevelsConnectedByOpenSwitchTest() {
+ // CGMES network:
+ // VoltageLevel VL_1 and VL_2 in Substation ST are connected by Switch SW_12 which is open in SSH.
+ // IIDM network:
+ // Ends of switches must be in the same VoltageLevel regardless the switch status in SSH.
+
+ Network network = readCgmesResources(DIR,
+ "voltage_levels_connected_by_open_switch_EQ.xml", "voltage_levels_connected_by_open_switch_SSH.xml");
+ assertNotNull(network);
+
+ // The Switch is indeed open.
+ assertTrue(network.getSwitch("SW_12").isOpen());
+
+ // Still there is only one VoltageLevel.
+ assertEquals(1, network.getVoltageLevelCount());
+ assertNotNull(network.getVoltageLevel("VL_1"));
+ }
+}
diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/OperationalLimitsGroupTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/OperationalLimitsGroupTest.java
index 21b6a67f6e9..e778289b383 100644
--- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/OperationalLimitsGroupTest.java
+++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/OperationalLimitsGroupTest.java
@@ -16,9 +16,9 @@
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static com.powsybl.cgmes.conversion.test.ConversionUtil.getUniqueMatches;
import static org.junit.jupiter.api.Assertions.*;
/**
@@ -65,10 +65,10 @@ void exportSelectedLimitsGroupTest() throws IOException {
String exportSelectedLimitsGroupXml = Files.readString(tmpDir.resolve("ExportSelectedLimitsGroup_EQ.xml"));
// There is 1 set on side 1 which is selected, and there are 2 sets on side 2 but none of them is selected
- assertEquals(1, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_SET).size());
- assertEquals(3, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size());
- assertEquals(0, getOccurrences(exportSelectedLimitsGroupXml, ACTIVE_POWER_LIMIT).size());
- assertEquals(3, getOccurrences(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size());
+ assertEquals(1, getUniqueMatches(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_SET).size());
+ assertEquals(3, getUniqueMatches(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size());
+ assertEquals(0, getUniqueMatches(exportSelectedLimitsGroupXml, ACTIVE_POWER_LIMIT).size());
+ assertEquals(3, getUniqueMatches(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size());
// Manually select one of the limits group on side 2 and export again
Line line = network.getLine("Line");
@@ -77,10 +77,10 @@ void exportSelectedLimitsGroupTest() throws IOException {
exportSelectedLimitsGroupXml = Files.readString(tmpDir.resolve("ExportSelectedLimitsGroup_EQ.xml"));
// That makes 1 set selected on each side = 2 in total
- assertEquals(2, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_SET).size());
- assertEquals(3, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size());
- assertEquals(0, getOccurrences(exportSelectedLimitsGroupXml, ACTIVE_POWER_LIMIT).size());
- assertEquals(6, getOccurrences(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size());
+ assertEquals(2, getUniqueMatches(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_SET).size());
+ assertEquals(3, getUniqueMatches(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size());
+ assertEquals(0, getUniqueMatches(exportSelectedLimitsGroupXml, ACTIVE_POWER_LIMIT).size());
+ assertEquals(6, getUniqueMatches(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size());
}
@Test
@@ -95,19 +95,10 @@ void exportAllLimitsGroupTest() throws IOException {
String exportAllLimitsGroupXml = Files.readString(tmpDir.resolve("ExportAllLimitsGroup_EQ.xml"));
// All 3 OperationalLimitsGroup are exported, even though only 2 are selected
- assertEquals(3, getOccurrences(exportAllLimitsGroupXml, OPERATIONAL_LIMIT_SET).size());
- assertEquals(3, getOccurrences(exportAllLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size());
- assertEquals(3, getOccurrences(exportAllLimitsGroupXml, ACTIVE_POWER_LIMIT).size());
- assertEquals(9, getOccurrences(exportAllLimitsGroupXml, CURRENT_LIMIT).size());
- }
-
- private Set getOccurrences(String xml, Pattern pattern) {
- Set matches = new HashSet<>();
- Matcher matcher = pattern.matcher(xml);
- while (matcher.find()) {
- matches.add(matcher.group(1));
- }
- return matches;
+ assertEquals(3, getUniqueMatches(exportAllLimitsGroupXml, OPERATIONAL_LIMIT_SET).size());
+ assertEquals(3, getUniqueMatches(exportAllLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size());
+ assertEquals(3, getUniqueMatches(exportAllLimitsGroupXml, ACTIVE_POWER_LIMIT).size());
+ assertEquals(9, getUniqueMatches(exportAllLimitsGroupXml, CURRENT_LIMIT).size());
}
}
diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/modified/CgmesConformity1ModifiedConversionTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/modified/CgmesConformity1ModifiedConversionTest.java
index ce19719bc92..b9001de9f75 100644
--- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/modified/CgmesConformity1ModifiedConversionTest.java
+++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/modified/CgmesConformity1ModifiedConversionTest.java
@@ -836,21 +836,6 @@ void miniNodeBreakerLoadBreakSwitch() {
assertEquals(SwitchKind.LOAD_BREAK_SWITCH, sw.getKind());
}
- @Test
- void miniNodeBreakerCimLine() {
- Network network = new CgmesImport()
- .importData(CgmesConformity1ModifiedCatalog.miniNodeBreakerCimLine().dataSource(),
- NetworkFactory.findDefault(), importParams);
-
- VoltageLevel vl = network.getVoltageLevel("d3de846d-5271-465e-8558-3e736fa120c4_2_VL");
- assertNotNull(vl);
- assertNull(vl.getNullableSubstation());
-
- vl = network.getVoltageLevel("e2f8de8c-3191-4676-9ee7-f920e46f9085_2_VL");
- assertNotNull(vl);
- assertNull(vl.getNullableSubstation());
- }
-
@Test
void miniNodeBreakerProtectedSwitch() {
Network network = new CgmesImport()
@@ -863,19 +848,6 @@ void miniNodeBreakerProtectedSwitch() {
assertEquals(SwitchKind.BREAKER, sw.getKind());
}
- @Test
- void miniNodeBreakerSubstationNode() {
- Network network = new CgmesImport()
- .importData(CgmesConformity1ModifiedCatalog.miniNodeBreakerSubstationNode().dataSource(),
- NetworkFactory.findDefault(), importParams);
- assertNotNull(network); // Check it doesn't fail when a connectivity node is in substation
- // Check that the test load is connected to a proper bus in the bus view
- Load testLoad = network.getLoad("TEST_LOAD");
- assertNotNull(testLoad);
- Bus testBus = testLoad.getTerminal().getBusView().getBus();
- assertNotNull(testBus);
- }
-
@Test
void miniNodeBreakerMissingSubstationRegion() {
// Check that we fail with a powsybl exception instead of a NPE
diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java
index f5e9f4c5977..18b05913d20 100644
--- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java
+++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java
@@ -446,15 +446,17 @@ void testFromIidmDanglingLineNodeBreakerNoBoundaries() throws IOException {
@Test
void testLineContainersNotInBoundaries() throws IOException {
- ReadOnlyDataSource ds = CgmesConformity1ModifiedCatalog.miniNodeBreakerCimLine().dataSource();
- Network network = Network.read(CgmesConformity1ModifiedCatalog.miniNodeBreakerCimLine().dataSource(), importParams);
+ ReadOnlyDataSource ds = new ResourceDataSource("Node of T-junction in line container",
+ new ResourceSet("/issues/node-containers/", "line_with_t-junction.xml"));
+ Network network = Network.read(ds, importParams);
String exportFolder = "/test-line-containers-not-in-boundaries";
try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) {
// Export to CGMES and add boundary EQ for reimport
Path tmpDir = Files.createDirectory(fs.getPath(exportFolder));
String baseName = "testLineContainersNotInBoundaries";
- ReadOnlyDataSource exportedCgmes = exportAndAddBoundaries(network, tmpDir, baseName, ds);
+ network.write("CGMES", null, tmpDir.resolve(baseName));
+ ReadOnlyDataSource exportedCgmes = new GenericReadOnlyDataSource(tmpDir, baseName);
// Check that the exported CGMES model contains a fictitious substation
CgmesModel cgmes = CgmesModelFactory.create(exportedCgmes, TripleStoreFactory.defaultImplementation());
diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java
index 26f2917527d..e3d30b1021b 100644
--- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java
+++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java
@@ -15,7 +15,6 @@
import com.powsybl.cgmes.model.CgmesMetadataModel;
import com.powsybl.cgmes.model.CgmesNamespace;
import com.powsybl.cgmes.model.CgmesSubset;
-import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.datasource.DataSource;
import com.powsybl.commons.datasource.DirectoryDataSource;
import com.powsybl.commons.datasource.MemDataSource;
@@ -35,9 +34,9 @@
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.util.*;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static com.powsybl.cgmes.conversion.test.ConversionUtil.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -80,8 +79,8 @@ void testIgmExportNoModelsNoPropertiesVersion() throws IOException {
String exportedBeSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml"));
// There is no version number for original models, so if exported as IGM they would have version equals to 1
- assertEquals("1", getFirstOccurrence(exportedBeSshXml, REGEX_VERSION));
- assertEquals("1", getFirstOccurrence(exportedBeSvXml, REGEX_VERSION));
+ assertEquals("1", getFirstMatch(exportedBeSshXml, REGEX_VERSION));
+ assertEquals("1", getFirstMatch(exportedBeSvXml, REGEX_VERSION));
}
@Test
@@ -99,9 +98,9 @@ void testCgmExportWithModelsVersion() throws IOException {
String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml"));
// Version number should be increased from original models and be the same for all instance files
- assertEquals("1", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION));
- assertEquals("1", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION));
- assertEquals("1", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION));
+ assertEquals("1", getFirstMatch(updatedBeSshXml, REGEX_VERSION));
+ assertEquals("1", getFirstMatch(updatedNlSshXml, REGEX_VERSION));
+ assertEquals("1", getFirstMatch(updatedCgmSvXml, REGEX_VERSION));
}
@Test
@@ -119,20 +118,20 @@ void testCgmExportNoModelsNoProperties() throws IOException {
String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml"));
// Scenario time should be the same for all models
- assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME));
+ assertEquals("2021-02-03T04:30:00Z", getFirstMatch(updatedBeSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2021-02-03T04:30:00Z", getFirstMatch(updatedNlSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2021-02-03T04:30:00Z", getFirstMatch(updatedCgmSvXml, REGEX_SCENARIO_TIME));
// Description should be the default one
- assertEquals("SSH Model", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION));
- assertEquals("SSH Model", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION));
- assertEquals("SV Model", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION));
+ assertEquals("SSH Model", getFirstMatch(updatedBeSshXml, REGEX_DESCRIPTION));
+ assertEquals("SSH Model", getFirstMatch(updatedNlSshXml, REGEX_DESCRIPTION));
+ assertEquals("SV Model", getFirstMatch(updatedCgmSvXml, REGEX_DESCRIPTION));
// There is no version number for original models, so if exported as IGM they would have version equals to 1
// Version number for updated models is increased by 1, so it equals to 2 in the end
- assertEquals("2", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION));
- assertEquals("2", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION));
- assertEquals("2", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION));
+ assertEquals("2", getFirstMatch(updatedBeSshXml, REGEX_VERSION));
+ assertEquals("2", getFirstMatch(updatedNlSshXml, REGEX_VERSION));
+ assertEquals("2", getFirstMatch(updatedCgmSvXml, REGEX_VERSION));
// The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP
// Here the version number part of the id 1 for original models and 2 for updated ones
@@ -143,27 +142,27 @@ void testCgmExportNoModelsNoProperties() throws IOException {
String originalBeTpBdId = "urn:uuid:Network_BE_N_TOPOLOGY_BOUNDARY_2021-02-03T04:30:00Z_1_1D__FM";
String originalNlTpBdId = "urn:uuid:Network_NL_N_TOPOLOGY_BOUNDARY_2021-02-03T04:30:00Z_1_1D__FM";
Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, originalBeTpBdId, originalNlTpBdId);
- assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON));
+ assertEquals(expectedDependencies, getUniqueMatches(updatedCgmSvXml, REGEX_DEPENDENT_ON));
// Each updated IGM SSH should supersede the original one and depend on the original EQ
String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM";
String originalBeEqId = "urn:uuid:Network_BE_N_EQUIPMENT_2021-02-03T04:30:00Z_1_1D__FM";
String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM";
String originalNlEqId = "urn:uuid:Network_NL_N_EQUIPMENT_2021-02-03T04:30:00Z_1_1D__FM";
- assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES));
- assertEquals(originalBeEqId, getFirstOccurrence(updatedBeSshXml, REGEX_DEPENDENT_ON));
- assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES));
- assertEquals(originalNlEqId, getFirstOccurrence(updatedNlSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(originalBeSshId, getFirstMatch(updatedBeSshXml, REGEX_SUPERSEDES));
+ assertEquals(originalBeEqId, getFirstMatch(updatedBeSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(originalNlSshId, getFirstMatch(updatedNlSshXml, REGEX_SUPERSEDES));
+ assertEquals(originalNlEqId, getFirstMatch(updatedNlSshXml, REGEX_DEPENDENT_ON));
// Profiles should be consistent with the instance files
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE));
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE));
- assertEquals("http://entsoe.eu/CIM/StateVariables/4/1", getFirstOccurrence(updatedCgmSvXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedBeSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedNlSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/StateVariables/4/1", getFirstMatch(updatedCgmSvXml, REGEX_PROFILE));
// All MAS should be equal to the default one since none has been provided
- assertEquals("powsybl.org", getFirstOccurrence(updatedBeSshXml, REGEX_MAS));
- assertEquals("powsybl.org", getFirstOccurrence(updatedNlSshXml, REGEX_MAS));
- assertEquals("powsybl.org", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS));
+ assertEquals("powsybl.org", getFirstMatch(updatedBeSshXml, REGEX_MAS));
+ assertEquals("powsybl.org", getFirstMatch(updatedNlSshXml, REGEX_MAS));
+ assertEquals("powsybl.org", getFirstMatch(updatedCgmSvXml, REGEX_MAS));
}
@Test
@@ -182,19 +181,19 @@ void testCgmExportWithModelsForSubnetworks() throws IOException {
String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml"));
// Scenario time should be the same for all models
- assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME));
+ assertEquals("2021-02-03T04:30:00Z", getFirstMatch(updatedBeSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2021-02-03T04:30:00Z", getFirstMatch(updatedNlSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2021-02-03T04:30:00Z", getFirstMatch(updatedCgmSvXml, REGEX_SCENARIO_TIME));
// IGM descriptions should be the ones provided in subnetwork models, CGM description should be the default one
- assertEquals("BE network description", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION));
- assertEquals("NL network description", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION));
- assertEquals("SV Model", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION));
+ assertEquals("BE network description", getFirstMatch(updatedBeSshXml, REGEX_DESCRIPTION));
+ assertEquals("NL network description", getFirstMatch(updatedNlSshXml, REGEX_DESCRIPTION));
+ assertEquals("SV Model", getFirstMatch(updatedCgmSvXml, REGEX_DESCRIPTION));
// Version number should be increased from original models and be the same for all instance files
- assertEquals("2", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION));
- assertEquals("2", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION));
- assertEquals("2", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION));
+ assertEquals("2", getFirstMatch(updatedBeSshXml, REGEX_VERSION));
+ assertEquals("2", getFirstMatch(updatedNlSshXml, REGEX_VERSION));
+ assertEquals("2", getFirstMatch(updatedCgmSvXml, REGEX_VERSION));
// The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP
String updatedBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_2_1D__FM";
@@ -203,25 +202,25 @@ void testCgmExportWithModelsForSubnetworks() throws IOException {
String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM";
String originalTpBdId = "Common TP_BD model ID";
Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, originalTpBdId);
- assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON));
+ assertEquals(expectedDependencies, getUniqueMatches(updatedCgmSvXml, REGEX_DEPENDENT_ON));
// Each updated IGM SSH should supersede the original one and depend on the original EQ
String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM";
String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM";
- assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES));
- assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES));
- assertEquals(Set.of("BE EQ model ID"), getOccurrences(updatedBeSshXml, REGEX_DEPENDENT_ON));
- assertEquals(Set.of("NL EQ model ID"), getOccurrences(updatedNlSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(originalBeSshId, getFirstMatch(updatedBeSshXml, REGEX_SUPERSEDES));
+ assertEquals(originalNlSshId, getFirstMatch(updatedNlSshXml, REGEX_SUPERSEDES));
+ assertEquals(Set.of("BE EQ model ID"), getUniqueMatches(updatedBeSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(Set.of("NL EQ model ID"), getUniqueMatches(updatedNlSshXml, REGEX_DEPENDENT_ON));
// Profiles should be consistent with the instance files
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE));
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE));
- assertEquals("http://entsoe.eu/CIM/StateVariables/4/1", getFirstOccurrence(updatedCgmSvXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedBeSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedNlSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/StateVariables/4/1", getFirstMatch(updatedCgmSvXml, REGEX_PROFILE));
// IGM MAS should be the ones provided in subnetwork models, CGM MAS should be the default one
- assertEquals("http://elia.be/CGMES/2.4.15", getFirstOccurrence(updatedBeSshXml, REGEX_MAS));
- assertEquals("http://tennet.nl/CGMES/2.4.15", getFirstOccurrence(updatedNlSshXml, REGEX_MAS));
- assertEquals("powsybl.org", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS));
+ assertEquals("http://elia.be/CGMES/2.4.15", getFirstMatch(updatedBeSshXml, REGEX_MAS));
+ assertEquals("http://tennet.nl/CGMES/2.4.15", getFirstMatch(updatedNlSshXml, REGEX_MAS));
+ assertEquals("powsybl.org", getFirstMatch(updatedCgmSvXml, REGEX_MAS));
}
@Test
@@ -242,20 +241,20 @@ void testCgmExportWithModelsForAllNetworks() throws IOException {
// The main network has a different scenario time than the subnetworks
// All updated models should get that scenario time
- assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME));
+ assertEquals("2022-03-04T05:30:00Z", getFirstMatch(updatedBeSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2022-03-04T05:30:00Z", getFirstMatch(updatedNlSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2022-03-04T05:30:00Z", getFirstMatch(updatedCgmSvXml, REGEX_SCENARIO_TIME));
// IGM descriptions should be the ones provided in subnetwork models, CGM description should be the one provided in main network model
- assertEquals("BE network description", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION));
- assertEquals("NL network description", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION));
- assertEquals("Merged network description", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION));
+ assertEquals("BE network description", getFirstMatch(updatedBeSshXml, REGEX_DESCRIPTION));
+ assertEquals("NL network description", getFirstMatch(updatedNlSshXml, REGEX_DESCRIPTION));
+ assertEquals("Merged network description", getFirstMatch(updatedCgmSvXml, REGEX_DESCRIPTION));
// The main network has a different version number (3) than the subnetworks (1)
// Updated models should use next version taking into account the max version number of inputs (next version is 4)
- assertEquals("4", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION));
- assertEquals("4", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION));
- assertEquals("4", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION));
+ assertEquals("4", getFirstMatch(updatedBeSshXml, REGEX_VERSION));
+ assertEquals("4", getFirstMatch(updatedNlSshXml, REGEX_VERSION));
+ assertEquals("4", getFirstMatch(updatedCgmSvXml, REGEX_VERSION));
// The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP
// The model of the main network brings an additional dependency
@@ -267,27 +266,27 @@ void testCgmExportWithModelsForAllNetworks() throws IOException {
String additionalDependency = "Additional dependency";
Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId,
originalNlTpId, originalTpBdId, additionalDependency);
- assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON));
+ assertEquals(expectedDependencies, getUniqueMatches(updatedCgmSvXml, REGEX_DEPENDENT_ON));
// Each updated IGM SSH should supersede the original one and depend on the original EQ
String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM";
String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM";
- assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES));
- assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES));
- assertEquals(Set.of("BE EQ model ID"), getOccurrences(updatedBeSshXml, REGEX_DEPENDENT_ON));
- assertEquals(Set.of("NL EQ model ID"), getOccurrences(updatedNlSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(originalBeSshId, getFirstMatch(updatedBeSshXml, REGEX_SUPERSEDES));
+ assertEquals(originalNlSshId, getFirstMatch(updatedNlSshXml, REGEX_SUPERSEDES));
+ assertEquals(Set.of("BE EQ model ID"), getUniqueMatches(updatedBeSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(Set.of("NL EQ model ID"), getUniqueMatches(updatedNlSshXml, REGEX_DEPENDENT_ON));
// Profiles should be consistent with the instance files
// The model of the main network brings an additional profile
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE));
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedBeSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedNlSshXml, REGEX_PROFILE));
Set expectedProfiles = Set.of("Additional profile", "http://entsoe.eu/CIM/StateVariables/4/1");
- assertEquals(expectedProfiles, getOccurrences(updatedCgmSvXml, REGEX_PROFILE));
+ assertEquals(expectedProfiles, getUniqueMatches(updatedCgmSvXml, REGEX_PROFILE));
// IGM MAS should be the ones provided in subnetwork models, CGM MAS should be the one provided in main network model
- assertEquals("http://elia.be/CGMES/2.4.15", getFirstOccurrence(updatedBeSshXml, REGEX_MAS));
- assertEquals("http://tennet.nl/CGMES/2.4.15", getFirstOccurrence(updatedNlSshXml, REGEX_MAS));
- assertEquals("Modeling Authority", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS));
+ assertEquals("http://elia.be/CGMES/2.4.15", getFirstMatch(updatedBeSshXml, REGEX_MAS));
+ assertEquals("http://tennet.nl/CGMES/2.4.15", getFirstMatch(updatedNlSshXml, REGEX_MAS));
+ assertEquals("Modeling Authority", getFirstMatch(updatedCgmSvXml, REGEX_MAS));
}
@Test
@@ -309,19 +308,19 @@ void testCgmExportWithProperties() throws IOException {
String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml"));
// Scenario time should be the same for all models
- assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME));
+ assertEquals("2021-02-03T04:30:00Z", getFirstMatch(updatedBeSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2021-02-03T04:30:00Z", getFirstMatch(updatedNlSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2021-02-03T04:30:00Z", getFirstMatch(updatedCgmSvXml, REGEX_SCENARIO_TIME));
// Description should be the one provided as parameter and be the same for all instance files
- assertEquals("Common Grid Model export", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION));
- assertEquals("Common Grid Model export", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION));
- assertEquals("Common Grid Model export", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION));
+ assertEquals("Common Grid Model export", getFirstMatch(updatedBeSshXml, REGEX_DESCRIPTION));
+ assertEquals("Common Grid Model export", getFirstMatch(updatedNlSshXml, REGEX_DESCRIPTION));
+ assertEquals("Common Grid Model export", getFirstMatch(updatedCgmSvXml, REGEX_DESCRIPTION));
// Version number should be the one provided as parameter and be the same for all instance files
- assertEquals("4", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION));
- assertEquals("4", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION));
- assertEquals("4", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION));
+ assertEquals("4", getFirstMatch(updatedBeSshXml, REGEX_VERSION));
+ assertEquals("4", getFirstMatch(updatedNlSshXml, REGEX_VERSION));
+ assertEquals("4", getFirstMatch(updatedCgmSvXml, REGEX_VERSION));
// The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP
String updatedBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_4_1D__FM";
@@ -330,27 +329,27 @@ void testCgmExportWithProperties() throws IOException {
String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM";
String originalTpBdId = "ENTSOE TP_BD model ID";
Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, originalTpBdId);
- assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON));
+ assertEquals(expectedDependencies, getUniqueMatches(updatedCgmSvXml, REGEX_DEPENDENT_ON));
// Each updated IGM SSH should supersede the original one and depend on the original EQ
String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM";
String originalBeEqId = "urn:uuid:Network_BE_N_EQUIPMENT_2021-02-03T04:30:00Z_1_1D__FM";
String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM";
String originalNlEqId = "urn:uuid:Network_NL_N_EQUIPMENT_2021-02-03T04:30:00Z_1_1D__FM";
- assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES));
- assertEquals(originalBeEqId, getFirstOccurrence(updatedBeSshXml, REGEX_DEPENDENT_ON));
- assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES));
- assertEquals(originalNlEqId, getFirstOccurrence(updatedNlSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(originalBeSshId, getFirstMatch(updatedBeSshXml, REGEX_SUPERSEDES));
+ assertEquals(originalBeEqId, getFirstMatch(updatedBeSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(originalNlSshId, getFirstMatch(updatedNlSshXml, REGEX_SUPERSEDES));
+ assertEquals(originalNlEqId, getFirstMatch(updatedNlSshXml, REGEX_DEPENDENT_ON));
// Profiles should be consistent with the instance files
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE));
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE));
- assertEquals("http://entsoe.eu/CIM/StateVariables/4/1", getFirstOccurrence(updatedCgmSvXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedBeSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedNlSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/StateVariables/4/1", getFirstMatch(updatedCgmSvXml, REGEX_PROFILE));
// IGM MAS should be the default ones, CGM MAS should be the one provided as parameter
- assertEquals("powsybl.org", getFirstOccurrence(updatedBeSshXml, REGEX_MAS));
- assertEquals("powsybl.org", getFirstOccurrence(updatedNlSshXml, REGEX_MAS));
- assertEquals("Regional Coordination Center", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS));
+ assertEquals("powsybl.org", getFirstMatch(updatedBeSshXml, REGEX_MAS));
+ assertEquals("powsybl.org", getFirstMatch(updatedNlSshXml, REGEX_MAS));
+ assertEquals("Regional Coordination Center", getFirstMatch(updatedCgmSvXml, REGEX_MAS));
}
@Test
@@ -375,19 +374,19 @@ void testCgmExportWithModelsAndProperties() throws IOException {
// The main network has a different scenario time than the subnetworks
// All updated models should get that scenario time
- assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME));
- assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME));
+ assertEquals("2022-03-04T05:30:00Z", getFirstMatch(updatedBeSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2022-03-04T05:30:00Z", getFirstMatch(updatedNlSshXml, REGEX_SCENARIO_TIME));
+ assertEquals("2022-03-04T05:30:00Z", getFirstMatch(updatedCgmSvXml, REGEX_SCENARIO_TIME));
// Both the models and a property define the description. The property should prevail.
- assertEquals("Common Grid Model export", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION));
- assertEquals("Common Grid Model export", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION));
- assertEquals("Common Grid Model export", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION));
+ assertEquals("Common Grid Model export", getFirstMatch(updatedBeSshXml, REGEX_DESCRIPTION));
+ assertEquals("Common Grid Model export", getFirstMatch(updatedNlSshXml, REGEX_DESCRIPTION));
+ assertEquals("Common Grid Model export", getFirstMatch(updatedCgmSvXml, REGEX_DESCRIPTION));
// Both the models and a property define the version number. The property should prevail.
- assertEquals("4", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION));
- assertEquals("4", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION));
- assertEquals("4", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION));
+ assertEquals("4", getFirstMatch(updatedBeSshXml, REGEX_VERSION));
+ assertEquals("4", getFirstMatch(updatedNlSshXml, REGEX_VERSION));
+ assertEquals("4", getFirstMatch(updatedCgmSvXml, REGEX_VERSION));
// The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP
// The model of the main network brings an additional dependency
@@ -399,26 +398,26 @@ void testCgmExportWithModelsAndProperties() throws IOException {
String additionalDependency = "Additional dependency";
Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId,
originalNlTpId, originalTpBdId, additionalDependency);
- assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON));
+ assertEquals(expectedDependencies, getUniqueMatches(updatedCgmSvXml, REGEX_DEPENDENT_ON));
// Each updated IGM SSH should supersede the original one and depend on the original EQ
String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM";
String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM";
- assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES));
- assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES));
- assertEquals(Set.of("BE EQ model ID"), getOccurrences(updatedBeSshXml, REGEX_DEPENDENT_ON));
- assertEquals(Set.of("NL EQ model ID"), getOccurrences(updatedNlSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(originalBeSshId, getFirstMatch(updatedBeSshXml, REGEX_SUPERSEDES));
+ assertEquals(originalNlSshId, getFirstMatch(updatedNlSshXml, REGEX_SUPERSEDES));
+ assertEquals(Set.of("BE EQ model ID"), getUniqueMatches(updatedBeSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(Set.of("NL EQ model ID"), getUniqueMatches(updatedNlSshXml, REGEX_DEPENDENT_ON));
// Profiles should be consistent with the instance files, CGM SV has an additional profile
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE));
- assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedBeSshXml, REGEX_PROFILE));
+ assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstMatch(updatedNlSshXml, REGEX_PROFILE));
Set expectedProfiles = Set.of("Additional profile", "http://entsoe.eu/CIM/StateVariables/4/1");
- assertEquals(expectedProfiles, getOccurrences(updatedCgmSvXml, REGEX_PROFILE));
+ assertEquals(expectedProfiles, getUniqueMatches(updatedCgmSvXml, REGEX_PROFILE));
// Both the model and a property define the main network MAS. The property should prevail.
- assertEquals("http://elia.be/CGMES/2.4.15", getFirstOccurrence(updatedBeSshXml, REGEX_MAS));
- assertEquals("http://tennet.nl/CGMES/2.4.15", getFirstOccurrence(updatedNlSshXml, REGEX_MAS));
- assertEquals("Regional Coordination Center", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS));
+ assertEquals("http://elia.be/CGMES/2.4.15", getFirstMatch(updatedBeSshXml, REGEX_MAS));
+ assertEquals("http://tennet.nl/CGMES/2.4.15", getFirstMatch(updatedNlSshXml, REGEX_MAS));
+ assertEquals("Regional Coordination Center", getFirstMatch(updatedCgmSvXml, REGEX_MAS));
}
@Test
@@ -569,8 +568,8 @@ void testFaraoUseCaseManualExport() throws IOException {
assertEquals(expectedOutputVersion, sshVersionInOutput);
String outputSshXml = Files.readString(tmpFolder.resolve(filenameFromCgmesExport));
- assertEquals(Set.of("myDependency"), getOccurrences(outputSshXml, REGEX_DEPENDENT_ON));
- assertEquals(Set.of("mySupersede"), getOccurrences(outputSshXml, REGEX_SUPERSEDES));
+ assertEquals(Set.of("myDependency"), getUniqueMatches(outputSshXml, REGEX_DEPENDENT_ON));
+ assertEquals(Set.of("mySupersede"), getUniqueMatches(outputSshXml, REGEX_SUPERSEDES));
}
String filenameFromCgmesExport = basename + "_SV.xml";
// We read it from inside the SV file ...
@@ -578,7 +577,7 @@ void testFaraoUseCaseManualExport() throws IOException {
assertEquals(expectedOutputVersion, svVersionInOutput);
// Check the dependencies
String outputSvXml = Files.readString(tmpFolder.resolve(filenameFromCgmesExport));
- assertEquals(Set.of("mySvDependency1", "mySvDependency2"), getOccurrences(outputSvXml, REGEX_DEPENDENT_ON));
+ assertEquals(Set.of("mySvDependency1", "mySvDependency2"), getUniqueMatches(outputSvXml, REGEX_DEPENDENT_ON));
}
private static final Map TSO_BY_COUNTRY = Map.of(
@@ -729,21 +728,4 @@ private void addModelForNetwork(Network network, int version) {
.add();
}
- private String getFirstOccurrence(String xml, Pattern pattern) {
- Matcher matcher = pattern.matcher(xml);
- if (matcher.find()) {
- return matcher.group(1);
- }
- throw new PowsyblException("Pattern not found " + pattern);
- }
-
- private Set getOccurrences(String xml, Pattern pattern) {
- Set matches = new HashSet<>();
- Matcher matcher = pattern.matcher(xml);
- while (matcher.find()) {
- matches.add(matcher.group(1));
- }
- return matches;
- }
-
}
diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/line_container_3acls_EQ.xml b/cgmes/cgmes-conversion/src/test/resources/issues/line_container_3acls_EQ.xml
deleted file mode 100644
index 47e2f35b247..00000000000
--- a/cgmes/cgmes-conversion/src/test/resources/issues/line_container_3acls_EQ.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-
-
- 2024-03-28T11:00:00Z
- 2024-03-28T11:00:00Z
- 1
- Line containers and fictitious voltage levels
- https://www.powsybl.org/
- http://entsoe.eu/CIM/EquipmentCore/3/1
- http://entsoe.eu/CIM/EquipmentOperation/3/1
-
-
-
- GeographicalRegionTest
-
-
- SubGeographicalRegionTest
-
-
-
- Substation1
-
-
-
- 11 kV
- 11.0
-
-
-
- Line Container
-
-
- Node inside line container
-
-
-
- 11.0
- 10.45
- 11.55
-
-
-
-
-
-
- ACLS without Line Container
- 0.12
- 0
- 0.3
- 0.34
-
-
-
- T1
-
- 1
-
-
-
-
- T2
-
- 2
-
-
-
- CN1
-
-
-
-
-
- ACLS2
-
- 0.28
- ACLS2
- 0
- 0.72
- 0.78
-
-
-
- ACLS2 T1
-
- 1
-
-
-
-
- ACLS2 T2
-
- 2
-
-
-
- ACLS2 CN1
-
-
-
-
-
- ACLS3
-
- 0.28
- ACLS3
- 0
- 0.72
- 0.78
-
-
-
- ACLS3 T1
-
- 1
-
-
-
-
- ACLS3 T2
-
- 2
-
-
-
- ACLS3 CN1
-
-
-
\ No newline at end of file
diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/line_container_switch_EQ.xml b/cgmes/cgmes-conversion/src/test/resources/issues/line_container_switch_EQ.xml
new file mode 100644
index 00000000000..6b6fbf5d62a
--- /dev/null
+++ b/cgmes/cgmes-conversion/src/test/resources/issues/line_container_switch_EQ.xml
@@ -0,0 +1,83 @@
+
+
+ 2024-03-28T11:00:00Z
+ 2024-03-28T11:00:00Z
+ 1
+ Line containers and fictitious voltage levels
+ https://www.powsybl.org/
+ http://entsoe.eu/CIM/EquipmentCore/3/1
+ http://entsoe.eu/CIM/EquipmentOperation/3/1
+
+
+
+ GeographicalRegionTest
+
+
+ SubGeographicalRegionTest
+
+
+
+ Substation1
+
+
+
+ Substation2
+
+
+
+ 11 kV
+ 11.0
+
+
+
+ Line Container
+
+
+ Node inside line container
+
+
+
+ 11.0
+ 10.45
+ 11.55
+
+
+
+
+ Node inside voltage level 1
+
+
+
+ 11.0
+ 10.45
+ 11.55
+
+
+
+
+ Node inside voltage level 2
+
+
+
+
+ Disconnector 1
+
+ false
+ false
+
+
+
+ Disconnector 1 T1
+
+ 1
+
+
+
+
+ Disconnector 1 T2
+
+ 2
+
+
+
+
\ No newline at end of file
diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/chained_line_segments.xml b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/chained_line_segments.xml
new file mode 100644
index 00000000000..1034919c296
--- /dev/null
+++ b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/chained_line_segments.xml
@@ -0,0 +1,137 @@
+
+
+ 2021-03-01T23:00:00Z
+ 2021-03-02T10:22:58Z
+ Container mapping
+ 001
+ http://entsoe.eu/CIM/EquipmentCore/3/1
+ http://entsoe.eu/CIM/EquipmentOperation/3/1
+ powsybl.org
+
+
+ Geographical region
+
+
+ Subgeographical region
+
+
+
+ Substation 1
+
+
+
+ Voltage level 1
+
+
+
+
+ Node 1
+
+
+
+ Substation 2
+
+
+
+ Voltage level 2
+
+
+
+
+ Node 2
+
+
+
+ Line 12
+
+
+
+ Node A
+
+
+
+ Node B
+
+
+
+ 0
+ 0
+ 0
+ 0
+ Line 1A
+ 0.1
+ 0.1
+ 75
+ 1
+ 1
+
+
+
+
+ Terminal 1A 1
+ 1
+
+
+
+
+ Terminal 1A 2
+ 2
+
+
+
+
+ 0
+ 0
+ 0
+ 0
+ Line AB
+ 0.1
+ 0.1
+ 75
+ 1
+ 1
+
+
+
+
+ Terminal AB 1
+ 1
+
+
+
+
+ Terminal AB 2
+ 2
+
+
+
+
+ 0
+ 0
+ 0
+ 0
+ Line B2
+ 0.1
+ 0.1
+ 75
+ 1
+ 1
+
+
+
+ Terminal B2 1
+ 1
+
+
+
+
+ Terminal B2 2
+ 2
+
+
+
+
+ 110 kV
+ 110
+
+
diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/containers_connected_by_switches.xml b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/containers_connected_by_switches.xml
new file mode 100644
index 00000000000..f60f68a3258
--- /dev/null
+++ b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/containers_connected_by_switches.xml
@@ -0,0 +1,164 @@
+
+
+ 2021-03-01T23:00:00Z
+ 2021-03-02T10:22:58Z
+ Nodes containers mapping
+ 001
+ http://entsoe.eu/CIM/EquipmentCore/3/1
+ http://entsoe.eu/CIM/EquipmentOperation/3/1
+ powsybl.org
+
+
+ Geographical region
+
+
+ Subgeographical region
+
+
+
+ Substation 1
+
+
+
+ Voltage level 1A
+
+
+
+
+ Node 1A
+
+
+
+ Voltage level 1B
+
+
+
+
+ Node 1B
+
+
+
+ Substation 2
+
+
+
+ Voltage level 2
+
+
+
+
+ Node 2
+
+
+
+ Substation 3
+
+
+
+ Node 3
+
+
+
+ Line L
+
+
+
+ Node L
+
+
+
+ Switch AB
+
+ false
+ false
+
+
+ Terminal AB 1
+ 1
+
+
+
+
+ Terminal AB 2
+ 2
+
+
+
+
+ Switch 12
+
+ false
+ false
+
+
+ Terminal 12 1
+ 1
+
+
+
+
+ Terminal 12 2
+ 2
+
+
+
+
+ Switch 23
+
+ false
+ false
+
+
+ Terminal 23 1
+ 1
+
+
+
+
+ Terminal 23 2
+ 2
+
+
+
+
+ Switch 3L
+
+ false
+ false
+
+
+ Terminal 3L 1
+ 1
+
+
+
+
+ Terminal 3L 2
+ 2
+
+
+
+
+ Busbar section
+
+
+
+
+ Terminal BBS
+
+
+
+
+ Load
+
+
+
+ Terminal LD
+
+
+
+
+ 110 kV
+ 110
+
+
diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/line_with_t-junction.xml b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/line_with_t-junction.xml
new file mode 100644
index 00000000000..08c7662027a
--- /dev/null
+++ b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/line_with_t-junction.xml
@@ -0,0 +1,146 @@
+
+
+ 2021-03-01T23:00:00Z
+ 2021-03-02T10:22:58Z
+ Container mapping
+ 001
+ http://entsoe.eu/CIM/EquipmentCore/3/1
+ http://entsoe.eu/CIM/EquipmentOperation/3/1
+ powsybl.org
+
+
+ Geographical region
+
+
+ Subgeographical region
+
+
+
+ Substation 1
+
+
+
+ Voltage level 1
+
+
+
+
+ Node 1
+
+
+
+ Substation 2
+
+
+
+ Voltage level 2
+
+
+
+
+ Node 2
+
+
+
+ Substation 3
+
+
+
+ Voltage level 3
+
+
+
+
+ Node 3
+
+
+
+ Line A
+
+
+
+ Node A
+
+
+
+ 0
+ 0
+ 0
+ 0
+ Line 1A
+ 0.1
+ 0.1
+ 75
+ 1
+ 1
+
+
+
+
+ Terminal 1A 1
+ 1
+
+
+
+
+ Terminal 1A 2
+ 2
+
+
+
+
+ 0
+ 0
+ 0
+ 0
+ Line 2A
+ 0.1
+ 0.1
+ 75
+ 1
+ 1
+
+
+
+
+ Terminal 2A 1
+ 1
+
+
+
+
+ Terminal 2A 2
+ 2
+
+
+
+
+ 0
+ 0
+ 0
+ 0
+ Line 3A
+ 0.1
+ 0.1
+ 75
+ 1
+ 1
+
+
+
+ Terminal 3A 1
+ 1
+
+
+
+
+ Terminal 3A 2
+ 2
+
+
+
+
+ 110 kV
+ 110
+
+
diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/substations_connected_by_transformer.xml b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/substations_connected_by_transformer.xml
new file mode 100644
index 00000000000..b3ae6cfc189
--- /dev/null
+++ b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/substations_connected_by_transformer.xml
@@ -0,0 +1,139 @@
+
+
+ 2021-03-01T23:00:00Z
+ 2021-03-02T10:22:58Z
+ Container mapping
+ 001
+ http://entsoe.eu/CIM/EquipmentCore/3/1
+ http://entsoe.eu/CIM/EquipmentOperation/3/1
+ http://entsoe.eu/CIM/EquipmentShortCircuit/3/1
+ powsybl.org
+
+
+ Geographical region
+
+
+ Subgeographical region
+
+
+
+ Substation 1
+
+
+
+ Voltage level 1
+
+
+
+
+ Node 1
+
+
+
+ Substation 2
+
+
+
+ Voltage level 2
+
+
+
+
+ Node 2
+
+
+
+ Substation 3
+
+
+
+ Voltage level 3
+
+
+
+
+ Node 3
+
+
+
+ PowerTransformer 1
+
+ false
+
+
+ TransformerEnd 1
+ 1
+ false
+ 0
+ 0
+ 0
+ 0
+ 0.1
+ 0.1
+ 1
+ 1
+ 0
+ 110
+
+
+
+
+
+ TransformerEnd 2
+ 2
+ false
+ 0
+ 0
+ 0
+ 0
+ 0.1
+ 0.1
+ 1
+ 1
+ 0
+ 110
+
+
+
+
+
+ TransformerEnd 3
+ 3
+ false
+ 0
+ 0
+ 0
+ 0
+ 0.1
+ 0.1
+ 1
+ 1
+ 0
+ 110
+
+
+
+
+
+ Terminal 1
+ 1
+
+
+
+
+ Terminal 2
+ 2
+
+
+
+
+ Terminal 3
+ 3
+
+
+
+
+ 110 kV
+ 110
+
+
diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/voltage_levels_connected_by_open_switch_EQ.xml b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/voltage_levels_connected_by_open_switch_EQ.xml
new file mode 100644
index 00000000000..1c480ce5bc9
--- /dev/null
+++ b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/voltage_levels_connected_by_open_switch_EQ.xml
@@ -0,0 +1,62 @@
+
+
+ 2021-03-01T23:00:00Z
+ 2021-03-02T10:22:58Z
+ Nodes containers mapping
+ 001
+ http://entsoe.eu/CIM/EquipmentCore/3/1
+ http://entsoe.eu/CIM/EquipmentOperation/3/1
+ powsybl.org
+
+
+ Geographical region
+
+
+ Subgeographical region
+
+
+
+ Substation
+
+
+
+ Voltage level 1
+
+
+
+
+ Node 1
+
+
+
+ Voltage level 2
+
+
+
+
+ Node 2
+
+
+
+ Switch 12
+
+ true
+ true
+
+
+ Terminal 12 1
+ 1
+
+
+
+
+ Terminal 12 2
+ 2
+
+
+
+
+ 110 kV
+ 110
+
+
diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/voltage_levels_connected_by_open_switch_SSH.xml b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/voltage_levels_connected_by_open_switch_SSH.xml
new file mode 100644
index 00000000000..db2540d2b21
--- /dev/null
+++ b/cgmes/cgmes-conversion/src/test/resources/issues/node-containers/voltage_levels_connected_by_open_switch_SSH.xml
@@ -0,0 +1,20 @@
+
+
+ 2021-03-01T23:00:00Z
+ 2021-03-02T10:22:58Z
+ Nodes containers mapping
+ 001
+
+ http://entsoe.eu/CIM/SteadyStateHypothesis/1/1
+ powsybl.org
+
+
+ false
+
+
+ false
+
+
+ true
+
+
diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/AbstractCgmesModel.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/AbstractCgmesModel.java
index 1ccbd7dc77b..82b1c963ac0 100644
--- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/AbstractCgmesModel.java
+++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/AbstractCgmesModel.java
@@ -101,16 +101,8 @@ public CgmesContainer container(String containerId) {
if (cachedContainers == null) {
cachedContainers = computeContainers();
}
- if (cachedContainers.get(containerId) == null) { // container ID is substation
- String fixedContainerId = connectivityNodeContainers().stream()
- .filter(p -> p.containsKey(SUBSTATION))
- .filter(p -> p.getId(SUBSTATION).equals(containerId))
- .findFirst()
- .map(p -> p.getId("VoltageLevel"))
- .orElseThrow(() -> new CgmesModelException(containerId + " should be a connectivity node container containing at least one voltage level"));
- LOG.warn("{} is a substation, not a voltage level, a line or a bay but contains nodes. " +
- "The first CGMES voltage level found in this substation ({}}) is used instead.", containerId, fixedContainerId);
- cachedContainers.put(containerId, cachedContainers.get(fixedContainerId));
+ if (cachedContainers.get(containerId) == null) {
+ throw new CgmesModelException("Unexpected CgmesContainer for containerId: " + containerId);
}
return cachedContainers.get(containerId);
}
@@ -125,6 +117,32 @@ public double nominalVoltage(String baseVoltageId) {
return cachedBaseVoltages.getOrDefault(baseVoltageId, Double.NaN);
}
+ @Override
+ public Optional node(CgmesTerminal t, boolean nodeBreaker) {
+ cacheNodes();
+ String nodeId = nodeBreaker && t.connectivityNode() != null ? t.connectivityNode() : t.topologicalNode();
+ return nodeId != null ? Optional.of(nodeId) : Optional.empty();
+ }
+
+ @Override
+ public Optional nodeContainer(String nodeId) {
+ cacheNodes();
+
+ String containerId = null;
+
+ if (nodeId != null) {
+ PropertyBag node = cachedNodesById.get(nodeId);
+ if (node != null) {
+ containerId = node.getId(CgmesNames.CONNECTIVITY_NODE_CONTAINER);
+ } else {
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("Missing node {}", nodeId);
+ }
+ }
+ }
+ return containerId == null ? Optional.empty() : Optional.of(container(containerId));
+ }
+
private CgmesContainer container(CgmesTerminal t, boolean nodeBreaker) {
cacheNodes();
String containerId = null;
@@ -132,7 +150,7 @@ private CgmesContainer container(CgmesTerminal t, boolean nodeBreaker) {
if (nodeId != null) {
PropertyBag node = cachedNodesById.get(nodeId);
if (node != null) {
- containerId = node.getId("ConnectivityNodeContainer");
+ containerId = node.getId(CgmesNames.CONNECTIVITY_NODE_CONTAINER);
} else {
if (LOG.isWarnEnabled()) {
LOG.warn("Missing node {} from terminal {}", nodeId, t.id());
@@ -218,10 +236,13 @@ private Map computeDcTerminals() {
private Map computeContainers() {
Map cs = new HashMap<>();
connectivityNodeContainers().forEach(c -> {
- String id = c.getId("ConnectivityNodeContainer");
+ String id = c.getId(CgmesNames.CONNECTIVITY_NODE_CONTAINER);
String voltageLevel = c.getId("VoltageLevel");
String substation = c.getId(SUBSTATION);
- cs.put(id, new CgmesContainer(voltageLevel, substation));
+ String type = c.getId("connectivityNodeContainerType");
+ String line = type != null && type.contains("Line") ? id : null;
+ String name = c.get("name");
+ cs.put(id, new CgmesContainer(voltageLevel, substation, line, name));
});
return cs;
}
diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesContainer.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesContainer.java
index 2d407757f14..6ef5e403b0f 100644
--- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesContainer.java
+++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesContainer.java
@@ -13,9 +13,11 @@
*/
public class CgmesContainer {
- CgmesContainer(String voltageLevel, String substation) {
+ CgmesContainer(String voltageLevel, String substation, String line, String name) {
this.voltageLevel = voltageLevel;
this.substation = substation;
+ this.line = line;
+ this.name = name;
}
public String substation() {
@@ -26,6 +28,32 @@ public String voltageLevel() {
return voltageLevel;
}
+ public String name() {
+ return name;
+ }
+
+ public String id() {
+ if (line != null) {
+ return line;
+ } else if (voltageLevel != null) {
+ return voltageLevel;
+ } else if (substation != null) {
+ return substation;
+ } else {
+ throw new CgmesModelException("Unexpected null CgmesContainer");
+ }
+ }
+
+ public boolean isVoltageLevel() {
+ return voltageLevel != null;
+ }
+
+ public boolean isSubstation() {
+ return substation != null;
+ }
+
private final String voltageLevel;
private final String substation;
+ private final String line;
+ private final String name;
}
diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesModel.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesModel.java
index 84057f5c4e1..1312f12762c 100644
--- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesModel.java
+++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesModel.java
@@ -234,6 +234,10 @@ default void write(DataSource ds, CgmesSubset subset) {
*/
String voltageLevel(CgmesTerminal t, boolean nodeBreaker);
+ Optional node(CgmesTerminal t, boolean nodeBreaker);
+
+ Optional nodeContainer(String nodeId);
+
CgmesContainer container(String containerId);
double nominalVoltage(String baseVoltageId);
diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/InMemoryCgmesModel.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/InMemoryCgmesModel.java
index 76412620d91..69c279d00b4 100644
--- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/InMemoryCgmesModel.java
+++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/InMemoryCgmesModel.java
@@ -613,6 +613,16 @@ public String voltageLevel(CgmesTerminal t, boolean nodeBreaker) {
return null;
}
+ @Override
+ public Optional node(CgmesTerminal t, boolean nodeBreaker) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional nodeContainer(String nodeId) {
+ return Optional.empty();
+ }
+
@Override
public CgmesContainer container(String containerId) {
return null;
diff --git a/cgmes/cgmes-model/src/main/resources/CIM16.sparql b/cgmes/cgmes-model/src/main/resources/CIM16.sparql
index 2ac8ae113eb..5ec42873e9e 100644
--- a/cgmes/cgmes-model/src/main/resources/CIM16.sparql
+++ b/cgmes/cgmes-model/src/main/resources/CIM16.sparql
@@ -342,8 +342,9 @@ WHERE {
# A Bay may be in a different instance file of its containing Voltage Level,
# and a Voltage Level in a different instance file of its containing Substation.
# This is why we do not use a "restrictive" GRAPH clause here.
- ?ConnectivityNodeContainer a ?connectivityNodeContainerType .
- VALUES ?connectivityNodeContainerType { cim:VoltageLevel cim:Bay cim:Line } .
+ ?ConnectivityNodeContainer a ?connectivityNodeContainerType ;
+ VALUES ?connectivityNodeContainerType { cim:VoltageLevel cim:Bay cim:Line cim:Substation} .
+ ?ConnectivityNodeContainer cim:IdentifiedObject.name ?name .
OPTIONAL {
?ConnectivityNodeContainer
a cim:VoltageLevel ;
@@ -355,6 +356,10 @@ WHERE {
cim:Bay.VoltageLevel ?VoltageLevel .
?VoltageLevel cim:VoltageLevel.Substation ?Substation
}
+ OPTIONAL {
+ ?ConnectivityNodeContainer a cim:Substation ;
+ BIND ( ?ConnectivityNodeContainer AS ?Substation )
+ }
}
# query: voltages
diff --git a/commons/src/main/java/com/powsybl/commons/binary/BinReader.java b/commons/src/main/java/com/powsybl/commons/binary/BinReader.java
index ab0d90410c1..0b84a4b9ea8 100644
--- a/commons/src/main/java/com/powsybl/commons/binary/BinReader.java
+++ b/commons/src/main/java/com/powsybl/commons/binary/BinReader.java
@@ -76,6 +76,7 @@ private String readString() {
}
byte[] stringBytes = dis.readNBytes(stringNbBytes);
if (stringBytes.length != stringNbBytes) {
+ // this may happen when the attribute wasn't written in the first place, causing string length to be an aberrant number
throw new PowsyblException("Cannot read the full string, bytes missing: " + (stringNbBytes - stringBytes.length));
}
return new String(stringBytes, StandardCharsets.UTF_8);
diff --git a/distribution-core/pom.xml b/distribution-core/pom.xml
index a69bf7eac5f..8900ca8011c 100644
--- a/distribution-core/pom.xml
+++ b/distribution-core/pom.xml
@@ -509,6 +509,14 @@
powsybl-ucte-util
${project.version}
+
+
+
+ ${project.groupId}
+ powsybl-itools-packager-maven-plugin
+ ${project.version}
+ provided
+
diff --git a/docs/grid_exchange_formats/cgmes/import.md b/docs/grid_exchange_formats/cgmes/import.md
index 54bb25e7c3f..786c0c4415b 100644
--- a/docs/grid_exchange_formats/cgmes/import.md
+++ b/docs/grid_exchange_formats/cgmes/import.md
@@ -580,3 +580,8 @@ Optional property to define if subnetworks must be added to the network when imp
**iidm.import.cgmes.cgm-with-subnetworks-defined-by**
If `iidm.import.cgmes.cgm-with-subnetworks` is set to `true`, use this property to specify how the set of input files should be split by IGM: based on their filenames (use the value `FILENAME`) or by its modeling authority, read from the header (use the value `MODELING_AUTHORITY`).
Its default value is `MODELING_AUTHORITY`.
+
+**iidm.import.cgmes.create-fictitious-voltage-level-for-every-node**
+Optional property that defines the fictitious voltage levels created by line container. If it is set to `true`, a fictitious voltage level is created for each connectivity node inside the line container.
+If it is set to `false`, only one fictitious voltage level is created for each line container.
+`true` by default.
diff --git a/docs/user/configuration/import-export-parameters-default-value.md b/docs/user/configuration/import-export-parameters-default-value.md
index d04d145642e..604d440b801 100644
--- a/docs/user/configuration/import-export-parameters-default-value.md
+++ b/docs/user/configuration/import-export-parameters-default-value.md
@@ -1,23 +1,26 @@
# import-export-parameters-default-value
-The `import-export-parameters-default-value` module is an optional module used by the `com.powsybl.iidm.import_.Importers` class to initialize the parameters passed to configure the importer. This module supports 3 different types of properties:
-- Boolean
-- String
-- List of Strings
+The `import-export-parameters-default-value` module is an optional module used to configure the network importers and exporters.
-As the parameters are different from an importer to another, it is impossible to give an exhaustive list of supported
-properties. Please refer to the documentation of each [supported format](../../grid_exchange_formats/index.md) to know their specific configuration.
+The parameters are different from a format importer/exporter to another, please refer to the documentation of
+each [supported format](../../grid_exchange_formats/index.md) to learn more about their specific configuration.
## Examples
+In this example we configure:
+- the IIDM importer to throw an exception on trying to import an unknown or not deserializable extension
+- the IIDM exporter to export to IIDM in its version 1.12
+
**YAML configuration:**
```yaml
import-export-parameters-default-value:
iidm.import.xml.throw-exception-if-extension-not-found: true
+ iidm.export.xml.version: "1.12"
```
**XML configuration:**
```xml
true
+ 1.12
```
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DefaultNetworkListener.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DefaultNetworkListener.java
index 6f728ba70f9..e8d7bb2ad8a 100644
--- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DefaultNetworkListener.java
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DefaultNetworkListener.java
@@ -28,11 +28,6 @@ public void afterRemoval(String identifiable) {
// empty default implementation
}
- @Override
- public void onUpdate(Identifiable> identifiable, String attribute, Object oldValue, Object newValue) {
- // empty default implementation
- }
-
@Override
public void onUpdate(Identifiable> identifiable, String attribute, String variantId, Object oldValue, Object newValue) {
// empty default implementation
@@ -44,7 +39,7 @@ public void onExtensionCreation(Extension> extension) {
}
@Override
- public void onExtensionUpdate(Extension> extension, String attribute, Object oldValue, Object newValue) {
+ public void onExtensionUpdate(Extension> extension, String attribute, String variantId, Object oldValue, Object newValue) {
// empty default implementation
}
@@ -57,4 +52,34 @@ public void onExtensionBeforeRemoval(Extension> extension) {
public void onExtensionAfterRemoval(Identifiable> identifiable, String extensionName) {
// empty default implementation
}
+
+ @Override
+ public void onPropertyAdded(Identifiable> identifiable, String key, Object newValue) {
+ // empty default implementation
+ }
+
+ @Override
+ public void onPropertyReplaced(Identifiable> identifiable, String key, Object oldValue, Object newValue) {
+ // empty default implementation
+ }
+
+ @Override
+ public void onPropertyRemoved(Identifiable> identifiable, String key, Object oldValue) {
+ // empty default implementation
+ }
+
+ @Override
+ public void onVariantCreated(String sourceVariantId, String targetVariantId) {
+ // empty default implementation
+ }
+
+ @Override
+ public void onVariantOverwritten(String sourceVariantId, String targetVariantId) {
+ // empty default implementation
+ }
+
+ @Override
+ public void onVariantRemoved(String variantId) {
+ // empty default implementation
+ }
}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java
index 0a5ef1fbaa9..bf18803ba0c 100644
--- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java
@@ -24,7 +24,7 @@
*
* Importer
lookup is based on the ServiceLoader
* architecture so do not forget to create a
- * META-INF/services/com.powsybl.iidm.importData.Importer
file
+ * META-INF/services/com.powsybl.iidm.network.Importer
file
* with the fully qualified name of your Importer
implementation.
*
* @see java.util.ServiceLoader
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/NetworkEventRecorder.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/NetworkEventRecorder.java
new file mode 100644
index 00000000000..c04ed12215b
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/NetworkEventRecorder.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network;
+
+import com.powsybl.commons.extensions.Extension;
+import com.powsybl.iidm.network.events.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public class NetworkEventRecorder implements NetworkListener {
+
+ private final List events = new ArrayList<>();
+
+ public List getEvents() {
+ return events;
+ }
+
+ public void reset() {
+ events.clear();
+ }
+
+ @Override
+ public void onCreation(Identifiable> identifiable) {
+ events.add(new CreationNetworkEvent(identifiable.getId()));
+ }
+
+ @Override
+ public void beforeRemoval(Identifiable> identifiable) {
+ events.add(new RemovalNetworkEvent(identifiable.getId(), false));
+ }
+
+ @Override
+ public void afterRemoval(String id) {
+ events.add(new RemovalNetworkEvent(id, true));
+ }
+
+ @Override
+ public void onUpdate(Identifiable> identifiable, String attribute, String variantId, Object oldValue, Object newValue) {
+ events.add(new UpdateNetworkEvent(identifiable.getId(), attribute, variantId, oldValue, newValue));
+ }
+
+ @Override
+ public void onExtensionCreation(Extension> extension) {
+ events.add(new ExtensionCreationNetworkEvent(((Identifiable>) extension.getExtendable()).getId(), extension.getName()));
+ }
+
+ @Override
+ public void onExtensionAfterRemoval(Identifiable> identifiable, String extensionName) {
+ events.add(new ExtensionRemovalNetworkEvent(identifiable.getId(), extensionName, true));
+ }
+
+ @Override
+ public void onExtensionBeforeRemoval(Extension> extension) {
+ events.add(new ExtensionRemovalNetworkEvent(((Identifiable>) extension.getExtendable()).getId(), extension.getName(), false));
+ }
+
+ @Override
+ public void onExtensionUpdate(Extension> extension, String attribute, String variantId, Object oldValue, Object newValue) {
+ events.add(new ExtensionUpdateNetworkEvent(((Identifiable>) extension.getExtendable()).getId(), extension.getName(), attribute, variantId, oldValue, newValue));
+ }
+
+ @Override
+ public void onPropertyAdded(Identifiable> identifiable, String key, Object newValue) {
+ events.add(new PropertiesUpdateNetworkEvent(identifiable.getId(), key, PropertiesUpdateNetworkEvent.PropertyUpdateType.ADDED, null, newValue));
+ }
+
+ @Override
+ public void onPropertyReplaced(Identifiable> identifiable, String key, Object oldValue, Object newValue) {
+ events.add(new PropertiesUpdateNetworkEvent(identifiable.getId(), key, PropertiesUpdateNetworkEvent.PropertyUpdateType.REPLACED, oldValue, newValue));
+ }
+
+ @Override
+ public void onPropertyRemoved(Identifiable> identifiable, String key, Object oldValue) {
+ events.add(new PropertiesUpdateNetworkEvent(identifiable.getId(), key, PropertiesUpdateNetworkEvent.PropertyUpdateType.REMOVED, oldValue, null));
+ }
+
+ @Override
+ public void onVariantCreated(String sourceVariantId, String targetVariantId) {
+ events.add(new VariantNetworkEvent(sourceVariantId, targetVariantId, VariantNetworkEvent.VariantEventType.CREATED));
+ }
+
+ @Override
+ public void onVariantOverwritten(String sourceVariantId, String targetVariantId) {
+ events.add(new VariantNetworkEvent(sourceVariantId, targetVariantId, VariantNetworkEvent.VariantEventType.OVERWRITTEN));
+ }
+
+ @Override
+ public void onVariantRemoved(String variantId) {
+ events.add(new VariantNetworkEvent(variantId, null, VariantNetworkEvent.VariantEventType.REMOVED));
+ }
+}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/NetworkListener.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/NetworkListener.java
index 39a4ee2b159..71126530b45 100644
--- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/NetworkListener.java
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/NetworkListener.java
@@ -20,8 +20,6 @@ public interface NetworkListener {
void afterRemoval(String id);
- void onUpdate(Identifiable> identifiable, String attribute, Object oldValue, Object newValue);
-
void onUpdate(Identifiable> identifiable, String attribute, String variantId, Object oldValue, Object newValue);
void onExtensionCreation(Extension> extension);
@@ -30,29 +28,17 @@ public interface NetworkListener {
void onExtensionBeforeRemoval(Extension> extension);
- void onExtensionUpdate(Extension> extendable, String attribute, Object oldValue, Object newValue);
+ void onExtensionUpdate(Extension> extendable, String attribute, String variantId, Object oldValue, Object newValue);
- default void onElementAdded(Identifiable> identifiable, String attribute, Object newValue) {
- // empty default implementation
- }
+ void onPropertyAdded(Identifiable> identifiable, String key, Object newValue);
- default void onElementReplaced(Identifiable> identifiable, String attribute, Object oldValue, Object newValue) {
- // empty default implementation
- }
+ void onPropertyReplaced(Identifiable> identifiable, String key, Object oldValue, Object newValue);
- default void onElementRemoved(Identifiable> identifiable, String attribute, Object oldValue) {
- // empty default implementation
- }
+ void onPropertyRemoved(Identifiable> identifiable, String key, Object oldValue);
- default void onVariantCreated(String sourceVariantId, String targetVariantId) {
- // empty default implementation
- }
+ void onVariantCreated(String sourceVariantId, String targetVariantId);
- default void onVariantOverwritten(String sourceVariantId, String targetVariantId) {
- // empty default implementation
- }
+ void onVariantOverwritten(String sourceVariantId, String targetVariantId);
- default void onVariantRemoved(String variantId) {
- // empty default implementation
- }
+ void onVariantRemoved(String variantId);
}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/VoltageLevel.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/VoltageLevel.java
index d70e735f2df..053007a2326 100644
--- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/VoltageLevel.java
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/VoltageLevel.java
@@ -472,7 +472,7 @@ default int getMaximumNodeIndex() {
SwitchAdder newSwitch();
/**
- * Get a builder to create a new switch.
+ * Get a builder to create a new internal connection.
*/
InternalConnectionAdder newInternalConnection();
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/CreationNetworkEvent.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/CreationNetworkEvent.java
new file mode 100644
index 00000000000..900057183f5
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/CreationNetworkEvent.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.events;
+
+import java.util.Objects;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public record CreationNetworkEvent(String id) implements NetworkEvent {
+ public CreationNetworkEvent {
+ Objects.requireNonNull(id);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.CREATION;
+ }
+}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/ExtensionCreationNetworkEvent.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/ExtensionCreationNetworkEvent.java
new file mode 100644
index 00000000000..68d64f5f34e
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/ExtensionCreationNetworkEvent.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.events;
+
+import java.util.Objects;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public record ExtensionCreationNetworkEvent(String id, String extensionName) implements NetworkEvent {
+ public ExtensionCreationNetworkEvent {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(extensionName);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.EXTENSION_CREATION;
+ }
+}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/ExtensionRemovalNetworkEvent.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/ExtensionRemovalNetworkEvent.java
new file mode 100644
index 00000000000..ab8508ab895
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/ExtensionRemovalNetworkEvent.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.events;
+
+import java.util.Objects;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public record ExtensionRemovalNetworkEvent(String id, String extensionName, boolean after) implements NetworkEvent {
+ public ExtensionRemovalNetworkEvent {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(extensionName);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.EXTENSION_REMOVAL;
+ }
+}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/ExtensionUpdateNetworkEvent.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/ExtensionUpdateNetworkEvent.java
new file mode 100644
index 00000000000..05dfd5df103
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/ExtensionUpdateNetworkEvent.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.events;
+
+import java.util.Objects;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public record ExtensionUpdateNetworkEvent(String id, String extensionName, String attribute, String variantId, Object oldValue, Object newValue) implements NetworkEvent {
+ public ExtensionUpdateNetworkEvent {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(extensionName);
+ Objects.requireNonNull(attribute);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.EXTENSION_UPDATE;
+ }
+}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/NetworkEvent.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/NetworkEvent.java
new file mode 100644
index 00000000000..12132118863
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/NetworkEvent.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.events;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public interface NetworkEvent {
+ enum Type {
+ CREATION,
+ REMOVAL,
+ UPDATE,
+ PROPERTIES_UPDATE,
+ EXTENSION_CREATION,
+ EXTENSION_REMOVAL,
+ EXTENSION_UPDATE,
+ VARIANT,
+ }
+
+ Type getType();
+}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/PropertiesUpdateNetworkEvent.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/PropertiesUpdateNetworkEvent.java
new file mode 100644
index 00000000000..53ffaa95841
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/PropertiesUpdateNetworkEvent.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.events;
+
+import java.util.Objects;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public record PropertiesUpdateNetworkEvent(String id, String key, PropertyUpdateType updateType, Object oldValue, Object newValue) implements NetworkEvent {
+
+ public enum PropertyUpdateType {
+ ADDED,
+ REMOVED,
+ REPLACED;
+ }
+
+ public PropertiesUpdateNetworkEvent {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(updateType);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.PROPERTIES_UPDATE;
+ }
+}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/RemovalNetworkEvent.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/RemovalNetworkEvent.java
new file mode 100644
index 00000000000..ee55cbf7d79
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/RemovalNetworkEvent.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.events;
+
+import java.util.Objects;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public record RemovalNetworkEvent(String id, boolean after) implements NetworkEvent {
+ public RemovalNetworkEvent {
+ Objects.requireNonNull(id);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.REMOVAL;
+ }
+}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/UpdateNetworkEvent.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/UpdateNetworkEvent.java
new file mode 100644
index 00000000000..190915f7f1f
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/UpdateNetworkEvent.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.events;
+
+import java.util.Objects;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public record UpdateNetworkEvent(String id, String attribute, String variantId, Object oldValue, Object newValue) implements NetworkEvent {
+ public UpdateNetworkEvent {
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(attribute);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.UPDATE;
+ }
+}
diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/VariantNetworkEvent.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/VariantNetworkEvent.java
new file mode 100644
index 00000000000..fff0c2f8b13
--- /dev/null
+++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/events/VariantNetworkEvent.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.events;
+
+import java.util.Objects;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+public record VariantNetworkEvent(String sourceVariantId, String targetVariantId, VariantEventType eventType) implements NetworkEvent {
+
+ public enum VariantEventType {
+ CREATED,
+ OVERWRITTEN,
+ REMOVED;
+ }
+
+ public VariantNetworkEvent {
+ Objects.requireNonNull(sourceVariantId);
+ Objects.requireNonNull(eventType);
+ }
+
+ @Override
+ public Type getType() {
+ return Type.VARIANT;
+ }
+}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractConnectable.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractConnectable.java
index 7025a5084bf..756f6be24a2 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractConnectable.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractConnectable.java
@@ -72,7 +72,7 @@ public void remove() {
for (TerminalExt terminal : terminals) {
terminal.removeAsRegulationPoint();
VoltageLevelExt vl = terminal.getVoltageLevel();
- vl.detach(terminal);
+ vl.getTopologyModel().detach(terminal);
}
network.getListeners().notifyAfterRemoval(id);
@@ -181,10 +181,10 @@ protected void move(TerminalExt oldTerminal, TopologyPoint oldTopologyPoint, Str
private void attachTerminal(TerminalExt oldTerminal, TopologyPoint oldTopologyPoint, VoltageLevelExt voltageLevel, TerminalExt terminalExt) {
// first, attach new terminal to connectable and to voltage level of destination, to ensure that the new terminal is valid
terminalExt.setConnectable(this);
- voltageLevel.attach(terminalExt, false);
+ voltageLevel.getTopologyModel().attach(terminalExt, false);
// then we can detach the old terminal, as we now know that the new terminal is valid
- oldTerminal.getVoltageLevel().detach(oldTerminal);
+ oldTerminal.getVoltageLevel().getTopologyModel().detach(oldTerminal);
// replace the old terminal by the new terminal in the connectable
int iSide = terminals.indexOf(oldTerminal);
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractIdentifiable.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractIdentifiable.java
index 284a8361811..837269c396b 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractIdentifiable.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractIdentifiable.java
@@ -199,9 +199,9 @@ public String getProperty(String key, String defaultValue) {
public String setProperty(String key, String value) {
String oldValue = (String) properties.put(key, value);
if (Objects.isNull(oldValue)) {
- getNetwork().getListeners().notifyElementAdded(this, () -> getPropertyStringForNotification(key), value);
+ getNetwork().getListeners().notifyPropertyAdded(this, () -> getPropertyStringForNotification(key), value);
} else {
- getNetwork().getListeners().notifyElementReplaced(this, () -> getPropertyStringForNotification(key), oldValue, value);
+ getNetwork().getListeners().notifyPropertyReplaced(this, () -> getPropertyStringForNotification(key), oldValue, value);
}
return oldValue;
}
@@ -210,7 +210,7 @@ public String setProperty(String key, String value) {
public boolean removeProperty(String key) {
Object oldValue = properties.remove(key);
if (oldValue != null) {
- getNetwork().getListeners().notifyElementRemoved(this, () -> getPropertyStringForNotification(key), oldValue);
+ getNetwork().getListeners().notifyPropertyRemoved(this, () -> getPropertyStringForNotification(key), oldValue);
return true;
}
return false;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTerminal.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTerminal.java
index 58e89278588..0f7d2f336e1 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTerminal.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTerminal.java
@@ -182,7 +182,7 @@ public boolean connect(Predicate isTypeSwitchToOperate) {
String variantId = getVariantManagerHolder().getVariantManager().getVariantId(variantIndex);
boolean connectedBefore = isConnected();
connectable.notifyUpdate("beginConnect", variantId, connectedBefore, null);
- boolean connected = voltageLevel.connect(this, isTypeSwitchToOperate);
+ boolean connected = voltageLevel.getTopologyModel().connect(this, isTypeSwitchToOperate);
boolean connectedAfter = isConnected();
connectable.notifyUpdate("endConnect", variantId, null, connectedAfter);
return connected;
@@ -209,7 +209,7 @@ public boolean disconnect(Predicate isSwitchOpenable) {
String variantId = getVariantManagerHolder().getVariantManager().getVariantId(variantIndex);
boolean disconnectedBefore = !isConnected();
connectable.notifyUpdate("beginDisconnect", variantId, disconnectedBefore, null);
- boolean disconnected = voltageLevel.disconnect(this, isSwitchOpenable);
+ boolean disconnected = voltageLevel.getTopologyModel().disconnect(this, isSwitchOpenable);
boolean disconnectedAfter = !isConnected();
connectable.notifyUpdate("endDisconnect", variantId, null, disconnectedAfter);
return disconnected;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTopologyModel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTopologyModel.java
new file mode 100644
index 00000000000..91079102ccd
--- /dev/null
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTopologyModel.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.impl;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.primitives.Ints;
+import com.powsybl.commons.config.PlatformConfig;
+import com.powsybl.iidm.network.*;
+import com.powsybl.iidm.network.util.ShortIdDictionary;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.stream.Stream;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+abstract class AbstractTopologyModel implements TopologyModel {
+
+ public static final int DEFAULT_NODE_INDEX_LIMIT = 1000;
+
+ public static final int NODE_INDEX_LIMIT = loadNodeIndexLimit(PlatformConfig.defaultConfig());
+
+ protected final VoltageLevelExt voltageLevel;
+
+ protected AbstractTopologyModel(VoltageLevelExt voltageLevel) {
+ this.voltageLevel = Objects.requireNonNull(voltageLevel);
+ }
+
+ protected static int loadNodeIndexLimit(PlatformConfig platformConfig) {
+ return platformConfig
+ .getOptionalModuleConfig("iidm")
+ .map(moduleConfig -> moduleConfig.getIntProperty("node-index-limit", DEFAULT_NODE_INDEX_LIMIT))
+ .orElse(DEFAULT_NODE_INDEX_LIMIT);
+ }
+
+ protected NetworkImpl getNetwork() {
+ return voltageLevel.getNetwork();
+ }
+
+ protected static void addNextTerminals(TerminalExt otherTerminal, List nextTerminals) {
+ Objects.requireNonNull(otherTerminal);
+ Objects.requireNonNull(nextTerminals);
+ Connectable> otherConnectable = otherTerminal.getConnectable();
+ if (otherConnectable instanceof Branch> branch) {
+ if (branch.getTerminal1() == otherTerminal) {
+ nextTerminals.add((TerminalExt) branch.getTerminal2());
+ } else if (branch.getTerminal2() == otherTerminal) {
+ nextTerminals.add((TerminalExt) branch.getTerminal1());
+ } else {
+ throw new IllegalStateException();
+ }
+ } else if (otherConnectable instanceof ThreeWindingsTransformer ttc) {
+ if (ttc.getLeg1().getTerminal() == otherTerminal) {
+ nextTerminals.add((TerminalExt) ttc.getLeg2().getTerminal());
+ nextTerminals.add((TerminalExt) ttc.getLeg3().getTerminal());
+ } else if (ttc.getLeg2().getTerminal() == otherTerminal) {
+ nextTerminals.add((TerminalExt) ttc.getLeg1().getTerminal());
+ nextTerminals.add((TerminalExt) ttc.getLeg3().getTerminal());
+ } else if (ttc.getLeg3().getTerminal() == otherTerminal) {
+ nextTerminals.add((TerminalExt) ttc.getLeg1().getTerminal());
+ nextTerminals.add((TerminalExt) ttc.getLeg2().getTerminal());
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+ }
+
+ public void invalidateCache() {
+ invalidateCache(false);
+ }
+
+ public abstract Iterable getTerminals();
+
+ public abstract Stream getTerminalStream();
+
+ public Iterable getConnectables(Class clazz) {
+ Iterable terminals = getTerminals();
+ return FluentIterable.from(terminals)
+ .transform(Terminal::getConnectable)
+ .filter(clazz)
+ .toSet();
+ }
+
+ public Stream getConnectableStream(Class clazz) {
+ return getTerminalStream()
+ .map(Terminal::getConnectable)
+ .filter(clazz::isInstance)
+ .map(clazz::cast)
+ .distinct();
+ }
+
+ public int getConnectableCount(Class clazz) {
+ return Ints.checkedCast(getConnectableStream(clazz).count());
+ }
+
+ public Iterable getConnectables() {
+ return FluentIterable.from(getTerminals())
+ .transform(Terminal::getConnectable)
+ .toSet();
+ }
+
+ public Stream getConnectableStream() {
+ return getTerminalStream()
+ .map(Terminal::getConnectable)
+ .distinct();
+ }
+
+ public abstract VoltageLevelExt.NodeBreakerViewExt getNodeBreakerView();
+
+ public abstract VoltageLevelExt.BusBreakerViewExt getBusBreakerView();
+
+ public abstract VoltageLevelExt.BusViewExt getBusView();
+
+ public abstract Iterable getSwitches();
+
+ public abstract int getSwitchCount();
+
+ public abstract TopologyKind getTopologyKind();
+
+ public abstract void extendVariantArraySize(int initVariantArraySize, int number, int sourceIndex);
+
+ public abstract void reduceVariantArraySize(int number);
+
+ public abstract void deleteVariantArrayElement(int index);
+
+ public abstract void allocateVariantArrayElement(int[] indexes, int sourceIndex);
+
+ protected abstract void removeTopology();
+
+ public abstract void printTopology();
+
+ public abstract void printTopology(PrintStream out, ShortIdDictionary dict);
+
+ public abstract void exportTopology(Path file) throws IOException;
+
+ public abstract void exportTopology(Writer writer);
+
+ public abstract void exportTopology(Writer writer, Random random);
+}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BatteryAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BatteryAdderImpl.java
index dbe328bfa95..d865fce6cbe 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BatteryAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BatteryAdderImpl.java
@@ -92,7 +92,7 @@ public BatteryImpl add() {
BatteryImpl battery = new BatteryImpl(getNetworkRef(), id, getName(), isFictitious(), targetP, targetQ, minP, maxP);
battery.addTerminal(terminal);
- voltageLevel.attach(terminal, false);
+ voltageLevel.getTopologyModel().attach(terminal, false);
network.getIndex().checkAndAdd(battery);
network.getListeners().notifyCreation(battery);
return battery;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusAdderImpl.java
index dd663b3173f..fa139ab2165 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusAdderImpl.java
@@ -15,9 +15,9 @@
*/
class BusAdderImpl extends AbstractIdentifiableAdder implements BusAdder {
- private final BusBreakerVoltageLevel voltageLevel;
+ private final VoltageLevelExt voltageLevel;
- BusAdderImpl(BusBreakerVoltageLevel voltageLevel) {
+ BusAdderImpl(VoltageLevelExt voltageLevel) {
this.voltageLevel = voltageLevel;
}
@@ -35,7 +35,7 @@ protected String getTypeDescription() {
public ConfiguredBus add() {
String id = checkAndGetUniqueId();
ConfiguredBusImpl bus = new ConfiguredBusImpl(id, getName(), isFictitious(), voltageLevel);
- voltageLevel.addBus(bus);
+ ((BusBreakerTopologyModel) voltageLevel.getTopologyModel()).addBus(bus);
getNetwork().getListeners().notifyCreation(bus);
return bus;
}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerVoltageLevel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerTopologyModel.java
similarity index 91%
rename from iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerVoltageLevel.java
rename to iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerTopologyModel.java
index cc565709153..95d1a89dd97 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerVoltageLevel.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerTopologyModel.java
@@ -13,7 +13,6 @@
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.util.Colors;
import com.powsybl.iidm.network.*;
-import com.powsybl.commons.ref.Ref;
import com.powsybl.iidm.network.util.Identifiables;
import com.powsybl.iidm.network.util.Networks;
import com.powsybl.iidm.network.util.ShortIdDictionary;
@@ -44,11 +43,11 @@
/**
* @author Geoffroy Jamgotchian {@literal }
*/
-class BusBreakerVoltageLevel extends AbstractVoltageLevel {
+class BusBreakerTopologyModel extends AbstractTopologyModel {
private static final boolean DRAW_SWITCH_ID = true;
- private final class SwitchAdderImpl extends AbstractIdentifiableAdder implements BusBreakerView.SwitchAdder {
+ private final class SwitchAdderImpl extends AbstractIdentifiableAdder implements VoltageLevel.BusBreakerView.SwitchAdder {
private String busId1;
@@ -61,7 +60,7 @@ private SwitchAdderImpl() {
@Override
protected NetworkImpl getNetwork() {
- return BusBreakerVoltageLevel.this.getNetwork();
+ return BusBreakerTopologyModel.this.getNetwork();
}
@Override
@@ -70,19 +69,19 @@ protected String getTypeDescription() {
}
@Override
- public BusBreakerView.SwitchAdder setBus1(String bus1) {
+ public VoltageLevel.BusBreakerView.SwitchAdder setBus1(String bus1) {
this.busId1 = bus1;
return this;
}
@Override
- public BusBreakerView.SwitchAdder setBus2(String bus2) {
+ public VoltageLevel.BusBreakerView.SwitchAdder setBus2(String bus2) {
this.busId2 = bus2;
return this;
}
@Override
- public BusBreakerView.SwitchAdder setOpen(boolean open) {
+ public VoltageLevel.BusBreakerView.SwitchAdder setOpen(boolean open) {
this.open = open;
return this;
}
@@ -100,7 +99,7 @@ public Switch add() {
throw new ValidationException(this, "same bus at both ends");
}
- SwitchImpl aSwitch = new SwitchImpl(BusBreakerVoltageLevel.this, id, getName(), isFictitious(), SwitchKind.BREAKER, open, true);
+ SwitchImpl aSwitch = new SwitchImpl(voltageLevel, id, getName(), isFictitious(), SwitchKind.BREAKER, open, true);
addSwitch(aSwitch, busId1, busId2);
getNetwork().getListeners().notifyCreation(aSwitch);
return aSwitch;
@@ -122,7 +121,7 @@ private Integer getVertex(String busId, boolean throwException) {
if (throwException && v == null) {
throw new PowsyblException("Bus " + busId
+ " not found in voltage level "
- + BusBreakerVoltageLevel.this.id);
+ + voltageLevel.getId());
}
return v;
}
@@ -145,7 +144,7 @@ private Integer getEdge(String switchId, boolean throwException) {
if (throwException && e == null) {
throw new PowsyblException("Switch " + switchId
+ " not found in voltage level"
- + BusBreakerVoltageLevel.this.id);
+ + voltageLevel.getId());
}
return e;
}
@@ -215,9 +214,9 @@ protected boolean isBusValid(Set busSet) {
private MergedBus createMergedBus(int busNum, Set busSet) {
String suffix = "_" + busNum;
- String mergedBusId = Identifiables.getUniqueId(BusBreakerVoltageLevel.this.id + suffix, getNetwork().getIndex()::contains);
- String mergedBusName = BusBreakerVoltageLevel.this.name != null ? BusBreakerVoltageLevel.this.name + suffix : null;
- return new MergedBus(mergedBusId, mergedBusName, BusBreakerVoltageLevel.this.fictitious, busSet);
+ String mergedBusId = Identifiables.getUniqueId(voltageLevel.getId() + suffix, getNetwork().getIndex()::contains);
+ String mergedBusName = voltageLevel.getOptionalName().map(name -> name + suffix).orElse(null);
+ return new MergedBus(mergedBusId, mergedBusName, voltageLevel.isFictitious(), busSet);
}
private void updateCache() {
@@ -278,7 +277,7 @@ private MergedBus getMergedBus(String mergedBusId, boolean throwException) {
if (throwException && bus == null) {
throw new PowsyblException("Bus " + mergedBusId
+ " not found in voltage level "
- + BusBreakerVoltageLevel.this.id);
+ + voltageLevel.getId());
}
return bus;
}
@@ -309,11 +308,10 @@ public VariantImpl copy() {
protected final VariantArray variants;
- BusBreakerVoltageLevel(String id, String name, boolean fictitious, SubstationImpl substation, Ref ref,
- Ref subnetworkRef, double nominalV, double lowVoltageLimit, double highVoltageLimit) {
- super(id, name, fictitious, substation, ref, subnetworkRef, nominalV, lowVoltageLimit, highVoltageLimit);
+ BusBreakerTopologyModel(VoltageLevelExt voltageLevel) {
+ super(voltageLevel);
// the ref object of the variant array is the same as the current object
- variants = new VariantArray<>(ref, VariantImpl::new);
+ variants = new VariantArray<>(voltageLevel.getNetworkRef(), VariantImpl::new);
// invalidate topology and connected components
graph.addListener(new UndirectedGraphListener<>() {
@Override
@@ -388,14 +386,14 @@ static PowsyblException createNotSupportedBusBreakerTopologyException() {
return new PowsyblException("Not supported in a bus breaker topology");
}
- private final NodeBreakerViewExt nodeBreakerView = new NodeBreakerViewExt() {
+ private final VoltageLevelExt.NodeBreakerViewExt nodeBreakerView = new VoltageLevelExt.NodeBreakerViewExt() {
@Override
public double getFictitiousP0(int node) {
throw createNotSupportedBusBreakerTopologyException();
}
@Override
- public NodeBreakerView setFictitiousP0(int node, double p0) {
+ public VoltageLevel.NodeBreakerView setFictitiousP0(int node, double p0) {
throw createNotSupportedBusBreakerTopologyException();
}
@@ -405,7 +403,7 @@ public double getFictitiousQ0(int node) {
}
@Override
- public NodeBreakerView setFictitiousQ0(int node, double q0) {
+ public VoltageLevel.NodeBreakerView setFictitiousQ0(int node, double q0) {
throw createNotSupportedBusBreakerTopologyException();
}
@@ -576,11 +574,11 @@ public void traverse(int[] node, TopologyTraverser traverser) {
};
@Override
- public NodeBreakerViewExt getNodeBreakerView() {
+ public VoltageLevelExt.NodeBreakerViewExt getNodeBreakerView() {
return nodeBreakerView;
}
- private final BusBreakerViewExt busBreakerView = new BusBreakerViewExt() {
+ private final VoltageLevelExt.BusBreakerViewExt busBreakerView = new VoltageLevelExt.BusBreakerViewExt() {
@Override
public Iterable getBuses() {
@@ -599,22 +597,22 @@ public int getBusCount() {
@Override
public ConfiguredBus getBus(String id) {
- return BusBreakerVoltageLevel.this.getBus(id, false);
+ return BusBreakerTopologyModel.this.getBus(id, false);
}
@Override
public BusAdder newBus() {
- return new BusAdderImpl(BusBreakerVoltageLevel.this);
+ return new BusAdderImpl(voltageLevel);
}
@Override
public void removeBus(String busId) {
- BusBreakerVoltageLevel.this.removeBus(busId);
+ BusBreakerTopologyModel.this.removeBus(busId);
}
@Override
public void removeAllBuses() {
- BusBreakerVoltageLevel.this.removeAllBuses();
+ BusBreakerTopologyModel.this.removeAllBuses();
}
@Override
@@ -634,12 +632,12 @@ public int getSwitchCount() {
@Override
public void removeSwitch(String switchId) {
- BusBreakerVoltageLevel.this.removeSwitch(switchId);
+ BusBreakerTopologyModel.this.removeSwitch(switchId);
}
@Override
public void removeAllSwitches() {
- BusBreakerVoltageLevel.this.removeAllSwitches();
+ BusBreakerTopologyModel.this.removeAllSwitches();
}
@Override
@@ -671,11 +669,11 @@ public Stream getBusStreamFromBusViewBusId(String mergedBusId) {
@Override
public SwitchImpl getSwitch(String switchId) {
- return BusBreakerVoltageLevel.this.getSwitch(switchId, false);
+ return BusBreakerTopologyModel.this.getSwitch(switchId, false);
}
@Override
- public BusBreakerView.SwitchAdder newSwitch() {
+ public VoltageLevel.BusBreakerView.SwitchAdder newSwitch() {
return new SwitchAdderImpl();
}
@@ -690,11 +688,11 @@ public void traverse(Bus bus, TopologyTraverser traverser) {
};
@Override
- public BusBreakerViewExt getBusBreakerView() {
+ public VoltageLevelExt.BusBreakerViewExt getBusBreakerView() {
return busBreakerView;
}
- private final BusViewExt busView = new BusViewExt() {
+ private final VoltageLevelExt.BusViewExt busView = new VoltageLevelExt.BusViewExt() {
@Override
public Iterable getBuses() {
@@ -719,7 +717,7 @@ public Bus getMergedBus(String configuredBusId) {
};
@Override
- public BusViewExt getBusView() {
+ public VoltageLevelExt.BusViewExt getBusView() {
return busView;
}
@@ -748,7 +746,7 @@ void addBus(ConfiguredBus bus) {
private void removeBus(String busId) {
ConfiguredBus bus = getBus(busId, true);
if (bus.getTerminalCount() > 0) {
- throw new ValidationException(this, "Cannot remove bus "
+ throw new ValidationException(voltageLevel, "Cannot remove bus "
+ bus.getId() + " because of connectable equipments");
}
// TODO improve check efficency
@@ -777,11 +775,11 @@ private void removeBus(String busId) {
private void removeAllBuses() {
if (graph.getEdgeCount() > 0) {
- throw new ValidationException(this, "Cannot remove all buses because there is still some switches");
+ throw new ValidationException(voltageLevel, "Cannot remove all buses because there is still some switches");
}
for (ConfiguredBus bus : graph.getVerticesObj()) {
if (bus.getTerminalCount() > 0) {
- throw new ValidationException(this, "Cannot remove bus "
+ throw new ValidationException(voltageLevel, "Cannot remove bus "
+ bus.getId() + " because of connected equipments");
}
}
@@ -811,7 +809,7 @@ private void removeSwitch(String switchId) {
Integer e = switches.get(switchId);
if (e == null) {
throw new PowsyblException("Switch '" + switchId
- + "' not found in voltage level '" + id + "'");
+ + "' not found in voltage level '" + voltageLevel.getId() + "'");
}
NetworkImpl network = getNetwork();
SwitchImpl aSwitch = graph.getEdgeObject(e);
@@ -845,7 +843,7 @@ private void removeAllSwitches() {
private void checkTerminal(TerminalExt terminal) {
if (!(terminal instanceof BusTerminal)) {
throw new ValidationException(terminal.getConnectable(),
- "voltage level " + BusBreakerVoltageLevel.this.id + " has a bus/breaker topology"
+ "voltage level " + voltageLevel.getId() + " has a bus/breaker topology"
+ ", a bus connection should be specified instead of a node connection");
}
@@ -863,7 +861,7 @@ public void attach(final TerminalExt terminal, boolean test) {
return;
}
// create the link terminal -> voltage level
- terminal.setVoltageLevel(this);
+ terminal.setVoltageLevel(voltageLevel);
// create the link bus -> terminal
String connectableBusId = ((BusTerminal) terminal).getConnectableBusId();
@@ -891,7 +889,7 @@ public void detach(final TerminalExt terminal) {
getNetwork().getVariantManager().forEachVariant(() -> {
connectableBus.removeTerminal((BusTerminal) terminal);
- ((BusTerminal) terminal).setConnectableBusId(null);
+ ((BusTerminal) terminal).unsetConnectableBusId();
invalidateCache();
});
@@ -899,8 +897,7 @@ public void detach(final TerminalExt terminal) {
terminal.setVoltageLevel(null);
}
- @Override
- public boolean connect(TerminalExt terminal) {
+ boolean connect(TerminalExt terminal) {
if (!(terminal instanceof BusTerminal)) {
throw new IllegalStateException("Given TerminalExt not supported: " + terminal.getClass().getName());
}
@@ -923,8 +920,7 @@ public boolean connect(TerminalExt terminal, Predicate super SwitchImpl> isTyp
return connect(terminal);
}
- @Override
- public boolean disconnect(TerminalExt terminal) {
+ boolean disconnect(TerminalExt terminal) {
if (!(terminal instanceof BusTerminal)) {
throw new IllegalStateException("Given TerminalExt not supported: " + terminal.getClass().getName());
}
@@ -1021,25 +1017,21 @@ private static TraverseResult getTraverserResult(Set visitedTerminals,
@Override
public void extendVariantArraySize(int initVariantArraySize, int number, int sourceIndex) {
- super.extendVariantArraySize(initVariantArraySize, number, sourceIndex);
variants.push(number, () -> variants.copy(sourceIndex));
}
@Override
public void reduceVariantArraySize(int number) {
- super.reduceVariantArraySize(number);
variants.pop(number);
}
@Override
public void deleteVariantArrayElement(int index) {
- super.deleteVariantArrayElement(index);
variants.delete(index);
}
@Override
public void allocateVariantArrayElement(int[] indexes, final int sourceIndex) {
- super.allocateVariantArrayElement(indexes, sourceIndex);
variants.allocate(indexes, () -> variants.copy(sourceIndex));
}
@@ -1057,7 +1049,7 @@ public void printTopology() {
@Override
public void printTopology(PrintStream out, ShortIdDictionary dict) {
out.println("-------------------------------------------------------------");
- out.println("Topology of " + BusBreakerVoltageLevel.this.id);
+ out.println("Topology of " + voltageLevel.getId());
Function vertexToString = bus -> {
StringBuilder builder = new StringBuilder();
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusTerminal.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusTerminal.java
index 80e92cf69f8..383023ceea8 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusTerminal.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusTerminal.java
@@ -26,10 +26,14 @@
*/
class BusTerminal extends AbstractTerminal {
+ private BusBreakerTopologyModel getTopologyModel() {
+ return (BusBreakerTopologyModel) voltageLevel.getTopologyModel();
+ }
+
private final NodeBreakerView nodeBreakerView = new NodeBreakerView() {
@Override
public int getNode() {
- throw BusBreakerVoltageLevel.createNotSupportedBusBreakerTopologyException();
+ throw BusBreakerTopologyModel.createNotSupportedBusBreakerTopologyException();
}
@Override
@@ -56,7 +60,7 @@ public ConfiguredBus getConnectableBus() {
if (removed) {
throw new PowsyblException(CANNOT_ACCESS_BUS_REMOVED_EQUIPMENT + connectable.id);
}
- return ((BusBreakerVoltageLevel) voltageLevel).getBus(getConnectableBusId(), true);
+ return getTopologyModel().getBus(getConnectableBusId(), true);
}
@Override
@@ -65,15 +69,15 @@ public void setConnectableBus(String busId) {
throw new PowsyblException(UNMODIFIABLE_REMOVED_EQUIPMENT + connectable.id);
}
Objects.requireNonNull(busId);
- BusBreakerVoltageLevel vl = (BusBreakerVoltageLevel) voltageLevel;
+ BusBreakerTopologyModel topologyModel = getTopologyModel();
// Assert that the new bus exists
- vl.getBus(busId, true);
+ topologyModel.getBus(busId, true);
- vl.detach(BusTerminal.this);
+ topologyModel.detach(BusTerminal.this);
int variantIndex = getVariantManagerHolder().getVariantIndex();
String oldValue = BusTerminal.this.connectableBusId.set(variantIndex, busId);
- vl.attach(BusTerminal.this, false);
+ topologyModel.attach(BusTerminal.this, false);
String variantId = getVariantManagerHolder().getVariantManager().getVariantId(variantIndex);
getConnectable().notifyUpdate("connectableBusId", variantId, oldValue, busId);
}
@@ -108,8 +112,8 @@ public MergedBus getConnectableBus() {
if (removed) {
throw new PowsyblException(CANNOT_ACCESS_BUS_REMOVED_EQUIPMENT + connectable.id);
}
- ConfiguredBus bus = ((BusBreakerVoltageLevel) voltageLevel).getBus(getConnectableBusId(), true);
- return ((BusBreakerVoltageLevel) voltageLevel).calculatedBusTopology.getMergedBus(bus);
+ ConfiguredBus bus = getTopologyModel().getBus(getConnectableBusId(), true);
+ return getTopologyModel().calculatedBusTopology.getMergedBus(bus);
}
};
@@ -132,14 +136,12 @@ public MergedBus getConnectableBus() {
}
}
- void setConnectableBusId(String connectableBusId) {
+ void unsetConnectableBusId() {
if (removed) {
throw new PowsyblException(UNMODIFIABLE_REMOVED_EQUIPMENT + connectable.id);
}
int variantIndex = getVariantManagerHolder().getVariantIndex();
- String oldValue = this.connectableBusId.set(variantIndex, connectableBusId);
- String variantId = getVariantManagerHolder().getVariantManager().getVariantId(variantIndex);
- getConnectable().notifyUpdate("connectableBusId", variantId, oldValue, connectableBusId);
+ this.connectableBusId.set(variantIndex, null);
}
String getConnectableBusId() {
@@ -172,7 +174,7 @@ public boolean traverse(TopologyTraverser traverser, Set visitedTermin
if (removed) {
throw new PowsyblException(String.format("Associated equipment %s is removed", connectable.id));
}
- return ((BusBreakerVoltageLevel) voltageLevel).traverse(this, traverser, visitedTerminals, traversalType);
+ return getTopologyModel().traverse(this, traverser, visitedTerminals, traversalType);
}
@Override
@@ -185,7 +187,7 @@ public void traverse(TopologyTraverser traverser, TraversalType traversalType) {
if (removed) {
throw new PowsyblException(String.format("Associated equipment %s is removed", connectable.id));
}
- ((BusBreakerVoltageLevel) voltageLevel).traverse(this, traverser, traversalType);
+ getTopologyModel().traverse(this, traverser, traversalType);
}
@Override
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusbarSectionAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusbarSectionAdderImpl.java
index 1bd5708ecc7..0e513c7198b 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusbarSectionAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusbarSectionAdderImpl.java
@@ -50,7 +50,7 @@ public BusbarSection add() {
TerminalExt terminal = new NodeTerminal(voltageLevel.getNetworkRef(), null, node);
BusbarSectionImpl section = new BusbarSectionImpl(voltageLevel.getNetworkRef(), id, getName(), isFictitious());
section.addTerminal(terminal);
- voltageLevel.attach(terminal, false);
+ voltageLevel.getTopologyModel().attach(terminal, false);
getNetwork().getIndex().checkAndAdd(section);
getNetwork().getListeners().notifyCreation(section);
return section;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/CalculatedBusImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/CalculatedBusImpl.java
index bf1cd008d4c..60907282a5b 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/CalculatedBusImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/CalculatedBusImpl.java
@@ -29,7 +29,7 @@ class CalculatedBusImpl extends AbstractBus implements CalculatedBus {
private NodeTerminal terminalRef;
- CalculatedBusImpl(String id, String name, boolean fictitious, NodeBreakerVoltageLevel voltageLevel, TIntArrayList nodes, List terminals, Function getBusFromTerminal) {
+ CalculatedBusImpl(String id, String name, boolean fictitious, VoltageLevelExt voltageLevel, TIntArrayList nodes, List terminals, Function getBusFromTerminal) {
super(id, name, fictitious, voltageLevel);
this.terminals = Objects.requireNonNull(terminals);
this.getBusFromTerminal = Objects.requireNonNull(getBusFromTerminal);
@@ -47,7 +47,7 @@ class CalculatedBusImpl extends AbstractBus implements CalculatedBus {
* @param terminals The terminals belong to this bus
* @return The first terminal of the {@code terminals} list, or a terminal which belongs to an equivalent "electrical" bus.
*/
- private static NodeTerminal findTerminal(NodeBreakerVoltageLevel voltageLevel, TIntArrayList nodes, List terminals) {
+ private static NodeTerminal findTerminal(VoltageLevelExt voltageLevel, TIntArrayList nodes, List terminals) {
if (!terminals.isEmpty()) {
return terminals.get(0);
}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ConnectDisconnectUtil.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ConnectDisconnectUtil.java
index e178a176e2e..861c4883022 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ConnectDisconnectUtil.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ConnectDisconnectUtil.java
@@ -19,6 +19,7 @@
import java.util.function.Predicate;
import static com.powsybl.iidm.network.TopologyKind.BUS_BREAKER;
+import static com.powsybl.iidm.network.TopologyKind.NODE_BREAKER;
/**
* @author Nicolas Rol {@literal }
@@ -63,8 +64,9 @@ static boolean connectAllTerminals(Identifiable> identifiable, List extends
}
// If it's a node-breaker terminal, the switches to connect are added to a set
- if (terminal.getVoltageLevel() instanceof NodeBreakerVoltageLevel nodeBreakerVoltageLevel) {
- isNowConnected = nodeBreakerVoltageLevel.getConnectingSwitches(terminal, isTypeSwitchToOperate, switchForDisconnection);
+ if (terminal.getVoltageLevel().getTopologyKind() == NODE_BREAKER) {
+ NodeBreakerTopologyModel topologyModel = (NodeBreakerTopologyModel) ((VoltageLevelImpl) terminal.getVoltageLevel()).getTopologyModel();
+ isNowConnected = topologyModel.getConnectingSwitches(terminal, isTypeSwitchToOperate, switchForDisconnection);
}
// If it's a bus-breaker terminal, there is nothing to do
@@ -127,10 +129,12 @@ static boolean disconnectAllTerminals(Identifiable> identifiable, List exten
isAlreadyDisconnected = false;
// If it's a node-breaker terminal, the switches to disconnect are added to a set
- if (terminal.getVoltageLevel() instanceof NodeBreakerVoltageLevel nodeBreakerVoltageLevel
- && !nodeBreakerVoltageLevel.getDisconnectingSwitches(terminal, isSwitchOpenable, switchForDisconnection)) {
- // Exit if the terminal cannot be disconnected
- return false;
+ if (terminal.getVoltageLevel().getTopologyKind() == NODE_BREAKER) {
+ NodeBreakerTopologyModel topologyModel = (NodeBreakerTopologyModel) ((VoltageLevelImpl) terminal.getVoltageLevel()).getTopologyModel();
+ if (!topologyModel.getDisconnectingSwitches(terminal, isSwitchOpenable, switchForDisconnection)) {
+ // Exit if the terminal cannot be disconnected
+ return false;
+ }
}
// If it's a bus-breaker terminal, there is nothing to do
}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/DanglingLineAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/DanglingLineAdderImpl.java
index 7b5f0f8c4d7..3075da989ce 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/DanglingLineAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/DanglingLineAdderImpl.java
@@ -114,7 +114,7 @@ public DanglingLineImpl add() {
DanglingLineImpl danglingLine = new DanglingLineImpl(network.getRef(), id, getName(), isFictitious(), p0, q0, r, x, g, b, pairingKey, generation);
danglingLine.addTerminal(terminal);
- voltageLevel.attach(terminal, false);
+ voltageLevel.getTopologyModel().attach(terminal, false);
network.getIndex().checkAndAdd(danglingLine);
network.getListeners().notifyCreation(danglingLine);
return danglingLine;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorAdderImpl.java
index 45104f441b1..54afbc6133c 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorAdderImpl.java
@@ -131,7 +131,7 @@ id, getName(), isFictitious(), energySource,
targetP, targetQ, targetV,
ratedS, isCondenser);
generator.addTerminal(terminal);
- voltageLevel.attach(terminal, false);
+ voltageLevel.getTopologyModel().attach(terminal, false);
network.getIndex().checkAndAdd(generator);
network.getListeners().notifyCreation(generator);
return generator;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GroundAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GroundAdderImpl.java
index 02f4062dda5..fde2432c0ea 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GroundAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GroundAdderImpl.java
@@ -30,7 +30,7 @@ public GroundImpl add() {
TerminalExt terminal = checkAndGetTerminal();
GroundImpl ground = new GroundImpl(getNetworkRef(), id, getName());
ground.addTerminal(terminal);
- voltageLevel.attach(terminal, false);
+ voltageLevel.getTopologyModel().attach(terminal, false);
network.getIndex().checkAndAdd(ground);
network.getListeners().notifyCreation(ground);
return ground;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LccConverterStationAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LccConverterStationAdderImpl.java
index 37dedaa6be1..f1dd4524b39 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LccConverterStationAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LccConverterStationAdderImpl.java
@@ -43,7 +43,7 @@ public LccConverterStation add() {
LccConverterStationImpl converterStation
= new LccConverterStationImpl(getNetworkRef(), id, name, isFictitious(), getLossFactor(), powerFactor);
converterStation.addTerminal(terminal);
- getVoltageLevel().attach(terminal, false);
+ getVoltageLevel().getTopologyModel().attach(terminal, false);
getNetwork().getIndex().checkAndAdd(converterStation);
getNetwork().getListeners().notifyCreation(converterStation);
return converterStation;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LineAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LineAdderImpl.java
index e9701b6b432..aeeac6d7d60 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LineAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LineAdderImpl.java
@@ -111,11 +111,11 @@ public LineImpl add() {
line.addTerminal(terminal2);
// check that the line is attachable on both side
- voltageLevel1.attach(terminal1, true);
- voltageLevel2.attach(terminal2, true);
+ voltageLevel1.getTopologyModel().attach(terminal1, true);
+ voltageLevel2.getTopologyModel().attach(terminal2, true);
- voltageLevel1.attach(terminal1, false);
- voltageLevel2.attach(terminal2, false);
+ voltageLevel1.getTopologyModel().attach(terminal1, false);
+ voltageLevel2.getTopologyModel().attach(terminal2, false);
network.getIndex().checkAndAdd(line);
getNetwork().getListeners().notifyCreation(line);
return line;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LoadAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LoadAdderImpl.java
index 9d3c36a14d4..10518a388b2 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LoadAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/LoadAdderImpl.java
@@ -77,7 +77,7 @@ public LoadImpl add() {
model.setLoad(load);
}
load.addTerminal(terminal);
- voltageLevel.attach(terminal, false);
+ voltageLevel.getTopologyModel().attach(terminal, false);
network.getIndex().checkAndAdd(load);
network.getListeners().notifyCreation(load);
return load;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java
index de6ddb2c60a..344f3e246bf 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java
@@ -336,20 +336,17 @@ public VoltageLevelAdder newVoltageLevel() {
@Override
public Iterable getVoltageLevels() {
- return Iterables.concat(index.getAll(BusBreakerVoltageLevel.class),
- index.getAll(NodeBreakerVoltageLevel.class));
+ return Collections.unmodifiableCollection(index.getAll(VoltageLevelImpl.class));
}
@Override
public Stream getVoltageLevelStream() {
- return Stream.concat(index.getAll(BusBreakerVoltageLevel.class).stream(),
- index.getAll(NodeBreakerVoltageLevel.class).stream());
+ return index.getAll(VoltageLevelImpl.class).stream().map(Function.identity());
}
@Override
public int getVoltageLevelCount() {
- return index.getAll(BusBreakerVoltageLevel.class).size()
- + index.getAll(NodeBreakerVoltageLevel.class).size();
+ return index.getAll(VoltageLevelImpl.class).size();
}
@Override
@@ -1259,15 +1256,13 @@ public ValidationLevel getValidationLevel() {
}
@Override
- public Network setMinimumAcceptableValidationLevel(ValidationLevel validationLevel) {
- Objects.requireNonNull(validationLevel);
- if (this.validationLevel == null) {
- this.validationLevel = ValidationUtil.validate(Collections.unmodifiableCollection(index.getAll()), false, false, this.validationLevel, ReportNode.NO_OP);
+ public Network setMinimumAcceptableValidationLevel(ValidationLevel minLevel) {
+ Objects.requireNonNull(minLevel);
+ ValidationLevel currentLevel = getValidationLevel();
+ if (currentLevel.compareTo(minLevel) < 0) {
+ throw new ValidationException(this, "Network should be corrected in order to correspond to validation level " + minLevel);
}
- if (this.validationLevel.compareTo(validationLevel) < 0) {
- throw new ValidationException(this, "Network should be corrected in order to correspond to validation level " + validationLevel);
- }
- this.minValidationLevel = validationLevel;
+ this.minValidationLevel = minLevel;
return this;
}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkListenerList.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkListenerList.java
index fda2be587ab..91f127a9b04 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkListenerList.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkListenerList.java
@@ -37,23 +37,13 @@ void remove(NetworkListener listener) {
void notifyUpdate(Identifiable> identifiable, Supplier attribute, Object oldValue, Object newValue) {
if (!listeners.isEmpty() && !Objects.equals(oldValue, newValue)) {
- notifyUpdateListeners(identifiable, attribute.get(), oldValue, newValue);
+ notifyUpdateListeners(identifiable, attribute.get(), null, oldValue, newValue);
}
}
void notifyUpdate(Identifiable> identifiable, String attribute, Object oldValue, Object newValue) {
if (!listeners.isEmpty() && !Objects.equals(oldValue, newValue)) {
- notifyUpdateListeners(identifiable, attribute, oldValue, newValue);
- }
- }
-
- private void notifyUpdateListeners(Identifiable> identifiable, String attribute, Object oldValue, Object newValue) {
- for (NetworkListener listener : listeners) {
- try {
- listener.onUpdate(identifiable, attribute, oldValue, newValue);
- } catch (Exception t) {
- LOGGER.error(t.toString(), t);
- }
+ notifyUpdateListeners(identifiable, attribute, null, oldValue, newValue);
}
}
@@ -109,16 +99,16 @@ public void notifyExtensionAfterRemoval(Identifiable> identifiable, String ext
}
}
- public void notifyExtensionUpdate(Extension> extension, String attribute, Object oldValue, Object newValue) {
+ public void notifyExtensionUpdate(Extension> extension, String attribute, String variantId, Object oldValue, Object newValue) {
if (!listeners.isEmpty() && !Objects.equals(oldValue, newValue)) {
- notifyExtensionUpdateListeners(extension, attribute, oldValue, newValue);
+ notifyExtensionUpdateListeners(extension, attribute, variantId, oldValue, newValue);
}
}
- private void notifyExtensionUpdateListeners(Extension> extension, String attribute, Object oldValue, Object newValue) {
+ private void notifyExtensionUpdateListeners(Extension> extension, String attribute, String variantId, Object oldValue, Object newValue) {
for (NetworkListener listener : listeners) {
try {
- listener.onExtensionUpdate(extension, attribute, oldValue, newValue);
+ listener.onExtensionUpdate(extension, attribute, variantId, oldValue, newValue);
} catch (Exception t) {
LOGGER.error(t.toString(), t);
}
@@ -155,48 +145,48 @@ void notifyAfterRemoval(String id) {
}
}
- void notifyElementAdded(Identifiable> identifiable, Supplier attribute, Object newValue) {
+ void notifyPropertyAdded(Identifiable> identifiable, Supplier attribute, Object newValue) {
if (!listeners.isEmpty()) {
- notifyElementAdded(identifiable, attribute.get(), newValue);
+ notifyPropertyAdded(identifiable, attribute.get(), newValue);
}
}
- void notifyElementAdded(Identifiable> identifiable, String attribute, Object newValue) {
+ void notifyPropertyAdded(Identifiable> identifiable, String attribute, Object newValue) {
for (NetworkListener listener : listeners) {
try {
- listener.onElementAdded(identifiable, attribute, newValue);
+ listener.onPropertyAdded(identifiable, attribute, newValue);
} catch (Exception t) {
LOGGER.error(t.toString(), t);
}
}
}
- void notifyElementReplaced(Identifiable> identifiable, Supplier attribute, Object oldValue, Object newValue) {
+ void notifyPropertyReplaced(Identifiable> identifiable, Supplier attribute, Object oldValue, Object newValue) {
if (!listeners.isEmpty() && !Objects.equals(oldValue, newValue)) {
- notifyElementReplaced(identifiable, attribute.get(), oldValue, newValue);
+ notifyPropertyReplaced(identifiable, attribute.get(), oldValue, newValue);
}
}
- void notifyElementReplaced(Identifiable> identifiable, String attribute, Object oldValue, Object newValue) {
+ void notifyPropertyReplaced(Identifiable> identifiable, String attribute, Object oldValue, Object newValue) {
for (NetworkListener listener : listeners) {
try {
- listener.onElementReplaced(identifiable, attribute, oldValue, newValue);
+ listener.onPropertyReplaced(identifiable, attribute, oldValue, newValue);
} catch (Exception t) {
LOGGER.error(t.toString(), t);
}
}
}
- void notifyElementRemoved(Identifiable> identifiable, Supplier attribute, Object oldValue) {
+ void notifyPropertyRemoved(Identifiable> identifiable, Supplier attribute, Object oldValue) {
if (!listeners.isEmpty()) {
- notifyElementRemoved(identifiable, attribute.get(), oldValue);
+ notifyPropertyRemoved(identifiable, attribute.get(), oldValue);
}
}
- void notifyElementRemoved(Identifiable> identifiable, String attribute, Object oldValue) {
+ void notifyPropertyRemoved(Identifiable> identifiable, String attribute, Object oldValue) {
for (NetworkListener listener : listeners) {
try {
- listener.onElementRemoved(identifiable, attribute, oldValue);
+ listener.onPropertyRemoved(identifiable, attribute, oldValue);
} catch (Exception t) {
LOGGER.error(t.toString(), t);
}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerTopologyModel.java
similarity index 91%
rename from iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java
rename to iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerTopologyModel.java
index f8483387a6d..e58a76de39c 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerTopologyModel.java
@@ -14,8 +14,8 @@
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.util.Colors;
import com.powsybl.iidm.network.*;
+import com.powsybl.iidm.network.VoltageLevel.NodeBreakerView.InternalConnectionAdder;
import com.powsybl.iidm.network.VoltageLevel.NodeBreakerView.SwitchAdder;
-import com.powsybl.commons.ref.Ref;
import com.powsybl.iidm.network.util.Identifiables;
import com.powsybl.iidm.network.util.ShortIdDictionary;
import com.powsybl.iidm.network.util.SwitchPredicates;
@@ -50,9 +50,9 @@
/**
* @author Geoffroy Jamgotchian {@literal }
*/
-class NodeBreakerVoltageLevel extends AbstractVoltageLevel {
+class NodeBreakerTopologyModel extends AbstractTopologyModel {
- private static final Logger LOGGER = LoggerFactory.getLogger(NodeBreakerVoltageLevel.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(NodeBreakerTopologyModel.class);
private static final String WRONG_TERMINAL_TYPE_EXCEPTION_MESSAGE = "Given TerminalExt not supported: ";
@@ -85,7 +85,7 @@ public VariantImpl copy() {
private final VariantArray variants;
- private final class SwitchAdderImpl extends AbstractIdentifiableAdder implements NodeBreakerView.SwitchAdder {
+ private final class SwitchAdderImpl extends AbstractIdentifiableAdder implements SwitchAdder {
private Integer node1;
@@ -107,7 +107,7 @@ private SwitchAdderImpl(SwitchKind kind) {
@Override
protected NetworkImpl getNetwork() {
- return NodeBreakerVoltageLevel.this.getNetwork();
+ return NodeBreakerTopologyModel.this.getNetwork();
}
@Override
@@ -116,19 +116,19 @@ protected String getTypeDescription() {
}
@Override
- public NodeBreakerView.SwitchAdder setNode1(int node1) {
+ public SwitchAdder setNode1(int node1) {
this.node1 = node1;
return this;
}
@Override
- public NodeBreakerView.SwitchAdder setNode2(int node2) {
+ public SwitchAdder setNode2(int node2) {
this.node2 = node2;
return this;
}
@Override
- public NodeBreakerView.SwitchAdder setKind(SwitchKind kind) {
+ public SwitchAdder setKind(SwitchKind kind) {
if (kind == null) {
throw new NullPointerException("kind is null");
}
@@ -142,13 +142,13 @@ public SwitchAdder setKind(String kind) {
}
@Override
- public NodeBreakerView.SwitchAdder setOpen(boolean open) {
+ public SwitchAdder setOpen(boolean open) {
this.open = open;
return this;
}
@Override
- public NodeBreakerView.SwitchAdder setRetained(boolean retained) {
+ public SwitchAdder setRetained(boolean retained) {
this.retained = retained;
return this;
}
@@ -168,7 +168,7 @@ public Switch add() {
if (kind == null) {
throw new ValidationException(this, "kind is not set");
}
- SwitchImpl aSwitch = new SwitchImpl(NodeBreakerVoltageLevel.this, id, getName(), isFictitious(), kind, open, retained);
+ SwitchImpl aSwitch = new SwitchImpl(voltageLevel, id, getName(), isFictitious(), kind, open, retained);
graph.addVertexIfNotPresent(node1);
graph.addVertexIfNotPresent(node2);
graph.addEdge(node1, node2, aSwitch);
@@ -177,7 +177,7 @@ public Switch add() {
}
- private final class InternalConnectionAdderImpl implements NodeBreakerView.InternalConnectionAdder {
+ private final class InternalConnectionAdderImpl implements InternalConnectionAdder {
private Integer node1;
@@ -187,13 +187,13 @@ private InternalConnectionAdderImpl() {
}
@Override
- public NodeBreakerView.InternalConnectionAdder setNode1(int node1) {
+ public InternalConnectionAdder setNode1(int node1) {
this.node1 = node1;
return this;
}
@Override
- public NodeBreakerView.InternalConnectionAdder setNode2(int node2) {
+ public InternalConnectionAdder setNode2(int node2) {
this.node2 = node2;
return this;
}
@@ -201,10 +201,10 @@ public NodeBreakerView.InternalConnectionAdder setNode2(int node2) {
@Override
public void add() {
if (node1 == null) {
- throw new ValidationException(NodeBreakerVoltageLevel.this, "first connection node is not set");
+ throw new ValidationException(voltageLevel, "first connection node is not set");
}
if (node2 == null) {
- throw new ValidationException(NodeBreakerVoltageLevel.this, "second connection node is not set");
+ throw new ValidationException(voltageLevel, "second connection node is not set");
}
graph.addVertexIfNotPresent(node1);
graph.addVertexIfNotPresent(node2);
@@ -274,7 +274,7 @@ private void traverse(int n, boolean[] encountered, Predicate termin
}, encountered);
// check that the component is a bus
- String busId = Identifiables.getUniqueId(NAMING_STRATEGY.getId(NodeBreakerVoltageLevel.this, nodes), getNetwork().getIndex()::contains);
+ String busId = Identifiables.getUniqueId(NAMING_STRATEGY.getId(voltageLevel, nodes), getNetwork().getIndex()::contains);
CopyOnWriteArrayList terminals = new CopyOnWriteArrayList<>();
for (int i = 0; i < nodes.size(); i++) {
int n2 = nodes.getQuick(i);
@@ -291,9 +291,9 @@ private void traverse(int n, boolean[] encountered, Predicate termin
private void addBus(TIntArrayList nodes, Map id2bus, CalculatedBus[] node2bus,
String busId, CopyOnWriteArrayList terminals) {
- String busName = NAMING_STRATEGY.getName(NodeBreakerVoltageLevel.this, nodes);
+ String busName = NAMING_STRATEGY.getName(voltageLevel, nodes);
Function getBusFromTerminal = getBusChecker() == CALCULATED_BUS_CHECKER ? t -> t.getBusView().getBus() : t -> t.getBusBreakerView().getBus();
- CalculatedBusImpl bus = new CalculatedBusImpl(busId, busName, NodeBreakerVoltageLevel.this.fictitious, NodeBreakerVoltageLevel.this, nodes, terminals, getBusFromTerminal);
+ CalculatedBusImpl bus = new CalculatedBusImpl(busId, busName, voltageLevel.isFictitious(), voltageLevel, nodes, terminals, getBusFromTerminal);
id2bus.put(busId, bus);
for (int i = 0; i < nodes.size(); i++) {
node2bus[nodes.getQuick(i)] = bus;
@@ -304,7 +304,7 @@ protected void updateCache(final Predicate terminate) {
if (busCache != null) {
return;
}
- LOGGER.trace("Update bus topology of voltage level {}", NodeBreakerVoltageLevel.this.id);
+ LOGGER.trace("Update bus topology of voltage level {}", voltageLevel.getId());
Map id2bus = new LinkedHashMap<>();
CalculatedBus[] node2bus = new CalculatedBus[graph.getVertexCapacity()];
boolean[] encountered = new boolean[graph.getVertexCapacity()];
@@ -497,28 +497,27 @@ public boolean isValid(UndirectedGraph extends TerminalExt, SwitchImpl> graph,
private interface BusNamingStrategy {
- String getId(NodeBreakerVoltageLevel voltageLevel, TIntArrayList nodes);
+ String getId(VoltageLevel voltageLevel, TIntArrayList nodes);
- String getName(NodeBreakerVoltageLevel voltageLevel, TIntArrayList nodes);
+ String getName(VoltageLevel voltageLevel, TIntArrayList nodes);
}
private static final class LowestNodeNumberBusNamingStrategy implements BusNamingStrategy {
@Override
- public String getId(NodeBreakerVoltageLevel voltageLevel, TIntArrayList nodes) {
+ public String getId(VoltageLevel voltageLevel, TIntArrayList nodes) {
return voltageLevel.getId() + "_" + nodes.min();
}
@Override
- public String getName(NodeBreakerVoltageLevel voltageLevel, TIntArrayList nodes) {
- return voltageLevel.name != null ? voltageLevel.name + "_" + nodes.min() : null;
+ public String getName(VoltageLevel voltageLevel, TIntArrayList nodes) {
+ return voltageLevel.getOptionalName().map(name -> name + "_" + nodes.min()).orElse(null);
}
}
- NodeBreakerVoltageLevel(String id, String name, boolean fictitious, SubstationImpl substation, Ref ref,
- Ref subnetworkRef, double nominalV, double lowVoltageLimit, double highVoltageLimit) {
- super(id, name, fictitious, substation, ref, subnetworkRef, nominalV, lowVoltageLimit, highVoltageLimit);
- variants = new VariantArray<>(ref, VariantImpl::new);
+ NodeBreakerTopologyModel(VoltageLevelExt voltageLevel) {
+ super(voltageLevel);
+ variants = new VariantArray<>(voltageLevel.getNetworkRef(), VariantImpl::new);
graph.addListener(new DefaultUndirectedGraphListener<>() {
private static final String INTERNAL_CONNECTION = "internalConnection";
@@ -531,7 +530,7 @@ public void edgeAdded(int e, SwitchImpl aSwitch) {
switches.put(aSwitch.getId(), e);
network.getListeners().notifyCreation(aSwitch);
} else {
- network.getListeners().notifyElementAdded(NodeBreakerVoltageLevel.this, INTERNAL_CONNECTION, null);
+ network.getListeners().notifyPropertyAdded(voltageLevel, INTERNAL_CONNECTION, null);
}
invalidateCache();
}
@@ -553,7 +552,7 @@ public void edgeRemoved(int e, SwitchImpl aSwitch) {
switches.remove(switchId);
network.getListeners().notifyAfterRemoval(switchId);
} else {
- network.getListeners().notifyElementRemoved(NodeBreakerVoltageLevel.this, INTERNAL_CONNECTION, null);
+ network.getListeners().notifyPropertyRemoved(voltageLevel, INTERNAL_CONNECTION, null);
}
}
@@ -570,7 +569,7 @@ public void allEdgesRemoved(Collection aSwitches) {
if (ss != null) {
network.getIndex().remove(ss);
} else {
- network.getListeners().notifyElementRemoved(NodeBreakerVoltageLevel.this, INTERNAL_CONNECTION, null);
+ network.getListeners().notifyPropertyRemoved(voltageLevel, INTERNAL_CONNECTION, null);
}
});
switches.clear();
@@ -627,7 +626,7 @@ CalculatedBusTopology getCalculatedBusTopology() {
return variants.get().calculatedBusTopology;
}
- private final NodeBreakerViewExt nodeBreakerView = new NodeBreakerViewExt() {
+ private final VoltageLevelExt.NodeBreakerViewExt nodeBreakerView = new VoltageLevelExt.NodeBreakerViewExt() {
private final TIntObjectMap fictitiousP0ByNode = TCollections.synchronizedMap(new TIntObjectHashMap<>());
private final TIntObjectMap fictitiousQ0ByNode = TCollections.synchronizedMap(new TIntObjectHashMap<>());
@@ -642,9 +641,9 @@ public double getFictitiousP0(int node) {
}
@Override
- public NodeBreakerView setFictitiousP0(int node, double p0) {
+ public VoltageLevel.NodeBreakerView setFictitiousP0(int node, double p0) {
if (Double.isNaN(p0)) {
- throw new ValidationException(NodeBreakerVoltageLevel.this, "undefined value cannot be set as fictitious p0");
+ throw new ValidationException(voltageLevel, "undefined value cannot be set as fictitious p0");
}
TDoubleArrayList p0ByVariant = fictitiousP0ByNode.get(node);
if (p0ByVariant == null) {
@@ -660,7 +659,7 @@ public NodeBreakerView setFictitiousP0(int node, double p0) {
int variantIndex = getNetwork().getVariantIndex();
double oldValue = p0ByVariant.set(getNetwork().getVariantIndex(), p0);
String variantId = getNetwork().getVariantManager().getVariantId(variantIndex);
- getNetwork().getListeners().notifyUpdate(NodeBreakerVoltageLevel.this, "fictitiousP0", variantId, oldValue, p0);
+ getNetwork().getListeners().notifyUpdate(voltageLevel, "fictitiousP0", variantId, oldValue, p0);
TIntSet toRemove = clearFictitiousInjections(fictitiousP0ByNode);
synchronized (fictitiousP0ByNode) {
toRemove.forEach(n -> {
@@ -681,9 +680,9 @@ public double getFictitiousQ0(int node) {
}
@Override
- public NodeBreakerView setFictitiousQ0(int node, double q0) {
+ public VoltageLevel.NodeBreakerView setFictitiousQ0(int node, double q0) {
if (Double.isNaN(q0)) {
- throw new ValidationException(NodeBreakerVoltageLevel.this, "undefined value cannot be set as fictitious q0");
+ throw new ValidationException(voltageLevel, "undefined value cannot be set as fictitious q0");
}
TDoubleArrayList q0ByVariant = fictitiousQ0ByNode.get(node);
if (q0ByVariant == null) {
@@ -699,7 +698,7 @@ public NodeBreakerView setFictitiousQ0(int node, double q0) {
int variantIndex = getNetwork().getVariantIndex();
double oldValue = q0ByVariant.set(getNetwork().getVariantIndex(), q0);
String variantId = getNetwork().getVariantManager().getVariantId(variantIndex);
- getNetwork().getListeners().notifyUpdate(NodeBreakerVoltageLevel.this, "fictitiousQ0", variantId, oldValue, q0);
+ getNetwork().getListeners().notifyUpdate(voltageLevel, "fictitiousQ0", variantId, oldValue, q0);
TIntSet toRemove = clearFictitiousInjections(fictitiousQ0ByNode);
synchronized (fictitiousQ0ByNode) {
toRemove.forEach(n -> {
@@ -877,7 +876,7 @@ public void removeSwitch(String switchId) {
Integer e = switches.get(switchId);
if (e == null) {
throw new PowsyblException("Switch '" + switchId
- + "' not found in voltage level '" + id + "'");
+ + "' not found in voltage level '" + voltageLevel.getId() + "'");
}
graph.removeEdge(e);
graph.removeIsolatedVertices();
@@ -885,7 +884,7 @@ public void removeSwitch(String switchId) {
@Override
public BusbarSectionAdder newBusbarSection() {
- return new BusbarSectionAdderImpl(NodeBreakerVoltageLevel.this);
+ return new BusbarSectionAdderImpl(voltageLevel);
}
@Override
@@ -906,7 +905,7 @@ public int getBusbarSectionCount() {
@Override
public BusbarSection getBusbarSection(String id) {
BusbarSection bbs = getNetwork().getIndex().get(id, BusbarSection.class);
- if (bbs != null && bbs.getTerminal().getVoltageLevel() != NodeBreakerVoltageLevel.this) {
+ if (bbs != null && bbs.getTerminal().getVoltageLevel() != voltageLevel) {
return null;
}
return bbs;
@@ -942,11 +941,11 @@ private static TIntSet clearFictitiousInjections(TIntObjectMap
}
@Override
- public NodeBreakerViewExt getNodeBreakerView() {
+ public VoltageLevelExt.NodeBreakerViewExt getNodeBreakerView() {
return nodeBreakerView;
}
- private final BusViewExt busView = new BusViewExt() {
+ private final VoltageLevelExt.BusViewExt busView = new VoltageLevelExt.BusViewExt() {
@Override
public Iterable getBuses() {
@@ -972,11 +971,11 @@ public Bus getMergedBus(String busBarId) {
};
@Override
- public BusViewExt getBusView() {
+ public VoltageLevelExt.BusViewExt getBusView() {
return busView;
}
- private final BusBreakerViewExt busBreakerView = new BusBreakerViewExt() {
+ private final VoltageLevelExt.BusBreakerViewExt busBreakerView = new VoltageLevelExt.BusBreakerViewExt() {
@Override
public Iterable getBuses() {
@@ -1074,7 +1073,7 @@ public Switch getSwitch(String switchId) {
}
@Override
- public BusBreakerView.SwitchAdder newSwitch() {
+ public SwitchAdder newSwitch() {
throw createNotSupportedNodeBreakerTopologyException();
}
@@ -1085,7 +1084,7 @@ public void traverse(Bus bus, TopologyTraverser traverser) {
};
@Override
- public BusBreakerViewExt getBusBreakerView() {
+ public VoltageLevelExt.BusBreakerViewExt getBusBreakerView() {
return busBreakerView;
}
@@ -1107,7 +1106,7 @@ public TopologyKind getTopologyKind() {
private void checkTerminal(TerminalExt terminal) {
if (!(terminal instanceof NodeTerminal)) {
throw new ValidationException(terminal.getConnectable(),
- "voltage level " + NodeBreakerVoltageLevel.this.id + " has a node/breaker topology"
+ "voltage level " + voltageLevel.getId() + " has a node/breaker topology"
+ ", a node connection should be specified instead of a bus connection");
}
int node = ((NodeTerminal) terminal).getNode();
@@ -1116,7 +1115,7 @@ private void checkTerminal(TerminalExt terminal) {
throw new ValidationException(terminal.getConnectable(),
"an equipment (" + graph.getVertexObject(node).getConnectable().getId()
+ ") is already connected to node " + node + " of voltage level "
- + NodeBreakerVoltageLevel.this.id);
+ + voltageLevel.getId());
}
}
@@ -1129,12 +1128,12 @@ public void attach(TerminalExt terminal, boolean test) {
int node = ((NodeTerminal) terminal).getNode();
// create the link terminal <-> voltage level
- terminal.setVoltageLevel(NodeBreakerVoltageLevel.this);
+ terminal.setVoltageLevel(voltageLevel);
// create the link terminal <-> graph vertex
graph.setVertexObject(node, (NodeTerminal) terminal);
- getNetwork().getVariantManager().forEachVariant(this::invalidateCache);
+ getNetwork().getVariantManager().forEachVariant(NodeBreakerTopologyModel.this::invalidateCache);
}
@Override
@@ -1147,7 +1146,7 @@ public void detach(TerminalExt terminal) {
graph.setVertexObject(node, null);
- getNetwork().getVariantManager().forEachVariant(this::invalidateCache);
+ getNetwork().getVariantManager().forEachVariant(NodeBreakerTopologyModel.this::invalidateCache);
// remove the link terminal -> voltage level
terminal.setVoltageLevel(null);
@@ -1181,8 +1180,7 @@ private void checkTopologyKind(Terminal terminal) {
* @param terminal Terminal to connect
* @return true
if the terminal has been connected, false
if it hasn't or if it was already connected
*/
- @Override
- public boolean connect(TerminalExt terminal) {
+ boolean connect(TerminalExt terminal) {
// Only keep the closed non-fictional breakers in the nominal case
return connect(terminal, SwitchPredicates.IS_NONFICTIONAL_BREAKER);
}
@@ -1224,7 +1222,7 @@ boolean getConnectingSwitches(Terminal terminal, Predicate super SwitchImpl> i
// find all paths starting from the current terminal to a busbar section that does not contain an open switch
// that is not of the type of switch the user wants to operate
// Paths are already sorted by the number of open switches and by the size of the paths
- List paths = graph.findAllPaths(node, NodeBreakerVoltageLevel::isBusbarSection, sw -> checkNonClosableSwitch(sw, isSwitchOperable),
+ List paths = graph.findAllPaths(node, NodeBreakerTopologyModel::isBusbarSection, sw -> checkNonClosableSwitch(sw, isSwitchOperable),
Comparator.comparing((TIntArrayList o) -> o.grep(idx -> SwitchPredicates.IS_OPEN.test(graph.getEdgeObject(idx))).size())
.thenComparing(TIntArrayList::size));
if (!paths.isEmpty()) {
@@ -1245,8 +1243,7 @@ boolean getConnectingSwitches(Terminal terminal, Predicate super SwitchImpl> i
return false;
}
- @Override
- public boolean disconnect(TerminalExt terminal) {
+ boolean disconnect(TerminalExt terminal) {
// Only keep the closed non-fictional breakers in the nominal case
return disconnect(terminal, SwitchPredicates.IS_CLOSED_BREAKER);
}
@@ -1280,7 +1277,7 @@ boolean getDisconnectingSwitches(Terminal terminal, Predicate super SwitchImpl
int node = ((NodeTerminal) terminal).getNode();
// find all paths starting from the current terminal to a busbar section that does not contain an open switch
- List paths = graph.findAllPaths(node, NodeBreakerVoltageLevel::isBusbarSection, SwitchPredicates.IS_OPEN);
+ List paths = graph.findAllPaths(node, NodeBreakerTopologyModel::isBusbarSection, SwitchPredicates.IS_OPEN);
if (paths.isEmpty()) {
return false;
}
@@ -1385,25 +1382,21 @@ private static TraverseResult getTraverseResult(Set visitedTerminals,
@Override
public void extendVariantArraySize(int initVariantArraySize, int number, int sourceIndex) {
- super.extendVariantArraySize(initVariantArraySize, number, sourceIndex);
variants.push(number, VariantImpl::new);
}
@Override
public void reduceVariantArraySize(int number) {
- super.reduceVariantArraySize(number);
variants.pop(number);
}
@Override
public void deleteVariantArrayElement(int index) {
- super.deleteVariantArrayElement(index);
variants.delete(index);
}
@Override
public void allocateVariantArrayElement(int[] indexes, final int sourceIndex) {
- super.allocateVariantArrayElement(indexes, sourceIndex);
variants.allocate(indexes, VariantImpl::new);
}
@@ -1424,7 +1417,7 @@ public void printTopology() {
@Override
public void printTopology(PrintStream out, ShortIdDictionary dict) {
out.println("-------------------------------------------------------------");
- out.println("Topology of " + NodeBreakerVoltageLevel.this.id);
+ out.println("Topology of " + voltageLevel.getId());
graph.print(out, terminal -> terminal != null ? terminal.getConnectable().toString() : null, null);
}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeTerminal.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeTerminal.java
index 31fe6f1d27b..e1acd93912c 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeTerminal.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeTerminal.java
@@ -57,6 +57,10 @@ public void moveConnectable(int node, String voltageLevelId) {
}
};
+ private NodeBreakerTopologyModel getTopologyModel() {
+ return (NodeBreakerTopologyModel) voltageLevel.getTopologyModel();
+ }
+
private final BusBreakerViewExt busBreakerView = new BusBreakerViewExt() {
@Override
@@ -64,7 +68,7 @@ public BusExt getBus() {
if (removed) {
throw new PowsyblException(CANNOT_ACCESS_BUS_REMOVED_EQUIPMENT + connectable.id);
}
- return ((NodeBreakerVoltageLevel) voltageLevel).getCalculatedBusBreakerTopology().getBus(node);
+ return getTopologyModel().getCalculatedBusBreakerTopology().getBus(node);
}
@Override
@@ -72,12 +76,12 @@ public BusExt getConnectableBus() {
if (removed) {
throw new PowsyblException(CANNOT_ACCESS_BUS_REMOVED_EQUIPMENT + connectable.id);
}
- return ((NodeBreakerVoltageLevel) voltageLevel).getCalculatedBusBreakerTopology().getConnectableBus(node);
+ return getTopologyModel().getCalculatedBusBreakerTopology().getConnectableBus(node);
}
@Override
public void setConnectableBus(String busId) {
- throw NodeBreakerVoltageLevel.createNotSupportedNodeBreakerTopologyException();
+ throw NodeBreakerTopologyModel.createNotSupportedNodeBreakerTopologyException();
}
@Override
@@ -102,7 +106,7 @@ public BusExt getBus() {
if (removed) {
throw new PowsyblException(CANNOT_ACCESS_BUS_REMOVED_EQUIPMENT + connectable.id);
}
- return ((NodeBreakerVoltageLevel) voltageLevel).getCalculatedBusTopology().getBus(node);
+ return getTopologyModel().getCalculatedBusTopology().getBus(node);
}
@Override
@@ -110,7 +114,7 @@ public BusExt getConnectableBus() {
if (removed) {
throw new PowsyblException(CANNOT_ACCESS_BUS_REMOVED_EQUIPMENT + connectable.id);
}
- return ((NodeBreakerVoltageLevel) voltageLevel).getCalculatedBusTopology().getConnectableBus(node);
+ return getTopologyModel().getCalculatedBusTopology().getConnectableBus(node);
}
};
@@ -231,7 +235,7 @@ public boolean isConnected() {
if (removed) {
throw new PowsyblException("Cannot access connectivity status of removed equipment " + connectable.id);
}
- return ((NodeBreakerVoltageLevel) voltageLevel).isConnected(this);
+ return getTopologyModel().isConnected(this);
}
@Override
@@ -239,7 +243,7 @@ public boolean traverse(TopologyTraverser traverser, Set visitedTermin
if (removed) {
throw new PowsyblException(String.format("Associated equipment %s is removed", connectable.id));
}
- return ((NodeBreakerVoltageLevel) voltageLevel).traverse(this, traverser, visitedTerminals, traversalType);
+ return getTopologyModel().traverse(this, traverser, visitedTerminals, traversalType);
}
@Override
@@ -252,7 +256,7 @@ public void traverse(TopologyTraverser traverser, TraversalType traversalType) {
if (removed) {
throw new PowsyblException(String.format("Associated equipment %s is removed", connectable.id));
}
- ((NodeBreakerVoltageLevel) voltageLevel).traverse(this, traverser, traversalType);
+ getTopologyModel().traverse(this, traverser, traversalType);
}
@Override
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ShuntCompensatorAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ShuntCompensatorAdderImpl.java
index 5802e57907d..a83a21c13c4 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ShuntCompensatorAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ShuntCompensatorAdderImpl.java
@@ -225,7 +225,7 @@ id, getName(), isFictitious(), modelBuilder.build(), sectionCount,
voltageRegulatorOn, targetV, targetDeadband);
shunt.addTerminal(terminal);
- voltageLevel.attach(terminal, false);
+ voltageLevel.getTopologyModel().attach(terminal, false);
network.getIndex().checkAndAdd(shunt);
network.getListeners().notifyCreation(shunt);
return shunt;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/StaticVarCompensatorAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/StaticVarCompensatorAdderImpl.java
index 1952e3e1c8b..af6a95bbb75 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/StaticVarCompensatorAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/StaticVarCompensatorAdderImpl.java
@@ -91,7 +91,7 @@ public StaticVarCompensatorImpl add() {
regulationMode, regulatingTerminal != null ? regulatingTerminal : terminal,
getNetworkRef());
svc.addTerminal(terminal);
- voltageLevel.attach(terminal, false);
+ voltageLevel.getTopologyModel().attach(terminal, false);
network.getIndex().checkAndAdd(svc);
network.getListeners().notifyCreation(svc);
return svc;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubstationImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubstationImpl.java
index 1be352b22aa..d8348f9b21c 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubstationImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubstationImpl.java
@@ -203,8 +203,9 @@ public Substation addGeographicalTag(String tag) {
if (tag == null) {
throw new ValidationException(this, "geographical tag is null");
}
+ Set oldGeographicalTags = new LinkedHashSet<>(geographicalTags);
if (geographicalTags.add(tag)) {
- getNetwork().getListeners().notifyElementAdded(this, "geographicalTags", tag);
+ getNetwork().getListeners().notifyUpdate(this, "geographicalTags", oldGeographicalTags, geographicalTags);
}
return this;
}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SwitchImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SwitchImpl.java
index f66a27304dc..144f9fde0c3 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SwitchImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SwitchImpl.java
@@ -70,7 +70,7 @@ public void setOpen(boolean open) {
boolean oldValue = this.open.get(index);
if (oldValue != open) {
this.open.set(index, open);
- voltageLevel.invalidateCache(isRetained());
+ voltageLevel.getTopologyModel().invalidateCache(isRetained());
String variantId = network.getVariantManager().getVariantId(index);
network.getListeners().notifyUpdate(this, "open", variantId, oldValue, open);
}
@@ -91,7 +91,7 @@ public void setRetained(boolean retained) {
boolean oldValue = this.retained.get(index);
if (oldValue != retained) {
this.retained.set(index, retained);
- voltageLevel.invalidateCache();
+ voltageLevel.getTopologyModel().invalidateCache();
String variantId = network.getVariantManager().getVariantId(index);
network.getListeners().notifyUpdate(this, "retained", variantId, oldValue, retained);
}
@@ -102,7 +102,7 @@ public void setFictitious(boolean fictitious) {
boolean oldValue = this.fictitious;
if (oldValue != fictitious) {
this.fictitious = fictitious;
- voltageLevel.invalidateCache();
+ voltageLevel.getTopologyModel().invalidateCache();
NetworkImpl network = getNetwork();
network.getListeners().notifyUpdate(this, "fictitious", oldValue, fictitious);
}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerAdderImpl.java
index c426ceaa21a..aa556962e45 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerAdderImpl.java
@@ -291,14 +291,14 @@ public ThreeWindingsTransformerImpl add() {
// check that the 3 windings transformer is attachable on the 3 sides (only
// verify)
- voltageLevel1.attach(terminal1, true);
- voltageLevel2.attach(terminal2, true);
- voltageLevel3.attach(terminal3, true);
+ voltageLevel1.getTopologyModel().attach(terminal1, true);
+ voltageLevel2.getTopologyModel().attach(terminal2, true);
+ voltageLevel3.getTopologyModel().attach(terminal3, true);
// do attach
- voltageLevel1.attach(terminal1, false);
- voltageLevel2.attach(terminal2, false);
- voltageLevel3.attach(terminal3, false);
+ voltageLevel1.getTopologyModel().attach(terminal1, false);
+ voltageLevel2.getTopologyModel().attach(terminal2, false);
+ voltageLevel3.getTopologyModel().attach(terminal3, false);
getNetwork().getIndex().checkAndAdd(transformer);
getNetwork().getListeners().notifyCreation(transformer);
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TopologyModel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TopologyModel.java
new file mode 100644
index 00000000000..6914887600e
--- /dev/null
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TopologyModel.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2024, 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/.
+ * SPDX-License-Identifier: MPL-2.0
+ */
+package com.powsybl.iidm.network.impl;
+
+import java.util.function.Predicate;
+
+/**
+ * @author Geoffroy Jamgotchian {@literal }
+ */
+interface TopologyModel {
+
+ void invalidateCache(boolean exceptBusBreakerView);
+
+ void invalidateCache();
+
+ void attach(TerminalExt terminal, boolean test);
+
+ void detach(TerminalExt terminal);
+
+ boolean connect(TerminalExt terminal, Predicate super SwitchImpl> isTypeSwitchToOperate);
+
+ boolean disconnect(TerminalExt terminal, Predicate super SwitchImpl> isSwitchOpenable);
+}
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TwoWindingsTransformerAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TwoWindingsTransformerAdderImpl.java
index e9835ac7c06..bf553f4889c 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TwoWindingsTransformerAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TwoWindingsTransformerAdderImpl.java
@@ -124,11 +124,11 @@ public TwoWindingsTransformer add() {
transformer.addTerminal(terminal2);
// check that the two windings transformer is attachable on both side
- voltageLevel1.attach(terminal1, true);
- voltageLevel2.attach(terminal2, true);
+ voltageLevel1.getTopologyModel().attach(terminal1, true);
+ voltageLevel2.getTopologyModel().attach(terminal2, true);
- voltageLevel1.attach(terminal1, false);
- voltageLevel2.attach(terminal2, false);
+ voltageLevel1.getTopologyModel().attach(terminal1, false);
+ voltageLevel2.getTopologyModel().attach(terminal2, false);
getNetwork().getIndex().checkAndAdd(transformer);
getNetwork().getListeners().notifyCreation(transformer);
return transformer;
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelAdderImpl.java
index 3d8130a7b63..bff9f8ddab7 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelAdderImpl.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelAdderImpl.java
@@ -94,12 +94,7 @@ public VoltageLevel add() {
ValidationUtil.checkVoltageLimits(this, lowVoltageLimit, highVoltageLimit);
ValidationUtil.checkTopologyKind(this, topologyKind);
- VoltageLevelExt voltageLevel = switch (topologyKind) {
- case NODE_BREAKER ->
- new NodeBreakerVoltageLevel(id, getName(), isFictitious(), substation, networkRef, subnetworkRef, nominalV, lowVoltageLimit, highVoltageLimit);
- case BUS_BREAKER ->
- new BusBreakerVoltageLevel(id, getName(), isFictitious(), substation, networkRef, subnetworkRef, nominalV, lowVoltageLimit, highVoltageLimit);
- };
+ VoltageLevelExt voltageLevel = new VoltageLevelImpl(id, getName(), isFictitious(), substation, networkRef, subnetworkRef, nominalV, lowVoltageLimit, highVoltageLimit, topologyKind);
getNetwork().getIndex().checkAndAdd(voltageLevel);
Optional.ofNullable(substation).ifPresent(s -> s.addVoltageLevel(voltageLevel));
getNetwork().getListeners().notifyCreation(voltageLevel);
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelExt.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelExt.java
index 7a465e87fb3..7c9d1636688 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelExt.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelExt.java
@@ -7,16 +7,15 @@
*/
package com.powsybl.iidm.network.impl;
+import com.powsybl.iidm.network.Validable;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.commons.ref.Ref;
-import java.util.function.Predicate;
-
/**
*
* @author Geoffroy Jamgotchian {@literal }
*/
-interface VoltageLevelExt extends VoltageLevel, MultiVariantObject {
+interface VoltageLevelExt extends VoltageLevel, MultiVariantObject, Validable {
interface NodeBreakerViewExt extends NodeBreakerView {
@@ -45,29 +44,7 @@ interface BusViewExt extends BusView {
@Override
NetworkExt getParentNetwork();
- /**
- * Attach an equipment to the topology.
- */
- void attach(TerminalExt terminal, boolean test);
-
- /**
- * Detach an equipment from the topology.
- */
- void detach(TerminalExt terminal);
-
- boolean connect(TerminalExt terminal);
-
- boolean connect(TerminalExt terminal, Predicate super SwitchImpl> isTypeSwitchToOperate);
-
- boolean disconnect(TerminalExt terminal);
-
- boolean disconnect(TerminalExt terminal, Predicate super SwitchImpl> isSwitchOpenable);
-
- default void invalidateCache() {
- invalidateCache(false);
- }
-
- void invalidateCache(boolean exceptBusBreakerView);
+ TopologyModel getTopologyModel();
String getSubnetworkId();
diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractVoltageLevel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelImpl.java
similarity index 81%
rename from iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractVoltageLevel.java
rename to iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelImpl.java
index d438ab56d18..7fc379bc153 100644
--- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractVoltageLevel.java
+++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelImpl.java
@@ -7,14 +7,17 @@
*/
package com.powsybl.iidm.network.impl;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
import com.powsybl.commons.PowsyblException;
-import com.powsybl.commons.config.PlatformConfig;
import com.powsybl.iidm.network.*;
import com.powsybl.commons.ref.Ref;
+import com.powsybl.iidm.network.util.ShortIdDictionary;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -23,14 +26,10 @@
*
* @author Geoffroy Jamgotchian {@literal }
*/
-abstract class AbstractVoltageLevel extends AbstractIdentifiable implements VoltageLevelExt {
-
- private static final int DEFAULT_NODE_INDEX_LIMIT = 1000;
-
- public static final int NODE_INDEX_LIMIT = loadNodeIndexLimit(PlatformConfig.defaultConfig());
+class VoltageLevelImpl extends AbstractIdentifiable implements VoltageLevelExt {
private final Ref networkRef;
- private Ref subnetworkRef;
+ private final Ref subnetworkRef;
private final SubstationImpl substation;
@@ -40,13 +39,16 @@ abstract class AbstractVoltageLevel extends AbstractIdentifiable i
private double highVoltageLimit;
+ private final AbstractTopologyModel topologyModel;
+
/** Areas associated to this VoltageLevel, with at most one area for each area type */
private final Set areas = new LinkedHashSet<>();
private boolean removed = false;
- AbstractVoltageLevel(String id, String name, boolean fictitious, SubstationImpl substation, Ref networkRef,
- Ref subnetworkRef, double nominalV, double lowVoltageLimit, double highVoltageLimit) {
+ VoltageLevelImpl(String id, String name, boolean fictitious, SubstationImpl substation, Ref networkRef,
+ Ref subnetworkRef, double nominalV, double lowVoltageLimit, double highVoltageLimit,
+ TopologyKind topologyKind) {
super(id, name, fictitious);
this.substation = substation;
this.networkRef = networkRef;
@@ -54,13 +56,15 @@ abstract class AbstractVoltageLevel extends AbstractIdentifiable i
this.nominalV = nominalV;
this.lowVoltageLimit = lowVoltageLimit;
this.highVoltageLimit = highVoltageLimit;
+ this.topologyModel = switch (Objects.requireNonNull(topologyKind)) {
+ case NODE_BREAKER -> new NodeBreakerTopologyModel(this);
+ case BUS_BREAKER -> new BusBreakerTopologyModel(this);
+ };
}
- protected static int loadNodeIndexLimit(PlatformConfig platformConfig) {
- return platformConfig
- .getOptionalModuleConfig("iidm")
- .map(moduleConfig -> moduleConfig.getIntProperty("node-index-limit", DEFAULT_NODE_INDEX_LIMIT))
- .orElse(DEFAULT_NODE_INDEX_LIMIT);
+ @Override
+ public AbstractTopologyModel getTopologyModel() {
+ return topologyModel;
}
@Override
@@ -157,6 +161,21 @@ public Substation getNullableSubstation() {
return substation;
}
+ @Override
+ public NodeBreakerViewExt getNodeBreakerView() {
+ return topologyModel.getNodeBreakerView();
+ }
+
+ @Override
+ public BusBreakerViewExt getBusBreakerView() {
+ return topologyModel.getBusBreakerView();
+ }
+
+ @Override
+ public BusViewExt getBusView() {
+ return topologyModel.getBusView();
+ }
+
@Override
public NetworkImpl getNetwork() {
if (removed) {
@@ -247,39 +266,27 @@ public T getConnectable(String id, Class aClass) {
@Override
public Iterable getConnectables(Class