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

Even-Shiloach corrections #192

Merged
merged 7 commits into from
Jan 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -83,9 +83,6 @@ public void cut(V vertex1, V vertex2) {

graph.removeEdge(vertex1, vertex2);
cutEdges.add(Pair.of(vertex1, vertex2));
if (graph.getAllEdges(vertex1, vertex2).size() > 1) {
return;
}

GraphProcess processA = new GraphProcessA(vertex1, vertex2);
GraphProcessB processB = new GraphProcessB(vertex1, vertex2);
Expand Down Expand Up @@ -167,7 +164,7 @@ public List<Set<V>> getSmallComponents() {
sortedComponents = true;
connectedComponents.sort(Comparator.comparingInt(c -> -c.size()));
}
return connectedComponents.subList(1, connectedComponents.size());
return connectedComponents.isEmpty() ? Collections.emptyList() : connectedComponents.subList(1, connectedComponents.size());
}

private interface GraphProcess {
Expand Down Expand Up @@ -235,51 +232,66 @@ private class GraphProcessB implements GraphProcess {

private final Deque<V> verticesToUpdate;
private final Map<V, LevelNeighbours> savedChangedLevels;
private final V vertex1;
private final V vertex2;
private boolean init;

public GraphProcessB(V v, V w) {
public GraphProcessB(V vertex1, V vertex2) {
this.vertex1 = vertex1;
this.vertex2 = vertex2;
this.verticesToUpdate = new LinkedList<>();
this.savedChangedLevels = new HashMap<>();
LevelNeighbours nV = levelNeighboursMap.get(v);
LevelNeighbours nW = levelNeighboursMap.get(w);
if (nV.level == nW.level) {
nV.sameLevel.remove(w);
nW.sameLevel.remove(v);
this.init = false;
}

private void initialStep() {
LevelNeighbours ln1 = getLevelNeighbour(vertex1);
LevelNeighbours ln2 = getLevelNeighbour(vertex2);
if (ln1.level == ln2.level) {
ln1.sameLevel.remove(vertex2);
ln2.sameLevel.remove(vertex1);
} else {
V vertexLowLevel = nV.level < nW.level ? v : w;
V vertexBigLevel = nV.level < nW.level ? w : v;
LevelNeighbours nLowLevel = nV.level < nW.level ? nV : nW;
LevelNeighbours nBigLevel = nV.level < nW.level ? nW : nV;
V vertexLowLevel = ln1.level < ln2.level ? vertex1 : vertex2;
V vertexBigLevel = ln1.level < ln2.level ? vertex2 : vertex1;
LevelNeighbours nLowLevel = ln1.level < ln2.level ? ln1 : ln2;
LevelNeighbours nBigLevel = ln1.level < ln2.level ? ln2 : ln1;

nLowLevel.upperLevel.remove(vertexBigLevel);
nBigLevel.lowerLevel.remove(vertexLowLevel);
if (nBigLevel.lowerLevel.isEmpty()) {
verticesToUpdate.add(vertexBigLevel);
if (nBigLevel.lowerLevel.isEmpty() && graph.getAllEdges(vertex1, vertex2).isEmpty()) {
this.verticesToUpdate.add(vertexBigLevel);
}
}
}

private LevelNeighbours getLevelNeighbour(V v) {
LevelNeighbours levelNeighbours = levelNeighboursMap.get(v);
savedChangedLevels.computeIfAbsent(v, vertex -> new LevelNeighbours(levelNeighbours));
return levelNeighbours;
}

@Override
public void next() {
if (!init) {
initialStep();
init = true;
}
if (verticesToUpdate.isEmpty()) {
return; // step (1)/(9)
}
V w = verticesToUpdate.removeFirst(); // step (2)
LevelNeighbours levelNeighbours = levelNeighboursMap.get(w);
if (!savedChangedLevels.containsKey(w)) {
savedChangedLevels.put(w, new LevelNeighbours(levelNeighbours));
}
LevelNeighbours levelNeighbours = getLevelNeighbour(w);
levelNeighbours.level++; // step (3)
for (V localNeighbour : levelNeighbours.sameLevel) { // step (4)
if (w != localNeighbour) {
LevelNeighbours lnln = levelNeighboursMap.get(localNeighbour);
LevelNeighbours lnln = getLevelNeighbour(localNeighbour);
lnln.sameLevel.remove(w);
lnln.upperLevel.add(w);
}
}
levelNeighbours.lowerLevel.addAll(levelNeighbours.sameLevel); // step (5)
for (V upperNeighbour : levelNeighbours.upperLevel) { // step (6)
LevelNeighbours lnun = levelNeighboursMap.get(upperNeighbour);
LevelNeighbours lnun = getLevelNeighbour(upperNeighbour);
lnun.lowerLevel.remove(w);
lnun.sameLevel.add(w);
if (lnun.lowerLevel.isEmpty()) {
Expand All @@ -296,7 +308,7 @@ public void next() {

@Override
public boolean isHalted() {
return verticesToUpdate.isEmpty();
return init && verticesToUpdate.isEmpty();
}

public void undoChanges() {
Expand Down
92 changes: 92 additions & 0 deletions src/test/java/com/powsybl/openloadflow/graph/ConnectivityTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2021, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.openloadflow.graph;

import com.powsybl.iidm.network.Bus;
import com.powsybl.iidm.network.Network;
import com.powsybl.openloadflow.network.*;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* @author Gaël Macherel <gael.macherel at artelys.com>
*/
class ConnectivityTest {

@Test
void testConnectivity() {
testConnectivity(new NaiveGraphDecrementalConnectivity<>(LfBus::getNum));
testConnectivity(new EvenShiloachGraphDecrementalConnectivity<>());
}

private void testConnectivity(GraphDecrementalConnectivity<LfBus> connectivity) {
Network network = new ConnectedFactory().createThreeCcLinkedByASingleBus();
List<LfNetwork> lfNetworks = LfNetwork.load(network, new FirstSlackBusSelector());
LfNetwork lfNetwork = lfNetworks.get(0);

for (LfBus lfBus : lfNetwork.getBuses()) {
connectivity.addVertex(lfBus);
}

for (LfBranch lfBranch : lfNetwork.getBranches()) {
connectivity.addEdge(lfBranch.getBus1(), lfBranch.getBus2());
}

List<LfBranch> branchesToCut = Arrays.asList(lfNetwork.getBranchById("l34"), lfNetwork.getBranchById("l48"));
branchesToCut.forEach(lfBranch -> connectivity.cut(lfBranch.getBus2(), lfBranch.getBus1()));

assertEquals(1, connectivity.getComponentNumber(lfNetwork.getBusById("b3_vl_0")));
assertEquals(0, connectivity.getComponentNumber(lfNetwork.getBusById("b4_vl_0")));
assertEquals(2, connectivity.getComponentNumber(lfNetwork.getBusById("b8_vl_0")));
assertEquals(2, connectivity.getSmallComponents().size());
}

public static class ConnectedFactory extends AbstractLoadFlowNetworkFactory {
public Network createThreeCcLinkedByASingleBus() {
Network network = Network.create("test", "code");
Bus b1 = createBus(network, "b1");
Bus b2 = createBus(network, "b2");
Bus b3 = createBus(network, "b3");
Bus b4 = createBus(network, "b4");
Bus b5 = createBus(network, "b5");
Bus b6 = createBus(network, "b6");
Bus b7 = createBus(network, "b7");
Bus b8 = createBus(network, "b8");
Bus b9 = createBus(network, "b9");
Bus b10 = createBus(network, "b10");
createLine(network, b1, b2, "l12", 0.1f);
createLine(network, b1, b3, "l13", 0.1f);
createLine(network, b2, b3, "l23", 0.1f);
createLine(network, b3, b4, "l34", 0.1f);
createLine(network, b4, b5, "l45", 0.1f);
createLine(network, b5, b6, "l56", 0.1f);
createLine(network, b5, b7, "l57", 0.1f);
createLine(network, b6, b7, "l67", 0.1f);
createLine(network, b4, b8, "l48", 0.1f);
createLine(network, b8, b9, "l89", 0.1f);
createLine(network, b8, b10, "l810", 0.1f);
createLine(network, b9, b10, "l910", 0.1f);

createGenerator(b2, "g2", 3);
createGenerator(b6, "g6", 2);
createGenerator(b10, "g10", 4);
createLoad(b1, "d1", 1);
createLoad(b3, "d3", 1);
createLoad(b4, "d4", 1);
createLoad(b5, "d5", 2);
createLoad(b7, "d7", 2);
createLoad(b8, "d8", 1);
createLoad(b9, "d9", 1);

return network;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,18 @@
import com.powsybl.security.LimitViolationFilter;
import com.powsybl.security.SecurityAnalysisParameters;
import com.powsybl.security.detectors.DefaultLimitViolationDetector;
import org.junit.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Provider;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* @author Florian Dupuy <florian.dupuy at rte-france.com>
*/
Expand All @@ -48,7 +47,6 @@ class OpenSecurityAnalysisGraphTest {
private static final Logger LOGGER = LoggerFactory.getLogger(OpenSecurityAnalysisGraphTest.class);

private Network network;
private List<List<LfContingency>> reference;
private ContingenciesProvider contingenciesProvider;
private SecurityAnalysisParameters securityAnalysisParameters;

Expand All @@ -66,45 +64,66 @@ void setUp() {
lfParameters.addExtension(OpenLoadFlowParameters.class,
new OpenLoadFlowParameters().setSlackBusSelector(new FirstSlackBusSelector()));
securityAnalysisParameters = new SecurityAnalysisParameters().setLoadFlowParameters(lfParameters);

reference = getLoadFlowContingencies(() -> new NaiveGraphDecrementalConnectivity<>(LfBus::getNum));
LOGGER.info("Reference established (naive connectivity calculation) on test network containing {} branches", network.getBranchCount());
}

@Test
void testEvenShiloach() {
LOGGER.info("Test Even-Shiloach on test network containing {} branches", network.getBranchCount());
List<List<LfContingency>> lfContingencies = getLoadFlowContingencies(EvenShiloachGraphDecrementalConnectivity::new);
printResult(lfContingencies);
checkResult(lfContingencies);
checkResult(lfContingencies, computeReference());
}

@Test
void testMst() {
LOGGER.info("Test Minimum Spanning Tree on test network containing {} branches", network.getBranchCount());
List<List<LfContingency>> lfContingencies = getLoadFlowContingencies(MinimumSpanningTreeGraphDecrementalConnectivity::new);
printResult(lfContingencies);
checkResult(lfContingencies);
checkResult(lfContingencies, computeReference());
}

private List<List<LfContingency>> computeReference() {
List<List<LfContingency>> result = getLoadFlowContingencies(() -> new NaiveGraphDecrementalConnectivity<>(LfBus::getNum));
LOGGER.info("Reference established (naive connectivity calculation) on test network containing {} branches", network.getBranchCount());
return result;
}

@Test
void testNullVertices() {
network.getSwitch("B3").setOpen(true);
contingenciesProvider = n -> Collections.singletonList(
new Contingency("L1", new BranchContingency("L1")));
List<List<LfContingency>> reference = computeReference();
checkResult(getLoadFlowContingencies(MinimumSpanningTreeGraphDecrementalConnectivity::new), reference);
checkResult(getLoadFlowContingencies(EvenShiloachGraphDecrementalConnectivity::new), reference);

contingenciesProvider = n -> Collections.singletonList(
new Contingency("L2", new BranchContingency("L2")));
network.getSwitch("B3").setOpen(false);
network.getSwitch("B1").setOpen(true);
reference = computeReference();
checkResult(getLoadFlowContingencies(MinimumSpanningTreeGraphDecrementalConnectivity::new), reference);
checkResult(getLoadFlowContingencies(EvenShiloachGraphDecrementalConnectivity::new), reference);
}

private void checkResult(List<List<LfContingency>> result) {
Assert.assertEquals(reference.size(), result.size());
private static void checkResult(List<List<LfContingency>> result, List<List<LfContingency>> reference) {
assertEquals(reference.size(), result.size());
for (int iNetwork = 0; iNetwork < result.size(); iNetwork++) {
Assert.assertEquals(reference.get(iNetwork).size(), result.get(iNetwork).size());
assertEquals(reference.get(iNetwork).size(), result.get(iNetwork).size());
for (int iContingency = 0; iContingency < result.get(iNetwork).size(); iContingency++) {
LfContingency contingencyReference = reference.get(iNetwork).get(iContingency);
LfContingency contingencyResult = result.get(iNetwork).get(iContingency);
Assert.assertEquals(contingencyReference.getContingency().getId(), contingencyResult.getContingency().getId());
assertEquals(contingencyReference.getContingency().getId(), contingencyResult.getContingency().getId());

Set<LfBranch> branchesReference = contingencyReference.getBranches();
Set<LfBranch> branchesResult = contingencyResult.getBranches();
Assert.assertEquals(branchesReference.size(), branchesResult.size());
branchesReference.forEach(b -> Assert.assertTrue(branchesResult.stream().anyMatch(b1 -> b1.getId().equals(b.getId()))));
assertEquals(branchesReference.size(), branchesResult.size());
branchesReference.forEach(b -> assertTrue(branchesResult.stream().anyMatch(b1 -> b1.getId().equals(b.getId()))));

Set<LfBus> busesReference = contingencyReference.getBuses();
Set<LfBus> busesResult = contingencyResult.getBuses();
Assert.assertEquals(busesReference.size(), busesResult.size());
busesReference.forEach(b -> Assert.assertTrue(busesResult.stream().anyMatch(b1 -> b1.getId().equals(b.getId()))));
assertEquals(busesReference.size(), busesResult.size());
busesReference.forEach(b -> assertTrue(busesResult.stream().anyMatch(b1 -> b1.getId().equals(b.getId()))));
}
}
}
Expand Down