DistributedSlackOnLoadTest.java
/**
* Copyright (c) 2020, 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.computation.local.LocalComputationManager;
import com.powsybl.iidm.network.Load;
import com.powsybl.iidm.network.LoadType;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.Terminal;
import com.powsybl.iidm.network.extensions.LoadDetailAdder;
import com.powsybl.iidm.network.extensions.ReferencePriority;
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.ac.solver.NewtonRaphsonStoppingCriteriaType;
import com.powsybl.openloadflow.network.DistributedSlackNetworkFactory;
import com.powsybl.openloadflow.network.EurostagFactory;
import com.powsybl.openloadflow.network.ReferenceBusSelectionMode;
import com.powsybl.openloadflow.network.SlackBusSelectionMode;
import com.powsybl.openloadflow.util.LoadFlowResultBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CompletionException;
import static com.powsybl.openloadflow.util.LoadFlowAssert.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Anne Tilloy {@literal <anne.tilloy at rte-france.com>}
*/
class DistributedSlackOnLoadTest {
private Network network;
private Load l1;
private Load l2;
private Load l3;
private Load l4;
private Load l5;
private Load l6;
private LoadFlow.Runner loadFlowRunner;
private LoadFlowParameters parameters;
private OpenLoadFlowParameters parametersExt;
public static final double DELTA_MISMATCH = 1E-4d;
@BeforeEach
void setUp() {
network = DistributedSlackNetworkFactory.createNetworkWithLoads();
l1 = network.getLoad("l1");
l2 = network.getLoad("l2");
l3 = network.getLoad("l3");
l4 = network.getLoad("l4");
l5 = network.getLoad("l5");
l6 = network.getLoad("l6");
loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(new DenseMatrixFactory()));
parameters = new LoadFlowParameters().setDistributedSlack(true)
.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD);
parametersExt = OpenLoadFlowParameters.create(parameters)
.setSlackBusSelectionMode(SlackBusSelectionMode.MOST_MESHED);
}
@Test
void test() {
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertActivePowerEquals(35.294, l1.getTerminal());
assertActivePowerEquals(70.588, l2.getTerminal());
assertActivePowerEquals(58.824, l3.getTerminal());
assertActivePowerEquals(164.705, l4.getTerminal());
assertActivePowerEquals(11.765, l5.getTerminal());
assertActivePowerEquals(-41.176, l6.getTerminal());
LoadFlowResult loadFlowResultExpected = new LoadFlowResultBuilder(true)
.addMetrics("3", "CONVERGED")
.addComponentResult(0, 0, LoadFlowResult.ComponentResult.Status.CONVERGED, 3, "b4_vl_0", 1.6895598253796607E-7)
.build();
assertLoadFlowResultsEquals(loadFlowResultExpected, result);
}
@Test
void testWithLoadDetail() {
l2.newExtension(LoadDetailAdder.class)
.withVariableActivePower(40)
.withFixedActivePower(20)
.add();
l6.newExtension(LoadDetailAdder.class)
.withVariableActivePower(-25)
.withFixedActivePower(-25)
.add();
parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD);
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertActivePowerEquals(30.0, l1.getTerminal());
assertActivePowerEquals(96.923, l2.getTerminal());
assertActivePowerEquals(50.0, l3.getTerminal());
assertActivePowerEquals(140, l4.getTerminal());
assertActivePowerEquals(10.0, l5.getTerminal());
assertActivePowerEquals(-26.923, l6.getTerminal());
LoadFlowResult loadFlowResultExpected = new LoadFlowResultBuilder(true)
.addMetrics("3", "CONVERGED")
.addComponentResult(0, 0, LoadFlowResult.ComponentResult.Status.CONVERGED, 3, "b4_vl_0", 9.726437433243973E-8)
.build();
assertLoadFlowResultsEquals(loadFlowResultExpected, result);
}
private void assertPowerFactor(Network network) {
switch (parameters.getBalanceType()) {
case PROPORTIONAL_TO_CONFORM_LOAD, PROPORTIONAL_TO_LOAD:
for (Load load : network.getLoads()) {
assertEquals(load.getP0() / load.getQ0(),
load.getTerminal().getP() / load.getTerminal().getQ(),
DELTA_MISMATCH, "Power factor should be a constant value for load " + load.getId());
}
break;
default:
break;
}
}
@Test
void testPowerFactorConstant() {
// PROPORTIONAL_TO_LOAD and power factor constant for loads
parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD);
parametersExt.setLoadPowerFactorConstant(true);
Network network1 = EurostagFactory.fix(EurostagTutorialExample1Factory.create());
LoadFlowResult loadFlowResult1 = loadFlowRunner.run(network1, parameters);
assertPowerFactor(network1);
LoadFlowResult loadFlowResultExpected1 = new LoadFlowResultBuilder(true)
.addMetrics("4", "CONVERGED")
.addComponentResult(0, 0, LoadFlowResult.ComponentResult.Status.CONVERGED, 4, "VLHV1_0", 0.026900149770181514)
.build();
assertLoadFlowResultsEquals(loadFlowResultExpected1, loadFlowResult1);
// PROPORTIONAL_TO_CONFORM_LOAD and power factor constant for loads
parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD);
parametersExt.setLoadPowerFactorConstant(true);
Network network2 = EurostagFactory.fix(EurostagTutorialExample1Factory.create());
// fixedActivePower and FixedReactivePower are unbalanced
network2.getLoad("LOAD").newExtension(LoadDetailAdder.class)
.withFixedActivePower(500).withVariableActivePower(100)
.withFixedReactivePower(150).withVariableReactivePower(50)
.add();
//when
LoadFlowResult loadFlowResult2 = loadFlowRunner.run(network2, parameters);
// then
assertPowerFactor(network2);
LoadFlowResult loadFlowResultExpected2 = new LoadFlowResultBuilder(true).addMetrics("4", "CONVERGED")
.addComponentResult(0, 0, LoadFlowResult.ComponentResult.Status.CONVERGED, 4, "VLHV1_0", 0.026900149770181514)
.build();
assertLoadFlowResultsEquals(loadFlowResultExpected2, loadFlowResult2);
assertActivePowerEquals(601.440, network1.getLoad("LOAD").getTerminal());
// PROPORTIONAL_TO_CONFORM_LOAD and power factor constant for loads
parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD);
parametersExt.setLoadPowerFactorConstant(true);
Network network3 = EurostagFactory.fix(EurostagTutorialExample1Factory.create());
network3.getVoltageLevel("VLLOAD").newLoad().setId("LOAD1").setP0(-10).setQ0(1).setBus("NLOAD").setConnectableBus("NLOAD").add();
//when
LoadFlowResult loadFlowResult3 = loadFlowRunner.run(network3, parameters);
// then
assertPowerFactor(network3);
LoadFlowResult loadFlowResultExpected3 = new LoadFlowResultBuilder(true).addMetrics("5", "CONVERGED")
.addComponentResult(0, 0, LoadFlowResult.ComponentResult.Status.CONVERGED, 5, "VLHV1_0", 0.2263232679029059)
.build();
assertLoadFlowResultsEquals(loadFlowResultExpected3, loadFlowResult3);
assertActivePowerEquals(611.405, network3.getLoad("LOAD").getTerminal());
assertActivePowerEquals(-9.809, network3.getLoad("LOAD1").getTerminal());
}
@Test
void testNetworkWithoutConformingLoad() {
parameters
.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD);
parametersExt
.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.LEAVE_ON_SLACK_BUS);
ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("test").build();
LoadFlowResult result = loadFlowRunner.run(network, network.getVariantManager().getWorkingVariantId(), LocalComputationManager.getDefault(), parameters, reportNode);
LoadFlowResult.ComponentResult componentResult = result.getComponentResults().get(0);
assertTrue(result.isFullyConverged());
assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, componentResult.getStatus());
assertEquals(-60., componentResult.getSlackBusResults().get(0).getActivePowerMismatch(), 1e-6);
assertReportContains("Failed to distribute slack bus active power mismatch, [-+]?\\d*\\.\\d* MW remains", reportNode);
parametersExt
.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.FAIL);
reportNode = ReportNode.newRootReportNode().withMessageTemplate("test").build();
result = loadFlowRunner.run(network, network.getVariantManager().getWorkingVariantId(), LocalComputationManager.getDefault(), parameters, reportNode);
componentResult = result.getComponentResults().get(0);
assertFalse(result.isFullyConverged());
assertEquals(LoadFlowResult.ComponentResult.Status.FAILED, componentResult.getStatus());
assertEquals(-60., componentResult.getSlackBusResults().get(0).getActivePowerMismatch(), 1e-6);
assertReportContains("Failed to distribute slack bus active power mismatch, [-+]?\\d*\\.\\d* MW remains", reportNode);
parametersExt
.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.THROW);
assertThrows(CompletionException.class, () -> loadFlowRunner.run(network, parameters));
parametersExt
.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.DISTRIBUTE_ON_REFERENCE_GENERATOR)
.setReferenceBusSelectionMode(ReferenceBusSelectionMode.GENERATOR_REFERENCE_PRIORITY);
ReferencePriority.set(network.getGenerator("g1"), 1);
reportNode = ReportNode.newRootReportNode().withMessageTemplate("test").build();
result = loadFlowRunner.run(network, network.getVariantManager().getWorkingVariantId(), LocalComputationManager.getDefault(), parameters, reportNode);
componentResult = result.getComponentResults().get(0);
assertTrue(result.isFullyConverged());
assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, componentResult.getStatus());
assertEquals(-60., componentResult.getDistributedActivePower(), 1e-6);
assertActivePowerEquals(-40., network.getGenerator("g1").getTerminal());
assertReportContains("Slack bus active power \\([-+]?\\d*\\.\\d* MW\\) distributed in 1 distribution iteration\\(s\\)", reportNode);
}
@Test
void testNetworkWithPqPvTypeSwitch() {
// network has no conforming load, everything goes to reference generator
network = DistributedSlackNetworkFactory.createWithLossesAndPvPqTypeSwitch();
parameters
.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD);
parametersExt
.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.DISTRIBUTE_ON_REFERENCE_GENERATOR)
.setReferenceBusSelectionMode(ReferenceBusSelectionMode.GENERATOR_REFERENCE_PRIORITY);
ReferencePriority.set(network.getGenerator("g1"), 1);
var result = loadFlowRunner.run(network, parameters);
var componentResult = result.getComponentResults().get(0);
assertTrue(result.isFullyConverged());
assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, componentResult.getStatus());
assertEquals(120.193, componentResult.getDistributedActivePower(), 1e-3);
assertActivePowerEquals(-220.193, network.getGenerator("g1").getTerminal());
}
@Test
void testPowerFactorConstant2() {
network = DistributedSlackNetworkFactory.createNetworkWithLoads2();
parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD);
parametersExt.setLoadPowerFactorConstant(true).setNewtonRaphsonConvEpsPerEq(1e-6);
network.getLoad("l4").newExtension(LoadDetailAdder.class)
.withVariableActivePower(100)
.withFixedActivePower(0)
.add();
network.getLoad("l5").newExtension(LoadDetailAdder.class)
.withVariableActivePower(200)
.withFixedActivePower(100)
.add();
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertBusBalance(network, "b4", 10E-6, 10E-6);
assertPowerFactor(network);
}
@Test
void testPowerFactorConstant3() {
network = DistributedSlackNetworkFactory.createNetworkWithLoads2();
parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD);
parametersExt.setLoadPowerFactorConstant(true);
network.getGenerator("g1").setTargetP(-208);
l4 = network.getLoad("l4");
l4.setP0(0.0).setQ0(-50);
l4.newExtension(LoadDetailAdder.class)
.withFixedActivePower(0)
.withFixedReactivePower(0)
.withVariableActivePower(0)
.withVariableReactivePower(-50)
.add();
l5 = network.getLoad("l5");
l5.setP0(-10.0).setQ0(0.0);
l5.newExtension(LoadDetailAdder.class)
.withFixedActivePower(-10)
.withFixedReactivePower(0)
.withVariableActivePower(0)
.withVariableReactivePower(0.0)
.add();
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertBusBalance(network, "b4", 10E-3, 10E-3);
assertPowerFactor(network);
}
@Test
void testPowerFactorConstant4() {
network = DistributedSlackNetworkFactory.createNetworkWithLoads2();
parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_LOAD);
parametersExt.setLoadPowerFactorConstant(true)
.setNewtonRaphsonStoppingCriteriaType(NewtonRaphsonStoppingCriteriaType.PER_EQUATION_TYPE_CRITERIA)
.setMaxActivePowerMismatch(1e-2)
.setMaxReactivePowerMismatch(1e-2);
// network has 300 MW generation, we set 400MW total P0 load
l4 = network.getLoad("l4");
l4.setP0(0.0).setQ0(50.0); // 0MW -> 0% participation factor
l5 = network.getLoad("l5");
l5.setP0(400.0).setQ0(50.0); // only non-zero load -> 100% participation factor
// test with l4 being reactive only load
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertBusBalance(network, "b4", parametersExt.getMaxActivePowerMismatch(), parametersExt.getMaxReactivePowerMismatch());
assertPowerFactor(network);
assertActivePowerEquals(0.0, l4.getTerminal());
assertReactivePowerEquals(50.0, l4.getTerminal());
assertActivePowerEquals(300.0, l5.getTerminal());
assertReactivePowerEquals(37.5, l5.getTerminal());
// test also with l4 being zero load
l4.setP0(0.0).setQ0(0.0);
result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertBusBalance(network, "b4", parametersExt.getMaxActivePowerMismatch(), parametersExt.getMaxReactivePowerMismatch());
assertPowerFactor(network);
assertActivePowerEquals(0.0, l4.getTerminal());
assertReactivePowerEquals(0.0, l4.getTerminal());
assertActivePowerEquals(300.0, l5.getTerminal());
assertReactivePowerEquals(37.5, l5.getTerminal());
}
private void assertBusBalance(Network network, String busId, double pTol, double qTol) {
double sumP = 0.0;
double sumQ = 0.0;
for (Terminal t : network.getBusBreakerView().getBus(busId).getConnectedTerminals()) {
sumP += t.getP();
sumQ += t.getQ();
}
assertEquals(0.0, sumP, pTol);
assertEquals(0.0, sumQ, qTol);
}
@Test
void testFictitiousLoadBoolean() {
l1.setFictitious(true);
l2.setFictitious(true);
l3.setFictitious(true);
l4.setFictitious(false);
l5.setFictitious(true);
l6.setFictitious(true);
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertActivePowerEquals(l1.getP0(), l1.getTerminal());
assertActivePowerEquals(l2.getP0(), l2.getTerminal());
assertActivePowerEquals(l3.getP0(), l3.getTerminal());
assertActivePowerEquals(l4.getP0() + 60, l4.getTerminal());
assertActivePowerEquals(l5.getP0(), l5.getTerminal());
assertActivePowerEquals(l6.getP0(), l6.getTerminal());
LoadFlowResult loadFlowResultExpected = new LoadFlowResultBuilder(true)
.addMetrics("3", "CONVERGED")
.addComponentResult(0, 0, LoadFlowResult.ComponentResult.Status.CONVERGED, 3, "b4_vl_0", 4.0392134081912445E-9)
.build();
assertLoadFlowResultsEquals(loadFlowResultExpected, result);
}
@Test
void testFictitiousLoadType() {
l1.setLoadType(LoadType.AUXILIARY); // 30 MW
l2.setLoadType(LoadType.UNDEFINED); // 60 MW
l3.setLoadType(LoadType.FICTITIOUS); // 50 MW
l4.setLoadType(LoadType.AUXILIARY); // 140 MW
l5.setLoadType(LoadType.UNDEFINED); // 10 MW
l6.setLoadType(LoadType.FICTITIOUS); // -50 MW
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertActivePowerEquals(l1.getP0() + 7.5, l1.getTerminal());
assertActivePowerEquals(l2.getP0() + 15, l2.getTerminal());
assertActivePowerEquals(l3.getP0(), l3.getTerminal());
assertActivePowerEquals(l4.getP0() + 35, l4.getTerminal());
assertActivePowerEquals(l5.getP0() + 2.5, l5.getTerminal());
assertActivePowerEquals(l6.getP0(), l6.getTerminal());
LoadFlowResult loadFlowResultExpected = new LoadFlowResultBuilder(true)
.addMetrics("3", "CONVERGED")
.addComponentResult(0, 0, LoadFlowResult.ComponentResult.Status.CONVERGED, 3, "b4_vl_0", 4.0392134081912445E-9)
.build();
assertLoadFlowResultsEquals(loadFlowResultExpected, result);
}
}