powsybl / powsybl-core

A framework to build power system oriented software
https://www.powsybl.org
Mozilla Public License 2.0
122 stars 39 forks source link

Complete the nodebreaker/busbreaker/bus views/topology APIs #3107

Open jonenst opened 1 month ago

jonenst commented 1 month ago

Describe the current behavior

powysbl has many methods to associate objects between the different views depending on the different topology. For example, voltageLevel.getBusView().getMergedBus( string id ) gives the bus from the busview from either an id of busbarsection (nodebreaker topology) or from the id of a configured bus (busbreaker topology)

Similarly, vl.getBusBreakerView().getBusesFromBusViewBusId() gives buses of the busbreakerview for a bus of the busview

Networks.getNodes gives the nodes for a bus of the busview (kind of like getConnectedTerminals ..?)

Unless I'm mistaken, the system is not complete, you can't get from busbreakerbuses to their busviewbuses Another case, when a busbreakerbus doesn't have a busbar section, because a switch is open between the equipment and its busbar sections, you can't easily find the busbar section (that would be connected to after a call to connect() ). Something like getConnectableBusbarSection() is missing ? It would need the same predicate as connect()

Describe the expected behavior

Maybe add documentation on the modeling differences and commonalities between nodebreaker/busbreaker topology. Explain more how you can go from one view to another, etc. This should put some light on what is currently missing.

Describe the motivation

Have a homogeneous and simple and complete system

Extra Information

No response

jonenst commented 1 month ago

we should improve the drawing of https://javadoc.io/static/com.powsybl/powsybl-core/6.4.1/com/powsybl/iidm/network/doc-files/nodeBreakerTopologyGraph.svg to have the pink edge more closely associated with the coupler, it's far to the right and not obvious that this is what the pink edge represent (it's hard to draw something nice though, I guess that's why the pink edge was drawn directly between the nodes)

jonenst commented 1 month ago

We should add more voltage level examples to https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/VoltageLevel.html (keep the existing simple and standard one, but add more to show what is possible)

jonenst commented 1 month ago

We should highlight the differences/possibilities between a busbreakerview of a nodebreakertopology voltagelevel and an equivalent voltagelevel described directly in busbreaker topology. For example, when an equipment has an open switch to a busbar, in the busbreakerview it gets its own bus. To model this in busbreaker topology, you need to create this configured bus, but this is contrary to the docs that say

bus/breaker model: this is an aggregated form of the topology made of buses and breakers. A bus is the aggregation of busbar sections and closed switches.

here the bus does not aggregate busbars. So we need to explain more the differences between a computed busbreakerview and a modeled busbreakertopology, are you always supposed to be able to create equivalent ones or are there cases where it's not possible ?

Another example, in the busbreaker topology, an equipement is connectable only to one bus, whereas in the nodebreakertopology you can connect to any number of busbar. So you need to create more configured bus in the busbreakertopology to model this, but is it what you are supposed to do ?

jonenst commented 1 month ago

We should mention that in the busbreakertopology, the busbars from the nodebreakerview are still visible as equipments, it's a bit counterintutive (or maybe a feature ?)

jonenst commented 1 month ago

