Skip to content

Commit

Permalink
[SLD] Detect inconsistent busNodes positions (#573)
Browse files Browse the repository at this point in the history
* Add unit test
* Detect conflicting BusNodes when creating Subsections
* Problematic externCell detected beforehand, laid out at x=0 as squashed

Signed-off-by: Florian Dupuy <florian.dupuy@rte-france.com>
  • Loading branch information
flo-dup authored Dec 22, 2023
1 parent 40f2617 commit 5802a92
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/
package com.powsybl.sld.layout;

import com.powsybl.commons.PowsyblException;
import com.powsybl.sld.model.blocks.UndefinedBlock;
import com.powsybl.sld.model.cells.Cell;
import com.powsybl.sld.model.cells.ExternCell;
import com.powsybl.sld.model.cells.InternCell;
Expand All @@ -18,6 +20,7 @@
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -99,14 +102,24 @@ Set<InternCellSide> getInternCellSides() {
return internCellSides;
}

void setExtendedNodeSet(Collection<BusNode> busNodes) {
void addToExtendedNodeSet(Collection<BusNode> busNodes) {
if (busNodes.containsAll(busNodeSet)) {
extendedNodeSet.addAll(busNodes.stream().filter(Objects::nonNull).collect(Collectors.toList()));
// The given busNodes correspond to all vertical bus nodes for a specific index of the horizontalBusLanes:
// those nodes correspond to a slice of busbars.
// There can't be more than one busNode per busbar index, we check this by creating the following map with
// an exception-throwing merge method
Map<Integer, BusNode> indexToBusNode = busNodes.stream().filter(Objects::nonNull)
.collect(Collectors.toMap(BusNode::getBusbarIndex, Function.identity(), this::detectConflictingBusNodes));
extendedNodeSet.addAll(indexToBusNode.values());
} else {
LOGGER.error("ExtendedNodeSet inconsistent with NodeBusSet");
}
}

private BusNode detectConflictingBusNodes(BusNode busNode1, BusNode busNode2) {
throw new PowsyblException("Inconsistent legBusSet: extended node set contains two busNodes with same index");
}

Set<BusNode> getExtendedNodeSet() {
return extendedNodeSet;
}
Expand Down Expand Up @@ -175,7 +188,33 @@ private static boolean crossContains(List<BusNode> busNodes1, List<BusNode> busN
return busNodes1.containsAll(busNodes2) && busNodes2.containsAll(busNodes1);
}

private static boolean checkLbs(LegBusSet legBusSet) {
// FIXME: workaround to detect incoherent LegBusSet without any piece of refactoring
boolean externCellLbs = legBusSet.externCells.size() == 1; // detecting a posteriori a legBusSet created from an ExternCell
if (externCellLbs) {
List<Integer> busbarIndices = legBusSet.busNodeSet.stream().map(BusNode::getBusbarIndex).distinct().toList();
// Detecting incoherent bus positions set from the user (from extension or directly when creating the
// busNode, WHEN PositionFromExtension is used instead of PositionByClustering).
// We rule out PositionByClustering where all busbar indices are set to zero and still are at this point
// (note that zero means no value in the code so far) by dismissing the detection if there is a zero busbar
// index. There cannot be any zero busbar index with PositionFromExtension, they are replaced in the call
// PositionFromExtension::setMissingPositionIndices.
if (busbarIndices.size() < legBusSet.busNodeSet.size() && busbarIndices.get(0) != 0) {
// Corresponding extern cell set as undefined block, leading to a squashed externCell at abscissa 0
ExternCell externCell = legBusSet.externCells.iterator().next();
externCell.setRootBlock(new UndefinedBlock(List.of(externCell.getRootBlock())));
LOGGER.error("ExternCell pattern not handled: attached to several busbar sections with same busbar index");
return false;
}
}
return true;
}

public static void pushLBS(List<LegBusSet> legBusSets, LegBusSet legBusSet) {
if (!checkLbs(legBusSet)) {
return;
}

for (LegBusSet lbs : legBusSets) {
if (lbs.contains(legBusSet)) {
lbs.absorbs(legBusSet);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ static List<Subsection> createSubsections(VoltageLevelGraph graph, LBSCluster lb
subsections.add(currentSubsection);
int i = 0;
for (LegBusSet lbs : lbsCluster.getLbsList()) {
lbs.setExtendedNodeSet(lbsCluster.getVerticalBuseNodes(i));
lbs.addToExtendedNodeSet(lbsCluster.getVerticalBuseNodes(i));
if (!currentSubsection.checkAbsorbability(lbs)) {
currentSubsection = new Subsection(vSize);
subsections.add(currentSubsection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public Map<BusNode, Integer> indexBusPosition(List<BusNode> busNodes, List<BusCe
.sorted(Comparator.comparing(BusNode::getId))
.collect(Collectors.toList())) {
busToNb.put(n, i);
n.setBusBarIndexSectionIndex(0, 0);
i++;
}
return busToNb;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* 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.iidm;

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

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

/**
* <PRE>
* l
* |
* ___ b ____
* | |
* | |
* --d1-- x --d2--
* bbs1 dc bbs2
* </PRE>
*
* @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
*/
class TestCaseExternCellOnTwoSections extends AbstractTestCaseIidm {

@BeforeEach
public void setUp() {
network = Network.create("test", "test");
graphBuilder = new NetworkGraphBuilder(network);
substation = Networks.createSubstation(network, "s", "s", Country.FR);
vl = Networks.createVoltageLevel(substation, "vl", "vl", TopologyKind.NODE_BREAKER, 225);
Networks.createBusBarSection(vl, "bbs1", "bbs1", 0, 1, 1);
Networks.createBusBarSection(vl, "bbs2", "bbs2", 1, 1, 2);
Networks.createLoad(vl, "l", "l", "l", 0, ConnectablePosition.Direction.TOP, 2, 10, 10);
Networks.createSwitch(vl, "d1", "d1", SwitchKind.DISCONNECTOR, false, false, false, 0, 3);
Networks.createSwitch(vl, "d2", "d2", SwitchKind.DISCONNECTOR, false, false, false, 1, 3);
Networks.createSwitch(vl, "dc", "dc", SwitchKind.DISCONNECTOR, false, false, false, 0, 1);
Networks.createSwitch(vl, "b", "b", SwitchKind.BREAKER, false, false, false, 3, 2);
}

@Test
void test() {
VoltageLevelGraph g = graphBuilder.buildVoltageLevelGraph(vl.getId());
voltageLevelGraphLayout(g);
assertEquals(toString("/TestCaseExternCellOnTwoSections.svg"), toSVG(g, "/TestCaseExternCellOnTwoSections.svg"));
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 5802a92

Please sign in to comment.