SwitchesFlowTest.java

/**
 * Copyright (c) 2021, RTE (http://www.rte-france.com)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.iidm.network.impl.util;

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

import java.time.ZonedDateTime;
import org.junit.jupiter.api.Test;

import com.powsybl.iidm.network.EnergySource;
import com.powsybl.iidm.network.Generator;
import com.powsybl.iidm.network.Line;
import com.powsybl.iidm.network.Load;
import com.powsybl.iidm.network.LoadType;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.NetworkFactory;
import com.powsybl.iidm.network.Substation;
import com.powsybl.iidm.network.SwitchKind;
import com.powsybl.iidm.network.TopologyKind;
import com.powsybl.iidm.network.TwoWindingsTransformer;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.iidm.network.util.SwitchesFlow;

/**
 *
 * @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
 * @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
 */
class SwitchesFlowTest {

    @Test
    void switchesFlowNodeBreaker() {

        Network network = createNodeBreaker();

        VoltageLevel voltageLevel11 = network.getVoltageLevel("S1VL1");
        SwitchesFlow switchesFlow11 = new SwitchesFlow(voltageLevel11);

        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-0B-1B", -180.0, -19.0));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-0A-1A", 10.0, -1.0));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-1B-2B", 70.0, 10.0));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-0B-10", 100.0, 10.0));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-1A-8", -80.0, -10.0));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-0A-0B", -80.0, -9.0));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-2A-2B", 0.0, 0.0));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-1A-1B", 0.0, 0.0));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-2B-12", 70.0, 10.0));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-1A-2A", 90.0, 9.0));

        VoltageLevel voltageLevel12 = network.getVoltageLevel("S1VL2");
        SwitchesFlow switchesFlow12 = new SwitchesFlow(voltageLevel12);

        assertTrue(compareSwitchFlow(switchesFlow12, "S1VL2-SW-0-1", 251.0, 35.0));
        assertTrue(compareSwitchFlow(switchesFlow12, "S1VL2-SW-0-2", -251.0, -35.0));

        VoltageLevel voltageLevel21 = network.getVoltageLevel("S2VL1");
        SwitchesFlow switchesFlow21 = new SwitchesFlow(voltageLevel21);

        assertTrue(compareSwitchFlow(switchesFlow21, "S2VL1-SW-0-1", -75.0, -15.0));
        assertTrue(compareSwitchFlow(switchesFlow21, "S2VL1-SW-0-2", 75.0, 15.0));

        VoltageLevel voltageLevel31 = network.getVoltageLevel("S3VL1");
        SwitchesFlow switchesFlow31 = new SwitchesFlow(voltageLevel31);

        assertTrue(compareSwitchFlow(switchesFlow31, "S3VL1-SW-0-1", -71.0, -11.0));
        assertTrue(compareSwitchFlow(switchesFlow31, "S3VL1-SW-0-2", 71.0, 11.0));
    }

    private static Network createNodeBreaker() {
        return createNodeBreaker(NetworkFactory.findDefault());
    }

    private static Network createNodeBreaker(NetworkFactory networkFactory) {

        Network network = networkFactory.createNetwork("SwitchesFlow-NodeBreaker", "test");
        network.setCaseDate(ZonedDateTime.parse("2017-06-25T17:43:00.000+01:00"));
        network.setForecastDistance(0);

        // First substation
        Substation s1 = network.newSubstation()
                .setId("S1")
                .add();
        VoltageLevel s1vl1 = s1.newVoltageLevel()
                .setId("S1VL1")
                .setNominalV(225.0)
                .setLowVoltageLimit(220.0)
                .setHighVoltageLimit(240.0)
                .setTopologyKind(TopologyKind.NODE_BREAKER)
                .add();

        createBusbarSection(s1vl1, "S1VL1_BBS0A", "S1VL1_BBS0A", 0);
        createBusbarSection(s1vl1, "S1VL1_BBS1A", "S1VL1_BBS1A", 1);
        createBusbarSection(s1vl1, "S1VL1_BBS2A", "S1VL1_BBS2A", 2);

        createBusbarSection(s1vl1, "S1VL1_BBS0B", "S1VL1_BBS0B", 3);
        createBusbarSection(s1vl1, "S1VL1_BBS1B", "S1VL1_BBS1B", 4);
        createBusbarSection(s1vl1, "S1VL1_BBS2B", "S1VL1_BBS2B", 5);

        createBusbarSection(s1vl1, "S1VL1_BBS0C", "S1VL1_BBS0C", 6);

        VoltageLevel s1vl2 = s1.newVoltageLevel()
                .setId("S1VL2")
                .setNominalV(400.0)
                .setLowVoltageLimit(380.0)
                .setHighVoltageLimit(430.0)
                .setTopologyKind(TopologyKind.NODE_BREAKER)
                .add();

        createBusbarSection(s1vl2, "S1VL2_BBS0", "S1VL2_BBS0", 0);

        // Second substation
        Substation s2 = network.newSubstation()
                .setId("S2")
                .add();
        VoltageLevel s2vl1 = s2.newVoltageLevel()
                .setId("S2VL1")
                .setNominalV(225.0)
                .setLowVoltageLimit(220.0)
                .setHighVoltageLimit(240.0)
                .setTopologyKind(TopologyKind.NODE_BREAKER)
                .add();

        createBusbarSection(s2vl1, "S2VL1_BBS0", "S2VL1_BBS0", 0);

        // Third substation
        Substation s3 = network.newSubstation()
                .setId("S3")
                .add();
        VoltageLevel s3vl1 = s3.newVoltageLevel()
                .setId("S3VL1")
                .setNominalV(225.0)
                .setLowVoltageLimit(220.0)
                .setHighVoltageLimit(240.0)
                .setTopologyKind(TopologyKind.NODE_BREAKER)
                .add();

        createBusbarSection(s3vl1, "S3VL1_BBS0", "S3VL1_BBS0", 0);

        // Switches and internalConnections on the first voltage level of substation 1
        // Connect a load on the first voltage level of substation 1
        createSwitch(s1vl1, "S1VL1-SW-0A-1A", SwitchKind.BREAKER, false, 0, 1);
        createSwitch(s1vl1, "S1VL1-SW-1A-2A", SwitchKind.BREAKER, false, 1, 2);
        createSwitch(s1vl1, "S1VL1-SW-0B-1B", SwitchKind.BREAKER, false, 3, 4);
        createSwitch(s1vl1, "S1VL1-SW-1B-2B", SwitchKind.BREAKER, false, 4, 5);

        createSwitch(s1vl1, "S1VL1-SW-0A-0B", SwitchKind.BREAKER, false, 0, 3);
        createSwitch(s1vl1, "S1VL1-SW-1A-1B", SwitchKind.BREAKER, false, 1, 4);
        createSwitch(s1vl1, "S1VL1-SW-2A-2B", SwitchKind.BREAKER, false, 2, 5);

        createSwitch(s1vl1, "S1VL1-SW-0A-1B", SwitchKind.DISCONNECTOR, true, 0, 4);

        createSwitch(s1vl1, "S1VL1-SW-1A-8", SwitchKind.DISCONNECTOR, false, 1, 8);
        createSwitch(s1vl1, "S1VL1-SW-0B-10", SwitchKind.DISCONNECTOR, false, 3, 10);
        createSwitch(s1vl1, "S1VL1-SW-2B-12", SwitchKind.DISCONNECTOR, false, 5, 12);

        createSwitch(s1vl1, "S1VL1-SW-0C-13", SwitchKind.DISCONNECTOR, true, 6, 13);

        createInternalConnection(s1vl1, 0, 7);
        createInternalConnection(s1vl1, 2, 9);
        createInternalConnection(s1vl1, 4, 11);
        createInternalConnection(s1vl1, 5, 12);
        createInternalConnection(s1vl1, 13, 14);

        // Add two nodes
        createSwitch(s1vl2, "S1VL2-SW-0-1", SwitchKind.DISCONNECTOR, false, 0, 1);
        createSwitch(s1vl2, "S1VL2-SW-0-2", SwitchKind.DISCONNECTOR, false, 0, 2);

        createSwitch(s2vl1, "S2VL1-SW-0-1", SwitchKind.DISCONNECTOR, false, 0, 1);
        createSwitch(s2vl1, "S2VL1-SW-0-2", SwitchKind.DISCONNECTOR, false, 0, 2);

        createSwitch(s3vl1, "S3VL1-SW-0-1", SwitchKind.DISCONNECTOR, false, 0, 1);
        createSwitch(s3vl1, "S3VL1-SW-0-2", SwitchKind.DISCONNECTOR, false, 0, 2);

        // Create Loads
        createLoad(s1vl1, "S1VL1-Load-9", 90.0, 9.0, 9);
        createLoad(s1vl1, "S1VL1-Load-10", 100.0, 10.0, 10);
        createLoad(s1vl1, "S1VL1-Load-14", 0.0, 0.0, 14);

        // Generators
        createGenerator(s1vl1, "S1VL1-Generator-8", 80.0, 10.0, 8);

        createTwoWindingsTransformer(s1, "S1VL1", "S1VL2", "S1-TWT-11-0", -250.0, -29.0, 251.0, 35.0, 11, 1);

        // Connect a load at the first substation, second voltage level
        createLoad(s1vl2, "S1VL2-Load-0", -251.0, -35.0, 2);

        // Connect a load at the second substation
        createLoad(s2vl1, "S2VL1-Load-0", 75.0, 15.0, 2);

        // Connect a load at the third substation
        createLoad(s3vl1, "S3VL1-Load-0", 71.0, 11.0, 2);

        // Create two lines
        createLine(network, "S1VL1", "S2VL1", "Line-7-S2VL1", 70.0, 10.0, -75.0, -15.0, 7, 1);
        createLine(network, "S1VL1", "S3VL1", "Line-12-S3VL1", 70.0, 10.0, -71.0, -11.0, 12, 1);

        return network;
    }

    private static void createBusbarSection(VoltageLevel vl, String id, String name, int node) {
        vl.getNodeBreakerView().newBusbarSection()
            .setId(id)
            .setName(name)
            .setNode(node)
            .add();
    }

    private static void createSwitch(VoltageLevel vl, String id, SwitchKind kind, boolean open, int node1, int node2) {
        vl.getNodeBreakerView().newSwitch()
                .setId(id)
                .setName(id)
                .setKind(kind)
                .setRetained(kind.equals(SwitchKind.BREAKER))
                .setOpen(open)
                .setFictitious(false)
                .setNode1(node1)
                .setNode2(node2)
                .add();
    }

    private static void createInternalConnection(VoltageLevel vl, int node1, int node2) {
        vl.getNodeBreakerView().newInternalConnection()
                .setNode1(node1)
                .setNode2(node2)
                .add();
    }

    private static void createLoad(VoltageLevel vl, String id, double p, double q, int node) {
        Load load = vl.newLoad()
            .setId(id)
            .setLoadType(LoadType.UNDEFINED)
            .setP0(p)
            .setQ0(q)
            .setNode(node)
            .add();
        load.getTerminal().setP(p).setQ(q);
    }

    private static void createGenerator(VoltageLevel vl, String id, double p, double q, int node) {
        Generator generator = vl.newGenerator()
            .setId(id)
            .setEnergySource(EnergySource.HYDRO)
            .setMinP(0.0)
            .setMaxP(100.0)
            .setVoltageRegulatorOn(false)
            .setTargetP(p)
            .setTargetV(225.0)
            .setTargetQ(q)
            .setNode(node)
            .add();
        generator.newMinMaxReactiveLimits()
            .setMinQ(-100)
            .setMaxQ(100)
            .add();
        generator.getTerminal().setP(-p).setQ(-q);

    }

    private static void createLine(Network network, String vl1id, String vl2id, String id, double p1, double q1,
        double p2, double q2, int node1, int node2) {
        Line line = network.newLine()
            .setId(id)
            .setR(0.01)
            .setX(20.0)
            .setG1(0.0)
            .setB1(0.0)
            .setG2(0.0)
            .setB2(0.0)
            .setNode1(node1)
            .setVoltageLevel1(vl1id)
            .setNode2(node2)
            .setVoltageLevel2(vl2id)
            .add();
        line.getTerminal1().setP(p1).setQ(q1);
        line.getTerminal2().setP(p2).setQ(q2);
    }

    private static void createTwoWindingsTransformer(Substation s, String vl1id, String vl2id, String id,
        double p1, double q1, double p2, double q2, int node1, int node2) {
        TwoWindingsTransformer twt = s.newTwoWindingsTransformer()
            .setId(id)
            .setR(2.0)
            .setX(14.)
            .setG(0.0)
            .setB(0.0)
            .setRatedU1(225.0)
            .setRatedU2(400.0)
            .setNode1(node1)
            .setVoltageLevel1(vl1id)
            .setNode2(node2)
            .setVoltageLevel2(vl2id)
            .add();
        twt.newCurrentLimits1()
            .setPermanentLimit(1030.0)
            .add();
        twt.newCurrentLimits2()
            .setPermanentLimit(1030.0)
            .add();
        twt.getTerminal1().setP(p1).setQ(q1);
        twt.getTerminal2().setP(p2).setQ(q2);
    }

    @Test
    void switchesFlowBusBreaker() {

        Network network = createBusBreaker();

        VoltageLevel voltageLevel11 = network.getVoltageLevel("S1VL1");
        SwitchesFlow switchesFlow11 = new SwitchesFlow(voltageLevel11);

        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-BUS0-BUS1", -25.0, -10.5));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-BUS1-BUS2", -25.0, -10.5));
        assertTrue(compareSwitchFlow(switchesFlow11, "S1VL1-SW-BUS0-BUS2", 0.0, 0.0));

        VoltageLevel voltageLevel12 = network.getVoltageLevel("S1VL2");
        SwitchesFlow switchesFlow12 = new SwitchesFlow(voltageLevel12);

        assertTrue(switchesFlow12.isEmpty());
    }

    private static Network createBusBreaker() {
        return createBusBreaker(NetworkFactory.findDefault());
    }

    private static Network createBusBreaker(NetworkFactory networkFactory) {

        Network network = networkFactory.createNetwork("SwitchesFlow-BusBreaker", "test");
        network.setCaseDate(ZonedDateTime.parse("2017-06-25T17:43:00.000+01:00"));
        network.setForecastDistance(0);

        // First substation
        Substation s1 = network.newSubstation()
                .setId("S1")
                .add();
        VoltageLevel s1vl1 = s1.newVoltageLevel()
                .setId("S1VL1")
                .setNominalV(225.0)
                .setLowVoltageLimit(220.0)
                .setHighVoltageLimit(240.0)
                .setTopologyKind(TopologyKind.BUS_BREAKER)
                .add();

        createBus(s1vl1, "S1VL1-BUS0");
        createBus(s1vl1, "S1VL1-BUS1");
        createBus(s1vl1, "S1VL1-BUS2");

        VoltageLevel s1vl2 = s1.newVoltageLevel()
            .setId("S1VL2")
            .setNominalV(400.0)
            .setLowVoltageLimit(380.0)
            .setHighVoltageLimit(430.0)
            .setTopologyKind(TopologyKind.BUS_BREAKER)
            .add();

        createBus(s1vl2, "S1VL2-BUS0");

        // Switches
        createSwitch(s1vl1, "S1VL1-SW-BUS0-BUS1", false, "S1VL1-BUS0", "S1VL1-BUS1");
        createSwitch(s1vl1, "S1VL1-SW-BUS1-BUS2", false, "S1VL1-BUS1", "S1VL1-BUS2");
        createSwitch(s1vl1, "S1VL1-SW-BUS0-BUS2", false, "S1VL1-BUS0", "S1VL1-BUS2");

        createLoad(s1vl1, "S1VL1-Load", 25.0, 10.5, "S1VL1-BUS0");

        // Other voltage level

        createLoad(s1vl2, "S1VL2-Load", -26.0, -12.5, "S1VL2-BUS0");

        // Inside the substation
        createTwoWindingsTransformer(s1, "S1VL1", "S1VL2", "S1-TWT-BUS2-BUS0", -25.0, -10.5, 26.0, 12.5, "S1VL1-BUS2", "S1VL2-BUS0");

        return network;
    }

    private static void createBus(VoltageLevel voltageLevel, String id) {
        voltageLevel.getBusBreakerView()
            .newBus()
            .setName(id)
            .setId(id)
            .add();
    }

    private static void createSwitch(VoltageLevel voltageLevel, String id, boolean open, String busId1, String busId2) {
        voltageLevel.getBusBreakerView()
            .newSwitch()
            .setId(id)
            .setOpen(open)
            .setBus1(id)
            .setBus1(busId1)
            .setBus2(busId2)
            .add();
    }

    private static void createLoad(VoltageLevel voltageLevel, String id, double p, double q, String busId) {
        Load load = voltageLevel.newLoad()
            .setId(id)
            .setLoadType(LoadType.UNDEFINED)
            .setP0(p)
            .setQ0(q)
            .setBus(busId)
            .add();
        load.getTerminal().setP(p).setQ(q);
    }

    private static void createTwoWindingsTransformer(Substation s, String vl1id, String vl2id, String id,
        double p1, double q1, double p2, double q2, String busId1, String busId2) {
        TwoWindingsTransformer twt = s.newTwoWindingsTransformer()
            .setId(id)
            .setR(2.0)
            .setX(14.)
            .setG(0.0)
            .setB(0.0)
            .setRatedU1(225.0)
            .setRatedU2(400.0)
            .setBus1(busId1)
            .setVoltageLevel1(vl1id)
            .setBus2(busId2)
            .setVoltageLevel2(vl2id)
            .add();
        twt.newCurrentLimits1()
            .setPermanentLimit(1030.0)
            .add();
        twt.newCurrentLimits2()
            .setPermanentLimit(1030.0)
            .add();
        twt.getTerminal1().setP(p1).setQ(q1);
        twt.getTerminal2().setP(p2).setQ(q2);
    }

    private static boolean compareSwitchFlow(SwitchesFlow switchesFlow, String id, double p1, double q1) {
        double tol = 0.000001;
        if (Math.abs(p1 - switchesFlow.getP1(id)) > tol) {
            return false;
        }
        if (Math.abs(q1 - switchesFlow.getQ1(id)) > tol) {
            return false;
        }
        if (Math.abs(-p1 - switchesFlow.getP2(id)) > tol) {
            return false;
        }
        if (Math.abs(-q1 - switchesFlow.getQ2(id)) > tol) {
            return false;
        }
        return true;
    }
}