Skip to content

Refactor connectivity break analysis to add connectivity loss impact of the contingencies #1192

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

Merged
merged 29 commits into from
Apr 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
18ecb1f
Refactor connectivity break analysis to add connectivity loss impact
p-arvy Feb 18, 2025
6ed4611
Clean
p-arvy Feb 18, 2025
07046ea
Add TU
p-arvy Feb 18, 2025
15b5bd9
Update TU
p-arvy Feb 25, 2025
a9328cc
Merge branch 'refs/heads/main' into connectivity-loss-in-connectivity…
p-arvy Feb 25, 2025
ac766a8
Merge branch 'refs/heads/main' into connectivity-loss-in-connectivity…
p-arvy Mar 3, 2025
994f6a2
Small clean
p-arvy Mar 3, 2025
6cfcf3f
Update TU
p-arvy Mar 3, 2025
87938b6
Clean
Hadrien-Godard Mar 4, 2025
a5a862a
Refacto
Hadrien-Godard Mar 5, 2025
e4dc7c4
Add NO_DISABLED_ELEMENTS
p-arvy Mar 7, 2025
cad785b
Merge remote-tracking branch 'origin/main' into connectivity-loss-in-…
Hadrien-Godard Mar 10, 2025
31ac8e3
Remove ok field in ContingencyConnectivityLossImpact
Hadrien-Godard Mar 10, 2025
76de054
Proposal to reduce arguments in addPostContingencyAndOperatorStrategy…
Hadrien-Godard Mar 10, 2025
eb720dc
Add test to ensure proper behavior when HVDC (not in AC emulation) ha…
vidaldid-rte Mar 12, 2025
582a4f3
Sonar believes public test is bad. And some people pay attention to t…
vidaldid-rte Mar 12, 2025
32e2b81
fix comment
vidaldid-rte Mar 12, 2025
f87c848
Merge branch 'refs/heads/main' into connectivity-loss-in-connectivity…
p-arvy Mar 17, 2025
bbdf051
Merge remote-tracking branch 'origin/connectivity-loss-in-connectivit…
p-arvy Mar 17, 2025
711c4cf
Refactor records in WoodburyDcSecurityAnalysis
p-arvy Mar 18, 2025
eef4e96
Very small clean
p-arvy Mar 18, 2025
4f92d7c
Merge branch 'refs/heads/main' into connectivity-loss-in-connectivity…
p-arvy Mar 18, 2025
7014908
Merge branch 'main' into connectivity-loss-in-connectivity-break-anal…
Hadrien-Godard Mar 25, 2025
c50c5fb
Merge branch 'refs/heads/main' into connectivity-loss-in-connectivity…
p-arvy Mar 28, 2025
80e18b5
Update TUs to include slow mode
p-arvy Mar 28, 2025
d9ba68d
Merge branch 'refs/heads/main' into connectivity-loss-in-connectivity…
p-arvy Apr 2, 2025
2d234d5
Merge branch 'refs/heads/main' into connectivity-loss-in-connectivity…
p-arvy Apr 4, 2025
6857279
clean
p-arvy Apr 4, 2025
6ce81ca
clean
p-arvy Apr 4, 2025
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 @@ -15,10 +15,7 @@
import com.powsybl.openloadflow.dc.equations.DcVariableType;
import com.powsybl.openloadflow.equations.EquationSystem;
import com.powsybl.openloadflow.graph.GraphConnectivity;
import com.powsybl.openloadflow.network.ElementType;
import com.powsybl.openloadflow.network.LfBranch;
import com.powsybl.openloadflow.network.LfBus;
import com.powsybl.openloadflow.network.LfNetwork;
import com.powsybl.openloadflow.network.*;
import com.powsybl.openloadflow.network.impl.PropagatedContingency;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -36,49 +33,68 @@ public final class ConnectivityBreakAnalysis {

private static final Logger LOGGER = LoggerFactory.getLogger(ConnectivityBreakAnalysis.class);

public record DisabledElements(Set<LfBus> disabledBuses, Set<LfBranch> partialDisabledBranches, Set<LfHvdc> hvdcsWithoutPower) {

public static final DisabledElements NO_DISABLED_ELEMENTS = new DisabledElements(Collections.emptySet(), Collections.emptySet(), Collections.emptySet());

}

public static final class ConnectivityAnalysisResult {
private final PropagatedContingency propagatedContingency;

private PropagatedContingency contingency;
private final LfNetwork network;

private final Set<String> elementsToReconnect;

private final Set<LfBus> disabledBuses;
private final Set<LfBus> slackConnectedComponent; // buses of connected component where the slack is

private final Set<LfBus> slackConnectedComponent;
private final int createdSynchronousComponents;

private final Set<LfBranch> partialDisabledBranches; // branches disabled because of connectivity loss.
private final DisabledElements disabledElements;

private ConnectivityAnalysisResult(Set<String> elementsToReconnect,
GraphConnectivity<LfBus, LfBranch> connectivity,
LfNetwork lfNetwork) {
this.elementsToReconnect = elementsToReconnect;
slackConnectedComponent = connectivity.getConnectedComponent(lfNetwork.getSlackBus());
disabledBuses = connectivity.getVerticesRemovedFromMainComponent();
partialDisabledBranches = connectivity.getEdgesRemovedFromMainComponent();
public ConnectivityAnalysisResult(PropagatedContingency nonBreakingConnectivityContingency, LfNetwork network) {
this(nonBreakingConnectivityContingency, network, Collections.emptySet(), DisabledElements.NO_DISABLED_ELEMENTS, Collections.emptySet(), 0);
}

public PropagatedContingency getPropagatedContingency() {
return contingency;
public ConnectivityAnalysisResult(PropagatedContingency propagatedContingency, LfNetwork network, Set<String> elementsToReconnect,
DisabledElements disabledElements, Set<LfBus> slackConnectedComponentBuses,
int createdSynchronousComponents) {
this.propagatedContingency = Objects.requireNonNull(propagatedContingency);
this.network = Objects.requireNonNull(network);
this.elementsToReconnect = elementsToReconnect;
this.disabledElements = disabledElements;
this.slackConnectedComponent = slackConnectedComponentBuses;
this.createdSynchronousComponents = createdSynchronousComponents;
}

public void setPropagatedContingency(PropagatedContingency contingency) {
this.contingency = contingency;
public PropagatedContingency getPropagatedContingency() {
return propagatedContingency;
}

public Set<String> getElementsToReconnect() {
return elementsToReconnect;
}

public Set<LfBus> getDisabledBuses() {
return disabledBuses;
return disabledElements.disabledBuses;
}

public Set<LfBus> getSlackConnectedComponent() {
return slackConnectedComponent;
}

public Set<LfBranch> getPartialDisabledBranches() {
return partialDisabledBranches;
return disabledElements.partialDisabledBranches;
}

public Set<LfHvdc> getHvdcsWithoutPower() {
return disabledElements.hvdcsWithoutPower;
}

public Optional<LfContingency> toLfContingency() {
PropagatedContingency.ContingencyConnectivityLossImpactAnalysis analysis = (network, contingencyId, branchesToOpen, relocateSlackBus)
-> new PropagatedContingency.ContingencyConnectivityLossImpact(createdSynchronousComponents, disabledElements.disabledBuses, disabledElements.hvdcsWithoutPower);
return propagatedContingency.toLfContingency(network, false, analysis);
}
}

Expand Down Expand Up @@ -162,8 +178,12 @@ private static List<ConnectivityAnalysisResult> computeConnectivityData(LfNetwor
} else {
// only compute for factors that have to be computed for this contingency lost
Set<String> elementsToReconnect = computeElementsToReconnect(connectivity, breakingConnectivityElements);
ConnectivityAnalysisResult connectivityAnalysisResult = new ConnectivityAnalysisResult(elementsToReconnect, connectivity, lfNetwork);
connectivityAnalysisResult.setPropagatedContingency(contingency);
int createdSynchronousComponents = connectivity.getNbConnectedComponents() - 1;
Set<LfBus> disabledBuses = connectivity.getVerticesRemovedFromMainComponent();
Set<LfHvdc> hvdcsWithoutPower = PropagatedContingency.getHvdcsWithoutPower(lfNetwork, disabledBuses, connectivity);
ConnectivityAnalysisResult connectivityAnalysisResult = new ConnectivityAnalysisResult(contingency, lfNetwork, elementsToReconnect,
new DisabledElements(disabledBuses, connectivity.getEdgesRemovedFromMainComponent(), hvdcsWithoutPower),
connectivity.getConnectedComponent(lfNetwork.getSlackBus()), createdSynchronousComponents);
connectivityAnalysisResults.add(connectivityAnalysisResult);
}
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,10 +380,21 @@ private Map<LfBranch, DisabledBranchStatus> findBranchToOpenDirectlyImpactedByCo
return branchesToOpen;
}

record ContingencyConnectivityLossImpact(boolean ok, int createdSynchronousComponents, Set<LfBus> busesToLost, Set<LfHvdc> hvdcsWithoutPower) {
public record ContingencyConnectivityLossImpact(int createdSynchronousComponents, Set<LfBus> busesToLost, Set<LfHvdc> hvdcsWithoutPower) {
}

private ContingencyConnectivityLossImpact findBusesAndBranchesImpactedBecauseOfConnectivityLoss(LfNetwork network, Map<LfBranch, DisabledBranchStatus> branchesToOpen, boolean relocateSlackBus) {
public static Set<LfHvdc> getHvdcsWithoutPower(LfNetwork network, Set<LfBus> busesToLost, GraphConnectivity<LfBus, LfBranch> connectivity) {
Set<LfHvdc> hvdcsWithoutFlow = new HashSet<>();
for (LfHvdc hvdcLine : network.getHvdcs()) {
if (checkIsolatedBus(hvdcLine.getBus1(), hvdcLine.getBus2(), busesToLost, connectivity)
|| checkIsolatedBus(hvdcLine.getBus2(), hvdcLine.getBus1(), busesToLost, connectivity)) {
hvdcsWithoutFlow.add(hvdcLine);
}
}
return hvdcsWithoutFlow;
}

private static ContingencyConnectivityLossImpact findBusesAndBranchesImpactedBecauseOfConnectivityLoss(LfNetwork network, String contingencyId, Map<LfBranch, DisabledBranchStatus> branchesToOpen, boolean relocateSlackBus) {
// update connectivity with triggered branches of this network
// note that this will define the main component as the one containing the first slack bus
GraphConnectivity<LfBus, LfBranch> connectivity = network.getConnectivity();
Expand All @@ -395,7 +406,7 @@ private ContingencyConnectivityLossImpact findBusesAndBranchesImpactedBecauseOfC

if (relocateSlackBus && isSlackBusIsolated(connectivity, network.getSlackBus())) {
LOGGER.warn("Contingency '{}' leads to an isolated slack bus: relocate slack bus inside main component",
contingency.getId());
contingencyId);
// if a contingency leads to an isolated slack bus, we need to relocate the slack bus
// we select a new slack bus excluding buses from isolated component
Set<LfBus> excludedBuses = Sets.difference(Set.copyOf(network.getBuses()), connectivity.getLargestConnectedComponent());
Expand All @@ -411,22 +422,16 @@ private ContingencyConnectivityLossImpact findBusesAndBranchesImpactedBecauseOfC

// as we know here the connectivity after contingency, we have to reset active power flow of a hvdc line
// if one bus of the line is lost.
Set<LfHvdc> hvdcsWithoutFlow = new HashSet<>();
for (LfHvdc hvdcLine : network.getHvdcs()) {
if (checkIsolatedBus(hvdcLine.getBus1(), hvdcLine.getBus2(), busesToLost, connectivity)
|| checkIsolatedBus(hvdcLine.getBus2(), hvdcLine.getBus1(), busesToLost, connectivity)) {
hvdcsWithoutFlow.add(hvdcLine);
}
}
Set<LfHvdc> hvdcsWithoutFlow = getHvdcsWithoutPower(network, busesToLost, connectivity);

return new ContingencyConnectivityLossImpact(true, createdSynchronousComponents, busesToLost, hvdcsWithoutFlow);
return new ContingencyConnectivityLossImpact(createdSynchronousComponents, busesToLost, hvdcsWithoutFlow);
} finally {
// reset connectivity to discard triggered elements
connectivity.undoTemporaryChanges();
}
}

private boolean checkIsolatedBus(LfBus bus1, LfBus bus2, Set<LfBus> busesToLost, GraphConnectivity<LfBus, LfBranch> connectivity) {
private static boolean checkIsolatedBus(LfBus bus1, LfBus bus2, Set<LfBus> busesToLost, GraphConnectivity<LfBus, LfBranch> connectivity) {
return busesToLost.contains(bus1) && !busesToLost.contains(bus2) && Networks.isIsolatedBusForHvdc(bus1, connectivity);
}

Expand All @@ -440,20 +445,21 @@ private static boolean isConnectedAfterContingencySide2(Map<LfBranch, DisabledBr
return status == null || status == DisabledBranchStatus.SIDE_1;
}

public interface ContingencyConnectivityLossImpactAnalysis {
ContingencyConnectivityLossImpact run(LfNetwork network, String contingencyId, Map<LfBranch, DisabledBranchStatus> branchesToOpen, boolean relocateSlackBus);
}

public Optional<LfContingency> toLfContingency(LfNetwork network) {
return toLfContingency(network, true);
return toLfContingency(network, true, PropagatedContingency::findBusesAndBranchesImpactedBecauseOfConnectivityLoss);
}

public Optional<LfContingency> toLfContingency(LfNetwork network, boolean relocateSlackBus) {
public Optional<LfContingency> toLfContingency(LfNetwork network, boolean relocateSlackBus, ContingencyConnectivityLossImpactAnalysis analysis) {
// find branch to open because of direct impact of the contingency (including propagation is activated)
Map<LfBranch, DisabledBranchStatus> branchesToOpen = findBranchToOpenDirectlyImpactedByContingency(network);

// find branches to open and buses to lost not directly from the contingency impact but as a consequence of
// loss of connectivity once contingency applied on the network
ContingencyConnectivityLossImpact connectivityLossImpact = findBusesAndBranchesImpactedBecauseOfConnectivityLoss(network, branchesToOpen, relocateSlackBus);
if (!connectivityLossImpact.ok) {
return Optional.empty();
}
ContingencyConnectivityLossImpact connectivityLossImpact = analysis.run(network, contingency.getId(), branchesToOpen, relocateSlackBus);
Set<LfBus> busesToLost = connectivityLossImpact.busesToLost(); // nothing else

for (LfBus busToLost : busesToLost) {
Expand Down
Loading