AcloadFlowReactiveLimitsTest.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.iidm.network.*;
import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory;
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.*;
import com.powsybl.openloadflow.network.impl.Networks;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.List;

import static com.powsybl.openloadflow.util.LoadFlowAssert.assertActivePowerEquals;
import static com.powsybl.openloadflow.util.LoadFlowAssert.assertReactivePowerEquals;
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 AcloadFlowReactiveLimitsTest {

    private Network network;
    private VoltageLevel vlgen;
    private VoltageLevel vlgen2;
    private Generator gen;
    private Generator gen2;
    private Load load;
    private TwoWindingsTransformer nhv2Nload;
    private TwoWindingsTransformer ngen2Nhv1;

    private LoadFlow.Runner loadFlowRunner;
    private LoadFlowParameters parameters;
    private OpenLoadFlowParameters parametersExt;

    private void createNetwork() {
        network = EurostagFactory.fix(EurostagTutorialExample1Factory.create());

        // access to already created equipments
        load = network.getLoad("LOAD");
        vlgen = network.getVoltageLevel("VLGEN");
        nhv2Nload = network.getTwoWindingsTransformer("NHV2_NLOAD");
        gen = network.getGenerator("GEN");
        Substation p1 = network.getSubstation("P1");

        // reduce GEN reactive range
        gen.newMinMaxReactiveLimits()
                .setMinQ(0)
                .setMaxQ(280)
                .add();

        // create a new generator GEN2
        vlgen2 = p1.newVoltageLevel()
                .setId("VLGEN2")
                .setNominalV(24.0)
                .setTopologyKind(TopologyKind.BUS_BREAKER)
                .add();
        vlgen2.getBusBreakerView().newBus()
                .setId("NGEN2")
                .add();
        gen2 = vlgen2.newGenerator()
                .setId("GEN2")
                .setBus("NGEN2")
                .setConnectableBus("NGEN2")
                .setMinP(-9999.99)
                .setMaxP(9999.99)
                .setVoltageRegulatorOn(true)
                .setTargetV(24.5)
                .setTargetP(100)
                .add();
        gen2.newMinMaxReactiveLimits()
                .setMinQ(0)
                .setMaxQ(100)
                .add();
        int zb380 = 380 * 380 / 100;
        ngen2Nhv1 = p1.newTwoWindingsTransformer()
                .setId("NGEN2_NHV1")
                .setBus1("NGEN2")
                .setConnectableBus1("NGEN2")
                .setRatedU1(24.0)
                .setBus2("NHV1")
                .setConnectableBus2("NHV1")
                .setRatedU2(400.0)
                .setR(0.24 / 1800 * zb380)
                .setX(Math.sqrt(10 * 10 - 0.24 * 0.24) / 1800 * zb380)
                .add();

        // fix active power balance
        load.setP0(699.838);
    }

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

    @Test
    void diagramTest() {
        List<LfNetwork> lfNetworks = Networks.load(network, new FirstSlackBusSelector());
        assertEquals(1, lfNetworks.size());
        LfNetwork lfNetwork = lfNetworks.get(0);
        LfBus genBus = lfNetwork.getBus(0);
        assertEquals("VLGEN_0", genBus.getId());
        LfGenerator gen = genBus.getGenerators().get(0);
        assertEquals(0, gen.getMinQ(), 0);
        assertEquals(2.8, gen.getMaxQ(), 0);
    }

    @Test
    void test() {
        parameters.setUseReactiveLimits(false);
        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertReactivePowerEquals(-109.228, gen.getTerminal());
        assertReactivePowerEquals(-152.265, gen2.getTerminal());
        assertReactivePowerEquals(-199.998, nhv2Nload.getTerminal2());

        parameters.setUseReactiveLimits(true);
        result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertReactivePowerEquals(-164.315, gen.getTerminal());
        assertReactivePowerEquals(-100, gen2.getTerminal()); // GEN is correctly limited to 100 MVar
        assertReactivePowerEquals(100, ngen2Nhv1.getTerminal1());
        assertReactivePowerEquals(-200, nhv2Nload.getTerminal2());
    }

    @Test
    void testWithMixedGenLoad() {
        // add a 20 MVar to LOAD2 connected to same bus as GEN2 and allow 20 MVar additional max reactive power
        // to GEN2 => result should be the same
        vlgen2.newLoad()
                .setId("LOAD2")
                .setConnectableBus("NGEN2")
                .setBus("NGEN2")
                .setP0(0)
                .setQ0(20)
                .add();
        gen2.newMinMaxReactiveLimits()
                .setMinQ(0)
                .setMaxQ(120)
                .add();

        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertReactivePowerEquals(-164.315, gen.getTerminal());
        assertReactivePowerEquals(-120, gen2.getTerminal());
        assertReactivePowerEquals(100, ngen2Nhv1.getTerminal1());
    }

    @Test
    void testZeroReactiveRangeAndReactiveLimitsDisabled() {
        parameters.setUseReactiveLimits(false);

        gen2.newMinMaxReactiveLimits()
                .setMinQ(0)
                .setMaxQ(0)
                .add();
        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertReactivePowerEquals(-109.228, gen.getTerminal());
        assertReactivePowerEquals(-152.265, gen2.getTerminal());
        assertReactivePowerEquals(-199.998, nhv2Nload.getTerminal2());
    }

    @ParameterizedTest
    @ValueSource(booleans = {false, true})
    void testReactiveLimitsExtrapolation(boolean extrapolate) {
        parametersExt.setExtrapolateReactiveLimits(extrapolate);

        gen2.newReactiveCapabilityCurve() // gen2.targetP = 100 => when extrapolated, minQ = -200 and maxQ = 100
                .beginPoint().setP(200).setMinQ(-300).setMaxQ(150).endPoint()
                .beginPoint().setP(300).setMinQ(-400).setMaxQ(200).endPoint()
                .add();
        LoadFlowResult result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertActivePowerEquals(-100.0, gen2.getTerminal());
        assertReactivePowerEquals(extrapolate ? -100.0 : -150, gen2.getTerminal()); // reaching gen2 maxQ

        gen2.newReactiveCapabilityCurve() // gen2.targetP = 100 => when extrapolated, minQ = 200 and maxQ = 600
                .beginPoint().setP(200).setMinQ(300).setMaxQ(700).endPoint()
                .beginPoint().setP(300).setMinQ(400).setMaxQ(800).endPoint()
                .add();
        result = loadFlowRunner.run(network, parameters);
        assertTrue(result.isFullyConverged());
        assertActivePowerEquals(-100.0, gen2.getTerminal());
        assertReactivePowerEquals(extrapolate ? -200.0 : -300, gen2.getTerminal()); // reaching gen2 minQ
    }
}