Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support of BusbarSectionContingency for everything #667

Merged
merged 32 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fd27023
WIP.
annetill Dec 7, 2022
303b261
Merge.
annetill Dec 8, 2022
04884bb
Save work.
annetill Dec 8, 2022
3398fb7
Merge branch 'main' into busbarsection-contingency
annetill Jan 10, 2023
4e4bb39
Remove everything related to bus.
annetill Jan 11, 2023
a117eac
Add minimal tripping from busbar section contingency
flo-dup Jan 11, 2023
a8ababc
Merge.
annetill Jan 16, 2023
c57717c
Fix comment
flo-dup Jan 16, 2023
820e38b
WIP.
annetill Jan 16, 2023
4d62298
Merge branch 'busbarsection-contingency' of https://github.com/powsyb…
annetill Jan 16, 2023
d4e91e4
Merge branch 'main' into busbarsection-contingency
geofjamg Jan 17, 2023
dcd4583
Merge branch 'main' into busbarsection-contingency
annetill Jan 20, 2023
0db9492
Save
annetill Jan 20, 2023
4136262
WIP
annetill Jan 23, 2023
253f92d
WIP.
annetill Feb 9, 2023
8244ccf
Clean.
annetill Feb 9, 2023
1ee7fa1
Merge.
annetill Feb 9, 2023
247a619
Fix unit test.
annetill Feb 9, 2023
885b7cf
First working version on real test cases.
annetill Feb 9, 2023
991bc8a
Merge branch 'main' into busbarsection-contingency
annetill Feb 10, 2023
04a68c0
Merge branch 'main' into busbarsection-contingency
annetill Feb 15, 2023
43feccb
Merge.
annetill Mar 1, 2023
5d99f7d
Merge.
annetill Mar 23, 2023
80bafdc
Merge branch 'main' into busbarsection-contingency
geofjamg Mar 24, 2023
f7b906e
Clean
annetill Mar 26, 2023
736eba6
Merge branch 'main' into busbarsection-contingency
annetill Mar 27, 2023
c4fff73
Merge branch 'main' into busbarsection-contingency
annetill Mar 29, 2023
8e4298e
Merge branch 'main' into busbarsection-contingency
geofjamg Mar 29, 2023
4173139
Merge branch 'main' into busbarsection-contingency
annetill Mar 31, 2023
1f84ca5
Clean.
annetill Mar 31, 2023
95fbf8d
Merge branch 'main' into busbarsection-contingency
annetill Mar 31, 2023
b95c188
add comments.
annetill Mar 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.math.graph.TraverseResult;

import java.util.*;

