diff --git a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java index 957c23981c..67cdaede23 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java @@ -251,49 +251,64 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame SecurityAnalysisResult runSimulationsOnAllComponents(LfNetworkList networks, List propagatedContingencies, P parameters, SecurityAnalysisParameters securityAnalysisParameters, List operatorStrategies, List actions, List limitReductions, LoadFlowParameters lfParameters) { - // run simulation on main connected component network first - SecurityAnalysisResult result = networks.getList().stream().filter(n -> n.getValidity().equals(LfNetwork.Validity.VALID) && n.getNumCC() == ComponentConstants.MAIN_NUM) - .findFirst() - .map(n -> runSimulations(n, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions)) - .orElse(createNoResult()); + + List networkToSimulate = new ArrayList<>(getNetworksToSimulate(networks, lfParameters.getConnectedComponentMode())); + + if (networkToSimulate.isEmpty()) { + return createNoResult(); + } + + // run simulation on first lfNetwork to initialize results structures + LfNetwork firstNetwork = networkToSimulate.remove(0); + SecurityAnalysisResult result = runSimulations(firstNetwork, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions); List postContingencyResults = result.getPostContingencyResults(); List operatorStrategyResults = result.getOperatorStrategyResults(); NetworkResult mergedPreContingencyNetworkResult = result.getPreContingencyResult().getNetworkResult(); - List preContingenctViolations = result.getPreContingencyResult().getLimitViolationsResult().getLimitViolations(); + List preContingencyViolations = result.getPreContingencyResult().getLimitViolationsResult().getLimitViolations(); - if (lfParameters.getConnectedComponentMode() == LoadFlowParameters.ConnectedComponentMode.ALL) { - Map postContingencyResultMap = new LinkedHashMap<>(); - Map operatorStrategyResultMap = new LinkedHashMap<>(); - postContingencyResults.forEach(r -> postContingencyResultMap.put(r.getContingency().getId(), r)); - operatorStrategyResults.forEach(r -> operatorStrategyResultMap.put(r.getOperatorStrategy().getId(), r)); + Map postContingencyResultMap = new LinkedHashMap<>(); + Map operatorStrategyResultMap = new LinkedHashMap<>(); + postContingencyResults.forEach(r -> postContingencyResultMap.put(r.getContingency().getId(), r)); + operatorStrategyResults.forEach(r -> operatorStrategyResultMap.put(r.getOperatorStrategy().getId(), r)); - // Ensure the lists are writable and can be extended - preContingenctViolations = new ArrayList<>(preContingenctViolations); + // Ensure the lists are writable and can be extended + preContingencyViolations = new ArrayList<>(preContingencyViolations); - List networkToSimulate = networks.getList().stream() - .filter(n -> n.getNumCC() != ComponentConstants.MAIN_NUM && n.getValidity().equals(LfNetwork.Validity.VALID)).toList(); - for (LfNetwork n : networkToSimulate) { - SecurityAnalysisResult resultOtherComponent = runSimulations(n, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions); + for (LfNetwork n : networkToSimulate) { + SecurityAnalysisResult resultOtherComponent = runSimulations(n, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions); - // Merge into first result - // PreContingency results first - preContingenctViolations.addAll(resultOtherComponent.getPreContingencyResult().getLimitViolationsResult().getLimitViolations()); - mergedPreContingencyNetworkResult = mergeNetworkResult(mergedPreContingencyNetworkResult, resultOtherComponent.getPreContingencyResult().getNetworkResult()); + // Merge into first result + // PreContingency results first + preContingencyViolations.addAll(resultOtherComponent.getPreContingencyResult().getLimitViolationsResult().getLimitViolations()); + mergedPreContingencyNetworkResult = mergeNetworkResult(mergedPreContingencyNetworkResult, resultOtherComponent.getPreContingencyResult().getNetworkResult()); - // PostContingency and OperatorStrategies results - mergeSecurityAnalysisResult(resultOtherComponent, postContingencyResultMap, operatorStrategyResultMap, n.getNumCC()); - } - postContingencyResults = postContingencyResultMap.values().stream().toList(); - operatorStrategyResults = operatorStrategyResultMap.values().stream().toList(); + // PostContingency and OperatorStrategies results + mergeSecurityAnalysisResult(resultOtherComponent, postContingencyResultMap, operatorStrategyResultMap, n.getNumCC()); } + postContingencyResults = postContingencyResultMap.values().stream().toList(); + operatorStrategyResults = operatorStrategyResultMap.values().stream().toList(); + PreContingencyResult mergedPrecontingencyResult = - new PreContingencyResult(result.getPreContingencyResult().getStatus(), - new LimitViolationsResult(preContingenctViolations), - mergedPreContingencyNetworkResult); + new PreContingencyResult(result.getPreContingencyResult().getStatus(), + new LimitViolationsResult(preContingencyViolations), + mergedPreContingencyNetworkResult); return new SecurityAnalysisResult(mergedPrecontingencyResult, postContingencyResults, operatorStrategyResults); } + static List getNetworksToSimulate(LfNetworkList networks, LoadFlowParameters.ConnectedComponentMode mode) { + + if (LoadFlowParameters.ConnectedComponentMode.MAIN.equals(mode)) { + return networks.getList().stream() + .filter(n -> n.getNumCC() == ComponentConstants.MAIN_NUM && n.getValidity().equals(LfNetwork.Validity.VALID)).toList(); + } else if (LoadFlowParameters.ConnectedComponentMode.ALL.equals(mode)) { + return networks.getList().stream() + .filter(n -> n.getValidity().equals(LfNetwork.Validity.VALID)).toList(); + } else { + throw new PowsyblException("Unsupported ConnectedComponentMode " + mode); + } + } + void mergeSecurityAnalysisResult(SecurityAnalysisResult resultToMerge, Map postContingencyResults, Map operatorStrategyResults, int connectedComponentNum) { resultToMerge.getPostContingencyResults().forEach(postContingencyResult -> { diff --git a/src/test/java/com/powsybl/openloadflow/network/ConnectedComponentNetworkFactory.java b/src/test/java/com/powsybl/openloadflow/network/ConnectedComponentNetworkFactory.java index 117dc642dc..278fc52360 100644 --- a/src/test/java/com/powsybl/openloadflow/network/ConnectedComponentNetworkFactory.java +++ b/src/test/java/com/powsybl/openloadflow/network/ConnectedComponentNetworkFactory.java @@ -8,6 +8,7 @@ package com.powsybl.openloadflow.network; import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.HvdcLine; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.TwoWindingsTransformer; @@ -755,4 +756,119 @@ public static Network createTwoCcLinkedBySwitches() { createLoad(b2, "d2", 1); return network; } + + public static Network createNoCc0Sc0() { + // b01 -- b02 -- b03 -- b04 connected by DC lines + // b11 -- b12 connected by AC line + Network network = Network.create("test", "code"); + Bus b01 = createBus(network, "b01"); + Bus b02 = createBus(network, "b02"); + Bus b03 = createBus(network, "b03"); + Bus b04 = createBus(network, "b04"); + Bus b11 = createBus(network, "b11"); + Bus b12 = createBus(network, "b12"); + + b01.getVoltageLevel() + .newVscConverterStation() + .setId("cs1-12") + .setConnectableBus(b01.getId()) + .setBus(b01.getId()) + .setVoltageRegulatorOn(true) + .setVoltageSetpoint(1.0) + .setReactivePowerSetpoint(0.0) + .setLossFactor(0.0f) + .add(); + b02.getVoltageLevel() + .newVscConverterStation() + .setId("cs2-12") + .setConnectableBus(b02.getId()) + .setBus(b02.getId()) + .setVoltageRegulatorOn(true) + .setVoltageSetpoint(1.0) + .setReactivePowerSetpoint(0.0) + .setLossFactor(0.0f) + .add(); + network.newHvdcLine() + .setId("hvdc12") + .setConverterStationId1("cs1-12") + .setConverterStationId2("cs2-12") + .setNominalV(1.) + .setR(0.0) + .setActivePowerSetpoint(1.) + .setConvertersMode(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER) + .setMaxP(5) + .add(); + + b02.getVoltageLevel() + .newVscConverterStation() + .setId("cs1-23") + .setConnectableBus(b02.getId()) + .setBus(b02.getId()) + .setVoltageRegulatorOn(true) + .setVoltageSetpoint(1.0) + .setReactivePowerSetpoint(0.0) + .setLossFactor(0.0f) + .add(); + b03.getVoltageLevel() + .newVscConverterStation() + .setId("cs2-23") + .setConnectableBus(b03.getId()) + .setBus(b03.getId()) + .setVoltageRegulatorOn(true) + .setVoltageSetpoint(1.0) + .setReactivePowerSetpoint(0.0) + .setLossFactor(0.0f) + .add(); + network.newHvdcLine() + .setId("hvdc23") + .setConverterStationId1("cs1-23") + .setConverterStationId2("cs2-23") + .setNominalV(1.) + .setR(0.0) + .setActivePowerSetpoint(2.) + .setConvertersMode(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER) + .setMaxP(5) + .add(); + + b03.getVoltageLevel() + .newVscConverterStation() + .setId("cs1-34") + .setConnectableBus(b03.getId()) + .setBus(b03.getId()) + .setVoltageRegulatorOn(true) + .setVoltageSetpoint(1.0) + .setReactivePowerSetpoint(0.0) + .setLossFactor(0.0f) + .add(); + b04.getVoltageLevel() + .newVscConverterStation() + .setId("cs2-34") + .setConnectableBus(b04.getId()) + .setBus(b04.getId()) + .setVoltageRegulatorOn(true) + .setVoltageSetpoint(1.0) + .setReactivePowerSetpoint(0.0) + .setLossFactor(0.0f) + .add(); + network.newHvdcLine() + .setId("hvdc34") + .setConverterStationId1("cs1-34") + .setConverterStationId2("cs2-34") + .setNominalV(1.) + .setR(0.0) + .setActivePowerSetpoint(3.) + .setConvertersMode(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER) + .setMaxP(5) + .add(); + + createGenerator(b01, "g01", 1); + createGenerator(b02, "g02", 1); + createGenerator(b03, "g03", 1); + createGenerator(b04, "g04", 1); + createLoad(b04, "l04", 4); + createGenerator(b11, "g11", 2); + createLoad(b12, "l11", 2); + createLine(network, b11, b12, "l11-12", 0.1f); + return network; + } } diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index 1ee1b502df..2037ddab94 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -37,6 +37,8 @@ import com.powsybl.openloadflow.lf.outerloop.OuterLoopResult; import com.powsybl.openloadflow.lf.outerloop.OuterLoopStatus; import com.powsybl.openloadflow.network.*; +import com.powsybl.openloadflow.network.impl.LfNetworkList; +import com.powsybl.openloadflow.network.impl.Networks; import com.powsybl.openloadflow.network.impl.OlfBranchResult; import com.powsybl.openloadflow.network.impl.OlfThreeWindingsTransformerResult; import com.powsybl.openloadflow.util.LoadFlowAssert; @@ -60,6 +62,7 @@ import static com.powsybl.openloadflow.util.LoadFlowAssert.*; import static java.util.Collections.emptySet; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author Geoffroy Jamgotchian {@literal } @@ -3911,4 +3914,87 @@ void testNoMoreVoltageControlledBusTwoBusNetwork() { assertReactivePowerEquals(0.0, network.getGenerator("G2").getTerminal()); assertVoltageEquals(400.01, network.getBusBreakerView().getBus("B2")); } + + @Test + void testComponentSelectionOneCCtwoSC() { + // Network has one CC with two SC + Network network = HvdcNetworkFactory.createVsc(); + LfNetworkList networks = new LfNetworkList(Networks.load(network, new LfNetworkParameters().setComputeMainConnectedComponentOnly(false))); + assertEquals(2, networks.getList().size()); + + assertEquals(0, networks.getList().get(0).getNumCC()); + assertEquals(0, networks.getList().get(0).getNumSC()); + assertEquals(0, networks.getList().get(1).getNumCC()); + assertEquals(1, networks.getList().get(1).getNumSC()); + + // Main connected component mode and all connected component mode should yield same result + List componentMain = AbstractSecurityAnalysis.getNetworksToSimulate(networks, LoadFlowParameters.ConnectedComponentMode.MAIN); + assertEquals(2, componentMain.size()); + assertEquals(0, componentMain.get(0).getNumCC()); + assertEquals(0, componentMain.get(0).getNumSC()); + assertEquals(0, componentMain.get(1).getNumCC()); + assertEquals(1, componentMain.get(1).getNumSC()); + + List componentAll = AbstractSecurityAnalysis.getNetworksToSimulate(networks, LoadFlowParameters.ConnectedComponentMode.ALL); + assertEquals(2, componentAll.size()); + assertEquals(0, componentAll.get(0).getNumCC()); + assertEquals(0, componentAll.get(0).getNumSC()); + assertEquals(0, componentAll.get(1).getNumCC()); + assertEquals(1, componentAll.get(1).getNumSC()); + } + + @Test + void testComponentSelectionTwoCC() { + Network network = FourBusNetworkFactory.createWithTwoScs(); + LfNetworkList networks = new LfNetworkList(Networks.load(network, new LfNetworkParameters().setComputeMainConnectedComponentOnly(false))); + assertEquals(2, networks.getList().size()); + + assertEquals(0, networks.getList().get(0).getNumCC()); + assertEquals(0, networks.getList().get(0).getNumSC()); + assertEquals(1, networks.getList().get(1).getNumCC()); + assertEquals(1, networks.getList().get(1).getNumSC()); + + // Main connected component mode should only select component associated to main CC + List componentMain = AbstractSecurityAnalysis.getNetworksToSimulate(networks, LoadFlowParameters.ConnectedComponentMode.MAIN); + assertEquals(1, componentMain.size()); + assertEquals(0, componentMain.get(0).getNumCC()); + assertEquals(0, componentMain.get(0).getNumSC()); + + // All connected component mode should select all component + List componentAll = AbstractSecurityAnalysis.getNetworksToSimulate(networks, LoadFlowParameters.ConnectedComponentMode.ALL); + assertEquals(2, componentAll.size()); + assertEquals(0, componentAll.get(0).getNumCC()); + assertEquals(0, componentAll.get(0).getNumSC()); + assertEquals(1, componentAll.get(1).getNumCC()); + assertEquals(1, componentAll.get(1).getNumSC()); + } + + @Test + void testNoCc0Sc0() { + Network network = ConnectedComponentNetworkFactory.createNoCc0Sc0(); + var compByBus = network.getBusBreakerView().getBusStream().collect(Collectors.toMap(Identifiable::getId, b -> String.format("CC%d SC%d", b.getConnectedComponent().getNum(), b.getSynchronousComponent().getNum()))); + assertEquals(6, compByBus.size()); + assertEquals("CC0 SC1", compByBus.get("b01")); + assertEquals("CC0 SC2", compByBus.get("b02")); + assertEquals("CC0 SC3", compByBus.get("b03")); + assertEquals("CC0 SC4", compByBus.get("b04")); + assertEquals("CC1 SC0", compByBus.get("b11")); + assertEquals("CC1 SC0", compByBus.get("b12")); + LoadFlowParameters lfParametersAll = new LoadFlowParameters().setConnectedComponentMode(LoadFlowParameters.ConnectedComponentMode.ALL); + LoadFlowParameters lfParametersMain = new LoadFlowParameters().setConnectedComponentMode(LoadFlowParameters.ConnectedComponentMode.MAIN); + var lfResultAll = LoadFlow.run(network, lfParametersAll); + assertTrue(lfResultAll.isFullyConverged()); + var lfResultMain = LoadFlow.run(network, lfParametersMain); + assertTrue(lfResultAll.isFullyConverged()); + assertEquals(5, lfResultAll.getComponentResults().size()); // 5 SCs + assertTrue(lfResultMain.isFullyConverged()); + assertEquals(4, lfResultMain.getComponentResults().size()); // 4 SCs + + var saResultMain = runSecurityAnalysis(network, Collections.emptyList(), createNetworkMonitors(network), lfParametersMain); + assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, saResultMain.getPreContingencyResult().getStatus()); + assertEquals(4, saResultMain.getPreContingencyResult().getNetworkResult().getBusResults().size()); // 4 buses in CC0 + var saResultAll = runSecurityAnalysis(network, Collections.emptyList(), createNetworkMonitors(network), lfParametersAll); + assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, saResultAll.getPreContingencyResult().getStatus()); + assertEquals(6, saResultAll.getPreContingencyResult().getNetworkResult().getBusResults().size()); // 6 buses in total + } }