Skip to content

Commit

Permalink
Fix regression slack distribution reports
Browse files Browse the repository at this point in the history
Signed-off-by: Damien Jeandemange <damien.jeandemange@artelys.com>
  • Loading branch information
jeandemanged committed Dec 11, 2024
1 parent 36dc40a commit 6bb3947
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public OuterLoopResult check(AcOuterLoopContext context, ReportNode reportNode)
if (Math.abs(remainingMismatch) > ActivePowerDistribution.P_RESIDUE_EPS) {
Reports.reportMismatchDistributionFailure(iterationReportNode, remainingMismatch * PerUnit.SB);
} else {
reportAndLogSuccess(iterationReportNode, slackBusActivePowerMismatch, resultAfterBh);
ActivePowerDistribution.reportAndLogSuccess(iterationReportNode, slackBusActivePowerMismatch, resultAfterBh);
}
DistributedSlackContextData contextData = (DistributedSlackContextData) context.getData();
contextData.addDistributedActivePower(distributedActivePower);
Expand All @@ -97,10 +97,4 @@ public double getSlackBusActivePowerMismatch(AcOuterLoopContext context) {
return context.getLastSolverResult().getSlackBusActivePowerMismatch();
}

private static void reportAndLogSuccess(ReportNode reportNode, double slackBusActivePowerMismatch, ActivePowerDistribution.ResultAfterFailureBehaviorHandling result) {
Reports.reportMismatchDistributionSuccess(reportNode, slackBusActivePowerMismatch * PerUnit.SB, result.iteration());

LOGGER.info("Slack bus active power ({} MW) distributed in {} distribution iteration(s)",
slackBusActivePowerMismatch * PerUnit.SB, result.iteration());
}
}
68 changes: 37 additions & 31 deletions src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
package com.powsybl.openloadflow.dc;

import com.google.common.collect.Lists;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.math.matrix.MatrixException;
import com.powsybl.openloadflow.OpenLoadFlowParameters;
import com.powsybl.openloadflow.dc.equations.DcEquationType;
import com.powsybl.openloadflow.dc.equations.DcVariableType;
import com.powsybl.openloadflow.equations.*;
Expand All @@ -25,6 +25,7 @@
import com.powsybl.openloadflow.network.util.ActivePowerDistribution;
import com.powsybl.openloadflow.network.util.UniformValueVoltageInitializer;
import com.powsybl.openloadflow.network.util.VoltageInitializer;
import com.powsybl.openloadflow.util.PerUnit;
import com.powsybl.openloadflow.util.Reports;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
Expand Down Expand Up @@ -163,34 +164,6 @@ private void runOuterLoop(DcOuterLoop outerLoop, DcOuterLoopContext outerLoopCon
}
}

private double handleDistributionBehaviour(DcLoadFlowContext context, double distributedActivePower) {
double remainingMismatch = getActivePowerMismatch(context.getNetwork().getBuses());
if (remainingMismatch > context.getParameters().getSlackBusPMaxMismatch()) {
switch (context.getParameters().getSlackDistributionFailureBehavior()) {
case FAIL -> {
LOGGER.error("DC loadflow failed to distribute slack bus active power on network {}", context.getNetwork());
// TODO : how to return the failure in the DCLoadFlow result since it is not in an OuterLoop ?
return distributedActivePower;
}
case THROW -> throw new PowsyblException("DC loadflow failed to distribute slack bus active power on network");
case DISTRIBUTE_ON_REFERENCE_GENERATOR -> {
if (!context.getParameters().isAreaInterchangeControl()) {
LfGenerator referenceGenerator = context.getNetwork().getReferenceGenerator();
Objects.requireNonNull(referenceGenerator, () -> "No reference generator in " + context.getNetwork());
referenceGenerator.setTargetP(referenceGenerator.getTargetP() + remainingMismatch);
LOGGER.warn("Could not distribute slack bus active power, remaining mismatch {} is redistributed to reference generator {}", remainingMismatch, referenceGenerator.getId());
return distributedActivePower + remainingMismatch;
} else {
// If AreaInterchangeControl is activated, do not distribute in reference generator and behave like fail case
LOGGER.error("DC loadflow failed to distribute slack bus active power on network {}", context.getNetwork());
return distributedActivePower;
}
}
}
}
return distributedActivePower;
}