Expand All @@ -16,16 +17,25 @@
*/
public class ContingencyTripping {

private static final ContingencyTripping NO_OP_TRIPPING = new ContingencyTripping(Collections.emptyList());
private static final ContingencyTripping NO_OP_TRIPPING = new ContingencyTripping(Collections.emptyList(), (s, tt, nt, n, nbv) -> null);

@FunctionalInterface
private interface NodeBreakerTraverserFactory {
VoltageLevel.NodeBreakerView.TopologyTraverser create(
Set<Switch> stoppingSwitches, Set<Terminal> traversedTerminals, List<Terminal> neighbourTerminals,
int initNode, VoltageLevel.NodeBreakerView nodeBreakerView);
}

private final List<? extends Terminal> terminals;
private final NodeBreakerTraverserFactory nodeBreakerTraverserFactory;

public ContingencyTripping(List<? extends Terminal> terminals) {
public ContingencyTripping(List<? extends Terminal> terminals, NodeBreakerTraverserFactory nodeBreakerTraverserFactory) {
this.terminals = terminals;
this.nodeBreakerTraverserFactory = nodeBreakerTraverserFactory;
}

public ContingencyTripping(Terminal terminal) {
this(Collections.singletonList(terminal));
public ContingencyTripping(Terminal terminal, NodeBreakerTraverserFactory nodeBreakerTraverserFactory) {
this(List.of(terminal), nodeBreakerTraverserFactory);
}

public static ContingencyTripping createBranchTripping(Network network, Branch<?> branch) {
Expand All @@ -38,29 +48,53 @@ public static ContingencyTripping createBranchTripping(Network network, Branch<?

if (voltageLevelId != null) {
if (voltageLevelId.equals(branch.getTerminal1().getVoltageLevel().getId())) {
return new ContingencyTripping(branch.getTerminal1());
return new ContingencyTripping(branch.getTerminal1(), NodeBreakerTraverser::new);
} else if (voltageLevelId.equals(branch.getTerminal2().getVoltageLevel().getId())) {
return new ContingencyTripping(branch.getTerminal2());
return new ContingencyTripping(branch.getTerminal2(), NodeBreakerTraverser::new);
} else {
throw new PowsyblException("VoltageLevel '" + voltageLevelId + "' not connected to branch '" + branch.getId() + "'");
}
} else {
return new ContingencyTripping(branch.getTerminals());
return new ContingencyTripping(branch.getTerminals(), NodeBreakerTraverser::new);
}
}

public static ContingencyTripping createInjectionTripping(Network network, Injection<?> injection) {
Objects.requireNonNull(network);
Objects.requireNonNull(injection);

return new ContingencyTripping(injection.getTerminal());
return new ContingencyTripping(injection.getTerminal(), NodeBreakerTraverser::new);
}

public static ContingencyTripping createThreeWindingsTransformerTripping(Network network, ThreeWindingsTransformer twt) {
Objects.requireNonNull(network);
Objects.requireNonNull(twt);

return new ContingencyTripping(twt.getTerminals());
return new ContingencyTripping(twt.getTerminals(), NodeBreakerTraverser::new);
}

public static ContingencyTripping createBusbarSectionMinimalTripping(Network network, BusbarSection bbs) {
Objects.requireNonNull(network);
Objects.requireNonNull(bbs);

NodeBreakerTraverserFactory minimalTraverserFactory = (stoppingSwitches, neighbourTerminals, traversedTerminals, n, nbv) ->
// To have the minimal tripping ("no propagation") with a busbar section we still need to traverse the
// voltage level starting from that busbar section, stopping at first switch encountered (which will be
// marked as retained afterwards), in order to have the smallest lost bus in breaker view
// Note that neighbourTerminals is not filled up: we don't want to propagate to neighbouring voltage levels as
// this is a minimal tripping ("no propagation")
(nodeBefore, sw, nodeAfter) -> {
if (sw != null) {
if (!sw.isOpen()) {
stoppingSwitches.add(sw);
}
return TraverseResult.TERMINATE_PATH;
} else {
nbv.getOptionalTerminal(nodeAfter).ifPresent(traversedTerminals::add);
return TraverseResult.CONTINUE;
}
};
return new ContingencyTripping(bbs.getTerminal(), minimalTraverserFactory);
}

public static ContingencyTripping createContingencyTripping(Network network, Identifiable<?> identifiable) {
Expand All @@ -73,6 +107,7 @@ public static ContingencyTripping createContingencyTripping(Network network, Ide
case LOAD:
case SHUNT_COMPENSATOR:
case STATIC_VAR_COMPENSATOR:
case BUSBAR_SECTION:
return ContingencyTripping.createInjectionTripping(network, (Injection<?>) identifiable);
case THREE_WINDINGS_TRANSFORMER:
return ContingencyTripping.createThreeWindingsTransformerTripping(network, (ThreeWindingsTransformer) identifiable);
Expand Down Expand Up @@ -107,10 +142,10 @@ private void traverseFromTerminal(Terminal terminal, Set<Switch> switchesToOpen,

if (terminal.getVoltageLevel().getTopologyKind() == TopologyKind.NODE_BREAKER) {
traversedTerminals.add(terminal);
List<Terminal> nextTerminals = traverseNodeBreakerVoltageLevelsFromTerminal(terminal, switchesToOpen, traversedTerminals);
List<Terminal> neighbourTerminals = traverseNodeBreakerVoltageLevelsFromTerminal(terminal, switchesToOpen, traversedTerminals);

// Recursive call to continue the traverser in affected neighbouring voltage levels
nextTerminals.forEach(t -> traverseFromTerminal(t, switchesToOpen, traversedTerminals));
neighbourTerminals.forEach(t -> traverseFromTerminal(t, switchesToOpen, traversedTerminals));
} else {
// In bus breaker view we have no idea what kind of switch it was in the initial node/breaker topology
// so to keep things simple we do not propagate the fault
Expand All @@ -125,16 +160,12 @@ private List<Terminal> traverseNodeBreakerVoltageLevelsFromTerminal(Terminal ter
int initNode = terminal.getNodeBreakerView().getNode();
VoltageLevel.NodeBreakerView nodeBreakerView = terminal.getVoltageLevel().getNodeBreakerView();

NodeBreakerTraverser traverser = new NodeBreakerTraverser(switchesToOpen, initNode, nodeBreakerView);
List<Terminal> neighbourTerminals = new ArrayList<>();
VoltageLevel.NodeBreakerView.TopologyTraverser traverser = nodeBreakerTraverserFactory.create(
switchesToOpen, traversedTerminals, neighbourTerminals, initNode, nodeBreakerView);
nodeBreakerView.traverse(initNode, traverser);

List<Terminal> nextTerminals = new ArrayList<>();
traverser.getTraversedTerminals().forEach(t -> {
nextTerminals.addAll(t.getConnectable().getTerminals()); // the already traversed terminal are also added for the sake of simplicity
traversedTerminals.add(t);
});

return nextTerminals;
return neighbourTerminals;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import com.powsybl.iidm.network.*;
import com.powsybl.math.graph.TraverseResult;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
Expand All @@ -19,13 +19,15 @@ public class NodeBreakerTraverser implements VoltageLevel.NodeBreakerView.Topolo

private final Set<Switch> switchesToOpen;
private final Set<Terminal> traversedTerminals;
private final List<Terminal> nextTerminals;
private final int initNode;
private final VoltageLevel.NodeBreakerView nodeBreakerView;

public NodeBreakerTraverser(Set<Switch> switchesToOpen, int initNode,
VoltageLevel.NodeBreakerView nodeBreakerView) {
public NodeBreakerTraverser(Set<Switch> switchesToOpen, Set<Terminal> traversedTerminals, List<Terminal> nextTerminals,
int initNode, VoltageLevel.NodeBreakerView nodeBreakerView) {
this.switchesToOpen = switchesToOpen;
this.traversedTerminals = new HashSet<>();
this.traversedTerminals = traversedTerminals;
this.nextTerminals = nextTerminals;
this.initNode = initNode;
this.nodeBreakerView = nodeBreakerView;
}
Expand Down Expand Up @@ -59,7 +61,7 @@ public TraverseResult traverse(int nodeBefore, Switch sw, int nodeAfter) {
}
if (isEquivalentToStopAfterSwitch(sw, nodeAfter)) {
// Retaining the switch is equivalent to stop at the node after if the node after the switch is an end node (e.g. load or generator)
sw.getVoltageLevel().getNodeBreakerView().getOptionalTerminal(nodeAfter).ifPresent(traversedTerminals::add);
sw.getVoltageLevel().getNodeBreakerView().getOptionalTerminal(nodeAfter).ifPresent(this::terminalTraversed);
return TraverseResult.TERMINATE_PATH;
}
switchesToOpen.add(sw);
Expand All @@ -68,10 +70,17 @@ public TraverseResult traverse(int nodeBefore, Switch sw, int nodeAfter) {
}

// The traverser continues, hence nodeAfter is traversed
nodeBreakerView.getOptionalTerminal(nodeAfter).ifPresent(traversedTerminals::add);
nodeBreakerView.getOptionalTerminal(nodeAfter).ifPresent(this::terminalTraversed);
return TraverseResult.CONTINUE;
}

private void terminalTraversed(Terminal terminal) {
traversedTerminals.add(terminal);
((Connectable<?>) terminal.getConnectable()).getTerminals().stream()
.filter(t -> t != terminal)
.forEach(nextTerminals::add);
}

private static boolean isEquivalentToStopAfterSwitch(Switch sw, int nodeAfter) {
Terminal terminal2 = sw.getVoltageLevel().getNodeBreakerView().getTerminal(nodeAfter);
if (terminal2 != null) {
Expand Down Expand Up @@ -126,8 +135,4 @@ private static boolean isOpenable(Switch aSwitch) {
private static boolean isOpenOrOpenable(Switch aSwitch) {
return aSwitch.isOpen() || isOpenable(aSwitch);
}

protected Set<Terminal> getTraversedTerminals() {
return this.traversedTerminals;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ private static PropagatedContingency create(Network network, Contingency conting
Identifiable<?> identifiable = getIdentifiable(network, element);
if (contingencyPropagation) {
ContingencyTripping.createContingencyTripping(network, identifiable).traverse(switchesToOpen, terminalsToDisconnect);
} else if (identifiable instanceof BusbarSection) {
ContingencyTripping.createBusbarSectionMinimalTripping(network, (BusbarSection) identifiable).traverse(switchesToOpen, terminalsToDisconnect);
}
terminalsToDisconnect.addAll(getTerminals(identifiable));
if (identifiable instanceof Switch) {
Expand Down Expand Up @@ -157,6 +159,7 @@ private void complete(boolean shuntCompensatorVoltageControlOn, boolean slackDis

case GENERATOR:
case STATIC_VAR_COMPENSATOR:
case BATTERY:
generatorIdsToLose.add(connectable.getId());
break;

Expand All @@ -182,6 +185,8 @@ private void complete(boolean shuntCompensatorVoltageControlOn, boolean slackDis
if (control != null && control.isEnabled() && hvdcAcEmulation) {
hvdcIdsToOpen.add(station.getHvdcLine().getId());
}
// FIXME
// the other converter station should be considered to if in the same synchronous component (hvdc setpoint mode).
if (connectable instanceof VscConverterStation) {
generatorIdsToLose.add(connectable.getId());
} else {
Expand Down Expand Up @@ -273,6 +278,10 @@ private static Identifiable<?> getIdentifiable(Network network, ContingencyEleme
identifiable = network.getThreeWindingsTransformer(element.getId());
identifiableType = "Three windings transformer";
break;
case BUSBAR_SECTION:
identifiable = network.getBusbarSection(element.getId());
identifiableType = "Busbar section";
break;
default:
throw new UnsupportedOperationException("Unsupported contingency element type: " + element.getType());
}
Expand All @@ -296,6 +305,15 @@ public Optional<LfContingency> toLfContingency(LfNetwork network) {
.filter(LfBranch::isConnectedAtBothSides)
.forEach(connectivity::removeEdge);

if (connectivity.getConnectedComponent(network.getSlackBus()).size() == 1) {
// FIXME
// If a contingency leads to an isolated slack bus, this bus is considered as the main component.
// In that case, we have an issue with a different number of variables and equations.
LOGGER.error("Contingency '{}' leads to an isolated slack bus: not supported", contingency.getId());
connectivity.undoTemporaryChanges();
return Optional.empty();
}

// add to contingency description buses and branches that won't be part of the main connected
// component in post contingency state
int createdSynchronousComponents = connectivity.getNbConnectedComponents() - 1;
Expand All @@ -310,6 +328,15 @@ public Optional<LfContingency> toLfContingency(LfNetwork network) {
bus.getBranches().stream().filter(b -> !b.isConnectedAtBothSides()).forEach(branches::add);
}

for (LfHvdc hvdcLine : network.getHvdcs()) {
// FIXME
// if we loose a bus with a converter station, the other converter station should be considered to if in the
// same synchronous component (hvdc setpoint mode).
if (buses.contains(hvdcLine.getBus1()) || buses.contains(hvdcLine.getBus2())) {
hvdcIdsToOpen.add(hvdcLine.getId());
}
}

// reset connectivity to discard triggered branches
connectivity.undoTemporaryChanges();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public static Network create() {
.setId("C")
.setNode1(6)
.setNode2(1)
.setRetained(true)
.add();
vl1.getNodeBreakerView().newBreaker()
.setId("B1")
Expand All @@ -99,8 +100,8 @@ public static Network create() {
vl1.newGenerator()
.setId("G")
.setNode(4)
.setMinP(-9999.99)
.setMaxP(9999.99)
.setMinP(0.0)
.setMaxP(1000.0)
.setVoltageRegulatorOn(true)
.setTargetV(398)
.setTargetP(603.77)
Expand Down
Loading