LfBusImplTest.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.openloadflow.network.impl;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.VoltagePerReactivePowerControlAdder;
import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory;
import com.powsybl.iidm.network.test.FourSubstationsNodeBreakerFactory;
import com.powsybl.openloadflow.graph.NaiveGraphConnectivityFactory;
import com.powsybl.openloadflow.network.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static com.powsybl.openloadflow.util.LoadFlowAssert.DELTA_POWER;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author Fabien Rigaux (https://github.com/frigaux)
*/
class LfBusImplTest {
private Network network;
private LfNetwork lfNetwork;
private Bus bus1;
private Bus bus2;
private StaticVarCompensator svc1;
private StaticVarCompensator svc2;
private StaticVarCompensator svc3;
private Load load;
private Network createNetwork() {
Network network = Network.create("svc", "test");
Substation s1 = network.newSubstation()
.setId("S1")
.setCountry(Country.FR)
.add();
VoltageLevel vl1 = s1.newVoltageLevel()
.setId("vl1")
.setNominalV(400)
.setTopologyKind(TopologyKind.BUS_BREAKER)
.add();
VoltageLevel vl2 = s1.newVoltageLevel()
.setId("vl2")
.setNominalV(400)
.setTopologyKind(TopologyKind.BUS_BREAKER)
.add();
bus1 = vl1.getBusBreakerView().newBus()
.setId("b1")
.add();
bus2 = vl2.getBusBreakerView().newBus()
.setId("b2")
.add();
svc1 = vl1.newStaticVarCompensator()
.setId("svc1")
.setConnectableBus("b1")
.setBus("b1")
.setRegulationMode(StaticVarCompensator.RegulationMode.OFF)
.setBmin(-0.006)
.setBmax(0.006)
.add();
svc1.setVoltageSetpoint(385)
.setRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE)
.newExtension(VoltagePerReactivePowerControlAdder.class)
.withSlope(0.01)
.add();
svc2 = vl1.newStaticVarCompensator()
.setId("svc2")
.setConnectableBus("b1")
.setBus("b1")
.setRegulationMode(StaticVarCompensator.RegulationMode.OFF)
.setBmin(-0.001)
.setBmax(0.001)
.add();
svc2.setVoltageSetpoint(385)
.setRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE)
.newExtension(VoltagePerReactivePowerControlAdder.class)
.withSlope(0.015)
.add();
svc3 = vl1.newStaticVarCompensator()
.setId("svc3")
.setConnectableBus("b1")
.setBus("b1")
.setRegulationMode(StaticVarCompensator.RegulationMode.OFF)
.setBmin(-0.00075)
.setBmax(0.00075)
.add();
svc3.setVoltageSetpoint(385)
.setRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE)
.newExtension(VoltagePerReactivePowerControlAdder.class)
.withSlope(0.02)
.add();
load = vl2.newLoad()
.setId("load")
.setConnectableBus("b2")
.setBus("b2")
.setQ0(100)
.setP0(0)
.add();
network.newLine()
.setId("line")
.setBus1("b1")
.setBus2("b2")
.setR(1)
.setX(1)
.add();
return network;
}
@BeforeEach
void setUp() {
network = createNetwork();
List<LfNetwork> networks = Networks.load(network, new MostMeshedSlackBusSelector());
lfNetwork = networks.get(0);
}
@Test
void updateGeneratorsStateTest() {
List<LfNetwork> networks = Networks.load(EurostagTutorialExample1Factory.create(), new MostMeshedSlackBusSelector());
LfNetwork mainNetwork = networks.get(0);
LfNetworkParameters parameters = new LfNetworkParameters()
.setBreakers(true);
LfBusImpl lfBus = new LfBusImpl(bus1, mainNetwork, 385, 0, parameters, true);
LfNetworkLoadingReport lfNetworkLoadingReport = new LfNetworkLoadingReport();
lfBus.addStaticVarCompensator(svc1, parameters, lfNetworkLoadingReport);
lfBus.addStaticVarCompensator(svc2, parameters, lfNetworkLoadingReport);
lfBus.addStaticVarCompensator(svc3, parameters, lfNetworkLoadingReport);
double generationQ = -6.412103131789854;
lfBus.updateGeneratorsState(generationQ, true, ReactivePowerDispatchMode.Q_EQUAL_PROPORTION);
double sumQ = 0;
for (LfGenerator lfGenerator : lfBus.getGenerators()) {
sumQ += lfGenerator.getCalculatedQ();
}
assertEquals(generationQ, sumQ, DELTA_POWER, "sum of generators calculatedQ should be equals to qToDispatch");
}
private static List<LfGenerator> createLfGeneratorsWithInitQ(List<Double> initQs) {
return createLfGeneratorsWithInitQ(FourSubstationsNodeBreakerFactory.create(), initQs);
}
private static List<LfGenerator> createLfGeneratorsWithInitQ(Network network, List<Double> initQs) {
LfNetwork lfNetwork = new LfNetwork(0, 0, new FirstSlackBusSelector(), 1, new NaiveGraphConnectivityFactory<>(LfBus::getNum), new ReferenceBusFirstSlackSelector());
LfNetworkParameters parameters1 = new LfNetworkParameters()
.setPlausibleActivePowerLimit(100)
.setMinPlausibleTargetVoltage(0.9)
.setMaxPlausibleTargetVoltage(1.1);
LfNetworkLoadingReport lfNetworkLoadingReport = new LfNetworkLoadingReport();
LfGenerator lfGenerator1 = LfGeneratorImpl.create(network.getGenerator("GH1"), lfNetwork, parameters1, lfNetworkLoadingReport);
lfGenerator1.setCalculatedQ(initQs.get(0));
LfNetworkParameters parameters23 = new LfNetworkParameters()
.setPlausibleActivePowerLimit(200)
.setMinPlausibleTargetVoltage(0.9)
.setMaxPlausibleTargetVoltage(1.1);
LfGenerator lfGenerator2 = LfGeneratorImpl.create(network.getGenerator("GH2"), lfNetwork, parameters23, lfNetworkLoadingReport);
lfGenerator2.setCalculatedQ(initQs.get(1));
LfGenerator lfGenerator3 = LfGeneratorImpl.create(network.getGenerator("GH3"), lfNetwork, parameters23, lfNetworkLoadingReport);
lfGenerator3.setCalculatedQ(initQs.get(2));
List<LfGenerator> generators = new ArrayList<>();
generators.add(lfGenerator1);
generators.add(lfGenerator2);
generators.add(lfGenerator3);
return generators;
}
@Test
void dispatchQForMaxTest() {
Network network = FourSubstationsNodeBreakerFactory.create();
// GH1 reactive limits are not plausible => fallback into split Q equally
network.getGenerator("GH1").newMinMaxReactiveLimits().setMinQ(-10000).setMaxQ(10000).add();
List<LfGenerator> generators = createLfGeneratorsWithInitQ(network, List.of(0d, 0d, 0d));
LfGenerator generatorToRemove = generators.get(1);
double qToDispatch = 21;
double residueQ = AbstractLfBus.dispatchQ(generators, true, ReactivePowerDispatchMode.Q_EQUAL_PROPORTION, qToDispatch);
double totalCalculatedQ = generators.get(0).getCalculatedQ() + generators.get(1).getCalculatedQ() + generatorToRemove.getCalculatedQ();
assertEquals(7.0, generators.get(0).getCalculatedQ());
assertEquals(7.0, generators.get(1).getCalculatedQ());
assertEquals(2, generators.size());
assertEquals(qToDispatch - totalCalculatedQ, residueQ, 0.00001);
assertEquals(generatorToRemove.getMaxQ(), generatorToRemove.getCalculatedQ());
}
@Test
void dispatchQTestWithInitialQForMax() {
Network network = FourSubstationsNodeBreakerFactory.create();
// GH1 reactive limits are not plausible => fallback into split Q equally
network.getGenerator("GH1").newMinMaxReactiveLimits().setMinQ(-10000).setMaxQ(10000).add();
List<LfGenerator> generators = createLfGeneratorsWithInitQ(network, List.of(1.5d, 1d, 3d));
double qInitial = generators.get(0).getCalculatedQ() + generators.get(1).getCalculatedQ() + generators.get(2).getCalculatedQ();
LfGenerator generatorToRemove1 = generators.get(1);
LfGenerator generatorToRemove2 = generators.get(2);
double qToDispatch = 20;
double residueQ = AbstractLfBus.dispatchQ(generators, true, ReactivePowerDispatchMode.Q_EQUAL_PROPORTION, qToDispatch);
assertEquals(1, generators.size());
double totalCalculatedQ = generators.get(0).getCalculatedQ() + generatorToRemove1.getCalculatedQ() + generatorToRemove2.getCalculatedQ();
assertEquals(qToDispatch + qInitial - totalCalculatedQ, residueQ, 0.0001);
assertEquals(8.17, generators.get(0).getCalculatedQ(), 0.01);
assertEquals(generatorToRemove1.getMaxQ(), generatorToRemove1.getCalculatedQ(), 0.01);
assertEquals(generatorToRemove2.getMaxQ(), generatorToRemove2.getCalculatedQ(), 0.01);
}
@Test
void dispatchQForMinTest() {
Network network = FourSubstationsNodeBreakerFactory.create();
// GH1 reactive limits are not plausible => fallback into split Q equally
network.getGenerator("GH1").newMinMaxReactiveLimits().setMinQ(-10000).setMaxQ(10000).add();
List<LfGenerator> generators = createLfGeneratorsWithInitQ(network, List.of(0d, 0d, 0d));
LfGenerator generatorToRemove2 = generators.get(1);
LfGenerator generatorToRemove3 = generators.get(2);
double qToDispatch = -21;
double residueQ = AbstractLfBus.dispatchQ(generators, true, ReactivePowerDispatchMode.Q_EQUAL_PROPORTION, qToDispatch);
double totalCalculatedQ = generators.get(0).getCalculatedQ() + generatorToRemove2.getCalculatedQ() + generatorToRemove3.getCalculatedQ();
assertEquals(-7.0, generators.get(0).getCalculatedQ());
assertEquals(1, generators.size());
assertEquals(qToDispatch - totalCalculatedQ, residueQ, 0.00001);
assertEquals(generatorToRemove2.getMinQ(), generatorToRemove2.getCalculatedQ());
assertEquals(generatorToRemove3.getMinQ(), generatorToRemove3.getCalculatedQ());
}
@Test
void dispatchQEmptyListTest() {
List<LfGenerator> generators = new ArrayList<>();
double qToDispatch = -21;
Assertions.assertThrows(IllegalArgumentException.class, () -> AbstractLfBus.dispatchQ(generators, true, ReactivePowerDispatchMode.Q_EQUAL_PROPORTION, qToDispatch),
"the generator list to dispatch Q can not be empty");
}
@Test
void dispatchQwithKequalProportion() {
Network network = FourSubstationsNodeBreakerFactory.create();
List<LfGenerator> generators = createLfGeneratorsWithInitQ(network, List.of(0.3d, 0.1d, 0.4d));
LfGenerator g0 = generators.get(0);
LfGenerator g1 = generators.get(1);
LfGenerator g2 = generators.get(2);
g0.setCalculatedQ(0);
g1.setCalculatedQ(0);
g2.setCalculatedQ(0);
double qToDispatch = 20;
double residueQ = AbstractLfBus.dispatchQ(new ArrayList<>(generators), true, ReactivePowerDispatchMode.K_EQUAL_PROPORTION, qToDispatch);
assertEquals(0, residueQ, 0);
assertEquals(8.537, g0.getCalculatedQ(), 1e-3);
assertEquals(4.985, g1.getCalculatedQ(), 1e-3);
assertEquals(6.477, g2.getCalculatedQ(), 1e-3);
assertEquals(0.91, LfGenerator.qToK(g0, g0.getCalculatedQ()), 1e-3);
assertEquals(0.91, LfGenerator.qToK(g1, g1.getCalculatedQ()), 1e-3);
assertEquals(0.91, LfGenerator.qToK(g2, g2.getCalculatedQ()), 1e-3);
}
@Test
void dispatchQwithKequalProportionWithFallBack() {
Network network = FourSubstationsNodeBreakerFactory.create();
network.getGenerator("GH2").newMinMaxReactiveLimits().setMinQ(-9999).setMaxQ(9999).add();
List<LfGenerator> generators = createLfGeneratorsWithInitQ(network, List.of(0.3d, 0.1d, 0.4d));
LfGenerator g0 = generators.get(0);
LfGenerator g1 = generators.get(1);
LfGenerator g2 = generators.get(2);
g0.setCalculatedQ(0);
g1.setCalculatedQ(0);
g2.setCalculatedQ(0);
double qToDispatch = 20;
double residueQ = AbstractLfBus.dispatchQ(new ArrayList<>(generators), true, ReactivePowerDispatchMode.K_EQUAL_PROPORTION, qToDispatch);
assertEquals(0, residueQ, 0);
assertEquals(6.666, g0.getCalculatedQ(), 1e-3);
assertEquals(6.666, g1.getCalculatedQ(), 1e-3);
assertEquals(6.666, g2.getCalculatedQ(), 1e-3);
assertEquals(0.7, LfGenerator.qToK(g0, g0.getCalculatedQ()), 1e-3);
assertEquals(0.066, LfGenerator.qToK(g1, g1.getCalculatedQ()), 1e-3);
assertEquals(0.937, LfGenerator.qToK(g2, g2.getCalculatedQ()), 1e-3);
}
@Test
void testBusHasCountryAttributeAfterLoading() {
assertTrue(lfNetwork.getBusById("vl1_0").getCountry().isPresent());
assertTrue(lfNetwork.getBusById("vl2_0").getCountry().isPresent());
assertEquals(Country.FR, lfNetwork.getBusById("vl1_0").getCountry().orElseThrow());
assertEquals(Country.FR, lfNetwork.getBusById("vl2_0").getCountry().orElseThrow());
}
}