Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve AC Load Flow reports #977

Merged
merged 43 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9c7f5e6
make NR report more human-readable
caioluke Feb 8, 2024
1e45ce0
fix NR convergence status, add outerloop termination status
caioluke Feb 12, 2024
a304014
improve reporter on DistributedSlackOuterLoop
caioluke Feb 12, 2024
3d49680
improve reporter on MonitoringVoltageOuterLoop
caioluke Feb 12, 2024
9d6685f
improve reporter on ReactiveLimitsOuterLoop
caioluke Feb 12, 2024
710b64d
fix report PVPQ/PQPV ReactiveLimitsOuterLoop
caioluke Feb 12, 2024
d581a71
update esgTutoReportDetailedNrReportLf.txt
caioluke Feb 12, 2024
c8675c4
add report for AcIncrementalPhaseControlOuterLoop
caioluke Feb 14, 2024
ddde330
add report for IncrementalTransformerVoltageControlOuterLoop
caioluke Feb 14, 2024
5c763a3
add report for IncrementalTransformerReactivePowerControlOuterLoop
caioluke Feb 14, 2024
80c3fdc
add report for IncrementalShuntVoltageControlOuterLoop
caioluke Feb 14, 2024
c7f8afd
updated reports in tests
caioluke Feb 14, 2024
74c74f8
remove useless information from reporter
caioluke Feb 14, 2024
0d3f661
add extra nesting to outer loop reports
caioluke Feb 14, 2024
3854229
refacto newton raphson reporter, add state vector scaling reporter
caioluke Feb 15, 2024
740fcad
update test reports
caioluke Feb 16, 2024
53c6a1f
Merge branch 'main' into improve_NR_logs
caioluke Feb 19, 2024
2d2de6a
remove indentation
caioluke Feb 19, 2024
d781e81
fix after hadrien's comments
caioluke Feb 19, 2024
e343971
add extra indentation for newton raphson mismatches and for Increment…
caioluke Feb 19, 2024
dbd8d96
extra indentation for IncrementalTransformerVoltageControlOuterLoop
caioluke Feb 20, 2024
e0c9899
indent iteration information for IncrementalShuntVoltageControlOuterLoop
caioluke Feb 20, 2024
cdce116
Merge branch 'main' into improve_NR_logs
caioluke Feb 20, 2024
06f561b
add report tests for some outer loops
caioluke Feb 21, 2024
aad0335
change new tests olf parameters
caioluke Feb 21, 2024
5f1fdde
add constants for Reports and remove useless use of reporter in method
caioluke Feb 21, 2024
e545205
Merge branch 'main' into improve_NR_logs
caioluke Feb 21, 2024
f1740a0
Merge branch 'main' into improve_NR_logs
caioluke Feb 23, 2024
c89bffa
remove plural handling in OLF, remove extra imports in Reports
caioluke Feb 26, 2024
0a7c63e
wip review
jeandemanged Feb 27, 2024
ccb8c0f
wip review
jeandemanged Feb 28, 2024
d3878da
fix
jeandemanged Feb 28, 2024
0660935
fix
jeandemanged Feb 28, 2024
49ddaa1
Try to solve last issues
Hadrien-Godard Feb 29, 2024
73240da
fix record
jeandemanged Feb 29, 2024
b9df4ce
fix if condition in incremental transformer outerloops
caioluke Mar 5, 2024
e892840
Merge branch 'main' into improve_NR_logs
caioluke Mar 5, 2024
81aa44c
add new result for DISTRIBUTE_ON_REFERENCE_GENERATOR and added "distr…
caioluke Mar 5, 2024
c262548
fix logger info for incremental transformer outer loops
caioluke Mar 5, 2024
cd74118
Merge branch 'main' into improve_NR_logs
caioluke Mar 6, 2024
de8a567
put back mismatches, fix some logging messages
caioluke Mar 6, 2024
0ac7094
put back mismatches correctly
caioluke Mar 6, 2024
1e12c4b
Merge branch 'main' into improve_NR_logs
geofjamg Mar 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
*/
public class AcOuterLoopContext extends AbstractOuterLoopContext<AcVariableType, AcEquationType, AcLoadFlowParameters, AcLoadFlowContext> {

private int iteration;
private int iteration; // current iterations of this single outer loop type

private int outerLoopTotalIterations; // current total iterations over all outer loop types, for reporting purposes

private AcSolverResult lastSolverResult;

Expand All @@ -41,4 +43,12 @@ public AcSolverResult getLastSolverResult() {
public void setLastSolverResult(AcSolverResult lastSolverResult) {
this.lastSolverResult = lastSolverResult;
}

public int getOuterLoopTotalIterations() {
return outerLoopTotalIterations;
}

public void setOuterLoopTotalIterations(int outerLoopTotalIterations) {
this.outerLoopTotalIterations = outerLoopTotalIterations;
}
}
12 changes: 8 additions & 4 deletions src/main/java/com/powsybl/openloadflow/ac/AcloadFlowEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import com.google.common.collect.Lists;
import com.powsybl.commons.reporter.Reporter;
import com.powsybl.commons.reporter.TypedValue;
import com.powsybl.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.ac.solver.*;
Expand Down Expand Up @@ -76,6 +75,7 @@ private void runOuterLoop(AcOuterLoop outerLoop, AcOuterLoopContext outerLoopCon

// check outer loop status
outerLoopContext.setIteration(outerLoopIteration.getValue());
outerLoopContext.setOuterLoopTotalIterations(runningContext.outerLoopTotalIterations);
outerLoopContext.setLastSolverResult(runningContext.lastSolverResult);
outerLoopContext.setLoadFlowContext(context);
outerLoopStatus = outerLoop.check(outerLoopContext, olReporter);
Expand All @@ -90,7 +90,8 @@ private void runOuterLoop(AcOuterLoop outerLoop, AcOuterLoopContext outerLoopCon
solver.getName(),
context.getNetwork().getNumCC(),
context.getNetwork().getNumSC(),
outerLoopIteration.toInteger() + 1, outerLoop.getName());
runningContext.outerLoopTotalIterations + 1,
outerLoop.getName());
}

