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

Add an option that allow checking unrealistic voltage only on final state of the loadflow. #1087

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@murgeyseb can you add the documentation of the new parameter you have introduced. Or I can do it if you prefer. Just tell me !

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