sample code to improve:

      System.out.println();
        System.out.println(s1vl1.getTopologyKind());

        if (s1vl1.getTopologyKind() == TopologyKind.NODE_BREAKER) {
            System.out.println("busbars: " + s1vl1.getNodeBreakerView().getBusbarSectionStream().toList());
        }
        System.out.println("busbreakerviewbuses: " + s1vl1.getBusBreakerView().getBusStream().toList());
        System.out.println("busviewbuses: " + s1vl1.getBusView().getBusStream().toList());

        System.out.println("busviewbus -> busbreakerviewbus:" + s1vl1.getBusView().getBusStream().map(Bus::getId)
                .collect(Collectors.toMap(Function.identity(), s1vl1.getBusBreakerView()::getBusesFromBusViewBusId)));

        if (s1vl1.getTopologyKind() == TopologyKind.NODE_BREAKER) {
            System.out.println("busviewbus -> busbars:" + s1vl1.getBusView().getBusStream().map(Bus::getId)
                    .collect(Collectors.toMap(Function.identity(),
                    // can it be simpler than getNodes and then filter for busbar?
                            busid -> Networks.getNodes(busid, s1vl1, t -> t.getBusView().getBus())
                                    .mapToObj(node -> s1vl1.getNodeBreakerView().getTerminal(node))
                                    .map(Terminal::getConnectable)
                                    .filter(x -> x.getType() == IdentifiableType.BUSBAR_SECTION).toList())));

            System.out.println("busviewbus -> busbars:" + s1vl1.getBusView().getBusStream()
                    .collect(Collectors.toMap(Bus::getId,
                    // what's the difference with getNodes ?
                            bus -> bus.getConnectedTerminalStream()
                            .map(Terminal::getConnectable)
                            .filter(x -> x.getType() == IdentifiableType.BUSBAR_SECTION).toList())));

            System.out
                    .println("busbreakerviewbus -> busbars:" + s1vl1.getBusBreakerView().getBusStream().map(Bus::getId)
                            .collect(Collectors.toMap(Function.identity(),
                                    busid -> Networks.getNodes(busid, s1vl1, t -> t.getBusBreakerView().getBus())
                                            .mapToObj(node -> s1vl1.getNodeBreakerView().getTerminal(node))
                                            .map(Terminal::getConnectable)
                                            .filter(x -> x.getType() == IdentifiableType.BUSBAR_SECTION).toList())));

            System.out.println("busbars -> bus: " + s1vl1.getNodeBreakerView().getBusbarSectionStream()
                    .map(BusbarSection::getId).collect(Collectors.toMap(Function.identity(),
                            busid -> Optional.ofNullable(s1vl1.getBusView().getMergedBus(busid)))));

            System.out.println("busbreakerbus -> bus: not possible");
            System.out.println("busbreakerbus -> busbar: not possible for "disconnected equipment buses");
            System.out.println("bus -> connectable: "
                    + s1vl1.getBusBreakerView().getBusStream().collect(Collectors.toMap(Bus::getId,
                            bus -> bus.getConnectedTerminalStream().map(Terminal::getConnectable).toList())));

        }

        if (s1vl1.getTopologyKind() == TopologyKind.BUS_BREAKER) {
            System.out.println("busbreakerbus -> bus: " + s1vl1.getBusBreakerView().getBusStream().map(Bus::getId)
                    .collect(Collectors.toMap(Function.identity(),
                            busid -> Optional.ofNullable(s1vl1.getBusView().getMergedBus(busid)))));

            System.out.println("bus -> connectable: " + s1vl1.getBusView().getBusStream()
                    .collect(Collectors.toMap(Bus::getId,
                            bus -> bus.getConnectedTerminalStream().map(Terminal::getConnectable).toList())));
        }

NODE_BREAKER
busbars: [S1BBS1, S1BBS2, S1BBS3, S1BBS4, S1BBS5]
busbreakerviewbuses: [S1VL1_0, S1VL1_1, S1VL1_3, S1VL1_9, S1VL1_5]
busviewbuses: [S1VL1_0, S1VL1_3]
busviewbus -> busbreakerviewbus:{S1VL1_3=[S1VL1_3], S1VL1_0=[S1VL1_1, S1VL1_0]}
busviewbus -> busbars:{S1VL1_3=[S1BBS4], S1VL1_0=[S1BBS1, S1BBS2, S1BBS3]}
busviewbus -> busbars:{S1VL1_3=[S1BBS4], S1VL1_0=[S1BBS1, S1BBS2, S1BBS3]}
# notice empty list for S1VL1_5 the dangling load 
busbreakerviewbus -> busbars:{S1VL1_3=[S1BBS4], S1VL1_5=[], S1VL1_9=[S1BBS5], S1VL1_1=[S1BBS2], S1VL1_0=[S1BBS1, S1BBS3]}
# notice dangling busbar S1BBS5 removed from busview
busbars -> bus: {S1BBS5=Optional.empty, S1BBS4=Optional[S1VL1_3], S1BBS3=Optional[S1VL1_0], S1BBS2=Optional[S1VL1_0], S1BBS1=Optional[S1VL1_0]}
busbreakerbus -> bus: not possible
busbreakerbus -> busbar: not possible for 'disconnected equipment buses'
bus -> connectable: {S1VL1_3=[S1BBS4, load5], S1VL1_5=[load2], S1VL1_9=[S1BBS5], S1VL1_1=[S1BBS2, load3], S1VL1_0=[S1BBS1, S1BBS3, load4, load1]}

BUS_BREAKER
busbreakerviewbuses: [bus1]
busviewbuses: [S1VL2_0]
busviewbus -> busbreakerviewbus:{S1VL2_0=[bus1]}
busbreakerbus -> bus: {bus1=Optional[S1VL2_0]}
bus -> connectable: {S1VL2_0=[load12]}
S1VL1_0
``
jonenst commented 1 month ago

see https://github.com/powsybl/powsybl-tutorials/blob/main/topology/src/main/java/com/powsybl/tutorials/topology/VoltageLevelTopologyTutorial.java

jonenst commented 1 month ago

see https://powsybl.readthedocs.io/projects/powsybl-core/en/latest/grid_model/network_subnetwork.html (previously https://www.powsybl.org/pages/documentation/grid/model/ )