SwitchPqPvTest.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.ac;

import com.powsybl.commons.report.ReportNode;
import com.powsybl.commons.test.PowsyblCoreTestReportResourceBundle;
import com.powsybl.computation.local.LocalComputationManager;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.VoltagePerReactivePowerControlAdder;
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.OpenLoadFlowParameters;
import com.powsybl.openloadflow.OpenLoadFlowProvider;
import com.powsybl.openloadflow.network.AbstractLoadFlowNetworkFactory;
import com.powsybl.openloadflow.network.SlackBusSelectionMode;
import com.powsybl.openloadflow.util.LoadFlowAssert;
import com.powsybl.openloadflow.util.report.PowsyblOpenLoadFlowReportResourceBundle;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

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

/**
 *    g1     g2    g3
 *    |      |     |
 *    b1     b2    b3
 *    |      |     |
 *    -------b3-----
 *           |
 *           ld
 *
 * @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
 */
class SwitchPqPvTest extends AbstractLoadFlowNetworkFactory {

    private Network network;
    private Bus b1;
    private Bus b2;
    private Bus b3;
    private Bus b4;
    private Generator g1;
    private Generator g2;
    private Generator g3;
    private LoadFlow.Runner loadFlowRunner;
    private LoadFlowParameters parameters;
    private OpenLoadFlowParameters parametersExt;

