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

Zone diagram (multiple substations) on a substation grid #550

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
04c26f7
First try
tadam50 Sep 26, 2023
c283b71
Add default matrix behaviour
tadam50 Sep 26, 2023
9cc4dc9
Display Substation with Snakelines draw from Substation Layout
tadam50 Sep 28, 2023
b5cfea2
Move snakelines & voltagelevel at the same time
tadam50 Oct 3, 2023
f614186
Using path finding algo
tadam50 Oct 5, 2023
b56af2a
Improve path finding algo
tadam50 Oct 10, 2023
48bad88
Refactor path finder usage
tadam50 Oct 20, 2023
93dd34f
Fix bugs
tadam50 Oct 23, 2023
58d3a77
Extract parent link
tadam50 Oct 24, 2023
67d2b27
Improve path finder usage
tadam50 Nov 30, 2023
1a3618b
Increase cost of right angles in path finding algo
tadam50 Dec 5, 2023
a6318ed
Refactor + make unavailable previous snakeline
tadam50 Dec 11, 2023
9f19897
Documentation
tadam50 Dec 11, 2023
08a06ef
Documentation: english
tadam50 Dec 12, 2023
6b34186
Increase code coverage
tadam50 Dec 12, 2023
1f502cd
Increase code coverage + cleaning code
tadam50 Dec 13, 2023
f0f40f6
Update documentation
tadam50 Dec 13, 2023
ce0a926
Set with & height zone graph without diagram padding
tadam50 Dec 13, 2023
254e36b
Use com.powsybl.sld.model.coordinate.Point class
tadam50 Jan 16, 2024
dbc5735
Taking into account PR comments
tadam50 Jan 16, 2024
88ba4b1
Replace Vertical and Horizontal ZoneLayout by MatrixZoneLayout
tadam50 Jan 16, 2024
0b70fb4
Fix sonar issues
tadam50 Jan 16, 2024
2dba3fc
Put snakeline margin into LayoutParameters
tadam50 Jan 17, 2024
98383ed
Add path finder type into LayoutParameters
tadam50 Jan 24, 2024
1b65ea7
Height is computated by row & width is computed by column
tadam50 Jan 25, 2024
fe3a0dc
Fix code smells
tadam50 Jan 25, 2024
f483a33
Taking into account PR comments : refactor path finder parametrized +…
tadam50 Jan 26, 2024
cf9c5cc
Taking into account PR comments : remove Dijskra factory
tadam50 Jan 26, 2024
1e43767
Separate availability & cost into Grid.Node
tadam50 Jan 26, 2024
46b5fc9
Fix bad computation of path finder grid
tadam50 Jan 29, 2024
16e5f24
Center substation into own matrix cell
tadam50 Jan 29, 2024
0d8a9ad
Fix code style
tadam50 Jan 29, 2024
7591084
Update tests files
tadam50 Jan 29, 2024
ffcec64
Improve snakeline available path from stating point
tadam50 Jan 29, 2024
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 @@ -7,23 +7,30 @@
*/
package com.powsybl.sld.layout;

import com.powsybl.sld.model.graphs.SubstationGraph;
import com.powsybl.sld.model.graphs.VoltageLevelGraph;
import com.powsybl.sld.model.graphs.ZoneGraph;
import com.powsybl.sld.layout.pathfinding.*;
import com.powsybl.sld.model.graphs.*;
import com.powsybl.sld.model.nodes.*;

import java.util.Objects;
import java.util.*;

