From 6b08e09ebcedd0b78e16e3cf8ae578d7a1426eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Jeanson?= <39792051+BenoitJeanson@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:25:23 +0200 Subject: [PATCH] Documentation... and refactoring (#517) * Major dev documentation update * Refactor: Simplify LegBusSet * Refactor: renaming of LegBusSet/HorizontalBusLane/LBSCluster/VBSClusterSide to VerticalBusSet/HorizontalBusList/BSCluster/BSClusterSide * Refactor: renaming PositionFromExtension into PositionPredefined * Refactor: create new packages layout.position, layout.position.clustering and layout.position.predefined: new packages to separate the PositionFinder implementations from the rest of the layout package * Refactor: Clarify PositionFinder interface by transferring some of particulars methods to AbstractPositionFinder * Refactor: HorizontalBusListManager functional interface is now directly within PositionPredefined and PositionByClustering which should reduce the complexity... * Refactor: Remove cyclic dependency between Links and BSClusterSide. --- .../layout/LayoutToCgmesDlExporterTool.java | 6 +- .../LayoutToCgmesExtensionsConverter.java | 2 +- .../doc/BSCluster.adoc | 43 + .../doc/CellDetector.adoc | 5 - .../doc/HorizontalBusList.adoc | 1 + .../doc/PositionByClustering.adoc | 347 +++++++ .../doc/PositionFinder.adoc | 155 ++-- .../doc/PositionFromExtension.adoc | 224 +++++ .../doc/Subsection.adoc | 41 + .../doc/Subsections.adoc | 5 - .../doc/VerticalBusSet.adoc | 17 + .../doc/images/BSClusterByClusteringFinal.svg | 330 +++++++ .../doc/images/BSClusterFinal.svg | 339 +++++++ .../images/BSClusterFromExtensionFinal.svg | 363 ++++++++ .../doc/images/BSClusterInit.svg | 375 ++++++++ .../doc/images/CellType_INTERN.svg | 260 ------ .../doc/images/Cells.svg | 365 -------- .../doc/images/PositionByClusteringResult.svg | 562 ++++++++++++ .../rawGraph.svg.2019_04_01_16_43_32.0.svg | 865 ------------------ .../doc/images/rawGraphVBS.svg | 844 +++++++++++++++++ .../doc/images/rawPosition.svg | 580 ------------ .../images/verticalBusConnectionPattern.svg | 605 ------------ .../powsybl/sld/layout/BlockOrganizer.java | 3 + .../sld/layout/HorizontalBusLaneManager.java | 25 - .../sld/layout/ImplicitCellDetector.java | 2 + .../com/powsybl/sld/layout/LBSCluster.java | 146 --- ...oltageLevelLayoutFactorySmartSelector.java | 2 +- .../PositionVoltageLevelLayoutFactory.java | 7 +- .../VoltageLevelLayoutFactoryCreator.java | 1 + .../AbstractPositionFinder.java | 29 +- .../sld/layout/position/BSCluster.java | 139 +++ .../{ => position}/BlockPositionner.java | 6 +- .../HorizontalBusList.java} | 34 +- .../position/HorizontalBusListsMerger.java | 17 + .../layout/{ => position}/InternCellSide.java | 2 +- .../layout/{ => position}/PositionFinder.java | 13 +- .../sld/layout/{ => position}/Subsection.java | 64 +- .../VerticalBusSet.java} | 186 ++-- .../position/clustering/BSClusterSide.java | 102 +++ .../clustering}/Link.java | 115 ++- .../sld/layout/position/clustering/Links.java | 80 ++ .../clustering}/PositionByClustering.java | 135 ++- .../predefined/PositionPredefined.java} | 142 +-- .../HBLaneManagerByClustering.java | 85 -- .../positionbyclustering/LBSClusterSide.java | 122 --- .../layout/positionbyclustering/Links.java | 88 -- .../HBLaneManagerByExtension.java | 48 - .../powsybl/sld/layout/zonebygrid/Matrix.java | 2 +- .../sld/model/graphs/VoltageLevelGraph.java | 2 +- .../sld/{layout => util}/GraphTraversal.java | 4 +- .../TopologicallyConnectedNodesSet.java | 2 +- .../{layout => util}/TopologyCalculation.java | 2 +- .../iidm/TestInternalBranchesNodeBreaker.java | 4 +- .../sld/iidm/TestTopologyCalculation.java | 4 +- .../powsybl/sld/raw/TestOrderConsistency.java | 2 +- 55 files changed, 4331 insertions(+), 3618 deletions(-) create mode 100644 single-line-diagram/single-line-diagram-core/doc/BSCluster.adoc create mode 100644 single-line-diagram/single-line-diagram-core/doc/HorizontalBusList.adoc create mode 100644 single-line-diagram/single-line-diagram-core/doc/PositionByClustering.adoc create mode 100644 single-line-diagram/single-line-diagram-core/doc/PositionFromExtension.adoc create mode 100644 single-line-diagram/single-line-diagram-core/doc/Subsection.adoc delete mode 100644 single-line-diagram/single-line-diagram-core/doc/Subsections.adoc create mode 100644 single-line-diagram/single-line-diagram-core/doc/VerticalBusSet.adoc create mode 100644 single-line-diagram/single-line-diagram-core/doc/images/BSClusterByClusteringFinal.svg create mode 100644 single-line-diagram/single-line-diagram-core/doc/images/BSClusterFinal.svg create mode 100644 single-line-diagram/single-line-diagram-core/doc/images/BSClusterFromExtensionFinal.svg create mode 100644 single-line-diagram/single-line-diagram-core/doc/images/BSClusterInit.svg delete mode 100644 single-line-diagram/single-line-diagram-core/doc/images/CellType_INTERN.svg delete mode 100644 single-line-diagram/single-line-diagram-core/doc/images/Cells.svg create mode 100644 single-line-diagram/single-line-diagram-core/doc/images/PositionByClusteringResult.svg delete mode 100644 single-line-diagram/single-line-diagram-core/doc/images/rawGraph.svg.2019_04_01_16_43_32.0.svg create mode 100644 single-line-diagram/single-line-diagram-core/doc/images/rawGraphVBS.svg delete mode 100644 single-line-diagram/single-line-diagram-core/doc/images/rawPosition.svg delete mode 100644 single-line-diagram/single-line-diagram-core/doc/images/verticalBusConnectionPattern.svg delete mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalBusLaneManager.java delete mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LBSCluster.java rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{ => position}/AbstractPositionFinder.java (52%) create mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/BSCluster.java rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{ => position}/BlockPositionner.java (98%) rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{HorizontalBusLane.java => position/HorizontalBusList.java} (72%) create mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/HorizontalBusListsMerger.java rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{ => position}/InternCellSide.java (95%) rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{ => position}/PositionFinder.java (63%) rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{ => position}/Subsection.java (85%) rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{LegBusSet.java => position/VerticalBusSet.java} (66%) create mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/BSClusterSide.java rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{positionbyclustering => position/clustering}/Link.java (56%) create mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/Links.java rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{positionbyclustering => position/clustering}/PositionByClustering.java (50%) rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{positionfromextension/PositionFromExtension.java => position/predefined/PositionPredefined.java} (69%) delete mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/HBLaneManagerByClustering.java delete mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/LBSClusterSide.java delete mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/Links.java delete mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionfromextension/HBLaneManagerByExtension.java rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/{layout => util}/GraphTraversal.java (94%) rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/{layout => util}/TopologicallyConnectedNodesSet.java (97%) rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/{layout => util}/TopologyCalculation.java (99%) diff --git a/single-line-diagram/single-line-diagram-cgmes/single-line-diagram-cgmes-layout/src/main/java/com/powsybl/sld/cgmes/layout/LayoutToCgmesDlExporterTool.java b/single-line-diagram/single-line-diagram-cgmes/single-line-diagram-cgmes-layout/src/main/java/com/powsybl/sld/cgmes/layout/LayoutToCgmesDlExporterTool.java index c2543df25..fba7a967c 100644 --- a/single-line-diagram/single-line-diagram-cgmes/single-line-diagram-cgmes-layout/src/main/java/com/powsybl/sld/cgmes/layout/LayoutToCgmesDlExporterTool.java +++ b/single-line-diagram/single-line-diagram-cgmes/single-line-diagram-cgmes-layout/src/main/java/com/powsybl/sld/cgmes/layout/LayoutToCgmesDlExporterTool.java @@ -15,8 +15,8 @@ import com.powsybl.sld.cgmes.dl.conversion.CgmesDLExporter; import com.powsybl.sld.cgmes.dl.conversion.CgmesDLUtils; import com.powsybl.sld.layout.*; -import com.powsybl.sld.layout.positionbyclustering.PositionByClustering; -import com.powsybl.sld.layout.positionfromextension.PositionFromExtension; +import com.powsybl.sld.layout.position.clustering.PositionByClustering; +import com.powsybl.sld.layout.position.predefined.PositionPredefined; import com.powsybl.tools.Command; import com.powsybl.tools.Tool; import com.powsybl.tools.ToolOptions; @@ -46,7 +46,7 @@ public class LayoutToCgmesDlExporterTool implements Tool { private static final String DIAGRAM_NAME = "diagram-name"; private final Map voltageLevelsLayouts - = Map.ofEntries(Map.entry("auto-extensions", new PositionVoltageLevelLayoutFactory(new PositionFromExtension())), + = Map.ofEntries(Map.entry("auto-extensions", new PositionVoltageLevelLayoutFactory(new PositionPredefined())), Map.entry(DEFAULT_VOLTAGE_LAYOUT, new PositionVoltageLevelLayoutFactory(new PositionByClustering()))); private final Map substationsLayouts diff --git a/single-line-diagram/single-line-diagram-cgmes/single-line-diagram-cgmes-layout/src/main/java/com/powsybl/sld/cgmes/layout/LayoutToCgmesExtensionsConverter.java b/single-line-diagram/single-line-diagram-cgmes/single-line-diagram-cgmes-layout/src/main/java/com/powsybl/sld/cgmes/layout/LayoutToCgmesExtensionsConverter.java index ee4c6d56a..91c2c094b 100644 --- a/single-line-diagram/single-line-diagram-cgmes/single-line-diagram-cgmes-layout/src/main/java/com/powsybl/sld/cgmes/layout/LayoutToCgmesExtensionsConverter.java +++ b/single-line-diagram/single-line-diagram-cgmes/single-line-diagram-cgmes-layout/src/main/java/com/powsybl/sld/cgmes/layout/LayoutToCgmesExtensionsConverter.java @@ -12,7 +12,7 @@ import com.powsybl.sld.cgmes.dl.conversion.CgmesDLUtils; import com.powsybl.sld.cgmes.dl.iidm.extensions.*; import com.powsybl.sld.layout.*; -import com.powsybl.sld.layout.positionbyclustering.PositionByClustering; +import com.powsybl.sld.layout.position.clustering.PositionByClustering; import com.powsybl.sld.library.ComponentTypeName; import com.powsybl.sld.model.coordinate.Orientation; import com.powsybl.sld.model.graphs.*; diff --git a/single-line-diagram/single-line-diagram-core/doc/BSCluster.adoc b/single-line-diagram/single-line-diagram-core/doc/BSCluster.adoc new file mode 100644 index 000000000..ec9b9765a --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/BSCluster.adoc @@ -0,0 +1,43 @@ += `BSCluster` + +== Definition +`BSCluster` (`BusNode` Sets Cluster) are used by implementations of `PositionFinder`. + +**** +It is composed of 2 kinds of sets of `BusNode` Sets that present an horizontal and a vertical view of the structure of a `VoltageLevel`: + +- `List verticalBusSets` see link:VerticalBusSet.adoc[VerticalBusSet] +- `List horizontalBusLists` see link:HorizontalBusList.adoc[HorizontalBusList] +**** + + +[IMPORTANT] +==== +`VerticalBusSet` is a `Set` as it is important to have no duplicate `BusNodes` whereas, it is possible to have duplicate `BusNodes` in the `HorizontalBusList`. + +The rules are as follow: + +- for `VerticalBusSet`, a `BusNode` may appear: +* in multiple `VerticalBusSets`, +* but only once in a `VerticalBusSet`; +- for `HorizontalBusList`, a `BusNode` may appear: +* multiple times in a `HorizontalBusList`, in that case the occurences shall have contiguous indexes, +* shall appear only in one single `HorizontalBusList`. +==== + +The goal is at the end to be able to merge the `VoltageLevel` into a single `BSCluster` having to this kind of pattern: + +image::images/BSClusterFinal.svg[align="center"] + +== Key methods + +=== Build +A `BSCluster` is initiated with one `VerticalBusSet` that: + +- is put as is as first element `verticalBusSets`, +- inititiates one `HorizontalBusList` for each of its `BusNode` + +Each `PositionFinder` using `BSCluster` implementation provides a strategy to merge them together in order to get a single `BSCluster`. + +=== Merge +The merge of 2 `BSClusters` is done by calling the `merge` method giving it a `HorizontalBusListManager`. Indeed if the merging of the `verticalBusSets` is just a concatenation, the merging of the `horizontalBusLists` differs from one `PositionFinder` to another. \ No newline at end of file diff --git a/single-line-diagram/single-line-diagram-core/doc/CellDetector.adoc b/single-line-diagram/single-line-diagram-core/doc/CellDetector.adoc index 3e5239993..68522791d 100644 --- a/single-line-diagram/single-line-diagram-core/doc/CellDetector.adoc +++ b/single-line-diagram/single-line-diagram-core/doc/CellDetector.adoc @@ -121,8 +121,3 @@ Last, the second `ExternCell` cell is build with the second `SHUNT` node and the .Descrimination of a `SHUNT` cell image::images/rawGraphExternShunt.svg[align="center"] - -''' -==== The `PatternCellDetector` class - -This detector is based on pattern matching algorithm. The patterns are described in `/resources/pattern.xml`. \ No newline at end of file diff --git a/single-line-diagram/single-line-diagram-core/doc/HorizontalBusList.adoc b/single-line-diagram/single-line-diagram-core/doc/HorizontalBusList.adoc new file mode 100644 index 000000000..debd4b0c9 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/HorizontalBusList.adoc @@ -0,0 +1 @@ += `HorizontalBusList` \ No newline at end of file diff --git a/single-line-diagram/single-line-diagram-core/doc/PositionByClustering.adoc b/single-line-diagram/single-line-diagram-core/doc/PositionByClustering.adoc new file mode 100644 index 000000000..c362f4ef4 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/PositionByClustering.adoc @@ -0,0 +1,347 @@ += `PositionByClustering`: Establishing positions driven by similarities + +== Context +The goal of the algorithm is to find an organization of the `VoltageLevel` with no other information than the graph itself. + +== Algorithm +=== Principle +The positionning is based on sequential merges of `BSCluster` considering: + +* the fact that each external cells and any "leg" of an `InternCell` shall be *stackable* - meaning, that all the corresponding busbars are aligned in parallel to be able to connect them with a vertical string of isolators. This implies the `busNodes` of a leg shall be spread in different vertical structural positions. The initialization of the `VerticalBusSets` reflects this constraint. +* attractivity -- which is expressed in terms of strength of a `Link` between sides (left/right) of `BSCluster`. The stronger the link, the more likely it is to be subject to a merge. + +The name of the implementation comes from the fact that `BSClusters` are absorbing one another growing clusters to a single one. + +=== Steps +* `PositionByClustering::indexBusPosition` builds the `Map busToNb`. (The sorting order (`BusNode::getId`) only goal is to avoid randomness.) +* `PositionByClustering::organizeBusSets`: +** builds `ListbsClusters` +** calls `Links::create`, which creates: +*** `bsClusterSides` with all the initial `BSClusterSide` (ie `RIGHT` and `LEFT` for each `BSCluster`) +*** `linkSet` with all the feasible `Link` between them, sorted by the "strength" of their similarities +** successively: +*** merges the `BSClusterSide` of the first (and strongest) `Link`, +*** updates `bsClusterSides` list and `linkSet`. (Each merge reducing their size.) +*** until `linkSet` is empty (and `bsClusterSides` reduced to both sides of a single encompassing `BSCluster`) +** calls `tetrisHorizontalBusLists` which arranges the `HorizontalBusLists` to reduce their number, and consequently the number of necessary `busbarIndex` merging them when they don't overlap. +** transfers the resulting structure into `BusNode.busbarIndex` and `BusNode.sectionIndex`, and `ExternCell.order`. +** the remaining `BSCluster` is ready to be processed by `Subsection` class. + +=== Dedicated classes + +==== The `BSClusterSide` class + +The `BSClusterSide` is a composition of a `BSCluster` and a `Side` (`LEFT/RIGHT`). + +The `BSClusterSide` class provides methods to evaluate its contribution to the strength of a `Link` (see below). + +It is necessary to make the distinction between the sides of `BSCluster` when looking for similarities for merging. Indeed, once a `BSCluster` grows and has more than one `VerticalBusSet`, the attractivity characteristics of both sides are differing. For example, one `BusNode` could be right at the edge of the `LEFT` side in a `HorizontalBusList` that has other `BusNodes` (on its right). For linking with `COMMONBUSES`, the `BusNode` will be exposed only on the `LEFT` side. + +==== The `Link` class + +The key principle of the algorithm relies on the fact that the `Link` class implements `Comparable`, providing a comparison of the strength of each of the 2 considered `Link`. This is established by a lexicographic comparison of grades sorted per `Category`. + +The `enum Category` defines a lexicographic order of the considered kind of similarities between 2 `BSClusterSides`: + +* `COMMONBUSES` +* `FLATCELLS` +* `CROSSOVER` +* `SHUNT` + +The `Link` class holds: + +* 2 `BSClusterSide`, +* a map that holds the similarity grade per `Category`. + +Therefore, the comparison between 2 `Link` is as follow: + +* the more buses there is in common between the 2 `BSClusterSides` the greater the similarity is. +* If equal, then compare flat cells (see note below) as follow: + +** 100 * number of common candidate flat cells. +** minored by the sum of the distances of each flat cell to the edge of each `BSClusterSide` (more details in the note below) + +* if equal, then the number of `InternCell` that will never become flat (and will have to cross over the layout) is compared +* if equal, then compare the attractivity of `ShuntCells` in common (ie having one `ExternCell` in each `BSClusterSide` and for which attractivity is assessed considering their distance to the considered side edge). + +[NOTE] +.About flat cells +==== +* At that stage, they are only considered potential flat cell which type is `CANDIDATEFLATCELL`. They will have to be confirmed as `FLATCELL` later in `Subsection`, +* The flat cells are identified when both legs are at the appropriate side of the 2 considered `BSClusterSide`, which means they are: +** at the begining of a `HorizontalBusSet` if the `BSClusterSide` is `LEFT` +** at the end if it is `RIGHT` +* For a leg, respecting this criteria does not imply the distance to the edge is 0. For example, in the case of the `LEFT` side of the `BSClusterSide`, the `BusNode` involved in the cell shall be at the beginning of its `HorizontalBusSet`, but this does not imply the starting index of the `HorizontalBusSet` to be 0. The distance to the edge is this starting index. +* The value *100* that multiplies the number of flat cells is arbitrary. It must be big enough so that the number of flat cells is fostered over the penalty of the distances to the edges. By doing so, the distance minoration discriminates only when the numbers of flat cells are the same. +==== + +[IMPORTANT] +.Link and side +==== +* No `Link` shall be created between both `BSClusterSide` of the same `BSCluster` as it is not possible to merge them. +* Two `BSClusterSide` having the same side can have a `Link`. If they are to be merged, one will be flipped. +==== + +==== The `Links` class +The `Links` class holds and manages: + +* `bsClusterSides`: a list of all the `BSClusterSide` +* a `TreeSetlinkSet`: which stores all the `Link` feasible between the `bsClusterSides` elements. This `linkSet` is sorted accordingg to the `Comparable` implemented by `Link`. + +When a Link is selected for a merge of its `BSClusterSide`, all the `Link` involving any of both `BSClusterSide` are destroyed. New `Link` are created between both (left/right) `BSClusterSide` created with the newly merged `BSCluster` and each element of `bsClusterSides`. Therefore, `linkSet` always contains an updated sorted set of all the feasible `Link`. + +== Example +=== Input information +The raw graph looks: + +image::images/rawGraphVBS.svg[align="center"] + + +=== Steps +==== Step 1: Build of `VerticalBusSets` + +Contrary to `PositionFromExtension` no order is necessary, let's arbitrary use the suffix in the name of the `BusNode`. + +[cols="4*^"] + +|=== +|vbs | BusNodes(busBarIndex, sectionIndex) | ExternCells | InternCellSides + +|vbs-1 +|[ B1(2, 2), B3(1, 2) ] +|[ EC1 ] +|[ IC2.R, IC3.L ] + +|vbs-2 +|[ B1(2, 2), B4(1, 3) ] +|[ EC2, EC3, EC4 ] +|[ IC3.R ] + +|vbs-3 +|[ B2(2, 1) ] +| +|[ IC1.L ] + +|vbs-4 +|[ B5(1, 1) ] +| +|[ IC1.R, IC2.L ] + +|=== + +==== Step 2.1: Build of unitary `BSCluster` + +[cols="^1, ^2, ^1"] +|=== +|BSCluster | VerticalBusSets | HorizontalBusLists + +|bsc-1 +|[ ( [ B1, B3 ] , [ EC1 ] , [ IC2.R, IC3.L ] ) ] +|[ [ B1(2, 2) ] , [ B3(1, 2) ] ] + +|bsc-2 +|[ ( [ B1, B4 ] , [ EC2, EC3, EC4 ] , [ IC3.R ] ) ] +|[ [ B1(2, 2) ], [ B4(1, 3) ] ] + +|bsc-3 +|[ ( [ B2 ] , , [ IC1.L ] ) ] +|[ [ B2(2, 1) ] ] + +|bsc-4 +|[ ( [ B5 ] , , [ IC1.R , IC2.L ] ) ] +|[ [ B5(1, 1) ] ] + +|=== + +==== Step 2.2: Build the list of `BSClusterSide` and link them + + +Here is a list of all the `Link` that are created with the original list of `BSCluster`, and the grade assessed per `Category`. + +[cols="6*^"] +|=== +|BSClusterSide1|BSClusterSide2|Common Buses|Flat cells|Crossover|Shunt + +|bsc-1.L|bsc-2.L|[B1]-> *1* |[IC3]-> 100*1 = *100* | 0 | 0 +|bsc-1.L|bsc-2.R|[B1]-> *1* |[IC3]-> 100*1 = *100* | 0 | 0 +|bsc-1.R|bsc-2.L|[B1]-> *1* |[IC3]-> 100*1 = *100* | 0 | 0 +|bsc-1.R|bsc-2.R|[B1]-> *1* |[IC3]-> 100*1 = *100* | 0 | 0 +|bsc-1.L|bsc-3.L|*0* |*0* | 0 | 0 +|bsc-1.L|bsc-3.R|*0* |*0* | 0 | 0 +|bsc-1.R|bsc-3.L|*0* |*0* | 0 | 0 +|bsc-1.R|bsc-3.R|*0* |*0* | 0 | 0 +|bsc-1.L|bsc-4.L|*0* |[IC2]-> 100*1 = *100* | 0 | 0 +|bsc-1.L|bsc-4.R|*0* |[IC2]-> 100*1 = *100* | 0 | 0 +|bsc-1.R|bsc-4.L|*0* |[IC2]-> 100*1 = *100* | 0 | 0 +|bsc-1.R|bsc-4.R|*0* |[IC2]-> 100*1 = *100* | 0 | 0 +|bsc-2.L|bsc-3.L|*0* |*0* | 0 | 0 +|bsc-2.L|bsc-3.R|*0* |*0* | 0 | 0 +|bsc-2.R|bsc-3.L|*0* |*0* | 0 | 0 +|bsc-2.R|bsc-3.R|*0* |*0* | 0 | 0 +|bsc-2.L|bsc-4.L|*0* |*0* | 0 | 0 +|bsc-2.L|bsc-4.R|*0* |*0* | 0 | 0 +|bsc-2.R|bsc-4.L|*0* |*0* | 0 | 0 +|bsc-2.R|bsc-4.R|*0* |*0* | 0 | 0 +|bsc-3.L|bsc-4.L|*0* |[IC1]-> 100*1 = *100* | 0 | 0 +|bsc-3.L|bsc-4.R|*0* |[IC1]-> 100*1 = *100* | 0 | 0 +|bsc-3.R|bsc-4.L|*0* |[IC1]-> 100*1 = *100* | 0 | 0 +|bsc-3.R|bsc-4.R|*0* |[IC1]-> 100*1 = *100* | 0 | 0 +|=== + +Let's remove the unnecessary fields for the example (Crossover and Shunt) and sort the table according to the `Link` order. + +[cols="4*^"] +|=== +|BSClusterSide1|BSClusterSide2|Common Buses|Flat cells + +|bsc-1.L|bsc-2.L|[B1]-> *1* |[IC3]-> 100*1 = *100* +|bsc-1.L|bsc-2.R|[B1]-> *1* |[IC3]-> 100*1 = *100* +|bsc-1.R|bsc-2.L|[B1]-> *1* |[IC3]-> 100*1 = *100* +|bsc-1.R|bsc-2.R|[B1]-> *1* |[IC3]-> 100*1 = *100* +|bsc-3.L|bsc-4.L|*0* |[IC1]-> 100*1 = *100* +|bsc-3.L|bsc-4.R|*0* |[IC1]-> 100*1 = *100* +|bsc-3.R|bsc-4.L|*0* |[IC1]-> 100*1 = *100* +|bsc-3.R|bsc-4.R|*0* |[IC1]-> 100*1 = *100* +|bsc-1.L|bsc-4.L|*0* |[IC2]-> 100*1 = *100* +|bsc-1.L|bsc-4.R|*0* |[IC2]-> 100*1 = *100* +|bsc-1.R|bsc-4.L|*0* |[IC2]-> 100*1 = *100* +|bsc-1.R|bsc-4.R|*0* |[IC2]-> 100*1 = *100* +|bsc-1.L|bsc-3.L|*0* |*0* +|bsc-1.L|bsc-3.R|*0* |*0* +|bsc-1.R|bsc-3.L|*0* |*0* +|bsc-1.R|bsc-3.R|*0* |*0* +|bsc-2.L|bsc-3.L|*0* |*0* +|bsc-2.L|bsc-3.R|*0* |*0* +|bsc-2.R|bsc-3.L|*0* |*0* +|bsc-2.R|bsc-3.R|*0* |*0* +|bsc-2.L|bsc-4.L|*0* |*0* +|bsc-2.L|bsc-4.R|*0* |*0* +|bsc-2.R|bsc-4.L|*0* |*0* +|bsc-2.R|bsc-4.R|*0* |*0* +|=== + +==== Step 3: Merge of `BSClusters` into a single one +At the first iteration, the `Link` between *bsc-1.L* and *bsc-2.L* is the strongest. Let's merge them into *bsc-12*: + +[cols="^.^1, ^.^2, ^.^1"] +|=== +|BSCluster | VerticalBusSets | HorizontalBusLists + +|bsc-12 = bsc1 + bsc2 +|[ ( [ B1, B3 ] , [ EC1 ] , [ IC2.R, IC3.L ] ), + +( [ B1, B4 ] , [ EC2, EC3, EC4 ] , [ IC3.R ] ) + ] +|[ [ B1(2, 2) , B1(2, 2) ], + +[ B3(1, 2), B4(1, 3) ] ] + +|bsc-3 +|[ ( [ B2 ] , , [ IC1.L ] ) ] +|[ [ B2(2, 1) ] ] + +|bsc-4 +|[ ( [ B5 ] , , [ IC1.R , IC2.L ] ) ] +|[ [ B5(1, 1) ] ] + +|=== + +[NOTE] +.regarding the merge +==== +* *B1* is common to both, and therefore *B1* spans over 2 indexes in its resulting `HorizontalBusList` +* *IC3* links *B3* and *B4* so they are merged in the same `HorizontalBusList` +==== + +Let's update the list of `Link`. + +[cols="4*^"] +|=== +|BSClusterSide1|BSClusterSide2|Common Buses|Flat cells + +|bsc-3.L|bsc-4.L|*0* |[IC1]-> 100*1 = *100* +|bsc-3.L|bsc-4.R|*0* |[IC1]-> 100*1 = *100* +|bsc-3.R|bsc-4.L|*0* |[IC1]-> 100*1 = *100* +|bsc-3.R|bsc-4.R|*0* |[IC1]-> 100*1 = *100* +|bsc-12.L|bsc-4.L|*0* |[IC2]-> 100*1 = *100* +|bsc-12.L|bsc-4.R|*0* |[IC2]-> 100*1 = *100* +|bsc-12.R|bsc-4.L|*0* |[IC2]-> 100*1 = *100* +|bsc-12.R|bsc-4.R|*0* |[IC2]-> 100*1 = *100* +|bsc-12.L|bsc-3.L|*0* |*0* +|bsc-12.L|bsc-3.R|*0* |*0* +|bsc-12.R|bsc-3.L|*0* |*0* +|bsc-12.R|bsc-3.R|*0* |*0* +|=== + +Now the strongest link is between *bsc-3.L* and *bsc-4.L*. Let's merge them into *bsc-34*: + +[cols="^.^1, ^.^2, ^.^1"] +|=== +|BSCluster | VerticalBusSets | HorizontalBusLists + +|bsc-12 = bsc1 + bsc2 +|[ ( [ B1, B3 ] , [ EC1 ] , [ IC2.R, IC3.L ] ), + +( [ B1, B4 ] , [ EC2, EC3, EC4 ] , [ IC3.R ] )] + +|[ [ B1(2, 2) , B1(2, 2) ], + +[ B3(1, 2), B4(1, 3) ] ] + +|bsc-34 = bsc3 + bsc4 +|[ ( [ B2 ] , , [ IC1.L ] ), + +( [ B5 ] , , [ IC1.R , IC2.L ] ) ] +|[ [ B2(2, 1), B5[1, 1]] ] + +|=== + + +[cols="4*^"] +|=== +|BSClusterSide1|BSClusterSide2|Common Buses|Flat cells + +|bsc-12.L|bsc-34.R|*0* |[IC2]-> 100*1 = *100* +|bsc-12.R|bsc-34.R|*0* |[IC2]-> 100*1 = *100* +|bsc-12.L|bsc-34.L|*0* |*0* +|bsc-12.R|bsc-34.L|*0* |*0* +|=== + +*IC2* is what makes the strongest link. It is on the `RIGHT` side of *bsc-34*, and in its `HorizontalBuslList` *B3* is on its left, therefore *IC2* is not visible from the `LEFT` side, which explains why the flat cell grade is 0 for the last 2 `Link`. + +*Final merge*: *bsc-12.L* with *bsc-34.R* that will become *bsc-3412* as the right side of *bs-34* is connected to the left side of *bsc-12*. (Reminder, when merging 2 `BSClusterSide` having the same side, one is to be flipped -- which is actually not necessary here.) + +[cols="^.^1, ^.^2, ^.^1"] +|=== +|BSCluster | VerticalBusSets | HorizontalBusLists + +|bsc-3412 = bsc34 + bsc12 +|[ ( [ B2 ] , , [ IC1.L ] ), + +( [ B5 ] , , [ IC1.R , IC2.L ] ), + +( [ B1, B3 ] , [ EC1 ] , [ IC2.R, IC3.L ] ), + +( [ B1, B4 ] , [ EC2, EC3, EC4 ] , [ IC3.R ] ) ] + +|[ [ B2(2, 1), B5[1, 1], B1(2, 2) , B1(2, 2)], + +[ . , . , B3(1, 2), B4(1, 3) ] ] + +|=== + +Note that the second `HorizontalBusSet` starts with index 2. + +No "tetrissing" will be required as the final arrangement is directly correct. + +This results in: + +image::images/BSClusterByClusteringFinal.svg[align="center"] + +==== Step 4: Build of the `Listsubsections` +Done by calling `Subsection::createSubsections`. See link:Subsection.adoc[Subsection]. + +=== Final result + +image::images/PositionByClusteringResult.svg[align="center"] + diff --git a/single-line-diagram/single-line-diagram-core/doc/PositionFinder.adoc b/single-line-diagram/single-line-diagram-core/doc/PositionFinder.adoc index 5701be25c..2a8c06b90 100644 --- a/single-line-diagram/single-line-diagram-core/doc/PositionFinder.adoc +++ b/single-line-diagram/single-line-diagram-core/doc/PositionFinder.adoc @@ -1,86 +1,139 @@ -== Position of the Bus nodes and cells order +== Position of `BusNodes` and `Cells` order === Definitions and goal -Positioning the bus nodes and determination of the external cells order are performed by implementing `PositionFinder` interface. +Positioning the `BusNodes` and determination of the `ExternalCells` order are performed by implementing `PositionFinder` interface. -The goal is to set: +The goal is to: -* the horizontal and vertical *structural _(h,v)_* `Position` of the bus nodes: `NodeBus.structuralPosition` -* the horizontal order of the cells: `Cell.order` +* set the horizontal and vertical *structural _(h,v)_* `BusNode.structuralPosition` +* set the horizontal *order* of the cells: `Cell.order` +* provide a `List`. See link:Subsection.adoc[Subsection]. -The figure hereafter shows the information that are to be established. +The picture hereafter shows the information that are to be established. -.(h,v) positions of busbars and `ExternCell` cells order +.(h,v) positions of `BusNodes` and `ExternCell` cells order image::images/busbars.svg[align="center"] -The above structure will be used to explain the different steps of the positioning algorithms. +=== Available implementations +Two implementations are available: -This information can be +* `PositionFromExtension` which rely on explicitly given positions (for example to reflect the on-site real structure and/or the way the SCADA organises it). See link:PositionFromExtension.adoc[PositionFromExtension] +* `PositionByClustering` which finds an organization of the `VoltageLevel` with no other information than the graph itself. See link:PositionByClustering.adoc[PositionByClustering] -* explicitly given (for example to reflect the on site real structure and/or the way the SCADA organises it). One implementation is the `PositionFromExtension` class -* or automatically to fit some layout criteria. One implementation is the `PositionFree` class +Both rely on the `BSCluster` (see link:BSCluster.adoc[BSCluster]) and have the same skeleton: -=== Implementations -==== PositionFromExtension: explicit configuration -The `PositionFromExtension` takes the information from the `iidm` extension. -It is gathered: +* Step 1: Build of `VerticalBusSets` +* Step 2: Build of unitary `BSCluster` in the `bsClusters` list +* Step 3: Merge of `bsClsuters` into a single `BSCluster` +* Step 4: Build of the `Listsubsections` -* in `Graph.visitBusbarSection()` when building the graph for the NodeBus positions -* and completed in `PositionFromExtension.gatherLayoutExtensionInformation()` for the feeders' order, in which the `Cell.orderFromFeederOrders()` is called to determine the order of the cell. +=== Illustration of algorithms based on `BSCluster` +The illustration will be based on the following graph and shall result in the above layout. +image::images/rawGraphVBS.svg[align="center"] +==== step 1: Build `VerticalBusSets` +The result of `VerticalBusSet.createVerticalBusSets` is -==== The `PositionFree` class -The goal of the algorithm is to find an organization of the `VoltageLevel` with no other information that the graph itself. This is a "free" positioning since no constraints are given from a configuration source. - -The key criteria that is followed to find a suitable organisation is the fact that each external cells, and any "leg" of an internal cell can be "stacked" - meaning, that all the corresponding busbars are aligned in parallel to be able to connect them with a vertical string of isolators. (This criteria applies only when the structure of the cell allows it - which may not not the case) +[cols="1,1,1,1"] +|=== +|VerticalBusSet |BusNodes | ExternCells | InternCellSides -A straightforward approach to this criteria would lead to the following structure, that the algorithm will try to reduce. +|vbs-1 +|B3, B1 +|EC1 +|IC2.R, IC3.L -.Raw positionning (the information in gray are not known and will be established by the algorithm) -image::images/rawPosition.svg[align="center"] +|vbs-2 +|B2 +| +|IC1.L +|vbs-3 +|B1, B4 +|EC2, EC3, EC4 +|IC3.R -===== Gathering `VerticalBusConnectionPattern` elements -`VerticalBusConnectionPattern` is na inner class that represents the connection pattern to the busbars as a set of `busNode` for: +|vbs-4 +|B5 +| +|IC1.R, IC2.L -* an external cell -* or a leg of an intern cell. +|=== -Let's call them indistinctly as *cell leg*. +[NOTE] +At that stage, the `LEFT` and `RIGHT` side of an `IntenCell` is arbitrary. They will be flipped if necessary later on (handled in `Subsection.createSubsections`). -.verticalBusConnectionPattern of cell legs -image::images/verticalBusConnectionPattern.svg[align="center"] +==== step 2: Build unitary `BSClusters` -This enables to gather all the cell legs having the same pattern as they share the same organisation constraint. +This consist in creating one `BSCluster` per `VerticalBusSet`. This results in: -The possible `VerticalBusConnectionPatterns` are gathered as the key of the `vbcpToCells` map, the value being the list of `ExternCell` cells having this pattern (no need to gather internal cells). +[cols=".^1, .^2, 1"] +|=== +|BSCluster | VerticalBusSets | HorizontalBusLists -When the `vbcpToCells` map is built, if a pattern is fully included or fully embraces an already existing `vbcp`, they are merged into the embracing one. For example, the pattern (3) is included in the pattern (3,5). +|bsc-1 +|[ ( [B3, B1] , [EC1] , [IC2.R, IC3.L] ) ] +|[ [B3] , [B1] ] -CAUTION: Different merges of vbcp can occurs if the structure is not visited in the same order: for example if 3 external cells have (1,2) (1,3) and (1) as vbcp: the cell having the vbcp (1) could be included either into the (1,2) or (1,3) vbcp. +|bsc-2 +|[ ( [B2] , , [IC1.L] ) ] +|[ [B2] ] -===== Structuring with internal cells -If the two legs of an internal cell are fully included into a single known vbcp, then this cell is considered as vertical and is not structuring. Indeed, one cell leg already constraints all the busbars of the cell to be parallel. +|bsc-3 +|[ ( [B1, B4] , [EC2, EC3, EC4] , [IC3.R] ) ] +|[ [B1], [B4] ] -This would lead to: +|bsc-4 +|[ ( [B5] , , [IC1.L , IC2.L] ) ] +|[ [B5] ] -.vbcpToCell |=== -| vbcp | External Cells -|(1) | - -|(2) | - -|(3,4) | 1 External cells -|(3,5) | 3 External cells +image::images/BSClusterInit.svg[align="center"] + +[IMPORTANT] +.On this result: +==== + - It is representative of the general case. But, note that for `PositionFromExtension` the `verticalBusSets` is sorted to end up to a ready-to-merge `bsClusters`. See link:PositionFromExtension.adoc[PositionFromExtension]. + - Regarding the picture: the rows do not exist in real. It presented to highlight we have no clue of whether one `NodeBus` will be horizontally aligned with one another, except we know for sure that both `B1` shall end up in the same row. +==== + +==== step 3: Merge `BSClusters` into a single one +That's where the magic happens. This is where the implementations mainly differ. The goal is to merge the `BSClusters` to one another. + +The principle of the merging of a `BSCluster` are: + +- simply concat the `VerticalBusSet` List +- merge the `HorizontalBusList` using a proper implementation of `HorizontalBusLane::mergeHbl`. + +This expected result should be similar to the following `BSCluster`: + +image::images/BSClusterFinal.svg[align="center"] + +[cols="1, 1"] |=== +|VerticalBusSets | HorizontalBusLists + +| [ + +( [B2, B5] , , [IC1.L, IC1.R, IC2.L] ) , + +( [B4, B1] , [EC2, EC3, EC4] , [IC3.R] ) , + +( [B3, B1] , [EC1] , [IC2.R, IC3.L] ) -Therefore the algorithm works on structuring cells that are the ones that are not vertical. +] + +| [ + + [B2, B4, B3] , + + [B5, B1, B1] + +] +|=== -Among these structuring cells, the algorithm will look for candidate flat cells (reminder: a flat cell is a 3-node cell having the `BUS-SWITCH-BUS` pattern for which the two buses have the same vertical position, and have contiguous horizontal positions). +The way this example is handled is detailed in each implementation documentation: link:PositionFromExtension.adoc[PositionFromExtension], link:PositionByClustering[PositionByClustering]. -Then it will chain the `BUS` nodes with the `candidateFlatCell` into `HorizontalChain` objects. -`VerticalBusConnectionPattern` class:: -qsd -`VerticalBusConnectionPattern` class:: -qsd +==== step 4: Build the `List` diff --git a/single-line-diagram/single-line-diagram-core/doc/PositionFromExtension.adoc b/single-line-diagram/single-line-diagram-core/doc/PositionFromExtension.adoc new file mode 100644 index 000000000..e0b409ff1 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/PositionFromExtension.adoc @@ -0,0 +1,224 @@ += `PositionFromExtension`: Position from an explicit configuration + +== Context +`PositionFromExtension` implements link:PositionFinder.adoc[PositionFinder] and takes the information from the `iidm` extension to organize `BSClusters`. + When building the `VoltageLevelGraph`, `NetworkGraphBuilder` retrieves and sets: + +* `BusNode.busbarIndex` and `BusNode.sectionIndex`, +* `FeederNode.order` which is used to define `ExternCells::getOrder` + + +== Algorithm +=== Principle +The algorithm considers that the positional information given (by the *iidm* extension) is coherent. Sorting orders are defined based on this information and are used when building `VerticalBusSets` and `BSClusters`. These objects can then be arranged relying on the consistency inherited from the given coherent orders. + +The algorithm may sound not straightforward, but that approach eases the elaboration of the next step (building of `List`), and naturally addresses the constraints raised by non horizontaly symetrical arrangements of `BusNodes` (case developed in example below). + +=== Steps + +* `PositionFromExtension::indexBusPosition` builds the `Map busToNb` +* `PositionFromExtension::organizeBusSets`: +** builds `ListbsClusters` by providing the list of initial `VerticalBusSet` sorted by the comparator `VBSCOMPARATOR` +** merges `bsClusters` sequentially by merging the first `BSCluster` of the list with the second until only one remain. + +==== Sorting the `BusNodes` of the `VerticalBusSets` +This sorting order foster `busbarIndex` reflecting the *vertical view* of the structure. + +The algorithm rely on the fact that +`PositionFromExtension::indexBusPosition` sorts the `BusNodes` by their `busbarIndex` (first, and then by their `sectionIndex`). In `PositionFromExtension`, this order will never be modified later on, ensuring the lower the `busbarIndex` is, the higher it will be vertically laid out. + +For example, + +* if `BusNode` *A* has a lower `busBarIndex` than the one of *B*, +* *A* will be before *B* in a `VerticalBusSet`, +* *A* will be laid out above *B*. + +==== Sorting the `bsClusters` +This sorting order foster `sectionIndex` reflecting the *horizontal view* of the structure. + +`VBSCOMPARATOR` is designed to sort the given `ListverticalBusSet` using the positional information given by the extension. It sorts: + +* *First and most important sorting rule*: compare `sectionIndex` (which corresponds to horizontal order) of `BusNodes` having the same `busbarIndex`. ie: +** Match a pair of `BusNodes` having the same `busbarIndex`, taken in: +*** the right side of the first `BSCluster` +*** the left side of the second `BSCluster`. +** If they have different `BusNode.sectionIndex`, *return* the difference +** if they are equal, then try again with another pair. +* *Else*, compare `BusCell::getOrder` of `BusCells` in both `VerticalBusSets` +* *Else*, some rules, to ensure uniqueness. + +==== Merging +The merging process consists in repeating the merging of the first `BSCluster` with the second one until only one remains. + +`HorizontalBusListManager::mergeHbl` merges the `HorizontalBusLists` from the 2 `BSClusters` by concatenating them when they have the same `busbarIndex`. As the `BSClusters` si sorted by `busbarIndex`, there is a coherent progression in each merged `HorizontalBusList`. + +== Example +=== The end in mind +The picture hereafter shows what we expect to display. The complete information from the extension is given. + +.(h,v) positions of `BusNodes` and `ExternCell` cells order +image::images/busbars.svg[align="center"] + +=== Input information +The raw graph looks: + +image::images/rawGraphVBS.svg[align="center"] + +For which the information is represented as follow: + +[ cols="2*^" ] +|=== +|raw Bus id | (busbarIndex, sectionIndex) + +| B1 | (2, 2) +| B2 | (2, 1) +| B3 | (1, 2) +| B4 | (1, 3) +| B5 | (1, 1) +|=== + +And the `ExternalCell` `order` of *ECx* is *x*. + +=== Steps +==== Step 1: Build of `VerticalBusSets` + +First we define the `Map busToNb` according to the sorting order `(busbarIndex, sectionIndex)`: *vertical sorting*. + +[ cols="3*^" ] +|=== +|raw Bus id | Nb | (busbarIndex, sectionIndex) + +| B5 | 1 | (1, 1) +| B3 | 2 | (1, 2) +| B4 | 3 | (1, 3) +| B2 | 4 | (2, 1) +| B1 | 5 | (2, 2) +|=== + +Now `VerticalBusSet.createVerticalBusSets` will create the `VerticalBusSet` and they will be sort it according to `VBSCOMPARATOR`: *horizontal sorting*. + +[cols="4*^"] + +|=== +|vbs | BusNodes(busBarIndex, sectionIndex) | ExternCells | InternCellSides + +|vbs-1 +|[ B5(1, 1) ] +| +|[ IC1.R, IC2.L ] + +|vbs-2 +|[ B2(2, 1) ] +| +|[ IC1.L ] + +|vbs-3 +|[ B3(1, 2), B1(2, 2) ] +|[ EC1 ] +|[ IC2.R, IC3.L ] + +|vbs-4 +|[ B4(1, 3), B1(2, 2) ] +|[ EC2, EC3, EC4 ] +|[ IC3.R ] + +|=== + +Note the position of *vbs-2*, as *B2(2,1)* shall be before *B1(2,2)*. + +==== Step 2: Build of unitary `BSClusters` + +This consist in creating one `BSCluster` per `VerticalBusSet`. This results in: + +[cols="^1, ^2, ^1"] +|=== +|BSCluster | VerticalBusSets | HorizontalBusLists + +|bsc-1 +|[ ( [ B5 ] , , [ IC1.R , IC2.L ] ) ] +|[ [ B5(1, 1) ] ] + +|bsc-2 +|[ ( [ B2 ] , , [ IC1.L ] ) ] +|[ [ B2(2, 1) ] ] + +|bsc-3 +|[ ( [ B3, B1 ] , [ EC1 ] , [ IC2.R, IC3.L ] ) ] +|[ [ B3(1, 2) ] , [ B1(2, 2) ] ] + +|bsc-4 +|[ ( [ B1, B4 ] , [ EC2, EC3, EC4 ] , [ IC3.R ] ) ] +|[ [ B1(2, 2) ], [ B4(1, 3) ] ] + +|=== + +==== Step 3: Merge of `BSClusters` into a single one + +[cols="^.^1, ^.^2, ^.^2"] +|=== +|BSCluster | VerticalBusSets | HorizontalBusLists + +|bsc-12 + += bsc-1 + *bsc-2* + +| [ ( [ B5 ] , , [ IC1.R , IC2.L ] ), + +*( [ B2 ] , , [ IC1.L ] )* ] + +| [ [ B5(1,1) ], + +*[ B2(2,1) ]* ] + + +|bsc-123 + += bsc-12 + *bsc-3* + +| [ ( [ B5 ] , , [ IC1.R , IC2.L ] ), + +( [ B2 ] , , [ IC1.L ] ), + +*( [ B3, B1 ] , [ EC1 ] , [ IC2.R, IC3.L ] )* ] + +| [ [ B5(1,1), *B5(1,1), B3(1,2)* ], + +[ B2(2,1), *B1(2,2)* ] ] + + + + +|*Resulting BSCluster* + +bsc-1234 + += bsc-123 + *bsc-4* + +| [ ( [ B5 ] , , [ IC1.R , IC2.L ] ), + +( [ B2 ] , , [ IC1.L ] ), + +( [ B3, B1 ] , [ EC1 ] , [ IC2.R, IC3.L ] ), + +( *[ B1, B4 ] , [ EC2, EC3, EC4 ] , [ IC3.R ] )* ] + +| [ [ B5(1,1), B5(1,1), B3(1,2), *B4(1,3)* ], + +[ B2(2,1), B1(2,2), *B1(2,2)* ] ] + +|=== + +This results in: + +image::images/BSClusterFromExtensionFinal.svg[align="center"] + +[NOTE] +.On the merge of *bsc-1 + bsc-2*: +==== +* *bsc-1* and *bsc-2* have only one `NodeBus` in their `VerticalBusSet`. The parallelization of both will be handled by `Subsection::createSubsections` by an absorption mechanism. +* in its `HorizontalBusList`, *B5* is replicated until a change occurs, but this replication has no impact on the `VerticalBusSet` (ie *[ B2 ]* is not extended to *[ B5, B2 ]*). +* `HorizontalBusList` has a `startingIndex` which implies it does not necessarily align on the left side. That's the case of the second one for which the `startingIndex` is 2. +==== + +==== Step 4: Build of the `Listsubsections` +Done by calling `Subsection::createSubsections`. See link:Subsection.adoc[Subsection]. \ No newline at end of file diff --git a/single-line-diagram/single-line-diagram-core/doc/Subsection.adoc b/single-line-diagram/single-line-diagram-core/doc/Subsection.adoc new file mode 100644 index 000000000..615f1205d --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/Subsection.adoc @@ -0,0 +1,41 @@ +== `Subsection` +=== Context +A `Subsection` is a part of the `VoltageLevel` defined by the `BusNodes` that must be displayed in parallel. The `VoltageLevel` is partitionned into `Subsections` each of them having a different set of `BusNodes`. + +**** +image::images/subsections.svg[] +**** + +A `List` will then be given to `BlockPositionner` that will sequentially treat each `Subsection` assigning the `Positions` *(H,V)* of `BusNodes` and `Cells`. + +Therefore the `List` shall consistently respect the following rule: if a `BusNode` spans over many `Subsections`, it shall: + +- be in contiguous `Subsections` in the list +- have the same index (ie same vertical position) in each `Subsection` + +=== Definition +**** +A `Substation` has the following attributes: + +- `size` which is the number of parallel `BusNodes` and is defined in the constructor and cannot be changed. +- `busNodes` a `BusNodes` array of size `size` +- `externCells` a list of `ExternCell` +- `InternCellSides` a Set `InternCellSide` which correspond to the leg of one `InternCell` combined with its `Side`. +**** + +[NOTE] +.When an `InternCell` overlaps 2 `Subsections` +==== + - its `LEFT` leg will be on the right side of the first `Subsection`, ie at the end of `internCellSides` + - its `RIGHT` leg will be on the left side of the second `Subsection`, ie at the beginning of `internCellSides` +==== + + +=== Building the `List` +`Subsection` has `createSubsections` as a utility method to build the `List subsections` based on a consolidated `BSCluster` and is called by implementations of `PositionFinder`. + +It calls many methods that aim at ensuring the coherence of the position of the `Cells`. Especially, that's where + +- `InternCell.Shape` are defined, +- `InternCell` are flipped (`RIGHT` / `LEFT`) if needed to be consistent with the arrangement, +- in case of `handleShunt` is set, `ExternCells` involved in a `ShuntCell` are shifted to be as close to one another as possible. diff --git a/single-line-diagram/single-line-diagram-core/doc/Subsections.adoc b/single-line-diagram/single-line-diagram-core/doc/Subsections.adoc deleted file mode 100644 index 558d23f77..000000000 --- a/single-line-diagram/single-line-diagram-core/doc/Subsections.adoc +++ /dev/null @@ -1,5 +0,0 @@ -== Manages `Subsections` -Subsections detects and manages the case of none parallel busbar structures. -**** -image::images/subsections.svg[] -**** diff --git a/single-line-diagram/single-line-diagram-core/doc/VerticalBusSet.adoc b/single-line-diagram/single-line-diagram-core/doc/VerticalBusSet.adoc new file mode 100644 index 000000000..bf2a26154 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/VerticalBusSet.adoc @@ -0,0 +1,17 @@ += `VerticalBusSet` + +We have the principle that all the `BusNodes` of an `ExternCell` shall be presented in parallel, as well as all the `BusNode` of one side of an `InternCell`. + +Therefore, a `VerticalBusSet` gather a set of BusNode that shall be presented in parallel. + +**** +It is composed of: + +- a set of `BusNodes` that are to be in parallel +- a set of `ExternCell` and a set `InternCellSide` (composition of an InternCell and the `Side` of the leg) that put the verticality constraint. +**** + +When the set of `BusNodes` of one `VerticalBusSet` contains all the `BusNodes` of the one of another, then it can absorb it (all the constraints given by the second one are covered by the one of the first one). The absorbtion consist in merging the sets of cells. + +*** +`VerticalBusSet` has an utility method `createVerticalBusSets` that creates a `List` going through the cells of a `VoltageLevelGraph` (on which the cells detection was already done!) and ensuring that all the possible absorbtions are performed. \ No newline at end of file diff --git a/single-line-diagram/single-line-diagram-core/doc/images/BSClusterByClusteringFinal.svg b/single-line-diagram/single-line-diagram-core/doc/images/BSClusterByClusteringFinal.svg new file mode 100644 index 000000000..67fc72c0b --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/images/BSClusterByClusteringFinal.svg @@ -0,0 +1,330 @@ + + + + + + + + + + image/svg+xml + + + + + + + B4 + + + + B5 + + + + + BSCluster + + VerticalBusSet + + HorizontalBusList + + BusNode + + + B1 + + + + B1 + + + + B3 + + + + + + + B2 + + + + diff --git a/single-line-diagram/single-line-diagram-core/doc/images/BSClusterFinal.svg b/single-line-diagram/single-line-diagram-core/doc/images/BSClusterFinal.svg new file mode 100644 index 000000000..7839c008b --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/images/BSClusterFinal.svg @@ -0,0 +1,339 @@ + + + + + + + + + + image/svg+xml + + + + + + + + B3 + + + + B1 + + + + B2 + + + + B5 + + + + + + BSCluster + + VerticalBusSet + + HorizontalBusList + + BusNode + + + B3 + + + + B4 + + + + B1 + + + + + diff --git a/single-line-diagram/single-line-diagram-core/doc/images/BSClusterFromExtensionFinal.svg b/single-line-diagram/single-line-diagram-core/doc/images/BSClusterFromExtensionFinal.svg new file mode 100644 index 000000000..501de9b13 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/images/BSClusterFromExtensionFinal.svg @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + B4 + + + + B1 + + + + + + + + B2 + + + + + BSCluster + + VerticalBusSet + + HorizontalBusList + + BusNode + + + B3 + + + + B1 + + + + + + + B5 + + + + diff --git a/single-line-diagram/single-line-diagram-core/doc/images/BSClusterInit.svg b/single-line-diagram/single-line-diagram-core/doc/images/BSClusterInit.svg new file mode 100644 index 000000000..b48b5d470 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/images/BSClusterInit.svg @@ -0,0 +1,375 @@ + + + + + + + + + + image/svg+xml + + + + + + + + B3 + + + + B1 + + + + B2 + + + + B1 + + + + B4 + + + + B5 + + + + + + + + + + + + + + + + + BSCluster + + VerticalBusSet + + HorizontalBusList + + BusNode + diff --git a/single-line-diagram/single-line-diagram-core/doc/images/CellType_INTERN.svg b/single-line-diagram/single-line-diagram-core/doc/images/CellType_INTERN.svg deleted file mode 100644 index db3e02bd7..000000000 --- a/single-line-diagram/single-line-diagram-core/doc/images/CellType_INTERN.svg +++ /dev/null @@ -1,260 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - busbars - subsections - - - - diff --git a/single-line-diagram/single-line-diagram-core/doc/images/Cells.svg b/single-line-diagram/single-line-diagram-core/doc/images/Cells.svg deleted file mode 100644 index 69a1597f2..000000000 --- a/single-line-diagram/single-line-diagram-core/doc/images/Cells.svg +++ /dev/null @@ -1,365 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - busbars - subsections - - - - diff --git a/single-line-diagram/single-line-diagram-core/doc/images/PositionByClusteringResult.svg b/single-line-diagram/single-line-diagram-core/doc/images/PositionByClusteringResult.svg new file mode 100644 index 000000000..cf521d452 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/images/PositionByClusteringResult.svg @@ -0,0 +1,562 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + B1 + B3 + B4 + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + + + + + + + + + + + + + B5 + B2 + + IC1 + IC2 + IC3 + diff --git a/single-line-diagram/single-line-diagram-core/doc/images/rawGraph.svg.2019_04_01_16_43_32.0.svg b/single-line-diagram/single-line-diagram-core/doc/images/rawGraph.svg.2019_04_01_16_43_32.0.svg deleted file mode 100644 index 5bf92d807..000000000 --- a/single-line-diagram/single-line-diagram-core/doc/images/rawGraph.svg.2019_04_01_16_43_32.0.svg +++ /dev/null @@ -1,865 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/single-line-diagram/single-line-diagram-core/doc/images/rawGraphVBS.svg b/single-line-diagram/single-line-diagram-core/doc/images/rawGraphVBS.svg new file mode 100644 index 000000000..66e804192 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/doc/images/rawGraphVBS.svg @@ -0,0 +1,844 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IC1 + + IC2 + EC1 + EC2 + EC3 + IC3 + + EC4 + B2 + B5 + B3 + B1 + B4 + EC: ExternCellIC: InternCellB: BusNode + + + + + + diff --git a/single-line-diagram/single-line-diagram-core/doc/images/rawPosition.svg b/single-line-diagram/single-line-diagram-core/doc/images/rawPosition.svg deleted file mode 100644 index a94e75fcd..000000000 --- a/single-line-diagram/single-line-diagram-core/doc/images/rawPosition.svg +++ /dev/null @@ -1,580 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (1,1) - (2,1) - (1,2) - (2,2) - (1,3) - - - 1 - - - - 2 - - - - 3 - - - - 4 - - - - diff --git a/single-line-diagram/single-line-diagram-core/doc/images/verticalBusConnectionPattern.svg b/single-line-diagram/single-line-diagram-core/doc/images/verticalBusConnectionPattern.svg deleted file mode 100644 index f63a301fe..000000000 --- a/single-line-diagram/single-line-diagram-core/doc/images/verticalBusConnectionPattern.svg +++ /dev/null @@ -1,605 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 - - 2 - 3 - 4 - 5 - (1) - (2) - (1) - (3) - (4) - (5) - (3,5) - (3,4) - (3,5) - (3,5) - verticalBusConnectionPatterns - - - diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockOrganizer.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockOrganizer.java index 798a43208..7a7083f79 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockOrganizer.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockOrganizer.java @@ -7,6 +7,9 @@ package com.powsybl.sld.layout; import com.powsybl.commons.PowsyblException; +import com.powsybl.sld.layout.position.BlockPositionner; +import com.powsybl.sld.layout.position.PositionFinder; +import com.powsybl.sld.layout.position.Subsection; import com.powsybl.sld.model.blocks.FeederPrimaryBlock; import com.powsybl.sld.model.blocks.LegPrimaryBlock; import com.powsybl.sld.model.cells.*; diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalBusLaneManager.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalBusLaneManager.java deleted file mode 100644 index b9e889df2..000000000 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalBusLaneManager.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2020, 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/. - */ -package com.powsybl.sld.layout; - -/** - * @author Benoit Jeanson {@literal } - */ - -public interface HorizontalBusLaneManager { - - void mergeHorizontalBusLanes(LBSCluster leftCluster, LBSCluster rightCluster); - - default void mergeLanesWithNoLink(LBSCluster leftCluster, LBSCluster rightCluster) { - rightCluster.getHorizontalBusLanes().forEach(lane -> { - lane.shift(leftCluster.getLength()); - lane.setLbsCluster(leftCluster); - }); - leftCluster.getHorizontalBusLanes().addAll(rightCluster.getHorizontalBusLanes()); - rightCluster.getHorizontalBusLanes().removeAll(rightCluster.getHorizontalBusLanes()); - } -} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/ImplicitCellDetector.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/ImplicitCellDetector.java index db04e60fe..80924275b 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/ImplicitCellDetector.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/ImplicitCellDetector.java @@ -13,6 +13,8 @@ import com.powsybl.sld.model.coordinate.Side; import com.powsybl.sld.model.graphs.VoltageLevelGraph; import com.powsybl.sld.model.nodes.*; +import com.powsybl.sld.util.GraphTraversal; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LBSCluster.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LBSCluster.java deleted file mode 100644 index 54e98ea11..000000000 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LBSCluster.java +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) 2020, 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/. - */ -package com.powsybl.sld.layout; - -import com.powsybl.sld.model.cells.InternCell; -import com.powsybl.sld.model.coordinate.Side; -import com.powsybl.sld.model.nodes.BusNode; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * LBSCluster contains a list of LegBusSets (LBS) that is orderly build by successively merging LBSCluster initially - * containing a single LBS. - * LBSCluster handles the building of the horizontalBusLanes that are an horizontal strings of busNodes. - * - * @author Benoit Jeanson {@literal } - */ -public class LBSCluster { - - private final List lbsList = new ArrayList<>(); - private final List horizontalBusLanes = new ArrayList<>(); - private final int nb; - - public LBSCluster(LegBusSet lbs, int nb) { - Objects.requireNonNull(lbs); - lbsList.add(lbs); - lbs.getBusNodeSet().forEach(nodeBus -> horizontalBusLanes.add(new HorizontalBusLane(nodeBus, this))); - - this.nb = nb; - } - - public static List createLBSClusters(List legBusSets) { - List lbsClusters = new ArrayList<>(); - int nb = 0; - for (LegBusSet lbs : legBusSets) { - lbsClusters.add(new LBSCluster(lbs, nb++)); - } - return lbsClusters; - } - - public void merge(Side myConcernedSide, LBSCluster otherLbsCluster, Side otherSide, HorizontalBusLaneManager hblManager) { - if (myConcernedSide == Side.LEFT) { - reverse(); - } - if (otherSide == Side.RIGHT) { - otherLbsCluster.reverse(); - } - hblManager.mergeHorizontalBusLanes(this, otherLbsCluster); - lbsList.addAll(otherLbsCluster.lbsList); - } - - public List laneSideBuses(Side side) { - return laneSideBuses(side, horizontalBusLanes); - } - - public static List laneSideBuses(Side side, List horizontalBusLaneList) { - return horizontalBusLaneList.stream() - .map(hl -> hl.getSideNode(side)).collect(Collectors.toList()); - } - - public void removeHorizontalBusLane(HorizontalBusLane lane) { - horizontalBusLanes.remove(lane); - } - - public void establishBusNodePosition() { - int v = 1; - for (HorizontalBusLane lane : horizontalBusLanes) { - lane.establishBusPosition(v); - v++; - } - } - - public HorizontalBusLane getHorizontalLaneFromSideBus(BusNode busNode, Side side) { - return horizontalBusLanes - .stream() - .filter(horizontalBusLane -> horizontalBusLane.getSideNode(side) == busNode) - .findAny() - .orElse(null); - } - - List getVerticalBuseNodes(int i) { - return horizontalBusLanes.stream().map(hbl -> hbl.getBusNode(i)).collect(Collectors.toList()); - } - - private void reverse() { - Collections.reverse(lbsList); - horizontalBusLanes.forEach(lane -> lane.reverse(lbsList.size())); - } - - private LegBusSet getLbsSideFromBusNode(BusNode busNode, Side side) { - if (side != Side.RIGHT && side != Side.LEFT) { - return null; - } - for (int i = 0; i < lbsList.size(); i++) { - int j = side == Side.LEFT ? i : lbsList.size() - i - 1; - if (lbsList.get(j).getBusNodeSet().contains(busNode)) { - return lbsList.get(j); - } - } - return null; - } - - public List getSideCandidateFlatCell(Side side) { - return laneSideBuses(side).stream() - .map(busNode -> getLbsSideFromBusNode(busNode, side)) - .distinct().filter(Objects::nonNull) - .flatMap(lbs -> lbs.getInternCellsFromShape(InternCell.Shape.MAYBE_FLAT).stream()) - .collect(Collectors.toList()); - } - - public List getInternCellsFromShape(InternCell.Shape shape) { - return lbsList.stream() - .flatMap(legBusSet -> legBusSet.getInternCellsFromShape(shape).stream()) - .collect(Collectors.toList()); - } - - public void sortHorizontalBusLanesByVPos() { - horizontalBusLanes.sort(Comparator.comparingInt(hbl -> hbl.getBusNodes().get(0).getBusbarIndex())); - } - - public int getLength() { - return lbsList.size(); - } - - public List getHorizontalBusLanes() { - return horizontalBusLanes; - } - - public List getLbsList() { - return lbsList; - } - - @Override - public String toString() { - return lbsList.toString() + "\n" + horizontalBusLanes.toString(); - } - - int getNb() { - return nb; - } -} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionByClusterVoltageLevelLayoutFactorySmartSelector.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionByClusterVoltageLevelLayoutFactorySmartSelector.java index f0b7f2722..0dae41944 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionByClusterVoltageLevelLayoutFactorySmartSelector.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionByClusterVoltageLevelLayoutFactorySmartSelector.java @@ -9,7 +9,7 @@ import com.google.auto.service.AutoService; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.VoltageLevel; -import com.powsybl.sld.layout.positionbyclustering.PositionByClustering; +import com.powsybl.sld.layout.position.clustering.PositionByClustering; /** * @author Geoffroy Jamgotchian {@literal } diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayoutFactory.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayoutFactory.java index 32287891c..974c1a719 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayoutFactory.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayoutFactory.java @@ -6,7 +6,8 @@ */ package com.powsybl.sld.layout; -import com.powsybl.sld.layout.positionfromextension.PositionFromExtension; +import com.powsybl.sld.layout.position.PositionFinder; +import com.powsybl.sld.layout.position.predefined.PositionPredefined; import com.powsybl.sld.model.graphs.VoltageLevelGraph; import java.util.Objects; @@ -23,11 +24,11 @@ public class PositionVoltageLevelLayoutFactory implements VoltageLevelLayoutFact private PositionVoltageLevelLayoutFactoryParameters positionVoltageLevelLayoutFactoryParameters = new PositionVoltageLevelLayoutFactoryParameters(); public PositionVoltageLevelLayoutFactory() { - this(new PositionFromExtension()); + this(new PositionPredefined()); } public PositionVoltageLevelLayoutFactory(PositionVoltageLevelLayoutFactoryParameters positionVoltageLevelLayoutFactoryParameters) { - this(new PositionFromExtension()); + this(new PositionPredefined()); this.positionVoltageLevelLayoutFactoryParameters = positionVoltageLevelLayoutFactoryParameters; } diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayoutFactoryCreator.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayoutFactoryCreator.java index fb1a81975..4238a0e37 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayoutFactoryCreator.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayoutFactoryCreator.java @@ -9,6 +9,7 @@ package com.powsybl.sld.layout; import com.powsybl.iidm.network.Network; +import com.powsybl.sld.layout.position.PositionFinder; /** * diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/AbstractPositionFinder.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/AbstractPositionFinder.java similarity index 52% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/AbstractPositionFinder.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/AbstractPositionFinder.java index 8a24f1255..419d33074 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/AbstractPositionFinder.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/AbstractPositionFinder.java @@ -4,9 +4,9 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.layout.position; -import com.powsybl.sld.model.coordinate.Side; +import com.powsybl.sld.model.cells.BusCell; import com.powsybl.sld.model.graphs.VoltageLevelGraph; import com.powsybl.sld.model.nodes.BusNode; @@ -20,24 +20,31 @@ */ public abstract class AbstractPositionFinder implements PositionFinder { + public abstract Map indexBusPosition(List busNodes, List busCells); + + public abstract BSCluster organizeBusSets(VoltageLevelGraph graph, List verticalBusSets); + + public abstract void organizeDirections(VoltageLevelGraph graph, List subsections); + public List buildLayout(VoltageLevelGraph graph, boolean handleShunt) { if (graph.getNodes().isEmpty()) { return new ArrayList<>(); } Map busToNb = indexBusPosition(graph.getNodeBuses(), graph.getBusCells()); - List legBusSets = LegBusSet.createLegBusSets(graph, busToNb, handleShunt); - LBSCluster lbsCluster = organizeLegBusSets(graph, legBusSets); + List verticalBusSets = VerticalBusSet.createVerticalBusSets(graph, busToNb); + BSCluster bsCluster = organizeBusSets(graph, verticalBusSets); graph.setMaxBusPosition(); - List subsections = Subsection.createSubsections(graph, lbsCluster, handleShunt); + List subsections = Subsection.createSubsections(graph, bsCluster, busToNb, handleShunt); organizeDirections(graph, subsections); return subsections; } - public void forceSameOrientationForShuntedCell(VoltageLevelGraph graph) { - graph.getShuntCellStream().forEach(sc -> sc.alignDirections(Side.LEFT)); - } - - public void organizeDirections(VoltageLevelGraph graph, List subsections) { - forceSameOrientationForShuntedCell(graph); + public static void mergeHblWithNoLink(BSCluster leftCluster, BSCluster rightCluster) { + rightCluster.getHorizontalBusLists().forEach(hbl -> { + hbl.shift(leftCluster.getLength()); + hbl.setBsCluster(leftCluster); + }); + leftCluster.getHorizontalBusLists().addAll(rightCluster.getHorizontalBusLists()); + rightCluster.getHorizontalBusLists().removeAll(rightCluster.getHorizontalBusLists()); } } diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/BSCluster.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/BSCluster.java new file mode 100644 index 000000000..08d1643b2 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/BSCluster.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2020, 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/. + */ +package com.powsybl.sld.layout.position; + +import com.powsybl.sld.model.cells.InternCell; +import com.powsybl.sld.model.coordinate.Side; +import com.powsybl.sld.model.nodes.BusNode; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * BSCluster (Bus Set Cluster) contains a list of VerticalBusSets (VBS) that is orderly build by successively merging BSCluster initially + * containing a single VBS. + * BSCluster handles the building of the horizontalBusLists that are an horizontal strings of busNodes. + * + * @author Benoit Jeanson {@literal } + */ +public class BSCluster { + + private final List verticalBusSets = new ArrayList<>(); + private final List horizontalBusLists = new ArrayList<>(); + + public BSCluster(VerticalBusSet vbs) { + Objects.requireNonNull(vbs); + verticalBusSets.add(vbs); + vbs.getBusNodeSet().forEach(nodeBus -> horizontalBusLists.add(new HorizontalBusList(nodeBus, this))); + } + + public static List createBSClusters(List verticalBusSets) { + List bsClusters = new ArrayList<>(); + for (VerticalBusSet vbs : verticalBusSets) { + bsClusters.add(new BSCluster(vbs)); + } + return bsClusters; + } + + public void merge(Side myConcernedSide, BSCluster otherBsCluster, Side otherSide, + HorizontalBusListsMerger hblMerger) { + if (myConcernedSide == Side.LEFT) { + reverse(); + } + if (otherSide == Side.RIGHT) { + otherBsCluster.reverse(); + } + hblMerger.apply(this, otherBsCluster); + verticalBusSets.addAll(otherBsCluster.verticalBusSets); + } + + public List hblSideBuses(Side side) { + return hblSideBuses(side, horizontalBusLists); + } + + public static List hblSideBuses(Side side, List hblList) { + return hblList.stream() + .map(hl -> hl.getSideNode(side)).collect(Collectors.toList()); + } + + public void removeHbl(HorizontalBusList hbl) { + horizontalBusLists.remove(hbl); + } + + public void establishBusNodePosition() { + int v = 1; + for (HorizontalBusList hbl : horizontalBusLists) { + hbl.establishBusPosition(v); + v++; + } + } + + public Optional getHblFromSideBus(BusNode busNode, Side side) { + return horizontalBusLists + .stream() + .filter(hbl -> hbl.getSideNode(side) == busNode) + .findFirst(); + } + + List getVerticalBusNodes(int i) { + return horizontalBusLists.stream().map(hbl -> hbl.getBusNode(i)).filter(Objects::nonNull).collect(Collectors.toList()); + } + + private void reverse() { + Collections.reverse(verticalBusSets); + horizontalBusLists.forEach(hbl -> hbl.reverse(verticalBusSets.size())); + } + + private VerticalBusSet getVbsSideFromBusNode(BusNode busNode, Side side) { + if (side != Side.RIGHT && side != Side.LEFT) { + return null; + } + for (int i = 0; i < verticalBusSets.size(); i++) { + int j = side == Side.LEFT ? i : verticalBusSets.size() - i - 1; + if (verticalBusSets.get(j).getBusNodeSet().contains(busNode)) { + return verticalBusSets.get(j); + } + } + return null; + } + + public List getSideCandidateFlatCell(Side side) { + return hblSideBuses(side).stream() + .map(busNode -> getVbsSideFromBusNode(busNode, side)) + .distinct().filter(Objects::nonNull) + .flatMap(vbs -> vbs.getInternCellsFromShape(InternCell.Shape.MAYBE_FLAT).stream()) + .collect(Collectors.toList()); + } + + public List getInternCellsFromShape(InternCell.Shape shape) { + return verticalBusSets.stream() + .flatMap(verticalBusSet -> verticalBusSet.getInternCellsFromShape(shape).stream()) + .collect(Collectors.toList()); + } + + public void sortHblByVPos() { + horizontalBusLists.sort(Comparator.comparingInt(hbl -> hbl.getBusNodes().get(0).getBusbarIndex())); + } + + public int getLength() { + return verticalBusSets.size(); + } + + public List getHorizontalBusLists() { + return horizontalBusLists; + } + + public List getVerticalBusSets() { + return verticalBusSets; + } + + @Override + public String toString() { + return verticalBusSets.toString() + "\n" + horizontalBusLists.toString(); + } + +} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockPositionner.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/BlockPositionner.java similarity index 98% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockPositionner.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/BlockPositionner.java index d335febaf..3a725c5cf 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockPositionner.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/BlockPositionner.java @@ -4,7 +4,7 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.layout.position; import com.powsybl.sld.model.cells.BusCell; import com.powsybl.sld.model.cells.InternCell; @@ -22,9 +22,9 @@ /** * @author Benoit Jeanson {@literal } */ -class BlockPositionner { +public class BlockPositionner { - void determineBlockPositions(VoltageLevelGraph graph, List subsections, Map busInfoMap) { + public void determineBlockPositions(VoltageLevelGraph graph, List subsections, Map busInfoMap) { int hPos = 0; int prevHPos = 0; int hSpace = 0; diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalBusLane.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/HorizontalBusList.java similarity index 72% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalBusLane.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/HorizontalBusList.java index 3fe5fa4ad..42bebbd5a 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalBusLane.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/HorizontalBusList.java @@ -4,7 +4,7 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.layout.position; import com.powsybl.sld.model.coordinate.Side; import com.powsybl.sld.model.nodes.BusNode; @@ -13,26 +13,26 @@ import java.util.stream.Collectors; /** - * An horizontalBusLane contains a list of BusNodes that have to be displayed horizontally at the same height (same vPos). - * The startingIndex of the HorizontalBusLane is its horizontal position in the LBSCluster it belongs to. + * An HorizontalBusList contains a list of BusNodes that have to be displayed horizontally at the same height (same vPos). + * The startingIndex of the HorizontalBusList is its horizontal position in the LBSCluster it belongs to. * The horizontal position is the startingIndex in the cluster of the first LegBusSet that contains the first BusNode - * of the HorizontalBusLane. - * The length is the spanning of the HorizontalBusLane in the LBSCluster (note that a busNode can span over many - * LegBusSet in the cluster). Therefore startingIndex + length - 1 = the last position occupied by the HorizontalBusLane in + * of the HorizontalBusList. + * The length is the spanning of the HorizontalBusList in the LBSCluster (note that a busNode can span over many + * LegBusSet in the cluster). Therefore startingIndex + length - 1 = the last position occupied by the HorizontalBusList in * the LBSCluster. * * @author Benoit Jeanson {@literal } */ -public class HorizontalBusLane { +public class HorizontalBusList { private final List busNodes = new ArrayList<>(); private int startingIndex; - private LBSCluster lbsCluster; + private BSCluster bsCluster; - HorizontalBusLane(BusNode busNode, LBSCluster lbsCluster) { + HorizontalBusList(BusNode busNode, BSCluster bsCluster) { busNodes.add(busNode); - this.lbsCluster = lbsCluster; + this.bsCluster = bsCluster; startingIndex = 0; } @@ -45,13 +45,13 @@ public void shift(int i) { startingIndex += i; } - public void merge(HorizontalBusLane otherLane) { + public void merge(HorizontalBusList otherHbl) { BusNode myRightBus = getSideNode(Side.RIGHT); - for (int i = getEndingIndex(); i < otherLane.getStartingIndex() - + (lbsCluster == otherLane.lbsCluster ? 0 : lbsCluster.getLength()); i++) { - busNodes.add(myRightBus == otherLane.getSideNode(Side.LEFT) ? myRightBus : null); + for (int i = getEndingIndex(); i < otherHbl.getStartingIndex() + + (bsCluster == otherHbl.bsCluster ? 0 : bsCluster.getLength()); i++) { + busNodes.add(myRightBus == otherHbl.getSideNode(Side.LEFT) ? myRightBus : null); } - busNodes.addAll(otherLane.getBusNodes()); + busNodes.addAll(otherHbl.getBusNodes()); } void establishBusPosition(int v) { @@ -94,8 +94,8 @@ public BusNode getBusNode(int index) { return busNodes.get(index - startingIndex); } - public void setLbsCluster(LBSCluster lbsCluster) { - this.lbsCluster = lbsCluster; + public void setBsCluster(BSCluster bsCluster) { + this.bsCluster = bsCluster; } @Override diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/HorizontalBusListsMerger.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/HorizontalBusListsMerger.java new file mode 100644 index 000000000..215fd2ed8 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/HorizontalBusListsMerger.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2020, 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/. + */ +package com.powsybl.sld.layout.position; + +/** + * @author Benoit Jeanson {@literal } + */ +@FunctionalInterface +public interface HorizontalBusListsMerger { + + void apply(BSCluster leftCluster, BSCluster rightCluster); + +} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/InternCellSide.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/InternCellSide.java similarity index 95% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/InternCellSide.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/InternCellSide.java index 4becb1c08..b9dee4cb4 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/InternCellSide.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/InternCellSide.java @@ -4,7 +4,7 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.layout.position; import com.powsybl.sld.model.cells.InternCell; import com.powsybl.sld.model.coordinate.Side; diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFinder.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/PositionFinder.java similarity index 63% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFinder.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/PositionFinder.java index a14df46c9..cd027c487 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFinder.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/PositionFinder.java @@ -4,14 +4,11 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.layout.position; -import com.powsybl.sld.model.cells.BusCell; import com.powsybl.sld.model.graphs.VoltageLevelGraph; -import com.powsybl.sld.model.nodes.BusNode; import java.util.List; -import java.util.Map; /** * a PositionFinder determines: @@ -25,13 +22,5 @@ */ public interface PositionFinder { - Map indexBusPosition(List busNodes, List busCells); - - LBSCluster organizeLegBusSets(VoltageLevelGraph graph, List legBusSets); - List buildLayout(VoltageLevelGraph graph, boolean handleShunt); - - void forceSameOrientationForShuntedCell(VoltageLevelGraph graph); - - void organizeDirections(VoltageLevelGraph graph, List subsections); } diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/Subsection.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/Subsection.java similarity index 85% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/Subsection.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/Subsection.java index 84e17aba9..779ad371f 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/Subsection.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/Subsection.java @@ -4,8 +4,9 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.layout.position; +import com.powsybl.commons.PowsyblException; import com.powsybl.sld.model.blocks.BodyPrimaryBlock; import com.powsybl.sld.model.cells.*; import com.powsybl.sld.model.coordinate.Orientation; @@ -15,6 +16,9 @@ import com.powsybl.sld.model.nodes.ConnectivityNode; import com.powsybl.sld.model.nodes.FeederNode; import com.powsybl.sld.model.nodes.Node; +import com.powsybl.sld.util.GraphTraversal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.*; import java.util.function.Function; @@ -37,24 +41,27 @@ public class Subsection { private final List archCells = new ArrayList<>(); private static final Comparator COMPARE_ORDER = Comparator .comparingInt(extCell -> extCell.getOrder().orElse(-1)); + private static final Logger LOGGER = LoggerFactory.getLogger(Subsection.class); Subsection(int size) { this.size = size; busNodes = new BusNode[size]; } - private boolean checkAbsorbability(LegBusSet lbs) { - return lbs.getExtendedNodeSet().stream().noneMatch(busNode -> { + private boolean checkAbsorbability(Set extendedNodeSet) { + return extendedNodeSet.stream().noneMatch(busNode -> { int vIndex = busNode.getBusbarIndex() - 1; return busNodes[vIndex] != null && busNodes[vIndex] != busNode; }); } - private void addLegBusSet(LegBusSet lbs) { - lbs.getExtendedNodeSet().forEach(bus -> busNodes[bus.getBusbarIndex() - 1] = bus); - lbs.getExternCells().stream().sorted(COMPARE_ORDER).forEach(externCells::add); - lbs.getArchCells().stream().sorted(COMPARE_ORDER).forEach(archCells::add); - internCellSides.addAll(lbs.getInternCellSides()); + private void addVerticalBusSet(VerticalBusSet vbs, Set extendedNodeSet) { + extendedNodeSet.forEach(bus -> busNodes[bus.getBusbarIndex() - 1] = bus); + externCells.addAll(vbs.getExternCells()); + externCells.sort(COMPARE_ORDER); + archCells.addAll(vbs.getArchCells()); + archCells.sort(COMPARE_ORDER); + internCellSides.addAll(vbs.getInternCellSides()); } public int getSize() { @@ -94,24 +101,39 @@ private boolean containsAllBusNodes(List nodes) { return Arrays.asList(busNodes).containsAll(nodes); } - static List createSubsections(VoltageLevelGraph graph, LBSCluster lbsCluster, boolean handleShunts) { + static List createSubsections(VoltageLevelGraph graph, BSCluster bsCluster, Map busToNb, boolean handleShunts) { List subsections = new ArrayList<>(); int vSize = graph.getMaxVerticalBusPosition(); Subsection currentSubsection = new Subsection(vSize); subsections.add(currentSubsection); int i = 0; - for (LegBusSet lbs : lbsCluster.getLbsList()) { - lbs.addToExtendedNodeSet(lbsCluster.getVerticalBuseNodes(i)); - if (!currentSubsection.checkAbsorbability(lbs)) { + for (VerticalBusSet vbs : bsCluster.getVerticalBusSets()) { + Set extendedNodeSet = new TreeSet<>(Comparator.comparingInt(busToNb::get)); + List vbn = bsCluster.getVerticalBusNodes(i); + if (vbs.getBusNodeSet().containsAll(vbn)) { + // 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 a HashSet with busbar indices + Set indices = new HashSet<>(); + for (BusNode busNode : vbn) { + if (!indices.add(busNode.getBusbarIndex())) { + throw new PowsyblException("Inconsistent legBusSet: extended node set contains two busNodes with same index"); + } + } + extendedNodeSet.addAll(vbn); + } else { + LOGGER.error("ExtendedNodeSet inconsistent with NodeBusSet"); + } + if (!currentSubsection.checkAbsorbability(extendedNodeSet)) { currentSubsection = new Subsection(vSize); subsections.add(currentSubsection); } - currentSubsection.addLegBusSet(lbs); + currentSubsection.addVerticalBusSet(vbs, extendedNodeSet); i++; } - internCellCoherence(graph, lbsCluster.getLbsList(), subsections); + internCellCoherence(graph, bsCluster.getVerticalBusSets(), subsections); graph.getShuntCellStream().forEach(ShuntCell::alignExternCells); if (handleShunts) { @@ -121,14 +143,10 @@ static List createSubsections(VoltageLevelGraph graph, LBSCluster lb return subsections; } - static List createSubsections(VoltageLevelGraph graph, LBSCluster lbsCluster) { - return createSubsections(graph, lbsCluster, false); - } - - private static void internCellCoherence(VoltageLevelGraph vlGraph, List lbsList, List subsections) { + private static void internCellCoherence(VoltageLevelGraph vlGraph, List vbsList, List subsections) { identifyOneLegInternCells(vlGraph, subsections); identifyVerticalInternCells(vlGraph, subsections); - identifyFlatInternCells(lbsList); + identifyFlatInternCells(vbsList); identifyCrossOverAndCheckOrientation(subsections); slipInternCellSideToEdge(subsections); } @@ -162,9 +180,9 @@ private static void identifyVerticalInternCells(VoltageLevelGraph graph, List lbsList) { - lbsList.stream() - .flatMap(lbs -> lbs.getInternCellsFromShape(InternCell.Shape.MAYBE_FLAT).stream()) + private static void identifyFlatInternCells(List vbsList) { + vbsList.stream() + .flatMap(vbs -> vbs.getInternCellsFromShape(InternCell.Shape.MAYBE_FLAT).stream()) .distinct() .forEach(internCell -> { List buses = internCell.getBusNodes(); diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LegBusSet.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/VerticalBusSet.java similarity index 66% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LegBusSet.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/VerticalBusSet.java index 2a70778ac..67b383e4e 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LegBusSet.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/VerticalBusSet.java @@ -4,11 +4,13 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.layout.position; -import com.powsybl.commons.PowsyblException; import com.powsybl.sld.model.blocks.*; -import com.powsybl.sld.model.cells.*; +import com.powsybl.sld.model.cells.ArchCell; +import com.powsybl.sld.model.cells.Cell; +import com.powsybl.sld.model.cells.ExternCell; +import com.powsybl.sld.model.cells.InternCell; import com.powsybl.sld.model.coordinate.Direction; import com.powsybl.sld.model.coordinate.Side; import com.powsybl.sld.model.graphs.NodeFactory; @@ -18,70 +20,64 @@ import org.slf4j.LoggerFactory; import java.util.*; -import java.util.function.Function; import java.util.stream.Collectors; /** - * A LegBusSet contains the set of BusNodes that shall be vertically presented, and the cells that have a pattern of - * connection included in the busNodeSet. It is embedded into a LBSCluster. It contains links to all the other - * LegBusSet of the Graph. + * A VerticalBusSet contains the set of BusNodes that shall be vertically presented, and the cells that have a pattern of + * connection included in the busNodeSet. It is embedded into a BSCluster. * * @author Benoit Jeanson {@literal } */ -public final class LegBusSet { +public final class VerticalBusSet { - private static final Logger LOGGER = LoggerFactory.getLogger(LegBusSet.class); + private static final Logger LOGGER = LoggerFactory.getLogger(VerticalBusSet.class); private final Set busNodeSet; - private final Set extendedNodeSet; private final Set externCells = new LinkedHashSet<>(); private final List archCells = new ArrayList<>(); - private final Set internCellSides = new LinkedHashSet<>(); - private LegBusSet(Map nodeToNb, List busNodes) { - busNodeSet = new TreeSet<>(Comparator.comparingInt(nodeToNb::get)); - extendedNodeSet = new TreeSet<>(Comparator.comparingInt(nodeToNb::get)); + private VerticalBusSet(Map busToNb, List busNodes) { + busNodeSet = new TreeSet<>(Comparator.comparingInt(busToNb::get)); busNodeSet.addAll(busNodes); } - private LegBusSet(Map nodeToNb, ExternCell cell) { - this(nodeToNb, cell.getBusNodes()); + private VerticalBusSet(Map busToNb, ExternCell cell) { + this(busToNb, cell.getBusNodes()); externCells.add(cell); } - private LegBusSet(Map nodeToNb, ArchCell cell) { + private VerticalBusSet(Map nodeToNb, ArchCell cell) { this(nodeToNb, cell.getBusNodes()); archCells.add(cell); } - private LegBusSet(Map nodeToNb, ShuntCell cell) { - this(nodeToNb, cell.getParentBusNodes()); - externCells.addAll(cell.getSideCells()); - } - - private LegBusSet(Map nodeToNb, InternCell internCell, Side side) { - this(nodeToNb, internCell.getSideBusNodes(side)); + private VerticalBusSet(Map busToNb, InternCell internCell, Side side) { + this(busToNb, internCell.getSideBusNodes(side)); addInternCell(internCell, side); } - private LegBusSet(Map nodeToNb, BusNode busNode) { - this(nodeToNb, Collections.singletonList(busNode)); + private VerticalBusSet(Map busToNb, BusNode busNode) { + this(busToNb, Collections.singletonList(busNode)); } void addInternCell(InternCell internCell, Side side) { internCellSides.add(new InternCellSide(internCell, side)); } - private boolean contains(LegBusSet lbs) { - return busNodeSet.containsAll(lbs.getBusNodeSet()); + private boolean contains(Collection busNodeCollection) { + return busNodeSet.containsAll(busNodeCollection); } - private void absorbs(LegBusSet lbsToAbsorb) { - busNodeSet.addAll(lbsToAbsorb.busNodeSet); - externCells.addAll(lbsToAbsorb.externCells); - archCells.addAll(lbsToAbsorb.archCells); - internCellSides.addAll(lbsToAbsorb.internCellSides); + private boolean contains(VerticalBusSet vbs) { + return contains(vbs.getBusNodeSet()); + } + + private void absorbs(VerticalBusSet vbsToAbsorb) { + busNodeSet.addAll(vbsToAbsorb.busNodeSet); + externCells.addAll(vbsToAbsorb.externCells); + archCells.addAll(vbsToAbsorb.archCells); + internCellSides.addAll(vbsToAbsorb.internCellSides); } List getInternCellsFromShape(InternCell.Shape shape) { @@ -107,64 +103,38 @@ Set getInternCellSides() { return internCellSides; } - void addToExtendedNodeSet(Collection busNodes) { - if (busNodes.containsAll(busNodeSet)) { - // 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 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 getExtendedNodeSet() { - return extendedNodeSet; - } - - static List createLegBusSets(VoltageLevelGraph graph, Map nodeToNb, boolean handleShunts) { + static List createVerticalBusSets(VoltageLevelGraph graph, Map busToNb) { graph.getExternCellStream().toList().forEach(cell -> fixMultisectionExternCells(cell, graph)); List externCells = graph.getExternCellStream() .sorted(Comparator.comparing(Cell::getFullId)) // if order is not yet defined & avoid randomness .collect(Collectors.toList()); - List legBusSets = new ArrayList<>(); - - if (handleShunts) { - manageShunts(graph, externCells, legBusSets, nodeToNb); - } + List verticalBusSets = new ArrayList<>(); - externCells.forEach(cell -> pushLBS(legBusSets, new LegBusSet(nodeToNb, cell))); + externCells.forEach(cell -> pushVbs(verticalBusSets, new VerticalBusSet(busToNb, cell))); - graph.getArchCellStream().forEach(cell -> pushLBS(legBusSets, new LegBusSet(nodeToNb, cell))); + graph.getArchCellStream().forEach(cell -> pushVbs(verticalBusSets, new VerticalBusSet(busToNb, cell))); graph.getInternCellStream() .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 -> pushInternCell(legBusSets, nodeToNb, cell)); + .forEachOrdered(cell -> pushInternCell(verticalBusSets, busToNb, 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)); + .forEachOrdered(cell -> pushInternCell(verticalBusSets, busToNb, cell)); - // find orphan busNodes and build their LBS + // find orphan busNodes and build their VBS List allBusNodes = new ArrayList<>(graph.getNodeBuses()); - allBusNodes.removeAll(legBusSets.stream(). + allBusNodes.removeAll(verticalBusSets.stream(). flatMap(legBusSet -> legBusSet.getBusNodeSet().stream()).collect(Collectors.toList())); allBusNodes.stream() .sorted(Comparator.comparing(Node::getId)) //avoid randomness - .forEach(busNode -> legBusSets.add(new LegBusSet(nodeToNb, busNode))); - return legBusSets; + .forEach(busNode -> verticalBusSets.add(new VerticalBusSet(busToNb, busNode))); + return verticalBusSets; } private static void fixMultisectionExternCells(ExternCell externCell, VoltageLevelGraph graph) { @@ -272,62 +242,32 @@ private static void fixLegParallelExternCell(ExternCell externCell, VoltageLevel } } - private static void manageShunts(VoltageLevelGraph graph, List externCells, List legBusSets, Map nodeToNb) { - List> sameBusNodesShuntCells = graph.getShuntCellStream() - .map(sc -> new ArrayList<>(Collections.singletonList(sc))) - .collect(Collectors.toList()); - int i = 0; - while (i < sameBusNodesShuntCells.size()) { - int j = i + 1; - while (j < sameBusNodesShuntCells.size()) { - if (crossContains(sameBusNodesShuntCells.get(i).get(0).getParentBusNodes(), - sameBusNodesShuntCells.get(j).get(0).getParentBusNodes())) { - sameBusNodesShuntCells.get(i).addAll(sameBusNodesShuntCells.get(j)); - sameBusNodesShuntCells.remove(j); - } else { - j++; - } - } - i++; - } - - sameBusNodesShuntCells.stream().filter(scs -> scs.size() > 2).flatMap(List::stream) - .forEach(sc -> { - pushLBS(legBusSets, new LegBusSet(nodeToNb, sc)); - externCells.removeAll(sc.getSideCells()); - }); - } - - private static boolean crossContains(List busNodes1, List busNodes2) { - return busNodes1.containsAll(busNodes2) && busNodes2.containsAll(busNodes1); - } - - public static void pushLBS(List legBusSets, LegBusSet legBusSet) { - for (LegBusSet lbs : legBusSets) { - if (lbs.contains(legBusSet)) { - lbs.absorbs(legBusSet); + public static void pushVbs(List verticalBusSets, VerticalBusSet verticalBusSet) { + for (VerticalBusSet vbs : verticalBusSets) { + if (vbs.contains(verticalBusSet)) { + vbs.absorbs(verticalBusSet); return; } } - List absorbedLBS = new ArrayList<>(); - for (LegBusSet lbs : legBusSets) { - if (legBusSet.contains(lbs)) { - absorbedLBS.add(lbs); - legBusSet.absorbs(lbs); + List absorbedVbs = new ArrayList<>(); + for (VerticalBusSet vbs : verticalBusSets) { + if (verticalBusSet.contains(vbs)) { + absorbedVbs.add(vbs); + verticalBusSet.absorbs(vbs); } } - legBusSets.removeAll(absorbedLBS); - legBusSets.add(legBusSet); + verticalBusSets.removeAll(absorbedVbs); + verticalBusSets.add(verticalBusSet); } - private static void pushInternCell(List legBusSets, Map nodeToNb, InternCell internCell) { - List attachedLegBusSets = new ArrayList<>(); - for (LegBusSet lbs : legBusSets) { - boolean attachedToLbs = internCell.getBusNodes().stream().anyMatch(lbs.busNodeSet::contains); + private static void pushInternCell(List verticalBusSets, Map nodeToNb, InternCell internCell) { + List attachedLegBusSets = new ArrayList<>(); + for (VerticalBusSet vbs : verticalBusSets) { + boolean attachedToLbs = internCell.getBusNodes().stream().anyMatch(vbs.busNodeSet::contains); if (attachedToLbs) { - attachedLegBusSets.add(lbs); - if (lbs.busNodeSet.containsAll(internCell.getBusNodes())) { - lbs.addInternCell(internCell, Side.UNDEFINED); + attachedLegBusSets.add(vbs); + if (vbs.busNodeSet.containsAll(internCell.getBusNodes())) { + vbs.addInternCell(internCell, Side.UNDEFINED); if (internCell.getShape() == InternCell.Shape.MAYBE_ONE_LEG) { internCell.setShape(InternCell.Shape.ONE_LEG); } else { @@ -343,14 +283,14 @@ private static void pushInternCell(List legBusSets, Map attachedLegBusSets) { + private static void replaceByMultilegOrSetOneLeg(InternCell internCell, List attachedVerticalBusSets) { // 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 @@ -360,10 +300,10 @@ private static void replaceByMultilegOrSetOneLeg(InternCell internCell, List> groupSubBlocksLbs = subBlocks.stream().collect(Collectors.groupingBy( - sb -> attachedLegBusSets.stream().filter(lbs -> lbs.busNodeSet.contains(sb.getBusNode())).findFirst())).values(); + sb -> attachedVerticalBusSets.stream().filter(lbs -> lbs.busNodeSet.contains(sb.getBusNode())).findFirst())).values(); if (groupSubBlocksLbs.size() == 2) { replaceByMultiLeg(internCell, groupSubBlocksLbs); } else { diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/BSClusterSide.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/BSClusterSide.java new file mode 100644 index 000000000..a5c7330bf --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/BSClusterSide.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2019, 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/. + */ +package com.powsybl.sld.layout.position.clustering; + +import com.powsybl.sld.layout.position.BSCluster; +import com.powsybl.sld.layout.position.HorizontalBusList; +import com.powsybl.sld.layout.position.VerticalBusSet; +import com.powsybl.sld.model.cells.ExternCell; +import com.powsybl.sld.model.cells.InternCell; +import com.powsybl.sld.model.coordinate.Side; +import com.powsybl.sld.model.nodes.BusNode; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * BSClusterSide is the composition of a BSCluster and a Side (LEFT/RIGHT). + * + * @author Benoit Jeanson {@literal } + */ +class BSClusterSide { + + private final BSCluster bsCluster; + private final Side side; + private BSClusterSide otherSameRoot; + + BSClusterSide(BSCluster bsCluster, Side side) { + this.bsCluster = Objects.requireNonNull(bsCluster); + this.side = Objects.requireNonNull(side); + } + + void setOtherSameRoot(BSClusterSide otherSameRoot) { + this.otherSameRoot = otherSameRoot; + } + + Set getBusNodeSet() { + return new LinkedHashSet<>(bsCluster.hblSideBuses(side)); + } + + List getCandidateFlatCellList() { + return bsCluster.getSideCandidateFlatCell(side); + } + + List getExternCells() { + return bsCluster.getVerticalBusSets().stream().flatMap(vbs -> vbs.getExternCells().stream()).collect(Collectors.toList()); + } + + int getExternCellAttractionToEdge(ExternCell cell) { + List vbsList = bsCluster.getVerticalBusSets(); + return vbsList.stream().filter(vbs -> vbs.getExternCells().contains(cell)).findFirst() + .map(vbs -> side == Side.LEFT ? (vbsList.size() - vbsList.indexOf(vbs)) + : (vbsList.indexOf(vbs) + 1)).orElse(0); + } + + List getInternCellsFromShape(InternCell.Shape shape) { + return bsCluster.getInternCellsFromShape(shape); + } + + BSCluster getCluster() { + return bsCluster; + } + + Side getMySideInCluster() { + return side; + } + + BSClusterSide getOtherSameRoot() { + return otherSameRoot; + } + + int getCandidateFlatCellDistanceToEdge(InternCell internCell) { + List buses = internCell.getBusNodes(); + buses.retainAll(getBusNodeSet()); + if (buses.isEmpty()) { + return 100; + } + BusNode busNode = buses.get(0); //shall have only one as used for a flatCell + Optional horizontalBusList = bsCluster.getHorizontalBusLists() + .stream() + .filter(hbl -> side == Side.LEFT && hbl.getBusNodes().get(0) == busNode + || side == Side.RIGHT && hbl.getBusNodes().get(hbl.getBusNodes().size() - 1) == busNode) + .findFirst(); + if (!horizontalBusList.isPresent()) { + return 100; + } else { + if (side == Side.LEFT) { + return horizontalBusList.get().getStartingIndex(); + } else { + return bsCluster.getVerticalBusSets().size() - horizontalBusList.get().getEndingIndex(); + } + } + } + + @Override + public String toString() { + return side.toString() + " " + bsCluster.hblSideBuses(side).toString(); + } +} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/Link.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/Link.java similarity index 56% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/Link.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/Link.java index 4b076feb0..aae283a64 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/Link.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/Link.java @@ -4,9 +4,8 @@ * 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/. */ -package com.powsybl.sld.layout.positionbyclustering; +package com.powsybl.sld.layout.position.clustering; -import com.powsybl.sld.layout.HorizontalBusLaneManager; import com.powsybl.sld.model.cells.ExternCell; import com.powsybl.sld.model.cells.InternCell; import com.powsybl.sld.model.cells.ShuntCell; @@ -18,17 +17,17 @@ import java.util.stream.Collectors; /** - * A link is define between two lbsClusterSides (having the same implementation). - * It implements Comparable that compares the strength of the link between two lbsClusterSides. + * A link is define between two VBSClusterSides (having the same implementation). + * It implements Comparable that compares the strength of the link between two VBSClusterSides. * From the strongest to the weakest kind of link : *
    *
  • * having buses in common (the more there are the stronger is the link), *
  • *
  • - * having flatcells in common (ie a cell with only two buses, one in each lbsClusterSide), the more there are, + * having flatcells in common (ie a cell with only two buses, one in each VBSClusterSide), the more there are, * the stronger is the link (for flatcell, the notion of distance to the edge is added - * (in case of clustering by LBSClusterSide) to foster flatcell that are on the edge of the cluster) + * (in case of clustering by VBSClusterSide) to foster flatcell that are on the edge of the cluster) *
  • *
  • * having crossover cells in common defined as interncell that cannot be flatcell, and that potentially can span over @@ -48,118 +47,112 @@ class Link implements Comparable { enum LinkCategory { - COMMONBUSES, FLATCELLS, CROSSOVER, SHUNT + COMMON_BUSES, FLAT_CELLS, CROSSOVER, SHUNT } - private final LBSClusterSide lbsClusterSide1; - private final LBSClusterSide lbsClusterSide2; + private final BSClusterSide bsClusterSide1; + private final BSClusterSide bsClusterSide2; private final Map categoryToWeight = new EnumMap<>(LinkCategory.class); - private int nb; + private final int nb; - Link(LBSClusterSide lbsClusterSide1, LBSClusterSide lbsClusterSide2, int nb) { - this.lbsClusterSide1 = lbsClusterSide1; - this.lbsClusterSide2 = lbsClusterSide2; - lbsClusterSide1.addLink(this); - lbsClusterSide2.addLink(this); + Link(BSClusterSide bsClusterSide1, BSClusterSide bsClusterSide2, int nb) { + this.bsClusterSide1 = bsClusterSide1; + this.bsClusterSide2 = bsClusterSide2; this.nb = nb; assessLink(); } private void assessLink() { - categoryToWeight.put(LinkCategory.COMMONBUSES, assessCommonBusNodes()); - categoryToWeight.put(LinkCategory.FLATCELLS, assessFlatCell()); + categoryToWeight.put(LinkCategory.COMMON_BUSES, assessCommonBusNodes()); + categoryToWeight.put(LinkCategory.FLAT_CELLS, assessFlatCell()); categoryToWeight.put(LinkCategory.CROSSOVER, assessCrossOver()); categoryToWeight.put(LinkCategory.SHUNT, assessShunt()); } private int assessCommonBusNodes() { - Set nodeBusesIntersect = new LinkedHashSet<>(lbsClusterSide1.getBusNodeSet()); - nodeBusesIntersect.retainAll(lbsClusterSide2.getBusNodeSet()); + Set nodeBusesIntersect = new LinkedHashSet<>(bsClusterSide1.getBusNodeSet()); + nodeBusesIntersect.retainAll(bsClusterSide2.getBusNodeSet()); return nodeBusesIntersect.size(); } private int assessFlatCell() { - Set flatCellIntersect = new LinkedHashSet<>(lbsClusterSide1.getCandidateFlatCellList()); - flatCellIntersect.retainAll(lbsClusterSide2.getCandidateFlatCellList()); + Set flatCellIntersect = new LinkedHashSet<>(bsClusterSide1.getCandidateFlatCellList()); + flatCellIntersect.retainAll(bsClusterSide2.getCandidateFlatCellList()); return flatCellIntersect.size() * 100 - flatCellIntersect.stream() - .mapToInt(internCell -> flatCellDistanceToEdges(internCell, lbsClusterSide1, lbsClusterSide2)).sum(); + .mapToInt(internCell -> flatCellDistanceToEdges(internCell, bsClusterSide1, bsClusterSide2)).sum(); } - static int flatCellDistanceToEdges(InternCell cell, LBSClusterSide lbsCS1, LBSClusterSide lbsCS2) { - return lbsCS1.getCandidateFlatCellDistanceToEdge(cell) + lbsCS2.getCandidateFlatCellDistanceToEdge(cell); + static int flatCellDistanceToEdges(InternCell cell, BSClusterSide bsCS1, BSClusterSide bsCS2) { + return bsCS1.getCandidateFlatCellDistanceToEdge(cell) + bsCS2.getCandidateFlatCellDistanceToEdge(cell); } private int assessCrossOver() { - Set commonInternCells = new LinkedHashSet<>(lbsClusterSide1.getInternCellsFromShape(InternCell.Shape.UNDEFINED)); - commonInternCells.retainAll(lbsClusterSide2.getInternCellsFromShape(InternCell.Shape.UNDEFINED)); + Set commonInternCells = new LinkedHashSet<>(bsClusterSide1.getInternCellsFromShape(InternCell.Shape.UNDEFINED)); + commonInternCells.retainAll(bsClusterSide2.getInternCellsFromShape(InternCell.Shape.UNDEFINED)); return (int) (commonInternCells.stream() .flatMap(internCell -> internCell.getBusNodes().stream()).distinct() .count()); } private int assessShunt() { - List externCells1 = extractExternShuntedCells(lbsClusterSide1); - List externCells2 = extractExternShuntedCells(lbsClusterSide2); + List externCells1 = extractExternShuntedCells(bsClusterSide1); + List externCells2 = extractExternShuntedCells(bsClusterSide2); List shuntCells = extractShuntCells(externCells1); shuntCells.retainAll(extractShuntCells(externCells2)); List myShuntedExternCells = shuntCells.stream() .flatMap(sc -> sc.getSideCells().stream()).collect(Collectors.toList()); externCells1.retainAll(myShuntedExternCells); externCells2.retainAll(myShuntedExternCells); - return shuntAttractivity(externCells1, lbsClusterSide1) + shuntAttractivity(externCells2, lbsClusterSide2); + return shuntAttractivity(externCells1, bsClusterSide1) + shuntAttractivity(externCells2, bsClusterSide2); } - private List extractExternShuntedCells(LBSClusterSide lbsClusterSide) { - return lbsClusterSide.getExternCells().stream().filter(ExternCell::isShunted).collect(Collectors.toList()); + private List extractExternShuntedCells(BSClusterSide bsClusterSide) { + return bsClusterSide.getExternCells().stream().filter(ExternCell::isShunted).collect(Collectors.toList()); } private List extractShuntCells(List externCells) { return externCells.stream().map(ExternCell::getShuntCells).flatMap(List::stream).collect(Collectors.toList()); } - private int shuntAttractivity(List cells, LBSClusterSide lbsClusterSide) { - return cells.stream().mapToInt(lbsClusterSide::getExternCellAttractionToEdge).sum(); + private int shuntAttractivity(List cells, BSClusterSide bsClusterSide) { + return cells.stream().mapToInt(bsClusterSide::getExternCellAttractionToEdge).sum(); } private int getLinkCategoryWeight(LinkCategory cat) { return categoryToWeight.get(cat); } - LBSClusterSide getOtherlbsClusterSide(LBSClusterSide lbsClusterSide) { - if (lbsClusterSide == lbsClusterSide1) { - return lbsClusterSide2; + BSClusterSide getOtherBsClusterSide(BSClusterSide bsClusterSide) { + if (bsClusterSide == bsClusterSide1) { + return bsClusterSide2; } - if (lbsClusterSide == lbsClusterSide2) { - return lbsClusterSide1; + if (bsClusterSide == bsClusterSide2) { + return bsClusterSide1; } return null; } - LBSClusterSide getlbsClusterSide(int i) { + BSClusterSide getBsClusterSide(int i) { if (i == 0) { - return lbsClusterSide1; + return bsClusterSide1; } else if (i == 1) { - return lbsClusterSide2; + return bsClusterSide2; } return null; } - void mergeClusters(HorizontalBusLaneManager hblManager) { - if (lbsClusterSide1.getCluster() == lbsClusterSide2.getCluster() - || lbsClusterSide1.getMySideInCluster() == Side.UNDEFINED - || lbsClusterSide2.getMySideInCluster() == Side.UNDEFINED) { + void mergeClusters() { + if (bsClusterSide1.getCluster() == bsClusterSide2.getCluster() + || bsClusterSide1.getMySideInCluster() == Side.UNDEFINED + || bsClusterSide2.getMySideInCluster() == Side.UNDEFINED) { return; } - lbsClusterSide1.getCluster().merge( - lbsClusterSide1.getMySideInCluster(), - lbsClusterSide2.getCluster(), - lbsClusterSide2.getMySideInCluster(), hblManager); - } - - void removeMe() { - lbsClusterSide1.removeLink(this); - lbsClusterSide2.removeLink(this); + bsClusterSide1.getCluster().merge( + bsClusterSide1.getMySideInCluster(), + bsClusterSide2.getCluster(), + bsClusterSide2.getMySideInCluster(), + PositionByClustering::mergeHorizontalBusLists); } @Override @@ -167,13 +160,13 @@ public boolean equals(Object obj) { if (!(obj instanceof Link)) { return false; } - return lbsClusterSide1.equals(((Link) obj).lbsClusterSide1) - && lbsClusterSide2.equals(((Link) obj).lbsClusterSide2); + return bsClusterSide1.equals(((Link) obj).bsClusterSide1) + && bsClusterSide2.equals(((Link) obj).bsClusterSide2); } @Override public int hashCode() { - return super.hashCode(); + return nb; } @Override @@ -191,11 +184,11 @@ public int compareTo(@Nonnull Link oLink) { @Override public String toString() { - return "CommonBus: " + categoryToWeight.get(LinkCategory.COMMONBUSES) - + " FlatCell: " + categoryToWeight.get(LinkCategory.FLATCELLS) + return "CommonBus: " + categoryToWeight.get(LinkCategory.COMMON_BUSES) + + " FlatCell: " + categoryToWeight.get(LinkCategory.FLAT_CELLS) + " CrossOver: " + categoryToWeight.get(LinkCategory.CROSSOVER) + " Shunt: " + categoryToWeight.get(LinkCategory.SHUNT) - + "\n\tlbsClusterSide1: " + lbsClusterSide1.toString() - + "\n\tlbsClusterSide2: " + lbsClusterSide2.toString(); + + "\n\tvbsClusterSide1: " + bsClusterSide1.toString() + + "\n\tvbsClusterSide2: " + bsClusterSide2.toString(); } } diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/Links.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/Links.java new file mode 100644 index 000000000..feb767139 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/Links.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2019, 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/. + */ +package com.powsybl.sld.layout.position.clustering; + +import com.powsybl.sld.layout.position.BSCluster; +import com.powsybl.sld.model.coordinate.Side; + +import java.util.*; + +/** + * Manages the links between a list of BSClusterSides. + * + * @author Benoit Jeanson {@literal } + */ +final class Links { + + private final List bsClusterSides = new LinkedList<>(); + private final TreeSet linkSet = new TreeSet<>(); + private final Map> bsClusterSideToLink = new HashMap<>(); + private int linkCounter = 0; + + public Links(List bsClusters) { + bsClusters.forEach(this::addClusterSidesTwins); + } + + private void addClusterSidesTwins(BSCluster bsCluster) { + BSClusterSide bsSLeft = new BSClusterSide(bsCluster, Side.LEFT); + BSClusterSide bsSRight = new BSClusterSide(bsCluster, Side.RIGHT); + bsSLeft.setOtherSameRoot(bsSRight); + bsSRight.setOtherSameRoot(bsSLeft); + addBsClusterSide(bsSLeft); + addBsClusterSide(bsSRight); + } + + private void addBsClusterSide(BSClusterSide bsClusterSide) { + bsClusterSideToLink.put(bsClusterSide, new ArrayList<>()); + bsClusterSides.forEach(cc -> buildNewLink(cc, bsClusterSide)); + bsClusterSides.add(bsClusterSide); + } + + private void buildNewLink(BSClusterSide bsClusterSide1, BSClusterSide bsClusterSide2) { + if (bsClusterSide1.getCluster() != bsClusterSide2.getCluster()) { + Link linkToAdd = new Link(bsClusterSide1, bsClusterSide2, linkCounter++); + linkSet.add(linkToAdd); + bsClusterSideToLink.get(bsClusterSide1).add(linkToAdd); + bsClusterSideToLink.get(bsClusterSide2).add(linkToAdd); + } + } + + Link getStrongestLink() { + return linkSet.last(); + } + + void mergeLink(Link link) { + link.mergeClusters(); + BSCluster mergedCluster = link.getBsClusterSide(0).getCluster(); + removeBsClusterSide(link.getBsClusterSide(0)); + removeBsClusterSide(link.getBsClusterSide(1)); + removeBsClusterSide(link.getBsClusterSide(0).getOtherSameRoot()); + removeBsClusterSide(link.getBsClusterSide(1).getOtherSameRoot()); + addClusterSidesTwins(mergedCluster); + } + + private void removeBsClusterSide(BSClusterSide bsClusterSide) { + bsClusterSides.remove(bsClusterSide); + bsClusterSideToLink.get(bsClusterSide).forEach(linkSet::remove); + } + + boolean isEmpty() { + return linkSet.isEmpty(); + } + + BSCluster getFinalBsCluster() { + return bsClusterSides.get(0).getCluster(); + } +} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/PositionByClustering.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/PositionByClustering.java similarity index 50% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/PositionByClustering.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/PositionByClustering.java index fa82719b3..402f8e32a 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/PositionByClustering.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/clustering/PositionByClustering.java @@ -4,14 +4,16 @@ * 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/. */ -package com.powsybl.sld.layout.positionbyclustering; +package com.powsybl.sld.layout.position.clustering; -import com.powsybl.sld.layout.*; +import com.powsybl.sld.layout.position.*; import com.powsybl.sld.model.cells.BusCell; import com.powsybl.sld.model.cells.Cell; import com.powsybl.sld.model.cells.ExternCell; +import com.powsybl.sld.model.cells.InternCell; import com.powsybl.sld.model.cells.ShuntCell; import com.powsybl.sld.model.coordinate.Direction; +import com.powsybl.sld.model.coordinate.Side; import com.powsybl.sld.model.graphs.VoltageLevelGraph; import com.powsybl.sld.model.nodes.BusNode; import org.slf4j.Logger; @@ -34,21 +36,12 @@ * (and in many case having the same horizontal structuralPosition). * The first step consists in building LegBusSets that contains busBars that shall be vertically aligned (considering * they have legs of cell that impose it). - * Then LBSClusters are initiated by building one LBSCluster per LegBusSet. - * The LBSClusters are then merged 2 by 2 starting by LBSclusters that have the strongest Link. - * Two strategies of strength assessment of the links between clusters are implemented: - *
      - *
    • - * if useLBSLinkOnly is true: the strength between LegBusSets is considered: this means that the strength of - * the link between two clusters is the one of the strongest link between two LegBusSets (one per cluster). This - * is a simple implementation that is limited as it it does not consider the difference between the side of a - * cluster: if two clusters A and B are to be merged, the result can either be A-B or B-A. - *
    • - *
    • - * if useLBSLinkOnly is false: the strength between LBSClusterSide is considered. This is similar + * Then BSClusters are initiated by building one BSCluster per LegBusSet. + * The BSClusters are then merged 2 by 2 starting by BSClusters that have the strongest Link. + * We differentiate the side of a BSCluster using BSClusterSide is considered. This is similar * to what si done with LegBusSet but the assessment of the strength of the link considers both sides of the * cluster. - * Therefore, with cluster A and B, there are 4 LBSClusterSide A-Right A-Left B-Right and B-Left. The links that + * Therefore, with cluster A and B, there are 4 BSClusterSide A-Right A-Left B-Right and B-Left. The links that * are considered are (A-Right, B-Left), (A-Right, B-Right), (B-Right, B-Left), (B-Right, B-Right). When merging, * alignment is required (meaning that clusters could be reversed to ensure the connection sides between the * 2 clusters are respected : 1st cluster-Right is merged with 2nd cluster-left). @@ -63,7 +56,6 @@ public class PositionByClustering extends AbstractPositionFinder { private static final Logger LOGGER = LoggerFactory.getLogger(PositionByClustering.class); - private static final HBLaneManagerByClustering HBLMANAGER = new HBLaneManagerByClustering(); @Override public Map indexBusPosition(List busNodes, List busCells) { @@ -79,47 +71,47 @@ public Map indexBusPosition(List busNodes, List legBusSets) { - List lbsClusters = LBSCluster.createLBSClusters(legBusSets); - Links links = Links.create(lbsClusters, HBLMANAGER); + public BSCluster organizeBusSets(VoltageLevelGraph graph, List verticalBusSets) { + List bsClusters = BSCluster.createBSClusters(verticalBusSets); + Links links = new Links(bsClusters); while (!links.isEmpty()) { links.mergeLink(links.getStrongestLink()); } - LBSCluster lbsCluster = links.getFinalLBSCluster(); + BSCluster bsCluster = links.getFinalBsCluster(); - tetrisHorizontalLanes(lbsCluster); - lbsCluster.getHorizontalBusLanes().forEach(hl -> LOGGER.info(hl.toString())); - lbsCluster.establishBusNodePosition(); - establishFeederPositions(lbsCluster); + tetrisHorizontalBusLists(bsCluster); + bsCluster.getHorizontalBusLists().forEach(hl -> LOGGER.info(hl.toString())); + bsCluster.establishBusNodePosition(); + establishFeederPositions(bsCluster); - return lbsCluster; + return bsCluster; } - private void tetrisHorizontalLanes(LBSCluster lbsCluster) { - List horizontalBusLanes = lbsCluster.getHorizontalBusLanes(); + private void tetrisHorizontalBusLists(BSCluster bsCluster) { + List horizontalBusLists = bsCluster.getHorizontalBusLists(); if (LOGGER.isInfoEnabled()) { - LOGGER.info("{}", horizontalBusLanes); + LOGGER.info("{}", horizontalBusLists); } - List sortedLanes = horizontalBusLanes.stream() - .sorted(Comparator.comparingInt(HorizontalBusLane::getStartingIndex) + List sortedHbl = horizontalBusLists.stream() + .sorted(Comparator.comparingInt(HorizontalBusList::getStartingIndex) .thenComparing(hl -> hl.getBusNodes().get(0).getId())) // cope with randomness .collect(Collectors.toList()); - int clusterLength = sortedLanes.stream() - .mapToInt(HorizontalBusLane::getEndingIndex) + int clusterLength = sortedHbl.stream() + .mapToInt(HorizontalBusList::getEndingIndex) .max().orElse(0); int i = 0; - while (i < sortedLanes.size()) { - HorizontalBusLane lane = sortedLanes.get(i); + while (i < sortedHbl.size()) { + HorizontalBusList lane = sortedHbl.get(i); int actualMaxIndex = lane.getEndingIndex(); while (actualMaxIndex < clusterLength) { int finalActualMax = actualMaxIndex; - HorizontalBusLane laneToAdd = sortedLanes.stream() + HorizontalBusList hblToAdd = sortedHbl.stream() .filter(l -> l.getStartingIndex() >= finalActualMax) .findFirst().orElse(null); - if (laneToAdd != null) { - lane.merge(laneToAdd); - sortedLanes.remove(laneToAdd); - horizontalBusLanes.remove(laneToAdd); + if (hblToAdd != null) { + lane.merge(hblToAdd); + sortedHbl.remove(hblToAdd); + horizontalBusLists.remove(hblToAdd); actualMaxIndex = lane.getEndingIndex(); } else { i++; @@ -130,10 +122,10 @@ private void tetrisHorizontalLanes(LBSCluster lbsCluster) { } } - private void establishFeederPositions(LBSCluster lbsCluster) { + private void establishFeederPositions(BSCluster bsCluster) { int cellOrder = 0; - for (LegBusSet lbs : lbsCluster.getLbsList()) { - for (ExternCell cell : lbs.getExternCells()) { + for (VerticalBusSet vbs : bsCluster.getVerticalBusSets()) { + for (ExternCell cell : vbs.getExternCells()) { cell.setOrder(cellOrder++); } } @@ -186,4 +178,63 @@ private void shuntTraversal(ShuntCell shuntCell, Set visitedShuntCell .forEach(sc -> shuntTraversal(sc, visitedShuntCells, externCells)); } + public static void mergeHorizontalBusLists(BSCluster leftCluster, BSCluster rightCluster) { + List availableHblToMerge = new ArrayList<>(leftCluster.getHorizontalBusLists()); + mergeHblWithCommonBusNode(leftCluster, rightCluster, availableHblToMerge); + mergeHblWithFlatCell(leftCluster, rightCluster, availableHblToMerge); + mergeHblWithNoLink(leftCluster, rightCluster); + } + + private static void mergeHblWithCommonBusNode(BSCluster leftCluster, BSCluster rightCluster, List availableHblToMerge) { + List commonNodes = new ArrayList<>(leftCluster.hblSideBuses(Side.RIGHT)); + commonNodes.retainAll(rightCluster.hblSideBuses(Side.LEFT)); + commonNodes.forEach(busNode -> + finalizeHblBuilding(leftCluster, rightCluster, busNode, busNode, availableHblToMerge)); + } + + private static void mergeHblWithFlatCell(BSCluster leftCluster, BSCluster rightCluster, + List availableHblToMerge) { + List myAvailableRightBuses = BSCluster.hblSideBuses(Side.RIGHT, availableHblToMerge); + List myConcernedFlatCells = leftCluster.getSideCandidateFlatCell(Side.RIGHT) + .stream().filter(internCell -> { + List nodes = internCell.getBusNodes(); + nodes.retainAll(myAvailableRightBuses); + return !myAvailableRightBuses.isEmpty(); + }).sorted(Comparator.comparing(InternCell::getFullId)) //avoid randomness + .collect(Collectors.toList()); + List otherConcernedFlatCells = rightCluster.getSideCandidateFlatCell(Side.LEFT); + myConcernedFlatCells.retainAll(otherConcernedFlatCells); + + myConcernedFlatCells.stream() + .sorted(Comparator + .comparingInt(cell -> Link.flatCellDistanceToEdges(cell, + new BSClusterSide(leftCluster, Side.RIGHT), + new BSClusterSide(rightCluster, Side.LEFT))) + .thenComparing(InternCell::getFullId)) + .forEachOrdered(internCell -> { + Optional myNode = internCellNodeInHblSide(leftCluster, Side.RIGHT, internCell); + Optional otherNode = internCellNodeInHblSide(rightCluster, Side.LEFT, internCell); + if (myNode.isPresent() && otherNode.isPresent()) { + internCell.setFlat(); + finalizeHblBuilding(leftCluster, rightCluster, myNode.get(), otherNode.get(), availableHblToMerge); + } + }); + } + + private static Optional internCellNodeInHblSide(BSCluster bsCluster, Side side, InternCell cell) { + List hblBuses = bsCluster.hblSideBuses(side); + hblBuses.retainAll(cell.getBusNodes()); + return hblBuses.stream().findFirst(); + } + + private static void finalizeHblBuilding(BSCluster leftCluster, BSCluster rightCluster, + BusNode myNode, BusNode otherBus, List availableHblToMerge) { + Optional myHbl = leftCluster.getHblFromSideBus(myNode, Side.RIGHT); + Optional otherHbl = rightCluster.getHblFromSideBus(otherBus, Side.LEFT); + if (otherHbl.isPresent() && myHbl.isPresent()) { + myHbl.get().merge(otherHbl.get()); + rightCluster.removeHbl(otherHbl.get()); + availableHblToMerge.remove(myHbl.get()); + } + } } diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionfromextension/PositionFromExtension.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/predefined/PositionPredefined.java similarity index 69% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionfromextension/PositionFromExtension.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/predefined/PositionPredefined.java index e0f7125c6..45929fc9c 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionfromextension/PositionFromExtension.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/position/predefined/PositionPredefined.java @@ -4,9 +4,12 @@ * 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/. */ -package com.powsybl.sld.layout.positionfromextension; +package com.powsybl.sld.layout.position.predefined; -import com.powsybl.sld.layout.*; +import com.powsybl.sld.layout.position.AbstractPositionFinder; +import com.powsybl.sld.layout.position.BSCluster; +import com.powsybl.sld.layout.position.Subsection; +import com.powsybl.sld.layout.position.VerticalBusSet; import com.powsybl.sld.model.cells.*; import com.powsybl.sld.model.coordinate.Side; import com.powsybl.sld.model.graphs.VoltageLevelGraph; @@ -26,10 +29,53 @@ /** * @author Benoit Jeanson {@literal } */ -public class PositionFromExtension extends AbstractPositionFinder { - private static final Logger LOGGER = LoggerFactory.getLogger(PositionFromExtension.class); +public class PositionPredefined extends AbstractPositionFinder { + private static final Logger LOGGER = LoggerFactory.getLogger(PositionPredefined.class); + private static final Direction DEFAULTDIRECTION = Direction.TOP; - private static final HorizontalBusLaneManager HBLMANAGER = new HBLaneManagerByExtension(); + + private static final Comparator VBS_COMPARATOR = new Comparator<>() { + @Override + public int compare(VerticalBusSet vbs1, VerticalBusSet vbs2) { + for (BusNode busNode : vbs1.getBusNodeSet()) { + Optional optionalSectionIndex2 = vbs2.getBusNodeSet().stream() + .filter(busNode2 -> busNode2.getBusbarIndex() == busNode.getBusbarIndex()) + .findFirst().map(BusNode::getSectionIndex); + if (optionalSectionIndex2.isPresent() && optionalSectionIndex2.get() != busNode.getSectionIndex()) { + return busNode.getSectionIndex() - optionalSectionIndex2.get(); + } + } + + Optional order1 = externCellOrderNb(vbs1); + Optional order2 = externCellOrderNb(vbs2); + if (order1.isPresent() && order2.isPresent()) { + return order1.get() - order2.get(); + } + + int h1max = getMaxPos(vbs1.getBusNodeSet(), BusNode::getSectionIndex); + int h2max = getMaxPos(vbs2.getBusNodeSet(), BusNode::getSectionIndex); + if (h1max != h2max) { + return h1max - h2max; + } + + int v1max = getMaxPos(vbs1.getBusNodeSet(), BusNode::getBusbarIndex); + int v2max = getMaxPos(vbs2.getBusNodeSet(), BusNode::getBusbarIndex); + if (v1max != v2max) { + return v1max - v2max; + } + return vbs1.getBusNodeSet().size() - vbs2.getBusNodeSet().size(); + } + + private int getMaxPos(Set busNodes, Function fun) { + return busNodes.stream() + .map(fun).max(Integer::compareTo).orElse(0); + } + + private Optional externCellOrderNb(VerticalBusSet vbs) { + return vbs.getExternCells().stream().findFirst().map(exCell -> exCell.getOrder().orElse(-1)); + } + + }; /** * Builds the layout of the bus nodes, and organises cells (order and directions) @@ -100,23 +146,23 @@ private static void setMissingPositionIndices(BusNode busNode, List bus } @Override - public LBSCluster organizeLegBusSets(VoltageLevelGraph graph, List legBusSets) { + public BSCluster organizeBusSets(VoltageLevelGraph graph, List verticalBusSets) { gatherLayoutExtensionInformation(graph); - List lbsClusters = LBSCluster.createLBSClusters( - legBusSets.stream().sorted(sortLBS).collect(Collectors.toList())); + List bsClusters = BSCluster.createBSClusters( + verticalBusSets.stream().sorted(VBS_COMPARATOR).collect(Collectors.toList())); - LBSCluster lbsCluster = lbsClusters.get(0); + BSCluster bsCluster = bsClusters.get(0); - while (lbsClusters.size() != 1) { - lbsCluster.merge(Side.RIGHT, lbsClusters.get(1), Side.LEFT, HBLMANAGER); - lbsClusters.remove(1); + while (bsClusters.size() != 1) { + bsCluster.merge(Side.RIGHT, bsClusters.get(1), Side.LEFT, PositionPredefined::mergeHorizontalBusLists); + bsClusters.remove(1); } - lbsCluster.sortHorizontalBusLanesByVPos(); - return lbsCluster; + bsCluster.sortHblByVPos(); + return bsCluster; } - private void gatherLayoutExtensionInformation(VoltageLevelGraph graph) { + private static void gatherLayoutExtensionInformation(VoltageLevelGraph graph) { graph.getBusCellStream().forEach(bc -> { setDirection(bc); @@ -162,46 +208,30 @@ private static void setOrder(BusCell bc) { .ifPresent(bc::setOrder); } - private Comparator sortLBS = new Comparator() { - @Override - public int compare(LegBusSet lbs1, LegBusSet lbs2) { - for (BusNode busNode : lbs1.getBusNodeSet()) { - Optional optionalSectionIndex2 = lbs2.getBusNodeSet().stream() - .filter(busNode2 -> busNode2.getBusbarIndex() == busNode.getBusbarIndex()) - .findFirst().map(BusNode::getSectionIndex); - if (optionalSectionIndex2.isPresent() && optionalSectionIndex2.get() != busNode.getSectionIndex()) { - return busNode.getSectionIndex() - optionalSectionIndex2.get(); - } - } - - Optional order1 = externCellOrderNb(lbs1); - Optional order2 = externCellOrderNb(lbs2); - if (order1.isPresent() && order2.isPresent()) { - return order1.get() - order2.get(); - } - - int h1max = getMaxPos(lbs1.getBusNodeSet(), BusNode::getSectionIndex); - int h2max = getMaxPos(lbs2.getBusNodeSet(), BusNode::getSectionIndex); - if (h1max != h2max) { - return h1max - h2max; - } - - int v1max = getMaxPos(lbs1.getBusNodeSet(), BusNode::getBusbarIndex); - int v2max = getMaxPos(lbs2.getBusNodeSet(), BusNode::getBusbarIndex); - if (v1max != v2max) { - return v1max - v2max; - } - return lbs1.getBusNodeSet().size() - lbs2.getBusNodeSet().size(); - } - - private int getMaxPos(Set busNodes, Function fun) { - return busNodes.stream() - .map(fun).max(Integer::compareTo).orElse(0); - } - - private Optional externCellOrderNb(LegBusSet lbs) { - return lbs.getExternCells().stream().findAny().map(exCell -> exCell.getOrder().orElse(-1)); - } + public static void mergeHorizontalBusLists(BSCluster leftCluster, BSCluster rightCluster) { + //for this implementation, the busBar structuralPosition are already defined, + // we must ensure that structuralPosition vPos when merging left and right HorizontalPosition, + // and structuralPosition hPos are ordered + leftCluster.getHorizontalBusLists().forEach(hbl -> { + BusNode rightNodeOfLeftHbl = hbl.getSideNode(Side.RIGHT); + rightCluster.getHorizontalBusLists().stream() + .filter(hbl2 -> hbl2.getSideNode(Side.LEFT).getBusbarIndex() == rightNodeOfLeftHbl.getBusbarIndex()) + .findFirst() + .ifPresent(rightHbl -> { + BusNode leftNodeOfRightHbl = rightHbl.getSideNode(Side.LEFT); + if (leftNodeOfRightHbl == rightNodeOfLeftHbl + || rightNodeOfLeftHbl.getSectionIndex() < leftNodeOfRightHbl.getSectionIndex()) { + hbl.merge(rightHbl); + rightCluster.removeHbl(rightHbl); + } + }); + }); + mergeHblWithNoLink(leftCluster, rightCluster); + } - }; + @Override + public void organizeDirections(VoltageLevelGraph graph, List subsections) { + // Force same orientation for shunted cells + graph.getShuntCellStream().forEach(sc -> sc.alignDirections(Side.LEFT)); + } } diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/HBLaneManagerByClustering.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/HBLaneManagerByClustering.java deleted file mode 100644 index 3457c9681..000000000 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/HBLaneManagerByClustering.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2020, 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/. - */ -package com.powsybl.sld.layout.positionbyclustering; - -import com.powsybl.sld.layout.HorizontalBusLane; -import com.powsybl.sld.layout.HorizontalBusLaneManager; -import com.powsybl.sld.layout.LBSCluster; -import com.powsybl.sld.model.cells.InternCell; -import com.powsybl.sld.model.coordinate.Side; -import com.powsybl.sld.model.nodes.BusNode; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * @author Benoit Jeanson {@literal } - */ - -public class HBLaneManagerByClustering implements HorizontalBusLaneManager { - - @Override - public void mergeHorizontalBusLanes(LBSCluster leftCluster, LBSCluster rightCluster) { - List availableLanesToMerge = new ArrayList<>(leftCluster.getHorizontalBusLanes()); - mergeLanesWithCommonBusNode(leftCluster, rightCluster, availableLanesToMerge); - mergeLanesWithFlatCell(leftCluster, rightCluster, availableLanesToMerge); - mergeLanesWithNoLink(leftCluster, rightCluster); - } - - private void mergeLanesWithCommonBusNode(LBSCluster leftCluster, LBSCluster rightCluster, List availableLanesToMerge) { - List commonNodes = new ArrayList<>(leftCluster.laneSideBuses(Side.RIGHT)); - commonNodes.retainAll(rightCluster.laneSideBuses(Side.LEFT)); - commonNodes.forEach(busNode -> - finalizeLaneBuilding(leftCluster, rightCluster, busNode, busNode, availableLanesToMerge)); - } - - private void mergeLanesWithFlatCell(LBSCluster leftCluster, LBSCluster rightCluster, - List availableLanesToMerge) { - List myAvailableRightBuses = LBSCluster.laneSideBuses(Side.RIGHT, availableLanesToMerge); - List myConcernedFlatCells = leftCluster.getSideCandidateFlatCell(Side.RIGHT) - .stream().filter(internCell -> { - List nodes = internCell.getBusNodes(); - nodes.retainAll(myAvailableRightBuses); - return !myAvailableRightBuses.isEmpty(); - }).sorted(Comparator.comparing(InternCell::getFullId)) //avoid randomness - .collect(Collectors.toList()); - List otherConcernedFlatCells = rightCluster.getSideCandidateFlatCell(Side.LEFT); - myConcernedFlatCells.retainAll(otherConcernedFlatCells); - - myConcernedFlatCells.stream() - .sorted(Comparator - .comparingInt(cell -> Link.flatCellDistanceToEdges(cell, - new LBSClusterSide(leftCluster, Side.RIGHT), - new LBSClusterSide(rightCluster, Side.LEFT))) - .thenComparing(InternCell::getFullId)) - .forEachOrdered(internCell -> { - BusNode myNode = internCellNodeInLaneSide(leftCluster, Side.RIGHT, internCell); - BusNode otherNode = internCellNodeInLaneSide(rightCluster, Side.LEFT, internCell); - if (myNode != null && otherNode != null) { - internCell.setFlat(); - finalizeLaneBuilding(leftCluster, rightCluster, myNode, otherNode, availableLanesToMerge); - } - }); - } - - private BusNode internCellNodeInLaneSide(LBSCluster lbsCluster, Side side, InternCell cell) { - List laneBuses = lbsCluster.laneSideBuses(side); - laneBuses.retainAll(cell.getBusNodes()); - return laneBuses.isEmpty() ? null : laneBuses.get(0); - } - - private void finalizeLaneBuilding(LBSCluster leftCluster, LBSCluster rightCluster, - BusNode myNode, BusNode otherBus, List availableLanesToMerge) { - HorizontalBusLane myLane = leftCluster.getHorizontalLaneFromSideBus(myNode, Side.RIGHT); - HorizontalBusLane otherLane = rightCluster.getHorizontalLaneFromSideBus(otherBus, Side.LEFT); - if (otherLane != null && myLane != null) { - myLane.merge(otherLane); - rightCluster.removeHorizontalBusLane(otherLane); - availableLanesToMerge.remove(myLane); - } - } -} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/LBSClusterSide.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/LBSClusterSide.java deleted file mode 100644 index 4eb0bec4e..000000000 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/LBSClusterSide.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2019, 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/. - */ -package com.powsybl.sld.layout.positionbyclustering; - -import com.powsybl.sld.layout.HorizontalBusLane; -import com.powsybl.sld.layout.LBSCluster; -import com.powsybl.sld.layout.LegBusSet; -import com.powsybl.sld.model.cells.ExternCell; -import com.powsybl.sld.model.cells.InternCell; -import com.powsybl.sld.model.coordinate.Side; -import com.powsybl.sld.model.nodes.BusNode; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * LBSClusterSide is a ClusterConnector defined by one Side (LEFT/RIGHT) of a LBSCluster. - * - * @author Benoit Jeanson {@literal } - */ -class LBSClusterSide { - - private final LBSCluster lbsCluster; - private final Side side; - private final List myLinks = new ArrayList<>(); - private LBSClusterSide otherSameRoot; - - LBSClusterSide(LBSCluster lbsCluster, Side side) { - this.lbsCluster = Objects.requireNonNull(lbsCluster); - this.side = Objects.requireNonNull(side); - } - - void setOtherSameRoot(LBSClusterSide otherSameRoot) { - this.otherSameRoot = otherSameRoot; - } - - Set getBusNodeSet() { - return new LinkedHashSet<>(lbsCluster.laneSideBuses(side)); - } - - List getCandidateFlatCellList() { - return lbsCluster.getSideCandidateFlatCell(side); - } - - List getExternCells() { - return lbsCluster.getLbsList().stream().flatMap(lbs -> lbs.getExternCells().stream()).collect(Collectors.toList()); - } - - int getExternCellAttractionToEdge(ExternCell cell) { - List lbsList = lbsCluster.getLbsList(); - return lbsList.stream().filter(lbs -> lbs.getExternCells().contains(cell)).findFirst() - .map(lbs -> side == Side.LEFT ? (lbsList.size() - lbsList.indexOf(lbs)) - : (lbsList.indexOf(lbs) + 1)).orElse(0); - } - - List getInternCellsFromShape(InternCell.Shape shape) { - return lbsCluster.getInternCellsFromShape(shape); - } - - LBSCluster getCluster() { - return lbsCluster; - } - - Side getMySideInCluster() { - return side; - } - - boolean hasSameRoot(Object other) { - if (other.getClass() != LBSClusterSide.class) { - return false; - } - return this.lbsCluster == ((LBSClusterSide) other).getCluster(); - } - - LBSClusterSide getOtherSameRoot() { - return otherSameRoot; - } - - int getCandidateFlatCellDistanceToEdge(InternCell internCell) { - List buses = internCell.getBusNodes(); - buses.retainAll(getBusNodeSet()); - if (buses.isEmpty()) { - return 100; - } - BusNode busNode = buses.get(0); //shall have only one as used for a flatCell - Optional horizontalBusLane = lbsCluster.getHorizontalBusLanes() - .stream() - .filter(lane -> side == Side.LEFT && lane.getBusNodes().get(0) == busNode - || side == Side.RIGHT && lane.getBusNodes().get(lane.getBusNodes().size() - 1) == busNode) - .findFirst(); - if (!horizontalBusLane.isPresent()) { - return 100; - } else { - if (side == Side.LEFT) { - return horizontalBusLane.get().getStartingIndex(); - } else { - return lbsCluster.getLbsList().size() - horizontalBusLane.get().getEndingIndex(); - } - } - } - - void addLink(Link link) { - myLinks.add(link); - } - - void removeLink(Link link) { - myLinks.remove(link); - } - - List getLinks() { - return myLinks; - } - - @Override - public String toString() { - return side.toString() + " " + lbsCluster.laneSideBuses(side).toString(); - } -} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/Links.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/Links.java deleted file mode 100644 index 55f6c70f8..000000000 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionbyclustering/Links.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) 2019, 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/. - */ -package com.powsybl.sld.layout.positionbyclustering; - -import com.powsybl.sld.layout.HorizontalBusLaneManager; -import com.powsybl.sld.layout.LBSCluster; -import com.powsybl.sld.model.coordinate.Side; - -import java.util.*; - -/** - * Manages the links between a list of lbsClusterSides. - * - * @author Benoit Jeanson {@literal } - */ -final class Links { - - private final List lbsClusterSides = new LinkedList<>(); - private final TreeSet linkSet = new TreeSet<>(); - private HorizontalBusLaneManager hblManager; - private int linkCounter = 0; - - private Links(HorizontalBusLaneManager hblManager) { - this.hblManager = hblManager; - } - - public static Links create(List lbsClusters, HorizontalBusLaneManager hblManager) { - Links links = new Links(hblManager); - lbsClusters.forEach(lbsCluster -> addClusterSidesTwins(links, lbsCluster)); - return links; - } - - private static void addClusterSidesTwins(Links links, LBSCluster lbsCluster) { - LBSClusterSide lbsSLeft = new LBSClusterSide(lbsCluster, Side.LEFT); - LBSClusterSide lbsSRight = new LBSClusterSide(lbsCluster, Side.RIGHT); - lbsSLeft.setOtherSameRoot(lbsSRight); - lbsSRight.setOtherSameRoot(lbsSLeft); - links.addLBSClusterSide(lbsSLeft); - links.addLBSClusterSide(lbsSRight); - } - - private void addLBSClusterSide(LBSClusterSide lbsClusterSide) { - lbsClusterSides.forEach(cc -> buildNewLink(cc, lbsClusterSide)); - lbsClusterSides.add(lbsClusterSide); - } - - private void buildNewLink(LBSClusterSide lbsClusterSide1, LBSClusterSide lbsClusterSide2) { - if (!lbsClusterSide1.hasSameRoot(lbsClusterSide2)) { - Link linkToAdd = new Link(lbsClusterSide1, lbsClusterSide2, linkCounter++); - linkSet.add(linkToAdd); - } - } - - Link getStrongestLink() { - return linkSet.last(); - } - - void mergeLink(Link link) { - link.mergeClusters(hblManager); - LBSCluster mergedCluster = link.getlbsClusterSide(0).getCluster(); - removeLBSClusterSide(link.getlbsClusterSide(0)); - removeLBSClusterSide(link.getlbsClusterSide(1)); - removeLBSClusterSide(link.getlbsClusterSide(0).getOtherSameRoot()); - removeLBSClusterSide(link.getlbsClusterSide(1).getOtherSameRoot()); - addClusterSidesTwins(this, mergedCluster); - } - - private void removeLBSClusterSide(LBSClusterSide lbsClusterSide) { - lbsClusterSides.remove(lbsClusterSide); - List linksCopy = new ArrayList<>(lbsClusterSide.getLinks()); - linksCopy.forEach(link -> { - link.removeMe(); - linkSet.remove(link); - }); - } - - boolean isEmpty() { - return linkSet.isEmpty(); - } - - LBSCluster getFinalLBSCluster() { - return lbsClusterSides.get(0).getCluster(); - } -} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionfromextension/HBLaneManagerByExtension.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionfromextension/HBLaneManagerByExtension.java deleted file mode 100644 index a41cb5aa6..000000000 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/positionfromextension/HBLaneManagerByExtension.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) 2020, 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/. - */ -package com.powsybl.sld.layout.positionfromextension; - -import com.powsybl.sld.layout.HorizontalBusLane; -import com.powsybl.sld.layout.HorizontalBusLaneManager; -import com.powsybl.sld.layout.LBSCluster; -import com.powsybl.sld.model.coordinate.Side; -import com.powsybl.sld.model.nodes.BusNode; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Optional; - -/** - * @author Benoit Jeanson {@literal } - */ - -public class HBLaneManagerByExtension implements HorizontalBusLaneManager { - private static final Logger LOGGER = LoggerFactory.getLogger(HBLaneManagerByExtension.class); - - public void mergeHorizontalBusLanes(LBSCluster leftCluster, LBSCluster rightCluster) { - //for this implementation, the busBar structuralPosition are already defined, - // we must ensure that structuralPosition vPos when merging left and right HorizontalPosition, - // and structuralPosition hPos are ordered - leftCluster.getHorizontalBusLanes().forEach(hbl -> { - BusNode rightNodeOfLeftHbl = hbl.getSideNode(Side.RIGHT); - Optional rightHBL = rightCluster.getHorizontalBusLanes().stream() - .filter(hbl2 -> hbl2.getSideNode(Side.LEFT).getBusbarIndex() == rightNodeOfLeftHbl.getBusbarIndex()) - .findFirst(); - if (rightHBL.isPresent() - && (rightHBL.get().getSideNode(Side.LEFT) == rightNodeOfLeftHbl - || rightNodeOfLeftHbl.getSectionIndex() < rightHBL.get().getSideNode(Side.LEFT).getSectionIndex())) { - hbl.merge(rightHBL.get()); - rightCluster.removeHorizontalBusLane(rightHBL.get()); - } else { - // TODO : not true !!! - LOGGER.warn("incoherent structural horizontal positions in mergeHorizontalBusLanes"); - } - }); - mergeLanesWithNoLink(leftCluster, rightCluster); - } -} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/zonebygrid/Matrix.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/zonebygrid/Matrix.java index 05deff92d..93b59828f 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/zonebygrid/Matrix.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/zonebygrid/Matrix.java @@ -36,7 +36,7 @@ public Matrix set(int row, int col, MatrixCell cell) { } public Optional get(String id) { - return stream().filter(c -> c.getId().equals(id)).findAny(); + return stream().filter(c -> c.getId().equals(id)).findFirst(); } public MatrixCell get(int row, int col) { diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/model/graphs/VoltageLevelGraph.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/model/graphs/VoltageLevelGraph.java index ff0792bb7..88decf635 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/model/graphs/VoltageLevelGraph.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/model/graphs/VoltageLevelGraph.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.powsybl.commons.PowsyblException; -import com.powsybl.sld.layout.GraphTraversal; import com.powsybl.sld.library.ComponentTypeName; import com.powsybl.sld.model.cells.*; import com.powsybl.sld.model.coordinate.Direction; @@ -17,6 +16,7 @@ import com.powsybl.sld.model.nodes.*; import com.powsybl.sld.model.nodes.Node.NodeType; import com.powsybl.sld.model.nodes.feeders.FeederTwLeg; +import com.powsybl.sld.util.GraphTraversal; import org.jgrapht.graph.Pseudograph; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/GraphTraversal.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/util/GraphTraversal.java similarity index 94% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/GraphTraversal.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/util/GraphTraversal.java index ada4f4fee..bafa395cd 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/GraphTraversal.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/util/GraphTraversal.java @@ -4,7 +4,7 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.util; import com.powsybl.sld.model.nodes.Node; @@ -52,7 +52,7 @@ public static boolean run(Node node, Predicate extremityCriteria, Predicat return true; } - static Set run(Node node, Predicate extremityCriteria, Set outsideNodes) { + public static Set run(Node node, Predicate extremityCriteria, Set outsideNodes) { Set nodesResult = new LinkedHashSet<>(); run(node, extremityCriteria, n -> false, nodesResult, outsideNodes); return nodesResult; diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/TopologicallyConnectedNodesSet.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologicallyConnectedNodesSet.java similarity index 97% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/TopologicallyConnectedNodesSet.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologicallyConnectedNodesSet.java index 6ac3a843b..36675a89f 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/TopologicallyConnectedNodesSet.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologicallyConnectedNodesSet.java @@ -4,7 +4,7 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.util; import com.powsybl.sld.model.nodes.Node; diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/TopologyCalculation.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologyCalculation.java similarity index 99% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/TopologyCalculation.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologyCalculation.java index c71d49872..f75d5a76b 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/TopologyCalculation.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologyCalculation.java @@ -4,7 +4,7 @@ * 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/. */ -package com.powsybl.sld.layout; +package com.powsybl.sld.util; import com.powsybl.sld.model.graphs.VoltageLevelGraph; import com.powsybl.sld.model.nodes.Node; diff --git a/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/iidm/TestInternalBranchesNodeBreaker.java b/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/iidm/TestInternalBranchesNodeBreaker.java index 405a73304..75b35f3b3 100644 --- a/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/iidm/TestInternalBranchesNodeBreaker.java +++ b/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/iidm/TestInternalBranchesNodeBreaker.java @@ -12,7 +12,7 @@ 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.layout.position.predefined.PositionPredefined; import com.powsybl.sld.model.graphs.SubstationGraph; import com.powsybl.sld.model.graphs.VoltageLevelGraph; import org.junit.jupiter.api.BeforeEach; @@ -51,7 +51,7 @@ void testVLGraphExternal2WT() { // 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); + new PositionVoltageLevelLayoutFactory(new PositionPredefined(), pvllfParameters).create(g).run(this.layoutParameters); assertEquals(toString("/InternalBranchesNodeBreaker_externalPst.svg"), toSVG(g, "/InternalBranchesNodeBreaker_externalPst.svg", componentLibrary, layoutParameters, svgParameters, getDefaultDiagramLabelProvider(), getDefaultDiagramStyleProvider())); } diff --git a/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/iidm/TestTopologyCalculation.java b/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/iidm/TestTopologyCalculation.java index e19c71054..0390e7c4b 100644 --- a/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/iidm/TestTopologyCalculation.java +++ b/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/iidm/TestTopologyCalculation.java @@ -13,11 +13,11 @@ import com.powsybl.iidm.network.TopologyKind; import com.powsybl.iidm.network.extensions.ConnectablePosition; import com.powsybl.sld.builders.NetworkGraphBuilder; -import com.powsybl.sld.layout.TopologicallyConnectedNodesSet; -import com.powsybl.sld.layout.TopologyCalculation; import com.powsybl.sld.model.graphs.VoltageLevelGraph; import com.powsybl.sld.model.nodes.Node; import com.powsybl.sld.model.nodes.SwitchNode; +import com.powsybl.sld.util.TopologicallyConnectedNodesSet; +import com.powsybl.sld.util.TopologyCalculation; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/raw/TestOrderConsistency.java b/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/raw/TestOrderConsistency.java index 55654e79a..ce9625439 100644 --- a/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/raw/TestOrderConsistency.java +++ b/single-line-diagram/single-line-diagram-core/src/test/java/com/powsybl/sld/raw/TestOrderConsistency.java @@ -9,7 +9,7 @@ import com.powsybl.sld.builders.VoltageLevelRawBuilder; import com.powsybl.sld.layout.PositionVoltageLevelLayoutFactory; import com.powsybl.sld.layout.PositionVoltageLevelLayoutFactoryParameters; -import com.powsybl.sld.layout.positionbyclustering.PositionByClustering; +import com.powsybl.sld.layout.position.clustering.PositionByClustering; import com.powsybl.sld.model.graphs.VoltageLevelGraph; import com.powsybl.sld.model.nodes.BusNode; import com.powsybl.sld.model.nodes.ConnectivityNode;