diff --git a/src/main/java/com/powsybl/openloadflow/network/LfBranch.java b/src/main/java/com/powsybl/openloadflow/network/LfBranch.java index f5c0040d5d..d0064eafd9 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfBranch.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfBranch.java @@ -77,9 +77,7 @@ default List getLimits2(LimitType type) { void setDiscreteVoltageControl(DiscreteVoltageControl discreteVoltageControl); - default BranchResult createBranchResult() { - throw new PowsyblException("Unsupported type of branch for branch result: " + getId()); - } + BranchResult createBranchResult(double preContingencyP1, double branchInContingencyP1); boolean isDisabled(); diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfBranchImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfBranchImpl.java index fbb9ac358c..662a0dd9b3 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfBranchImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfBranchImpl.java @@ -202,11 +202,15 @@ public Evaluable getI2() { } @Override - public BranchResult createBranchResult() { + public BranchResult createBranchResult(double preContingencyP1, double branchInContingencyP1) { + double flowTransfer = Double.NaN; + if (preContingencyP1 != Double.NaN && branchInContingencyP1 != Double.NaN) { + flowTransfer = (p1.eval() * PerUnit.SB - preContingencyP1) / branchInContingencyP1; + } double currentScale1 = PerUnit.ib(branch.getTerminal1().getVoltageLevel().getNominalV()); double currentScale2 = PerUnit.ib(branch.getTerminal2().getVoltageLevel().getNominalV()); return new BranchResult(getId(), p1.eval() * PerUnit.SB, q1.eval() * PerUnit.SB, currentScale1 * i1.eval(), - p2.eval() * PerUnit.SB, q2.eval() * PerUnit.SB, currentScale2 * i2.eval()); + p2.eval() * PerUnit.SB, q2.eval() * PerUnit.SB, currentScale2 * i2.eval(), flowTransfer); } @Override diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBranch.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBranch.java index 65c8f9026f..7ab6cdb987 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBranch.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfDanglingLineBranch.java @@ -6,9 +6,11 @@ */ package com.powsybl.openloadflow.network.impl; +import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.DanglingLine; import com.powsybl.iidm.network.LimitType; import com.powsybl.openloadflow.network.*; +import com.powsybl.security.results.BranchResult; import java.util.List; import java.util.Objects; @@ -51,6 +53,11 @@ public boolean hasPhaseControlCapability() { return false; } + @Override + public BranchResult createBranchResult(double preContingencyP1, double branchInContingencyP1) { + throw new PowsyblException("Unsupported type of branch for branch result: " + getId()); + } + @Override public List getLimits1(final LimitType type) { switch (type) { diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfLegBranch.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfLegBranch.java index 1660906c57..bd0b2cc032 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfLegBranch.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfLegBranch.java @@ -12,6 +12,7 @@ import com.powsybl.iidm.network.RatioTapChanger; import com.powsybl.iidm.network.ThreeWindingsTransformer; import com.powsybl.openloadflow.network.*; +import com.powsybl.security.results.BranchResult; import java.util.*; @@ -103,6 +104,11 @@ public boolean hasPhaseControlCapability() { return leg.getPhaseTapChanger() != null; } + @Override + public BranchResult createBranchResult(double preContingencyP1, double branchInContingencyP1) { + throw new PowsyblException("Unsupported type of branch for branch result: " + getId()); + } + @Override public List getLimits1(final LimitType type) { switch (type) { diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfSwitch.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfSwitch.java index c60db4d46d..8929ac1c79 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfSwitch.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfSwitch.java @@ -102,7 +102,7 @@ public Evaluable getI2() { } @Override - public BranchResult createBranchResult() { + public BranchResult createBranchResult(double preContingencyP1, double branchInContingencyP1) { throw new PowsyblException("Unsupported type of branch for branch result: " + aSwitch.getId()); } diff --git a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java index fc5e4f8090..2f1f62ca25 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java @@ -233,10 +233,22 @@ List createContingencies(List propagatedCo } protected void addMonitorInfo(LfNetwork network, StateMonitor monitor, Collection branchResultConsumer, - Collection busResultsConsumer, Collection threeWindingsTransformerResultConsumer) { + Collection busResultsConsumer, Collection threeWindingsTransformerResultConsumer, + Map preContingencyBranchResults, String contingencyId) { network.getBranches().stream().filter(lfBranch -> monitor.getBranchIds().contains(lfBranch.getId())) .filter(lfBranch -> !lfBranch.isDisabled()) - .forEach(lfBranch -> branchResultConsumer.add(lfBranch.createBranchResult())); + .forEach(lfBranch -> { + BranchResult branchResult; + if (contingencyId == null) { + branchResult = lfBranch.createBranchResult(Double.NaN, Double.NaN); + preContingencyBranchResults.put(lfBranch.getId(), branchResult); + } else { + double preContingencyP1 = preContingencyBranchResults.get(lfBranch.getId()) != null ? preContingencyBranchResults.get(lfBranch.getId()).getP1() : Double.NaN; + double branchInContingencyP1 = preContingencyBranchResults.get(contingencyId) != null ? preContingencyBranchResults.get(contingencyId).getP1() : Double.NaN; + branchResult = lfBranch.createBranchResult(preContingencyP1, branchInContingencyP1); + } + branchResultConsumer.add(branchResult); + }); network.getBuses().stream().filter(lfBus -> monitor.getVoltageLevelIds().contains(lfBus.getVoltageLevelId())) .filter(lfBus -> !lfBus.isDisabled()) .forEach(lfBus -> busResultsConsumer.add(lfBus.createBusResult())); diff --git a/src/main/java/com/powsybl/openloadflow/sa/AcSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/AcSecurityAnalysis.java index 09ae229422..94ebd9c49d 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/AcSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/AcSecurityAnalysis.java @@ -115,10 +115,11 @@ private SecurityAnalysisResult runSimulations(LfNetwork network, List results = new LinkedHashMap<>(); addMonitorInfo(network, monitorIndex.getNoneStateMonitor(), preContingencyBranchResults, preContingencyBusResults, - preContingencyThreeWindingsTransformerResults); + preContingencyThreeWindingsTransformerResults, results, null); addMonitorInfo(network, monitorIndex.getAllStateMonitor(), preContingencyBranchResults, preContingencyBusResults, - preContingencyThreeWindingsTransformerResults); + preContingencyThreeWindingsTransformerResults, results, null); boolean preContingencyComputationOk = preContingencyLoadFlowResult.getNewtonRaphsonStatus() == NewtonRaphsonStatus.CONVERGED; Map, LimitViolation> preContingencyLimitViolations = new LinkedHashMap<>(); @@ -150,7 +151,7 @@ private SecurityAnalysisResult runSimulations(LfNetwork network, List, LimitViolation> preContingencyLimitViolations) { + Map, LimitViolation> preContingencyLimitViolations, + Map preContingencyBranchResults) { LOGGER.info("Start post contingency '{}' simulation", lfContingency.getContingency().getId()); Stopwatch stopwatch = Stopwatch.createStarted(); @@ -192,10 +194,12 @@ private PostContingencyResult runPostContingencySimulation(LfNetwork network, Ac network.getBranches().stream().filter(b -> !b.isDisabled()), network.getBuses().stream().filter(b -> !b.isDisabled()), postContingencyLimitViolations); - addMonitorInfo(network, monitorIndex.getAllStateMonitor(), branchResults, busResults, threeWindingsTransformerResults); + + addMonitorInfo(network, monitorIndex.getAllStateMonitor(), branchResults, busResults, threeWindingsTransformerResults, preContingencyBranchResults, lfContingency.getContingency().getId()); + StateMonitor stateMonitor = monitorIndex.getSpecificStateMonitors().get(lfContingency.getContingency().getId()); if (stateMonitor != null) { - addMonitorInfo(network, stateMonitor, branchResults, busResults, threeWindingsTransformerResults); + addMonitorInfo(network, stateMonitor, branchResults, busResults, threeWindingsTransformerResults, preContingencyBranchResults, lfContingency.getContingency().getId()); } } diff --git a/src/main/java/com/powsybl/openloadflow/sa/DcSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/DcSecurityAnalysis.java index b889c3d585..4c600aa61b 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/DcSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/DcSecurityAnalysis.java @@ -13,12 +13,14 @@ import com.powsybl.security.*; import com.powsybl.security.detectors.LoadingLimitType; import com.powsybl.security.monitor.StateMonitor; +import com.powsybl.security.results.BranchResult; import com.powsybl.security.results.PostContingencyResult; import com.powsybl.security.detectors.DefaultLimitViolationDetector; import com.powsybl.sensitivity.SensitivityAnalysisParameters; import java.util.*; import java.util.function.Supplier; +import java.util.stream.Collectors; public class DcSecurityAnalysis extends AbstractSecurityAnalysis { @@ -55,11 +57,15 @@ SecurityAnalysisReport runSync(final SecurityAnalysisParameters securityAnalysis DefaultLimitViolationDetector detector = new DefaultLimitViolationDetector(1.0f, EnumSet.allOf(LoadingLimitType.class)); + StateMonitor monitor = monitorIndex.getAllStateMonitor(); + Map preContingencyBranchResults = new HashMap<>(); + List preContingencyLimitViolations = new ArrayList<>(); for (SensitivityValue2 sensValue : res.getValues(null)) { SensitivityFactor2 factor = (SensitivityFactor2) sensValue.getFactorContext(); String branchId = factor.getFunctionId(); Branch branch = network.getBranch(branchId); + preContingencyBranchResults.put(branchId, new BranchResult(branchId, sensValue.getFunctionReference(), Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN)); detector.checkActivePower(branch, Branch.Side.ONE, Math.abs(sensValue.getFunctionReference()), preContingencyLimitViolations::add); } @@ -67,19 +73,28 @@ SecurityAnalysisReport runSync(final SecurityAnalysisParameters securityAnalysis List postContingencyResults = new ArrayList<>(); for (Contingency contingency : contingencies) { + Map postContingencyBranchResults = new HashMap<>(); List values = res.getValues(contingency.getId()); List violations = new ArrayList<>(); + double branchInContingencyP1 = preContingencyBranchResults.get(contingency.getId()).getP1(); for (SensitivityValue2 v : values) { SensitivityFactor2 factor = (SensitivityFactor2) v.getFactorContext(); String branchId = factor.getFunctionId(); Branch branch = network.getBranch(branchId); + + if (monitor.getBranchIds().contains(branchId)) { + BranchResult preContingencyBranchResult = preContingencyBranchResults.get(branchId); + double flowTransfer = (v.getFunctionReference() - preContingencyBranchResult.getP1()) / branchInContingencyP1; + postContingencyBranchResults.put(branchId, new BranchResult(branchId, v.getFunctionReference(), Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, flowTransfer)); + } + detector.checkActivePower(branch, Branch.Side.ONE, Math.abs(v.getFunctionReference()), violations::add); } - postContingencyResults.add(new PostContingencyResult(contingency, true, violations)); + postContingencyResults.add(new PostContingencyResult(contingency, true, violations, postContingencyBranchResults, Collections.emptyMap(), Collections.emptyMap())); } - return new SecurityAnalysisReport(new SecurityAnalysisResult(preContingencyResult, postContingencyResults)); + return new SecurityAnalysisReport(new SecurityAnalysisResult(preContingencyResult, postContingencyResults, preContingencyBranchResults.values().stream().collect(Collectors.toList()), Collections.emptyList(), Collections.emptyList())); } } diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index 6ec7c8027b..cf89b574c5 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -598,10 +598,13 @@ void testSADcMode() { fourBusNetwork.getLine("l34").newActivePowerLimits1().setPermanentLimit(0.15).add(); fourBusNetwork.getLine("l13").newActivePowerLimits1().setPermanentLimit(0.1).add(); + List monitors = new ArrayList<>(); + monitors.add(new StateMonitor(ContingencyContext.all(), Set.of("l14", "l12", "l23", "l34", "l13"), Collections.emptySet(), Collections.emptySet())); + OpenSecurityAnalysisProvider osaProvider = new OpenSecurityAnalysisProvider(new DenseMatrixFactory(), EvenShiloachGraphDecrementalConnectivity::new); CompletableFuture futureResult = osaProvider.run(fourBusNetwork, fourBusNetwork.getVariantManager().getWorkingVariantId(), new DefaultLimitViolationDetector(), new LimitViolationFilter(), null, saParameters, - contingenciesProvider, Collections.emptyList(), Collections.emptyList()); + contingenciesProvider, Collections.emptyList(), monitors); SecurityAnalysisResult result = futureResult.join().getResult(); assertTrue(result.getPreContingencyResult().getLimitViolationsResult().isComputationOk()); @@ -613,6 +616,33 @@ void testSADcMode() { assertEquals(4, result.getPostContingencyResults().get(3).getLimitViolationsResult().getLimitViolations().size()); assertEquals(4, result.getPostContingencyResults().get(4).getLimitViolationsResult().getLimitViolations().size()); + //Branch result for first contingency + assertEquals(5, result.getPostContingencyResults().get(0).getBranchResults().size()); + + //Check branch results for flowTransfer computation for contingency on l14 + PostContingencyResult postContl14 = result.getPostContingencyResults().stream().filter(r -> r.getContingency().getId().equals("l14")).findFirst().get(); + assertEquals("l14", postContl14.getContingency().getId()); + + BranchResult brl14l12 = postContl14.getBranchResult("l12"); + assertEquals(0.33, brl14l12.getP1(), 1e-2); + assertEquals(0.33, brl14l12.getFlowTransfer(), 1e-2); + + BranchResult brl14l14 = postContl14.getBranchResult("l14"); + assertEquals(0.0, brl14l14.getP1(), 1e-2); + assertEquals(-1.0, brl14l14.getFlowTransfer(), 1e-2); + + BranchResult brl14l23 = postContl14.getBranchResult("l23"); + assertEquals(1.33, brl14l23.getP1(), 1e-2); + assertEquals(0.33, brl14l23.getFlowTransfer(), 1e-2); + + BranchResult brl14l34 = postContl14.getBranchResult("l34"); + assertEquals(-1.0, brl14l34.getP1(), 1e-2); + assertEquals(1.0, brl14l34.getFlowTransfer(), 1e-2); + + BranchResult brl14l13 = postContl14.getBranchResult("l13"); + assertEquals(1.66, brl14l13.getP1(), 1e-2); + assertEquals(0.66, brl14l13.getFlowTransfer(), 1e-2); + StringWriter writer = new StringWriter(); Security.print(result, fourBusNetwork, writer, new AsciiTableFormatterFactory(), new TableFormatterConfig()); } @@ -672,4 +702,57 @@ private static SecurityAnalysisResult runSecurityAnalysis(Network network, List< monitors); return futureResult.join().getResult(); } + + @Test + void testSAmodeACAllBranchMonitoredFlowTransfer() { + Network network = FourBusNetworkFactory.create(); + SecurityAnalysisParameters saParameters = new SecurityAnalysisParameters(); + LoadFlowParameters lfParameters = new LoadFlowParameters(); + OpenLoadFlowParameters olfParameters = new OpenLoadFlowParameters() + .setSlackBusSelectionMode(SlackBusSelectionMode.MOST_MESHED); + lfParameters.addExtension(OpenLoadFlowParameters.class, olfParameters); + saParameters.setLoadFlowParameters(lfParameters); + + // Testing all contingencies at once + ContingenciesProvider contingenciesProvider = n -> n.getBranchStream() + .map(b -> new Contingency(b.getId(), new BranchContingency(b.getId()))) + .collect(Collectors.toList()); + + Set allBranchIds = network.getBranchStream().map(b -> b.getId()).collect(Collectors.toSet()); + + List monitors = new ArrayList<>(); + monitors.add(new StateMonitor(ContingencyContext.all(), allBranchIds, Collections.emptySet(), Collections.emptySet())); + + OpenSecurityAnalysisProvider osaProvider = new OpenSecurityAnalysisProvider(new DenseMatrixFactory(), EvenShiloachGraphDecrementalConnectivity::new); + CompletableFuture futureResult = osaProvider.run(network, network.getVariantManager().getWorkingVariantId(), + new DefaultLimitViolationDetector(), new LimitViolationFilter(), null, saParameters, + contingenciesProvider, Collections.emptyList(), monitors); + SecurityAnalysisResult result = futureResult.join().getResult(); + + assertEquals(5, result.getPostContingencyResults().size()); + + for (PostContingencyResult r : result.getPostContingencyResults()) { + assertEquals(4, r.getBranchResults().size()); + } + + //Check branch results for flowTransfer computation for contingency on l14 + PostContingencyResult postContl14 = result.getPostContingencyResults().stream().filter(r -> r.getContingency().getId().equals("l14")).findFirst().get(); + assertEquals("l14", postContl14.getContingency().getId()); + + BranchResult brl14l12 = postContl14.getBranchResult("l12"); + assertEquals(0.33, brl14l12.getP1(), 1e-2); + assertEquals(0.33, brl14l12.getFlowTransfer(), 1e-2); + + BranchResult brl14l23 = postContl14.getBranchResult("l23"); + assertEquals(1.33, brl14l23.getP1(), 1e-2); + assertEquals(0.33, brl14l23.getFlowTransfer(), 1e-2); + + BranchResult brl14l34 = postContl14.getBranchResult("l34"); + assertEquals(-1.0, brl14l34.getP1(), 1e-2); + assertEquals(1.0, brl14l34.getFlowTransfer(), 1e-2); + + BranchResult brl14l13 = postContl14.getBranchResult("l13"); + assertEquals(1.66, brl14l13.getP1(), 1e-2); + assertEquals(0.66, brl14l13.getFlowTransfer(), 1e-2); + } }