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 one leg intern cells #595

Merged
merged 13 commits into from
Mar 25, 2024
Merged
53 changes: 53 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 @@ -2211,6 +2211,59 @@ public static Network createNetworkGroundDisconnectorOnBusBarBusBreaker() {
return network;
}

public static Network createNetworkWithInternCellDifferentSubsections() {
Network network = Network.create("testCaseOneLegInternCellOnDifferentSubsections", "test");
Substation substation = Networks.createSubstation(network, "s", "s", Country.FR);
VoltageLevel voltageLevel = Networks.createVoltageLevel(substation, "vl", "vl", TopologyKind.NODE_BREAKER, 380);
Networks.createBusBarSection(voltageLevel, "1.1", "1.1", 0, 1, 1);
Networks.createBusBarSection(voltageLevel, "1.2", "1.2", 1, 1, 2);
Networks.createBusBarSection(voltageLevel, "1.3", "1.3", 2, 1, 3);

Networks.createSwitch(voltageLevel, "d1", "d1", SwitchKind.DISCONNECTOR, false, true, false, 0, 11);
Networks.createSwitch(voltageLevel, "d2", "d2", SwitchKind.DISCONNECTOR, false, true, false, 11, 2);

return network;
}

public static Network createNetworkWithComplexInternCellDifferentSubsections1() {
Network network = Network.create("testCaseComplexInternCellOnDifferentSubsections", "test");
Substation substation = Networks.createSubstation(network, "s", "s", Country.FR);
VoltageLevel voltageLevel = Networks.createVoltageLevel(substation, "vl", "vl", TopologyKind.NODE_BREAKER, 380);

Networks.createBusBarSection(voltageLevel, "1.1", "1.1", 0, 1, 1);
Networks.createBusBarSection(voltageLevel, "1.2", "1.2", 1, 2, 1);
Networks.createBusBarSection(voltageLevel, "2.1", "2.1", 2, 1, 2);
Networks.createLoad(voltageLevel, "load", "load", "load", null, ConnectablePosition.Direction.TOP, 3, 10d, 10d);
Networks.createSwitch(voltageLevel, "dl11", "dl111", SwitchKind.DISCONNECTOR, false, false, false, 0, 3);
Networks.createSwitch(voltageLevel, "dl121", "dl12", SwitchKind.DISCONNECTOR, false, true, false, 1, 3);
Networks.createSwitch(voltageLevel, "d11", "d11", SwitchKind.DISCONNECTOR, false, false, false, 0, 11);
Networks.createSwitch(voltageLevel, "d12", "d12", SwitchKind.DISCONNECTOR, false, true, false, 1, 11);
Networks.createSwitch(voltageLevel, "d21", "d21", SwitchKind.DISCONNECTOR, false, false, false, 2, 11);

return network;
}

public static Network createNetworkWithComplexInternCellDifferentSubsections() {
Network network = Network.create("testCaseComplexInternCellOnDifferentSubsections", "test");
Substation substation = Networks.createSubstation(network, "s", "s", Country.FR);
VoltageLevel voltageLevel = Networks.createVoltageLevel(substation, "vl", "vl", TopologyKind.NODE_BREAKER, 380);

Networks.createBusBarSection(voltageLevel, "1.1", "1.1", 0, 1, 1);
Networks.createBusBarSection(voltageLevel, "1.2", "1.2", 1, 2, 1);
Networks.createBusBarSection(voltageLevel, "2.1", "2.1", 2, 1, 2);

Networks.createLoad(voltageLevel, LOAD_1_ID, LOAD_1_ID, LOAD_1_ID, null, ConnectablePosition.Direction.TOP, 3, 10d, 10d);
Networks.createSwitch(voltageLevel, "dl1", "dl1", SwitchKind.DISCONNECTOR, false, false, false, 0, 3);
Networks.createLoad(voltageLevel, LOAD_2_ID, LOAD_2_ID, LOAD_2_ID, null, ConnectablePosition.Direction.TOP, 4, 10d, 10d);
Networks.createSwitch(voltageLevel, "dl2", "dl2", SwitchKind.DISCONNECTOR, false, false, false, 2, 4);

Networks.createSwitch(voltageLevel, "d11", "d11", SwitchKind.DISCONNECTOR, false, true, false, 0, 11);
Networks.createSwitch(voltageLevel, "d12", "d12", SwitchKind.DISCONNECTOR, false, false, false, 1, 11);
Networks.createSwitch(voltageLevel, "d21", "d21", SwitchKind.DISCONNECTOR, false, false, false, 2, 11);

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 @@ -65,7 +65,7 @@ public void organize(VoltageLevelGraph graph, LayoutParameters layoutParameters)
CellBlockDecomposer.determineComplexCell(graph, cell, exceptionIfPatternNotHandled);
checkBlocks(cell, layoutParameters);
if (cell.getType() == INTERN) {
((InternCell) cell).organizeBlocks();
((InternCell) cell).organizeBlocks(exceptionIfPatternNotHandled);
}
});
graph.getShuntCellStream().forEach(CellBlockDecomposer::determineShuntCellBlocks);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ public void visit(UndefinedBlock block) {
calculateSubHeight(block, Math::max);
}

