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

Add parameter to disable/enable internal 2wt #594

Merged
merged 6 commits into from
Mar 15, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import com.powsybl.sld.model.graphs.*;
import com.powsybl.sld.model.nodes.*;
import com.powsybl.sld.postprocessor.GraphBuildPostProcessor;
import org.jgrapht.alg.connectivity.ConnectivityInspector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -109,8 +108,6 @@ private void buildGraph(VoltageLevelGraph graph, VoltageLevel vl) {
LOGGER.info("{} nodes, {} edges", graph.getNodes().size(), graph.getEdges().size());

handleGraphPostProcessors(graph);

handleConnectedComponents(graph);
}

private void addBranchEdges(VoltageLevelGraph graph, VoltageLevel vl) {
Expand Down Expand Up @@ -262,10 +259,6 @@ private FeederNode createFeederLccNode(VoltageLevelGraph graph, HvdcConverterSta
.orElseGet(() -> NodeFactory.createLccConverterStationInjection(graph, hvdcStation.getId(), hvdcStation.getNameOrId()));
}

private Node createInternal2wtSideNode(VoltageLevelGraph graph, TwoWindingsTransformer branch, TwoSides side) {
return NodeFactory.createConnectivityNode(graph, branch.getId() + "_" + side.name(), NODE);
}

private FeederNode createFeeder2wtNode(VoltageLevelGraph graph, TwoWindingsTransformer branch, TwoSides side) {
String id = branch.getId() + "_" + side.name();
String name = branch.getNameOrId();
Expand Down Expand Up @@ -410,9 +403,7 @@ public void visitStaticVarCompensator(StaticVarCompensator svc) {

@Override
public void visitTwoWindingsTransformer(TwoWindingsTransformer transformer, TwoSides side) {
Node transformerNode = isInternalToVoltageLevel(transformer)
? createInternal2wtSideNode(graph, transformer, side)
: createFeeder2wtNode(graph, transformer, side);
Node transformerNode = createFeeder2wtNode(graph, transformer, side);
addTerminalNode(transformerNode, transformer.getTerminal(side));
}

Expand Down Expand Up @@ -624,34 +615,6 @@ private void ensureNodeExists(VoltageLevelGraph graph, int n, Map<Integer, Node>
nodesByNumber.computeIfAbsent(n, k -> NodeFactory.createConnectivityNode(graph, String.valueOf(k)));
}

/**
* Check if the graph is connected or not
*/
private void handleConnectedComponents(VoltageLevelGraph graph) {
List<Set<Node>> connectedSets = new ConnectivityInspector<>(graph.toJgrapht()).connectedSets();
if (connectedSets.size() != 1) {
LOGGER.warn("{} connected components found", connectedSets.size());
connectedSets.stream()
.sorted(Comparator.comparingInt(Set::size))
.map(setNodes -> setNodes.stream().map(Node::getId).collect(Collectors.toSet()))
.forEach(strings -> LOGGER.warn(" - {}", strings));
}
connectedSets.forEach(s -> ensureOneBusInConnectedComponent(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"));
}

/**
* Discover and apply postprocessor plugins to add custom nodes
**/
Expand Down Expand Up @@ -705,16 +668,7 @@ private boolean addLineEdge(Graph graph, String lineId, Terminal t1, Terminal t2
return isNotNull;
}

private void add2wtEdges(VoltageLevelGraph graph, List<TwoWindingsTransformer> twoWindingsTransformers) {
for (TwoWindingsTransformer transfo : twoWindingsTransformers) {
Node n1 = graph.getNode(transfo.getId() + "_" + TwoSides.ONE);
Node n2 = graph.getNode(transfo.getId() + "_" + TwoSides.TWO);
NodeFactory.createInternal2WTNode(graph, transfo.getId(), transfo.getNameOrId(),
n1, n2, transfo.hasPhaseTapChanger());
}
}

private void add2wtEdges(SubstationGraph graph, List<TwoWindingsTransformer> twoWindingsTransformers) {
private void add2wtEdges(BaseGraph graph, List<TwoWindingsTransformer> twoWindingsTransformers) {
for (TwoWindingsTransformer transfo : twoWindingsTransformers) {
Terminal t1 = transfo.getTerminal1();
Terminal t2 = transfo.getTerminal2();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@
*/
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.*;
import com.powsybl.sld.model.nodes.BusNode;
import com.powsybl.sld.model.nodes.Node;
import org.jgrapht.alg.connectivity.ConnectivityInspector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
Expand All @@ -21,15 +28,23 @@
* @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
*/
public class GraphRefiner {
private static final Logger LOGGER = LoggerFactory.getLogger(GraphRefiner.class);
private final boolean removeUnnecessaryFictitiousNodes;
private final boolean substituteSingularFictitiousByFeederNode;
private final boolean substituteInternalMiddle2wtByEquipmentNodes;

public GraphRefiner(boolean removeUnnecessaryFictitiousNodes, boolean substituteSingularFictitiousByFeederNode) {
public GraphRefiner(boolean removeUnnecessaryFictitiousNodes, boolean substituteSingularFictitiousByFeederNode,
boolean substituteInternalMiddle2wtByEquipmentNodes) {
this.removeUnnecessaryFictitiousNodes = removeUnnecessaryFictitiousNodes;
this.substituteSingularFictitiousByFeederNode = substituteSingularFictitiousByFeederNode;
this.substituteInternalMiddle2wtByEquipmentNodes = substituteInternalMiddle2wtByEquipmentNodes;
}

void run(VoltageLevelGraph graph, LayoutParameters layoutParameters) {
if (substituteInternalMiddle2wtByEquipmentNodes) {
graph.substituteInternalMiddle2wtByEquipmentNodes();
}
handleConnectedComponents(graph);
graph.substituteFictitiousNodesMirroringBusNodes();
if (removeUnnecessaryFictitiousNodes) {
graph.removeUnnecessaryConnectivityNodes();
Expand All @@ -51,6 +66,34 @@ void run(VoltageLevelGraph graph, LayoutParameters layoutParameters) {
graph.substituteNodesMirroringGroundDisconnectionComponent();
}

/**
* Check if the graph is connected or not
*/
private void handleConnectedComponents(VoltageLevelGraph graph) {
List<Set<Node>> connectedSets = new ConnectivityInspector<>(graph.toJgrapht()).connectedSets();
if (connectedSets.size() != 1) {
LOGGER.warn("{} connected components found", connectedSets.size());
connectedSets.stream()
.sorted(Comparator.comparingInt(Set::size))
.map(setNodes -> setNodes.stream().map(Node::getId).collect(Collectors.toSet()))
.forEach(strings -> LOGGER.warn(" - {}", strings));
}
connectedSets.forEach(s -> ensureOneBusInConnectedComponent(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 Predicate<Node> getNodesOnBusPredicate(VoltageLevelGraph graph, List<String> componentsOnBusbars) {
Set<Node> nodesOnBusBetweenBuses = getNodesOnBusBetweenBuses(graph, componentsOnBusbars);
return node -> componentsOnBusbars.contains(node.getComponentType()) && !nodesOnBusBetweenBuses.contains(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ public PositionVoltageLevelLayoutFactory(PositionFinder positionFinder, Position
@Override
public Layout create(VoltageLevelGraph graph) {
// For adapting the graph to the diagram layout
GraphRefiner graphRefiner = new GraphRefiner(positionVoltageLevelLayoutFactoryParameters.isRemoveUnnecessaryFictitiousNodes(), positionVoltageLevelLayoutFactoryParameters.isSubstituteSingularFictitiousByFeederNode());
GraphRefiner graphRefiner = new GraphRefiner(
positionVoltageLevelLayoutFactoryParameters.isRemoveUnnecessaryFictitiousNodes(),
positionVoltageLevelLayoutFactoryParameters.isSubstituteSingularFictitiousByFeederNode(),
positionVoltageLevelLayoutFactoryParameters.isSubstituteInternalMiddle2wtByEquipmentNodes());

// For cell detection
ImplicitCellDetector cellDetector = new ImplicitCellDetector(positionVoltageLevelLayoutFactoryParameters.isExceptionIfPatternNotHandled());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class PositionVoltageLevelLayoutFactoryParameters {
private boolean exceptionIfPatternNotHandled = false;
private boolean handleShunts = false;
private Map<String, Side> busInfoMap = new HashMap<>();
private boolean substituteInternalMiddle2wtByEquipmentNodes = true;

public boolean isFeederStacked() {
return feederStacked;
Expand Down Expand Up @@ -79,4 +80,13 @@ public PositionVoltageLevelLayoutFactoryParameters setBusInfoMap(Map<String, Sid
this.busInfoMap = busInfoMap;
return this;
}

public boolean isSubstituteInternalMiddle2wtByEquipmentNodes() {
return substituteInternalMiddle2wtByEquipmentNodes;
}

public PositionVoltageLevelLayoutFactoryParameters setSubstituteInternalMiddle2wtByEquipmentNodes(boolean substituteInternalMiddle2wtByEquipmentNodes) {
this.substituteInternalMiddle2wtByEquipmentNodes = substituteInternalMiddle2wtByEquipmentNodes;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,8 @@ public static SwitchNode createSwitchNode(VoltageLevelGraph graph, String id, St
return sn;
}

public static EquipmentNode createInternal2WTNode(VoltageLevelGraph graph, String id, String nameOrId, Node n1, Node n2, boolean hasPhaseTapChanger) {
String component = hasPhaseTapChanger ? PHASE_SHIFT_TRANSFORMER : TWO_WINDINGS_TRANSFORMER;
EquipmentNode i2wt = new Internal2WTNode(id, nameOrId, component);
public static EquipmentNode createInternal2WTNode(VoltageLevelGraph graph, String id, String nameOrId, String equipmentId, Node n1, Node n2, String component) {
EquipmentNode i2wt = new Internal2WTNode(id, nameOrId, equipmentId, component);
graph.addNode(i2wt);
graph.addEdge(n1, i2wt);
graph.addEdge(n2, i2wt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.powsybl.sld.model.coordinate.Point;
import com.powsybl.sld.model.nodes.*;
import com.powsybl.sld.model.nodes.Node.NodeType;
import com.powsybl.sld.model.nodes.feeders.FeederTwLeg;
import org.jgrapht.graph.Pseudograph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -496,6 +497,10 @@ private Stream<FeederNode> getFeederNodeStream() {
.map(FeederNode.class::cast);
}

private Stream<FeederNode> getFeederNodeStream(FeederType feederType) {
return getFeederNodeStream().filter(feederNode -> feederNode.getFeeder().getFeederType() == feederType);
}

public List<Node> getNodes() {
return new ArrayList<>(nodes);
}
Expand Down Expand Up @@ -661,7 +666,7 @@ public double getInnerHeight(double verticalSpaceBus) {
}

public void substituteNodesMirroringGroundDisconnectionComponent() {
List<GroundDisconnection> groundDisconnections = getFeederNodeStream().filter(feederNode -> feederNode.getFeeder().getFeederType() == FeederType.GROUND)
List<GroundDisconnection> groundDisconnections = getFeederNodeStream(FeederType.GROUND)
.map(this::getGroundDisconnection)
.filter(Objects::nonNull)
.toList();
Expand All @@ -687,6 +692,44 @@ private GroundDisconnection getGroundDisconnection(FeederNode groundFeederNode)
return null;
}

public void substituteInternalMiddle2wtByEquipmentNodes() {
for (FeederNode feederNode : getFeederNodeStream(FeederType.TWO_WINDINGS_TRANSFORMER_LEG).toList()) {
if (nodesById.get(feederNode.getId()) == feederNode) { // check if not already removed
substituteInternalMiddle2wtByEquipmentNode(feederNode);
}
}
}

private void substituteInternalMiddle2wtByEquipmentNode(FeederNode feederNode) {
MiddleTwtNode middleNode = getMultiTermNodes().stream()
.filter(m2wtn -> m2wtn.getAdjacentNodes().stream().anyMatch(n -> n == feederNode))
.findFirst().orElse(null);
Node otherSideNode = Optional.ofNullable(middleNode)
.flatMap(m2wtn -> m2wtn.getAdjacentNodes().stream().filter(n -> n != feederNode).findFirst())
.orElse(null);
if (middleNode instanceof Middle2WTNode middle2WTNode
&& otherSideNode instanceof FeederNode otherSideFeederNode
&& otherSideFeederNode.getFeeder() instanceof FeederTwLeg otherSideFeederTwLeg
&& otherSideFeederTwLeg.getOwnVoltageLevelInfos().getId().equals(voltageLevelInfos.getId())) {

ConnectivityNode connectivityNodeA = NodeFactory.createConnectivityNode(this, feederNode.getId(), ComponentTypeName.NODE);
ConnectivityNode connectivityNodeB = NodeFactory.createConnectivityNode(this, otherSideNode.getId(), ComponentTypeName.NODE);

// substitute nodes in voltage level
substituteNode(feederNode, connectivityNodeA);
substituteNode(otherSideNode, connectivityNodeB);

// substitute the node which was "outside" the voltage level (multiTermNode / snake line) by a node inside the voltage level
ConnectivityNode connectivityNode1 = otherSideFeederTwLeg.getSide() == NodeSide.ONE ? connectivityNodeB : connectivityNodeA;
ConnectivityNode connectivityNode2 = otherSideFeederTwLeg.getSide() == NodeSide.ONE ? connectivityNodeA : connectivityNodeB;
NodeFactory.createInternal2WTNode(this,
middle2WTNode.getId(), middle2WTNode.getName(), middle2WTNode.getEquipmentId(),
connectivityNode1, connectivityNode2, middle2WTNode.getComponentType());
multiTermNodes.remove(middleNode);
twtEdges.removeAll(middleNode.getAdjacentEdges());
}
}

private record GroundDisconnection(List<Node> nodes, FeederNode ground, SwitchNode disconnector, Node forkNode) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
* @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
*/
public class Internal2WTNode extends EquipmentNode {
public Internal2WTNode(String id, String nameOrId, String componentType) {
super(NodeType.INTERNAL, id, nameOrId, id, componentType, false);
public Internal2WTNode(String id, String nameOrId, String equipmentId, String componentType) {
super(NodeType.INTERNAL, id, nameOrId, equipmentId, componentType, false);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
import com.powsybl.diagram.test.Networks;
import com.powsybl.sld.builders.NetworkGraphBuilder;
import com.powsybl.sld.layout.PositionVoltageLevelLayoutFactory;
import com.powsybl.sld.layout.PositionVoltageLevelLayoutFactoryParameters;
import com.powsybl.sld.layout.VerticalSubstationLayoutFactory;
import com.powsybl.sld.layout.positionfromextension.PositionFromExtension;
import com.powsybl.sld.model.graphs.SubstationGraph;
import com.powsybl.sld.model.graphs.VoltageLevelGraph;
import com.powsybl.sld.svg.DefaultSVGWriter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

Expand All @@ -36,14 +37,23 @@ void testVLGraph() {
// build graph
VoltageLevelGraph g = graphBuilder.buildVoltageLevelGraph(network.getVoltageLevel("VL1").getId());

// Run layout
// Run layout with default parameters and compare subsequent SVG with reference
voltageLevelGraphLayout(g);

// write SVG and compare to reference
DefaultSVGWriter defaultSVGWriter = new DefaultSVGWriter(componentLibrary, layoutParameters, svgParameters);
assertEquals(toString("/InternalBranchesNodeBreaker.svg"),
toSVG(g, "/InternalBranchesNodeBreaker.svg", componentLibrary, layoutParameters, svgParameters, getDefaultDiagramLabelProvider(), getDefaultDiagramStyleProvider()));
}

@Test
void testVLGraphExternal2WT() {
// build graph
VoltageLevelGraph g = graphBuilder.buildVoltageLevelGraph(network.getVoltageLevel("VL1").getId());

// Run layout with specific parameters and compare subsequent SVG with reference
PositionVoltageLevelLayoutFactoryParameters pvllfParameters = new PositionVoltageLevelLayoutFactoryParameters()
.setSubstituteInternalMiddle2wtByEquipmentNodes(false);
new PositionVoltageLevelLayoutFactory(new PositionFromExtension(), pvllfParameters).create(g).run(this.layoutParameters);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
new PositionVoltageLevelLayoutFactory(new PositionFromExtension(), pvllfParameters).create(g).run(this.layoutParameters);
new PositionVoltageLevelLayoutFactory(pvllfParameters).create(g).run(this.layoutParameters);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to put this explicitly, as this default choice does not seem clear to me. And I'd like to remove one day that constructor which assumes that it should be a PositionFromExtension by default (without any javadoc specifying it!)

assertEquals(toString("/InternalBranchesNodeBreaker_externalPst.svg"),
toSVG(g, "/InternalBranchesNodeBreaker_externalPst.svg", componentLibrary, layoutParameters, svgParameters, getDefaultDiagramLabelProvider(), getDefaultDiagramStyleProvider()));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ void test() {
parameters.setSubstituteSingularFictitiousByFeederNode(false);
assertFalse(parameters.isSubstituteSingularFictitiousByFeederNode());

assertTrue(parameters.isSubstituteInternalMiddle2wtByEquipmentNodes());
parameters.setSubstituteInternalMiddle2wtByEquipmentNodes(false);
assertFalse(parameters.isSubstituteInternalMiddle2wtByEquipmentNodes());

assertFalse(parameters.isExceptionIfPatternNotHandled());
parameters.setExceptionIfPatternNotHandled(true);
assertTrue(parameters.isExceptionIfPatternNotHandled());
Expand Down