From b5831ddaf5a051e2d679d5918c86b4baf055385d Mon Sep 17 00:00:00 2001 From: jeandemanged Date: Tue, 4 Jun 2024 08:52:27 +0200 Subject: [PATCH] Fix bus imbalance with slack distribution on load (#1040) Signed-off-by: Damien Jeandemange --- .../openloadflow/network/impl/LfLoadImpl.java | 6 +- .../ac/DistributedSlackOnLoadTest.java | 71 ++++++++++++++----- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/powsybl/openloadflow/network/impl/LfLoadImpl.java b/src/main/java/com/powsybl/openloadflow/network/impl/LfLoadImpl.java index 52c8564545..fa37b81e40 100644 --- a/src/main/java/com/powsybl/openloadflow/network/impl/LfLoadImpl.java +++ b/src/main/java/com/powsybl/openloadflow/network/impl/LfLoadImpl.java @@ -82,9 +82,10 @@ void add(Load load, LfNetworkParameters parameters) { loadsRefs.add(Ref.create(load, parameters.isCacheEnabled())); loadsDisablingStatus.put(load.getId(), false); double p0 = load.getP0(); + double q0 = load.getQ0(); targetP += p0 / PerUnit.SB; initialTargetP += p0 / PerUnit.SB; - targetQ += load.getQ0() / PerUnit.SB; + targetQ += q0 / PerUnit.SB; boolean hasVariableActivePower = false; if (parameters.isDistributedOnConformLoad()) { LoadDetail loadDetail = load.getExtension(LoadDetail.class); @@ -92,7 +93,8 @@ void add(Load load, LfNetworkParameters parameters) { hasVariableActivePower = loadDetail.getFixedActivePower() != load.getP0(); } } - if (p0 < 0 || hasVariableActivePower) { + boolean reactiveOnlyLoad = p0 == 0 && q0 != 0; + if (p0 < 0 || hasVariableActivePower || reactiveOnlyLoad) { ensurePowerFactorConstantByLoad = true; } double absTargetP = getAbsVariableTargetP(load); diff --git a/src/test/java/com/powsybl/openloadflow/ac/DistributedSlackOnLoadTest.java b/src/test/java/com/powsybl/openloadflow/ac/DistributedSlackOnLoadTest.java index 6635d2ab96..a6dae6ae6a 100644 --- a/src/test/java/com/powsybl/openloadflow/ac/DistributedSlackOnLoadTest.java +++ b/src/test/java/com/powsybl/openloadflow/ac/DistributedSlackOnLoadTest.java @@ -9,6 +9,7 @@ import com.powsybl.iidm.network.Load; import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Terminal; import com.powsybl.iidm.network.extensions.LoadDetailAdder; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.loadflow.LoadFlow; @@ -17,6 +18,7 @@ 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.SlackBusSelectionMode; @@ -26,8 +28,7 @@ import java.util.concurrent.CompletionException; -import static com.powsybl.openloadflow.util.LoadFlowAssert.assertActivePowerEquals; -import static com.powsybl.openloadflow.util.LoadFlowAssert.assertLoadFlowResultsEquals; +import static com.powsybl.openloadflow.util.LoadFlowAssert.*; import static org.junit.jupiter.api.Assertions.*; /** @@ -207,7 +208,7 @@ void testNetworkWithoutConformingLoad() { void testPowerFactorConstant2() { Network network = DistributedSlackNetworkFactory.createNetworkWithLoads2(); parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_CONFORM_LOAD); - parametersExt.setLoadPowerFactorConstant(true); + parametersExt.setLoadPowerFactorConstant(true).setNewtonRaphsonConvEpsPerEq(1e-6); network.getLoad("l4").newExtension(LoadDetailAdder.class) .withVariableActivePower(100) .withFixedActivePower(0) @@ -218,13 +219,7 @@ void testPowerFactorConstant2() { .add(); LoadFlowResult result = loadFlowRunner.run(network, parameters); assertTrue(result.isFullyConverged()); - double sumBus = 0.0; - sumBus += network.getLine("l14").getTerminal2().getQ(); - sumBus += network.getLine("l24").getTerminal2().getQ(); - sumBus += network.getLine("l34").getTerminal2().getQ(); - sumBus += network.getLoad("l4").getTerminal().getQ(); - sumBus += network.getLoad("l5").getTerminal().getQ(); - assertEquals(0.0, sumBus, 10E-6); + assertBusBalance(network, "b4", 10E-6, 10E-6); assertPowerFactor(network); } @@ -252,13 +247,55 @@ void testPowerFactorConstant3() { .add(); LoadFlowResult result = loadFlowRunner.run(network, parameters); assertTrue(result.isFullyConverged()); - double sumBus = 0.0; - sumBus += network.getLine("l14").getTerminal2().getQ(); - sumBus += network.getLine("l24").getTerminal2().getQ(); - sumBus += network.getLine("l34").getTerminal2().getQ(); - sumBus += network.getLoad("l4").getTerminal().getQ(); - sumBus += network.getLoad("l5").getTerminal().getQ(); - assertEquals(0.0, sumBus, 10E-3); + assertBusBalance(network, "b4", 10E-3, 10E-3); + assertPowerFactor(network); + } + + @Test + void testPowerFactorConstant4() { + Network 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 + Load l4 = network.getLoad("l4"); + l4.setP0(0.0).setQ0(50.0); // 0MW -> 0% participation factor + Load 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); } }