    @BeforeEach
    void setUp() {
        network = Network.create("switch-pq-pv-test", "code");
        Substation s = network.newSubstation()
                .setId("s")
                .add();
        VoltageLevel vl1 = s.newVoltageLevel()
                .setId("vl1")
                .setNominalV(20)
                .setTopologyKind(TopologyKind.BUS_BREAKER)
                .add();
        b1 = vl1.getBusBreakerView().newBus()
                .setId("b1")
                .add();
        VoltageLevel vl2 = s.newVoltageLevel()
                .setId("vl2")
                .setNominalV(20)
                .setTopologyKind(TopologyKind.BUS_BREAKER)
                .add();
        b2 = vl2.getBusBreakerView().newBus()
                .setId("b2")
                .add();
        VoltageLevel vl3 = s.newVoltageLevel()
                .setId("vl3")
                .setNominalV(20)
                .setTopologyKind(TopologyKind.BUS_BREAKER)
                .add();
        b3 = vl3.getBusBreakerView().newBus()
                .setId("b3")
                .add();
        VoltageLevel vl4 = s.newVoltageLevel()
                .setId("vl4")
                .setNominalV(380)
                .setTopologyKind(TopologyKind.BUS_BREAKER)
                .add();
        b4 = vl4.getBusBreakerView().newBus()
                .setId("b4")
                .add();
        vl4.newLoad()
                .setId("ld")
                .setBus("b4")
                .setConnectableBus("b4")
                .setP0(300)
                .setQ0(200)
                .add();
        g1 = b1.getVoltageLevel()
                .newGenerator()
                .setId("g1")
                .setBus("b1")
                .setConnectableBus("b1")
                .setEnergySource(EnergySource.THERMAL)
                .setMinP(0)
                .setMaxP(200)
                .setTargetP(100)
                .setTargetV(17)
                .setVoltageRegulatorOn(true)
                .add();
        g1.newMinMaxReactiveLimits()
                .setMinQ(-179)
                .setMaxQ(1000)
                .add();
        g2 = b2.getVoltageLevel()
                .newGenerator()
                .setId("g2")
                .setBus("b2")
                .setConnectableBus("b2")
                .setEnergySource(EnergySource.THERMAL)
                .setMinP(0)
                .setMaxP(200)
                .setTargetP(100)
                .setTargetV(21)
                .setVoltageRegulatorOn(true)
                .add();
        g2.newMinMaxReactiveLimits()
                .setMinQ(-1000)
                .setMaxQ(411)
                .add();
        g3 = b3.getVoltageLevel()
                .newGenerator()
                .setId("g3")
                .setBus("b3")
                .setConnectableBus("b3")
                .setEnergySource(EnergySource.THERMAL)
                .setMinP(0)
                .setMaxP(200)
                .setTargetP(100)
                .setTargetV(20)
                .setVoltageRegulatorOn(true)
                .add();
        g3.newMinMaxReactiveLimits()
                .setMinQ(-1000)
                .setMaxQ(30)
                .add();
        s.newTwoWindingsTransformer()
                .setId("tr14")
                .setBus1(b1.getId())
                .setConnectableBus1(b1.getId())
                .setBus2(b4.getId())
                .setConnectableBus2(b4.getId())
                .setRatedU1(20.5)
                .setRatedU2(399)
                .setR(1)
                .setX(100)
                .add();
        s.newTwoWindingsTransformer()
                .setId("tr24")
                .setBus1(b2.getId())
                .setConnectableBus1(b2.getId())
                .setBus2(b4.getId())
                .setConnectableBus2(b4.getId())
                .setRatedU1(20.5)
                .setRatedU2(397)
                .setR(0.5)
                .setX(20)
                .add();
        s.newTwoWindingsTransformer()
                .setId("tr34")
                .setBus1(b3.getId())
                .setConnectableBus1(b3.getId())
                .setBus2(b4.getId())
                .setConnectableBus2(b4.getId())
                .setRatedU1(20.5)
                .setRatedU2(397)
                .setR(0.5)
                .setX(10)
                .add();

        loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory()));
        parameters = new LoadFlowParameters()
                .setDistributedSlack(false);
        parametersExt = OpenLoadFlowParameters.create(parameters)
                .setSlackBusSelectionMode(SlackBusSelectionMode.MOST_MESHED);
    }

    @Test
    void test() {
        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        // bus 1 and 3 switch PQ at first outer loop, then at next outer loop bus 3 go back PV
        assertVoltageEquals(17.032769, b1); // PQ => v != 17
        assertVoltageEquals(21, b2); // PV
        assertVoltageEquals(20, b3); // PV
    }

    @Test
    void testMultipleSwitch() throws IOException {
        g2.newMinMaxReactiveLimits()
                .setMinQ(-179)
                .setMaxQ(700)
                .add();
        g2.setTargetV(22);
        ReportNode reportNode = ReportNode.newRootReportNode()
                .withResourceBundles(PowsyblOpenLoadFlowReportResourceBundle.BASE_NAME, PowsyblCoreTestReportResourceBundle.TEST_BASE_NAME)
                .withMessageTemplate("test")
                .build();
        LoadFlowResult result = loadFlowRunner.run(network, network.getVariantManager().getWorkingVariantId(), LocalComputationManager.getDefault(), parameters, reportNode);
        assertTrue(result.isFullyConverged());
        // bus 1 and 3 switch PQ at first outer loop, then at next outer loop bus 3 go back PV
        assertVoltageEquals(17.441, b1); // PQ => v != 17
        assertVoltageEquals(21.99, b2); // PQ => v != 22
        assertVoltageEquals(20, b3); // PV
        String expected = """
                + test
                   + Load flow on network 'switch-pq-pv-test'
                      + Network CC0 SC0
                         + Network info
                            Network has 4 buses and 3 branches
                            Network balance: active generation=300 MW, active load=300 MW, reactive generation=0 MVar, reactive load=200 MVar
                            Angle reference bus: vl4_0
                            Slack bus: vl4_0
                         Outer loop VoltageMonitoring
                         + Outer loop ReactiveLimits
                            + Outer loop iteration 1
                               + 2 buses switched PV -> PQ (1 buses remain PV)
                                  Switch bus 'vl1_0' PV -> PQ, q=-200.872086 < minQ=-179
                                  Switch bus 'vl2_0' PV -> PQ, q=712.632433 > maxQ=700
                         Outer loop VoltageMonitoring
                         Outer loop ReactiveLimits
                         AC load flow completed successfully (solverStatus=CONVERGED, outerloopStatus=STABLE)
                """;
        LoadFlowAssert.assertReportEqualsString(expected, reportNode);
    }

    @Test
    void testWithSlope() {
        g3.remove();
        double value = b3.getVoltageLevel().getNominalV() * b3.getVoltageLevel().getNominalV();
        StaticVarCompensator svc3 = b3.getVoltageLevel()
                .newStaticVarCompensator()
                .setId("svc3")
                .setBus("b3")
                .setConnectableBus("b3")
                .setVoltageSetpoint(20)
                .setRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE)
                .setBmax(30 / value)
                .setBmin(-1000 / value)
                .add();
        g3 = b3.getVoltageLevel()
                .newGenerator()
                .setId("g3")
                .setBus("b3")
                .setConnectableBus("b3")
                .setEnergySource(EnergySource.THERMAL)
                .setMinP(0)
                .setMaxP(200)
                .setTargetP(100)
                .setTargetQ(0)
                .setVoltageRegulatorOn(false)
                .add();
        g3.newMinMaxReactiveLimits()
                .setMinQ(0)
                .setMaxQ(0)
                .add();

        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        // bus 1 and 3 switch PQ at first outer loop, then at next outer loop bus 3 go back PV
        assertVoltageEquals(17.032769, b1); // PQ => v != 17
        assertVoltageEquals(21, b2); // PV
        assertVoltageEquals(20, b3); // PV

        parametersExt.setVoltagePerReactivePowerControl(true);
        svc3.newExtension(VoltagePerReactivePowerControlAdder.class).withSlope(0.00001).add();
        LoadFlowResult result2 = loadFlowRunner.run(network, parameters);
        assertTrue(result2.isFullyConverged());
        // bus 1 and 3 switch PQ at first outer loop, then at next outer loop bus 3 does not go back PV
        assertVoltageEquals(17.034003, b1); // PQ => v != 17
        assertVoltageEquals(21, b2); // PV
        assertEquals(20.00140, b3.getV(), 10E-3); // remains PQ because of slope
    }
}