/**
* @author Thomas Adam {@literal <tadam at silicom.fr>}
*/
public abstract class AbstractZoneLayout extends AbstractBaseLayout<ZoneGraph> {
protected SubstationLayoutFactory sLayoutFactory;
protected VoltageLevelLayoutFactory vLayoutFactory;
protected Map<BaseGraph, AbstractLayout<SubstationGraph>> layoutBySubstation;
protected PathFinder pathFinder;

protected AbstractZoneLayout(ZoneGraph graph, SubstationLayoutFactory sLayoutFactory, VoltageLevelLayoutFactory vLayoutFactory) {
super(graph);
this.sLayoutFactory = Objects.requireNonNull(sLayoutFactory);
this.vLayoutFactory = Objects.requireNonNull(vLayoutFactory);
this.layoutBySubstation = new HashMap<>();
for (SubstationGraph subGraph : getGraph().getSubstations()) {
Layout sLayout = sLayoutFactory.create(subGraph, vLayoutFactory);
layoutBySubstation.put(subGraph, (AbstractLayout<SubstationGraph>) sLayout);
}
}

@Override
Expand All @@ -37,9 +44,19 @@ public void run(LayoutParameters layoutParameters) {

protected abstract void calculateCoordSubstations(LayoutParameters layoutParameters);

protected void move(SubstationGraph subGraph, double dx, double dy) {
protected void move(BaseGraph subGraph, double dx, double dy) {
for (VoltageLevelGraph vlGraph : subGraph.getVoltageLevels()) {
vlGraph.setCoord(vlGraph.getX() + dx, vlGraph.getY() + dy);
vlGraph.getLineEdges().forEach(s -> s.shiftSnakeLine(dx, dy));
}
subGraph.getMultiTermNodes().forEach(node -> {
node.setCoordinates(node.getX() + dx, node.getY() + dy);
node.getAdjacentEdges().forEach(edge -> {
if (edge instanceof BranchEdge branch) {
branch.shiftSnakeLine(dx, dy);
}
});
});
subGraph.getLineEdges().forEach(s -> s.shiftSnakeLine(dx, dy));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
package com.powsybl.sld.layout;

import com.powsybl.sld.model.graphs.ZoneGraph;
import com.powsybl.sld.model.graphs.*;

/**
* @author Thomas Adam {@literal <tadam at silicom.fr>}
Expand All @@ -16,6 +16,6 @@ public class HorizontalZoneLayoutFactory implements ZoneLayoutFactory {

@Override
public Layout create(ZoneGraph graph, SubstationLayoutFactory sLayoutFactory, VoltageLevelLayoutFactory vLayoutFactory) {
return new HorizontalZoneLayout(graph, sLayoutFactory, vLayoutFactory);
return new MatrixZoneLayoutFactory().create(graph, sLayoutFactory, vLayoutFactory);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public class LayoutParameters {
private double cgmesScaleFactor = 1;
private String cgmesDiagramName = null;
private boolean cgmesUseNames = true;
private double zoneLayoutSnakeLinePadding = 90;
private PathFinderType zoneLayoutPathFinder = PathFinderType.DIJKSTRA;

@JsonIgnore
private Map<String, ComponentSize> componentsSize;
Expand Down Expand Up @@ -77,7 +79,9 @@ public LayoutParameters(@JsonProperty("verticalSpaceBus") double verticalSpaceBu
@JsonProperty("removeFictitiousSwitchNodes") boolean removeFictitiousSwitchNodes,
@JsonProperty("cgmesScaleFactor") double cgmesScaleFactor,
@JsonProperty("cgmesDiagramName") String cgmesDiagramName,
@JsonProperty("cgmesUseNames") boolean cgmesUseNames) {
@JsonProperty("cgmesUseNames") boolean cgmesUseNames,
@JsonProperty("zoneLayoutSnakeLinePadding") int zoneLayoutSnakeLinePadding,
@JsonProperty("zoneLayoutPathFinder") PathFinderType zoneLayoutPathFinder) {

this.verticalSpaceBus = verticalSpaceBus;
this.horizontalBusPadding = horizontalBusPadding;
Expand All @@ -100,6 +104,8 @@ public LayoutParameters(@JsonProperty("verticalSpaceBus") double verticalSpaceBu
this.cgmesDiagramName = cgmesDiagramName;
this.cgmesScaleFactor = cgmesScaleFactor;
this.cgmesUseNames = cgmesUseNames;
this.zoneLayoutSnakeLinePadding = zoneLayoutSnakeLinePadding;
this.zoneLayoutPathFinder = zoneLayoutPathFinder;
}

public LayoutParameters(LayoutParameters other) {
Expand All @@ -126,6 +132,8 @@ public LayoutParameters(LayoutParameters other) {
cgmesScaleFactor = other.cgmesScaleFactor;
cgmesDiagramName = other.cgmesDiagramName;
cgmesUseNames = other.cgmesUseNames;
zoneLayoutSnakeLinePadding = other.zoneLayoutSnakeLinePadding;
zoneLayoutPathFinder = other.zoneLayoutPathFinder;
}

public double getVerticalSpaceBus() {
Expand Down Expand Up @@ -330,8 +338,30 @@ public LayoutParameters setCgmesUseNames(boolean cgmesUseNames) {
return this;
}

public double getZoneLayoutSnakeLinePadding() {
return zoneLayoutSnakeLinePadding;
}

public LayoutParameters setZoneLayoutSnakeLinePadding(double zoneLayoutSnakeLinePadding) {
this.zoneLayoutSnakeLinePadding = zoneLayoutSnakeLinePadding;
return this;
}

public PathFinderType getZoneLayoutPathFinder() {
return zoneLayoutPathFinder;
}

public LayoutParameters setZoneLayoutPathFinder(PathFinderType zoneLayoutPathFinder) {
this.zoneLayoutPathFinder = zoneLayoutPathFinder;
return this;
}

public enum PathFinderType {
DIJKSTRA
}

public enum Alignment {
FIRST, LAST, MIDDLE, NONE;
FIRST, LAST, MIDDLE, NONE
}

public static class Padding {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2023, 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/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.sld.layout;

import com.powsybl.commons.*;
import com.powsybl.sld.layout.pathfinding.*;
import com.powsybl.sld.layout.zonebygrid.*;
import com.powsybl.sld.model.coordinate.*;
import com.powsybl.sld.model.coordinate.Point;
import com.powsybl.sld.model.graphs.*;
import com.powsybl.sld.model.nodes.Node;
import org.jgrapht.alg.util.*;

import java.util.*;

/**
* @author Thomas Adam {@literal <tadam at neverhack.com>}
*/
public class MatrixZoneLayout extends AbstractZoneLayout {
private final MatrixZoneLayoutModel model;

protected MatrixZoneLayout(ZoneGraph graph, String[][] matrix, ZoneLayoutPathFinderFactory pathFinderFactory, SubstationLayoutFactory sLayoutFactory, VoltageLevelLayoutFactory vLayoutFactory) {
super(graph, sLayoutFactory, vLayoutFactory);
this.pathFinder = pathFinderFactory.create();
this.model = new MatrixZoneLayoutModel(Objects.requireNonNull(matrix));
for (int row = 0; row < matrix.length; row++) {
for (int col = 0; col < matrix[row].length; col++) {
String id = matrix[row][col];
SubstationGraph sGraph = graph.getSubstationGraph(id);
if (sGraph == null && !id.isEmpty()) {
throw new PowsyblException("Substation '" + id + "' was not found in zone graph '" + getGraph().getId() + "'");
}
model.addSubstationGraph(sGraph, row, col);
}
}
}

/**
* Calculate relative coordinate of substations in the zone
*/
@Override
protected void calculateCoordSubstations(LayoutParameters layoutParameters) {
Matrix matrix = model.getMatrix();
// Display substations on not empty Matrix cell
matrix.stream().filter(c -> !c.isEmpty()).map(MatrixCell::graph).forEach(graph -> layoutBySubstation.get(graph).run(layoutParameters));
// Height by rows
int maxHeightRow = 0;
// Width by col
int maxWidthCol = 0;
// Snakeline hallway (horizontal & vertical)
int snakelineMargin = (int) layoutParameters.getZoneLayoutSnakeLinePadding();
// Zone size
int nbRows = matrix.rowCount();
int nbCols = matrix.columnCount();
// FIXME : padding diagram is canceled here !
double leftPadding = layoutParameters.getDiagramPadding().getLeft();
double topPadding = layoutParameters.getDiagramPadding().getTop();
// Move each substation into its matrix position
for (int row = 0; row < nbRows; row++) {
maxWidthCol = 0;
for (int col = 0; col < nbCols; col++) {
MatrixCell cell = matrix.get(row, col);
BaseGraph graph = cell.graph();
if (graph != null) {
// Compute delta in order to center substations into own matrix cell
int deltaX = (int) (matrix.getMatrixCellWidth(col) % graph.getWidth()) / 2;
int deltaY = (int) (matrix.getMatrixCellHeight(row) % graph.getHeight()) / 2;
double dx = maxWidthCol + (col + 1.0) * snakelineMargin;
double dy = maxHeightRow + (row + 1.0) * snakelineMargin;
move(graph, dx - leftPadding + deltaX, dy - topPadding + deltaY);
}
maxWidthCol += matrix.getMatrixCellWidth(col);
}
maxHeightRow += matrix.getMatrixCellHeight(row);
}
double zoneWidth = maxWidthCol + (nbCols + 1.0) * snakelineMargin;
double zoneHeight = maxHeightRow + (nbRows + 1.0) * snakelineMargin;
getGraph().setSize(zoneWidth, zoneHeight);
}

@Override
protected List<Point> calculatePolylineSnakeLine(LayoutParameters layoutParameters, Pair<Node, Node> nodes,
boolean increment) {
List<Point> polyline = new ArrayList<>();
Node node1 = nodes.getFirst();
Node node2 = nodes.getSecond();
VoltageLevelGraph vlGraph1 = getGraph().getVoltageLevelGraph(node1);
VoltageLevelGraph vlGraph2 = getGraph().getVoltageLevelGraph(node2);
SubstationGraph ss1Graph = getGraph().getSubstationGraph(node1).orElse(null);
SubstationGraph ss2Graph = getGraph().getSubstationGraph(node2).orElse(null);
if (ss1Graph != null && ss2Graph != null &&
model.contains(ss1Graph.getId()) && model.contains(ss2Graph.getId())) { // in the same Zone
Point p1 = vlGraph1.getShiftedPoint(node1);
Point p2 = vlGraph2.getShiftedPoint(node2);
Direction dNode1 = getNodeDirection(node1, 1);
Direction dNode2 = getNodeDirection(node2, 2);
polyline = new ArrayList<>();
// Add starting point
polyline.add(p1);
// Find snakeline path
polyline.addAll(model.buildSnakeline(pathFinder, ss1Graph.getId(), p1, dNode1, ss2Graph.getId(), p2, dNode2, layoutParameters.getZoneLayoutSnakeLinePadding()));
// Add ending point
polyline.add(p2);
}
return polyline;
}

@Override
public void manageSnakeLines(LayoutParameters layoutParameters) {
model.computePathFindingGrid(getGraph(), layoutParameters);

// Draw snakelines between Substations
manageSnakeLines(getGraph(), layoutParameters);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright (c) 2023, 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/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.sld.layout;

import com.powsybl.sld.layout.pathfinding.*;
import com.powsybl.sld.model.graphs.*;

import java.util.*;

/**
* @author Thomas Adam {@literal <tadam at neverhack.com>}
*/
public class MatrixZoneLayoutFactory implements ZoneLayoutFactory {

public Layout create(ZoneGraph graph, String[][] matrix, ZoneLayoutPathFinderFactory pathFinderFactory, SubstationLayoutFactory sLayoutFactory, VoltageLevelLayoutFactory vLayoutFactory) {
return new MatrixZoneLayout(graph, matrix, pathFinderFactory, sLayoutFactory, vLayoutFactory);
}

@Override
public Layout create(ZoneGraph graph, SubstationLayoutFactory sLayoutFactory, VoltageLevelLayoutFactory vLayoutFactory) {
// By default, work as Horizontal layout
List<String> substations = graph.getSubstations().stream().map(SubstationGraph::getId).toList();
String[] array = new String[substations.size()];
String[][] matrix = new String[1][];
matrix[0] = substations.toArray(array);
return new MatrixZoneLayout(graph, matrix, DijkstraPathFinder::new, sLayoutFactory, vLayoutFactory);
}
}
Loading