From 352dd988b806b3a1b2df075a899406f46f5b8637 Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Thu, 9 Jan 2025 11:19:22 +0100 Subject: [PATCH 01/10] Fix SA reports in MT, an support operator strategies in MT Signed-off-by: Didier Vidal --- .../sa/AbstractSecurityAnalysis.java | 62 ++++++--- .../sa/WoodburyDcSecurityAnalysis.java | 38 ++++-- .../powsybl/openloadflow/util/Reports.java | 11 +- .../sa/OpenSecurityAnalysisTest.java | 125 +++++++++++++++--- 4 files changed, 187 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java index 57d634ffc2..c6f7616533 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java @@ -42,6 +42,8 @@ import com.powsybl.security.results.*; import com.powsybl.security.strategy.ConditionalActions; import com.powsybl.security.strategy.OperatorStrategy; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -155,7 +157,8 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame // create networks including all necessary switches try (LfNetworkList lfNetworks = Networks.load(network, parameters.getNetworkParameters(), topoConfig, saReportNode)) { - finalResult = runSimulationsOnAllComponents(lfNetworks, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions, lfParameters); + finalResult = runSimulationsOnAllComponents(lfNetworks, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions, lfParameters, + lfNetworks.getList().stream().collect(Collectors.toMap(n -> new ImmutablePair<>(n.getNumCC(), n.getNumSC()), n -> n.getReportNode()))); } } else { @@ -171,6 +174,7 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame try { Lock networkLock = new ReentrantLock(); List> futures = new ArrayList<>(); + Map, ReportNode> ccNetworkReportNodes = new HashMap<>(); for (int i = 0; i < contingenciesPartitions.size(); i++) { final int partitionNum = i; var contingenciesPartition = contingenciesPartitions.get(i); @@ -192,6 +196,7 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame P parameters; networkLock.lock(); try { + boolean first = ccNetworkReportNodes.isEmpty(); network.getVariantManager().setWorkingVariant(workingVariantId); propagatedContingencies = PropagatedContingency.createList(network, contingenciesPartition, partitionTopoConfig, creationParameters); @@ -199,15 +204,24 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame parameters = createParameters(lfParameters, lfParametersExt, partitionTopoConfig.isBreaker()); // create networks including all necessary switches - lfNetworks = Networks.load(network, parameters.getNetworkParameters(), partitionTopoConfig, saReportNode); + // LfNetworks creation reports are only made for the first partition + lfNetworks = Networks.load(network, parameters.getNetworkParameters(), partitionTopoConfig, first ? saReportNode : ReportNode.NO_OP); lfNetworksList.add(0, lfNetworks); // FIXME to workaround variant removal bug, to fix in core + + if (first) { + // List CC reports to attach post contingency result + for (LfNetwork n : lfNetworks.getList()) { + ccNetworkReportNodes.put(new ImmutablePair<>(n.getNumCC(), n.getNumSC()), n.getReportNode()); + } + } } finally { networkLock.unlock(); } // run simulation on largest network partitionResults.set(partitionNum, runSimulationsOnAllComponents( - lfNetworks, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions, lfParameters)); + lfNetworks, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions, lfParameters, + ccNetworkReportNodes)); return null; }, executor)); @@ -250,7 +264,8 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame SecurityAnalysisResult runSimulationsOnAllComponents(LfNetworkList networks, List propagatedContingencies, P parameters, SecurityAnalysisParameters securityAnalysisParameters, List operatorStrategies, - List actions, List limitReductions, LoadFlowParameters lfParameters) { + List actions, List limitReductions, LoadFlowParameters lfParameters, + Map, ReportNode> postCtgReportNodeMap) { List networkToSimulate = new ArrayList<>(getNetworksToSimulate(networks, lfParameters.getConnectedComponentMode())); @@ -260,7 +275,7 @@ SecurityAnalysisResult runSimulationsOnAllComponents(LfNetworkList networks, Lis // run simulation on first lfNetwork to initialize results structures LfNetwork firstNetwork = networkToSimulate.remove(0); - SecurityAnalysisResult result = runSimulations(firstNetwork, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions); + SecurityAnalysisResult result = runSimulations(firstNetwork, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions, postCtgReportNodeMap); List postContingencyResults = result.getPostContingencyResults(); List operatorStrategyResults = result.getOperatorStrategyResults(); @@ -276,7 +291,8 @@ SecurityAnalysisResult runSimulationsOnAllComponents(LfNetworkList networks, Lis preContingencyViolations = new ArrayList<>(preContingencyViolations); for (LfNetwork n : networkToSimulate) { - SecurityAnalysisResult resultOtherComponent = runSimulations(n, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions); + SecurityAnalysisResult resultOtherComponent = runSimulations(n, propagatedContingencies, parameters, securityAnalysisParameters, + operatorStrategies, actions, limitReductions, postCtgReportNodeMap); // Merge into first result // PreContingency results first @@ -477,7 +493,8 @@ protected static Map indexActionsById(List actions) { protected static Map> indexOperatorStrategiesByContingencyId(List propagatedContingencies, List operatorStrategies, Map actionsById, - Set neededActions) { + Set neededActions, + boolean tolerateMissingContingencies) { Set contingencyIds = propagatedContingencies.stream().map(propagatedContingency -> propagatedContingency.getContingency().getId()).collect(Collectors.toSet()); Map> operatorStrategiesByContingencyId = new HashMap<>(); for (OperatorStrategy operatorStrategy : operatorStrategies) { @@ -496,8 +513,10 @@ protected static Map> indexOperatorStrategiesByCo operatorStrategiesByContingencyId.computeIfAbsent(operatorStrategy.getContingencyContext().getContingencyId(), key -> new ArrayList<>()) .add(operatorStrategy); } else { - throw new PowsyblException("Operator strategy '" + operatorStrategy.getId() + "' is associated to contingency '" - + operatorStrategy.getContingencyContext().getContingencyId() + "' but this contingency is not present in the list"); + if (!tolerateMissingContingencies) { + throw new PowsyblException("Operator strategy '" + operatorStrategy.getId() + "' is associated to contingency '" + + operatorStrategy.getContingencyContext().getContingencyId() + "' but this contingency is not present in the list"); + } } } return operatorStrategiesByContingencyId; @@ -621,10 +640,15 @@ protected void afterPreContingencySimulation(P acParameters) { protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List propagatedContingencies, P acParameters, SecurityAnalysisParameters securityAnalysisParameters, List operatorStrategies, - List actions, List limitReductions) { + List actions, List limitReductions, Map, ReportNode> postContReportMap) { Map actionsById = indexActionsById(actions); Set neededActions = new HashSet<>(actionsById.size()); - Map> operatorStrategiesByContingencyId = indexOperatorStrategiesByContingencyId(propagatedContingencies, operatorStrategies, actionsById, neededActions); + boolean tolerateMissingContingencies = false; + OpenSecurityAnalysisParameters securityAnalysisParametersExt = securityAnalysisParameters.getExtension(OpenSecurityAnalysisParameters.class); + tolerateMissingContingencies = securityAnalysisParametersExt != null && securityAnalysisParametersExt.getThreadCount() > 1; + + Map> operatorStrategiesByContingencyId = + indexOperatorStrategiesByContingencyId(propagatedContingencies, operatorStrategies, actionsById, neededActions, tolerateMissingContingencies); Map lfActionById = createLfActions(lfNetwork, neededActions, network, acParameters.getNetworkParameters()); // only convert needed actions LoadFlowParameters loadFlowParameters = securityAnalysisParameters.getLoadFlowParameters(); @@ -633,9 +657,16 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List(lfNetwork.getNumCC(), lfNetwork.getNumSC())); + synchronized (ccReportNode) { + // In an MT run, the report is a NO_OP if not the first thread to run on this CC, to avoid duplicate + // functional reports of lf network loading and precontingency run + preContReportNode = (ccReportNode.getChildren().size() == 1) ? ccReportNode : ReportNode.NO_OP; + ReportNode preContSimReportNode = Reports.createPreContingencySimulation(preContReportNode); + lfNetwork.setReportNode(preContSimReportNode); + } + // run pre-contingency simulation R preContingencyLoadFlowResult = createLoadFlowEngine(context) @@ -666,7 +697,8 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List { // only process contingencies that impact the network - ReportNode postContSimReportNode = Reports.createPostContingencySimulation(networkReportNode, lfContingency.getId()); + ReportNode postContSimReportNode = + Reports.createPostContingencySimulation(ccReportNode, lfContingency.getId()); lfNetwork.setReportNode(postContSimReportNode); lfContingency.apply(loadFlowParameters.getBalanceType()); diff --git a/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java index 4f812249fb..d60c6e78d0 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java @@ -41,6 +41,8 @@ import com.powsybl.security.monitor.StateMonitor; import com.powsybl.security.results.*; import com.powsybl.security.strategy.OperatorStrategy; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; import java.util.*; import java.util.function.Function; @@ -316,11 +318,12 @@ private void addPostContingencyAndOperatorStrategyResults(DcLoadFlowContext cont Map lfActionById, Supplier toPostContingencyStates, Function, double[]> toPostContingencyAndOperatorStrategyStates, Runnable restorePreContingencyStates, LimitViolationManager preContingencyLimitViolationManager, PreContingencyNetworkResult preContingencyNetworkResult, boolean createResultExtension, SecurityAnalysisParameters.IncreasedViolationsParameters violationsParameters, List limitReductions, - List postContingencyResults, List operatorStrategyResults) { + List postContingencyResults, List operatorStrategyResults, + ReportNode saReportNode) { LfNetwork lfNetwork = context.getNetwork(); // process results only if contingency impacts the network contingency.toLfContingency(lfNetwork, false).ifPresent(lfContingency -> { - ReportNode postContSimReportNode = Reports.createPostContingencySimulation(lfNetwork.getReportNode(), contingency.getContingency().getId()); + ReportNode postContSimReportNode = Reports.createPostContingencySimulation(saReportNode, contingency.getContingency().getId()); lfNetwork.setReportNode(postContSimReportNode); // process post contingency result with supplier giving post contingency states @@ -367,20 +370,29 @@ private static Map createTapPositionCh @Override protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List propagatedContingencies, DcLoadFlowParameters dcParameters, SecurityAnalysisParameters securityAnalysisParameters, List operatorStrategies, - List actions, List limitReductions) { + List actions, List limitReductions, Map, ReportNode> postContReportMap) { // Verify only PST actions are given filterActions(actions); Map actionsById = indexActionsById(actions); Set neededActions = new HashSet<>(actionsById.size()); - Map> operatorStrategiesByContingencyId = indexOperatorStrategiesByContingencyId(propagatedContingencies, operatorStrategies, actionsById, neededActions); + boolean tolerateMissingContingencies = false; + OpenSecurityAnalysisParameters securityAnalysisParametersExt = securityAnalysisParameters.getExtension(OpenSecurityAnalysisParameters.class); + tolerateMissingContingencies = securityAnalysisParametersExt != null && securityAnalysisParametersExt.getThreadCount() > 1; + + Map> operatorStrategiesByContingencyId = + indexOperatorStrategiesByContingencyId(propagatedContingencies, operatorStrategies, actionsById, neededActions, tolerateMissingContingencies); + Map lfActionById = createLfActions(lfNetwork, neededActions, network, dcParameters.getNetworkParameters()); // only convert needed actions OpenSecurityAnalysisParameters openSecurityAnalysisParameters = OpenSecurityAnalysisParameters.getOrDefault(securityAnalysisParameters); boolean createResultExtension = openSecurityAnalysisParameters.isCreateResultExtension(); + Pair networkKey = new ImmutablePair<>(lfNetwork.getNumCC(), lfNetwork.getNumSC()); + try (DcLoadFlowContext context = new DcLoadFlowContext(lfNetwork, dcParameters, false)) { - ReportNode networkReportNode = lfNetwork.getReportNode(); - ReportNode preContSimReportNode = Reports.createPreContingencySimulation(networkReportNode); + ReportNode preContReportNode = lfNetwork.getReportNode(); // in MT mode the lefNetwork's report node is a NO_OP report node if not first partition + // to avoid duplicate reports of netowrk loading and precontingency run + ReportNode preContSimReportNode = Reports.createPreContingencySimulation(preContReportNode); lfNetwork.setReportNode(preContSimReportNode); // prepare contingencies for connectivity analysis and Woodbury engine @@ -389,7 +401,7 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List { // supplier to compute post contingency states Supplier toPostContingencyStates = () -> calculatePostContingencyStates(context, connectivityBreakAnalysisResults.contingenciesStates(), workingContingencyStates, - nonBreakingConnectivityContingency, connectivityBreakAnalysisResults.contingencyElementByBranch(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), reportNode); + nonBreakingConnectivityContingency, connectivityBreakAnalysisResults.contingencyElementByBranch(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), postContReportMap.get(networkKey)); // function to compute post contingency and post operator strategy states Function, double[]> toPostContingencyAndOperatorStrategyStates = operatorStrategyLfActions -> calculatePostContingencyAndOperatorStrategyStates(context, connectivityBreakAnalysisResults.contingenciesStates(), workingContingencyStates, nonBreakingConnectivityContingency, connectivityBreakAnalysisResults.contingencyElementByBranch(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), - operatorStrategyLfActions, tapPositionChangeElementsByBranchId, actionsStates, reportNode); + operatorStrategyLfActions, tapPositionChangeElementsByBranchId, actionsStates, postContReportMap.get(networkKey)); // runnable to restore pre contingency states, after modifications applied to the lfNetwork Runnable restorePreContingencyStates = () -> { // update workingContingencyStates as it may have been updated by post contingency states calculation @@ -444,7 +456,7 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List toPostContingencyStates = () -> calculatePostContingencyStatesForAContingencyBreakingConnectivity(connectivityAnalysisResult, context, - connectivityBreakAnalysisResults.contingencyElementByBranch(), workingContingencyStates, connectivityBreakAnalysisResults.contingenciesStates(), reportNode); + connectivityBreakAnalysisResults.contingencyElementByBranch(), workingContingencyStates, connectivityBreakAnalysisResults.contingenciesStates(), postContReportMap.get(networkKey)); // function to compute post contingency and post operator strategy states Function, double[]> toPostContingencyAndOperatorStrategyStates = operatorStrategyLfActions -> calculatePostContingencyAndOperatorStrategyStatesForAContingencyBreakingConnectivity(connectivityAnalysisResult, context, connectivityBreakAnalysisResults.contingencyElementByBranch(), workingContingencyStates, connectivityBreakAnalysisResults.contingenciesStates(), - operatorStrategyLfActions, tapPositionChangeElementsByBranchId, actionsStates, reportNode); + operatorStrategyLfActions, tapPositionChangeElementsByBranchId, actionsStates, postContReportMap.get(networkKey)); // runnable to restore pre contingency states, after modifications applied to the lfNetwork // no need to update workingContingencyStates as an override of flow states will be computed Runnable restorePreContingencyStates = networkState::restore; addPostContingencyAndOperatorStrategyResults(context, breakingConnectivityContingency, operatorStrategiesByContingencyId, lfActionById, toPostContingencyStates, toPostContingencyAndOperatorStrategyStates, restorePreContingencyStates, preContingencyLimitViolationManager, preContingencyNetworkResult, createResultExtension, - securityAnalysisParameters.getIncreasedViolationsParameters(), limitReductions, postContingencyResults, operatorStrategyResults); + securityAnalysisParameters.getIncreasedViolationsParameters(), limitReductions, postContingencyResults, operatorStrategyResults, postContReportMap.get(networkKey)); }); return new SecurityAnalysisResult( diff --git a/src/main/java/com/powsybl/openloadflow/util/Reports.java b/src/main/java/com/powsybl/openloadflow/util/Reports.java index 79eb42a984..69398d7ac2 100644 --- a/src/main/java/com/powsybl/openloadflow/util/Reports.java +++ b/src/main/java/com/powsybl/openloadflow/util/Reports.java @@ -470,10 +470,13 @@ public static ReportNode createPreContingencySimulation(ReportNode reportNode) { } public static ReportNode createPostContingencySimulation(ReportNode reportNode, String contingencyId) { - return reportNode.newReportNode() - .withMessageTemplate("postContingencySimulation", "Post-contingency simulation '${contingencyId}'") - .withUntypedValue("contingencyId", contingencyId) - .add(); + // This cn be called in an MT context on the same saNode + synchronized (reportNode) { + return reportNode.newReportNode() + .withMessageTemplate("postContingencySimulation", "Post-contingency simulation '${contingencyId}'") + .withUntypedValue("contingencyId", contingencyId) + .add(); + } } public static ReportNode createOperatorStrategySimulation(ReportNode reportNode, String operatorStrategyId) { diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index 07a08ee6b8..10b5ce5237 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -54,6 +54,7 @@ import org.mockito.Mockito; import java.io.IOException; +import java.io.StringWriter; import java.util.*; import java.util.concurrent.CompletionException; import java.util.stream.Collectors; @@ -3508,7 +3509,7 @@ void testPhaseTapChangerContingency(boolean dcFastMode) { } @Test - void testMultiThreads() { + void testMultiThreads() throws IOException { Network network = createNodeBreakerNetwork(); assertFalse(network.getVariantManager().isVariantMultiThreadAccessAllowed()); @@ -3523,12 +3524,33 @@ void testMultiThreads() { .map(id -> new Contingency(id, new BranchContingency(id))) .toList(); - SecurityAnalysisResult resultOneThread = runSecurityAnalysis(network, contingencies, Collections.emptyList(), securityAnalysisParameters); + + ReportNode reportNodeOneThread = ReportNode.newRootReportNode() + .withMessageTemplate("MT_SA_TEST", "MT SA Report Node") + .build(); + SecurityAnalysisResult resultOneThread = + runSecurityAnalysis(network, contingencies, Collections.emptyList(), securityAnalysisParameters, reportNodeOneThread); + securityAnalysisParametersExt.setThreadCount(2); - SecurityAnalysisResult resultTwoThreads = runSecurityAnalysis(network, contingencies, Collections.emptyList(), securityAnalysisParameters); + ReportNode reportNodeTwoThreads = ReportNode.newRootReportNode() + .withMessageTemplate("MT_SA_TEST", "MT SA Report Node") + .build(); + SecurityAnalysisResult resultTwoThreads = + runSecurityAnalysis(network, contingencies, Collections.emptyList(), securityAnalysisParameters, reportNodeOneThread); + assertFalse(network.getVariantManager().isVariantMultiThreadAccessAllowed()); assertEquals(resultOneThread.getPostContingencyResults().size(), resultTwoThreads.getPostContingencyResults().size()); assertEquals(resultOneThread.getOperatorStrategyResults().size(), resultTwoThreads.getOperatorStrategyResults().size()); + + StringWriter swOneThread = new StringWriter(); + reportNodeTwoThreads.print(swOneThread); + + StringWriter swTwoThreads = new StringWriter(); + reportNodeTwoThreads.print(swTwoThreads); + + // The two report nodes must have the same size (but the order of contingenceis may change) + // The test would fail if an indentation level changes or some reports are ommited or duplicated in MT + assertEquals(swOneThread.toString().length(), swTwoThreads.toString().length()); } @Test @@ -3652,8 +3674,9 @@ private void testWithFictitiousLoad2(LoadFlowParameters.BalanceType balanceType) assertEquals(network.getLine("l34").getTerminal1().getP(), networkResultContingency0.getBranchResult("l34").getP1(), LoadFlowAssert.DELTA_POWER); } - @Test - void multiComponentSaTest() { + @ParameterizedTest + @ValueSource(ints = {1, 2}) + void multiComponentSaTest(int threadCount) throws IOException { Network network = FourBusNetworkFactory.createWithTwoScs(); // Add a load on small component network.getBusBreakerView().getBus("c1") @@ -3666,20 +3689,42 @@ void multiComponentSaTest() { .add(); SecurityAnalysisParameters securityAnalysisParameters = new SecurityAnalysisParameters(); + OpenSecurityAnalysisParameters securityAnalysisParametersExt = new OpenSecurityAnalysisParameters() + .setThreadCount(threadCount); + securityAnalysisParameters.addExtension(OpenSecurityAnalysisParameters.class, securityAnalysisParametersExt); securityAnalysisParameters.getLoadFlowParameters().setConnectedComponentMode(LoadFlowParameters.ConnectedComponentMode.ALL); - List contingencies = List.of(new Contingency("l13", new BranchContingency("l13")), + List checkedContingencies = List.of(new Contingency("l13", new BranchContingency("l13")), new Contingency("dummyLoad", new LoadContingency("dummyLoad"))); + // Add more contingencies to activate multi thread + List contingencies = new ArrayList<>(checkedContingencies); + for (int i = 0; i < 10; i++) { + contingencies.add(new Contingency("l13_" + i, new BranchContingency("l13"))); + contingencies.add(new Contingency("dummyLoad_" + i, new LoadContingency("dummyLoad"))); + } + + // Monitor branch in both components List monitors = List.of( new StateMonitor(ContingencyContext.all(), Set.of("l14", "l12", "l23", "lc12", "lc12Bis"), emptySet(), emptySet()) ); - SecurityAnalysisResult results = runSecurityAnalysis(network, contingencies, monitors, securityAnalysisParameters); + ReportNode testReport = ReportNode.newRootReportNode() + .withMessageTemplate("TEST", "TEST Report Node") + .build(); + + SecurityAnalysisResult results = runSecurityAnalysis(network, contingencies, monitors, securityAnalysisParameters, testReport); NetworkResult preContingencyResults = results.getPreContingencyResult().getNetworkResult(); - NetworkResult postContingencyResultsl13 = results.getPostContingencyResults().get(0).getNetworkResult(); - NetworkResult postContingencyResultslc12Bis = results.getPostContingencyResults().get(1).getNetworkResult(); + NetworkResult postContingencyResultsl13 = results.getPostContingencyResults().stream() + .filter(r -> r.getContingency().getId().equals("l13")) + .findAny() + .map(AbstractContingencyResult::getNetworkResult).orElseThrow(); + + NetworkResult postContingencyResultsDummyLoad = results.getPostContingencyResults().stream() + .filter(r -> r.getContingency().getId().equals("dummyLoad")) + .findAny() + .map(AbstractContingencyResult::getNetworkResult).orElseThrow(); // Result for base case is available for all components assertEquals(0.084, preContingencyResults.getBranchResult("l14").getP1(), LoadFlowAssert.DELTA_POWER); @@ -3698,12 +3743,33 @@ void multiComponentSaTest() { assertNull(postContingencyResultsl13.getBranchResult("lc12Bis")); // Result for post contingency on dummyLoad is available for part of the network on which the contingency has an impact - assertEquals(0.498, postContingencyResultslc12Bis.getBranchResult("lc12").getP1(), LoadFlowAssert.DELTA_POWER); - assertEquals(0.498, postContingencyResultslc12Bis.getBranchResult("lc12Bis").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(0.498, postContingencyResultsDummyLoad.getBranchResult("lc12").getP1(), LoadFlowAssert.DELTA_POWER); + assertEquals(0.498, postContingencyResultsDummyLoad.getBranchResult("lc12Bis").getP1(), LoadFlowAssert.DELTA_POWER); + + StringWriter sw = new StringWriter(); + testReport.print(sw); + // The purpose of this test is to check that the report have the same size with one or two threadd + // The content should be the same, but not the order... + assertEquals(7278, sw.toString().length()); + // Check also that the preCont report is before the postContResults in the second CC + String expected = + " + Network CC1 SC1\n" + + " + Network info\n" + + " Network has 2 buses and 2 branches\n" + + " Network balance: active generation=2.0 MW, active load=2.0 MW, reactive generation=0.0 MVar, reactive load=0.0 MVar\n" + + " Angle reference bus: c1_vl_0\n" + + " Slack bus: c1_vl_0\n" + + " + Pre-contingency simulation\n" + + " Outer loop DistributedSlack\n" + + " Outer loop VoltageMonitoring\n" + + " Outer loop ReactiveLimits\n" + + " AC load flow completed successfully (solverStatus=CONVERGED, outerloopStatus=STABLE)"; + assertTrue(sw.toString().contains(expected)); } - @Test - void multiComponentSaTestContingencyBothComponentsAndOperatorStrategy() { + @ParameterizedTest + @ValueSource(ints = {1, 2}) + void multiComponentSaTestContingencyBothComponentsAndOperatorStrategy(int threadCount) throws IOException { Network network = FourBusNetworkFactory.createWithTwoScs(); // Add a load on small component @@ -3717,10 +3783,19 @@ void multiComponentSaTestContingencyBothComponentsAndOperatorStrategy() { .add(); SecurityAnalysisParameters securityAnalysisParameters = new SecurityAnalysisParameters(); + OpenSecurityAnalysisParameters securityAnalysisParametersExt = new OpenSecurityAnalysisParameters() + .setThreadCount(threadCount); + securityAnalysisParameters.addExtension(OpenSecurityAnalysisParameters.class, securityAnalysisParametersExt); securityAnalysisParameters.getLoadFlowParameters().setConnectedComponentMode(LoadFlowParameters.ConnectedComponentMode.ALL); // This contingency should impact both components - List contingencies = List.of(new Contingency("compositeContingency", List.of(new BranchContingency("l13"), new LoadContingency("dummyLoad")))); + List checkedContingencies = List.of(new Contingency("compositeContingency", List.of(new BranchContingency("l13"), new LoadContingency("dummyLoad")))); + + // Add more contingencies to activate multi thread + List contingencies = new ArrayList<>(checkedContingencies); + for (int i = 0; i < 10; i++) { + contingencies.add(new Contingency("compositeContingency_" + i, List.of(new BranchContingency("l13"), new LoadContingency("dummyLoad")))); + } // Monitor branch in both components List monitors = List.of( @@ -3729,9 +3804,15 @@ void multiComponentSaTestContingencyBothComponentsAndOperatorStrategy() { List actions = List.of(new TerminalsConnectionAction("open_l13", "l13", true), new LoadActionBuilder().withId("dc2").withLoadId("dc2").withRelativeValue(false).withActivePowerValue(1).build()); - List operatorStrategies = List.of(new OperatorStrategy("strategy1", ContingencyContext.specificContingency("compositeContingency"), new TrueCondition(), List.of("open_l13", "dc2"))); + List operatorStrategies = contingencies.stream() + .map(c -> new OperatorStrategy("strategy_" + c.getId(), ContingencyContext.specificContingency(c.getId()), new TrueCondition(), List.of("open_l13", "dc2"))) + .toList(); - SecurityAnalysisResult results = runSecurityAnalysis(network, contingencies, monitors, securityAnalysisParameters, operatorStrategies, actions, ReportNode.NO_OP); + ReportNode reportNode = ReportNode.newRootReportNode() + .withMessageTemplate("TEST", "Test Report Node") + .build(); + + SecurityAnalysisResult results = runSecurityAnalysis(network, contingencies, monitors, securityAnalysisParameters, operatorStrategies, actions, reportNode); NetworkResult preContingencyResults = results.getPreContingencyResult().getNetworkResult(); // Result for base case is available for all components @@ -3741,7 +3822,11 @@ void multiComponentSaTestContingencyBothComponentsAndOperatorStrategy() { assertEquals(0.493, preContingencyResults.getBranchResult("lc12").getP1(), LoadFlowAssert.DELTA_POWER); assertEquals(0.493, preContingencyResults.getBranchResult("lc12Bis").getP1(), LoadFlowAssert.DELTA_POWER); - NetworkResult postContingencyResults = results.getPostContingencyResults().get(0).getNetworkResult(); + NetworkResult postContingencyResults = results.getPostContingencyResults().stream() + .filter(r -> r.getContingency().getId().equals("compositeContingency")) + .findAny() + .map(AbstractContingencyResult::getNetworkResult) + .orElseThrow(); // Because contingency impact both networks, each simulation should output results for its components // Results should then be merged in a single post contingency result @@ -3759,6 +3844,12 @@ void multiComponentSaTestContingencyBothComponentsAndOperatorStrategy() { assertEquals(1.311, operatorStrategyResult.getBranchResult("l23").getP1(), LoadFlowAssert.DELTA_POWER); assertEquals(0.499, operatorStrategyResult.getBranchResult("lc12").getP1(), LoadFlowAssert.DELTA_POWER); assertEquals(0.499, operatorStrategyResult.getBranchResult("lc12Bis").getP1(), LoadFlowAssert.DELTA_POWER); + + StringWriter sw = new StringWriter(); + reportNode.print(sw); + // The purpose of this test is to check that the report have the same size with one or two threadd + // The content should be the same, but not the order... + assertEquals(14292, sw.toString().length()); } /** From c35013fbeb4367a9c28ce374c9af6b1da43c3aa1 Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Thu, 9 Jan 2025 11:31:55 +0100 Subject: [PATCH 02/10] remove offending empty lines Signed-off-by: Didier Vidal --- .../com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java | 1 - .../com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java index c6f7616533..c335ab309c 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java @@ -667,7 +667,6 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List new Contingency(id, new BranchContingency(id))) .toList(); - ReportNode reportNodeOneThread = ReportNode.newRootReportNode() .withMessageTemplate("MT_SA_TEST", "MT SA Report Node") .build(); @@ -3704,7 +3703,6 @@ void multiComponentSaTest(int threadCount) throws IOException { contingencies.add(new Contingency("dummyLoad_" + i, new LoadContingency("dummyLoad"))); } - // Monitor branch in both components List monitors = List.of( new StateMonitor(ContingencyContext.all(), Set.of("l14", "l12", "l23", "lc12", "lc12Bis"), emptySet(), emptySet()) From 01e021d0e3af3dc8e63b0ea5b28eeab979178307 Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Thu, 9 Jan 2025 11:42:14 +0100 Subject: [PATCH 03/10] Support Windows build - it changes report comparison Signed-off-by: Didier Vidal --- .../openloadflow/sa/OpenSecurityAnalysisTest.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index 1a4285bf74..2b3f30256c 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -3746,9 +3746,12 @@ void multiComponentSaTest(int threadCount) throws IOException { StringWriter sw = new StringWriter(); testReport.print(sw); + // Remove Windows EOL + String reportString = sw.toString().replaceAll("\\r$", ""); + // The purpose of this test is to check that the report have the same size with one or two threadd // The content should be the same, but not the order... - assertEquals(7278, sw.toString().length()); + assertEquals(7278, reportString.length()); // Check also that the preCont report is before the postContResults in the second CC String expected = " + Network CC1 SC1\n" + @@ -3762,7 +3765,7 @@ void multiComponentSaTest(int threadCount) throws IOException { " Outer loop VoltageMonitoring\n" + " Outer loop ReactiveLimits\n" + " AC load flow completed successfully (solverStatus=CONVERGED, outerloopStatus=STABLE)"; - assertTrue(sw.toString().contains(expected)); + assertTrue(reportString.contains(expected)); } @ParameterizedTest @@ -3845,9 +3848,11 @@ void multiComponentSaTestContingencyBothComponentsAndOperatorStrategy(int thread StringWriter sw = new StringWriter(); reportNode.print(sw); + // Remove Windows EOL + String reportString = sw.toString().replaceAll("\\r$", ""); // The purpose of this test is to check that the report have the same size with one or two threadd // The content should be the same, but not the order... - assertEquals(14292, sw.toString().length()); + assertEquals(14292, reportString.length()); } /** From fb6549c06ac29c6ac7598785cb28fb5d1d2f5179 Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Thu, 9 Jan 2025 13:05:45 +0100 Subject: [PATCH 04/10] instrument for windows debug Signed-off-by: Didier Vidal --- .../powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index 2b3f30256c..4cd820cb87 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -3749,6 +3749,11 @@ void multiComponentSaTest(int threadCount) throws IOException { // Remove Windows EOL String reportString = sw.toString().replaceAll("\\r$", ""); + // TODO remove this debug trace + System.out.println("threadCount: " + threadCount); + System.out.println(reportString); + System.out.println(reportString.length()); + // The purpose of this test is to check that the report have the same size with one or two threadd // The content should be the same, but not the order... assertEquals(7278, reportString.length()); From 7ac62fff04ee4a88d57561cffaf8b60d276c8995 Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Thu, 9 Jan 2025 13:28:38 +0100 Subject: [PATCH 05/10] fix windows string correction Signed-off-by: Didier Vidal --- .../com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index 4cd820cb87..98f97bd5c8 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -3747,7 +3747,7 @@ void multiComponentSaTest(int threadCount) throws IOException { StringWriter sw = new StringWriter(); testReport.print(sw); // Remove Windows EOL - String reportString = sw.toString().replaceAll("\\r$", ""); + String reportString = sw.toString().replaceAll("\\r\\n", "\\n"); // TODO remove this debug trace System.out.println("threadCount: " + threadCount); From bb3fead43d4cb7e0d89548d23312cb64c6021dcb Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Thu, 9 Jan 2025 13:44:14 +0100 Subject: [PATCH 06/10] another attempt to fix tests on windows. And use alternate synch mechanism to address Sonar warning Signed-off-by: Didier Vidal --- .../openloadflow/sa/AbstractSecurityAnalysis.java | 2 +- .../openloadflow/sa/WoodburyDcSecurityAnalysis.java | 2 +- .../java/com/powsybl/openloadflow/util/Reports.java | 12 +++++------- .../openloadflow/sa/OpenSecurityAnalysisTest.java | 5 +++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java index c335ab309c..08e979894b 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java @@ -697,7 +697,7 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List { // only process contingencies that impact the network ReportNode postContSimReportNode = - Reports.createPostContingencySimulation(ccReportNode, lfContingency.getId()); + Reports.createPostContingencySimulationMTSafe(ccReportNode, lfContingency.getId()); lfNetwork.setReportNode(postContSimReportNode); lfContingency.apply(loadFlowParameters.getBalanceType()); diff --git a/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java index d60c6e78d0..fc5d00fde7 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java @@ -323,7 +323,7 @@ private void addPostContingencyAndOperatorStrategyResults(DcLoadFlowContext cont LfNetwork lfNetwork = context.getNetwork(); // process results only if contingency impacts the network contingency.toLfContingency(lfNetwork, false).ifPresent(lfContingency -> { - ReportNode postContSimReportNode = Reports.createPostContingencySimulation(saReportNode, contingency.getContingency().getId()); + ReportNode postContSimReportNode = Reports.createPostContingencySimulationMTSafe(saReportNode, contingency.getContingency().getId()); lfNetwork.setReportNode(postContSimReportNode); // process post contingency result with supplier giving post contingency states diff --git a/src/main/java/com/powsybl/openloadflow/util/Reports.java b/src/main/java/com/powsybl/openloadflow/util/Reports.java index 69398d7ac2..2e2e7f8a9e 100644 --- a/src/main/java/com/powsybl/openloadflow/util/Reports.java +++ b/src/main/java/com/powsybl/openloadflow/util/Reports.java @@ -469,14 +469,12 @@ public static ReportNode createPreContingencySimulation(ReportNode reportNode) { .add(); } - public static ReportNode createPostContingencySimulation(ReportNode reportNode, String contingencyId) { + public static synchronized ReportNode createPostContingencySimulationMTSafe(ReportNode reportNode, String contingencyId) { // This cn be called in an MT context on the same saNode - synchronized (reportNode) { - return reportNode.newReportNode() - .withMessageTemplate("postContingencySimulation", "Post-contingency simulation '${contingencyId}'") - .withUntypedValue("contingencyId", contingencyId) - .add(); - } + return reportNode.newReportNode() + .withMessageTemplate("postContingencySimulation", "Post-contingency simulation '${contingencyId}'") + .withUntypedValue("contingencyId", contingencyId) + .add(); } public static ReportNode createOperatorStrategySimulation(ReportNode reportNode, String operatorStrategyId) { diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index 98f97bd5c8..bdbe6ee85f 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -3770,7 +3770,8 @@ void multiComponentSaTest(int threadCount) throws IOException { " Outer loop VoltageMonitoring\n" + " Outer loop ReactiveLimits\n" + " AC load flow completed successfully (solverStatus=CONVERGED, outerloopStatus=STABLE)"; - assertTrue(reportString.contains(expected)); + // Remove Windows EOL for comparison + assertTrue(reportString.contains(expected.replaceAll("\\r\\n", "\\n"))); } @ParameterizedTest @@ -3854,7 +3855,7 @@ void multiComponentSaTestContingencyBothComponentsAndOperatorStrategy(int thread StringWriter sw = new StringWriter(); reportNode.print(sw); // Remove Windows EOL - String reportString = sw.toString().replaceAll("\\r$", ""); + String reportString = sw.toString().replaceAll("\\r\\n", "\\n"); // The purpose of this test is to check that the report have the same size with one or two threadd // The content should be the same, but not the order... assertEquals(14292, reportString.length()); From 6fa67753c2e247c11f1e7acdf48efefef0071155 Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Thu, 9 Jan 2025 13:55:39 +0100 Subject: [PATCH 07/10] another attempt Signed-off-by: Didier Vidal --- .../com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index bdbe6ee85f..d93cab6e98 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -3747,7 +3747,7 @@ void multiComponentSaTest(int threadCount) throws IOException { StringWriter sw = new StringWriter(); testReport.print(sw); // Remove Windows EOL - String reportString = sw.toString().replaceAll("\\r\\n", "\\n"); + String reportString = sw.toString().replaceAll("\\r\\n", "\n"); // TODO remove this debug trace System.out.println("threadCount: " + threadCount); @@ -3771,7 +3771,7 @@ void multiComponentSaTest(int threadCount) throws IOException { " Outer loop ReactiveLimits\n" + " AC load flow completed successfully (solverStatus=CONVERGED, outerloopStatus=STABLE)"; // Remove Windows EOL for comparison - assertTrue(reportString.contains(expected.replaceAll("\\r\\n", "\\n"))); + assertTrue(reportString.contains(expected)); } @ParameterizedTest From 34c6a15611e84fe674ccf4f093f05e332a9feb8b Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Thu, 9 Jan 2025 13:59:19 +0100 Subject: [PATCH 08/10] reuse from TestUtil... Signed-off-by: Didier Vidal --- .../powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index d93cab6e98..db86192752 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -13,6 +13,7 @@ import com.powsybl.action.TerminalsConnectionAction; import com.powsybl.commons.PowsyblException; import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.test.TestUtil; import com.powsybl.contingency.*; import com.powsybl.ieeecdf.converter.IeeeCdfNetworkFactory; import com.powsybl.iidm.criteria.AtLeastOneNominalVoltageCriterion; @@ -3747,7 +3748,7 @@ void multiComponentSaTest(int threadCount) throws IOException { StringWriter sw = new StringWriter(); testReport.print(sw); // Remove Windows EOL - String reportString = sw.toString().replaceAll("\\r\\n", "\n"); + String reportString = TestUtil.normalizeLineSeparator(sw.toString()); // TODO remove this debug trace System.out.println("threadCount: " + threadCount); @@ -3855,7 +3856,7 @@ void multiComponentSaTestContingencyBothComponentsAndOperatorStrategy(int thread StringWriter sw = new StringWriter(); reportNode.print(sw); // Remove Windows EOL - String reportString = sw.toString().replaceAll("\\r\\n", "\\n"); + String reportString = TestUtil.normalizeLineSeparator(sw.toString()); // The purpose of this test is to check that the report have the same size with one or two threadd // The content should be the same, but not the order... assertEquals(14292, reportString.length()); From 90299a90c1fcd57dd8c71371ba035350774d2bc0 Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Thu, 9 Jan 2025 14:04:19 +0100 Subject: [PATCH 09/10] Eventually... Now removing test debug traces Signed-off-by: Didier Vidal --- .../powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java index db86192752..3e0ba146c9 100644 --- a/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java +++ b/src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisTest.java @@ -3750,11 +3750,6 @@ void multiComponentSaTest(int threadCount) throws IOException { // Remove Windows EOL String reportString = TestUtil.normalizeLineSeparator(sw.toString()); - // TODO remove this debug trace - System.out.println("threadCount: " + threadCount); - System.out.println(reportString); - System.out.println(reportString.length()); - // The purpose of this test is to check that the report have the same size with one or two threadd // The content should be the same, but not the order... assertEquals(7278, reportString.length()); From 85863a4126d811ca2536417f9ff7d362a5095aa1 Mon Sep 17 00:00:00 2001 From: Didier Vidal Date: Fri, 10 Jan 2025 16:44:29 +0100 Subject: [PATCH 10/10] Fix criteria for adding precontingency report --- .../sa/AbstractSecurityAnalysis.java | 34 ++++++++++--------- .../sa/WoodburyDcSecurityAnalysis.java | 17 +++++----- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java index 08e979894b..4804b552e7 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/AbstractSecurityAnalysis.java @@ -49,6 +49,7 @@ import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; @@ -76,6 +77,8 @@ public abstract class AbstractSecurityAnalysis & Quantity, E e private static final String NOT_FOUND = "' not found in the network"; + protected record ReportNodeSync(ReportNode reportNode, AtomicInteger runCount) { } + protected AbstractSecurityAnalysis(Network network, MatrixFactory matrixFactory, GraphConnectivityFactory connectivityFactory, List stateMonitors, ReportNode reportNode) { this.network = Objects.requireNonNull(network); @@ -158,7 +161,8 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame // create networks including all necessary switches try (LfNetworkList lfNetworks = Networks.load(network, parameters.getNetworkParameters(), topoConfig, saReportNode)) { finalResult = runSimulationsOnAllComponents(lfNetworks, propagatedContingencies, parameters, securityAnalysisParameters, operatorStrategies, actions, limitReductions, lfParameters, - lfNetworks.getList().stream().collect(Collectors.toMap(n -> new ImmutablePair<>(n.getNumCC(), n.getNumSC()), n -> n.getReportNode()))); + lfNetworks.getList().stream() + .collect(Collectors.toMap(n -> new ImmutablePair<>(n.getNumCC(), n.getNumSC()), n -> new ReportNodeSync(n.getReportNode(), new AtomicInteger(0))))); } } else { @@ -174,7 +178,7 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame try { Lock networkLock = new ReentrantLock(); List> futures = new ArrayList<>(); - Map, ReportNode> ccNetworkReportNodes = new HashMap<>(); + Map, ReportNodeSync> ccNetworkReportNodes = new HashMap<>(); for (int i = 0; i < contingenciesPartitions.size(); i++) { final int partitionNum = i; var contingenciesPartition = contingenciesPartitions.get(i); @@ -211,7 +215,7 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame if (first) { // List CC reports to attach post contingency result for (LfNetwork n : lfNetworks.getList()) { - ccNetworkReportNodes.put(new ImmutablePair<>(n.getNumCC(), n.getNumSC()), n.getReportNode()); + ccNetworkReportNodes.put(new ImmutablePair<>(n.getNumCC(), n.getNumSC()), new ReportNodeSync(n.getReportNode(), new AtomicInteger(0))); } } } finally { @@ -265,7 +269,7 @@ SecurityAnalysisReport runSync(SecurityAnalysisParameters securityAnalysisParame SecurityAnalysisResult runSimulationsOnAllComponents(LfNetworkList networks, List propagatedContingencies, P parameters, SecurityAnalysisParameters securityAnalysisParameters, List operatorStrategies, List actions, List limitReductions, LoadFlowParameters lfParameters, - Map, ReportNode> postCtgReportNodeMap) { + Map, ReportNodeSync> postCtgReportNodeMap) { List networkToSimulate = new ArrayList<>(getNetworksToSimulate(networks, lfParameters.getConnectedComponentMode())); @@ -640,12 +644,11 @@ protected void afterPreContingencySimulation(P acParameters) { protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List propagatedContingencies, P acParameters, SecurityAnalysisParameters securityAnalysisParameters, List operatorStrategies, - List actions, List limitReductions, Map, ReportNode> postContReportMap) { + List actions, List limitReductions, Map, ReportNodeSync> postContReportMap) { Map actionsById = indexActionsById(actions); Set neededActions = new HashSet<>(actionsById.size()); - boolean tolerateMissingContingencies = false; OpenSecurityAnalysisParameters securityAnalysisParametersExt = securityAnalysisParameters.getExtension(OpenSecurityAnalysisParameters.class); - tolerateMissingContingencies = securityAnalysisParametersExt != null && securityAnalysisParametersExt.getThreadCount() > 1; + boolean tolerateMissingContingencies = securityAnalysisParametersExt != null && securityAnalysisParametersExt.getThreadCount() > 1; Map> operatorStrategiesByContingencyId = indexOperatorStrategiesByContingencyId(propagatedContingencies, operatorStrategies, actionsById, neededActions, tolerateMissingContingencies); @@ -658,14 +661,13 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List(lfNetwork.getNumCC(), lfNetwork.getNumSC())); - synchronized (ccReportNode) { - // In an MT run, the report is a NO_OP if not the first thread to run on this CC, to avoid duplicate - // functional reports of lf network loading and precontingency run - preContReportNode = (ccReportNode.getChildren().size() == 1) ? ccReportNode : ReportNode.NO_OP; - ReportNode preContSimReportNode = Reports.createPreContingencySimulation(preContReportNode); - lfNetwork.setReportNode(preContSimReportNode); - } + final ReportNodeSync ccReportNodeSync = postContReportMap.get(new ImmutablePair<>(lfNetwork.getNumCC(), lfNetwork.getNumSC())); + + // In an MT run, the report is a NO_OP if not the first thread to run on this CC, to avoid duplicate + // functional reports of lf network loading and precontingency run + preContReportNode = (ccReportNodeSync.runCount.incrementAndGet() == 1) ? ccReportNodeSync.reportNode : ReportNode.NO_OP; + ReportNode preContSimReportNode = Reports.createPreContingencySimulation(preContReportNode); + lfNetwork.setReportNode(preContSimReportNode); // run pre-contingency simulation R preContingencyLoadFlowResult = createLoadFlowEngine(context) @@ -697,7 +699,7 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List { // only process contingencies that impact the network ReportNode postContSimReportNode = - Reports.createPostContingencySimulationMTSafe(ccReportNode, lfContingency.getId()); + Reports.createPostContingencySimulationMTSafe(ccReportNodeSync.reportNode, lfContingency.getId()); lfNetwork.setReportNode(postContSimReportNode); lfContingency.apply(loadFlowParameters.getBalanceType()); diff --git a/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java index fc5d00fde7..e913b7abf0 100644 --- a/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java @@ -370,14 +370,13 @@ private static Map createTapPositionCh @Override protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List propagatedContingencies, DcLoadFlowParameters dcParameters, SecurityAnalysisParameters securityAnalysisParameters, List operatorStrategies, - List actions, List limitReductions, Map, ReportNode> postContReportMap) { + List actions, List limitReductions, Map, ReportNodeSync> postContReportMap) { // Verify only PST actions are given filterActions(actions); Map actionsById = indexActionsById(actions); Set neededActions = new HashSet<>(actionsById.size()); - boolean tolerateMissingContingencies = false; OpenSecurityAnalysisParameters securityAnalysisParametersExt = securityAnalysisParameters.getExtension(OpenSecurityAnalysisParameters.class); - tolerateMissingContingencies = securityAnalysisParametersExt != null && securityAnalysisParametersExt.getThreadCount() > 1; + boolean tolerateMissingContingencies = securityAnalysisParametersExt != null && securityAnalysisParametersExt.getThreadCount() > 1; Map> operatorStrategiesByContingencyId = indexOperatorStrategiesByContingencyId(propagatedContingencies, operatorStrategies, actionsById, neededActions, tolerateMissingContingencies); @@ -442,11 +441,11 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List { // supplier to compute post contingency states Supplier toPostContingencyStates = () -> calculatePostContingencyStates(context, connectivityBreakAnalysisResults.contingenciesStates(), workingContingencyStates, - nonBreakingConnectivityContingency, connectivityBreakAnalysisResults.contingencyElementByBranch(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), postContReportMap.get(networkKey)); + nonBreakingConnectivityContingency, connectivityBreakAnalysisResults.contingencyElementByBranch(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), postContReportMap.get(networkKey).reportNode()); // function to compute post contingency and post operator strategy states Function, double[]> toPostContingencyAndOperatorStrategyStates = operatorStrategyLfActions -> calculatePostContingencyAndOperatorStrategyStates(context, connectivityBreakAnalysisResults.contingenciesStates(), workingContingencyStates, nonBreakingConnectivityContingency, connectivityBreakAnalysisResults.contingencyElementByBranch(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), - operatorStrategyLfActions, tapPositionChangeElementsByBranchId, actionsStates, postContReportMap.get(networkKey)); + operatorStrategyLfActions, tapPositionChangeElementsByBranchId, actionsStates, postContReportMap.get(networkKey).reportNode()); // runnable to restore pre contingency states, after modifications applied to the lfNetwork Runnable restorePreContingencyStates = () -> { // update workingContingencyStates as it may have been updated by post contingency states calculation @@ -456,7 +455,7 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List toPostContingencyStates = () -> calculatePostContingencyStatesForAContingencyBreakingConnectivity(connectivityAnalysisResult, context, - connectivityBreakAnalysisResults.contingencyElementByBranch(), workingContingencyStates, connectivityBreakAnalysisResults.contingenciesStates(), postContReportMap.get(networkKey)); + connectivityBreakAnalysisResults.contingencyElementByBranch(), workingContingencyStates, connectivityBreakAnalysisResults.contingenciesStates(), postContReportMap.get(networkKey).reportNode()); // function to compute post contingency and post operator strategy states Function, double[]> toPostContingencyAndOperatorStrategyStates = operatorStrategyLfActions -> calculatePostContingencyAndOperatorStrategyStatesForAContingencyBreakingConnectivity(connectivityAnalysisResult, context, connectivityBreakAnalysisResults.contingencyElementByBranch(), workingContingencyStates, connectivityBreakAnalysisResults.contingenciesStates(), - operatorStrategyLfActions, tapPositionChangeElementsByBranchId, actionsStates, postContReportMap.get(networkKey)); + operatorStrategyLfActions, tapPositionChangeElementsByBranchId, actionsStates, postContReportMap.get(networkKey).reportNode()); // runnable to restore pre contingency states, after modifications applied to the lfNetwork // no need to update workingContingencyStates as an override of flow states will be computed Runnable restorePreContingencyStates = networkState::restore; addPostContingencyAndOperatorStrategyResults(context, breakingConnectivityContingency, operatorStrategiesByContingencyId, lfActionById, toPostContingencyStates, toPostContingencyAndOperatorStrategyStates, restorePreContingencyStates, preContingencyLimitViolationManager, preContingencyNetworkResult, createResultExtension, - securityAnalysisParameters.getIncreasedViolationsParameters(), limitReductions, postContingencyResults, operatorStrategyResults, postContReportMap.get(networkKey)); + securityAnalysisParameters.getIncreasedViolationsParameters(), limitReductions, postContingencyResults, operatorStrategyResults, postContReportMap.get(networkKey).reportNode()); }); return new SecurityAnalysisResult(