Skip to content

Commit 851aa13

Browse files
authored
Support three windings transformer in terminals connection action (#1338)
Signed-off-by: Bertrand Rix <bertrand.rix@artelys.com>
1 parent 5cf145d commit 851aa13

File tree

13 files changed

+136
-83
lines changed

13 files changed

+136
-83
lines changed

docs/security/inputs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ With Open Load Flow only the following remedial actions are currently implemente
88

99
- `LoadAction`
1010
- `SwitchAction`
11-
- `TerminalsConnectionAction`
11+
- `TerminalsConnectionAction` (Only supporting actions on branches and three windings transformers terminals)
1212
- `PhaseTapChangerTapPositionAction`
1313
- `RatioTapChangerTapPositionAction`
1414
- `ShuntCompensatorPositionAction`

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

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@
2828
import org.slf4j.Logger;
2929
import org.slf4j.LoggerFactory;
3030

31-
import java.util.Collection;
32-
import java.util.HashMap;
33-
import java.util.LinkedHashMap;
34-
import java.util.Map;
35-
import java.util.Objects;
31+
import java.util.*;
3632
import java.util.concurrent.atomic.AtomicInteger;
3733
import java.util.stream.Collectors;
3834
import java.util.stream.Stream;
@@ -133,36 +129,36 @@ static DenseMatrix calculateElementsStates(DcLoadFlowContext loadFlowContext, Co
133129
return elementsStates;
134130
}
135131

136-
static Map<LfAction, ComputedElement> createActionElementsIndexByLfAction(Map<String, LfAction> lfActionById, EquationSystem<DcVariableType, DcEquationType> equationSystem) {
137-
Map<LfAction, ComputedElement> computedElements = lfActionById.values().stream()
138-
.flatMap(lfAction -> {
139-
ComputedElement element = switch (lfAction.getType()) {
140-
case SwitchAction.NAME, TerminalsConnectionAction.NAME -> {
141-
AbstractLfBranchAction<?> lfBranchAction = (AbstractLfBranchAction<?>) lfAction;
142-
if (lfBranchAction.getEnabledBranch() != null) {
143-
yield ComputedSwitchBranchElement.create(lfBranchAction.getEnabledBranch(), true, equationSystem);
144-
} else if (lfBranchAction.getDisabledBranch() != null) {
145-
yield ComputedSwitchBranchElement.create(lfBranchAction.getDisabledBranch(), false, equationSystem);
146-
}
147-
yield null;
132+
static Map<LfAction, List<ComputedElement>> createActionElementsIndexByLfAction(Map<String, LfAction> lfActionById, EquationSystem<DcVariableType, DcEquationType> equationSystem) {
133+
Map<LfAction, List<ComputedElement>> computedElements = lfActionById.values().stream()
134+
.flatMap(lfAction -> {
135+
List<ComputedElement> elements = new ArrayList<>();
136+
switch (lfAction.getType()) {
137+
case SwitchAction.NAME, TerminalsConnectionAction.NAME -> {
138+
AbstractLfBranchAction<?> lfBranchAction = (AbstractLfBranchAction<?>) lfAction;
139+
if (!lfBranchAction.getEnabledBranches().isEmpty()) {
140+
elements.addAll(lfBranchAction.getEnabledBranches().stream().map(b -> ComputedSwitchBranchElement.create(b, true, equationSystem)).toList());
141+
} else if (!lfBranchAction.getDisabledBranches().isEmpty()) {
142+
elements.addAll(lfBranchAction.getDisabledBranches().stream().map(b -> ComputedSwitchBranchElement.create(b, false, equationSystem)).toList());
148143
}
149-
case PhaseTapChangerTapPositionAction.NAME ->
150-
new ComputedTapPositionChangeElement(((AbstractLfTapChangerAction<?>) lfAction).getChange(), equationSystem);
151-
default -> throw new IllegalStateException("Only tap position change and branch enabling/disabling are supported in WoodburyDcSecurityAnalysis");
152-
};
153-
if (element == null) {
154-
return Stream.empty();
155144
}
156-
return Stream.of(Map.entry(lfAction, element));
157-
})
158-
.filter(e -> e.getValue().getLfBranchEquation() != null)
159-
.collect(Collectors.toMap(
160-
Map.Entry::getKey,
161-
Map.Entry::getValue,
162-
(existing, replacement) -> existing,
163-
LinkedHashMap::new
164-
));
165-
ComputedElement.setComputedElementIndexes(computedElements.values());
145+
case PhaseTapChangerTapPositionAction.NAME ->
146+
elements.add(new ComputedTapPositionChangeElement(((AbstractLfTapChangerAction<?>) lfAction).getChange(), equationSystem));
147+
default -> throw new IllegalStateException("Only tap position change and branch enabling/disabling are supported in WoodburyDcSecurityAnalysis");
148+
}
149+
if (elements.isEmpty()) {
150+
return Stream.empty();
151+
}
152+
return Stream.of(Map.entry(lfAction, elements));
153+
})
154+
.filter(e -> e.getValue().stream().filter(b -> b.getLfBranchEquation() == null).findAny().isEmpty())
155+
.collect(Collectors.toMap(
156+
Map.Entry::getKey,
157+
Map.Entry::getValue,
158+
(existing, replacement) -> existing,
159+
LinkedHashMap::new
160+
));
161+
ComputedElement.setComputedElementIndexes(computedElements.values().stream().flatMap(Collection::stream).toList());
166162
return computedElements;
167163
}
168164

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ private static void detectPotentialConnectivityBreak(LfNetwork lfNetwork, DenseM
152152
*/
153153
private static boolean isConnectivityPotentiallyModifiedByContingencyAndOperatorStrategy(LfNetwork lfNetwork, States states, PropagatedContingency contingency,
154154
Map<String, ComputedContingencyElement> contingencyElementByBranch, List<LfAction> operatorStrategyLfActions,
155-
Map<LfAction, ComputedElement> actionElementByBranch, EquationSystem<DcVariableType, DcEquationType> equationSystem) {
155+
Map<LfAction, List<ComputedElement>> actionElementByBranch, EquationSystem<DcVariableType, DcEquationType> equationSystem) {
156156
List<ComputedContingencyElement> contingencyElements = contingency.getBranchIdsToOpen().keySet().stream()
157157
.map(contingencyElementByBranch::get)
158158
.collect(Collectors.toList());
@@ -162,8 +162,10 @@ private static boolean isConnectivityPotentiallyModifiedByContingencyAndOperator
162162
// it is not necessary to consider them to ensure that there is no loss of connectivity.
163163
List<ComputedElement> actionElements = operatorStrategyLfActions.stream()
164164
.map(actionElementByBranch::get)
165+
.filter(Objects::nonNull)
166+
.flatMap(Collection::stream)
165167
.filter(actionElement -> actionElement instanceof ComputedSwitchBranchElement computedSwitchBranchElement && !computedSwitchBranchElement.isEnabled())
166-
.collect(Collectors.toList());
168+
.toList();
167169
return isGroupOfElementsBreakingConnectivity(lfNetwork, states.contingencyStates(), contingencyElements, states.actionStates(), actionElements, equationSystem);
168170
}
169171

@@ -200,14 +202,14 @@ private static boolean isGroupOfElementsBreakingConnectivity(LfNetwork lfNetwork
200202
*/
201203
private static Optional<ConnectivityAnalysisResult> computeConnectivityAnalysisResult(LfNetwork lfNetwork,
202204
PropagatedContingency contingency, Map<String, ComputedContingencyElement> contingencyElementByBranch,
203-
LfOperatorStrategy operatorStrategy, Map<LfAction, ComputedElement> actionElementByBranch) {
205+
LfOperatorStrategy operatorStrategy, Map<LfAction, List<ComputedElement>> actionElementByBranch) {
204206
GraphConnectivity<LfBus, LfBranch> connectivity = lfNetwork.getConnectivity();
205207

206208
// concatenate all computed elements, to apply them on the connectivity
207209
List<LfAction> lfActions = operatorStrategy == null ? Collections.emptyList() : operatorStrategy.getActions().stream().filter(LfAction::isValid).toList();
208210
List<ComputedElement> modifyingConnectivityCandidates = Stream.concat(
209211
contingency.getBranchIdsToOpen().keySet().stream().map(contingencyElementByBranch::get),
210-
lfActions.stream().map(actionElementByBranch::get)
212+
lfActions.stream().map(actionElementByBranch::get).flatMap(Collection::stream)
211213
).sorted(Comparator.comparing(element -> element.getLfBranch().getId())).toList();
212214

213215
// we confirm the breaking of connectivity by network connectivity
@@ -353,7 +355,7 @@ public static ConnectivityBreakAnalysisResults run(DcLoadFlowContext loadFlowCon
353355
*/
354356
public static ConnectivityAnalysisResult processPostContingencyAndPostOperatorStrategyConnectivityAnalysisResult(DcLoadFlowContext loadFlowContext, ConnectivityAnalysisResult postContingencyConnectivityAnalysisResult,
355357
Map<String, ComputedContingencyElement> contingencyElementByBranch, DenseMatrix contingenciesStates,
356-
LfOperatorStrategy operatorStrategy, Map<LfAction, ComputedElement> actionElementsIndexByLfAction, DenseMatrix actionsStates) {
358+
LfOperatorStrategy operatorStrategy, Map<LfAction, List<ComputedElement>> actionElementsIndexByLfAction, DenseMatrix actionsStates) {
357359
// if there is no topological action, no need to process anything as the connectivity has not changed from post contingency result
358360
boolean hasAnyTopologicalAction = operatorStrategy.getActions().stream().anyMatch(lfAction -> lfAction instanceof AbstractLfBranchAction<?>);
359361
if (!hasAnyTopologicalAction) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ public static double[] runDcLoadFlowWithModifiedTargetVector(DcLoadFlowContext l
131131
// set transformer phase shift to 0 for disabled phase tap changers by actions
132132
lfActions.stream()
133133
.filter(AbstractLfBranchAction.class::isInstance)
134-
.map(lfAction -> ((AbstractLfBranchAction<?>) lfAction).getDisabledBranch())
134+
.map(lfAction -> ((AbstractLfBranchAction<?>) lfAction).getDisabledBranches())
135135
.filter(Objects::nonNull)
136-
.flatMap(lfBranch -> loadFlowContext.getEquationSystem().getEquation(lfBranch.getNum(), DcEquationType.BRANCH_TARGET_ALPHA1).stream())
136+
.flatMap(lfBranches -> lfBranches.stream().flatMap(b -> loadFlowContext.getEquationSystem().getEquation(b.getNum(), DcEquationType.BRANCH_TARGET_ALPHA1).stream()))
137137
.map(Equation::getColumn)
138138
.forEach(column -> targetVectorArray[column] = 0);
139139
}

src/main/java/com/powsybl/openloadflow/network/LfNetwork.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ public void removeBranch(String branchId) {
318318
throw new PowsyblException("Branch " + branchId + " not found in network " + this);
319319
}
320320
branches.remove(branch);
321+
branch.getOriginalIds().forEach(branchesByOriginalId::remove);
321322
invalidateSlackAndReference();
322323
if (connectivity != null) {
323324
connectivity.removeEdge(branch);

src/main/java/com/powsybl/openloadflow/network/action/AbstractLfBranchAction.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
import com.powsybl.openloadflow.graph.GraphConnectivity;
1313
import com.powsybl.openloadflow.network.*;
1414

15-
import java.util.HashSet;
16-
import java.util.Set;
15+
import java.util.*;
1716

1817
/**
1918
* @author Bertrand Rix {@literal <bertrand.rix at artelys.com>}
@@ -22,36 +21,38 @@
2221
*/
2322
public abstract class AbstractLfBranchAction<A extends Action> extends AbstractLfAction<A> {
2423

25-
private LfBranch disabledBranch = null; // switch to open
24+
private final List<LfBranch> disabledBranch = new ArrayList<>(); // switch to open
2625

27-
private LfBranch enabledBranch = null; // switch to close
26+
private final List<LfBranch> enabledBranch = new ArrayList<>(); // switch to close
2827

2928
AbstractLfBranchAction(A action, LfNetwork lfNetwork) {
3029
super(action);
3130
findEnabledDisabledBranches(lfNetwork);
3231
}
3332

34-
protected void setDisabledBranch(LfBranch disabledBranch) {
35-
this.disabledBranch = disabledBranch;
33+
protected void addDisabledBranch(LfBranch disabledBranch) {
34+
Objects.requireNonNull(disabledBranch);
35+
this.disabledBranch.add(disabledBranch);
3636
}
3737

38-
protected void setEnabledBranch(LfBranch enabledBranch) {
39-
this.enabledBranch = enabledBranch;
38+
protected void addEnabledBranch(LfBranch enabledBranch) {
39+
Objects.requireNonNull(enabledBranch);
40+
this.enabledBranch.add(enabledBranch);
4041
}
4142

42-
public LfBranch getDisabledBranch() {
43+
public List<LfBranch> getDisabledBranches() {
4344
return this.disabledBranch;
4445
}
4546

46-
public LfBranch getEnabledBranch() {
47+
public List<LfBranch> getEnabledBranches() {
4748
return this.enabledBranch;
4849
}
4950

5051
abstract void findEnabledDisabledBranches(LfNetwork lfNetwork);
5152

5253
@Override
5354
public boolean isValid() {
54-
return disabledBranch != null || enabledBranch != null;
55+
return !disabledBranch.isEmpty() || !enabledBranch.isEmpty();
5556
}
5657

5758
/**
@@ -86,18 +87,17 @@ public boolean apply(LfNetwork network, LfContingency contingency, LfNetworkPara
8687
* Optimized apply on an existing connectivity (to apply several branch actions at the same time)
8788
*/
8889
public boolean applyOnConnectivity(GraphConnectivity<LfBus, LfBranch> connectivity) {
89-
boolean found = disabledBranch != null || enabledBranch != null;
9090
updateConnectivity(connectivity);
91-
return found;
91+
return isValid();
9292
}
9393

9494
private void updateConnectivity(GraphConnectivity<LfBus, LfBranch> connectivity) {
95-
if (disabledBranch != null && disabledBranch.getBus1() != null && disabledBranch.getBus2() != null) {
96-
connectivity.removeEdge(disabledBranch);
97-
}
98-
if (enabledBranch != null) {
99-
connectivity.addEdge(enabledBranch.getBus1(), enabledBranch.getBus2(), enabledBranch);
100-
}
95+
disabledBranch.forEach(branch -> {
96+
if (branch.getBus1() != null && branch.getBus2() != null) {
97+
connectivity.removeEdge(branch);
98+
}
99+
});
100+
enabledBranch.forEach(branch -> connectivity.addEdge(branch.getBus1(), branch.getBus2(), branch));
101101
}
102102

103103
public static void updateBusesAndBranchStatus(GraphConnectivity<LfBus, LfBranch> connectivity) {

src/main/java/com/powsybl/openloadflow/network/action/Actions.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ public static void checkValidity(Network network, List<Action> actions) {
6565

6666
case TerminalsConnectionAction.NAME: {
6767
TerminalsConnectionAction terminalsConnectionAction = (TerminalsConnectionAction) action;
68-
if (network.getBranch(terminalsConnectionAction.getElementId()) == null) {
69-
throw new PowsyblException("Branch '" + terminalsConnectionAction.getElementId() + NOT_FOUND);
68+
if (network.getBranch(terminalsConnectionAction.getElementId()) == null &&
69+
network.getThreeWindingsTransformer(terminalsConnectionAction.getElementId()) == null) {
70+
throw new PowsyblException("Branch or three windings transformer '" + terminalsConnectionAction.getElementId() + NOT_FOUND);
7071
}
7172
break;
7273
}

src/main/java/com/powsybl/openloadflow/network/action/LfSwitchAction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ void findEnabledDisabledBranches(LfNetwork lfNetwork) {
3232
LfBranch branch = lfNetwork.getBranchById(action.getSwitchId());
3333
if (branch != null) {
3434
if (action.isOpen()) {
35-
setDisabledBranch(branch);
35+
addDisabledBranch(branch);
3636
} else {
37-
setEnabledBranch(branch);
37+
addEnabledBranch(branch);
3838
}
3939
} else {
4040
LOGGER.warn("Switch action {}: branch matching switch id {} not found", action.getId(), action.getSwitchId());

src/main/java/com/powsybl/openloadflow/network/action/LfTerminalsConnectionAction.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import org.slf4j.Logger;
1515
import org.slf4j.LoggerFactory;
1616

17+
import java.util.List;
18+
1719
/**
1820
* @author Bertrand Rix {@literal <bertrand.rix at artelys.com>}
1921
* @author Anne Tilloy {@literal <anne.tilloy at rte-france.com>}
@@ -32,15 +34,23 @@ public LfTerminalsConnectionAction(TerminalsConnectionAction action, LfNetwork l
3234

3335
@Override
3436
void findEnabledDisabledBranches(LfNetwork lfNetwork) {
35-
LfBranch branch = lfNetwork.getBranchById(action.getElementId());
36-
if (branch != null && branch.getBus1() != null && branch.getBus2() != null) {
37+
List<LfBranch> branches = lfNetwork.getBranchesByOriginalId(action.getElementId());
38+
if (branches != null) {
39+
branches.forEach(b -> applyEnabledDisabled(b, action));
40+
} else {
41+
LOGGER.warn("TerminalsConnectionAction action {}: branch or three windings transformer matching element id {} not found", action.getId(), action.getElementId());
42+
}
43+
}
44+
45+
void applyEnabledDisabled(LfBranch branch, TerminalsConnectionAction action) {
46+
if (branch.getBus1() != null && branch.getBus2() != null) {
3747
if (action.isOpen()) {
38-
setDisabledBranch(branch);
48+
addDisabledBranch(branch);
3949
} else {
40-
setEnabledBranch(branch);
50+
addEnabledBranch(branch);
4151
}
4252
} else {
43-
LOGGER.warn("TerminalsConnectionAction action {}: branch matching element id {} not found", action.getId(), action.getElementId());
53+
LOGGER.warn("TerminalsConnectionAction action {}: branch matching element id {} has one missing bus", action.getId(), action.getElementId());
4454
}
4555
}
4656
}

0 commit comments

Comments
 (0)