// if not yet stable, restart solver
Expand All @@ -104,6 +105,10 @@ private void runOuterLoop(AcOuterLoop outerLoop, AcOuterLoopContext outerLoopCon
} while (outerLoopStatus == OuterLoopStatus.UNSTABLE
&& runningContext.lastSolverResult.getStatus() == AcSolverStatus.CONVERGED
&& runningContext.outerLoopTotalIterations < context.getParameters().getMaxOuterLoopIterations());

if (outerLoopStatus != OuterLoopStatus.STABLE) {
Reports.reportUnsuccessfulOuterLoop(olReporter, outerLoopStatus.name());
}
}

@Override
Expand Down Expand Up @@ -206,8 +211,7 @@ public AcLoadFlowResult run() {

LOGGER.info("Ac loadflow complete on network {} (result={})", context.getNetwork(), result);

Reports.reportAcLfComplete(context.getNetwork().getReporter(), result.getSolverStatus().name(),
result.getSolverStatus() == AcSolverStatus.CONVERGED ? TypedValue.INFO_SEVERITY : TypedValue.ERROR_SEVERITY);
Reports.reportAcLfComplete(context.getNetwork().getReporter(), result.isSuccess(), result.getSolverStatus().name(), result.getOuterLoopStatus().name());

context.setResult(result);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
import com.powsybl.openloadflow.lf.outerloop.OuterLoopStatus;
import com.powsybl.openloadflow.network.*;
import com.powsybl.openloadflow.util.PerUnit;
import com.powsybl.openloadflow.util.Reports;
import org.apache.commons.lang3.Range;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableInt;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
Expand Down Expand Up @@ -77,6 +78,7 @@ public DenseMatrix calculateSensitivityValues(List<LfBranch> controllerBranches,
return rhs;
}

@SuppressWarnings("unchecked")
private EquationTerm<AcVariableType, AcEquationType> getI1(LfBranch controlledBranch) {
return (EquationTerm<AcVariableType, AcEquationType>) controlledBranch.getI1();
}
Expand All @@ -92,9 +94,9 @@ public double calculateSensitivityFromA2I(LfBranch controllerBranch, LfBranch co
}
}