public static boolean solve(double[] targetVectorArray,
JacobianMatrix<DcVariableType, DcEquationType> jacobianMatrix,
ReportNode reportNode) {
Expand Down Expand Up @@ -235,8 +208,41 @@ public DcLoadFlowResult run() {
double initialSlackBusActivePowerMismatch = getActivePowerMismatch(network.getBuses());
double distributedActivePower = 0.0;
if (parameters.isDistributedSlack() || parameters.isAreaInterchangeControl()) {
distributedActivePower = distributeSlack(network, network.getBuses(), parameters.getBalanceType(), parameters.getNetworkParameters().isUseActiveLimits());
distributedActivePower = handleDistributionBehaviour(context, distributedActivePower);
LoadFlowParameters.BalanceType balanceType = parameters.getBalanceType();
boolean useActiveLimits = parameters.getNetworkParameters().isUseActiveLimits();
ActivePowerDistribution activePowerDistribution = ActivePowerDistribution.create(balanceType, false, useActiveLimits);
var result = activePowerDistribution.run(network, initialSlackBusActivePowerMismatch);
final LfGenerator referenceGenerator;
final OpenLoadFlowParameters.SlackDistributionFailureBehavior behavior;
if (parameters.isAreaInterchangeControl()) {
// actual behavior will be handled by the outerloop itself, just leave on slack bus here
behavior = OpenLoadFlowParameters.SlackDistributionFailureBehavior.LEAVE_ON_SLACK_BUS;
referenceGenerator = null;
} else {
behavior = parameters.getSlackDistributionFailureBehavior();
referenceGenerator = context.getNetwork().getReferenceGenerator();
}
ActivePowerDistribution.ResultAfterFailureBehaviorHandling resultAfterBh = ActivePowerDistribution.handleDistributionFailureBehavior(
behavior,
referenceGenerator,
initialSlackBusActivePowerMismatch,
result,
"Failed to distribute slack bus active power mismatch, %.2f MW remains"
);
double remainingMismatch = resultAfterBh.remainingMismatch();
distributedActivePower = initialSlackBusActivePowerMismatch - remainingMismatch;
distributedActivePower += resultAfterBh.additionalDistributedActivePower();
if (Math.abs(remainingMismatch) > ActivePowerDistribution.P_RESIDUE_EPS) {
Reports.reportMismatchDistributionFailure(reportNode, remainingMismatch * PerUnit.SB);
} else {
ActivePowerDistribution.reportAndLogSuccess(reportNode, initialSlackBusActivePowerMismatch, resultAfterBh);
}
if (resultAfterBh.failed()) {
runningContext.lastSolverSuccess = false;
runningContext.lastOuterLoopResult = new OuterLoopResult("DistributedSlack", OuterLoopStatus.FAILED, resultAfterBh.failedMessage());
Reports.reportDcLfComplete(reportNode, runningContext.lastSolverSuccess, runningContext.lastOuterLoopResult.status().name());
return buildDcLoadFlowResult(network, runningContext, initialSlackBusActivePowerMismatch, distributedActivePower);
}
}

// we need to copy the target array because JacobianMatrix.solveTransposed take as an input the second member
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
package com.powsybl.openloadflow.network.util;

import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.openloadflow.OpenLoadFlowParameters;
import com.powsybl.openloadflow.network.LfBus;
import com.powsybl.openloadflow.network.LfGenerator;
import com.powsybl.openloadflow.network.LfNetwork;
import com.powsybl.openloadflow.util.PerUnit;
import com.powsybl.openloadflow.util.Reports;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -164,4 +166,11 @@ public static ResultAfterFailureBehaviorHandling handleDistributionFailureBehavi

return resultAfterFailureBehaviorHandling;
}

public static void reportAndLogSuccess(ReportNode reportNode, double slackBusActivePowerMismatch, ResultAfterFailureBehaviorHandling result) {
Reports.reportMismatchDistributionSuccess(reportNode, slackBusActivePowerMismatch * PerUnit.SB, result.iteration());

LOGGER.info("Slack bus active power ({} MW) distributed in {} distribution iteration(s)",
slackBusActivePowerMismatch * PerUnit.SB, result.iteration());
}
}
45 changes: 41 additions & 4 deletions src/test/java/com/powsybl/openloadflow/dc/DcLoadFlowTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,33 @@ void tuto1Test() {
assertEquals(-450, line2.getTerminal2().getP(), 0.01);
}

