Skip to content

Commit 475f4c2

Browse files
committed
Handle three windings transformer terminal connection action properly in woodbury DC security analysis
Signed-off-by: Bertrand Rix <bertrand.rix@artelys.com>
1 parent 3b9e23a commit 475f4c2

File tree

3 files changed

+79
-31
lines changed

3 files changed

+79
-31
lines changed

src/main/java/com/powsybl/openloadflow/dc/fastdc/ConnectivityBreakAnalysis.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ private static void detectPotentialConnectivityBreak(LfNetwork lfNetwork, DenseM
151151
*/
152152
private static boolean isConnectivityPotentiallyModifiedByContingencyAndOperatorStrategy(LfNetwork lfNetwork, States states, PropagatedContingency contingency,
153153
Map<String, ComputedContingencyElement> contingencyElementByBranch, List<LfAction> operatorStrategyLfActions,
154-
Map<LfAction, ComputedElement> actionElementByBranch, EquationSystem<DcVariableType, DcEquationType> equationSystem) {
154+
Map<LfAction, List<ComputedElement>> actionElementByBranch, EquationSystem<DcVariableType, DcEquationType> equationSystem) {
155155
List<ComputedContingencyElement> contingencyElements = contingency.getBranchIdsToOpen().keySet().stream()
156156
.map(contingencyElementByBranch::get)
157157
.collect(Collectors.toList());
@@ -161,8 +161,9 @@ private static boolean isConnectivityPotentiallyModifiedByContingencyAndOperator
161161
// it is not necessary to consider them to ensure that there is no loss of connectivity.
162162
List<ComputedElement> actionElements = operatorStrategyLfActions.stream()
163163
.map(actionElementByBranch::get)
164+
.flatMap(Collection::stream)
164165
.filter(actionElement -> actionElement instanceof ComputedSwitchBranchElement computedSwitchBranchElement && !computedSwitchBranchElement.isEnabled())
165-
.collect(Collectors.toList());
166+
.toList();
166167
return isGroupOfElementsBreakingConnectivity(lfNetwork, states.contingencyStates(), contingencyElements, states.actionStates(), actionElements, equationSystem);
167168
}
168169

@@ -221,13 +222,13 @@ private static List<ConnectivityAnalysisResult> computeConnectivityData(LfNetwor
221222
*/
222223
private static ConnectivityAnalysisResult computeConnectivityAnalysisResult(LfNetwork lfNetwork,
223224
PropagatedContingency contingency, Map<String, ComputedContingencyElement> contingencyElementByBranch,
224-
List<LfAction> lfActions, Map<LfAction, ComputedElement> actionElementByBranch) {
225+
List<LfAction> lfActions, Map<LfAction, List<ComputedElement>> actionElementByBranch) {
225226
GraphConnectivity<LfBus, LfBranch> connectivity = lfNetwork.getConnectivity();
226227

227228
// concatenate all computed elements, to apply them on the connectivity
228229
List<ComputedElement> modifyingConnectivityCandidates = Stream.concat(
229230
contingency.getBranchIdsToOpen().keySet().stream().map(contingencyElementByBranch::get),
230-
lfActions.stream().map(actionElementByBranch::get)
231+
lfActions.stream().map(actionElementByBranch::get).flatMap(Collection::stream)
231232
).sorted(Comparator.comparing(element -> element.getLfBranch().getId())).toList();
232233

233234
// we confirm the breaking of connectivity by network connectivity
@@ -354,7 +355,7 @@ public static ConnectivityBreakAnalysisResults run(DcLoadFlowContext loadFlowCon
354355
*/
355356
public static ConnectivityAnalysisResult processPostContingencyAndPostOperatorStrategyConnectivityAnalysisResult(DcLoadFlowContext loadFlowContext, ConnectivityAnalysisResult postContingencyConnectivityAnalysisResult,
356357
Map<String, ComputedContingencyElement> contingencyElementByBranch, DenseMatrix contingenciesStates,
357-
List<LfAction> lfActions, Map<LfAction, ComputedElement> actionElementsIndexByLfAction, DenseMatrix actionsStates) {
358+
List<LfAction> lfActions, Map<LfAction, List<ComputedElement>> actionElementsIndexByLfAction, DenseMatrix actionsStates) {
358359
// if there is no topological action, no need to process anything as the connectivity has not changed from post contingency result
359360
boolean hasAnyTopologicalAction = lfActions.stream().anyMatch(lfAction -> lfAction instanceof AbstractLfBranchAction<?>);
360361
if (!hasAnyTopologicalAction) {

src/main/java/com/powsybl/openloadflow/sa/WoodburyDcSecurityAnalysis.java

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ private double[] calculatePostContingencyStates(DcLoadFlowContext loadFlowContex
126126
*/
127127
private double[] calculatePostContingencyAndOperatorStrategyStates(DcLoadFlowContext loadFlowContext, DenseMatrix contingenciesStates, double[] flowStates,
128128
ConnectivityAnalysisResult connectivityAnalysisResult, Map<String, ComputedContingencyElement> contingencyElementByBranch,
129-
Map<LfAction, ComputedElement> actionElementByLfAction, DenseMatrix actionsStates, ReportNode reportNode) {
129+
Map<LfAction, List<ComputedElement>> actionElementByLfAction, DenseMatrix actionsStates, ReportNode reportNode) {
130130
PropagatedContingency contingency = connectivityAnalysisResult.getPropagatedContingency();
131131
Set<LfBus> disabledBuses = connectivityAnalysisResult.getDisabledBuses();
132132
Set<LfBranch> partialDisabledBranches = connectivityAnalysisResult.getPartialDisabledBranches();
@@ -145,6 +145,7 @@ private double[] calculatePostContingencyAndOperatorStrategyStates(DcLoadFlowCon
145145
.collect(Collectors.toList());
146146
List<ComputedElement> actionElements = operatorStrategyLfActions.stream()
147147
.map(actionElementByLfAction::get)
148+
.flatMap(Collection::stream)
148149
.filter(actionElement -> !elementsToReconnect.contains(actionElement.getLfBranch().getId()))
149150
.collect(Collectors.toList());
150151

@@ -365,29 +366,31 @@ private void addPostContingencyAndOperatorStrategyResults(WoodburyContext woodbu
365366
});
366367
}
367368

368-
private static Map<LfAction, ComputedElement> createActionElementsIndexByLfAction(Map<String, LfAction> lfActionById, EquationSystem<DcVariableType, DcEquationType> equationSystem) {
369-
Map<LfAction, ComputedElement> computedElements = lfActionById.values().stream()
370-
.map(lfAction -> {
371-
ComputedElement element;
372-
if (lfAction instanceof AbstractLfTapChangerAction<?> abstractLfTapChangerAction) {
373-
element = new ComputedTapPositionChangeElement(abstractLfTapChangerAction.getChange(), equationSystem);
374-
} else if (lfAction instanceof AbstractLfBranchAction<?> abstractLfBranchAction && !abstractLfBranchAction.getEnabledBranches().isEmpty()) {
375-
element = new ComputedSwitchBranchElement(abstractLfBranchAction.getEnabledBranches().getFirst(), true, equationSystem);
376-
} else if (lfAction instanceof AbstractLfBranchAction<?> abstractLfBranchAction && !abstractLfBranchAction.getDisabledBranches().isEmpty()) {
377-
element = new ComputedSwitchBranchElement(abstractLfBranchAction.getDisabledBranches().getFirst(), false, equationSystem);
378-
} else {
379-
throw new IllegalStateException("Only tap position change and branch enabling/disabling are supported in WoodburyDcSecurityAnalysis");
380-
}
381-
return Map.entry(lfAction, element);
382-
})
383-
.filter(e -> e.getValue().getLfBranchEquation() != null)
384-
.collect(Collectors.toMap(
385-
Map.Entry::getKey,
386-
Map.Entry::getValue,
387-
(existing, replacement) -> existing,
388-
LinkedHashMap::new
389-
));
390-
ComputedElement.setComputedElementIndexes(computedElements.values());
369+
private static Map<LfAction, List<ComputedElement>> createActionElementsIndexByLfAction(Map<String, LfAction> lfActionById, EquationSystem<DcVariableType, DcEquationType> equationSystem) {
370+
Map<LfAction, List<ComputedElement>> computedElements = new HashMap<>();
371+
lfActionById.values().forEach(lfAction -> {
372+
List<ComputedElement> elements = new ArrayList<>();
373+
switch (lfAction) {
374+
case AbstractLfTapChangerAction<?> abstractLfTapChangerAction -> {
375+
elements.add(new ComputedTapPositionChangeElement(abstractLfTapChangerAction.getChange(), equationSystem));
376+
}
377+
case
378+
AbstractLfBranchAction<?> abstractLfBranchAction when !abstractLfBranchAction.getEnabledBranches().isEmpty() -> {
379+
elements.addAll(abstractLfBranchAction.getEnabledBranches().stream().map(
380+
e -> new ComputedSwitchBranchElement(e, true, equationSystem)).toList());
381+
}
382+
case
383+
AbstractLfBranchAction<?> abstractLfBranchAction when !abstractLfBranchAction.getDisabledBranches().isEmpty() -> {
384+
elements.addAll(abstractLfBranchAction.getDisabledBranches().stream().map(
385+
e -> new ComputedSwitchBranchElement(e, false, equationSystem)).toList());
386+
}
387+
default -> {
388+
throw new IllegalStateException("Only tap position change and branch enabling/disabling are supported in WoodburyDcSecurityAnalysis");
389+
}
390+
}
391+
computedElements.put(lfAction, elements.stream().filter(e -> e.getLfBranchEquation() != null).toList());
392+
});
393+
ComputedElement.setComputedElementIndexes(computedElements.values().stream().flatMap(Collection::stream).toList());
391394
return computedElements;
392395
}
393396

@@ -449,11 +452,11 @@ protected SecurityAnalysisResult runSimulations(LfNetwork lfNetwork, List<Propag
449452
ConnectivityBreakAnalysis.ConnectivityBreakAnalysisResults connectivityBreakAnalysisResults = ConnectivityBreakAnalysis.run(context, propagatedContingencies);
450453

451454
// the map is indexed by lf actions as different kind of actions can be given on the same branch
452-
Map<LfAction, ComputedElement> actionElementsIndexByLfAction = createActionElementsIndexByLfAction(lfActionById, context.getEquationSystem());
455+
Map<LfAction, List<ComputedElement>> actionElementsIndexByLfAction = createActionElementsIndexByLfAction(lfActionById, context.getEquationSystem());
453456

454457
// compute states with +1 -1 to model the actions in Woodbury engine
455458
// note that the number of columns in the matrix depends on the number of distinct branches affected by the action elements
456-
DenseMatrix actionsStates = ComputedElement.calculateElementsStates(context, actionElementsIndexByLfAction.values());
459+
DenseMatrix actionsStates = ComputedElement.calculateElementsStates(context, actionElementsIndexByLfAction.values().stream().flatMap(Collection::stream).toList());
457460

458461
// save base state for later restoration after each contingency/action
459462
NetworkState networkState = NetworkState.save(lfNetwork);

src/test/java/com/powsybl/openloadflow/sa/OpenSecurityAnalysisWithActionsTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,50 @@ void testTerminalsConnectionActionWith3WindingsTransformer() {
18691869
assertEquals(11.226, result.getOperatorStrategyResults().getFirst().getNetworkResult().getBranchResult("LINE_12").getP1(), LoadFlowAssert.DELTA_POWER);
18701870
}
18711871

1872+
@Test
1873+
void testTerminalsConnectionActionWith3WindingsTransformerAc() {
1874+
LoadFlowParameters parameters = new LoadFlowParameters();
1875+
parameters.setDistributedSlack(true);
1876+
parameters.setComponentMode(LoadFlowParameters.ComponentMode.ALL_CONNECTED);
1877+
SecurityAnalysisParameters securityAnalysisParameters = new SecurityAnalysisParameters();
1878+
securityAnalysisParameters.setLoadFlowParameters(parameters);
1879+
testTerminalsConnectionActionWith3WindingsTransformer(securityAnalysisParameters);
1880+
}
1881+
1882+
@Test
1883+
void testTerminalsConnectionActionWith3WindingsTransformerFastDc() {
1884+
LoadFlowParameters parameters = new LoadFlowParameters();
1885+
parameters.setDistributedSlack(true);
1886+
parameters.setDc(true);
1887+
parameters.setComponentMode(LoadFlowParameters.ComponentMode.ALL_CONNECTED);
1888+
SecurityAnalysisParameters securityAnalysisParameters = new SecurityAnalysisParameters();
1889+
securityAnalysisParameters.setLoadFlowParameters(parameters);
1890+
OpenSecurityAnalysisParameters ext = new OpenSecurityAnalysisParameters()
1891+
.setDcFastMode(true);
1892+
securityAnalysisParameters.addExtension(OpenSecurityAnalysisParameters.class, ext);
1893+
testTerminalsConnectionActionWith3WindingsTransformer(securityAnalysisParameters);
1894+
}
1895+
1896+
void testTerminalsConnectionActionWith3WindingsTransformer(SecurityAnalysisParameters parameters) {
1897+
Network network = VoltageControlNetworkFactory.createNetworkWithT3wt();
1898+
1899+
List<Contingency> contingencies = Stream.of("LOAD_4")
1900+
.map(id -> new Contingency(id, new LoadContingency(id)))
1901+
.toList();
1902+
1903+
List<Action> actions = List.of(new TerminalsConnectionAction("disconnect3WT", "T3wT", true));
1904+
List<OperatorStrategy> operatorStrategies = List.of(new OperatorStrategy("Strategy3WT", ContingencyContext.specificContingency("LOAD_4"), new TrueCondition(), List.of("disconnect3WT")));
1905+
1906+
List<StateMonitor> monitors = createAllBranchesMonitors(network);
1907+
SecurityAnalysisResult result = runSecurityAnalysis(network, contingencies, monitors, parameters,
1908+
operatorStrategies, actions, ReportNode.NO_OP);
1909+
1910+
assertTrue(result.getOperatorStrategyResults().stream().findAny().isPresent());
1911+
1912+
// Only flow related to G1 and LD2 should be present
1913+
assertEquals(11.2, result.getOperatorStrategyResults().getFirst().getNetworkResult().getBranchResult("LINE_12").getP1(), 1e-1);
1914+
}
1915+
18721916
@Test
18731917
void testOperatorStrategyNoMoreBusVoltageControlled() throws IOException {
18741918
Network network = EurostagFactory.fix(EurostagTutorialExample1Factory.create());

0 commit comments

Comments
 (0)