private boolean checkCurrentLimiterPhaseControls(AcSensitivityContext sensitivityContext, IncrementalContextData contextData,
List<TransformerPhaseControl> currentLimiterPhaseControls) {
MutableBoolean updated = new MutableBoolean(false);
private int checkCurrentLimiterPhaseControls(AcSensitivityContext sensitivityContext, IncrementalContextData contextData,
List<TransformerPhaseControl> currentLimiterPhaseControls) {
MutableInt numOfCurrentLimiterPstsThatChangedTap = new MutableInt(0);

for (TransformerPhaseControl phaseControl : currentLimiterPhaseControls) {
LfBranch controllerBranch = phaseControl.getControllerBranch();
Expand All @@ -116,11 +118,11 @@ private boolean checkCurrentLimiterPhaseControls(AcSensitivityContext sensitivit
Range<Integer> tapPositionRange = piModel.getTapPositionRange();
piModel.updateTapPositionToExceedNewA1(da, MAX_TAP_SHIFT, controllerContext.getAllowedDirection()).ifPresent(direction -> {
controllerContext.updateAllowedDirection(direction);
updated.setValue(true);
numOfCurrentLimiterPstsThatChangedTap.add(1);
});

if (piModel.getTapPosition() != oldTapPosition) {
logger.debug("Controller branch '{}' change tap from {} to {} to limit current (full range: {})", controllerBranch.getId(),
logger.debug("Controller branch '{}' changed tap from {} to {} to limit current (full range: {})", controllerBranch.getId(),
oldTapPosition, piModel.getTapPosition(), tapPositionRange);

double discreteDa = piModel.getA1() - oldA1;
Expand All @@ -129,8 +131,7 @@ private boolean checkCurrentLimiterPhaseControls(AcSensitivityContext sensitivit
}
}
}

return updated.booleanValue();
return numOfCurrentLimiterPstsThatChangedTap.getValue();
}

private void checkImpactOnOtherPhaseShifters(AcSensitivityContext sensitivityContext, TransformerPhaseControl phaseControl,
Expand Down Expand Up @@ -169,7 +170,6 @@ private static double computeI(TransformerPhaseControl phaseControl) {

@Override
public OuterLoopStatus check(AcOuterLoopContext context, Reporter reporter) {
OuterLoopStatus status = OuterLoopStatus.STABLE;

var contextData = (IncrementalContextData) context.getData();

Expand All @@ -195,24 +195,43 @@ public OuterLoopStatus check(AcOuterLoopContext context, Reporter reporter) {
});
}

if (!currentLimiterPhaseControls.isEmpty() || !activePowerControlPhaseControls.isEmpty()) {
var sensitivityContext = new AcSensitivityContext(network,
controllerBranches,
context.getLoadFlowContext().getEquationSystem(),
context.getLoadFlowContext().getJacobianMatrix());

if (!currentLimiterPhaseControls.isEmpty()
&& checkCurrentLimiterPhaseControls(sensitivityContext,
contextData,
currentLimiterPhaseControls)) {
status = OuterLoopStatus.UNSTABLE;
}
OuterLoopStatus status = OuterLoopStatus.STABLE;

if (currentLimiterPhaseControls.isEmpty() && activePowerControlPhaseControls.isEmpty()) {
return status;
}

var sensitivityContext = new AcSensitivityContext(network,
controllerBranches,
context.getLoadFlowContext().getEquationSystem(),
context.getLoadFlowContext().getJacobianMatrix());

final int numOfCurrentLimiterPstsThatChangedTap;
final int numOfActivePowerControlPstsThatChangedTap;
if (!currentLimiterPhaseControls.isEmpty()) {
numOfCurrentLimiterPstsThatChangedTap = checkCurrentLimiterPhaseControls(sensitivityContext,
contextData,
currentLimiterPhaseControls);
} else {
numOfCurrentLimiterPstsThatChangedTap = 0;
}

if (!activePowerControlPhaseControls.isEmpty()
&& checkActivePowerControlPhaseControls(sensitivityContext,
contextData,
activePowerControlPhaseControls)) {
status = OuterLoopStatus.UNSTABLE;
if (!activePowerControlPhaseControls.isEmpty()) {
numOfActivePowerControlPstsThatChangedTap = checkActivePowerControlPhaseControls(sensitivityContext,
contextData,
activePowerControlPhaseControls);
} else {
numOfActivePowerControlPstsThatChangedTap = 0;
}

if (numOfCurrentLimiterPstsThatChangedTap + numOfActivePowerControlPstsThatChangedTap != 0) {
status = OuterLoopStatus.UNSTABLE;
Reporter iterationReporter = Reports.createOuterLoopIterationReporter(reporter, context.getOuterLoopTotalIterations() + 1);
if (numOfCurrentLimiterPstsThatChangedTap != 0) {
Reports.reportCurrentLimiterPstsChangedTaps(iterationReporter, numOfCurrentLimiterPstsThatChangedTap);
}
if (numOfActivePowerControlPstsThatChangedTap != 0) {
Reports.reportActivePowerControlPstsChangedTaps(iterationReporter, numOfActivePowerControlPstsThatChangedTap);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,68 +53,71 @@ public void initialize(AcOuterLoopContext context) {
@Override
public OuterLoopStatus check(AcOuterLoopContext context, Reporter reporter) {
double slackBusActivePowerMismatch = context.getLastSolverResult().getSlackBusActivePowerMismatch();
if (Math.abs(slackBusActivePowerMismatch) > slackBusPMaxMismatch / PerUnit.SB) {
ActivePowerDistribution.Result result = activePowerDistribution.run(context.getNetwork(), slackBusActivePowerMismatch);
double remainingMismatch = result.remainingMismatch();
double distributedActivePower = slackBusActivePowerMismatch - remainingMismatch;
DistributedSlackContextData contextData = (DistributedSlackContextData) context.getData();
contextData.addDistributedActivePower(distributedActivePower);
if (Math.abs(remainingMismatch) > ActivePowerDistribution.P_RESIDUE_EPS) {
OpenLoadFlowParameters.SlackDistributionFailureBehavior slackDistributionFailureBehavior = context.getLoadFlowContext().getParameters().getSlackDistributionFailureBehavior();
LfGenerator referenceGenerator = context.getNetwork().getReferenceGenerator();
if (OpenLoadFlowParameters.SlackDistributionFailureBehavior.DISTRIBUTE_ON_REFERENCE_GENERATOR == slackDistributionFailureBehavior) {
if (referenceGenerator == null) {
// no reference generator, fall back internally to FAIL mode
slackDistributionFailureBehavior = OpenLoadFlowParameters.SlackDistributionFailureBehavior.FAIL;
Reports.reportMismatchDistributionFailure(reporter, context.getIteration(), remainingMismatch * PerUnit.SB);
}
} else {
Reports.reportMismatchDistributionFailure(reporter, context.getIteration(), remainingMismatch * PerUnit.SB);
}
boolean shouldDistributeSlack = Math.abs(slackBusActivePowerMismatch) > slackBusPMaxMismatch / PerUnit.SB;

if (!shouldDistributeSlack) {
LOGGER.debug("Already balanced");
return OuterLoopStatus.STABLE;
}

switch (slackDistributionFailureBehavior) {
case THROW ->
throw new PowsyblException("Failed to distribute slack bus active power mismatch, "
+ remainingMismatch * PerUnit.SB + " MW remains");
case LEAVE_ON_SLACK_BUS -> {
LOGGER.warn("Failed to distribute slack bus active power mismatch, {} MW remains",
remainingMismatch * PerUnit.SB);
return result.movedBuses() ? OuterLoopStatus.UNSTABLE : OuterLoopStatus.STABLE;
}
case DISTRIBUTE_ON_REFERENCE_GENERATOR -> {
Objects.requireNonNull(referenceGenerator, () -> "No reference generator in " + context.getNetwork());
// remaining goes to reference generator, without any limit consideration
referenceGenerator.setTargetP(referenceGenerator.getTargetP() + remainingMismatch);
reportAndLogSuccess(context, reporter, slackBusActivePowerMismatch, result);
return OuterLoopStatus.UNSTABLE;
}
case FAIL -> {
LOGGER.error("Failed to distribute slack bus active power mismatch, {} MW remains",
remainingMismatch * PerUnit.SB);
// Mismatches reported in LoadFlowResult on slack bus(es) are the mismatches of the last NR run.
// Since we will not be re-running an NR, revert distributedActivePower reporting which would otherwise be misleading.
// Said differently, we report that we didn't distribute anything, and this is indeed consistent with the network state.
contextData.addDistributedActivePower(-distributedActivePower);
return OuterLoopStatus.FAILED;
}
Reporter iterationReporter = Reports.createOuterLoopIterationReporter(reporter, context.getOuterLoopTotalIterations() + 1);
ActivePowerDistribution.Result result = activePowerDistribution.run(context.getNetwork(), slackBusActivePowerMismatch);
double remainingMismatch = result.remainingMismatch();
double distributedActivePower = slackBusActivePowerMismatch - remainingMismatch;
DistributedSlackContextData contextData = (DistributedSlackContextData) context.getData();
contextData.addDistributedActivePower(distributedActivePower);
if (Math.abs(remainingMismatch) > ActivePowerDistribution.P_RESIDUE_EPS) {
OpenLoadFlowParameters.SlackDistributionFailureBehavior slackDistributionFailureBehavior = context.getLoadFlowContext().getParameters().getSlackDistributionFailureBehavior();
LfGenerator referenceGenerator = context.getNetwork().getReferenceGenerator();
if (OpenLoadFlowParameters.SlackDistributionFailureBehavior.DISTRIBUTE_ON_REFERENCE_GENERATOR == slackDistributionFailureBehavior) {
if (referenceGenerator == null) {
// no reference generator, fall back internally to FAIL mode
slackDistributionFailureBehavior = OpenLoadFlowParameters.SlackDistributionFailureBehavior.FAIL;
Reports.reportMismatchDistributionFailure(iterationReporter, remainingMismatch * PerUnit.SB);
}
} else {
reportAndLogSuccess(context, reporter, slackBusActivePowerMismatch, result);
return OuterLoopStatus.UNSTABLE;
Reports.reportMismatchDistributionFailure(iterationReporter, remainingMismatch * PerUnit.SB);
}
}

Reports.reportNoMismatchDistribution(reporter, context.getIteration());

LOGGER.debug("Already balanced");

return OuterLoopStatus.STABLE;
switch (slackDistributionFailureBehavior) {
case THROW ->
throw new PowsyblException("Failed to distribute slack bus active power mismatch, "
+ remainingMismatch * PerUnit.SB + " MW remains");
case LEAVE_ON_SLACK_BUS -> {
LOGGER.warn("Failed to distribute slack bus active power mismatch, {} MW remains",
remainingMismatch * PerUnit.SB);
return result.movedBuses() ? OuterLoopStatus.UNSTABLE : OuterLoopStatus.STABLE;
}
case DISTRIBUTE_ON_REFERENCE_GENERATOR -> {
Objects.requireNonNull(referenceGenerator, () -> "No reference generator in " + context.getNetwork());
// remaining goes to reference generator, without any limit consideration
referenceGenerator.setTargetP(referenceGenerator.getTargetP() + remainingMismatch);
// create a new result with iteration++, 0.0 mismatch and movedBuses to true
result = new ActivePowerDistribution.Result(result.iteration() + 1, 0.0, true);
reportAndLogSuccess(iterationReporter, slackBusActivePowerMismatch, result);
return OuterLoopStatus.UNSTABLE;
}
case FAIL -> {
LOGGER.error("Failed to distribute slack bus active power mismatch, {} MW remains",
remainingMismatch * PerUnit.SB);
// Mismatches reported in LoadFlowResult on slack bus(es) are the mismatches of the last NR run.
// Since we will not be re-running an NR, revert distributedActivePower reporting which would otherwise be misleading.
// Said differently, we report that we didn't distribute anything, and this is indeed consistent with the network state.
contextData.addDistributedActivePower(-distributedActivePower);
return OuterLoopStatus.FAILED;
}
default -> throw new IllegalArgumentException("Unknown slackDistributionFailureBehavior");
}
} else {
reportAndLogSuccess(iterationReporter, slackBusActivePowerMismatch, result);
return OuterLoopStatus.UNSTABLE;
}
}

private static void reportAndLogSuccess(AcOuterLoopContext context, Reporter reporter, double slackBusActivePowerMismatch, ActivePowerDistribution.Result result) {
Reports.reportMismatchDistributionSuccess(reporter, context.getIteration(), slackBusActivePowerMismatch * PerUnit.SB, result.iteration());
private static void reportAndLogSuccess(Reporter reporter, double slackBusActivePowerMismatch, ActivePowerDistribution.Result result) {
Reports.reportMismatchDistributionSuccess(reporter, slackBusActivePowerMismatch * PerUnit.SB, result.iteration());

LOGGER.info("Slack bus active power ({} MW) distributed in {} iterations",
LOGGER.info("Slack bus active power ({} MW) distributed in {} distribution iteration(s)",
slackBusActivePowerMismatch * PerUnit.SB, result.iteration());
}
}
Loading