@Test
void testSlackDistributionEnabledResults() {
Network network = EurostagFactory.fix(EurostagTutorialExample1Factory.create());

LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
var componentResults = result.getComponentResults();
assertEquals(1, componentResults.size());
assertEquals(1, componentResults.get(0).getSlackBusResults().size());
assertEquals(-7.0, componentResults.get(0).getDistributedActivePower(), 1e-3);
assertEquals(0.0, componentResults.get(0).getSlackBusResults().get(0).getActivePowerMismatch(), 1e-3);
}

@Test
void testSlackDistributionDisabledResults() {
Network network = EurostagFactory.fix(EurostagTutorialExample1Factory.create());

parameters.setDistributedSlack(false);
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
var componentResults = result.getComponentResults();
assertEquals(1, componentResults.size());
assertEquals(1, componentResults.get(0).getSlackBusResults().size());
assertEquals(0.0, componentResults.get(0).getDistributedActivePower(), 1e-3);
assertEquals(-7.0, componentResults.get(0).getSlackBusResults().get(0).getActivePowerMismatch(), 1e-3);
}

@Test
void fourBusesTest() {
Network network = FourBusNetworkFactory.create();
Expand Down Expand Up @@ -470,16 +497,26 @@ void testDcSlackDistributionFailureBehavior() {
parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_GENERATION_P);
Generator referenceGenerator = network.getGenerator("B1-G");

parametersExt.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.FAIL);
parametersExt.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.LEAVE_ON_SLACK_BUS);
var result = loadFlowRunner.run(network, parameters);
assertTrue(result.isFullyConverged());
assertEquals(1, result.getComponentResults().size());
assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus());
assertEquals(321.9, result.getComponentResults().get(0).getSlackBusResults().get(0).getActivePowerMismatch(), 0.01);
assertEquals(0, result.getComponentResults().get(0).getDistributedActivePower(), 0.01);

parametersExt.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.FAIL);
result = loadFlowRunner.run(network, parameters);
assertFalse(result.isFullyConverged());
assertEquals(1, result.getComponentResults().size());
assertEquals(LoadFlowResult.ComponentResult.Status.FAILED, result.getComponentResults().get(0).getStatus());
assertEquals("Outer loop failed: Failed to distribute slack bus active power mismatch, 321.90 MW remains", result.getComponentResults().get(0).getStatusText());
assertEquals(321.9, result.getComponentResults().get(0).getSlackBusResults().get(0).getActivePowerMismatch(), 0.01);
assertEquals(0, result.getComponentResults().get(0).getDistributedActivePower(), 0.01);
assertActivePowerEquals(-128.9, referenceGenerator.getTerminal());
// TODO : receive failure

parametersExt.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.THROW);
CompletionException e = assertThrows(CompletionException.class, () -> loadFlowRunner.run(network, parameters));
assertEquals("DC loadflow failed to distribute slack bus active power on network", e.getCause().getMessage());
assertEquals("Failed to distribute slack bus active power mismatch, 321.90 MW remains", e.getCause().getMessage());

parametersExt.setSlackDistributionFailureBehavior(OpenLoadFlowParameters.SlackDistributionFailureBehavior.DISTRIBUTE_ON_REFERENCE_GENERATOR);
parametersExt.setReferenceBusSelectionMode(ReferenceBusSelectionMode.GENERATOR_REFERENCE_PRIORITY);
Expand Down
2 changes: 2 additions & 0 deletions src/test/resources/multipleConnectedComponentsDcReport.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
Network balance: active generation=3.0 MW, active load=2.0 MW, reactive generation=0.0 MVar, reactive load=0.0 MVar
Angle reference bus: b1_vl_0
Slack bus: b1_vl_0
Slack bus active power (-0.9999999999999997 MW) distributed in 1 distribution iteration(s)
DC load flow completed (solverSuccess=true, outerloopStatus=STABLE)
+ Network CC1 SC1
+ Network info
Network has 3 buses and 4 branches
Network balance: active generation=2.0 MW, active load=4.0 MW, reactive generation=0.0 MVar, reactive load=0.0 MVar
Angle reference bus: b5_vl_0
Slack bus: b5_vl_0
Slack bus active power (2.0 MW) distributed in 1 distribution iteration(s)
DC load flow completed (solverSuccess=true, outerloopStatus=STABLE)
No calculation will be done on 1 network(s) that have no generators

0 comments on commit 6bb3947

Please sign in to comment.