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

Fix empty node set exception #596

Merged
merged 8 commits into from
Mar 26, 2024
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
101 changes: 101 additions & 0 deletions diagram-test/src/main/java/com/powsybl/diagram/test/Networks.java
Original file line number Diff line number Diff line change
Expand Up @@ -1724,6 +1724,10 @@ public static VoltageLevel createVoltageLevel(Substation s, String id, String na
.add();
}

public static void createSwitch(VoltageLevel vl, String id, SwitchKind kind, boolean retained, boolean open, boolean fictitious, int node1, int node2) {
createSwitch(vl, id, id, kind, retained, open, fictitious, node1, node2);
}

public static void createSwitch(VoltageLevel vl, String id, String name, SwitchKind kind, boolean retained, boolean open, boolean fictitious, int node1, int node2) {
vl.getNodeBreakerView().newSwitch()
.setId(id)
Expand All @@ -1744,6 +1748,10 @@ public static void createInternalConnection(VoltageLevel vl, int node1, int node
.add();
}

public static void createBusBarSection(VoltageLevel vl, String id, int node, int busbarIndex, int sectionIndex) {
createBusBarSection(vl, id, id, node, busbarIndex, sectionIndex);
}

public static void createBusBarSection(VoltageLevel vl, String id, String name, int node, int busbarIndex, int sectionIndex) {
BusbarSection bbs = vl.getNodeBreakerView().newBusbarSection()
.setId(id)
Expand All @@ -1756,6 +1764,11 @@ public static void createBusBarSection(VoltageLevel vl, String id, String name,
.add();
}

public static void createLoad(VoltageLevel vl, String id, Integer feederOrder,
ConnectablePosition.Direction direction, int node, double p0, double q0) {
createLoad(vl, id, id, id, feederOrder, direction, node, p0, q0);
}

public static void createLoad(VoltageLevel vl, String id, String name, String feederName, Integer feederOrder,
ConnectablePosition.Direction direction, int node, double p0, double q0) {
Load load = vl.newLoad()
Expand All @@ -1768,6 +1781,13 @@ public static void createLoad(VoltageLevel vl, String id, String name, String fe
addFeederPosition(load, feederName, feederOrder, direction);
}

public static void createGenerator(VoltageLevel vl, String id, Integer feederOrder,
ConnectablePosition.Direction direction, int node,
double minP, double maxP, boolean voltageRegulator,
double targetP, double targetQ) {
createGenerator(vl, id, id, id, feederOrder, direction, node, minP, maxP, voltageRegulator, targetP, targetQ);
}

public static void createGenerator(VoltageLevel vl, String id, String name, String feederName, Integer feederOrder,
ConnectablePosition.Direction direction, int node,
double minP, double maxP, boolean voltageRegulator,
Expand Down Expand Up @@ -2264,6 +2284,87 @@ public static Network createNetworkWithComplexInternCellDifferentSubsections() {
return network;
}

/**
* <pre>
* vl1 vl2
* | |
* L1 | | L2
* | |
* --*--- Fictitious busbar section
* |
* L3 |
* |
* vl3
*
* </pre>
*/
public static Network createTeePointNetwork() {
Network network = Network.create("testCase1", "test");
VoltageLevel vl = network.newVoltageLevel()
.setId("vl")
.setNominalV(50)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();

VoltageLevel vl1 = network.newVoltageLevel()
.setId("vl1")
.setNominalV(10)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();

VoltageLevel vl2 = network.newVoltageLevel()
.setId("vl2")
.setNominalV(30)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();

VoltageLevel vl3 = network.newVoltageLevel()
.setId("vl3")
.setNominalV(10)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();

createInternalConnection(vl, 1, 0);
createInternalConnection(vl, 2, 0);
createInternalConnection(vl, 3, 0);

createLine(network, "L1", "L1", 1.0, 1.0, 1.0, 0.0, 0.0, 0.0,
1, 10, vl.getId(), vl1.getId(),
"L1", 0, ConnectablePosition.Direction.TOP,
"L1", 1, ConnectablePosition.Direction.TOP);

createLine(network, "L2", "L2", 1.0, 1.0, 1.0, 0.0, 0.0, 0.0,
2, 20, vl.getId(), vl2.getId(),
"L2", 1, ConnectablePosition.Direction.BOTTOM,
"L2", 0, ConnectablePosition.Direction.TOP);

createLine(network, "L3", "L3", 1.0, 1.0, 1.0, 0.0, 0.0, 0.0,
3, 30, vl.getId(), vl3.getId(),
"L3", 2, ConnectablePosition.Direction.TOP,
"L3", 0, ConnectablePosition.Direction.TOP);

return network;
}

public static Network createDanglingConnectablesNetwork() {
Network network = Network.create("testDLoad", "testDLoad");
VoltageLevel vl = network.newVoltageLevel()
.setId("vl")
.setNominalV(50)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();
createBusBarSection(vl, "bbs1", 0, 1, 1);
createBusBarSection(vl, "bbs2", 1, 1, 2);
createSwitch(vl, "d", SwitchKind.DISCONNECTOR, true, false, false, 0, 2);
createSwitch(vl, "d12", SwitchKind.DISCONNECTOR, true, false, false, 0, 1);
createSwitch(vl, "ddl2", SwitchKind.DISCONNECTOR, true, false, false, 4, 5);
createLoad(vl, "load", 1, ConnectablePosition.Direction.TOP, 2, 0, 0);
createLoad(vl, "dLoad1", 2, ConnectablePosition.Direction.BOTTOM, 3, 0, 0);
createLoad(vl, "dLoad2", 0, ConnectablePosition.Direction.TOP, 4, 10, 0);
createGenerator(vl, "dGen", null, ConnectablePosition.Direction.TOP, 5, 50, 100, false, 100, 400);
return network;
}

public static void createLine(Bus bus1, Bus bus2) {
String id = String.format("%s - %s",
bus1.getVoltageLevel().getSubstation().orElseThrow().getId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
package com.powsybl.sld.layout;

import com.powsybl.commons.PowsyblException;
import com.powsybl.sld.model.graphs.NodeFactory;
import com.powsybl.sld.model.graphs.VoltageLevelGraph;
import com.powsybl.sld.model.nodes.BusNode;
Expand Down Expand Up @@ -78,20 +77,29 @@ private void handleConnectedComponents(VoltageLevelGraph graph) {
.map(setNodes -> setNodes.stream().map(Node::getId).collect(Collectors.toSet()))
.forEach(strings -> LOGGER.warn(" - {}", strings));
}
connectedSets.forEach(s -> ensureOneBusInConnectedComponent(graph, s));
// Add a fictitious bus for all connected components without any bus
connectedSets.stream()
.filter(s -> s.stream().noneMatch(node -> node.getType() == Node.NodeType.BUS))
.forEach(s -> addFictitiousBusInConnectedComponent(graph, s));
}

private void ensureOneBusInConnectedComponent(VoltageLevelGraph graph, Set<Node> nodes) {
if (nodes.stream().anyMatch(node -> node.getType() == Node.NodeType.BUS)) {
return;
}
Node biggestFn = nodes.stream()
.filter(node -> node.getType() == Node.NodeType.INTERNAL)
.min(Comparator.<Node>comparingInt(node -> node.getAdjacentEdges().size())
.reversed()
.thenComparing(Node::getId)) // for stable fictitious node selection, also sort on id
.orElseThrow(() -> new PowsyblException("Empty node set"));
graph.substituteNode(biggestFn, NodeFactory.createFictitiousBusNode(graph, biggestFn.getId() + "FictitiousBus"));
private void addFictitiousBusInConnectedComponent(VoltageLevelGraph graph, Set<Node> nodes) {
// Replace the most meshed fictitious node by a fictitious BusNode.
// If no fictitious node, insert/add a fictitious BusNode at the most meshed node of the set.
Comparator<Node> mostMeshedComparator = Comparator.<Node>comparingInt(node -> node.getAdjacentEdges().size()).reversed().thenComparing(Node::getId); // for stable fictitious node selection, also sort on id
int sectionIndex = 1 + graph.getNodeBuses().stream().mapToInt(BusNode::getSectionIndex).max().orElse(0);
nodes.stream().filter(node -> node.getType() == Node.NodeType.INTERNAL)
.min(mostMeshedComparator)
.ifPresentOrElse(
mostMeshedFictitiousNode -> {
BusNode busNode = NodeFactory.createFictitiousBusNode(graph, mostMeshedFictitiousNode.getId() + "_FictitiousBus", 1, sectionIndex);
graph.substituteNode(mostMeshedFictitiousNode, busNode);
},
() -> {
Node mostMeshedNode = nodes.stream().min(mostMeshedComparator).orElseThrow(); // always non-empty set
BusNode busNode = NodeFactory.createFictitiousBusNode(graph, mostMeshedNode.getId() + "_FictitiousBus", 1, sectionIndex);
graph.insertNodeNextTo(busNode, mostMeshedNode);
});
}

private Predicate<Node> getNodesOnBusPredicate(VoltageLevelGraph graph, List<String> componentsOnBusbars) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ public static BusNode createBusNode(VoltageLevelGraph graph, String id, String n
return bn;
}

public static BusNode createFictitiousBusNode(VoltageLevelGraph graph, String id) {
public static BusNode createFictitiousBusNode(VoltageLevelGraph graph, String id, int busbarIndex, int sectionIndex) {
BusNode bn = new BusNode(id, null, true);
bn.setBusBarIndexSectionIndex(1, 1);
bn.setBusBarIndexSectionIndex(busbarIndex, sectionIndex);
graph.addNode(bn);
return bn;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,15 @@ private void substituteInternalMiddle2wtByEquipmentNode(FeederNode feederNode) {
}
}

public void insertNodeNextTo(Node nodeToInsert, Node adjacentNode) {
List<Node> neighbours = adjacentNode.getAdjacentNodes();
if (neighbours.isEmpty()) {
addEdge(nodeToInsert, adjacentNode);
} else {
insertNode(adjacentNode, nodeToInsert, neighbours.get(0));
}
}

private record GroundDisconnection(List<Node> nodes, FeederNode ground, SwitchNode disconnector, Node forkNode) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,12 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.regex.Pattern;

/**
* @author Benoit Jeanson {@literal <benoit.jeanson at rte-france.com>}
*/
public abstract class AbstractTestCase {

private static final Pattern SVG_FIX_PATTERN = Pattern.compile(">\\s*(<\\!\\[CDATA\\[.*?]]>)\\s*</", Pattern.DOTALL);

protected boolean debugJsonFiles = false;
protected boolean debugSvgFiles = false;
protected boolean overrideTestReferences = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,94 +8,31 @@
package com.powsybl.sld.iidm;

import com.powsybl.diagram.test.Networks;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.TopologyKind;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.iidm.network.extensions.ConnectablePosition;
import com.powsybl.sld.builders.NetworkGraphBuilder;
import com.powsybl.sld.model.graphs.VoltageLevelGraph;
import com.powsybl.sld.svg.styles.iidm.TopologicalStyleProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

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

/**
* <pre>
* vl1 vl2
* | |
* L1 | | L2
* | |
* --*--- Fictitious busbar section
* |
* L3 |
* |
* vl3
*
* </pre>
*
* @author Thomas Adam {@literal <tadam at silicom.fr>}
*/
class TestCaseFictitiousBus extends AbstractTestCaseIidm {

private VoltageLevel vl1;
private VoltageLevel vl2;
private VoltageLevel vl3;

@BeforeEach
public void setUp() {
network = Network.create("testCase1", "test");
graphBuilder = new NetworkGraphBuilder(network);
substation = null;
vl = network.newVoltageLevel()
.setId("vl")
.setNominalV(50)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();

vl1 = network.newVoltageLevel()
.setId("vl1")
.setNominalV(10)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();

vl2 = network.newVoltageLevel()
.setId("vl2")
.setNominalV(30)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();

vl3 = network.newVoltageLevel()
.setId("vl3")
.setNominalV(10)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();

Networks.createInternalConnection(vl, 1, 0);
Networks.createInternalConnection(vl, 2, 0);
Networks.createInternalConnection(vl, 3, 0);

Networks.createLine(network, "L1", "L1", 1.0, 1.0, 1.0, 0.0, 0.0, 0.0,
1, 10, vl.getId(), vl1.getId(),
"L1", 0, ConnectablePosition.Direction.TOP,
"L1", 1, ConnectablePosition.Direction.TOP);

Networks.createLine(network, "L2", "L2", 1.0, 1.0, 1.0, 0.0, 0.0, 0.0,
2, 20, vl.getId(), vl2.getId(),
"L2", 1, ConnectablePosition.Direction.BOTTOM,
"L2", 0, ConnectablePosition.Direction.TOP);

Networks.createLine(network, "L3", "L3", 1.0, 1.0, 1.0, 0.0, 0.0, 0.0,
3, 30, vl.getId(), vl3.getId(),
"L3", 2, ConnectablePosition.Direction.TOP,
"L3", 0, ConnectablePosition.Direction.TOP);

@Override
public void setUp() throws IOException {
// no common setup
}

@Test
void testBasic() {
network = Networks.createTeePointNetwork();

// build graph
VoltageLevelGraph g = graphBuilder.buildVoltageLevelGraph(vl.getId());
VoltageLevelGraph g = new NetworkGraphBuilder(network).buildVoltageLevelGraph("vl");

// Run layout
voltageLevelGraphLayout(g);
Expand All @@ -107,8 +44,10 @@ void testBasic() {

@Test
void testTopological() {
network = Networks.createTeePointNetwork();

// build graph
VoltageLevelGraph g = graphBuilder.buildVoltageLevelGraph(vl.getId());
VoltageLevelGraph g = new NetworkGraphBuilder(network).buildVoltageLevelGraph("vl");

// Run layout
voltageLevelGraphLayout(g);
Expand All @@ -117,4 +56,19 @@ void testTopological() {
assertEquals(toString("/TestCaseFictitiousBusTopological.svg"),
toSVG(g, "/TestCaseFictitiousBusTopological.svg", componentLibrary, layoutParameters, svgParameters, getDefaultDiagramLabelProvider(), new TopologicalStyleProvider(network)));
}

@Test
void testDanglingConnectables() {
network = Networks.createDanglingConnectablesNetwork();

// build graph
VoltageLevelGraph g = new NetworkGraphBuilder(network).buildVoltageLevelGraph("vl");

// Run layout
voltageLevelGraphLayout(g);

// write Json and compare to reference
assertEquals(toString("/TestCaseFictitiousBusDanglingConnectables.svg"),
toSVG(g, "/TestCaseFictitiousBusDanglingConnectables.svg", componentLibrary, layoutParameters, svgParameters, getDefaultDiagramLabelProvider(), new TopologicalStyleProvider(network)));
}
}
Loading