From f2c256087a6f2ff813adc0feed551b9b584790c9 Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Wed, 11 Sep 2024 17:07:24 +0200 Subject: [PATCH 01/14] Create an OperationalLimitsGroup for each combination of OperationalLimit subclass and OperationalLimitSet at CGMES import Signed-off-by: Romain Courtier --- .../powsybl/cgmes/conversion/Conversion.java | 17 ++ .../elements/OperationalLimitConversion.java | 146 ++++++++++------ .../test/OperationalLimitsGroupTest.java | 35 ++++ .../src/test/resources/OperationalLimits.xml | 161 ++++++++++++++++++ .../java/com/powsybl/iidm/network/Branch.java | 4 + .../powsybl/iidm/network/DanglingLine.java | 2 + .../network/ThreeWindingsTransformer.java | 2 + .../impl/AbstractConnectableBranch.java | 6 +- .../iidm/network/impl/DanglingLineImpl.java | 5 + .../impl/ThreeWindingsTransformerImpl.java | 5 + .../iidm/network/impl/TieLineImpl.java | 10 ++ 11 files changed, 335 insertions(+), 58 deletions(-) create mode 100644 cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/OperationalLimitsGroupTest.java create mode 100644 cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java index 869455c855a..91ba4a9fe5f 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java @@ -219,6 +219,7 @@ public Network convert(ReportNode reportNode) { convert(cgmes.operationalLimits(), l -> new OperationalLimitConversion(l, context)); context.loadingLimitsMapping().addAll(); + setSelectedOperationalLimitGroup(context); if (config.convertSvInjections()) { convert(cgmes.svInjections(), si -> new SvInjectionConversion(si, context)); @@ -268,6 +269,22 @@ public Network convert(ReportNode reportNode) { return network; } + private void setSelectedOperationalLimitGroup(Context context) { + Set limitsHolders = new HashSet<>(); + context.network().getBranchStream().forEach(b -> limitsHolders.add(b.getOperationalLimitsHolder1())); + context.network().getBranchStream().forEach(b -> limitsHolders.add(b.getOperationalLimitsHolder2())); + context.network().getDanglingLineStream().forEach(dl -> limitsHolders.add(dl.getOperationalLimitsHolder())); + context.network().getThreeWindingsTransformerStream().forEach(tw3 -> tw3.getLegStream().forEach(leg -> limitsHolders.add(leg.getOperationalLimitsHolder()))); + + for (FlowsLimitsHolder limitsHolder : limitsHolders) { + if (limitsHolder.getOperationalLimitsGroups().size() == 1) { + limitsHolder.setSelectedOperationalLimitsGroup(limitsHolder.getOperationalLimitsGroups().iterator().next().getId()); + } else { + // TODO + } + } + } + private void handleDangingLineDisconnectedAtBoundary(Network network, Context context) { if (config.disconnectNetworkSideOfDanglingLinesIfBoundaryIsDisconnected()) { for (DanglingLine dl : network.getDanglingLines()) { diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java index 01ddb9b76ce..f335277f8d0 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java @@ -27,13 +27,14 @@ public class OperationalLimitConversion extends AbstractIdentifiedObjectConversi private static final String OPERATIONAL_LIMIT = "Operational limit"; private static final String OPERATIONAL_LIMIT_TYPE_NAME = "operationalLimitTypeName"; private static final String OPERATIONAL_LIMIT_SUBCLASS = "OperationalLimitSubclass"; - + private static final String OPERATIONAL_LIMIT_SET = "OperationalLimitSet"; private static final String PERMANENT_LIMIT = "Permanent Limit"; private static final String TEMPORARY_LIMIT = "Temporary Limit"; public OperationalLimitConversion(PropertyBag l, Context context) { super("OperationalLimit", l, context); String limitSubclass = p.getLocal(OPERATIONAL_LIMIT_SUBCLASS); + String limitSet = p.getLocal(OPERATIONAL_LIMIT_SET); // Limit can associated to a Terminal or to an Equipment terminalId = l.getId("Terminal"); equipmentId = l.getId("Equipment"); @@ -43,11 +44,11 @@ public OperationalLimitConversion(PropertyBag l, Context context) { terminal = context.terminalMapping().findForFlowLimits(terminalId); } if (terminal != null) { - createLimitsAdder(context.terminalMapping().number(terminalId), limitSubclass, terminal.getConnectable()); + createLimitsAdder(context.terminalMapping().number(terminalId), limitSubclass, limitSet, terminal.getConnectable()); } else if (equipmentId != null) { // The equipment may be a Branch, a Dangling line, a Switch ... Identifiable identifiable = context.network().getIdentifiable(equipmentId); - createLimitsAdder(-1, limitSubclass, identifiable); + createLimitsAdder(-1, limitSubclass, limitSet, identifiable); } } else if (limitSubclass.equals("VoltageLimit")) { if (terminalId != null) { @@ -73,89 +74,122 @@ private void setVoltageLevelForVoltageLimit(Terminal terminal) { } } - private static Supplier> getLoadingLimitAdderSupplier(String limitSubClass, FlowsLimitsHolder holder) { - if (limitSubClass == null) { - return holder::newCurrentLimits; - } - switch (limitSubClass) { - case ACTIVE_POWER_LIMIT: - return holder::newActivePowerLimits; - case APPARENT_POWER_LIMIT: - return holder::newApparentPowerLimits; - case CURRENT_LIMIT: - return holder::newCurrentLimits; - default: - throw new IllegalStateException(); - } + /** + * Get the LoadingLimitsAdder supplier for the given FlowsLimitsHolder and limit set + subclass. + * @param limitSubClass The subclass of the OperationalLimit. + * @param limitSet The set containing the OperationalLimit. + * @param holder The equipment to which the OperationalLimit applies. + * @return The appropriate LoadingLimitsAdder supplier. + */ + private static Supplier> getLoadingLimitAdderSupplier(String limitSubClass, String limitSet, FlowsLimitsHolder holder) { + OperationalLimitsGroup limitsGroup = holder.getOperationalLimitsGroup(limitSet).orElseGet(() -> holder.newOperationalLimitsGroup(limitSet)); + return getLoadingLimitAdderSupplier(limitsGroup, limitSubClass); } - private static Supplier> getLoadingLimitAdder1Supplier(String limitSubClass, Branch b) { - if (limitSubClass == null) { - return b::newCurrentLimits1; - } - switch (limitSubClass) { - case ACTIVE_POWER_LIMIT: - return b::newActivePowerLimits1; - case APPARENT_POWER_LIMIT: - return b::newApparentPowerLimits1; - case CURRENT_LIMIT: - return b::newCurrentLimits1; - default: - throw new IllegalStateException(); - } + /** + * Get the LoadingLimitsAdder supplier for the side 1 of the given branch and limit set + subclass. + * @param limitSubClass The subclass of the OperationalLimit. + * @param limitSet The set containing the OperationalLimit. + * @param b The branch to which the OperationalLimit applies on side 1. + * @return The appropriate LoadingLimitsAdder supplier. + */ + private static Supplier> getLoadingLimitAdder1Supplier(String limitSubClass, String limitSet, Branch b) { + OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup1(limitSet).orElseGet(() -> b.newOperationalLimitsGroup1(limitSet)); + return getLoadingLimitAdderSupplier(limitsGroup, limitSubClass); + } + + /** + * Get the LoadingLimitsAdder supplier for the side 2 of the given branch and limit set + subclass. + * @param limitSubClass The subclass of the OperationalLimit. + * @param limitSet The set containing the OperationalLimit. + * @param b The branch to which the OperationalLimit applies on side 2. + * @return The appropriate LoadingLimitsAdder supplier. + */ + private static Supplier> getLoadingLimitAdder2Supplier(String limitSubClass, String limitSet, Branch b) { + OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup2(limitSet).orElseGet(() -> b.newOperationalLimitsGroup2(limitSet)); + return getLoadingLimitAdderSupplier(limitsGroup, limitSubClass); } - private static Supplier> getLoadingLimitAdder2Supplier(String limitSubClass, Branch b) { + /** + * Get the LoadingLimitsAdder supplier for the given limits group and subclass. + * @param limitsGroup The limit group instance for which the adder is called. + * @param limitSubClass The subclass of the OperationalLimit. + * @return The appropriate LoadingLimitsAdder supplier. + */ + private static Supplier> getLoadingLimitAdderSupplier(OperationalLimitsGroup limitsGroup, String limitSubClass) { if (limitSubClass == null) { - return b::newCurrentLimits2; + return limitsGroup::newCurrentLimits; } switch (limitSubClass) { case ACTIVE_POWER_LIMIT: - return b::newActivePowerLimits2; + return limitsGroup::newActivePowerLimits; case APPARENT_POWER_LIMIT: - return b::newApparentPowerLimits2; + return limitsGroup::newApparentPowerLimits; case CURRENT_LIMIT: - return b::newCurrentLimits2; + return limitsGroup::newCurrentLimits; default: throw new IllegalStateException(); } } - private void createLimitsAdder(int terminalNumber, String limitSubClass, Branch b) { + /** + * Create the LoadingLimitsAdder for the given branch + side and the given limit set + subclass. + * @param terminalNumber The side of the branch to which the OperationalLimit applies. + * @param limitSubClass The subclass of the OperationalLimit. + * @param limitSet The set containing the OperationalLimit. + * @param b The branch to which the OperationalLimit applies. + */ + private void createLimitsAdder(int terminalNumber, String limitSubClass, String limitSet, Branch b) { if (terminalNumber == 1) { - Supplier> loadingLimitAdder1Supplier = getLoadingLimitAdder1Supplier(limitSubClass, b); - loadingLimitsAdder1 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_1_" + limitSubClass, loadingLimitAdder1Supplier); + loadingLimitsAdder1 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_1_" + limitSubClass + "_" + limitSet, + getLoadingLimitAdder1Supplier(limitSubClass, limitSet, b)); } else if (terminalNumber == 2) { - Supplier> loadingLimitAdder2Supplier = getLoadingLimitAdder2Supplier(limitSubClass, b); - loadingLimitsAdder2 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_2_" + limitSubClass, loadingLimitAdder2Supplier); + loadingLimitsAdder2 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_2_" + limitSubClass + "_" + limitSet, + getLoadingLimitAdder2Supplier(limitSubClass, limitSet, b)); } else { notAssigned(b); } } - private void createLimitsAdder(int terminalNumber, String limitSubClass, ThreeWindingsTransformer twt) { + /** + * Create the LoadingLimitsAdder for the given 3w-transformer + side and the given limit set + subclass. + * @param terminalNumber The side of the transformer to which the OperationalLimit applies. + * @param limitSubClass The subclass of the OperationalLimit. + * @param limitSet The set containing the OperationalLimit. + * @param twt The 3w-transformer to which the OperationalLimit applies. + */ + private void createLimitsAdder(int terminalNumber, String limitSubClass, String limitSet, ThreeWindingsTransformer twt) { if (terminalNumber == 1) { - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_1_" + limitSubClass, - getLoadingLimitAdderSupplier(limitSubClass, twt.getLeg1())); + loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_1_" + limitSubClass + "_" + limitSet, + getLoadingLimitAdderSupplier(limitSubClass, limitSet, twt.getLeg1())); } else if (terminalNumber == 2) { - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_2_" + limitSubClass, - getLoadingLimitAdderSupplier(limitSubClass, twt.getLeg2())); + loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_2_" + limitSubClass + "_" + limitSet, + getLoadingLimitAdderSupplier(limitSubClass, limitSet, twt.getLeg2())); } else if (terminalNumber == 3) { - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_3_" + limitSubClass, - getLoadingLimitAdderSupplier(limitSubClass, twt.getLeg3())); + loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_3_" + limitSubClass + "_" + limitSet, + getLoadingLimitAdderSupplier(limitSubClass, limitSet, twt.getLeg3())); } else { notAssigned(twt); } } - private void createLimitsAdder(int terminalNumber, String limitSubClass, Identifiable identifiable) { + /** + * Create LoadingLimitsAdder(s) as per the given inputs. + * If the inputs are inconsistent, no limit adder is created. + * @param terminalNumber The side of the equipment to which the OperationalLimit applies. + * @param limitSubClass The subclass of the OperationalLimit. + * @param limitSet The set containing the OperationalLimit. + * @param identifiable The equipment to which the OperationalLimit applies. + */ + private void createLimitsAdder(int terminalNumber, String limitSubClass, String limitSet, Identifiable identifiable) { if (identifiable instanceof Line) { Branch b = (Branch) identifiable; if (terminalNumber == -1) { - loadingLimitsAdder1 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_1", getLoadingLimitAdder1Supplier(limitSubClass, b)); - loadingLimitsAdder2 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_2", getLoadingLimitAdder2Supplier(limitSubClass, b)); + // Limits appliy to the whole equipment == to both sides + createLimitsAdder(1, limitSubClass, limitSet, b); + createLimitsAdder(2, limitSubClass, limitSet, b); } else { - createLimitsAdder(terminalNumber, limitSubClass, b); + createLimitsAdder(terminalNumber, limitSubClass, limitSet, b); } } else if (identifiable instanceof TwoWindingsTransformer) { Branch b = (Branch) identifiable; @@ -163,17 +197,17 @@ private void createLimitsAdder(int terminalNumber, String limitSubClass, Identif context.ignored(limitSubClass, "Defined for Equipment TwoWindingsTransformer. Should be defined for one Terminal of Two"); notAssigned(b); } else { - createLimitsAdder(terminalNumber, limitSubClass, b); + createLimitsAdder(terminalNumber, limitSubClass, limitSet, b); } } else if (identifiable instanceof DanglingLine danglingLine) { - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(danglingLine.getId() + "_" + limitSubClass, - getLoadingLimitAdderSupplier(limitSubClass, danglingLine)); + loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(danglingLine.getId() + "_" + limitSubClass + "_" + limitSet, + getLoadingLimitAdderSupplier(limitSubClass, limitSet, danglingLine)); } else if (identifiable instanceof ThreeWindingsTransformer twt) { if (terminalNumber == -1) { context.ignored(limitSubClass, "Defined for Equipment ThreeWindingsTransformer. Should be defined for one Terminal of Three"); notAssigned(twt); } else { - createLimitsAdder(terminalNumber, limitSubClass, twt); + createLimitsAdder(terminalNumber, limitSubClass, limitSet, twt); } } else if (identifiable instanceof Switch) { Switch aswitch = context.network().getSwitch(equipmentId); 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 new file mode 100644 index 00000000000..1537c821035 --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/OperationalLimitsGroupTest.java @@ -0,0 +1,35 @@ +/** + * 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.commons.test.AbstractSerDeTest; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.OperationalLimitsGroup; +import org.junit.jupiter.api.Test; + +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Luma ZamarreƱo {@literal } + */ + +class OperationalLimitsGroupTest extends AbstractSerDeTest { + + @Test + void multipleLimitsGroupsOnLineTest() { + Network network = Network.read("OperationalLimits.xml", getClass().getResourceAsStream("/OperationalLimits.xml")); + Line line = network.getLine("Line"); + Collection limitsGroups = line.getOperationalLimitsGroups1(); + assertEquals(3, limitsGroups.size()); + } + +} diff --git a/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml b/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml new file mode 100644 index 00000000000..f22d71bcf97 --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml @@ -0,0 +1,161 @@ + + + 2021-03-01T23:00:00Z + 2021-03-02T10:22:58Z + OLS + 001 + http://entsoe.eu/CIM/EquipmentCore/3/1 + http://entsoe.eu/CIM/EquipmentOperation/3/1 + powsybl.org + + + 1 + 110 + + + false + 0 + 0 + Line + 0 + 0 + 1 + Line + 0.1 + 0.1 + 0 + 1 + 0.1 + + + + Term1 + 1 + + + + + Term2 + 2 + + + + + Node1 + + + + Node2 + + + + VL1 + + + + + VL2 + + + + + Substation1 + Substation1 + + + + Substation2 + Substation2 + + + + France + + + IDF + + + + OLS_1 + + + + Limit1 + 100 + + + + + Limit2 + 200 + + + + + Limit3 + 300 + + + + + OLS_2 + + + + Limit1 + 100 + + + + + Limit2 + 200 + + + + + Limit3 + 300 + + + + + OLS_3 + + + + Limit1 + 100 + + + + + Limit2 + 200 + + + + + Limit3 + 300 + + + + + + + IST + + + + + IT20 + 1200 + + + + + IT5 + 300 + + diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java index 20107ac6326..ce3c1de1c61 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java @@ -118,6 +118,10 @@ public interface Branch> extends Identifiable { TwoSides getSide(Terminal terminal); + FlowsLimitsHolder getOperationalLimitsHolder1(); + + FlowsLimitsHolder getOperationalLimitsHolder2(); + /** * Get the collection of the defined {@link OperationalLimitsGroup} on side 1. * @return the {@link OperationalLimitsGroup} s on side 1. diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DanglingLine.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DanglingLine.java index fcde3d29e94..2cca22c7410 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DanglingLine.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DanglingLine.java @@ -200,6 +200,8 @@ interface Generation extends ReactiveLimitsHolder { Generation setTargetV(double targetV); } + FlowsLimitsHolder getOperationalLimitsHolder(); + boolean isPaired(); /** diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java index a8a074eb6f7..6aa84b28c59 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java @@ -208,6 +208,8 @@ public interface ThreeWindingsTransformer extends Connectable getOperationalLimitsGroups() { return operationalLimitsGroups.getOperationalLimitsGroups(); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java index 454691ab6f0..a0c753f00b1 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java @@ -166,6 +166,11 @@ public void setPhaseTapChanger(PhaseTapChangerImpl phaseTapChanger) { phaseTapChanger); } + @Override + public FlowsLimitsHolder getOperationalLimitsHolder() { + return operationalLimitsHolder; + } + @Override public Collection getOperationalLimitsGroups() { return operationalLimitsHolder.getOperationalLimitsGroups(); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java index 7f2472a7fd1..69ce453e90e 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java @@ -239,6 +239,11 @@ public Optional getSelectedOperationalLimitsGroupId1() { return danglingLine1.getSelectedOperationalLimitsGroupId(); } + @Override + public FlowsLimitsHolder getOperationalLimitsHolder1() { + return danglingLine1.getOperationalLimitsHolder(); + } + @Override public Collection getOperationalLimitsGroups1() { return danglingLine1.getOperationalLimitsGroups(); @@ -289,6 +294,11 @@ public ApparentPowerLimitsAdder newApparentPowerLimits1() { return danglingLine1.newApparentPowerLimits(); } + @Override + public FlowsLimitsHolder getOperationalLimitsHolder2() { + return danglingLine2.getOperationalLimitsHolder(); + } + @Override public Collection getOperationalLimitsGroups2() { return danglingLine2.getOperationalLimitsGroups(); From 2e7671fce2e1ef70700b52455d4cb2b30dab21cf Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Thu, 12 Sep 2024 10:57:42 +0200 Subject: [PATCH 02/14] Create a common OperationalLimitsGroup for OperationalLimitSet having the same name for a given equipment side Signed-off-by: Romain Courtier --- .../elements/OperationalLimitConversion.java | 11 ++-- .../test/OperationalLimitsGroupTest.java | 36 +++++++++--- .../src/test/resources/OperationalLimits.xml | 58 +++++++++++++------ .../src/main/resources/CIM16.sparql | 1 + 4 files changed, 76 insertions(+), 30 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java index f335277f8d0..1d83513b6b8 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java @@ -28,13 +28,14 @@ public class OperationalLimitConversion extends AbstractIdentifiedObjectConversi private static final String OPERATIONAL_LIMIT_TYPE_NAME = "operationalLimitTypeName"; private static final String OPERATIONAL_LIMIT_SUBCLASS = "OperationalLimitSubclass"; private static final String OPERATIONAL_LIMIT_SET = "OperationalLimitSet"; + private static final String OPERATIONAL_LIMIT_SET_NAME = "limitSetName"; private static final String PERMANENT_LIMIT = "Permanent Limit"; private static final String TEMPORARY_LIMIT = "Temporary Limit"; public OperationalLimitConversion(PropertyBag l, Context context) { super("OperationalLimit", l, context); String limitSubclass = p.getLocal(OPERATIONAL_LIMIT_SUBCLASS); - String limitSet = p.getLocal(OPERATIONAL_LIMIT_SET); + String limitSet = p.getLocal(OPERATIONAL_LIMIT_SET_NAME) != null ? p.getLocal(OPERATIONAL_LIMIT_SET_NAME) : p.getLocal(OPERATIONAL_LIMIT_SET); // Limit can associated to a Terminal or to an Equipment terminalId = l.getId("Terminal"); equipmentId = l.getId("Equipment"); @@ -81,7 +82,7 @@ private void setVoltageLevelForVoltageLimit(Terminal terminal) { * @param holder The equipment to which the OperationalLimit applies. * @return The appropriate LoadingLimitsAdder supplier. */ - private static Supplier> getLoadingLimitAdderSupplier(String limitSubClass, String limitSet, FlowsLimitsHolder holder) { + private Supplier> getLoadingLimitAdderSupplier(String limitSubClass, String limitSet, FlowsLimitsHolder holder) { OperationalLimitsGroup limitsGroup = holder.getOperationalLimitsGroup(limitSet).orElseGet(() -> holder.newOperationalLimitsGroup(limitSet)); return getLoadingLimitAdderSupplier(limitsGroup, limitSubClass); } @@ -93,7 +94,7 @@ private void setVoltageLevelForVoltageLimit(Terminal terminal) { * @param b The branch to which the OperationalLimit applies on side 1. * @return The appropriate LoadingLimitsAdder supplier. */ - private static Supplier> getLoadingLimitAdder1Supplier(String limitSubClass, String limitSet, Branch b) { + private Supplier> getLoadingLimitAdder1Supplier(String limitSubClass, String limitSet, Branch b) { OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup1(limitSet).orElseGet(() -> b.newOperationalLimitsGroup1(limitSet)); return getLoadingLimitAdderSupplier(limitsGroup, limitSubClass); } @@ -105,7 +106,7 @@ private void setVoltageLevelForVoltageLimit(Terminal terminal) { * @param b The branch to which the OperationalLimit applies on side 2. * @return The appropriate LoadingLimitsAdder supplier. */ - private static Supplier> getLoadingLimitAdder2Supplier(String limitSubClass, String limitSet, Branch b) { + private Supplier> getLoadingLimitAdder2Supplier(String limitSubClass, String limitSet, Branch b) { OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup2(limitSet).orElseGet(() -> b.newOperationalLimitsGroup2(limitSet)); return getLoadingLimitAdderSupplier(limitsGroup, limitSubClass); } @@ -116,7 +117,7 @@ private void setVoltageLevelForVoltageLimit(Terminal terminal) { * @param limitSubClass The subclass of the OperationalLimit. * @return The appropriate LoadingLimitsAdder supplier. */ - private static Supplier> getLoadingLimitAdderSupplier(OperationalLimitsGroup limitsGroup, String limitSubClass) { + private Supplier> getLoadingLimitAdderSupplier(OperationalLimitsGroup limitsGroup, String limitSubClass) { if (limitSubClass == null) { return limitsGroup::newCurrentLimits; } 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 1537c821035..e2012a913da 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 @@ -9,27 +9,49 @@ package com.powsybl.cgmes.conversion.test; import com.powsybl.commons.test.AbstractSerDeTest; -import com.powsybl.iidm.network.Line; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.OperationalLimitsGroup; +import com.powsybl.iidm.network.*; import org.junit.jupiter.api.Test; -import java.util.Collection; +import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; /** - * @author Luma ZamarreƱo {@literal } + * @author Romain Courtier {@literal } */ class OperationalLimitsGroupTest extends AbstractSerDeTest { @Test void multipleLimitsGroupsOnLineTest() { + // Retrieve line Network network = Network.read("OperationalLimits.xml", getClass().getResourceAsStream("/OperationalLimits.xml")); Line line = network.getLine("Line"); - Collection limitsGroups = line.getOperationalLimitsGroups1(); - assertEquals(3, limitsGroups.size()); + + // There are 4 CGMES OperationalLimitSets on side 1 merged into 3 IIDM OperationalLimitsGroup + FlowsLimitsHolder holder1 = line.getOperationalLimitsHolder1(); + FlowsLimitsHolder holder2 = line.getOperationalLimitsHolder2(); + assertEquals(3, holder1.getOperationalLimitsGroups().size()); + assertEquals(0, holder2.getOperationalLimitsGroups().size()); + + // The CGMES winter current and active power limits have been merged into the same limits group + // since their OperationalLimitSet name are equals + Optional winterLimits = holder1.getOperationalLimitsGroup("WINTER"); + assertTrue(winterLimits.isPresent()); + assertTrue(winterLimits.get().getCurrentLimits().isPresent()); + assertTrue(winterLimits.get().getActivePowerLimits().isPresent()); + + // The CGMES spring current limits and summer active power limits have different limits group + // since their OperationalLimitSet name are distinct + Optional springLimits = holder1.getOperationalLimitsGroup("SPRING"); + assertTrue(springLimits.isPresent()); + assertTrue(springLimits.get().getCurrentLimits().isPresent()); + assertTrue(springLimits.get().getActivePowerLimits().isEmpty()); + + Optional summerLimits = holder1.getOperationalLimitsGroup("SUMMER"); + assertTrue(summerLimits.isPresent()); + assertTrue(summerLimits.get().getCurrentLimits().isEmpty()); + assertTrue(summerLimits.get().getActivePowerLimits().isPresent()); } } diff --git a/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml b/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml index f22d71bcf97..33f1daa44b6 100644 --- a/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml +++ b/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml @@ -76,71 +76,93 @@ - OLS_1 + WINTER Limit1 - 100 + 101 Limit2 - 200 + 201 Limit3 - 300 + 301 - OLS_2 + WINTER - + Limit1 - 100 + 102 - - + + Limit2 - 200 + 202 - - + + Limit3 - 300 + 302 - + - OLS_3 + SPRING Limit1 - 100 + 103 Limit2 - 200 + 203 Limit3 - 300 + 303 + + SUMMER + + + + Limit1 + 104 + + + + + Limit2 + 204 + + + + + Limit3 + 304 + + + diff --git a/cgmes/cgmes-model/src/main/resources/CIM16.sparql b/cgmes/cgmes-model/src/main/resources/CIM16.sparql index c72ad19a4ca..70f17d0b917 100644 --- a/cgmes/cgmes-model/src/main/resources/CIM16.sparql +++ b/cgmes/cgmes-model/src/main/resources/CIM16.sparql @@ -382,6 +382,7 @@ WHERE OPTIONAL { ?OperationalLimit cim:VoltageLimit.value ?value } # operational limit sets can be attached to terminals or equipments OPTIONAL { ?OperationalLimitSet cim:OperationalLimitSet.Terminal ?Terminal } + OPTIONAL { ?OperationalLimitSet cim:IdentifiedObject.name ?limitSetName } OPTIONAL { GRAPH ?graph2 { ?OperationalLimitSet cim:OperationalLimitSet.Equipment ?Equipment . ?Equipment cim:Equipment.EquipmentContainer ?EquipmentContainer From 70f6a1163c6e2d5b6a0a2462a9b975eac0a883c8 Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Thu, 12 Sep 2024 15:40:52 +0200 Subject: [PATCH 03/14] Fix CGMES export to avoid duplicate OperationalLimitType and map 1 CGMES OperationalLimitSet with 1 IIDM LoadingLimits Signed-off-by: Romain Courtier --- ...MicroGridTestConfiguration_BC_BE_EQ_V2.xml | 2 +- .../conversion/export/EquipmentExport.java | 167 +++++++++--------- .../elements/OperationalLimitTypeEq.java | 4 +- .../src/main/resources/CIM100.sparql | 1 + .../src/main/resources/CIM16.sparql | 2 +- 5 files changed, 89 insertions(+), 87 deletions(-) diff --git a/cgmes/cgmes-conformity/src/main/resources/conformity-modified/cas-1.1.3-data-4.0.3/MicroGrid/BaseCase/BC_BE_v2_limits/MicroGridTestConfiguration_BC_BE_EQ_V2.xml b/cgmes/cgmes-conformity/src/main/resources/conformity-modified/cas-1.1.3-data-4.0.3/MicroGrid/BaseCase/BC_BE_v2_limits/MicroGridTestConfiguration_BC_BE_EQ_V2.xml index eae1a7a2270..23ffa5577c9 100644 --- a/cgmes/cgmes-conformity/src/main/resources/conformity-modified/cas-1.1.3-data-4.0.3/MicroGrid/BaseCase/BC_BE_v2_limits/MicroGridTestConfiguration_BC_BE_EQ_V2.xml +++ b/cgmes/cgmes-conformity/src/main/resources/conformity-modified/cas-1.1.3-data-4.0.3/MicroGrid/BaseCase/BC_BE_v2_limits/MicroGridTestConfiguration_BC_BE_EQ_V2.xml @@ -2206,7 +2206,7 @@ - _OLS_V_1000 + Limits at Port 1 diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java index a69c4f5fccc..69cb35eca95 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java @@ -9,7 +9,6 @@ import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.Conversion; -import com.powsybl.cgmes.conversion.naming.CgmesObjectReference; import com.powsybl.cgmes.conversion.naming.NamingStrategy; import com.powsybl.cgmes.conversion.export.elements.*; import com.powsybl.cgmes.extensions.*; @@ -80,6 +79,7 @@ public static void write(Network network, XMLStreamWriter writer, CgmesExportCon Map mapTerminal2Id = new HashMap<>(); Set regulatingControlsWritten = new HashSet<>(); Set exportedBaseVoltagesByNominalV = new HashSet<>(); + Set exportedLimitTypes = new HashSet<>(); LoadGroups loadGroups = new LoadGroups(); if (writeConnectivityNodes) { @@ -97,11 +97,11 @@ public static void write(Network network, XMLStreamWriter writer, CgmesExportCon writeBatteries(network, cimNamespace, writeInitialP, writer, context); writeShuntCompensators(network, mapTerminal2Id, regulatingControlsWritten, cimNamespace, writer, context); writeStaticVarCompensators(network, mapTerminal2Id, regulatingControlsWritten, cimNamespace, writer, context); - writeLines(network, mapTerminal2Id, cimNamespace, euNamespace, limitValueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); - writeTwoWindingsTransformers(network, mapTerminal2Id, regulatingControlsWritten, cimNamespace, euNamespace, limitValueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); - writeThreeWindingsTransformers(network, mapTerminal2Id, regulatingControlsWritten, cimNamespace, euNamespace, limitValueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + writeLines(network, mapTerminal2Id, cimNamespace, euNamespace, limitValueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); + writeTwoWindingsTransformers(network, mapTerminal2Id, regulatingControlsWritten, cimNamespace, euNamespace, limitValueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); + writeThreeWindingsTransformers(network, mapTerminal2Id, regulatingControlsWritten, cimNamespace, euNamespace, limitValueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); - writeDanglingLines(network, mapTerminal2Id, cimNamespace, euNamespace, limitValueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context, exportedBaseVoltagesByNominalV); + writeDanglingLines(network, mapTerminal2Id, cimNamespace, euNamespace, limitValueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context, exportedBaseVoltagesByNominalV); writeHvdcLines(network, mapTerminal2Id, mapNodeKey2NodeId, cimNamespace, writer, context); writeControlAreas(loadAreaId, network, cimNamespace, euNamespace, writer, context); @@ -610,19 +610,19 @@ private static void writeStaticVarCompensators(Network network, Map mapTerminal2Id, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + private static void writeLines(Network network, Map mapTerminal2Id, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { for (Line line : network.getLines()) { String baseVoltage = null; if (line.getTerminal1().getVoltageLevel().getNominalV() == line.getTerminal2().getVoltageLevel().getNominalV()) { baseVoltage = context.getBaseVoltageByNominalVoltage(line.getTerminal1().getVoltageLevel().getNominalV()).getId(); } AcLineSegmentEq.write(context.getNamingStrategy().getCgmesId(line), line.getNameOrId(), baseVoltage, line.getR(), line.getX(), line.getG1() + line.getG2(), line.getB1() + line.getB2(), cimNamespace, writer, context); - writeBranchLimits(line, exportedTerminalId(mapTerminal2Id, line.getTerminal1()), exportedTerminalId(mapTerminal2Id, line.getTerminal2()), cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + writeBranchLimits(line, exportedTerminalId(mapTerminal2Id, line.getTerminal1()), exportedTerminalId(mapTerminal2Id, line.getTerminal2()), cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } } private static void writeTwoWindingsTransformers(Network network, Map mapTerminal2Id, Set regulatingControlsWritten, String cimNamespace, - String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { for (TwoWindingsTransformer twt : network.getTwoWindingsTransformers()) { CgmesExportUtil.addUpdateCgmesTapChangerExtension(twt, context); @@ -656,7 +656,7 @@ private static void writeTwoWindingsTransformers(Network network, Map mapTerminal2Id, Set regulatingControlsWritten, String cimNamespace, - String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { for (ThreeWindingsTransformer twt : network.getThreeWindingsTransformers()) { CgmesExportUtil.addUpdateCgmesTapChangerExtension(twt, context); @@ -690,11 +690,11 @@ private static void writeThreeWindingsTransformers(Network network, Map regulatingControlsWritten, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + private static void writeThreeWindingsTransformerEnd(ThreeWindingsTransformer twt, String twtId, String twtName, String endId, int endNumber, int legNumber, ThreeWindingsTransformer.Leg leg, double ratedU0, String terminalId, Set regulatingControlsWritten, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { // structural ratio at end1 double a0 = leg.getRatedU() / ratedU0; // move structural ratio from end1 to end2 @@ -828,7 +828,7 @@ private static void writeThreeWindingsTransformerEnd(ThreeWindingsTransformer tw PowerTransformerEq.writeEnd(endId, twtName, twtId, endNumber, r, x, g, b, leg.getRatedS(), leg.getRatedU(), terminalId, baseVoltage.getId(), cimNamespace, writer, context); writePhaseTapChanger(twt, leg.getPhaseTapChanger(), twtName, legNumber, endId, leg.getRatedU(), regulatingControlsWritten, cimNamespace, writer, context); writeRatioTapChanger(twt, leg.getRatioTapChanger(), twtName, legNumber, endId, leg.getRatedU(), regulatingControlsWritten, cimNamespace, writer, context); - writeFlowsLimits(leg, terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + writeFlowsLimits(leg, terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } private static > void writePhaseTapChanger(C eq, PhaseTapChanger ptc, String twtName, int endNumber, String endId, double neutralU, Set regulatingControlsWritten, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { @@ -950,12 +950,12 @@ private static int getRatioTapChangerNeutralStep(RatioTapChanger rtc) { } private static void writeDanglingLines(Network network, Map mapTerminal2Id, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, - String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context, Set exportedBaseVoltagesByNominalV) throws XMLStreamException { + String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context, Set exportedBaseVoltagesByNominalV) throws XMLStreamException { List exported = new ArrayList<>(); for (DanglingLine danglingLine : network.getDanglingLines(DanglingLineFilter.UNPAIRED)) { writeUnpairedOrPairedDanglingLines(Collections.singletonList(danglingLine), mapTerminal2Id, cimNamespace, euNamespace, - valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, + valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context, exportedBaseVoltagesByNominalV, exported); } @@ -963,13 +963,13 @@ private static void writeDanglingLines(Network network, Map ma for (String pairingKey : pairingKeys) { List danglingLineList = network.getDanglingLineStream(DanglingLineFilter.PAIRED).filter(danglingLine -> pairingKey.equals(danglingLine.getPairingKey())).toList(); writeUnpairedOrPairedDanglingLines(danglingLineList, mapTerminal2Id, cimNamespace, euNamespace, - valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, + valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context, exportedBaseVoltagesByNominalV, exported); } } private static void writeUnpairedOrPairedDanglingLines(List danglingLineList, Map mapTerminal2Id, String cimNamespace, String euNamespace, - String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, + String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context, Set exportedBaseVoltagesByNominalV, List exported) throws XMLStreamException { String baseVoltageId = writeDanglingLinesBaseVoltage(danglingLineList, cimNamespace, writer, context, exportedBaseVoltagesByNominalV); @@ -983,10 +983,10 @@ private static void writeUnpairedOrPairedDanglingLines(List dangli AcLineSegmentEq.write(context.getNamingStrategy().getCgmesId(danglingLine), danglingLine.getNameOrId(), context.getBaseVoltageByNominalVoltage(danglingLine.getTerminal().getVoltageLevel().getNominalV()).getId(), danglingLine.getR(), danglingLine.getX(), danglingLine.getG(), danglingLine.getB(), cimNamespace, writer, context); - writeFlowsLimits(danglingLine, exportedTerminalId(mapTerminal2Id, danglingLine.getTerminal()), cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + writeFlowsLimits(danglingLine, exportedTerminalId(mapTerminal2Id, danglingLine.getTerminal()), cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); danglingLine.getAliasFromType("CGMES." + TERMINAL_BOUNDARY).ifPresent(terminalBdId -> { try { - writeFlowsLimits(danglingLine, terminalBdId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + writeFlowsLimits(danglingLine, terminalBdId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } catch (XMLStreamException e) { throw new UncheckedXmlStreamException(e); } @@ -1164,85 +1164,86 @@ private static String writeFictitiousVoltageLevelFor(Identifiable identifiabl return voltageLevelId; } - private static void writeBranchLimits(Branch branch, String terminalId1, String terminalId2, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { - Optional activePowerLimits1 = branch.getActivePowerLimits1(); - if (activePowerLimits1.isPresent()) { - writeLoadingLimits(activePowerLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); - } - Optional activePowerLimits2 = branch.getActivePowerLimits2(); - if (activePowerLimits2.isPresent()) { - writeLoadingLimits(activePowerLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); - } - Optional apparentPowerLimits1 = branch.getApparentPowerLimits1(); - if (apparentPowerLimits1.isPresent()) { - writeLoadingLimits(apparentPowerLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); - } - Optional apparentPowerLimits2 = branch.getApparentPowerLimits2(); - if (apparentPowerLimits2.isPresent()) { - writeLoadingLimits(apparentPowerLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); - } - Optional currentLimits1 = branch.getCurrentLimits1(); - if (currentLimits1.isPresent()) { - writeLoadingLimits(currentLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + private static void writeBranchLimits(Branch branch, String terminalId1, String terminalId2, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + for (OperationalLimitsGroup limitsGroup : branch.getOperationalLimitsGroups1()) { + Optional activePowerLimits1 = limitsGroup.getActivePowerLimits(); + if (activePowerLimits1.isPresent()) { + writeLoadingLimits(activePowerLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); + } + Optional apparentPowerLimits1 = limitsGroup.getApparentPowerLimits(); + if (apparentPowerLimits1.isPresent()) { + writeLoadingLimits(apparentPowerLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); + } + Optional currentLimits1 = limitsGroup.getCurrentLimits(); + if (currentLimits1.isPresent()) { + writeLoadingLimits(currentLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); + } } - Optional currentLimits2 = branch.getCurrentLimits2(); - if (currentLimits2.isPresent()) { - writeLoadingLimits(currentLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + + for (OperationalLimitsGroup limitsGroup : branch.getOperationalLimitsGroups2()) { + Optional activePowerLimits2 = limitsGroup.getActivePowerLimits(); + if (activePowerLimits2.isPresent()) { + writeLoadingLimits(activePowerLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); + } + Optional apparentPowerLimits2 = limitsGroup.getApparentPowerLimits(); + if (apparentPowerLimits2.isPresent()) { + writeLoadingLimits(apparentPowerLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); + } + Optional currentLimits2 = limitsGroup.getCurrentLimits(); + if (currentLimits2.isPresent()) { + writeLoadingLimits(currentLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); + } } } - private static void writeFlowsLimits(FlowsLimitsHolder holder, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { - Optional activePowerLimits = holder.getActivePowerLimits(); - if (activePowerLimits.isPresent()) { - writeLoadingLimits(activePowerLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); - } - Optional apparentPowerLimits = holder.getApparentPowerLimits(); - if (apparentPowerLimits.isPresent()) { - writeLoadingLimits(apparentPowerLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); - } - Optional currentLimits = holder.getCurrentLimits(); - if (currentLimits.isPresent()) { - writeLoadingLimits(currentLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + private static void writeFlowsLimits(FlowsLimitsHolder holder, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + for (OperationalLimitsGroup limitsGroup : holder.getOperationalLimitsGroups()) { + Optional activePowerLimits = limitsGroup.getActivePowerLimits(); + if (activePowerLimits.isPresent()) { + writeLoadingLimits(activePowerLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); + } + Optional apparentPowerLimits = limitsGroup.getApparentPowerLimits(); + if (apparentPowerLimits.isPresent()) { + writeLoadingLimits(apparentPowerLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); + } + Optional currentLimits = limitsGroup.getCurrentLimits(); + if (currentLimits.isPresent()) { + writeLoadingLimits(currentLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); + } } } - private static void writeLoadingLimits(LoadingLimits limits, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + private static void writeLoadingLimits(LoadingLimits limits, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, String limitSetName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + // Write the limit set. It contains at least the permanent limit String className = loadingLimitClassName(limits); + String operationalLimitSetId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), OPERATIONAL_LIMIT_SET, ref(limitSetName)); + OperationalLimitSetEq.write(operationalLimitSetId, limitSetName, terminalId, cimNamespace, writer, context); - if (!Double.isNaN(limits.getPermanentLimit())) { - CgmesObjectReference[] refs = {ref(terminalId), ref(className), null, PATL}; - String name = className + " PATL"; - - refs[2] = OPERATIONAL_LIMIT_TYPE; - String operationalLimitTypeId = context.getNamingStrategy().getCgmesId(refs); + // Write the permanent limit type (if not already written) + String operationalLimitTypeId = context.getNamingStrategy().getCgmesId(OPERATIONAL_LIMIT_TYPE, PATL); + if (!exportedLimitTypes.contains(operationalLimitTypeId)) { OperationalLimitTypeEq.writePatl(operationalLimitTypeId, cimNamespace, euNamespace, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + exportedLimitTypes.add(operationalLimitTypeId); + } - refs[2] = OPERATIONAL_LIMIT_SET; - String operationalLimitSetId = context.getNamingStrategy().getCgmesId(refs); - OperationalLimitSetEq.write(operationalLimitSetId, name, terminalId, cimNamespace, writer, context); + // Write the permanent limit + String operationalLimitId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), OPERATIONAL_LIMIT_VALUE, PATL); + LoadingLimitEq.write(operationalLimitId, limits, "PATL", limits.getPermanentLimit(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, valueAttributeName, writer, context); - refs[2] = OPERATIONAL_LIMIT_VALUE; - String limitId = context.getNamingStrategy().getCgmesId(refs); - LoadingLimitEq.write(limitId, limits, name, limits.getPermanentLimit(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, valueAttributeName, writer, context); - } if (!limits.getTemporaryLimits().isEmpty()) { - CgmesObjectReference[] refs = {ref(terminalId), ref(className), null, TATL, null}; for (LoadingLimits.TemporaryLimit temporaryLimit : limits.getTemporaryLimits()) { int acceptableDuration = temporaryLimit.getAcceptableDuration(); - refs[4] = ref(acceptableDuration); - String name = className + " TATL " + acceptableDuration; - refs[2] = OPERATIONAL_LIMIT_TYPE; - String operationalLimitTypeId = context.getNamingStrategy().getCgmesId(refs); - OperationalLimitTypeEq.writeTatl(operationalLimitTypeId, name, temporaryLimit.getAcceptableDuration(), cimNamespace, euNamespace, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); - - refs[2] = OPERATIONAL_LIMIT_SET; - String operationalLimitSetId = context.getNamingStrategy().getCgmesId(refs); - OperationalLimitSetEq.write(operationalLimitSetId, name, terminalId, cimNamespace, writer, context); + // Write the temporary limit type (if not already written) + operationalLimitTypeId = context.getNamingStrategy().getCgmesId(OPERATIONAL_LIMIT_TYPE, TATL, ref(acceptableDuration)); + if (!exportedLimitTypes.contains(operationalLimitTypeId)) { + OperationalLimitTypeEq.writeTatl(operationalLimitTypeId, temporaryLimit.getAcceptableDuration(), cimNamespace, euNamespace, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); + exportedLimitTypes.add(operationalLimitTypeId); + } - refs[2] = OPERATIONAL_LIMIT_VALUE; - String limitId = context.getNamingStrategy().getCgmesId(refs); - LoadingLimitEq.write(limitId, limits, temporaryLimit.getName(), temporaryLimit.getValue(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, valueAttributeName, writer, context); + // Write the temporary limit + operationalLimitId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), OPERATIONAL_LIMIT_VALUE, TATL, ref(acceptableDuration)); + LoadingLimitEq.write(operationalLimitId, limits, temporaryLimit.getName(), temporaryLimit.getValue(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, valueAttributeName, writer, context); } } } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/OperationalLimitTypeEq.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/OperationalLimitTypeEq.java index 6c80fbacaad..ed1a4bd5b8c 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/OperationalLimitTypeEq.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/OperationalLimitTypeEq.java @@ -35,9 +35,9 @@ public static void writePatl(String id, String cimNamespace, String euNamespace, writer.writeEndElement(); } - public static void writeTatl(String id, String name, int acceptableDuration, String cimNamespace, String euNamespace, + public static void writeTatl(String id, int acceptableDuration, String cimNamespace, String euNamespace, String limitTypeAttributeName, String limitKindClassName, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { - CgmesExportUtil.writeStartIdName("OperationalLimitType", id, name, cimNamespace, writer, context); + CgmesExportUtil.writeStartIdName("OperationalLimitType", id, "TATL " + acceptableDuration, cimNamespace, writer, context); writeDirection(cimNamespace, writer); writeKind(TATL, euNamespace, limitTypeAttributeName, limitKindClassName, writer); if (writeInfiniteDuration) { diff --git a/cgmes/cgmes-model/src/main/resources/CIM100.sparql b/cgmes/cgmes-model/src/main/resources/CIM100.sparql index cf0b350cb2c..675a391ea3d 100644 --- a/cgmes/cgmes-model/src/main/resources/CIM100.sparql +++ b/cgmes/cgmes-model/src/main/resources/CIM100.sparql @@ -114,6 +114,7 @@ WHERE { OPTIONAL { ?OperationalLimit cim:CurrentLimit.normalValue ?normalValue } OPTIONAL { ?OperationalLimit cim:ApparentPowerLimit.normalValue ?normalValue } OPTIONAL { ?OperationalLimit cim:VoltageLimit.normalValue ?normalValue } + OPTIONAL { ?OperationalLimitSet cim:IdentifiedObject.name ?limitSetName } # operational limit sets can be attached to terminals or equipments OPTIONAL { ?OperationalLimitSet cim:OperationalLimitSet.Terminal ?Terminal } OPTIONAL { GRAPH ?graph2 { diff --git a/cgmes/cgmes-model/src/main/resources/CIM16.sparql b/cgmes/cgmes-model/src/main/resources/CIM16.sparql index 70f17d0b917..a98860078c0 100644 --- a/cgmes/cgmes-model/src/main/resources/CIM16.sparql +++ b/cgmes/cgmes-model/src/main/resources/CIM16.sparql @@ -380,9 +380,9 @@ WHERE OPTIONAL { ?OperationalLimit cim:CurrentLimit.value ?value } OPTIONAL { ?OperationalLimit cim:ApparentPowerLimit.value ?value } OPTIONAL { ?OperationalLimit cim:VoltageLimit.value ?value } + OPTIONAL { ?OperationalLimitSet cim:IdentifiedObject.name ?limitSetName } # operational limit sets can be attached to terminals or equipments OPTIONAL { ?OperationalLimitSet cim:OperationalLimitSet.Terminal ?Terminal } - OPTIONAL { ?OperationalLimitSet cim:IdentifiedObject.name ?limitSetName } OPTIONAL { GRAPH ?graph2 { ?OperationalLimitSet cim:OperationalLimitSet.Equipment ?Equipment . ?Equipment cim:Equipment.EquipmentContainer ?EquipmentContainer From fac231f93e71c439ae63e4a0bf7e47da2273c7ec Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Mon, 16 Sep 2024 09:02:21 +0200 Subject: [PATCH 04/14] Reworked setting of selected group to avoid changing IIDM API Signed-off-by: Romain Courtier --- .../powsybl/cgmes/conversion/Conversion.java | 48 ++++++++++++++----- .../test/OperationalLimitsGroupTest.java | 12 ++--- .../java/com/powsybl/iidm/network/Branch.java | 4 -- .../powsybl/iidm/network/DanglingLine.java | 2 - .../network/ThreeWindingsTransformer.java | 2 - .../impl/AbstractConnectableBranch.java | 6 +-- .../iidm/network/impl/DanglingLineImpl.java | 5 -- .../impl/ThreeWindingsTransformerImpl.java | 5 -- .../iidm/network/impl/TieLineImpl.java | 10 ---- 9 files changed, 43 insertions(+), 51 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java index 91ba4a9fe5f..c3e698ebc03 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java @@ -34,6 +34,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import java.util.stream.Stream; import static com.powsybl.cgmes.conversion.CgmesReports.importedCgmesNetworkReport; @@ -219,7 +220,7 @@ public Network convert(ReportNode reportNode) { convert(cgmes.operationalLimits(), l -> new OperationalLimitConversion(l, context)); context.loadingLimitsMapping().addAll(); - setSelectedOperationalLimitGroup(context); + setSelectedOperationalLimitsGroup(context); if (config.convertSvInjections()) { convert(cgmes.svInjections(), si -> new SvInjectionConversion(si, context)); @@ -269,18 +270,41 @@ public Network convert(ReportNode reportNode) { return network; } - private void setSelectedOperationalLimitGroup(Context context) { - Set limitsHolders = new HashSet<>(); - context.network().getBranchStream().forEach(b -> limitsHolders.add(b.getOperationalLimitsHolder1())); - context.network().getBranchStream().forEach(b -> limitsHolders.add(b.getOperationalLimitsHolder2())); - context.network().getDanglingLineStream().forEach(dl -> limitsHolders.add(dl.getOperationalLimitsHolder())); - context.network().getThreeWindingsTransformerStream().forEach(tw3 -> tw3.getLegStream().forEach(leg -> limitsHolders.add(leg.getOperationalLimitsHolder()))); + /** + * Retrieve the Collection of OperationalLimitGroups for identifiable that have flow limits (branch, dangling line, 3w-transformer). + * If the collection has only one element, it gets to be the identifiable's selectedGroup. + * If there is more than one element in the collection, do nothing. The identifiable's selectedGroup should then be set by the user after the conversion. + * @param context The conversion's Context. + */ + private void setSelectedOperationalLimitsGroup(Context context) { + // Set selected limits group for branches + for (Branch branch : context.network().getBranches()) { + // Side 1 + Collection limitsHolder1 = branch.getOperationalLimitsGroups1(); + if (limitsHolder1.size() == 1) { + branch.setSelectedOperationalLimitsGroup1(limitsHolder1.iterator().next().getId()); + } + // Side 2 + Collection limitsHolder2 = branch.getOperationalLimitsGroups2(); + if (limitsHolder2.size() == 1) { + branch.setSelectedOperationalLimitsGroup2(limitsHolder2.iterator().next().getId()); + } + } - for (FlowsLimitsHolder limitsHolder : limitsHolders) { - if (limitsHolder.getOperationalLimitsGroups().size() == 1) { - limitsHolder.setSelectedOperationalLimitsGroup(limitsHolder.getOperationalLimitsGroups().iterator().next().getId()); - } else { - // TODO + // Set selected limits group for Dangling lines + for (DanglingLine dl : context.network().getDanglingLines()) { + Collection limitsHolder = dl.getOperationalLimitsGroups(); + if (limitsHolder.size() == 1) { + dl.setSelectedOperationalLimitsGroup(limitsHolder.iterator().next().getId()); + } + } + + // Set selected limits group for 3w transformers legs + for (ThreeWindingsTransformer.Leg leg : context.network().getThreeWindingsTransformerStream() + .flatMap(ThreeWindingsTransformer::getLegStream).collect(Collectors.toSet())) { + Collection limitsHolder = leg.getOperationalLimitsGroups(); + if (limitsHolder.size() == 1) { + leg.setSelectedOperationalLimitsGroup(limitsHolder.iterator().next().getId()); } } } 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 e2012a913da..5ae4d685c8b 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 @@ -29,26 +29,24 @@ void multipleLimitsGroupsOnLineTest() { Line line = network.getLine("Line"); // There are 4 CGMES OperationalLimitSets on side 1 merged into 3 IIDM OperationalLimitsGroup - FlowsLimitsHolder holder1 = line.getOperationalLimitsHolder1(); - FlowsLimitsHolder holder2 = line.getOperationalLimitsHolder2(); - assertEquals(3, holder1.getOperationalLimitsGroups().size()); - assertEquals(0, holder2.getOperationalLimitsGroups().size()); + assertEquals(3, line.getOperationalLimitsGroups1().size()); + assertEquals(0, line.getOperationalLimitsGroups2().size()); // The CGMES winter current and active power limits have been merged into the same limits group // since their OperationalLimitSet name are equals - Optional winterLimits = holder1.getOperationalLimitsGroup("WINTER"); + Optional winterLimits = line.getOperationalLimitsGroup1("WINTER"); assertTrue(winterLimits.isPresent()); assertTrue(winterLimits.get().getCurrentLimits().isPresent()); assertTrue(winterLimits.get().getActivePowerLimits().isPresent()); // The CGMES spring current limits and summer active power limits have different limits group // since their OperationalLimitSet name are distinct - Optional springLimits = holder1.getOperationalLimitsGroup("SPRING"); + Optional springLimits = line.getOperationalLimitsGroup1("SPRING"); assertTrue(springLimits.isPresent()); assertTrue(springLimits.get().getCurrentLimits().isPresent()); assertTrue(springLimits.get().getActivePowerLimits().isEmpty()); - Optional summerLimits = holder1.getOperationalLimitsGroup("SUMMER"); + Optional summerLimits = line.getOperationalLimitsGroup1("SUMMER"); assertTrue(summerLimits.isPresent()); assertTrue(summerLimits.get().getCurrentLimits().isEmpty()); assertTrue(summerLimits.get().getActivePowerLimits().isPresent()); diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java index ce3c1de1c61..20107ac6326 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java @@ -118,10 +118,6 @@ public interface Branch> extends Identifiable { TwoSides getSide(Terminal terminal); - FlowsLimitsHolder getOperationalLimitsHolder1(); - - FlowsLimitsHolder getOperationalLimitsHolder2(); - /** * Get the collection of the defined {@link OperationalLimitsGroup} on side 1. * @return the {@link OperationalLimitsGroup} s on side 1. diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DanglingLine.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DanglingLine.java index 2cca22c7410..fcde3d29e94 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DanglingLine.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/DanglingLine.java @@ -200,8 +200,6 @@ interface Generation extends ReactiveLimitsHolder { Generation setTargetV(double targetV); } - FlowsLimitsHolder getOperationalLimitsHolder(); - boolean isPaired(); /** diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java index 6aa84b28c59..a8a074eb6f7 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java @@ -208,8 +208,6 @@ public interface ThreeWindingsTransformer extends Connectable getOperationalLimitsGroups() { return operationalLimitsGroups.getOperationalLimitsGroups(); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java index a0c753f00b1..454691ab6f0 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java @@ -166,11 +166,6 @@ public void setPhaseTapChanger(PhaseTapChangerImpl phaseTapChanger) { phaseTapChanger); } - @Override - public FlowsLimitsHolder getOperationalLimitsHolder() { - return operationalLimitsHolder; - } - @Override public Collection getOperationalLimitsGroups() { return operationalLimitsHolder.getOperationalLimitsGroups(); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java index 69ce453e90e..7f2472a7fd1 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java @@ -239,11 +239,6 @@ public Optional getSelectedOperationalLimitsGroupId1() { return danglingLine1.getSelectedOperationalLimitsGroupId(); } - @Override - public FlowsLimitsHolder getOperationalLimitsHolder1() { - return danglingLine1.getOperationalLimitsHolder(); - } - @Override public Collection getOperationalLimitsGroups1() { return danglingLine1.getOperationalLimitsGroups(); @@ -294,11 +289,6 @@ public ApparentPowerLimitsAdder newApparentPowerLimits1() { return danglingLine1.newApparentPowerLimits(); } - @Override - public FlowsLimitsHolder getOperationalLimitsHolder2() { - return danglingLine2.getOperationalLimitsHolder(); - } - @Override public Collection getOperationalLimitsGroups2() { return danglingLine2.getOperationalLimitsGroups(); From 6c1779cf666e4df13fc940d53894d718bfd7932f Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Mon, 16 Sep 2024 11:35:30 +0200 Subject: [PATCH 05/14] Add a parameter to export all OperationalLimitsGroup or only the selected one during CGMES export Signed-off-by: Romain Courtier --- .../powsybl/cgmes/conversion/CgmesExport.java | 7 ++ .../conversion/export/CgmesExportContext.java | 11 +++ .../conversion/export/EquipmentExport.java | 28 ++++++-- .../test/OperationalLimitsGroupTest.java | 71 ++++++++++++++++++- .../issues/ExportNumberMaxValueTest.java | 2 +- 5 files changed, 111 insertions(+), 8 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java index 1fa2f960162..146ff543736 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java @@ -421,6 +421,7 @@ private void addParametersToContext(CgmesExportContext context, Properties param .setExportFlowsForSwitches(Parameter.readBoolean(getFormat(), params, EXPORT_POWER_FLOWS_FOR_SWITCHES_PARAMETER, defaultValueConfig)) .setExportTransformersWithHighestVoltageAtEnd1(Parameter.readBoolean(getFormat(), params, EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1_PARAMETER, defaultValueConfig)) .setExportLoadFlowStatus(Parameter.readBoolean(getFormat(), params, EXPORT_LOAD_FLOW_STATUS_PARAMETER, defaultValueConfig)) + .setExportAllLimitsGroup(Parameter.readBoolean(getFormat(), params, EXPORT_ALL_LIMITS_GROUP_PARAMETER, defaultValueConfig)) .setMaxPMismatchConverged(Parameter.readDouble(getFormat(), params, MAX_P_MISMATCH_CONVERGED_PARAMETER, defaultValueConfig)) .setMaxQMismatchConverged(Parameter.readDouble(getFormat(), params, MAX_Q_MISMATCH_CONVERGED_PARAMETER, defaultValueConfig)) .setExportSvInjectionsForSlacks(Parameter.readBoolean(getFormat(), params, EXPORT_SV_INJECTIONS_FOR_SLACKS_PARAMETER, defaultValueConfig)) @@ -541,6 +542,7 @@ public String getFormat() { public static final String MODEL_DESCRIPTION = "iidm.export.cgmes.model-description"; public static final String EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1 = "iidm.export.cgmes.export-transformers-with-highest-voltage-at-end1"; public static final String EXPORT_LOAD_FLOW_STATUS = "iidm.export.cgmes.export-load-flow-status"; + public static final String EXPORT_ALL_LIMITS_GROUP = "iidm.export.cgmes.export-all-limits-group"; public static final String MAX_P_MISMATCH_CONVERGED = "iidm.export.cgmes.max-p-mismatch-converged"; public static final String MAX_Q_MISMATCH_CONVERGED = "iidm.export.cgmes.max-q-mismatch-converged"; public static final String EXPORT_SV_INJECTIONS_FOR_SLACKS = "iidm.export.cgmes.export-sv-injections-for-slacks"; @@ -630,6 +632,11 @@ public String getFormat() { ParameterType.BOOLEAN, "Export load flow status of topological islands", CgmesExportContext.EXPORT_LOAD_FLOW_STATUS_DEFAULT_VALUE); + private static final Parameter EXPORT_ALL_LIMITS_GROUP_PARAMETER = new Parameter( + EXPORT_ALL_LIMITS_GROUP, + ParameterType.BOOLEAN, + "True to export all OperationalLimitsGroup, False to export only the selected group", + CgmesExportContext.EXPORT_LOAD_FLOW_STATUS_DEFAULT_VALUE); private static final Parameter MAX_P_MISMATCH_CONVERGED_PARAMETER = new Parameter( MAX_P_MISMATCH_CONVERGED, ParameterType.DOUBLE, diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java index a40a2338ad1..a4f8f6df99f 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java @@ -73,6 +73,7 @@ public class CgmesExportContext { public static final boolean EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1_DEFAULT_VALUE = false; public static final boolean ENCODE_IDS_DEFAULT_VALUE = true; public static final boolean EXPORT_LOAD_FLOW_STATUS_DEFAULT_VALUE = true; + public static final boolean EXPORT_ALL_LIMITS_GROUP_DEFAULT_VALUE = true; // From QoCDC 3.3.1 rules IGMConvergence, KirchhoffsFirstLaw, ... that refer to SV_INJECTION_LIMIT=0.1 public static final double MAX_P_MISMATCH_CONVERGED_DEFAULT_VALUE = 0.1; public static final double MAX_Q_MISMATCH_CONVERGED_DEFAULT_VALUE = 0.1; @@ -86,6 +87,7 @@ public class CgmesExportContext { private boolean exportFlowsForSwitches = EXPORT_POWER_FLOWS_FOR_SWITCHES_DEFAULT_VALUE; private boolean exportTransformersWithHighestVoltageAtEnd1 = EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1_DEFAULT_VALUE; private boolean exportLoadFlowStatus = EXPORT_LOAD_FLOW_STATUS_DEFAULT_VALUE; + private boolean exportAllLimitsGroup = EXPORT_ALL_LIMITS_GROUP_DEFAULT_VALUE; private double maxPMismatchConverged = MAX_P_MISMATCH_CONVERGED_DEFAULT_VALUE; private double maxQMismatchConverged = MAX_Q_MISMATCH_CONVERGED_DEFAULT_VALUE; private boolean isExportSvInjectionsForSlacks = EXPORT_SV_INJECTIONS_FOR_SLACKS_DEFAULT_VALUE; @@ -608,6 +610,15 @@ public CgmesExportContext setExportLoadFlowStatus(boolean exportLoadFlowStatus) return this; } + public boolean isExportAllLimitsGroup() { + return exportAllLimitsGroup; + } + + public CgmesExportContext setExportAllLimitsGroup(boolean exportAllLimitsGroup) { + this.exportAllLimitsGroup = exportAllLimitsGroup; + return this; + } + public double getMaxPMismatchConverged() { return maxPMismatchConverged; } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java index 69cb35eca95..dfe1e9e6db2 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java @@ -1165,7 +1165,13 @@ private static String writeFictitiousVoltageLevelFor(Identifiable identifiabl } private static void writeBranchLimits(Branch branch, String terminalId1, String terminalId2, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { - for (OperationalLimitsGroup limitsGroup : branch.getOperationalLimitsGroups1()) { + Collection limitsGroups1 = new ArrayList<>(); + if (context.isExportAllLimitsGroup()) { + limitsGroups1.addAll(branch.getOperationalLimitsGroups1()); + } else if (branch.getSelectedOperationalLimitsGroup1().isPresent()) { + limitsGroups1.add(branch.getSelectedOperationalLimitsGroup1().get()); + } + for (OperationalLimitsGroup limitsGroup : limitsGroups1) { Optional activePowerLimits1 = limitsGroup.getActivePowerLimits(); if (activePowerLimits1.isPresent()) { writeLoadingLimits(activePowerLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); @@ -1180,7 +1186,13 @@ private static void writeBranchLimits(Branch branch, String terminalId1, Stri } } - for (OperationalLimitsGroup limitsGroup : branch.getOperationalLimitsGroups2()) { + Collection limitsGroups2 = new ArrayList<>(); + if (context.isExportAllLimitsGroup()) { + limitsGroups2.addAll(branch.getOperationalLimitsGroups2()); + } else if (branch.getSelectedOperationalLimitsGroup2().isPresent()) { + limitsGroups2.add(branch.getSelectedOperationalLimitsGroup2().get()); + } + for (OperationalLimitsGroup limitsGroup : limitsGroups2) { Optional activePowerLimits2 = limitsGroup.getActivePowerLimits(); if (activePowerLimits2.isPresent()) { writeLoadingLimits(activePowerLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); @@ -1197,7 +1209,13 @@ private static void writeBranchLimits(Branch branch, String terminalId1, Stri } private static void writeFlowsLimits(FlowsLimitsHolder holder, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { - for (OperationalLimitsGroup limitsGroup : holder.getOperationalLimitsGroups()) { + Collection limitsGroups = new ArrayList<>(); + if (context.isExportAllLimitsGroup()) { + limitsGroups.addAll(holder.getOperationalLimitsGroups()); + } else if (holder.getSelectedOperationalLimitsGroup().isPresent()) { + limitsGroups.add(holder.getSelectedOperationalLimitsGroup().get()); + } + for (OperationalLimitsGroup limitsGroup : limitsGroups) { Optional activePowerLimits = limitsGroup.getActivePowerLimits(); if (activePowerLimits.isPresent()) { writeLoadingLimits(activePowerLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); @@ -1227,7 +1245,7 @@ private static void writeLoadingLimits(LoadingLimits limits, String terminalId, } // Write the permanent limit - String operationalLimitId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), OPERATIONAL_LIMIT_VALUE, PATL); + String operationalLimitId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), ref(limitSetName), OPERATIONAL_LIMIT_VALUE, PATL); LoadingLimitEq.write(operationalLimitId, limits, "PATL", limits.getPermanentLimit(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, valueAttributeName, writer, context); if (!limits.getTemporaryLimits().isEmpty()) { @@ -1242,7 +1260,7 @@ private static void writeLoadingLimits(LoadingLimits limits, String terminalId, } // Write the temporary limit - operationalLimitId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), OPERATIONAL_LIMIT_VALUE, TATL, ref(acceptableDuration)); + operationalLimitId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), ref(limitSetName), OPERATIONAL_LIMIT_VALUE, TATL, ref(acceptableDuration)); LoadingLimitEq.write(operationalLimitId, limits, temporaryLimit.getName(), temporaryLimit.getValue(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, valueAttributeName, writer, context); } } 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 5ae4d685c8b..ae2cdeede73 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 @@ -8,11 +8,16 @@ package com.powsybl.cgmes.conversion.test; +import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.commons.test.AbstractSerDeTest; import com.powsybl.iidm.network.*; import org.junit.jupiter.api.Test; -import java.util.Optional; +import java.io.IOException; +import java.nio.file.Files; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.junit.jupiter.api.Assertions.*; @@ -22,8 +27,13 @@ class OperationalLimitsGroupTest extends AbstractSerDeTest { + private static final Pattern OPERATIONAL_LIMIT_SET = Pattern.compile(""); + private static final Pattern OPERATIONAL_LIMIT_TYPE = Pattern.compile(""); + private static final Pattern ACTIVE_POWER_LIMIT = Pattern.compile(""); + private static final Pattern CURRENT_LIMIT = Pattern.compile(""); + @Test - void multipleLimitsGroupsOnLineTest() { + void importMultipleLimitsGroupsOnSameLineEndTest() { // Retrieve line Network network = Network.read("OperationalLimits.xml", getClass().getResourceAsStream("/OperationalLimits.xml")); Line line = network.getLine("Line"); @@ -52,4 +62,61 @@ void multipleLimitsGroupsOnLineTest() { assertTrue(summerLimits.get().getActivePowerLimits().isPresent()); } + @Test + void exportSelectedLimitsGroupTest() throws IOException { + // Import and export CGMES limits + Network network = Network.read("OperationalLimits.xml", getClass().getResourceAsStream("/OperationalLimits.xml")); + + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.EXPORT_ALL_LIMITS_GROUP, false); + exportParams.put(CgmesExport.PROFILES, List.of("EQ")); + network.write("CGMES", exportParams, tmpDir.resolve("ExportSelectedLimitsGroup.xml")); + String exportSelectedLimitsGroupXml = Files.readString(tmpDir.resolve("ExportSelectedLimitsGroup_EQ.xml")); + + // No OperationalLimitsGroup is exported + assertEquals(0, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_SET).size()); + assertEquals(0, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size()); + assertEquals(0, getOccurrences(exportSelectedLimitsGroupXml, ACTIVE_POWER_LIMIT).size()); + assertEquals(0, getOccurrences(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size()); + + // Manually select one limits group + Line line = network.getLine("Line"); + line.setSelectedOperationalLimitsGroup1("WINTER"); + network.write("CGMES", exportParams, tmpDir.resolve("ExportSelectedLimitsGroup.xml")); + exportSelectedLimitsGroupXml = Files.readString(tmpDir.resolve("ExportSelectedLimitsGroup_EQ.xml")); + + // The selected limits group contains 2 sets of limits (current and active power) + assertEquals(2, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_SET).size()); + assertEquals(3, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size()); + assertEquals(3, getOccurrences(exportSelectedLimitsGroupXml, ACTIVE_POWER_LIMIT).size()); + assertEquals(3, getOccurrences(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size()); + } + + @Test + void exportAllLimitsGroupTest() throws IOException { + // Import and export CGMES limits + Network network = Network.read("OperationalLimits.xml", getClass().getResourceAsStream("/OperationalLimits.xml")); + + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.EXPORT_ALL_LIMITS_GROUP, true); + exportParams.put(CgmesExport.PROFILES, List.of("EQ")); + network.write("CGMES", exportParams, tmpDir.resolve("ExportAllLimitsGroup.xml")); + String exportAllLimitsGroupXml = Files.readString(tmpDir.resolve("ExportAllLimitsGroup_EQ.xml")); + + // All 4 OperationalLimitsGroup are exported + assertEquals(4, getOccurrences(exportAllLimitsGroupXml, OPERATIONAL_LIMIT_SET).size()); + assertEquals(3, getOccurrences(exportAllLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size()); + assertEquals(6, getOccurrences(exportAllLimitsGroupXml, ACTIVE_POWER_LIMIT).size()); + assertEquals(6, 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; + } + } diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ExportNumberMaxValueTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ExportNumberMaxValueTest.java index 48ddcdd5868..3ae8eb77236 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ExportNumberMaxValueTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ExportNumberMaxValueTest.java @@ -58,7 +58,7 @@ void testTemporaryLimitWithoutValue() throws IOException { // Look for the value of the temporary limit with acceptable duration 60 // We know exactly which identifier to look for // Include multiple lines and non-greedy matches - Pattern tatl60Pattern = Pattern.compile(".*?(.*?)", Pattern.DOTALL); + Pattern tatl60Pattern = Pattern.compile(".*?(.*?)", Pattern.DOTALL); Matcher tatl60Value = tatl60Pattern.matcher(eq); assertTrue(tatl60Value.find()); // Read the value as a double From 3045afab90184286133930eb36a02ec80d36566a Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Mon, 16 Sep 2024 12:02:45 +0200 Subject: [PATCH 06/14] Reworked to comply with quality gate Signed-off-by: Romain Courtier --- .../cgmes/conversion/export/EquipmentExport.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java index dfe1e9e6db2..b9d33eb6014 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java @@ -1168,8 +1168,8 @@ private static void writeBranchLimits(Branch branch, String terminalId1, Stri Collection limitsGroups1 = new ArrayList<>(); if (context.isExportAllLimitsGroup()) { limitsGroups1.addAll(branch.getOperationalLimitsGroups1()); - } else if (branch.getSelectedOperationalLimitsGroup1().isPresent()) { - limitsGroups1.add(branch.getSelectedOperationalLimitsGroup1().get()); + } else { + branch.getSelectedOperationalLimitsGroup1().ifPresent(limitsGroups1::add); } for (OperationalLimitsGroup limitsGroup : limitsGroups1) { Optional activePowerLimits1 = limitsGroup.getActivePowerLimits(); @@ -1189,8 +1189,8 @@ private static void writeBranchLimits(Branch branch, String terminalId1, Stri Collection limitsGroups2 = new ArrayList<>(); if (context.isExportAllLimitsGroup()) { limitsGroups2.addAll(branch.getOperationalLimitsGroups2()); - } else if (branch.getSelectedOperationalLimitsGroup2().isPresent()) { - limitsGroups2.add(branch.getSelectedOperationalLimitsGroup2().get()); + } else { + branch.getSelectedOperationalLimitsGroup2().ifPresent(limitsGroups2::add); } for (OperationalLimitsGroup limitsGroup : limitsGroups2) { Optional activePowerLimits2 = limitsGroup.getActivePowerLimits(); @@ -1212,8 +1212,8 @@ private static void writeFlowsLimits(FlowsLimitsHolder holder, String terminalId Collection limitsGroups = new ArrayList<>(); if (context.isExportAllLimitsGroup()) { limitsGroups.addAll(holder.getOperationalLimitsGroups()); - } else if (holder.getSelectedOperationalLimitsGroup().isPresent()) { - limitsGroups.add(holder.getSelectedOperationalLimitsGroup().get()); + } else { + holder.getSelectedOperationalLimitsGroup().ifPresent(limitsGroups::add); } for (OperationalLimitsGroup limitsGroup : limitsGroups) { Optional activePowerLimits = limitsGroup.getActivePowerLimits(); From 9916f5e0efd799fc081899527cb626459637b30e Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Tue, 17 Sep 2024 11:17:58 +0200 Subject: [PATCH 07/14] Add documentation (CGMES import and export and IIDM modeling) Signed-off-by: Romain Courtier --- docs/grid_exchange_formats/cgmes/export.md | 4 +++ docs/grid_exchange_formats/cgmes/import.md | 35 ++++++++++++++++++++-- docs/grid_model/additional.md | 31 ++++++++++++++++++- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/docs/grid_exchange_formats/cgmes/export.md b/docs/grid_exchange_formats/cgmes/export.md index b8121191c1f..99921e3c1e2 100644 --- a/docs/grid_exchange_formats/cgmes/export.md +++ b/docs/grid_exchange_formats/cgmes/export.md @@ -387,6 +387,10 @@ the sums of active power and reactive power at the bus are higher than a thresho `iidm.export.cgmes.max-p-mismatch-converged` and `iidm.export.cgmes.max-q-mismatch-converged`. This property is set to `true` by default. +**iidm.export.cgmes.export-all-limits-group** +Optional property that defines whether all OperationalLimitsGroup should be exported, or only the selected (active) ones. +This property is set to `true` by default, which means all groups are exported (not only the active ones). + **iidm.export.cgmes.max-p-mismatch-converged** Optional property that defines the threshold below which a bus is considered to be balanced for the load flow status of the `TopologicalIsland` in active power. If the sum of all the active power of the terminals connected to the bus is greater than this threshold, then the load flow is considered to be divergent. Its default value is `0.1`, and it should be used only if the `iidm.export.cgmes.export-load-flow-status` property is set to `true`. diff --git a/docs/grid_exchange_formats/cgmes/import.md b/docs/grid_exchange_formats/cgmes/import.md index 1a1544448b9..e692b381ec0 100644 --- a/docs/grid_exchange_formats/cgmes/import.md +++ b/docs/grid_exchange_formats/cgmes/import.md @@ -304,8 +304,39 @@ Sections are created from the lowest CGMES `sectionNumber` to the highest and ea TODO regulation (cgmes-operational_limit-import)= -### OperationalLimit -TODO +### OperationalLimits + +OperationalLimits model a specification of limits associated with equipments. + +#### OperationalLimitSet + +A CGMES `OperationalLimitSet` is a set of `OperationalLimit` associated with equipment or terminal. It is mapped to a PowSyBl [`OperationalLimitsGroup`](../../grid_model/additional.md#limit-group-collection). +The `id` attribute is copied from CGMES `name`. + +Just like CGMES allows to attach multiple `OperationalLimitSet` on the same equipment or terminal, PowSyBl stores a collection of `OperationalLimitsGroup` for every +[`Line`](../../grid_model/network_subnetwork.md#line) (actually 2 collections for a Line: one for each side), [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line) and [`ThreeWindingTransformer.Leg`](../../grid_model/network_subnetwork.md#three-winding-transformer-leg). + +In case of multiple `OperationalLimitSet` attached to the same end: +- If the sets have different names, they are imported in different `OperationalLimitsGroup`. For instance, a `WINTER` set and a `SUMMER` set will be imported in two distinct groups. +- If the sets have the same name, the sets are merged in the same `OperationalLimitsGroup`. For instance, a `WINTER` set of CurrentLimit and a `WINTER` set of ActivePowerLimit will be imported in the same group. + +If there is only one `OperationalLimitsGroup` on an end, it automatically gets to be selected (active). However, if there is multiple groups, none is selected: the user has to choose which set is active. + +#### OperationalLimit + +A CGMES `OperationalLimit` is an abstract class that represent different kinds of limits: current, active power or apparent power. The collection of CGMES `OperationalLimit` in the set is mapped to a PowSyBl [`LoadingLimits`](../../grid_model/additional.md#loading-limits) as follows: +- The set of CGMES `CurrentLimit` in the `OperationalLimitSet` are mapped to the `currentLimits` attribute of the PowSyBl `OperationalLimitsGroup` corresponding to the set. +- The set of CGMES `ActivePowerLimit` in the `OperationalLimitSet` are mapped to the `activePowerLimits` attribute of the PowSyBl `OperationalLimitsGroup` corresponding to the set. +- The set of CGMES `ApparentPowerLimit` in the `OperationalLimitSet` are mapped to the `apparentPowerLimits` attribute of the PowSyBl `OperationalLimitsGroup` corresponding to the set. + +A particular CGMES `OperationalLimit` is mapped differently depending on its associated CGMES `OperationalLimitType`: +- A permanent limit (`OperationalLimitType.limitType` is `LimitTypeKind.patl`) is mapped as follows: + - PowSyBl `LoadingLimits.permanentLimit` is copied from CGMES `OperationalLimit.value` +- A temporary limit (`OperationalLimitType.limitType` is `LimitTypeKind.tatl`) is mapped as follows: + - A new entry is created in PowSyBl `LoadingLimits.temporaryLimits` + - `name` is copied from `OperationalLimit.name` + - `value` is copied from `OperationalLimit.value` + - `acceptableDuration` is copied from `OperationalType.acceptableDuration` (cgmes-power-transformer-import)= ### PowerTransformer diff --git a/docs/grid_model/additional.md b/docs/grid_model/additional.md index cc504bf09fb..b6d1a59fac8 100644 --- a/docs/grid_model/additional.md +++ b/docs/grid_model/additional.md @@ -100,7 +100,7 @@ In PowSyBl, users can store a collection of limits: #### Examples -Two examples are provided below, with their corresponding limits scheme, to show clearly how to create a new `CurrentLimits` instance. +Three examples are provided below, with their corresponding limits scheme, to show clearly how to create new `CurrentLimits` instances. ##### First example This first example creates a `CurrentLimits` instance containing one permanent limit and two temporary limits. @@ -147,6 +147,35 @@ CurrentLimits currentLimits = network.getDanglingLine("DL").newCurrentLimits() ![Current limits scheme_example2](img/current-limits-example2.svg){align=center class="only-light"} ![Current limits scheme_example2](img/dark_mode/current-limits-example2.svg){align=center class="only-dark"} +##### Third example +This third example shows how to create multiple OperationalLimitsGroup on the same end, and set one of these as the selected (active) one. +```java +Line line = network.getLine("Line"); + +line.newOperationalLimitsGroup1("SUMMER") + .newCurrentLimits() + .setPermanentLimit(60) + .beginTemporaryLimit() + .setName("TATL-10") + .setValue(80) + .setAcceptableDuration(10 * 60) + .endTemporaryLimit() + .add(); + +line.newOperationalLimitsGroup1("WINTER") + .newCurrentLimits() + .setPermanentLimit(100) + .beginTemporaryLimit() + .setName("TATL-10") + .setValue(120) + .setAcceptableDuration(10 * 60) + .endTemporaryLimit() + .add(); + +line.setSelectedOperationalLimitsGroup1("WINTER"); +``` +In this example, there is two sets of limits on the same line side (1). The selected set is the winter one: the limits violations will be tested against this set. + (phase-tap-changer)= ## Phase tap changer [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/PhaseTapChanger.html) From 20a4dfa102cf8d5378497b67b510dea504c07bb7 Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Thu, 19 Sep 2024 14:23:54 +0200 Subject: [PATCH 08/14] Map the CGMES OperationalLimitSet.rdfid to the IIDM OperationalLimitsGroup.id, ensuring the id's unicity and a strict 1:1 mapping. Signed-off-by: Romain Courtier --- ...MicroGridTestConfiguration_BC_BE_EQ_V2.xml | 6 +- .../elements/OperationalLimitConversion.java | 3 +- .../conversion/export/EquipmentExport.java | 73 +++++++------------ .../test/OperationalLimitsGroupTest.java | 51 ++++++------- .../test/export/ExportXmlCompare.java | 7 +- .../issues/ExportNumberMaxValueTest.java | 2 +- .../src/test/resources/OperationalLimits.xml | 62 ++++++++-------- .../src/main/resources/CIM100.sparql | 1 - .../src/main/resources/CIM16.sparql | 1 - docs/grid_exchange_formats/cgmes/import.md | 16 ++-- 10 files changed, 94 insertions(+), 128 deletions(-) diff --git a/cgmes/cgmes-conformity/src/main/resources/conformity-modified/cas-1.1.3-data-4.0.3/MicroGrid/BaseCase/BC_BE_v2_limits/MicroGridTestConfiguration_BC_BE_EQ_V2.xml b/cgmes/cgmes-conformity/src/main/resources/conformity-modified/cas-1.1.3-data-4.0.3/MicroGrid/BaseCase/BC_BE_v2_limits/MicroGridTestConfiguration_BC_BE_EQ_V2.xml index 23ffa5577c9..64c6837aa2f 100644 --- a/cgmes/cgmes-conformity/src/main/resources/conformity-modified/cas-1.1.3-data-4.0.3/MicroGrid/BaseCase/BC_BE_v2_limits/MicroGridTestConfiguration_BC_BE_EQ_V2.xml +++ b/cgmes/cgmes-conformity/src/main/resources/conformity-modified/cas-1.1.3-data-4.0.3/MicroGrid/BaseCase/BC_BE_v2_limits/MicroGridTestConfiguration_BC_BE_EQ_V2.xml @@ -2205,10 +2205,6 @@ _OLS_V_1000 - - Limits at Port 1 - - _OLS_V_1000 @@ -2261,7 +2257,7 @@ patl_apparent_power - + 22863.1 diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java index 1d83513b6b8..b5dcc6fb27a 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java @@ -28,14 +28,13 @@ public class OperationalLimitConversion extends AbstractIdentifiedObjectConversi private static final String OPERATIONAL_LIMIT_TYPE_NAME = "operationalLimitTypeName"; private static final String OPERATIONAL_LIMIT_SUBCLASS = "OperationalLimitSubclass"; private static final String OPERATIONAL_LIMIT_SET = "OperationalLimitSet"; - private static final String OPERATIONAL_LIMIT_SET_NAME = "limitSetName"; private static final String PERMANENT_LIMIT = "Permanent Limit"; private static final String TEMPORARY_LIMIT = "Temporary Limit"; public OperationalLimitConversion(PropertyBag l, Context context) { super("OperationalLimit", l, context); String limitSubclass = p.getLocal(OPERATIONAL_LIMIT_SUBCLASS); - String limitSet = p.getLocal(OPERATIONAL_LIMIT_SET_NAME) != null ? p.getLocal(OPERATIONAL_LIMIT_SET_NAME) : p.getLocal(OPERATIONAL_LIMIT_SET); + String limitSet = p.getLocal(OPERATIONAL_LIMIT_SET); // Limit can associated to a Terminal or to an Equipment terminalId = l.getId("Terminal"); equipmentId = l.getId("Equipment"); diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java index b9d33eb6014..3dc8e31000e 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java @@ -1172,18 +1172,7 @@ private static void writeBranchLimits(Branch branch, String terminalId1, Stri branch.getSelectedOperationalLimitsGroup1().ifPresent(limitsGroups1::add); } for (OperationalLimitsGroup limitsGroup : limitsGroups1) { - Optional activePowerLimits1 = limitsGroup.getActivePowerLimits(); - if (activePowerLimits1.isPresent()) { - writeLoadingLimits(activePowerLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); - } - Optional apparentPowerLimits1 = limitsGroup.getApparentPowerLimits(); - if (apparentPowerLimits1.isPresent()) { - writeLoadingLimits(apparentPowerLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); - } - Optional currentLimits1 = limitsGroup.getCurrentLimits(); - if (currentLimits1.isPresent()) { - writeLoadingLimits(currentLimits1.get(), terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); - } + writeLimitsGroup(limitsGroup, terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } Collection limitsGroups2 = new ArrayList<>(); @@ -1193,18 +1182,7 @@ private static void writeBranchLimits(Branch branch, String terminalId1, Stri branch.getSelectedOperationalLimitsGroup2().ifPresent(limitsGroups2::add); } for (OperationalLimitsGroup limitsGroup : limitsGroups2) { - Optional activePowerLimits2 = limitsGroup.getActivePowerLimits(); - if (activePowerLimits2.isPresent()) { - writeLoadingLimits(activePowerLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); - } - Optional apparentPowerLimits2 = limitsGroup.getApparentPowerLimits(); - if (apparentPowerLimits2.isPresent()) { - writeLoadingLimits(apparentPowerLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); - } - Optional currentLimits2 = limitsGroup.getCurrentLimits(); - if (currentLimits2.isPresent()) { - writeLoadingLimits(currentLimits2.get(), terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); - } + writeLimitsGroup(limitsGroup, terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } } @@ -1216,36 +1194,41 @@ private static void writeFlowsLimits(FlowsLimitsHolder holder, String terminalId holder.getSelectedOperationalLimitsGroup().ifPresent(limitsGroups::add); } for (OperationalLimitsGroup limitsGroup : limitsGroups) { - Optional activePowerLimits = limitsGroup.getActivePowerLimits(); - if (activePowerLimits.isPresent()) { - writeLoadingLimits(activePowerLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); - } - Optional apparentPowerLimits = limitsGroup.getApparentPowerLimits(); - if (apparentPowerLimits.isPresent()) { - writeLoadingLimits(apparentPowerLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); - } - Optional currentLimits = limitsGroup.getCurrentLimits(); - if (currentLimits.isPresent()) { - writeLoadingLimits(currentLimits.get(), terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, limitsGroup.getId(), exportedLimitTypes, writeInfiniteDuration, writer, context); - } + writeLimitsGroup(limitsGroup, terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } } - private static void writeLoadingLimits(LoadingLimits limits, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, String limitSetName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { - // Write the limit set. It contains at least the permanent limit - String className = loadingLimitClassName(limits); - String operationalLimitSetId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), OPERATIONAL_LIMIT_SET, ref(limitSetName)); - OperationalLimitSetEq.write(operationalLimitSetId, limitSetName, terminalId, cimNamespace, writer, context); + private static void writeLimitsGroup(OperationalLimitsGroup limitsGroup, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + // Write the OperationalLimitSet + String operationalLimitSetId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(limitsGroup.getId()), OPERATIONAL_LIMIT_SET); + OperationalLimitSetEq.write(operationalLimitSetId, limitsGroup.getId(), terminalId, cimNamespace, writer, context); + + // Write the OperationalLimit objects + Optional activePowerLimits = limitsGroup.getActivePowerLimits(); + if (activePowerLimits.isPresent()) { + writeLoadingLimits(activePowerLimits.get(), cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, operationalLimitSetId, exportedLimitTypes, writeInfiniteDuration, writer, context); + } + Optional apparentPowerLimits = limitsGroup.getApparentPowerLimits(); + if (apparentPowerLimits.isPresent()) { + writeLoadingLimits(apparentPowerLimits.get(), cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, operationalLimitSetId, exportedLimitTypes, writeInfiniteDuration, writer, context); + } + Optional currentLimits = limitsGroup.getCurrentLimits(); + if (currentLimits.isPresent()) { + writeLoadingLimits(currentLimits.get(), cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, operationalLimitSetId, exportedLimitTypes, writeInfiniteDuration, writer, context); + } + } + private static void writeLoadingLimits(LoadingLimits limits, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, String operationalLimitSetId, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { // Write the permanent limit type (if not already written) - String operationalLimitTypeId = context.getNamingStrategy().getCgmesId(OPERATIONAL_LIMIT_TYPE, PATL); + String operationalLimitTypeId = context.getNamingStrategy().getCgmesId(PATL, OPERATIONAL_LIMIT_TYPE); if (!exportedLimitTypes.contains(operationalLimitTypeId)) { OperationalLimitTypeEq.writePatl(operationalLimitTypeId, cimNamespace, euNamespace, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); exportedLimitTypes.add(operationalLimitTypeId); } // Write the permanent limit - String operationalLimitId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), ref(limitSetName), OPERATIONAL_LIMIT_VALUE, PATL); + String className = loadingLimitClassName(limits); + String operationalLimitId = context.getNamingStrategy().getCgmesId(ref(operationalLimitSetId), ref(className), PATL, OPERATIONAL_LIMIT_VALUE); LoadingLimitEq.write(operationalLimitId, limits, "PATL", limits.getPermanentLimit(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, valueAttributeName, writer, context); if (!limits.getTemporaryLimits().isEmpty()) { @@ -1253,14 +1236,14 @@ private static void writeLoadingLimits(LoadingLimits limits, String terminalId, int acceptableDuration = temporaryLimit.getAcceptableDuration(); // Write the temporary limit type (if not already written) - operationalLimitTypeId = context.getNamingStrategy().getCgmesId(OPERATIONAL_LIMIT_TYPE, TATL, ref(acceptableDuration)); + operationalLimitTypeId = context.getNamingStrategy().getCgmesId(TATL, ref(acceptableDuration), OPERATIONAL_LIMIT_TYPE); if (!exportedLimitTypes.contains(operationalLimitTypeId)) { OperationalLimitTypeEq.writeTatl(operationalLimitTypeId, temporaryLimit.getAcceptableDuration(), cimNamespace, euNamespace, limitTypeAttributeName, limitKindClassName, writeInfiniteDuration, writer, context); exportedLimitTypes.add(operationalLimitTypeId); } // Write the temporary limit - operationalLimitId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(className), ref(limitSetName), OPERATIONAL_LIMIT_VALUE, TATL, ref(acceptableDuration)); + operationalLimitId = context.getNamingStrategy().getCgmesId(ref(operationalLimitSetId), ref(className), TATL, ref(acceptableDuration), OPERATIONAL_LIMIT_VALUE); LoadingLimitEq.write(operationalLimitId, limits, temporaryLimit.getName(), temporaryLimit.getValue(), operationalLimitTypeId, operationalLimitSetId, cimNamespace, valueAttributeName, writer, context); } } 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 ae2cdeede73..a3a640a5cbc 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 @@ -38,28 +38,19 @@ void importMultipleLimitsGroupsOnSameLineEndTest() { Network network = Network.read("OperationalLimits.xml", getClass().getResourceAsStream("/OperationalLimits.xml")); Line line = network.getLine("Line"); - // There are 4 CGMES OperationalLimitSets on side 1 merged into 3 IIDM OperationalLimitsGroup - assertEquals(3, line.getOperationalLimitsGroups1().size()); - assertEquals(0, line.getOperationalLimitsGroups2().size()); + // There is 1 set on side 1, 2 sets on side 2 + assertEquals(1, line.getOperationalLimitsGroups1().size()); + assertEquals(2, line.getOperationalLimitsGroups2().size()); - // The CGMES winter current and active power limits have been merged into the same limits group - // since their OperationalLimitSet name are equals - Optional winterLimits = line.getOperationalLimitsGroup1("WINTER"); + // The winter set (_OLS_3) contains current limits and active power limits + Optional winterLimits = line.getOperationalLimitsGroup2("_OLS_3"); assertTrue(winterLimits.isPresent()); assertTrue(winterLimits.get().getCurrentLimits().isPresent()); assertTrue(winterLimits.get().getActivePowerLimits().isPresent()); - // The CGMES spring current limits and summer active power limits have different limits group - // since their OperationalLimitSet name are distinct - Optional springLimits = line.getOperationalLimitsGroup1("SPRING"); - assertTrue(springLimits.isPresent()); - assertTrue(springLimits.get().getCurrentLimits().isPresent()); - assertTrue(springLimits.get().getActivePowerLimits().isEmpty()); - - Optional summerLimits = line.getOperationalLimitsGroup1("SUMMER"); - assertTrue(summerLimits.isPresent()); - assertTrue(summerLimits.get().getCurrentLimits().isEmpty()); - assertTrue(summerLimits.get().getActivePowerLimits().isPresent()); + // When an end has only 1 set, this set gets selected, otherwise none is + assertTrue(line.getSelectedOperationalLimitsGroup1().isPresent()); + assertFalse(line.getSelectedOperationalLimitsGroup2().isPresent()); } @Test @@ -73,23 +64,23 @@ void exportSelectedLimitsGroupTest() throws IOException { network.write("CGMES", exportParams, tmpDir.resolve("ExportSelectedLimitsGroup.xml")); String exportSelectedLimitsGroupXml = Files.readString(tmpDir.resolve("ExportSelectedLimitsGroup_EQ.xml")); - // No OperationalLimitsGroup is exported - assertEquals(0, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_SET).size()); - assertEquals(0, getOccurrences(exportSelectedLimitsGroupXml, OPERATIONAL_LIMIT_TYPE).size()); + // 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(0, getOccurrences(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size()); + assertEquals(3, getOccurrences(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size()); - // Manually select one limits group + // Manually select one of the limits group on side 2 and export again Line line = network.getLine("Line"); - line.setSelectedOperationalLimitsGroup1("WINTER"); + line.setSelectedOperationalLimitsGroup2("_OLS_2"); network.write("CGMES", exportParams, tmpDir.resolve("ExportSelectedLimitsGroup.xml")); exportSelectedLimitsGroupXml = Files.readString(tmpDir.resolve("ExportSelectedLimitsGroup_EQ.xml")); - // The selected limits group contains 2 sets of limits (current and active power) + // 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(3, getOccurrences(exportSelectedLimitsGroupXml, ACTIVE_POWER_LIMIT).size()); - assertEquals(3, getOccurrences(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size()); + assertEquals(0, getOccurrences(exportSelectedLimitsGroupXml, ACTIVE_POWER_LIMIT).size()); + assertEquals(6, getOccurrences(exportSelectedLimitsGroupXml, CURRENT_LIMIT).size()); } @Test @@ -103,11 +94,11 @@ void exportAllLimitsGroupTest() throws IOException { network.write("CGMES", exportParams, tmpDir.resolve("ExportAllLimitsGroup.xml")); String exportAllLimitsGroupXml = Files.readString(tmpDir.resolve("ExportAllLimitsGroup_EQ.xml")); - // All 4 OperationalLimitsGroup are exported - assertEquals(4, getOccurrences(exportAllLimitsGroupXml, OPERATIONAL_LIMIT_SET).size()); + // 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(6, getOccurrences(exportAllLimitsGroupXml, ACTIVE_POWER_LIMIT).size()); - assertEquals(6, getOccurrences(exportAllLimitsGroupXml, CURRENT_LIMIT).size()); + assertEquals(3, getOccurrences(exportAllLimitsGroupXml, ACTIVE_POWER_LIMIT).size()); + assertEquals(9, getOccurrences(exportAllLimitsGroupXml, CURRENT_LIMIT).size()); } private Set getOccurrences(String xml, Pattern pattern) { diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportXmlCompare.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportXmlCompare.java index 621962f15c5..93801d956dc 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportXmlCompare.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportXmlCompare.java @@ -130,6 +130,8 @@ private static boolean isConsideredForEQNetwork(Attr attr) { if (elementName != null) { if (elementName.equals("danglingLine")) { ignored = true; + } else if (elementName.contains("operationalLimitsGroup")) { + ignored = attr.getLocalName().equals("id"); } else if (elementName.startsWith("network")) { ignored = attr.getLocalName().equals("id") || attr.getLocalName().equals("forecastDistance") || attr.getLocalName().equals("caseDate") || attr.getLocalName().equals("sourceFormat"); } else if (elementName.startsWith("voltageLevel")) { @@ -145,7 +147,10 @@ private static boolean isConsideredForEQNetwork(Attr attr) { // So we do not enforce this attribute to be equal in the original and exported network ignored = attr.getLocalName().equals("ratedS"); } else { - ignored = attr.getLocalName().contains("node") || attr.getLocalName().contains("bus") || attr.getLocalName().contains("Bus"); + ignored = attr.getLocalName().contains("node") + || attr.getLocalName().contains("bus") + || attr.getLocalName().contains("Bus") + || attr.getLocalName().contains("selectedOperationalLimitsGroupId"); } } return !ignored; diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ExportNumberMaxValueTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ExportNumberMaxValueTest.java index 3ae8eb77236..2e58607c14e 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ExportNumberMaxValueTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ExportNumberMaxValueTest.java @@ -58,7 +58,7 @@ void testTemporaryLimitWithoutValue() throws IOException { // Look for the value of the temporary limit with acceptable duration 60 // We know exactly which identifier to look for // Include multiple lines and non-greedy matches - Pattern tatl60Pattern = Pattern.compile(".*?(.*?)", Pattern.DOTALL); + Pattern tatl60Pattern = Pattern.compile(".*?(.*?)", Pattern.DOTALL); Matcher tatl60Value = tatl60Pattern.matcher(eq); assertTrue(tatl60Value.find()); // Read the value as a double diff --git a/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml b/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml index 33f1daa44b6..6d260090e89 100644 --- a/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml +++ b/cgmes/cgmes-conversion/src/test/resources/OperationalLimits.xml @@ -76,7 +76,7 @@ - WINTER + SPRING @@ -98,30 +98,30 @@ - WINTER - + SPRING + - + Limit1 102 - - + + Limit2 202 - - + + Limit3 302 - + - SPRING - + WINTER + Limit1 @@ -141,28 +141,24 @@ - - SUMMER - - - - Limit1 - 104 - - - - - Limit2 - 204 - - - - - Limit3 - 304 - - - + + Limit4 + 104 + + + + + Limit5 + 204 + + + + + Limit6 + 304 + + + diff --git a/cgmes/cgmes-model/src/main/resources/CIM100.sparql b/cgmes/cgmes-model/src/main/resources/CIM100.sparql index 675a391ea3d..cf0b350cb2c 100644 --- a/cgmes/cgmes-model/src/main/resources/CIM100.sparql +++ b/cgmes/cgmes-model/src/main/resources/CIM100.sparql @@ -114,7 +114,6 @@ WHERE { OPTIONAL { ?OperationalLimit cim:CurrentLimit.normalValue ?normalValue } OPTIONAL { ?OperationalLimit cim:ApparentPowerLimit.normalValue ?normalValue } OPTIONAL { ?OperationalLimit cim:VoltageLimit.normalValue ?normalValue } - OPTIONAL { ?OperationalLimitSet cim:IdentifiedObject.name ?limitSetName } # operational limit sets can be attached to terminals or equipments OPTIONAL { ?OperationalLimitSet cim:OperationalLimitSet.Terminal ?Terminal } OPTIONAL { GRAPH ?graph2 { diff --git a/cgmes/cgmes-model/src/main/resources/CIM16.sparql b/cgmes/cgmes-model/src/main/resources/CIM16.sparql index a98860078c0..c72ad19a4ca 100644 --- a/cgmes/cgmes-model/src/main/resources/CIM16.sparql +++ b/cgmes/cgmes-model/src/main/resources/CIM16.sparql @@ -380,7 +380,6 @@ WHERE OPTIONAL { ?OperationalLimit cim:CurrentLimit.value ?value } OPTIONAL { ?OperationalLimit cim:ApparentPowerLimit.value ?value } OPTIONAL { ?OperationalLimit cim:VoltageLimit.value ?value } - OPTIONAL { ?OperationalLimitSet cim:IdentifiedObject.name ?limitSetName } # operational limit sets can be attached to terminals or equipments OPTIONAL { ?OperationalLimitSet cim:OperationalLimitSet.Terminal ?Terminal } OPTIONAL { GRAPH ?graph2 { diff --git a/docs/grid_exchange_formats/cgmes/import.md b/docs/grid_exchange_formats/cgmes/import.md index e692b381ec0..687363d6843 100644 --- a/docs/grid_exchange_formats/cgmes/import.md +++ b/docs/grid_exchange_formats/cgmes/import.md @@ -311,23 +311,21 @@ OperationalLimits model a specification of limits associated with equipments. #### OperationalLimitSet A CGMES `OperationalLimitSet` is a set of `OperationalLimit` associated with equipment or terminal. It is mapped to a PowSyBl [`OperationalLimitsGroup`](../../grid_model/additional.md#limit-group-collection). -The `id` attribute is copied from CGMES `name`. Just like CGMES allows to attach multiple `OperationalLimitSet` on the same equipment or terminal, PowSyBl stores a collection of `OperationalLimitsGroup` for every -[`Line`](../../grid_model/network_subnetwork.md#line) (actually 2 collections for a Line: one for each side), [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line) and [`ThreeWindingTransformer.Leg`](../../grid_model/network_subnetwork.md#three-winding-transformer-leg). +[`Line`](../../grid_model/network_subnetwork.md#line) side, [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line) and [`ThreeWindingTransformer.Leg`](../../grid_model/network_subnetwork.md#three-winding-transformer-leg). -In case of multiple `OperationalLimitSet` attached to the same end: -- If the sets have different names, they are imported in different `OperationalLimitsGroup`. For instance, a `WINTER` set and a `SUMMER` set will be imported in two distinct groups. -- If the sets have the same name, the sets are merged in the same `OperationalLimitsGroup`. For instance, a `WINTER` set of CurrentLimit and a `WINTER` set of ActivePowerLimit will be imported in the same group. +The same way a CGMES `OperationalLimitSet` may contain `OperationalLimit` of different subclasses, a PowSyBl `OperationalLimitsGroup` may have multipe non-null `LoadingLimits`. If there is only one `OperationalLimitsGroup` on an end, it automatically gets to be selected (active). However, if there is multiple groups, none is selected: the user has to choose which set is active. #### OperationalLimit -A CGMES `OperationalLimit` is an abstract class that represent different kinds of limits: current, active power or apparent power. The collection of CGMES `OperationalLimit` in the set is mapped to a PowSyBl [`LoadingLimits`](../../grid_model/additional.md#loading-limits) as follows: -- The set of CGMES `CurrentLimit` in the `OperationalLimitSet` are mapped to the `currentLimits` attribute of the PowSyBl `OperationalLimitsGroup` corresponding to the set. -- The set of CGMES `ActivePowerLimit` in the `OperationalLimitSet` are mapped to the `activePowerLimits` attribute of the PowSyBl `OperationalLimitsGroup` corresponding to the set. -- The set of CGMES `ApparentPowerLimit` in the `OperationalLimitSet` are mapped to the `apparentPowerLimits` attribute of the PowSyBl `OperationalLimitsGroup` corresponding to the set. +A CGMES `OperationalLimit` is an abstract class that represent different kinds of limits: current, active power or apparent power. +The collection of the same subclass of CGMES `OperationalLimit` in the set is mapped to a PowSyBl [`LoadingLimits`](../../grid_model/additional.md#loading-limits) as follows: +- The collection of CGMES `CurrentLimit` in the `OperationalLimitSet` is mapped to the `currentLimits` attribute of the PowSyBl `OperationalLimitsGroup` corresponding to the set. +- The collection of CGMES `ActivePowerLimit` in the `OperationalLimitSet` is mapped to the `activePowerLimits` attribute of the PowSyBl `OperationalLimitsGroup` corresponding to the set. +- The collection of CGMES `ApparentPowerLimit` in the `OperationalLimitSet` is mapped to the `apparentPowerLimits` attribute of the PowSyBl `OperationalLimitsGroup` corresponding to the set. A particular CGMES `OperationalLimit` is mapped differently depending on its associated CGMES `OperationalLimitType`: - A permanent limit (`OperationalLimitType.limitType` is `LimitTypeKind.patl`) is mapped as follows: From 1be3e9e191a51df0ec95deccb27af88b353b7588 Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Fri, 20 Sep 2024 19:12:50 +0200 Subject: [PATCH 09/14] Support CGMES import and export of OperationalLimitSet.rdfid and name in properties attached to the equipment Signed-off-by: Romain Courtier --- .../elements/OperationalLimitConversion.java | 155 +++++++++--------- .../conversion/export/EquipmentExport.java | 29 ++-- .../test/export/EquipmentExportTest.java | 16 +- .../test/export/ExportXmlCompare.java | 22 ++- .../com/powsybl/cgmes/model/CgmesNames.java | 2 + .../src/main/resources/CIM100.sparql | 1 + .../src/main/resources/CIM14.sparql | 1 + .../src/main/resources/CIM16.sparql | 1 + 8 files changed, 136 insertions(+), 91 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java index b5dcc6fb27a..b84ce3f3025 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java @@ -9,6 +9,8 @@ package com.powsybl.cgmes.conversion.elements; import com.powsybl.cgmes.conversion.Context; +import com.powsybl.cgmes.conversion.Conversion; +import com.powsybl.cgmes.model.CgmesNames; import com.powsybl.iidm.network.*; import com.powsybl.triplestore.api.PropertyBag; @@ -27,14 +29,18 @@ public class OperationalLimitConversion extends AbstractIdentifiedObjectConversi private static final String OPERATIONAL_LIMIT = "Operational limit"; private static final String OPERATIONAL_LIMIT_TYPE_NAME = "operationalLimitTypeName"; private static final String OPERATIONAL_LIMIT_SUBCLASS = "OperationalLimitSubclass"; - private static final String OPERATIONAL_LIMIT_SET = "OperationalLimitSet"; + private static final String OPERATIONAL_LIMIT_SET_ID = "OperationalLimitSet"; + private static final String OPERATIONAL_LIMIT_SET_NAME = "OperationalLimitSetName"; + private static final String PROPERTY_PREFIX = Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + CgmesNames.OPERATIONAL_LIMIT_SET + "_"; private static final String PERMANENT_LIMIT = "Permanent Limit"; private static final String TEMPORARY_LIMIT = "Temporary Limit"; public OperationalLimitConversion(PropertyBag l, Context context) { super("OperationalLimit", l, context); String limitSubclass = p.getLocal(OPERATIONAL_LIMIT_SUBCLASS); - String limitSet = p.getLocal(OPERATIONAL_LIMIT_SET); + String limitSetId = p.getLocal(OPERATIONAL_LIMIT_SET_ID); + String limitSetName = p.getLocal(OPERATIONAL_LIMIT_SET_NAME); + limitSetName = limitSetName != null ? limitSetName : limitSetId; // Limit can associated to a Terminal or to an Equipment terminalId = l.getId("Terminal"); equipmentId = l.getId("Equipment"); @@ -44,11 +50,11 @@ public OperationalLimitConversion(PropertyBag l, Context context) { terminal = context.terminalMapping().findForFlowLimits(terminalId); } if (terminal != null) { - createLimitsAdder(context.terminalMapping().number(terminalId), limitSubclass, limitSet, terminal.getConnectable()); + checkAndCreateLimitsAdder(context.terminalMapping().number(terminalId), limitSubclass, limitSetId, limitSetName, terminal.getConnectable()); } else if (equipmentId != null) { // The equipment may be a Branch, a Dangling line, a Switch ... Identifiable identifiable = context.network().getIdentifiable(equipmentId); - createLimitsAdder(-1, limitSubclass, limitSet, identifiable); + checkAndCreateLimitsAdder(-1, limitSubclass, limitSetId, limitSetName, identifiable); } } else if (limitSubclass.equals("VoltageLimit")) { if (terminalId != null) { @@ -74,42 +80,6 @@ private void setVoltageLevelForVoltageLimit(Terminal terminal) { } } - /** - * Get the LoadingLimitsAdder supplier for the given FlowsLimitsHolder and limit set + subclass. - * @param limitSubClass The subclass of the OperationalLimit. - * @param limitSet The set containing the OperationalLimit. - * @param holder The equipment to which the OperationalLimit applies. - * @return The appropriate LoadingLimitsAdder supplier. - */ - private Supplier> getLoadingLimitAdderSupplier(String limitSubClass, String limitSet, FlowsLimitsHolder holder) { - OperationalLimitsGroup limitsGroup = holder.getOperationalLimitsGroup(limitSet).orElseGet(() -> holder.newOperationalLimitsGroup(limitSet)); - return getLoadingLimitAdderSupplier(limitsGroup, limitSubClass); - } - - /** - * Get the LoadingLimitsAdder supplier for the side 1 of the given branch and limit set + subclass. - * @param limitSubClass The subclass of the OperationalLimit. - * @param limitSet The set containing the OperationalLimit. - * @param b The branch to which the OperationalLimit applies on side 1. - * @return The appropriate LoadingLimitsAdder supplier. - */ - private Supplier> getLoadingLimitAdder1Supplier(String limitSubClass, String limitSet, Branch b) { - OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup1(limitSet).orElseGet(() -> b.newOperationalLimitsGroup1(limitSet)); - return getLoadingLimitAdderSupplier(limitsGroup, limitSubClass); - } - - /** - * Get the LoadingLimitsAdder supplier for the side 2 of the given branch and limit set + subclass. - * @param limitSubClass The subclass of the OperationalLimit. - * @param limitSet The set containing the OperationalLimit. - * @param b The branch to which the OperationalLimit applies on side 2. - * @return The appropriate LoadingLimitsAdder supplier. - */ - private Supplier> getLoadingLimitAdder2Supplier(String limitSubClass, String limitSet, Branch b) { - OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup2(limitSet).orElseGet(() -> b.newOperationalLimitsGroup2(limitSet)); - return getLoadingLimitAdderSupplier(limitsGroup, limitSubClass); - } - /** * Get the LoadingLimitsAdder supplier for the given limits group and subclass. * @param limitsGroup The limit group instance for which the adder is called. @@ -128,7 +98,7 @@ private void setVoltageLevelForVoltageLimit(Terminal terminal) { case CURRENT_LIMIT: return limitsGroup::newCurrentLimits; default: - throw new IllegalStateException(); + throw new IllegalArgumentException(); } } @@ -136,40 +106,73 @@ private void setVoltageLevelForVoltageLimit(Terminal terminal) { * Create the LoadingLimitsAdder for the given branch + side and the given limit set + subclass. * @param terminalNumber The side of the branch to which the OperationalLimit applies. * @param limitSubClass The subclass of the OperationalLimit. - * @param limitSet The set containing the OperationalLimit. + * @param limitSetId The id of the set containing the OperationalLimit. + * @param limitSetName The name of the set containing the OperationalLimit. * @param b The branch to which the OperationalLimit applies. */ - private void createLimitsAdder(int terminalNumber, String limitSubClass, String limitSet, Branch b) { + private void createLimitsAdder(int terminalNumber, String limitSubClass, String limitSetId, String limitSetName, Branch b) { if (terminalNumber == 1) { - loadingLimitsAdder1 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_1_" + limitSubClass + "_" + limitSet, - getLoadingLimitAdder1Supplier(limitSubClass, limitSet, b)); + OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup1(limitSetId).orElseGet(() -> { + b.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); + return b.newOperationalLimitsGroup1(limitSetId); }); + loadingLimitsAdder1 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_1_" + limitSubClass + "_" + limitSetId, + getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); } else if (terminalNumber == 2) { - loadingLimitsAdder2 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_2_" + limitSubClass + "_" + limitSet, - getLoadingLimitAdder2Supplier(limitSubClass, limitSet, b)); + OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup2(limitSetId).orElseGet(() -> { + b.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); + return b.newOperationalLimitsGroup2(limitSetId); }); + loadingLimitsAdder2 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_2_" + limitSubClass + "_" + limitSetId, + getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); } else { - notAssigned(b); + throw new IllegalArgumentException(); } } + /** + * Create the LoadingLimitsAdder for the given dangling line and the given limit set + subclass. + * @param terminalNumber The side of the branch to which the OperationalLimit applies. + * @param limitSubClass The subclass of the OperationalLimit. + * @param limitSetId The set containing the OperationalLimit. + * @param limitSetName The name of the set containing the OperationalLimit. + * @param dl The branch to which the OperationalLimit applies. + */ + private void createLimitsAdder(String limitSubClass, String limitSetId, String limitSetName, DanglingLine dl) { + OperationalLimitsGroup limitsGroup = dl.getOperationalLimitsGroup(limitSetId).orElseGet(() -> { + dl.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); + return dl.newOperationalLimitsGroup(limitSetId); }); + loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(dl.getId() + "_" + limitSubClass + "_" + limitSetId, + getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); + } + /** * Create the LoadingLimitsAdder for the given 3w-transformer + side and the given limit set + subclass. * @param terminalNumber The side of the transformer to which the OperationalLimit applies. * @param limitSubClass The subclass of the OperationalLimit. - * @param limitSet The set containing the OperationalLimit. + * @param limitSetId The set containing the OperationalLimit. + * @param limitSetName The name of the set containing the OperationalLimit. * @param twt The 3w-transformer to which the OperationalLimit applies. */ - private void createLimitsAdder(int terminalNumber, String limitSubClass, String limitSet, ThreeWindingsTransformer twt) { + private void createLimitsAdder(int terminalNumber, String limitSubClass, String limitSetId, String limitSetName, ThreeWindingsTransformer twt) { if (terminalNumber == 1) { - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_1_" + limitSubClass + "_" + limitSet, - getLoadingLimitAdderSupplier(limitSubClass, limitSet, twt.getLeg1())); + OperationalLimitsGroup limitsGroup = twt.getLeg1().getOperationalLimitsGroup(limitSetId).orElseGet(() -> { + twt.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); + return twt.getLeg1().newOperationalLimitsGroup(limitSetId); }); + loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_1_" + limitSubClass + "_" + limitSetId, + getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); } else if (terminalNumber == 2) { - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_2_" + limitSubClass + "_" + limitSet, - getLoadingLimitAdderSupplier(limitSubClass, limitSet, twt.getLeg2())); + OperationalLimitsGroup limitsGroup = twt.getLeg2().getOperationalLimitsGroup(limitSetId).orElseGet(() -> { + twt.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); + return twt.getLeg2().newOperationalLimitsGroup(limitSetId); }); + loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_2_" + limitSubClass + "_" + limitSetId, + getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); } else if (terminalNumber == 3) { - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_3_" + limitSubClass + "_" + limitSet, - getLoadingLimitAdderSupplier(limitSubClass, limitSet, twt.getLeg3())); + OperationalLimitsGroup limitsGroup = twt.getLeg3().getOperationalLimitsGroup(limitSetId).orElseGet(() -> { + twt.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); + return twt.getLeg3().newOperationalLimitsGroup(limitSetId); }); + loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_3_" + limitSubClass + "_" + limitSetId, + getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); } else { - notAssigned(twt); + throw new IllegalArgumentException(); } } @@ -178,36 +181,42 @@ private void createLimitsAdder(int terminalNumber, String limitSubClass, String * If the inputs are inconsistent, no limit adder is created. * @param terminalNumber The side of the equipment to which the OperationalLimit applies. * @param limitSubClass The subclass of the OperationalLimit. - * @param limitSet The set containing the OperationalLimit. + * @param limitSetId The set containing the OperationalLimit. + * @param limitSetName The name of the set containing the OperationalLimit. * @param identifiable The equipment to which the OperationalLimit applies. */ - private void createLimitsAdder(int terminalNumber, String limitSubClass, String limitSet, Identifiable identifiable) { + private void checkAndCreateLimitsAdder(int terminalNumber, String limitSubClass, String limitSetId, String limitSetName, Identifiable identifiable) { if (identifiable instanceof Line) { Branch b = (Branch) identifiable; if (terminalNumber == -1) { // Limits appliy to the whole equipment == to both sides - createLimitsAdder(1, limitSubClass, limitSet, b); - createLimitsAdder(2, limitSubClass, limitSet, b); + createLimitsAdder(1, limitSubClass, limitSetId, limitSetName, b); + createLimitsAdder(2, limitSubClass, limitSetId, limitSetName, b); + } else if (terminalNumber == 1 || terminalNumber == 2) { + createLimitsAdder(terminalNumber, limitSubClass, limitSetId, limitSetName, b); } else { - createLimitsAdder(terminalNumber, limitSubClass, limitSet, b); + notAssigned(b); } } else if (identifiable instanceof TwoWindingsTransformer) { Branch b = (Branch) identifiable; - if (terminalNumber == -1) { - context.ignored(limitSubClass, "Defined for Equipment TwoWindingsTransformer. Should be defined for one Terminal of Two"); - notAssigned(b); + if (terminalNumber == 1 || terminalNumber == 2) { + createLimitsAdder(terminalNumber, limitSubClass, limitSetId, limitSetName, b); } else { - createLimitsAdder(terminalNumber, limitSubClass, limitSet, b); + if (terminalNumber == -1) { + context.ignored(limitSubClass, "Defined for Equipment TwoWindingsTransformer. Should be defined for one Terminal of Two"); + } + notAssigned(b); } - } else if (identifiable instanceof DanglingLine danglingLine) { - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(danglingLine.getId() + "_" + limitSubClass + "_" + limitSet, - getLoadingLimitAdderSupplier(limitSubClass, limitSet, danglingLine)); + } else if (identifiable instanceof DanglingLine dl) { + createLimitsAdder(limitSubClass, limitSetId, limitSetName, dl); } else if (identifiable instanceof ThreeWindingsTransformer twt) { - if (terminalNumber == -1) { - context.ignored(limitSubClass, "Defined for Equipment ThreeWindingsTransformer. Should be defined for one Terminal of Three"); - notAssigned(twt); + if (terminalNumber == 1 || terminalNumber == 2 || terminalNumber == 3) { + createLimitsAdder(terminalNumber, limitSubClass, limitSetId, limitSetName, twt); } else { - createLimitsAdder(terminalNumber, limitSubClass, limitSet, twt); + if (terminalNumber == -1) { + context.ignored(limitSubClass, "Defined for Equipment ThreeWindingsTransformer. Should be defined for one Terminal of Three"); + } + notAssigned(twt); } } else if (identifiable instanceof Switch) { Switch aswitch = context.network().getSwitch(equipmentId); diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java index 3dc8e31000e..c9ec4f180c9 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java @@ -828,7 +828,7 @@ private static void writeThreeWindingsTransformerEnd(ThreeWindingsTransformer tw PowerTransformerEq.writeEnd(endId, twtName, twtId, endNumber, r, x, g, b, leg.getRatedS(), leg.getRatedU(), terminalId, baseVoltage.getId(), cimNamespace, writer, context); writePhaseTapChanger(twt, leg.getPhaseTapChanger(), twtName, legNumber, endId, leg.getRatedU(), regulatingControlsWritten, cimNamespace, writer, context); writeRatioTapChanger(twt, leg.getRatioTapChanger(), twtName, legNumber, endId, leg.getRatedU(), regulatingControlsWritten, cimNamespace, writer, context); - writeFlowsLimits(leg, terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); + writeFlowsLimits(twt, leg, terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } private static > void writePhaseTapChanger(C eq, PhaseTapChanger ptc, String twtName, int endNumber, String endId, double neutralU, Set regulatingControlsWritten, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { @@ -983,10 +983,10 @@ private static void writeUnpairedOrPairedDanglingLines(List dangli AcLineSegmentEq.write(context.getNamingStrategy().getCgmesId(danglingLine), danglingLine.getNameOrId(), context.getBaseVoltageByNominalVoltage(danglingLine.getTerminal().getVoltageLevel().getNominalV()).getId(), danglingLine.getR(), danglingLine.getX(), danglingLine.getG(), danglingLine.getB(), cimNamespace, writer, context); - writeFlowsLimits(danglingLine, exportedTerminalId(mapTerminal2Id, danglingLine.getTerminal()), cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); + writeFlowsLimits(danglingLine, danglingLine, exportedTerminalId(mapTerminal2Id, danglingLine.getTerminal()), cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); danglingLine.getAliasFromType("CGMES." + TERMINAL_BOUNDARY).ifPresent(terminalBdId -> { try { - writeFlowsLimits(danglingLine, terminalBdId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); + writeFlowsLimits(danglingLine, danglingLine, terminalBdId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } catch (XMLStreamException e) { throw new UncheckedXmlStreamException(e); } @@ -1172,7 +1172,7 @@ private static void writeBranchLimits(Branch branch, String terminalId1, Stri branch.getSelectedOperationalLimitsGroup1().ifPresent(limitsGroups1::add); } for (OperationalLimitsGroup limitsGroup : limitsGroups1) { - writeLimitsGroup(limitsGroup, terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); + writeLimitsGroup(branch, limitsGroup, terminalId1, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } Collection limitsGroups2 = new ArrayList<>(); @@ -1182,11 +1182,11 @@ private static void writeBranchLimits(Branch branch, String terminalId1, Stri branch.getSelectedOperationalLimitsGroup2().ifPresent(limitsGroups2::add); } for (OperationalLimitsGroup limitsGroup : limitsGroups2) { - writeLimitsGroup(limitsGroup, terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); + writeLimitsGroup(branch, limitsGroup, terminalId2, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } } - private static void writeFlowsLimits(FlowsLimitsHolder holder, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + private static void writeFlowsLimits(Identifiable identifiable, FlowsLimitsHolder holder, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { Collection limitsGroups = new ArrayList<>(); if (context.isExportAllLimitsGroup()) { limitsGroups.addAll(holder.getOperationalLimitsGroups()); @@ -1194,14 +1194,23 @@ private static void writeFlowsLimits(FlowsLimitsHolder holder, String terminalId holder.getSelectedOperationalLimitsGroup().ifPresent(limitsGroups::add); } for (OperationalLimitsGroup limitsGroup : limitsGroups) { - writeLimitsGroup(limitsGroup, terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); + writeLimitsGroup(identifiable, limitsGroup, terminalId, cimNamespace, euNamespace, valueAttributeName, limitTypeAttributeName, limitKindClassName, exportedLimitTypes, writeInfiniteDuration, writer, context); } } - private static void writeLimitsGroup(OperationalLimitsGroup limitsGroup, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + private static void writeLimitsGroup(Identifiable identifiable, OperationalLimitsGroup limitsGroup, String terminalId, String cimNamespace, String euNamespace, String valueAttributeName, String limitTypeAttributeName, String limitKindClassName, Set exportedLimitTypes, boolean writeInfiniteDuration, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { // Write the OperationalLimitSet - String operationalLimitSetId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(limitsGroup.getId()), OPERATIONAL_LIMIT_SET); - OperationalLimitSetEq.write(operationalLimitSetId, limitsGroup.getId(), terminalId, cimNamespace, writer, context); + String operationalLimitSetId; + String operationalLimitSetName; + String propertyKey = Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + CgmesNames.OPERATIONAL_LIMIT_SET + "_" + limitsGroup.getId(); + if (identifiable.hasProperty(propertyKey)) { + operationalLimitSetId = limitsGroup.getId(); + operationalLimitSetName = identifiable.getProperty(propertyKey); + } else { + operationalLimitSetId = context.getNamingStrategy().getCgmesId(ref(terminalId), ref(limitsGroup.getId()), OPERATIONAL_LIMIT_SET); + operationalLimitSetName = limitsGroup.getId(); + } + OperationalLimitSetEq.write(operationalLimitSetId, operationalLimitSetName, terminalId, cimNamespace, writer, context); // Write the OperationalLimit objects Optional activePowerLimits = limitsGroup.getActivePowerLimits(); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java index 1c74cb09b75..42f4c34dda4 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java @@ -218,7 +218,13 @@ void nordic32() throws IOException, XMLStreamException { TwoWindingsTransformer twta = actual.getTwoWindingsTransformerStream().findFirst().orElseThrow(); network.getTwoWindingsTransformers().forEach(twtn -> twtn.setRatedS(twta.getRatedS())); - assertTrue(compareNetworksEQdata(network, actual)); + // Ignore OperationalLimitsGroup id + DifferenceEvaluator knownDiffs = DifferenceEvaluators.chain( + DifferenceEvaluators.Default, + ExportXmlCompare::numericDifferenceEvaluator, + ExportXmlCompare::ignoringNonEQ, + ExportXmlCompare::ignoringOperationalLimitsGroupId); + assertTrue(compareNetworksEQdata(network, actual, knownDiffs)); } @Test @@ -238,7 +244,13 @@ void nordic32SortTransformerEnds() throws IOException, XMLStreamException { TwoWindingsTransformer twta = actual.getTwoWindingsTransformerStream().findFirst().orElseThrow(); network.getTwoWindingsTransformers().forEach(twtn -> twtn.setRatedS(twta.getRatedS())); - assertTrue(compareNetworksEQdata(network, actual)); + // Ignore OperationalLimitsGroup id + DifferenceEvaluator knownDiffs = DifferenceEvaluators.chain( + DifferenceEvaluators.Default, + ExportXmlCompare::numericDifferenceEvaluator, + ExportXmlCompare::ignoringNonEQ, + ExportXmlCompare::ignoringOperationalLimitsGroupId); + assertTrue(compareNetworksEQdata(network, actual, knownDiffs)); } private void prepareNetworkForSortedTransformerEndsComparison(Network network) { diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportXmlCompare.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportXmlCompare.java index 93801d956dc..dca16ba77ba 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportXmlCompare.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportXmlCompare.java @@ -130,8 +130,6 @@ private static boolean isConsideredForEQNetwork(Attr attr) { if (elementName != null) { if (elementName.equals("danglingLine")) { ignored = true; - } else if (elementName.contains("operationalLimitsGroup")) { - ignored = attr.getLocalName().equals("id"); } else if (elementName.startsWith("network")) { ignored = attr.getLocalName().equals("id") || attr.getLocalName().equals("forecastDistance") || attr.getLocalName().equals("caseDate") || attr.getLocalName().equals("sourceFormat"); } else if (elementName.startsWith("voltageLevel")) { @@ -147,10 +145,7 @@ private static boolean isConsideredForEQNetwork(Attr attr) { // So we do not enforce this attribute to be equal in the original and exported network ignored = attr.getLocalName().equals("ratedS"); } else { - ignored = attr.getLocalName().contains("node") - || attr.getLocalName().contains("bus") - || attr.getLocalName().contains("Bus") - || attr.getLocalName().contains("selectedOperationalLimitsGroupId"); + ignored = attr.getLocalName().contains("node") || attr.getLocalName().contains("bus") || attr.getLocalName().contains("Bus"); } } return !ignored; @@ -388,6 +383,21 @@ static ComparisonResult ignoringNonEQ(Comparison comparison, ComparisonResult re return result; } + static ComparisonResult ignoringOperationalLimitsGroupId(Comparison comparison, ComparisonResult result) { + if (result == ComparisonResult.DIFFERENT) { + Node control = comparison.getControlDetails().getTarget(); + if (comparison.getType() == ComparisonType.ATTR_VALUE) { + if (comparison.getControlDetails().getXPath().contains("operationalLimitsGroup") && control != null + && control.getLocalName().equals("id")) { + return ComparisonResult.EQUAL; + } else if (control != null && control.getLocalName().contains("selectedOperationalLimitsGroupId")) { + return ComparisonResult.EQUAL; + } + } + } + return result; + } + static ComparisonResult ignoringJunctionOrBusbarTerminals(Comparison comparison, ComparisonResult result) { // If control node is a terminal of a junction, ignore the difference // Means that we also have to ignore length of children of RDF element diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesNames.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesNames.java index 43dd42986da..78f763985ed 100644 --- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesNames.java +++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesNames.java @@ -129,6 +129,8 @@ public final class CgmesNames { public static final String EQUIVALENT_INJECTION_TERMINAL = "EquivalentInjectionTerminal"; public static final String EXTERNAL_NETWORK_INJECTION = "ExternalNetworkInjection"; + public static final String OPERATIONAL_LIMIT_SET = "OperationalLimitSet"; + private CgmesNames() { } } diff --git a/cgmes/cgmes-model/src/main/resources/CIM100.sparql b/cgmes/cgmes-model/src/main/resources/CIM100.sparql index cf0b350cb2c..19e355b9414 100644 --- a/cgmes/cgmes-model/src/main/resources/CIM100.sparql +++ b/cgmes/cgmes-model/src/main/resources/CIM100.sparql @@ -114,6 +114,7 @@ WHERE { OPTIONAL { ?OperationalLimit cim:CurrentLimit.normalValue ?normalValue } OPTIONAL { ?OperationalLimit cim:ApparentPowerLimit.normalValue ?normalValue } OPTIONAL { ?OperationalLimit cim:VoltageLimit.normalValue ?normalValue } + OPTIONAL { ?OperationalLimitSet cim:IdentifiedObject.name ?OperationalLimitSetName } # operational limit sets can be attached to terminals or equipments OPTIONAL { ?OperationalLimitSet cim:OperationalLimitSet.Terminal ?Terminal } OPTIONAL { GRAPH ?graph2 { diff --git a/cgmes/cgmes-model/src/main/resources/CIM14.sparql b/cgmes/cgmes-model/src/main/resources/CIM14.sparql index 1df2d25ac3c..d6b510ea199 100644 --- a/cgmes/cgmes-model/src/main/resources/CIM14.sparql +++ b/cgmes/cgmes-model/src/main/resources/CIM14.sparql @@ -143,6 +143,7 @@ SELECT * # cim:VoltageLimit.value ? value . #} #} + OPTIONAL { ?OperationalLimitSet cim:IdentifiedObject.name ?OperationalLimitSetName } ?OperationalLimitType cim:IdentifiedObject.name ?operationalLimitTypeName . OPTIONAL { ?OperationalLimitType cim:OperationalLimitType.direction ?direction } diff --git a/cgmes/cgmes-model/src/main/resources/CIM16.sparql b/cgmes/cgmes-model/src/main/resources/CIM16.sparql index c72ad19a4ca..2ac8ae113eb 100644 --- a/cgmes/cgmes-model/src/main/resources/CIM16.sparql +++ b/cgmes/cgmes-model/src/main/resources/CIM16.sparql @@ -380,6 +380,7 @@ WHERE OPTIONAL { ?OperationalLimit cim:CurrentLimit.value ?value } OPTIONAL { ?OperationalLimit cim:ApparentPowerLimit.value ?value } OPTIONAL { ?OperationalLimit cim:VoltageLimit.value ?value } + OPTIONAL { ?OperationalLimitSet cim:IdentifiedObject.name ?OperationalLimitSetName } # operational limit sets can be attached to terminals or equipments OPTIONAL { ?OperationalLimitSet cim:OperationalLimitSet.Terminal ?Terminal } OPTIONAL { GRAPH ?graph2 { From 041d5fe0b356dd8906ef9744ddb604841ac6ae44 Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Fri, 27 Sep 2024 16:57:35 +0200 Subject: [PATCH 10/14] Review the LoadingLimitsMapping class: map the LoadingLimitsAdders with the OperationalLimitsGroups. Signed-off-by: Romain Courtier --- .../conversion/LoadingLimitsMapping.java | 48 +++++++++++++----- .../elements/OperationalLimitConversion.java | 49 +++++-------------- .../com/powsybl/cgmes/model/CgmesNames.java | 3 ++ 3 files changed, 51 insertions(+), 49 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java index 7161dbed447..badf86ead45 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java @@ -7,37 +7,63 @@ */ package com.powsybl.cgmes.conversion; -import com.powsybl.iidm.network.LoadingLimitsAdder; +import com.powsybl.cgmes.model.CgmesNames; +import com.powsybl.iidm.network.*; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.function.Supplier; /** * @author Miora Ralambotiana {@literal } + * @author Romain Courtier {@literal } */ public class LoadingLimitsMapping { - protected final Map> adders = new HashMap<>(); private final Context context; + private final Map currentLimitsAdders; + private final Map activePowerLimitsAdders; + private final Map apparentPowerLimitsAdders; LoadingLimitsMapping(Context context) { this.context = Objects.requireNonNull(context); + this.currentLimitsAdders = new HashMap<>(); + this.activePowerLimitsAdders = new HashMap<>(); + this.apparentPowerLimitsAdders = new HashMap<>(); } - public LoadingLimitsAdder computeIfAbsentLoadingLimitsAdder(String id, Supplier> supplier) { - return adders.computeIfAbsent(id, s -> supplier.get()); + /** + * Get or create the limit adder for the given limits group and limit subclass. + * Different CGMES OperationalLimit can be mapped to the same IIDM LoadingLimit, + * hence the need to retrieve the limit adder if it has already been created before. + * @param limitsGroup The OperationalLimitsGroup to which the LoadingLimitsAdder shall add the limits. + * @param limitSubClass The operational limit subclass indicating which LoadingLimitsAdder subclass shall be used. + * @return The LoadingLimitsAdder for the given limits group and limit subclass. + */ + public LoadingLimitsAdder getLoadingLimitsAdder(OperationalLimitsGroup limitsGroup, String limitSubClass) { + return switch (limitSubClass) { + case CgmesNames.CURRENT_LIMIT -> currentLimitsAdders.computeIfAbsent(limitsGroup, s -> limitsGroup.newCurrentLimits()); + case CgmesNames.ACTIVE_POWER_LIMIT -> activePowerLimitsAdders.computeIfAbsent(limitsGroup, s -> limitsGroup.newActivePowerLimits()); + case CgmesNames.APPARENT_POWER_LIMIT -> apparentPowerLimitsAdders.computeIfAbsent(limitsGroup, s -> limitsGroup.newApparentPowerLimits()); + default -> throw new IllegalArgumentException(); + }; } + /** + * Execute the add method for all the LoadingLimitsAdder stored in this mapping class. + * This method shall be called after all the CGMES OperationalLimit have been converted. + */ void addAll() { - for (Map.Entry> entry : adders.entrySet()) { - if (!Double.isNaN(entry.getValue().getPermanentLimit()) || entry.getValue().hasTemporaryLimits()) { - entry.getValue() - .fixLimits(context.config().getMissingPermanentLimitPercentage(), context::fixed) - .add(); + for (Map adder : List.of(currentLimitsAdders, activePowerLimitsAdders, apparentPowerLimitsAdders)) { + for (Map.Entry entry : adder.entrySet()) { + if (!Double.isNaN(entry.getValue().getPermanentLimit()) || entry.getValue().hasTemporaryLimits()) { + entry.getValue() + .fixLimits(context.config().getMissingPermanentLimitPercentage(), context::fixed) + .add(); + } } + adder.clear(); } - adders.clear(); } } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java index b84ce3f3025..9fc2ef7caa8 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java @@ -22,9 +22,6 @@ */ public class OperationalLimitConversion extends AbstractIdentifiedObjectConversion { - private static final String ACTIVE_POWER_LIMIT = "ActivePowerLimit"; - private static final String APPARENT_POWER_LIMIT = "ApparentPowerLimit"; - private static final String CURRENT_LIMIT = "CurrentLimit"; private static final String LIMIT_TYPE = "limitType"; private static final String OPERATIONAL_LIMIT = "Operational limit"; private static final String OPERATIONAL_LIMIT_TYPE_NAME = "operationalLimitTypeName"; @@ -45,7 +42,11 @@ public OperationalLimitConversion(PropertyBag l, Context context) { terminalId = l.getId("Terminal"); equipmentId = l.getId("Equipment"); Terminal terminal = null; - if (limitSubclass == null || limitSubclass.equals(ACTIVE_POWER_LIMIT) || limitSubclass.equals(APPARENT_POWER_LIMIT) || limitSubclass.equals(CURRENT_LIMIT)) { + if (limitSubclass == null || limitSubclass.equals(CgmesNames.ACTIVE_POWER_LIMIT) || limitSubclass.equals(CgmesNames.APPARENT_POWER_LIMIT) || limitSubclass.equals(CgmesNames.CURRENT_LIMIT)) { + if (limitSubclass == null) { + // Support for CIM14, all limits are assumed to be current + limitSubclass = CgmesNames.CURRENT_LIMIT; + } if (terminalId != null) { terminal = context.terminalMapping().findForFlowLimits(terminalId); } @@ -80,28 +81,6 @@ private void setVoltageLevelForVoltageLimit(Terminal terminal) { } } - /** - * Get the LoadingLimitsAdder supplier for the given limits group and subclass. - * @param limitsGroup The limit group instance for which the adder is called. - * @param limitSubClass The subclass of the OperationalLimit. - * @return The appropriate LoadingLimitsAdder supplier. - */ - private Supplier> getLoadingLimitAdderSupplier(OperationalLimitsGroup limitsGroup, String limitSubClass) { - if (limitSubClass == null) { - return limitsGroup::newCurrentLimits; - } - switch (limitSubClass) { - case ACTIVE_POWER_LIMIT: - return limitsGroup::newActivePowerLimits; - case APPARENT_POWER_LIMIT: - return limitsGroup::newApparentPowerLimits; - case CURRENT_LIMIT: - return limitsGroup::newCurrentLimits; - default: - throw new IllegalArgumentException(); - } - } - /** * Create the LoadingLimitsAdder for the given branch + side and the given limit set + subclass. * @param terminalNumber The side of the branch to which the OperationalLimit applies. @@ -115,14 +94,12 @@ private void createLimitsAdder(int terminalNumber, String limitSubClass, String OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup1(limitSetId).orElseGet(() -> { b.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); return b.newOperationalLimitsGroup1(limitSetId); }); - loadingLimitsAdder1 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_1_" + limitSubClass + "_" + limitSetId, - getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); + loadingLimitsAdder1 = context.loadingLimitsMapping().getLoadingLimitsAdder(limitsGroup, limitSubClass); } else if (terminalNumber == 2) { OperationalLimitsGroup limitsGroup = b.getOperationalLimitsGroup2(limitSetId).orElseGet(() -> { b.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); return b.newOperationalLimitsGroup2(limitSetId); }); - loadingLimitsAdder2 = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(b.getId() + "_2_" + limitSubClass + "_" + limitSetId, - getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); + loadingLimitsAdder2 = context.loadingLimitsMapping().getLoadingLimitsAdder(limitsGroup, limitSubClass); } else { throw new IllegalArgumentException(); } @@ -140,8 +117,7 @@ private void createLimitsAdder(String limitSubClass, String limitSetId, String l OperationalLimitsGroup limitsGroup = dl.getOperationalLimitsGroup(limitSetId).orElseGet(() -> { dl.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); return dl.newOperationalLimitsGroup(limitSetId); }); - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(dl.getId() + "_" + limitSubClass + "_" + limitSetId, - getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); + loadingLimitsAdder = context.loadingLimitsMapping().getLoadingLimitsAdder(limitsGroup, limitSubClass); } /** @@ -157,20 +133,17 @@ private void createLimitsAdder(int terminalNumber, String limitSubClass, String OperationalLimitsGroup limitsGroup = twt.getLeg1().getOperationalLimitsGroup(limitSetId).orElseGet(() -> { twt.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); return twt.getLeg1().newOperationalLimitsGroup(limitSetId); }); - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_1_" + limitSubClass + "_" + limitSetId, - getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); + loadingLimitsAdder = context.loadingLimitsMapping().getLoadingLimitsAdder(limitsGroup, limitSubClass); } else if (terminalNumber == 2) { OperationalLimitsGroup limitsGroup = twt.getLeg2().getOperationalLimitsGroup(limitSetId).orElseGet(() -> { twt.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); return twt.getLeg2().newOperationalLimitsGroup(limitSetId); }); - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_2_" + limitSubClass + "_" + limitSetId, - getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); + loadingLimitsAdder = context.loadingLimitsMapping().getLoadingLimitsAdder(limitsGroup, limitSubClass); } else if (terminalNumber == 3) { OperationalLimitsGroup limitsGroup = twt.getLeg3().getOperationalLimitsGroup(limitSetId).orElseGet(() -> { twt.setProperty(PROPERTY_PREFIX + limitSetId, limitSetName); return twt.getLeg3().newOperationalLimitsGroup(limitSetId); }); - loadingLimitsAdder = context.loadingLimitsMapping().computeIfAbsentLoadingLimitsAdder(twt.getId() + "_3_" + limitSubClass + "_" + limitSetId, - getLoadingLimitAdderSupplier(limitsGroup, limitSubClass)); + loadingLimitsAdder = context.loadingLimitsMapping().getLoadingLimitsAdder(limitsGroup, limitSubClass); } else { throw new IllegalArgumentException(); } diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesNames.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesNames.java index 78f763985ed..e4d9a813a44 100644 --- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesNames.java +++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesNames.java @@ -130,6 +130,9 @@ public final class CgmesNames { public static final String EXTERNAL_NETWORK_INJECTION = "ExternalNetworkInjection"; public static final String OPERATIONAL_LIMIT_SET = "OperationalLimitSet"; + public static final String CURRENT_LIMIT = "CurrentLimit"; + public static final String ACTIVE_POWER_LIMIT = "ActivePowerLimit"; + public static final String APPARENT_POWER_LIMIT = "ApparentPowerLimit"; private CgmesNames() { } From 7046313ba49258809ed9c047a148869d4c3bf88c Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Fri, 27 Sep 2024 17:12:27 +0200 Subject: [PATCH 11/14] Using OperationalLimitSet mRID instead of rdf:ID. Signed-off-by: Romain Courtier --- .../cgmes/conversion/elements/OperationalLimitConversion.java | 2 +- .../cgmes/conversion/test/OperationalLimitsGroupTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java index 9fc2ef7caa8..bdfe3b62608 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/OperationalLimitConversion.java @@ -35,7 +35,7 @@ public class OperationalLimitConversion extends AbstractIdentifiedObjectConversi public OperationalLimitConversion(PropertyBag l, Context context) { super("OperationalLimit", l, context); String limitSubclass = p.getLocal(OPERATIONAL_LIMIT_SUBCLASS); - String limitSetId = p.getLocal(OPERATIONAL_LIMIT_SET_ID); + String limitSetId = p.getId(OPERATIONAL_LIMIT_SET_ID); String limitSetName = p.getLocal(OPERATIONAL_LIMIT_SET_NAME); limitSetName = limitSetName != null ? limitSetName : limitSetId; // Limit can associated to a Terminal or to an Equipment 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 a3a640a5cbc..21b6a67f6e9 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 @@ -43,7 +43,7 @@ void importMultipleLimitsGroupsOnSameLineEndTest() { assertEquals(2, line.getOperationalLimitsGroups2().size()); // The winter set (_OLS_3) contains current limits and active power limits - Optional winterLimits = line.getOperationalLimitsGroup2("_OLS_3"); + Optional winterLimits = line.getOperationalLimitsGroup2("OLS_3"); assertTrue(winterLimits.isPresent()); assertTrue(winterLimits.get().getCurrentLimits().isPresent()); assertTrue(winterLimits.get().getActivePowerLimits().isPresent()); @@ -72,7 +72,7 @@ void exportSelectedLimitsGroupTest() throws IOException { // Manually select one of the limits group on side 2 and export again Line line = network.getLine("Line"); - line.setSelectedOperationalLimitsGroup2("_OLS_2"); + line.setSelectedOperationalLimitsGroup2("OLS_2"); network.write("CGMES", exportParams, tmpDir.resolve("ExportSelectedLimitsGroup.xml")); exportSelectedLimitsGroupXml = Files.readString(tmpDir.resolve("ExportSelectedLimitsGroup_EQ.xml")); From c141a486d8006a01df74aa61b8fc05b87d186ee0 Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Mon, 30 Sep 2024 11:24:46 +0200 Subject: [PATCH 12/14] Simplify the addAll method in LoadingLimitsMapping Signed-off-by: Romain Courtier --- .../conversion/LoadingLimitsMapping.java | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java index badf86ead45..0c3d33157fb 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java @@ -10,10 +10,8 @@ import com.powsybl.cgmes.model.CgmesNames; import com.powsybl.iidm.network.*; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import java.util.stream.Stream; /** * @author Miora Ralambotiana {@literal } @@ -55,15 +53,13 @@ public LoadingLimitsAdder getLoadingLimitsAdder(OperationalLimitsGroup limitsGro * This method shall be called after all the CGMES OperationalLimit have been converted. */ void addAll() { - for (Map adder : List.of(currentLimitsAdders, activePowerLimitsAdders, apparentPowerLimitsAdders)) { - for (Map.Entry entry : adder.entrySet()) { - if (!Double.isNaN(entry.getValue().getPermanentLimit()) || entry.getValue().hasTemporaryLimits()) { - entry.getValue() - .fixLimits(context.config().getMissingPermanentLimitPercentage(), context::fixed) - .add(); - } + Stream.of(currentLimitsAdders, activePowerLimitsAdders, apparentPowerLimitsAdders).flatMap(m -> m.values().stream()).forEach(adder -> { + if (!Double.isNaN(adder.getPermanentLimit()) || adder.hasTemporaryLimits()) { + adder.fixLimits(context.config().getMissingPermanentLimitPercentage(), context::fixed) + .add(); } - adder.clear(); - } + }); + + Stream.of(currentLimitsAdders, activePowerLimitsAdders, apparentPowerLimitsAdders).forEach(Map::clear); } } From 6684c683c4f88ca7040ea3f9bb27cb153ecfce99 Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Mon, 30 Sep 2024 12:58:22 +0200 Subject: [PATCH 13/14] Integrate the limits validity check in a filter of the stream Signed-off-by: Romain Courtier --- .../cgmes/conversion/LoadingLimitsMapping.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java index 0c3d33157fb..faca6145a49 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/LoadingLimitsMapping.java @@ -49,16 +49,15 @@ public LoadingLimitsAdder getLoadingLimitsAdder(OperationalLimitsGroup limitsGro } /** - * Execute the add method for all the LoadingLimitsAdder stored in this mapping class. + * Execute the add method for all the valid LoadingLimitsAdder stored in this mapping class. * This method shall be called after all the CGMES OperationalLimit have been converted. */ void addAll() { - Stream.of(currentLimitsAdders, activePowerLimitsAdders, apparentPowerLimitsAdders).flatMap(m -> m.values().stream()).forEach(adder -> { - if (!Double.isNaN(adder.getPermanentLimit()) || adder.hasTemporaryLimits()) { - adder.fixLimits(context.config().getMissingPermanentLimitPercentage(), context::fixed) - .add(); - } - }); + Stream.of(currentLimitsAdders, activePowerLimitsAdders, apparentPowerLimitsAdders) + .flatMap(m -> m.values().stream()) + .filter(adder -> !Double.isNaN(adder.getPermanentLimit()) || adder.hasTemporaryLimits()) + .forEach(adder -> adder.fixLimits(context.config().getMissingPermanentLimitPercentage(), context::fixed) + .add()); Stream.of(currentLimitsAdders, activePowerLimitsAdders, apparentPowerLimitsAdders).forEach(Map::clear); } From 6b6a1251f6e69deeae23466a079237f0cb207d30 Mon Sep 17 00:00:00 2001 From: Romain Courtier Date: Mon, 30 Sep 2024 14:36:34 +0200 Subject: [PATCH 14/14] Keep the 3w-transformer stream instead of casting it to a set Signed-off-by: Romain Courtier --- .../powsybl/cgmes/conversion/Conversion.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java index 1f584b0c8a2..84b63d2bba4 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java @@ -34,7 +34,6 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.Stream; import static com.powsybl.cgmes.conversion.Conversion.Config.StateProfile.SSH; @@ -281,14 +280,15 @@ public Network convert(ReportNode reportNode) { } /** - * Retrieve the Collection of OperationalLimitGroups for identifiable that have flow limits (branch, dangling line, 3w-transformer). + * Retrieve the Collection of OperationalLimitGroups for identifiable that have flow limits + * (branch, dangling line, 3w-transformer). * If the collection has only one element, it gets to be the identifiable's selectedGroup. - * If there is more than one element in the collection, do nothing. The identifiable's selectedGroup should then be set by the user after the conversion. + * If there is more than one element in the collection, don't set any as selected. * @param context The conversion's Context. */ private void setSelectedOperationalLimitsGroup(Context context) { // Set selected limits group for branches - for (Branch branch : context.network().getBranches()) { + context.network().getBranchStream().map(b -> (Branch) b).forEach(branch -> { // Side 1 Collection limitsHolder1 = branch.getOperationalLimitsGroups1(); if (limitsHolder1.size() == 1) { @@ -299,24 +299,23 @@ private void setSelectedOperationalLimitsGroup(Context context) { if (limitsHolder2.size() == 1) { branch.setSelectedOperationalLimitsGroup2(limitsHolder2.iterator().next().getId()); } - } + }); // Set selected limits group for Dangling lines - for (DanglingLine dl : context.network().getDanglingLines()) { + context.network().getDanglingLineStream().forEach(dl -> { Collection limitsHolder = dl.getOperationalLimitsGroups(); if (limitsHolder.size() == 1) { dl.setSelectedOperationalLimitsGroup(limitsHolder.iterator().next().getId()); } - } + }); // Set selected limits group for 3w transformers legs - for (ThreeWindingsTransformer.Leg leg : context.network().getThreeWindingsTransformerStream() - .flatMap(ThreeWindingsTransformer::getLegStream).collect(Collectors.toSet())) { + context.network().getThreeWindingsTransformerStream().flatMap(ThreeWindingsTransformer::getLegStream).forEach(leg -> { Collection limitsHolder = leg.getOperationalLimitsGroups(); if (limitsHolder.size() == 1) { leg.setSelectedOperationalLimitsGroup(limitsHolder.iterator().next().getId()); } - } + }); } private void handleDangingLineDisconnectedAtBoundary(Network network, Context context) {