NonImpedantBranchTest.java

/**
 * 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/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.openloadflow;

import com.powsybl.computation.local.LocalComputationManager;
import com.powsybl.contingency.BranchContingency;
import com.powsybl.contingency.ContingenciesProvider;
import com.powsybl.contingency.Contingency;
import com.powsybl.contingency.ContingencyContext;
import com.powsybl.iidm.network.Bus;
import com.powsybl.iidm.network.Line;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.TwoWindingsTransformer;
import com.powsybl.loadflow.LoadFlow;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.loadflow.LoadFlowResult;
import com.powsybl.math.matrix.DenseMatrixFactory;
import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory;
import com.powsybl.openloadflow.network.AbstractLoadFlowNetworkFactory;
import com.powsybl.openloadflow.network.SlackBusSelectionMode;
import com.powsybl.openloadflow.network.ZeroImpedanceNetworkFactory;
import com.powsybl.openloadflow.network.impl.OlfBranchResult;
import com.powsybl.openloadflow.sa.OpenSecurityAnalysisParameters;
import com.powsybl.openloadflow.sa.OpenSecurityAnalysisProvider;
import com.powsybl.security.*;
import com.powsybl.security.monitor.StateMonitor;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.List;
import java.util.Set;

import static com.powsybl.openloadflow.util.LoadFlowAssert.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
 */
class NonImpedantBranchTest extends AbstractLoadFlowNetworkFactory {

    private LoadFlow.Runner loadFlowRunner;

    private LoadFlowParameters parameters;

    private OpenLoadFlowParameters parametersExt;

