From f5270473a3029f02f4be41070ba6dafb474d7faf Mon Sep 17 00:00:00 2001 From: jamal-khey Date: Thu, 5 Dec 2024 13:28:13 +0100 Subject: [PATCH] Add computation utility class (#84) Signed-off-by: jamal-khey --- pom.xml | 6 + .../utils/ComputationResultUtils.java | 90 ++++++++++ .../computation/ComputationUtilTest.java | 168 ++++++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 src/main/java/com/powsybl/ws/commons/computation/utils/ComputationResultUtils.java create mode 100644 src/test/java/com/powsybl/ws/commons/computation/ComputationUtilTest.java diff --git a/pom.xml b/pom.xml index d7af159..f8a1252 100644 --- a/pom.xml +++ b/pom.xml @@ -122,6 +122,11 @@ ${powsybl-network-store-client.version} true + + com.powsybl + powsybl-security-analysis-api + true + @@ -146,6 +151,7 @@ slf4j-simple test + org.springframework.boot spring-boot-starter-web diff --git a/src/main/java/com/powsybl/ws/commons/computation/utils/ComputationResultUtils.java b/src/main/java/com/powsybl/ws/commons/computation/utils/ComputationResultUtils.java new file mode 100644 index 0000000..3adacc9 --- /dev/null +++ b/src/main/java/com/powsybl/ws/commons/computation/utils/ComputationResultUtils.java @@ -0,0 +1,90 @@ +/** + * 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/. + */ +package com.powsybl.ws.commons.computation.utils; + +import com.powsybl.iidm.network.*; +import com.powsybl.security.BusBreakerViolationLocation; +import com.powsybl.security.LimitViolation; +import com.powsybl.security.NodeBreakerViolationLocation; +import com.powsybl.security.ViolationLocation; + +import java.util.*; +import java.util.stream.Collectors; + +import static com.powsybl.iidm.network.IdentifiableType.BUSBAR_SECTION; +import static com.powsybl.security.ViolationLocation.Type.NODE_BREAKER; + +/** + * @author Jamal KHEYYAD + */ +public final class ComputationResultUtils { + + private ComputationResultUtils() { + } + + public static String getViolationLocationId(LimitViolation limitViolation, Network network) { + Optional violationLocation = limitViolation.getViolationLocation(); + if (violationLocation.isEmpty()) { + return limitViolation.getSubjectId(); + } + + ViolationLocation location = violationLocation.get(); + if (location.getType() == NODE_BREAKER) { + return getNodeBreakerViolationLocationId((NodeBreakerViolationLocation) location, network); + } else { + return getBusBreakerViolationLocationId((BusBreakerViolationLocation) location, network, limitViolation.getSubjectId()); + } + } + + private static String getNodeBreakerViolationLocationId(NodeBreakerViolationLocation nodeBreakerViolationLocation, Network network) { + VoltageLevel vl = network.getVoltageLevel(nodeBreakerViolationLocation.getVoltageLevelId()); + + List busBarIds = nodeBreakerViolationLocation.getNodes().stream() + .map(node -> vl.getNodeBreakerView().getTerminal(node)) + .filter(Objects::nonNull) + .map(Terminal::getConnectable) + .filter(t -> t.getType() == BUSBAR_SECTION) + .map(Identifiable::getId) + .distinct() + .toList(); + + String busId = null; + if (!busBarIds.isEmpty()) { + busId = getBusId(vl, new HashSet<>(busBarIds)); + } + return formatViolationLocationId(busId != null ? List.of() : busBarIds, busId != null ? busId : nodeBreakerViolationLocation.getVoltageLevelId()); + } + + private static String getBusId(VoltageLevel voltageLevel, Set sjbIds) { + Optional bus = voltageLevel.getBusView() + .getBusStream() + .filter(b -> { + Set busSjbIds = b.getConnectedTerminalStream().map(Terminal::getConnectable).filter(c -> c.getType() == BUSBAR_SECTION).map(Connectable::getId).collect(Collectors.toSet()); + return busSjbIds.equals(sjbIds); + }) + .findFirst(); + return bus.map(Identifiable::getId).orElse(null); + } + + private static String formatViolationLocationId(List elementsIds, String subjectId) { + return !elementsIds.isEmpty() ? + subjectId + " (" + String.join(", ", elementsIds) + ")" : + subjectId; + } + + private static String getBusBreakerViolationLocationId(BusBreakerViolationLocation busBreakerViolationLocation, Network network, String subjectId) { + List busBreakerIds = busBreakerViolationLocation + .getBusView(network) + .getBusStream() + .map(Identifiable::getId) + .distinct() + .toList(); + + return busBreakerIds.size() == 1 ? formatViolationLocationId(List.of(), busBreakerIds.get(0)) : formatViolationLocationId(busBreakerIds, subjectId); + } + +} diff --git a/src/test/java/com/powsybl/ws/commons/computation/ComputationUtilTest.java b/src/test/java/com/powsybl/ws/commons/computation/ComputationUtilTest.java new file mode 100644 index 0000000..a23225d --- /dev/null +++ b/src/test/java/com/powsybl/ws/commons/computation/ComputationUtilTest.java @@ -0,0 +1,168 @@ +/** + * 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/. + */ +package com.powsybl.ws.commons.computation; + +import com.powsybl.iidm.network.*; +import com.powsybl.network.store.iidm.impl.NetworkFactoryImpl; +import com.powsybl.security.BusBreakerViolationLocation; +import com.powsybl.security.LimitViolation; +import com.powsybl.security.NodeBreakerViolationLocation; +import com.powsybl.security.ViolationLocation; +import com.powsybl.ws.commons.computation.utils.ComputationResultUtils; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author Jamal KHEYYAD + */ +class ComputationUtilTest { + + static Network createBusBreakerNetwork() { + Network network = new NetworkFactoryImpl().createNetwork("network", "test"); + Substation p1 = network.newSubstation() + .setId("P1") + .setCountry(Country.FR) + .setTso("RTE") + .setGeographicalTags("A") + .add(); + VoltageLevel vl = p1.newVoltageLevel() + .setId("VLGEN") + .setNominalV(24.0) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + + Bus ngen = vl.getBusBreakerView().newBus() + .setId("NGEN") + .add(); + + Bus ngen2 = vl.getBusBreakerView().newBus() + .setId("NGEN2") + .add(); + + vl.newLoad() + .setId("LD") + .setBus(ngen.getId()) + .setConnectableBus(ngen.getId()) + .setP0(600.0) + .setQ0(200.0) + .add(); + + vl.newLoad() + .setId("LD2") + .setBus(ngen2.getId()) + .setConnectableBus(ngen2.getId()) + .setP0(600.0) + .setQ0(200.0) + .add(); + + return network; + } + + public static Network createNodeBreakerNetwork() { + Network network = Network.create("network", "test"); + Substation s = network.newSubstation() + .setId("S") + .add(); + VoltageLevel vl = s.newVoltageLevel() + .setId("VL1") + .setNominalV(400) + .setLowVoltageLimit(370.) + .setHighVoltageLimit(420.) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS1") + .setNode(0) + .add(); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS2") + .setNode(1) + .add(); + + vl.newLoad() + .setId("LD") + .setNode(3) + .setP0(600.0) + .setQ0(200.0) + .add(); + + vl.getNodeBreakerView().newBreaker() + .setId("BR") + .setOpen(false) + .setNode1(0) + .setNode2(3) + .add(); + + return network; + } + + @Test + void testViolationLocationIdBusBreaker() { + Network network = createBusBreakerNetwork(); + + LimitViolation limitViolation = mock(LimitViolation.class); + + when(limitViolation.getViolationLocation()).thenReturn(Optional.of(new BusBreakerViolationLocation(List.of("NGEN")))); + assertEquals("VLGEN_0", ComputationResultUtils.getViolationLocationId(limitViolation, network)); + + when(limitViolation.getViolationLocation()).thenReturn(Optional.of(new BusBreakerViolationLocation(List.of("NGEN2")))); + assertEquals("VLGEN_1", ComputationResultUtils.getViolationLocationId(limitViolation, network)); + + when(limitViolation.getViolationLocation()).thenReturn(Optional.of(new BusBreakerViolationLocation(List.of("NGEN", "NGEN2")))); + when(limitViolation.getSubjectId()).thenReturn("VLGEN"); + assertEquals("VLGEN (VLGEN_0, VLGEN_1)", ComputationResultUtils.getViolationLocationId(limitViolation, network)); + + when(limitViolation.getViolationLocation()).thenReturn(Optional.of(new BusBreakerViolationLocation(List.of()))); + when(limitViolation.getSubjectId()).thenReturn("VLGEN"); + assertEquals("VLGEN", ComputationResultUtils.getViolationLocationId(limitViolation, network)); + } + + @Test + void testNoViolationLocationIdBusBreaker() { + Network network = createBusBreakerNetwork(); + LimitViolation limitViolation = mock(LimitViolation.class); + + when(limitViolation.getViolationLocation()).thenReturn(Optional.empty()); + when(limitViolation.getSubjectId()).thenReturn("subjectId"); + assertEquals("subjectId", ComputationResultUtils.getViolationLocationId(limitViolation, network)); + } + + @Test + void testViolationLocationIdNodeBreaker() { + Network network = createNodeBreakerNetwork(); + + NodeBreakerViolationLocation nodeBreakerViolationLocation = mock(NodeBreakerViolationLocation.class); + when(nodeBreakerViolationLocation.getType()).thenReturn(ViolationLocation.Type.NODE_BREAKER); + when(nodeBreakerViolationLocation.getVoltageLevelId()).thenReturn("VL1"); + when(nodeBreakerViolationLocation.getNodes()).thenReturn(List.of()); + + LimitViolation limitViolation = mock(LimitViolation.class); + when(limitViolation.getViolationLocation()).thenReturn(Optional.of(nodeBreakerViolationLocation)); + when(limitViolation.getSubjectId()).thenReturn("VLHV1"); + + String locationId = ComputationResultUtils.getViolationLocationId(limitViolation, network); + assertEquals("VL1", locationId); + + when(nodeBreakerViolationLocation.getNodes()).thenReturn(List.of(0, 1)); + locationId = ComputationResultUtils.getViolationLocationId(limitViolation, network); + assertEquals("VL1 (BBS1, BBS2)", locationId); + + when(nodeBreakerViolationLocation.getNodes()).thenReturn(List.of(0)); + locationId = ComputationResultUtils.getViolationLocationId(limitViolation, network); + assertEquals("VL1_0", locationId); + + when(nodeBreakerViolationLocation.getNodes()).thenReturn(List.of(1)); + locationId = ComputationResultUtils.getViolationLocationId(limitViolation, network); + assertEquals("VL1 (BBS2)", locationId); + } +}