private void calculateSubHeight(ComposedBlock block, DoubleBinaryOperator merge) {
private <T extends Block> void calculateSubHeight(ComposedBlock<T> block, DoubleBinaryOperator merge) {
blockHeight = 0.;
for (Block sub : block.getSubBlocks()) {
for (T sub : block.getSubBlocks()) {
// Here, when the subBlocks are positioned in parallel we calculate the max
// height of all these subBlocks
// when the subBlocks are serial, we calculate the sum
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import com.powsybl.sld.model.coordinate.Position;
import com.powsybl.sld.model.nodes.Node;

import java.util.List;

import static com.powsybl.sld.model.coordinate.Orientation.*;
import static com.powsybl.sld.model.coordinate.Position.Dimension.*;
import static com.powsybl.sld.model.coordinate.Coord.Dimension.*;
Expand All @@ -33,26 +35,27 @@ public static CalculateCoordBlockVisitor create(LayoutParameters layoutParameter

@Override
public void visit(BodyPrimaryBlock block) {
List<Node> blockNodes = block.getNodes();
if (blockNodes.size() == 1) {
blockNodes.get(0).setCoordinates(block.getCoord().get(X), block.getCoord().get(Y));
return;
}

if (block.getPosition().getOrientation().isVertical()) {
int sign = block.getOrientation() == UP ? 1 : -1;
double y0 = block.getCoord().get(Y) + sign * block.getCoord().getSpan(Y) / 2;
double yPxStep = block.getPosition().getSpan(V) == 0 ? 0
: sign * block.getCoord().getSpan(Y) / (block.getNodes().size() - 1);
int v = 0;
for (Node node : block.getNodes()) {
node.setCoordinates(block.getCoord().get(X), y0 - yPxStep * v);
v++;
double yPxStep = sign * block.getCoord().getSpan(Y) / (blockNodes.size() - 1);
for (int i = 0; i < blockNodes.size(); i++) {
blockNodes.get(i).setCoordinates(block.getCoord().get(X), y0 - yPxStep * i);
}
} else {
double x0 = block.getCoord().get(X) - block.getCoord().getSpan(X) / 2;
if (layoutContext.isInternCell() && !layoutContext.isFlat()) {
x0 += layoutParameters.getCellWidth() / 2;
}
double xPxStep = block.getCoord().getSpan(X) / (block.getNodes().size() - 1);
int h = 0;
for (Node node : block.getNodes()) {
node.setCoordinates(x0 + xPxStep * h, block.getCoord().get(Y));
h++;
double xPxStep = block.getCoord().getSpan(X) / (blockNodes.size() - 1);
for (int i = 0; i < blockNodes.size(); i++) {
blockNodes.get(i).setCoordinates(x0 + xPxStep * i, block.getCoord().get(Y));
}
}
}
Expand Down Expand Up @@ -120,19 +123,19 @@ public void visit(LegParallelBlock block) {
}
}

void translatePosInCoord(ComposedBlock block, Coord.Dimension cDimSteady,
<T extends Block> void translatePosInCoord(ComposedBlock<T> block, Coord.Dimension cDimSteady,
Coord.Dimension cDimVariable, Position.Dimension pDim, int sign) {
replicateCoordInSubblocks(block, cDimSteady);
distributeCoordInSubblocs(block, pDim, cDimVariable, sign);
block.getSubBlocks().forEach(sub -> sub.accept(this));
}

void replicateCoordInSubblocks(ComposedBlock block, Coord.Dimension dim) {
<T extends Block> void replicateCoordInSubblocks(ComposedBlock<T> block, Coord.Dimension dim) {
block.getCoord().getSegment(dim)
.replicateMe(block.getSubBlocks().stream().map(b -> b.getCoord().getSegment(dim)));
}

void distributeCoordInSubblocs(ComposedBlock block, Position.Dimension pDim, Coord.Dimension cDim, int sign) {
<T extends Block> void distributeCoordInSubblocs(ComposedBlock<T> block, Position.Dimension pDim, Coord.Dimension cDim, int sign) {
// Computes the step, avoiding the division by 0 for 0-span composed block (e.g.
// LegPrimaryBlock + Feeder)
double init = block.getCoord().get(cDim) - sign * block.getCoord().getSpan(cDim) / 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,9 @@ private static boolean searchParallelMerge(List<Block> blocks) {
}
}
for (List<Block> blocksBundle : blocksBundlesToMerge) {
Block parallelBlock;
if (blocksBundle.stream().anyMatch(b -> !(b instanceof LegPrimaryBlock))) {
parallelBlock = new BodyParallelBlock(blocksBundle, false);
} else {
parallelBlock = new LegParallelBlock(blocksBundle, true);
}
blocks.add(parallelBlock);
blocks.add(blocksBundle.stream().allMatch(LegPrimaryBlock.class::isInstance)
? new LegParallelBlock(blocksBundle.stream().filter(LegPrimaryBlock.class::isInstance).map(LegPrimaryBlock.class::cast).toList(), true)
: new BodyParallelBlock(blocksBundle, false));
}
return !blocksBundlesToMerge.isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,6 @@
public class ImplicitCellDetector implements CellDetector {

private static final Logger LOGGER = LoggerFactory.getLogger(ImplicitCellDetector.class);
private final boolean exceptionIfPatternNotHandled;

public ImplicitCellDetector(boolean exceptionIfPatternNotHandled) {
this.exceptionIfPatternNotHandled = exceptionIfPatternNotHandled;
}

public ImplicitCellDetector() {
this(false);
}

/**
* internCell detection : an internal cell is composed of nodes connecting BUSes without connecting Feeder.
Expand All @@ -65,7 +56,7 @@ public void detectCells(VoltageLevelGraph graph) {
stopTypes.add(BUS);
List<Set<Node>> internCellsNodes = detectCell(graph, stopTypes, exclusionTypes, allocatedNodes);
for (Set<Node> nodes : internCellsNodes) {
graph.addCell(new InternCell(graph.getNextCellNumber(), nodes, exceptionIfPatternNotHandled));
graph.addCell(new InternCell(graph.getNextCellNumber(), nodes));
}

// ****************EXTERN AND SHUNT CELLS******
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
package com.powsybl.sld.layout;

import com.powsybl.commons.PowsyblException;
import com.powsybl.sld.model.blocks.LegParallelBlock;
import com.powsybl.sld.model.blocks.LegPrimaryBlock;
import com.powsybl.sld.model.blocks.UndefinedBlock;
import com.powsybl.sld.model.cells.Cell;
import com.powsybl.sld.model.cells.ExternCell;
Expand Down Expand Up @@ -69,12 +71,8 @@ void addInternCell(InternCell internCell, Side side) {
internCellSides.add(new InternCellSide(internCell, side));
}

private boolean contains(Collection<BusNode> busNodeCollection) {
return busNodeSet.containsAll(busNodeCollection);
}

private boolean contains(LegBusSet lbs) {
return contains(lbs.getBusNodeSet());
return busNodeSet.containsAll(lbs.getBusNodeSet());
}

private void absorbs(LegBusSet lbsToAbsorb) {
Expand Down Expand Up @@ -138,15 +136,15 @@ static List<LegBusSet> createLegBusSets(VoltageLevelGraph graph, Map<BusNode, In
externCells.forEach(cell -> pushLBS(legBusSets, new LegBusSet(nodeToNb, cell)));

graph.getInternCellStream()
.filter(cell -> cell.checkIsShape(InternCell.Shape.ONE_LEG))
.sorted(Comparator.comparing(Cell::getFullId)) // if order is not yet defined & avoid randomness
.forEachOrdered(cell -> pushLBS(legBusSets, new LegBusSet(nodeToNb, cell, Side.UNDEFINED)));

graph.getInternCellStream()
.filter(cell -> cell.checkIsNotShape(InternCell.Shape.ONE_LEG, InternCell.Shape.UNHANDLED_PATTERN))
.filter(cell -> cell.checkIsNotShape(InternCell.Shape.MAYBE_ONE_LEG, InternCell.Shape.UNHANDLED_PATTERN))
.sorted(Comparator.comparing(cell -> -((InternCell) cell).getBusNodes().size()) // bigger first to identify encompassed InternCell at the end with the smaller one
.thenComparing(cell -> ((InternCell) cell).getFullId())) // avoid randomness
.forEachOrdered(cell -> pushNonUnilegInternCell(legBusSets, nodeToNb, cell));
.forEachOrdered(cell -> pushInternCell(legBusSets, nodeToNb, cell));

graph.getInternCellStream()
.filter(cell -> cell.checkIsShape(InternCell.Shape.MAYBE_ONE_LEG))
.sorted(Comparator.comparing(Cell::getFullId)) // if order is not yet defined & avoid randomness
.forEachOrdered(cell -> pushInternCell(legBusSets, nodeToNb, cell));

// find orphan busNodes and build their LBS
List<BusNode> allBusNodes = new ArrayList<>(graph.getNodeBuses());
Expand Down Expand Up @@ -232,15 +230,72 @@ public static void pushLBS(List<LegBusSet> legBusSets, LegBusSet legBusSet) {
legBusSets.add(legBusSet);
}

private static void pushNonUnilegInternCell(List<LegBusSet> legBusSets, Map<BusNode, Integer> nodeToNb, InternCell internCell) {
private static void pushInternCell(List<LegBusSet> legBusSets, Map<BusNode, Integer> nodeToNb, InternCell internCell) {
List<LegBusSet> attachedLegBusSets = new ArrayList<>();
for (LegBusSet lbs : legBusSets) {
if (lbs.contains(internCell.getBusNodes())) {
lbs.addInternCell(internCell, Side.UNDEFINED);
internCell.setShape(InternCell.Shape.VERTICAL);
return;
boolean attachedToLbs = internCell.getBusNodes().stream().anyMatch(lbs.busNodeSet::contains);
if (attachedToLbs) {
attachedLegBusSets.add(lbs);
if (lbs.busNodeSet.containsAll(internCell.getBusNodes())) {
lbs.addInternCell(internCell, Side.UNDEFINED);
if (internCell.getShape() == InternCell.Shape.MAYBE_ONE_LEG) {
internCell.setShape(InternCell.Shape.ONE_LEG);
} else {
internCell.setShape(InternCell.Shape.VERTICAL);
}
return;
}
}
}
pushLBS(legBusSets, new LegBusSet(nodeToNb, internCell, Side.LEFT));
pushLBS(legBusSets, new LegBusSet(nodeToNb, internCell, Side.RIGHT));

// We didn't find any legBusSet which absorbs the intern cell
if (internCell.getShape() == InternCell.Shape.MAYBE_ONE_LEG) {
replaceByMultilegOrSetOneLeg(internCell, attachedLegBusSets);
}
if (internCell.getShape() != InternCell.Shape.ONE_LEG) {
pushLBS(legBusSets, new LegBusSet(nodeToNb, internCell, Side.LEFT));
pushLBS(legBusSets, new LegBusSet(nodeToNb, internCell, Side.RIGHT));
} else {
pushLBS(legBusSets, new LegBusSet(nodeToNb, internCell, Side.UNDEFINED));
}
}

private static void replaceByMultilegOrSetOneLeg(InternCell internCell, List<LegBusSet> attachedLegBusSets) {
// We consider that a one leg intern cell should not force the corresponding busNodes to be in the same LegBusSet
// (forcing them to be parallel), hence we try to replace that one leg by a multileg
// The goal here is to split the corresponding LegParallelBlock into 2 stacked parts
LegParallelBlock oneLeg = (LegParallelBlock) internCell.getSideToLeg(Side.UNDEFINED);
List<LegPrimaryBlock> subBlocks = oneLeg.getSubBlocks();
if (subBlocks.size() == 2) {
internCell.replaceOneLegByMultiLeg(subBlocks.get(0), subBlocks.get(1));
} else {
// Each subBlock has one BusNode which might be in the existing LegBusSets.
// The LegBusSets which contain at least one BusNode from current internCell are given as attachedLegBusSets parameter.
// We first try to split the subBlocks based on the LegBusSets
Collection<List<LegPrimaryBlock>> groupSubBlocksLbs = subBlocks.stream().collect(Collectors.groupingBy(
sb -> attachedLegBusSets.stream().filter(lbs -> lbs.busNodeSet.contains(sb.getBusNode())).findFirst())).values();
if (groupSubBlocksLbs.size() == 2) {
replaceByMultiLeg(internCell, groupSubBlocksLbs);
} else {
Copy link
Member

Choose a reason for hiding this comment

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

This else statement is never used (neither in unit tests nor in other tests I ran with French substations).
Shall we remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now there's one unit test which uses it. And I fixed the content of the else, adding comments to explain better what's done.

// We then try to split the subBlocks using the sectionIndex of their busNode
Collection<List<LegPrimaryBlock>> groupSubBlocksSi = subBlocks.stream().collect(Collectors.groupingBy(
sb -> sb.getBusNode().getSectionIndex())).values();
if (groupSubBlocksSi.size() == 2) {
replaceByMultiLeg(internCell, groupSubBlocksSi);
} else {
// Failed to replace it by a multileg -> marks it one leg
internCell.setOneLeg();
}
}
}
}

private static void replaceByMultiLeg(InternCell internCell, Collection<List<LegPrimaryBlock>> groupSubBlocks) {
var it = groupSubBlocks.iterator();
List<LegPrimaryBlock> left = it.next();
List<LegPrimaryBlock> right = it.next();
internCell.replaceOneLegByMultiLeg(
left.size() == 1 ? left.get(0) : new LegParallelBlock(left, true),
right.size() == 1 ? right.get(0) : new LegParallelBlock(right, true));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public Layout create(VoltageLevelGraph graph) {
positionVoltageLevelLayoutFactoryParameters.isSubstituteInternalMiddle2wtByEquipmentNodes());

// For cell detection
ImplicitCellDetector cellDetector = new ImplicitCellDetector(positionVoltageLevelLayoutFactoryParameters.isExceptionIfPatternNotHandled());
ImplicitCellDetector cellDetector = new ImplicitCellDetector();

// For building blocks from cells
BlockOrganizer blockOrganizer = new BlockOrganizer(positionFinder, positionVoltageLevelLayoutFactoryParameters.isFeederStacked(), positionVoltageLevelLayoutFactoryParameters.isExceptionIfPatternNotHandled(), positionVoltageLevelLayoutFactoryParameters.isHandleShunts(), positionVoltageLevelLayoutFactoryParameters.getBusInfoMap());
Expand Down
Loading