Skip to content

Commit

Permalink
Add an option that allow checking unrealistic voltage only on final s…
Browse files Browse the repository at this point in the history
…tate of the loadflow. It allows outerloops to fix some voltage issues before failing.

Signed-off-by: MURGEY Sebastien <[email protected]>
  • Loading branch information
murgeyseb committed Sep 20, 2024
1 parent c4393ca commit d874b63
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 37 deletions.
21 changes: 21 additions & 0 deletions src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ public enum FictitiousGeneratorVoltageControlCheckMode {

public static final String SLACK_DISTRIBUTION_FAILURE_BEHAVIOR_PARAM_NAME = "slackDistributionFailureBehavior";

public static final String UNREALISTIC_VOLTAGE_CHECK_BEHAVIOR_PARAM_NAME = "unrealisticVoltageCheckBehavior";

public static final String VOLTAGE_REMOTE_CONTROL_PARAM_NAME = "voltageRemoteControl";

public static final String GENERATOR_REACTIVE_POWER_REMOTE_CONTROL_PARAM_NAME = "generatorReactivePowerRemoteControl";
Expand Down Expand Up @@ -336,6 +338,7 @@ public static <E extends Enum<E>> List<Object> getEnumPossibleValues(Class<E> en
new Parameter(LOW_IMPEDANCE_BRANCH_MODE_PARAM_NAME, ParameterType.STRING, "Low impedance branch mode", LOW_IMPEDANCE_BRANCH_MODE_DEFAULT_VALUE.name(), getEnumPossibleValues(LowImpedanceBranchMode.class), ParameterScope.FUNCTIONAL, MODEL_CATEGORY_KEY),
new Parameter(VOLTAGE_REMOTE_CONTROL_PARAM_NAME, ParameterType.BOOLEAN, "Generator voltage remote control", VOLTAGE_REMOTE_CONTROL_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, GENERATOR_VOLTAGE_CONTROL_CATEGORY_KEY),
new Parameter(SLACK_DISTRIBUTION_FAILURE_BEHAVIOR_PARAM_NAME, ParameterType.STRING, "Behavior in case of slack distribution failure", SLACK_DISTRIBUTION_FAILURE_BEHAVIOR_DEFAULT_VALUE.name(), getEnumPossibleValues(SlackDistributionFailureBehavior.class), ParameterScope.FUNCTIONAL, SLACK_DISTRIBUTION_CATEGORY_KEY),
new Parameter(UNREALISTIC_VOLTAGE_CHECK_BEHAVIOR_PARAM_NAME, ParameterType.STRING, "Behavior in case of unrealistic voltage", NewtonRaphsonParameters.DEFAULT_UNREALISTIC_VOLTAGE_CHECK_BEHAVIOR.name(), getEnumPossibleValues(NewtonRaphsonParameters.UnrealisticVoltageCheckBehavior.class)),
new Parameter(LOAD_POWER_FACTOR_CONSTANT_PARAM_NAME, ParameterType.BOOLEAN, "Load power factor is constant", LOAD_POWER_FACTOR_CONSTANT_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, SLACK_DISTRIBUTION_CATEGORY_KEY),
new Parameter(PLAUSIBLE_ACTIVE_POWER_LIMIT_PARAM_NAME, ParameterType.DOUBLE, "Plausible active power limit", LfNetworkParameters.PLAUSIBLE_ACTIVE_POWER_LIMIT_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, SLACK_DISTRIBUTION_CATEGORY_KEY),
new Parameter(SLACK_BUS_P_MAX_MISMATCH_PARAM_NAME, ParameterType.DOUBLE, "Slack bus max active power mismatch", SLACK_BUS_P_MAX_MISMATCH_DEFAULT_VALUE, ParameterScope.FUNCTIONAL, SLACK_DISTRIBUTION_CATEGORY_KEY),
Expand Down Expand Up @@ -435,6 +438,8 @@ public enum ReportedFeatures {

private SlackDistributionFailureBehavior slackDistributionFailureBehavior = SLACK_DISTRIBUTION_FAILURE_BEHAVIOR_DEFAULT_VALUE;

private NewtonRaphsonParameters.UnrealisticVoltageCheckBehavior unrealisticVoltageCheckBehavior = NewtonRaphsonParameters.DEFAULT_UNREALISTIC_VOLTAGE_CHECK_BEHAVIOR;

private boolean voltageRemoteControl = VOLTAGE_REMOTE_CONTROL_DEFAULT_VALUE;

private LowImpedanceBranchMode lowImpedanceBranchMode = LOW_IMPEDANCE_BRANCH_MODE_DEFAULT_VALUE;
Expand Down Expand Up @@ -627,6 +632,15 @@ public OpenLoadFlowParameters setSlackDistributionFailureBehavior(SlackDistribut
return this;
}

public NewtonRaphsonParameters.UnrealisticVoltageCheckBehavior getUnrealisticVoltageCheckBehavior() {
return unrealisticVoltageCheckBehavior;
}

public OpenLoadFlowParameters setUnrealisticVoltageCheckBehavior(NewtonRaphsonParameters.UnrealisticVoltageCheckBehavior unrealisticVoltageCheckBehavior) {
this.unrealisticVoltageCheckBehavior = Objects.requireNonNull(unrealisticVoltageCheckBehavior);
return this;
}

public boolean isVoltageRemoteControl() {
return voltageRemoteControl;
}
Expand Down Expand Up @@ -1285,6 +1299,7 @@ public static OpenLoadFlowParameters load(PlatformConfig platformConfig) {
.setLowImpedanceBranchMode(config.getEnumProperty(LOW_IMPEDANCE_BRANCH_MODE_PARAM_NAME, LowImpedanceBranchMode.class, LOW_IMPEDANCE_BRANCH_MODE_DEFAULT_VALUE))
.setVoltageRemoteControl(config.getBooleanProperty(VOLTAGE_REMOTE_CONTROL_PARAM_NAME, VOLTAGE_REMOTE_CONTROL_DEFAULT_VALUE))
.setSlackDistributionFailureBehavior(config.getEnumProperty(SLACK_DISTRIBUTION_FAILURE_BEHAVIOR_PARAM_NAME, SlackDistributionFailureBehavior.class, SLACK_DISTRIBUTION_FAILURE_BEHAVIOR_DEFAULT_VALUE))
.setUnrealisticVoltageCheckBehavior(config.getEnumProperty(UNREALISTIC_VOLTAGE_CHECK_BEHAVIOR_PARAM_NAME, NewtonRaphsonParameters.UnrealisticVoltageCheckBehavior.class, NewtonRaphsonParameters.DEFAULT_UNREALISTIC_VOLTAGE_CHECK_BEHAVIOR))
.setLoadPowerFactorConstant(config.getBooleanProperty(LOAD_POWER_FACTOR_CONSTANT_PARAM_NAME, LOAD_POWER_FACTOR_CONSTANT_DEFAULT_VALUE))
.setPlausibleActivePowerLimit(config.getDoubleProperty(PLAUSIBLE_ACTIVE_POWER_LIMIT_PARAM_NAME, LfNetworkParameters.PLAUSIBLE_ACTIVE_POWER_LIMIT_DEFAULT_VALUE))
.setNewtonRaphsonStoppingCriteriaType(config.getEnumProperty(NEWTONRAPHSON_STOPPING_CRITERIA_TYPE_PARAM_NAME, NewtonRaphsonStoppingCriteriaType.class, NEWTONRAPHSON_STOPPING_CRITERIA_TYPE_DEFAULT_VALUE))
Expand Down Expand Up @@ -1373,6 +1388,8 @@ public OpenLoadFlowParameters update(Map<String, String> properties) {
.ifPresent(prop -> this.setVoltageRemoteControl(Boolean.parseBoolean(prop)));
Optional.ofNullable(properties.get(SLACK_DISTRIBUTION_FAILURE_BEHAVIOR_PARAM_NAME))
.ifPresent(prop -> this.setSlackDistributionFailureBehavior(SlackDistributionFailureBehavior.valueOf(prop)));
Optional.ofNullable(properties.get(UNREALISTIC_VOLTAGE_CHECK_BEHAVIOR_PARAM_NAME))
.ifPresent(prop -> this.setUnrealisticVoltageCheckBehavior(NewtonRaphsonParameters.UnrealisticVoltageCheckBehavior.valueOf(prop)));
Optional.ofNullable(properties.get(LOAD_POWER_FACTOR_CONSTANT_PARAM_NAME))
.ifPresent(prop -> this.setLoadPowerFactorConstant(Boolean.parseBoolean(prop)));
Optional.ofNullable(properties.get(PLAUSIBLE_ACTIVE_POWER_LIMIT_PARAM_NAME))
Expand Down Expand Up @@ -1510,6 +1527,7 @@ public Map<String, Object> toMap() {
map.put(SLACK_BUS_SELECTION_MODE_PARAM_NAME, slackBusSelectionMode);
map.put(SLACK_BUSES_IDS_PARAM_NAME, slackBusesIds);
map.put(SLACK_DISTRIBUTION_FAILURE_BEHAVIOR_PARAM_NAME, slackDistributionFailureBehavior);
map.put(UNREALISTIC_VOLTAGE_CHECK_BEHAVIOR_PARAM_NAME, unrealisticVoltageCheckBehavior);
map.put(VOLTAGE_REMOTE_CONTROL_PARAM_NAME, voltageRemoteControl);
map.put(LOW_IMPEDANCE_BRANCH_MODE_PARAM_NAME, lowImpedanceBranchMode);
map.put(LOAD_POWER_FACTOR_CONSTANT_PARAM_NAME, loadPowerFactorConstant);
Expand Down Expand Up @@ -1782,6 +1800,7 @@ public static AcLoadFlowParameters createAcParameters(LoadFlowParameters paramet
var newtonRaphsonParameters = new NewtonRaphsonParameters()
.setStoppingCriteria(createNewtonRaphsonStoppingCriteria(parametersExt))
.setMaxIterations(parametersExt.getMaxNewtonRaphsonIterations())
.setUnrealisticVoltageCheckBehavior(parametersExt.getUnrealisticVoltageCheckBehavior())
.setMinRealisticVoltage(parametersExt.getMinRealisticVoltage())
.setMaxRealisticVoltage(parametersExt.getMaxRealisticVoltage())
.setStateVectorScalingMode(parametersExt.getStateVectorScalingMode())
Expand Down Expand Up @@ -1915,6 +1934,7 @@ public static boolean equals(LoadFlowParameters parameters1, LoadFlowParameters
return extension1.getSlackBusSelectionMode() == extension2.getSlackBusSelectionMode() &&
extension1.getSlackBusesIds().equals(extension2.getSlackBusesIds()) &&
extension1.getSlackDistributionFailureBehavior() == extension2.getSlackDistributionFailureBehavior() &&
extension1.getUnrealisticVoltageCheckBehavior() == extension2.getUnrealisticVoltageCheckBehavior() &&
extension1.isVoltageRemoteControl() == extension2.isVoltageRemoteControl() &&
extension1.getLowImpedanceBranchMode() == extension2.getLowImpedanceBranchMode() &&
extension1.isLoadPowerFactorConstant() == extension2.isLoadPowerFactorConstant() &&
Expand Down Expand Up @@ -2008,6 +2028,7 @@ public static LoadFlowParameters clone(LoadFlowParameters parameters) {
.setSlackBusSelectionMode(extension.getSlackBusSelectionMode())
.setSlackBusesIds(new ArrayList<>(extension.getSlackBusesIds()))
.setSlackDistributionFailureBehavior(extension.getSlackDistributionFailureBehavior())
.setUnrealisticVoltageCheckBehavior(extension.getUnrealisticVoltageCheckBehavior())
.setVoltageRemoteControl(extension.isVoltageRemoteControl())
.setLowImpedanceBranchMode(extension.getLowImpedanceBranchMode())
.setLoadPowerFactorConstant(extension.isLoadPowerFactorConstant())
Expand Down
27 changes: 19 additions & 8 deletions src/main/java/com/powsybl/openloadflow/ac/AcloadFlowEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;

/**
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
Expand Down Expand Up @@ -108,14 +105,28 @@ private void runOuterLoop(AcOuterLoop outerLoop, AcOuterLoopContext outerLoopCon
outerLoopIteration.increment();
}
} while (outerLoopResult.status() == OuterLoopStatus.UNSTABLE
&& runningContext.lastSolverResult.getStatus() == AcSolverStatus.CONVERGED
&& lastResultStatusAcceptable(runningContext, context.getParameters().getNewtonRaphsonParameters())
&& runningContext.outerLoopTotalIterations < context.getParameters().getMaxOuterLoopIterations());

if (outerLoopResult.status() != OuterLoopStatus.STABLE) {
Reports.reportUnsuccessfulOuterLoop(olReportNode, outerLoopResult.status().name());
}
}

private static boolean lastResultStatusAcceptable(RunningContext runningContext, NewtonRaphsonParameters nrParameters) {
switch (runningContext.lastSolverResult.getStatus()) {
case CONVERGED -> {
return true;
}
case UNREALISTIC_STATE -> {
return nrParameters.getUnrealisticVoltageCheckBehavior() == NewtonRaphsonParameters.UnrealisticVoltageCheckBehavior.FAIL_AT_UNREALISTIC_STATE_IN_FINAL_ITERATION_ONLY;
}
default -> {
return false;
}
}
}

@Override
public AcLoadFlowResult run() {
LOGGER.info("Start AC loadflow on network {}", context.getNetwork());
Expand Down Expand Up @@ -159,7 +170,7 @@ public AcLoadFlowResult run() {
runningContext.nrTotalIterations.add(runningContext.lastSolverResult.getIterations());

// continue with outer loops only if solver succeed
if (runningContext.lastSolverResult.getStatus() == AcSolverStatus.CONVERGED) {
if (lastResultStatusAcceptable(runningContext, context.getParameters().getNewtonRaphsonParameters())) {

// re-run all outer loops until solver failed or no more solver iterations are needed
int oldNrTotalIterations;
Expand All @@ -174,14 +185,14 @@ public AcLoadFlowResult run() {
// - last solver run succeed,
// - last OuterLoopStatus is not FAILED
// - we have not reached max number of outer loop iteration
if (runningContext.lastSolverResult.getStatus() != AcSolverStatus.CONVERGED
if (!lastResultStatusAcceptable(runningContext, context.getParameters().getNewtonRaphsonParameters())
|| runningContext.lastOuterLoopResult.status() == OuterLoopStatus.FAILED
|| runningContext.outerLoopTotalIterations >= context.getParameters().getMaxOuterLoopIterations()) {
break;
}
}
} while (runningContext.nrTotalIterations.getValue() > oldNrTotalIterations
&& runningContext.lastSolverResult.getStatus() == AcSolverStatus.CONVERGED
&& lastResultStatusAcceptable(runningContext, context.getParameters().getNewtonRaphsonParameters())
&& runningContext.lastOuterLoopResult.status() != OuterLoopStatus.FAILED
&& runningContext.outerLoopTotalIterations < context.getParameters().getMaxOuterLoopIterations());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2024, 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.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.ac.solver.NewtonRaphsonParameters;
import com.powsybl.openloadflow.equations.*;
import com.powsybl.openloadflow.network.LfNetwork;
import com.powsybl.openloadflow.util.Reports;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
* @author Sebastien Murgey {@literal <sebastien.murgey at rte-france.com>}
*/
public final class UnrealisticVoltageCheck {
private static final Logger LOGGER = LoggerFactory.getLogger(UnrealisticVoltageCheck.class);

private UnrealisticVoltageCheck() {
}

public static boolean isStateUnrealistic(LfNetwork network, EquationSystem<AcVariableType, AcEquationType> equationSystem, NewtonRaphsonParameters parameters, ReportNode reportNode) {
Map<String, Double> busesOutOfNormalVoltageRange = new LinkedHashMap<>();
for (Variable<AcVariableType> v : equationSystem.getIndex().getSortedVariablesToFind()) {
if (v.getType() == AcVariableType.BUS_V && !network.getBus(v.getElementNum()).isFictitious()) {
double value = equationSystem.getStateVector().get(v.getRow());
if (value < parameters.getMinRealisticVoltage() || value > parameters.getMaxRealisticVoltage()) {
busesOutOfNormalVoltageRange.put(network.getBus(v.getElementNum()).getId(), value);
}
}
}
if (!busesOutOfNormalVoltageRange.isEmpty()) {
if (LOGGER.isTraceEnabled()) {
for (var e : busesOutOfNormalVoltageRange.entrySet()) {
LOGGER.trace("Bus '{}' has an unrealistic voltage magnitude: {} pu", e.getKey(), e.getValue());
}
}
LOGGER.error("{} buses have a voltage magnitude out of range [{}, {}]: {}",
busesOutOfNormalVoltageRange.size(), parameters.getMinRealisticVoltage(), parameters.getMaxRealisticVoltage(), busesOutOfNormalVoltageRange);

Reports.reportNewtonRaphsonBusesOutOfRealisticVoltageRange(reportNode, busesOutOfNormalVoltageRange, parameters.getMinRealisticVoltage(), parameters.getMaxRealisticVoltage());
}
return !busesOutOfNormalVoltageRange.isEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import com.powsybl.commons.report.ReportNode;
import com.powsybl.math.matrix.MatrixException;
import com.powsybl.openloadflow.ac.UnrealisticVoltageCheck;
import com.powsybl.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.equations.*;
Expand Down Expand Up @@ -172,30 +173,6 @@ private AcSolverStatus runIteration(StateVectorScaling svScaling, MutableInt ite
}
}

private boolean isStateUnrealistic(ReportNode reportNode) {
Map<String, Double> busesOutOfNormalVoltageRange = new LinkedHashMap<>();
for (Variable<AcVariableType> v : equationSystem.getIndex().getSortedVariablesToFind()) {
if (v.getType() == AcVariableType.BUS_V && !network.getBus(v.getElementNum()).isFictitious()) {
double value = equationSystem.getStateVector().get(v.getRow());
if (value < parameters.getMinRealisticVoltage() || value > parameters.getMaxRealisticVoltage()) {
busesOutOfNormalVoltageRange.put(network.getBus(v.getElementNum()).getId(), value);
}
}
}
if (!busesOutOfNormalVoltageRange.isEmpty()) {
if (LOGGER.isTraceEnabled()) {
for (var e : busesOutOfNormalVoltageRange.entrySet()) {
LOGGER.trace("Bus '{}' has an unrealistic voltage magnitude: {} pu", e.getKey(), e.getValue());
}
}
LOGGER.error("{} buses have a voltage magnitude out of range [{}, {}]: {}",
busesOutOfNormalVoltageRange.size(), parameters.getMinRealisticVoltage(), parameters.getMaxRealisticVoltage(), busesOutOfNormalVoltageRange);

Reports.reportNewtonRaphsonBusesOutOfRealisticVoltageRange(reportNode, busesOutOfNormalVoltageRange, parameters.getMinRealisticVoltage(), parameters.getMaxRealisticVoltage());
}
return !busesOutOfNormalVoltageRange.isEmpty();
}

@Override
public AcSolverResult run(VoltageInitializer voltageInitializer, ReportNode reportNode) {
// initialize state vector
Expand Down Expand Up @@ -236,7 +213,8 @@ public AcSolverResult run(VoltageInitializer voltageInitializer, ReportNode repo
}

// update network state variable
if (status == AcSolverStatus.CONVERGED && isStateUnrealistic(reportNode)) {
if (status == AcSolverStatus.CONVERGED &&
UnrealisticVoltageCheck.isStateUnrealistic(network, equationSystem, parameters, reportNode)) {
status = AcSolverStatus.UNREALISTIC_STATE;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
*/
public class NewtonRaphsonParameters extends AbstractNewtonParameters<NewtonRaphsonParameters> {

public enum UnrealisticVoltageCheckBehavior {
FAIL_AT_UNREALISTIC_STATE_IN_ANY_ITERATION,
FAIL_AT_UNREALISTIC_STATE_IN_FINAL_ITERATION_ONLY
}

public static final UnrealisticVoltageCheckBehavior DEFAULT_UNREALISTIC_VOLTAGE_CHECK_BEHAVIOR = UnrealisticVoltageCheckBehavior.FAIL_AT_UNREALISTIC_STATE_IN_ANY_ITERATION;

public static final int DEFAULT_MAX_ITERATIONS = 15;
public static final double DEFAULT_MIN_REALISTIC_VOLTAGE = 0.5;
public static final double DEFAULT_MAX_REALISTIC_VOLTAGE = 2;
Expand All @@ -24,6 +31,8 @@ public NewtonRaphsonParameters() {
super(DEFAULT_MAX_ITERATIONS);
}

private UnrealisticVoltageCheckBehavior unrealisticVoltageCheckBehavior;

private double minRealisticVoltage = DEFAULT_MIN_REALISTIC_VOLTAGE;

private double maxRealisticVoltage = DEFAULT_MAX_REALISTIC_VOLTAGE;
Expand All @@ -42,6 +51,15 @@ public NewtonRaphsonParameters() {

private boolean alwaysUpdateNetwork = ALWAYS_UPDATE_NETWORK_DEFAULT_VALUE;

public UnrealisticVoltageCheckBehavior getUnrealisticVoltageCheckBehavior() {
return unrealisticVoltageCheckBehavior;
}

public NewtonRaphsonParameters setUnrealisticVoltageCheckBehavior(UnrealisticVoltageCheckBehavior unrealisticVoltageCheckBehavior) {
this.unrealisticVoltageCheckBehavior = unrealisticVoltageCheckBehavior;
return this;
}

public double getMinRealisticVoltage() {
return minRealisticVoltage;
}
Expand Down Expand Up @@ -128,6 +146,7 @@ public NewtonRaphsonParameters setMaxVoltageChangeStateVectorScalingMaxDphi(doub
public String toString() {
return "NewtonRaphsonParameters(" +
"maxIterations=" + maxIterations +
", unrealisticVoltageCheckBehavior=" + unrealisticVoltageCheckBehavior +
", minRealisticVoltage=" + minRealisticVoltage +
", maxRealisticVoltage=" + maxRealisticVoltage +
", stoppingCriteria=" + stoppingCriteria.getClass().getSimpleName() +
Expand Down
Loading

0 comments on commit d874b63

Please sign in to comment.