    @BeforeEach
    void setUp() {
        loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory()));
        parameters = new LoadFlowParameters().setWriteSlackBus(false);
        parametersExt = OpenLoadFlowParameters.create(parameters);
    }

    @Test
    void threeBusesTest() {
        Network network = Network.create("ThreeBusesWithNonImpedantLine", "code");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        Bus b3 = createBus(network, "b3");
        createGenerator(b1, "g1", 2, 1);
        createLoad(b3, "l1", 1.99, 1);
        createLine(network, b1, b2, "l12", 0.1);
        Line l23 = createLine(network, b2, b3, "l23", 0); // non impedant branch

        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertVoltageEquals(1, b1);
        assertVoltageEquals(0.858, b2);
        assertVoltageEquals(0.858, b3);
        assertAngleEquals(13.36967, b1);
        assertAngleEquals(0, b2);
        assertAngleEquals(0, b3);

        assertActivePowerEquals(1.99, l23.getTerminal1());
        assertActivePowerEquals(-1.99, l23.getTerminal2());
        assertReactivePowerEquals(1, l23.getTerminal1());
        assertReactivePowerEquals(-1, l23.getTerminal2());

        // use low impedance cut strategy (state is changed a little bit)
        parametersExt.setLowImpedanceBranchMode(OpenLoadFlowParameters.LowImpedanceBranchMode.REPLACE_BY_MIN_IMPEDANCE_LINE);
        result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertVoltageEquals(1, b1);
        assertVoltageEquals(0.856, b2);
        assertVoltageEquals(0.856, b3);
        assertAngleEquals(13.520904, b1);
        assertAngleEquals(0, b2);
        assertAngleEquals(0, b3);

        // test in DC mode
        parameters.setDc(true);
        parametersExt.setSlackBusSelectionMode(SlackBusSelectionMode.FIRST);
        result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertTrue(Double.isNaN(b1.getV()));
        assertTrue(Double.isNaN(b2.getV()));
        assertTrue(Double.isNaN(b3.getV()));
        assertAngleEquals(0, b1);
        assertAngleEquals(-11.40186, b2);
        assertAngleEquals(-11.40186, b3);
    }

    @Test
    void fourBusesTest() {
        Network network = Network.create("FourBusesWithNonImpedantLine", "code");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        Bus b3 = createBus(network, "b3");
        Bus b4 = createBus(network, "b4");
        createGenerator(b1, "g1", 2, 1);
        createLoad(b4, "l1", 1.99, 1);
        createLine(network, b1, b2, "l12", 0.05);
        createLine(network, b2, b3, "l23", 0); // non impedant branch
        createLine(network, b3, b4, "l34", 0.05);

        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertVoltageEquals(1, b1);
        assertVoltageEquals(0.921, b2);
        assertVoltageEquals(0.921, b3);
        assertVoltageEquals(0.855, b4);
        assertAngleEquals(6.2301, b1);
        assertAngleEquals(0, b2);
        assertAngleEquals(0, b3);
        assertAngleEquals(-7.248787, b4);

        parameters.setDc(true);
        parametersExt.setSlackBusSelectionMode(SlackBusSelectionMode.FIRST);
        result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertTrue(Double.isNaN(b1.getV()));
        assertTrue(Double.isNaN(b2.getV()));
        assertTrue(Double.isNaN(b3.getV()));
        assertTrue(Double.isNaN(b4.getV()));
        assertAngleEquals(0, b1);
        assertAngleEquals(-5.70093, b2);
        assertAngleEquals(-5.70093, b3);
        assertAngleEquals(-11.40186, b4);
    }

    @Test
    void threeBusesAndNonImpTransfoTest() {
        Network network = Network.create("ThreeBusesWithNonImpedantTransfo", "code");
        Bus b1 = createBus(network, "s", "b1");
        Bus b2 = createBus(network, "s", "b2");
        Bus b3 = createBus(network, "s", "b3");
        createGenerator(b1, "g1", 2, 1);
        createLoad(b3, "l1", 1.99, 1);
        createLine(network, b1, b2, "l12", 0.1);
        TwoWindingsTransformer l23 = createTransformer(network, "s", b2, b3, "l23", 0, 1.1); // non impedant branch

        // TODO: low impedance transformer flow calculation not yet supported
        assertTrue(Double.isNaN(l23.getTerminal1().getP()));
        assertTrue(Double.isNaN(l23.getTerminal2().getP()));
        assertTrue(Double.isNaN(l23.getTerminal1().getQ()));
        assertTrue(Double.isNaN(l23.getTerminal2().getQ()));

        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertVoltageEquals(1, b1);
        assertVoltageEquals(0.858, b2);
        assertVoltageEquals(0.944, b3);
        assertAngleEquals(13.36967, b1);
        assertAngleEquals(0, b2);
        assertAngleEquals(0, b3);
    }

    /**
     *  g1
     *  |
     *  b1 === b2 --- b3 --- b4
     *         |             |
     *         g2            l1
     */
    @Test
    void inconsistentTargetVoltagesTest() {
        Network network = Network.create("FourBusesWithNonImpedantLineAndInconsistentTargetVoltages", "code");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        Bus b3 = createBus(network, "b3");
        Bus b4 = createBus(network, "b4");
        createGenerator(b1, "g1", 1, 1);
        createGenerator(b2, "g2", 1, 1.01);
        createLoad(b4, "l1", 1.99, 1);
        createLine(network, b1, b2, "l12", 0); // non impedant branch
        createLine(network, b2, b3, "l23", 0.05);
        createLine(network, b3, b4, "l34", 0.05);

        loadFlowRunner.run(network, parameters);

        assertEquals(1.01, b1.getV(), DELTA_V);
        assertEquals(1.01, b2.getV(), DELTA_V);
    }

    @Test
    void parallelNonImpedantBranchTest() {
        Network network = Network.create("ParallelNonImpedantBranch", "code");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        Bus b3 = createBus(network, "b3");
        createGenerator(b1, "g1", 2, 1);
        createLoad(b3, "l1", 1.99, 1);
        createLine(network, b1, b2, "l12", 0.1);
        createLine(network, b2, b3, "l23", 0); // non impedant branch
        createLine(network, b2, b3, "l23bis", 0); // non impedant branch

        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
    }

    @Test
    void parallelNonImpedantAndImpedantBranchTest() {
        Network network = Network.create("ParallelNonImpedantAndImpedantBranch", "code");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        Bus b3 = createBus(network, "b3");
        createGenerator(b1, "g1", 2, 1);
        createLoad(b3, "l1", 1.99, 1);
        createLine(network, b1, b2, "l12", 0.1);
        createLine(network, b2, b3, "l23", 0.1);
        createLine(network, b2, b3, "l23bis", 0); // non impedant branch

        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
    }

    @Test
    void loopNonImpedantBranchTest() {
        Network network = Network.create("LoopNonImpedantBranch", "code");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        Bus b3 = createBus(network, "b3");
        createGenerator(b1, "g1", 2, 1);
        createLoad(b3, "l1", 1.99, 1);
        createLine(network, b1, b2, "l12", 0); // non impedant branch
        createLine(network, b2, b3, "l23", 0); // non impedant branch
        createLine(network, b1, b3, "l13", 0); // non impedant branch

        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());

        // also test that it works in DC mode
        parameters.setDc(true);
        result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
    }

    @Test
    void twoLinkedPVBusesTest() {
        Network network = Network.create("TwoPVBusesWithNonImpLine", "code");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        createGenerator(b1, "g1", 2, 1);
        createGenerator(b2, "g2", 2, 1);
        Line l12 = createLine(network, b1, b2, "l12", 0);

        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertVoltageEquals(1, b1);
        assertVoltageEquals(1, b2);
        assertAngleEquals(0, b1);
        assertAngleEquals(0, b2);
        assertActivePowerEquals(0, l12.getTerminal1());
        assertActivePowerEquals(0, l12.getTerminal2());
        assertReactivePowerEquals(0, l12.getTerminal1());
        assertReactivePowerEquals(0, l12.getTerminal2());
    }

    /**
     *
     * g1 (2MW)    g3 (2 MW)
     * |           |
     * b1 -- b2 -- b3
     *       |
     *       l2 (4 MW, 2 MVar)
     */
    @Test
    void nonImpedantNetworkWithTwoPVBusesTest() {
        Network network = Network.create("TwoPVBusesInNonImpNet", "code");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        Bus b3 = createBus(network, "b3");
        createGenerator(b1, "g1", 2, 1);
        createGenerator(b3, "g3", 2, 1);
        createLoad(b2, "l2", 4, 2);
        Line l12 = createLine(network, b1, b2, "l12", 0);
        Line l23 = createLine(network, b2, b3, "l23", 0);

        LoadFlowResult result = loadFlowRunner.run(network, parameters);

        assertTrue(result.isFullyConverged());
        assertVoltageEquals(1, b1);
        assertVoltageEquals(1, b2);
        assertVoltageEquals(1, b3);
        assertAngleEquals(0, b1);
        assertAngleEquals(0, b2);
        assertAngleEquals(0, b3);
        assertActivePowerEquals(2, l12.getTerminal1());
        assertActivePowerEquals(-2, l12.getTerminal2());
        assertActivePowerEquals(-2, l23.getTerminal1());
        assertActivePowerEquals(2, l23.getTerminal2());
        assertReactivePowerEquals(1, l12.getTerminal1());
        assertReactivePowerEquals(-1, l12.getTerminal2());
        assertReactivePowerEquals(-1, l23.getTerminal1());
        assertReactivePowerEquals(1, l23.getTerminal2());
    }

    @Test
    void nonImpedantNetworkWithCycleTest() {
        Network network = Network.create("ThreeBusesNetworkWithCycle", "code");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        Bus b3 = createBus(network, "b3");
        createGenerator(b1, "g1", 2, 1);
        createGenerator(b3, "g3", 2, 1);
        createLoad(b2, "l2", 4, 2);
        createLine(network, b1, b2, "l12", 0);
        createLine(network, b2, b3, "l23", 0);
        createLine(network, b3, b1, "l31", 0);

        LoadFlowResult result = loadFlowRunner.run(network, parameters);

        assertTrue(result.isFullyConverged());
        assertVoltageEquals(1, b1);
        assertVoltageEquals(1, b2);
        assertVoltageEquals(1, b3);
        assertAngleEquals(0, b1);
        assertAngleEquals(0, b2);
        assertAngleEquals(0, b3);
    }

    @Test
    void securityAnalysisTest() {
        Network network = Network.create("test", "code");
        Bus b0 = createBus(network, "b0");
        Bus b1 = createBus(network, "b1");
        Bus b2 = createBus(network, "b2");
        Bus b3 = createBus(network, "b3");
        Bus b5 = createBus(network, "b5");
        Bus b7 = createBus(network, "b7");
        Bus b9 = createBus(network, "b9");
        createGenerator(b1, "g1", 2);
        createGenerator(b9, "g9", 1);
        createLoad(b2, "d2", 1);
        createLoad(b7, "d7", 4);
        createLine(network, b0, b1, "l01", 0.0);
        createLine(network, b0, b2, "l02", 0.0);
        createLine(network, b1, b3, "l13", 0.0);
        createLine(network, b2, b3, "l23", 0.0);
        createLine(network, b2, b5, "l25", 0.0);
        createLine(network, b5, b9, "l59", 0.1);
        createLine(network, b7, b9, "l79", 0.1);

        List<Contingency> contingencies = List.of(new Contingency("contingency1", List.of(new BranchContingency("l01"), new BranchContingency("l02"))),
                                                  new Contingency("contingency2", List.of(new BranchContingency("l01"), new BranchContingency("l13"))));

        ContingenciesProvider provider = n -> contingencies;
        SecurityAnalysisProvider securityAnalysisProvider = new OpenSecurityAnalysisProvider(new DenseMatrixFactory(), new EvenShiloachGraphDecrementalConnectivityFactory<>());
        SecurityAnalysisRunParameters runParameters = new SecurityAnalysisRunParameters()
                .setFilter(new LimitViolationFilter())
                .setComputationManager(LocalComputationManager.getDefault())
                .setSecurityAnalysisParameters(new SecurityAnalysisParameters());
        SecurityAnalysisReport report = securityAnalysisProvider.run(network, network.getVariantManager().getWorkingVariantId(), provider, runParameters).join();
        assertEquals(PostContingencyComputationStatus.CONVERGED, report.getResult().getPostContingencyResults().get(0).getStatus());
        assertEquals(PostContingencyComputationStatus.CONVERGED, report.getResult().getPostContingencyResults().get(1).getStatus());
    }

    /**
     *
     * g0 (regulate b1)                     g4
     * |                                    | t34 (regulate b3)
     * b0 ----- b1 ===== b2 ===== b3 --OO-- b4
     *          |                 |
     *           ------- b5 ------
     *                   |
     *                   ld5
     */
    @Test
    void securityAnalysisNotSameNumberOfVariablesAndEquationsIssueTest() {
        Network network = ZeroImpedanceNetworkFactory.createWithVoltageControl();
        List<Contingency> contingencies = List.of(new Contingency("contingency", List.of(new BranchContingency("l01"))));
        LoadFlowParameters loadFlowParameters = new LoadFlowParameters()
                .setDistributedSlack(false)
                .setTransformerVoltageControlOn(true);
        OpenLoadFlowParameters.create(loadFlowParameters);
        SecurityAnalysisParameters securityAnalysisParameters = new SecurityAnalysisParameters()
                .setLoadFlowParameters(loadFlowParameters);
        OpenSecurityAnalysisParameters openSecurityAnalysisParameters = new OpenSecurityAnalysisParameters()
                .setCreateResultExtension(true);
        securityAnalysisParameters.addExtension(OpenSecurityAnalysisParameters.class, openSecurityAnalysisParameters);
        SecurityAnalysisProvider provider = new OpenSecurityAnalysisProvider(new DenseMatrixFactory(), new EvenShiloachGraphDecrementalConnectivityFactory<>());
        List<StateMonitor> monitors = List.of(new StateMonitor(ContingencyContext.all(),
                Set.of("tr34"),
                Set.of("b0_vl", "b1_vl", "b2_vl", "b3_vl", "b4_vl", "b5_vl"),
                Collections.emptySet()));
        SecurityAnalysisRunParameters runParameters = new SecurityAnalysisRunParameters()
                .setFilter(new LimitViolationFilter())
                .setComputationManager(LocalComputationManager.getDefault())
                .setSecurityAnalysisParameters(securityAnalysisParameters)
                .setMonitors(monitors);
        SecurityAnalysisResult result = provider.run(network,
                        network.getVariantManager().getWorkingVariantId(),
                        n -> contingencies,
                        runParameters)
                .join()
                .getResult();
        assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getPreContingencyResult().getStatus());
        assertEquals(1d, result.getPreContingencyResult().getNetworkResult().getBusResult("b1").getV(), 1e-6); // g0 is controlling voltage of b1
        assertEquals(PostContingencyComputationStatus.CONVERGED, result.getPostContingencyResults().get(0).getStatus());
        assertEquals(1.131391d, result.getPostContingencyResults().get(0).getNetworkResult().getBusResult("b3").getV(), 1e-6); // tr34 is controlling voltage of b1 at tap 0 (ratio 0.9)
        assertEquals(0.918304d, result.getPostContingencyResults().get(0).getNetworkResult().getBranchResult("tr34").getExtension(OlfBranchResult.class).getContinuousR1(), 1e-6);
    }
}