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

Shunt incremental voltage control #692

Merged
merged 30 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1b2e1d9
Beginning
Hadrien-Godard Jan 5, 2023
aa520a6
[WIP] continuing
Hadrien-Godard Jan 5, 2023
6495437
[WIP] continuing
Hadrien-Godard Jan 6, 2023
df75000
[WIP] : fix some issues and use a given order for shunts and controllers
Hadrien-Godard Jan 6, 2023
6195001
Merge branch 'main' into shunt_incremental_Voltage_control
Hadrien-Godard Jan 12, 2023
4d23066
[WIP] Align with transformer voltage control outer loop
Hadrien-Godard Jan 12, 2023
47bdbd1
Basic tests ok
Hadrien-Godard Jan 12, 2023
577e7c3
Merge remote-tracking branch 'origin/main' into shunt_incremental_Vol…
Hadrien-Godard Jan 16, 2023
b48bff8
WIP test
Hadrien-Godard Jan 16, 2023
da105c9
New tests
Hadrien-Godard Jan 16, 2023
ee4ed7a
Merge remote-tracking branch 'origin/main' into shunt_incremental_Vol…
Hadrien-Godard Jan 16, 2023
0bd17ed
Fix targetdeadband at controller level and new tests
Hadrien-Godard Jan 19, 2023
fe4c529
Merge remote-tracking branch 'origin/main' into shunt_incremental_Vol…
Hadrien-Godard Jan 19, 2023
ed901a7
Fix merge
Hadrien-Godard Jan 19, 2023
9389aa1
Fix test
Hadrien-Godard Jan 20, 2023
cad338b
Merge branch 'main' into shunt_incremental_Voltage_control
Hadrien-Godard Jan 23, 2023
4098293
Fix merge
Hadrien-Godard Jan 23, 2023
2898504
Merge branch 'main' into shunt_incremental_Voltage_control
Hadrien-Godard Jan 23, 2023
06228c2
Merge.
annetill Jan 24, 2023
7e45cda
Clean config.
annetill Jan 24, 2023
2acfcac
Move deadband to control.
annetill Jan 24, 2023
2ae77d7
Refactoring.
annetill Jan 24, 2023
7ae1b90
Clean.
annetill Jan 24, 2023
a424abb
Clean
geofjamg Jan 25, 2023
6e03da1
Clean
geofjamg Jan 25, 2023
40bff50
Fix copyright
geofjamg Jan 25, 2023
67a0ae0
Refactor
geofjamg Jan 25, 2023
2adafc3
Fix.
annetill Jan 25, 2023
d3244a1
Refactor
geofjamg Jan 25, 2023
e3d9ab3
Refactor
geofjamg Jan 25, 2023
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 @@ -47,7 +47,13 @@ public List<OuterLoop> configure(LoadFlowParameters parameters, OpenLoadFlowPara
}
}
if (parameters.isShuntCompensatorVoltageControlOn()) {
outerLoops.add(new ShuntVoltageControlOuterLoop());
if (parametersExt.getShuntVoltageControlMode() == OpenLoadFlowParameters.ShuntVoltageControlMode.WITH_GENERATOR_VOLTAGE_CONTROL) {
outerLoops.add(new ShuntVoltageControlOuterLoop());
} else if (parametersExt.getShuntVoltageControlMode() == OpenLoadFlowParameters.ShuntVoltageControlMode.INCREMENTAL_VOLTAGE_CONTROL) {
outerLoops.add(new IncrementalShuntVoltageControlOuterLoop());
} else {
throw new IllegalStateException("Unknown shunt voltage control mode: " + parametersExt.getShuntVoltageControlMode());
}
}
return outerLoops;
}
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/powsybl/openloadflow/OpenLoadFlowParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ public class OpenLoadFlowParameters extends AbstractExtension<LoadFlowParameters

public static final String TRANSFORMER_VOLTAGE_CONTROL_MODE_NAME = "transformerVoltageControlMode";

public static final String SHUNT_VOLTAGE_CONTROL_MODE_NAME = "shuntVoltageControlMode";

public static final String DC_POWER_FACTOR_NAME = "dcPowerFactor";

public static final String MIN_PLAUSIBLE_TARGET_VOLTAGE_NAME = "minPlausibleTargetVoltage";
Expand Down Expand Up @@ -129,6 +131,7 @@ public class OpenLoadFlowParameters extends AbstractExtension<LoadFlowParameters
NEWTON_RAPHSON_CONV_EPS_PER_EQ_NAME,
VOLTAGE_INIT_MODE_OVERRIDE_NAME,
TRANSFORMER_VOLTAGE_CONTROL_MODE_NAME,
SHUNT_VOLTAGE_CONTROL_MODE_NAME,
DC_POWER_FACTOR_NAME,
MIN_PLAUSIBLE_TARGET_VOLTAGE_NAME,
MAX_PLAUSIBLE_TARGET_VOLTAGE_NAME,
Expand All @@ -155,8 +158,15 @@ public enum TransformerVoltageControlMode {
INCREMENTAL_VOLTAGE_CONTROL
}

public enum ShuntVoltageControlMode {
WITH_GENERATOR_VOLTAGE_CONTROL,
INCREMENTAL_VOLTAGE_CONTROL
}

public static final TransformerVoltageControlMode TRANSFORMER_VOLTAGE_CONTROL_MODE_DEFAULT_VALUE = TransformerVoltageControlMode.WITH_GENERATOR_VOLTAGE_CONTROL;
Copy link
Member

Choose a reason for hiding this comment

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

Both default values of voltage control for transformers and for shunts can be changed in the future, if the incremental outerloops are mature enough.


public static final ShuntVoltageControlMode SHUNT_VOLTAGE_CONTROL_MODE_DEFAULT_VALUE = ShuntVoltageControlMode.WITH_GENERATOR_VOLTAGE_CONTROL;

private SlackBusSelectionMode slackBusSelectionMode = SLACK_BUS_SELECTION_DEFAULT_VALUE;

private List<String> slackBusesIds = Collections.emptyList();
Expand Down Expand Up @@ -190,6 +200,8 @@ public enum LowImpedanceBranchMode {

private TransformerVoltageControlMode transformerVoltageControlMode = TRANSFORMER_VOLTAGE_CONTROL_MODE_DEFAULT_VALUE;

private ShuntVoltageControlMode shuntVoltageControlMode = SHUNT_VOLTAGE_CONTROL_MODE_DEFAULT_VALUE;

private double dcPowerFactor = DC_POWER_FACTOR_DEFAULT_VALUE;

private double minPlausibleTargetVoltage = LfNetworkParameters.MIN_PLAUSIBLE_TARGET_VOLTAGE_DEFAULT_VALUE;
Expand Down Expand Up @@ -357,6 +369,15 @@ public OpenLoadFlowParameters setTransformerVoltageControlMode(TransformerVoltag
return this;
}

public ShuntVoltageControlMode getShuntVoltageControlMode() {
return shuntVoltageControlMode;
}

public OpenLoadFlowParameters setShuntVoltageControlMode(ShuntVoltageControlMode shuntVoltageControlMode) {
this.shuntVoltageControlMode = Objects.requireNonNull(shuntVoltageControlMode);
return this;
}

public double getDcPowerFactor() {
return dcPowerFactor;
}
Expand Down Expand Up @@ -483,6 +504,7 @@ public static OpenLoadFlowParameters load(PlatformConfig platformConfig) {
.setNewtonRaphsonConvEpsPerEq(config.getDoubleProperty(NEWTON_RAPHSON_CONV_EPS_PER_EQ_NAME, DefaultNewtonRaphsonStoppingCriteria.DEFAULT_CONV_EPS_PER_EQ))
.setVoltageInitModeOverride(config.getEnumProperty(VOLTAGE_INIT_MODE_OVERRIDE_NAME, VoltageInitModeOverride.class, VOLTAGE_INIT_MODE_OVERRIDE_DEFAULT_VALUE))
.setTransformerVoltageControlMode(config.getEnumProperty(TRANSFORMER_VOLTAGE_CONTROL_MODE_NAME, TransformerVoltageControlMode.class, TRANSFORMER_VOLTAGE_CONTROL_MODE_DEFAULT_VALUE))
.setShuntVoltageControlMode(config.getEnumProperty(SHUNT_VOLTAGE_CONTROL_MODE_NAME, ShuntVoltageControlMode.class, SHUNT_VOLTAGE_CONTROL_MODE_DEFAULT_VALUE))
.setDcPowerFactor(config.getDoubleProperty(DC_POWER_FACTOR_NAME, DC_POWER_FACTOR_DEFAULT_VALUE))
.setMinPlausibleTargetVoltage(config.getDoubleProperty(MIN_PLAUSIBLE_TARGET_VOLTAGE_NAME, LfNetworkParameters.MIN_PLAUSIBLE_TARGET_VOLTAGE_DEFAULT_VALUE))
.setMaxPlausibleTargetVoltage(config.getDoubleProperty(MAX_PLAUSIBLE_TARGET_VOLTAGE_NAME, LfNetworkParameters.MAX_PLAUSIBLE_TARGET_VOLTAGE_DEFAULT_VALUE))
Expand Down Expand Up @@ -531,6 +553,8 @@ public OpenLoadFlowParameters update(Map<String, String> properties) {
.ifPresent(prop -> this.setVoltageInitModeOverride(VoltageInitModeOverride.valueOf(prop)));
Optional.ofNullable(properties.get(TRANSFORMER_VOLTAGE_CONTROL_MODE_NAME))
.ifPresent(prop -> this.setTransformerVoltageControlMode(TransformerVoltageControlMode.valueOf(prop)));
Optional.ofNullable(properties.get(SHUNT_VOLTAGE_CONTROL_MODE_NAME))
.ifPresent(prop -> this.setShuntVoltageControlMode(ShuntVoltageControlMode.valueOf(prop)));
Optional.ofNullable(properties.get(DC_POWER_FACTOR_NAME))
.ifPresent(prop -> this.setDcPowerFactor(Double.parseDouble(prop)));
Optional.ofNullable(properties.get(MIN_PLAUSIBLE_TARGET_VOLTAGE_NAME))
Expand Down Expand Up @@ -573,6 +597,7 @@ public String toString() {
", newtonRaphsonConvEpsPerEq=" + newtonRaphsonConvEpsPerEq +
", voltageInitModeOverride=" + voltageInitModeOverride +
", transformerVoltageControlMode=" + transformerVoltageControlMode +
", shuntVoltageControlMode=" + shuntVoltageControlMode +
", dcPowerFactor=" + dcPowerFactor +
", minPlausibleTargetVoltage=" + minPlausibleTargetVoltage +
", maxPlausibleTargetVoltage=" + maxPlausibleTargetVoltage +
Expand Down Expand Up @@ -865,6 +890,7 @@ public static boolean equals(LoadFlowParameters parameters1, LoadFlowParameters
extension1.getNewtonRaphsonConvEpsPerEq() == extension2.getNewtonRaphsonConvEpsPerEq() &&
extension1.getVoltageInitModeOverride() == extension2.getVoltageInitModeOverride() &&
extension1.getTransformerVoltageControlMode() == extension2.getTransformerVoltageControlMode() &&
extension1.getShuntVoltageControlMode() == extension2.getShuntVoltageControlMode() &&
extension1.getDcPowerFactor() == extension2.getDcPowerFactor() &&
extension1.getMinPlausibleTargetVoltage() == extension2.getMinPlausibleTargetVoltage() &&
extension1.getMaxPlausibleTargetVoltage() == extension2.getMaxPlausibleTargetVoltage() &&
Expand Down Expand Up @@ -913,6 +939,7 @@ public static LoadFlowParameters clone(LoadFlowParameters parameters) {
.setNewtonRaphsonConvEpsPerEq(extension.getNewtonRaphsonConvEpsPerEq())
.setVoltageInitModeOverride(extension.getVoltageInitModeOverride())
.setTransformerVoltageControlMode(extension.getTransformerVoltageControlMode())
.setShuntVoltageControlMode(extension.getShuntVoltageControlMode())
.setDcPowerFactor(extension.getDcPowerFactor())
.setMinPlausibleTargetVoltage(extension.getMinPlausibleTargetVoltage())
.setMaxPlausibleTargetVoltage(extension.getMaxPlausibleTargetVoltage())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/**
* Copyright (c) 2021, 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/.
*/
package com.powsybl.openloadflow.ac;

import com.powsybl.commons.reporter.Reporter;
import com.powsybl.math.matrix.DenseMatrix;
import com.powsybl.openloadflow.ac.equations.AcEquationType;
import com.powsybl.openloadflow.ac.equations.AcVariableType;
import com.powsybl.openloadflow.ac.outerloop.OuterLoop;
import com.powsybl.openloadflow.ac.outerloop.OuterLoopContext;
import com.powsybl.openloadflow.ac.outerloop.OuterLoopStatus;
import com.powsybl.openloadflow.equations.EquationSystem;
import com.powsybl.openloadflow.equations.EquationTerm;
import com.powsybl.openloadflow.equations.JacobianMatrix;
import com.powsybl.openloadflow.network.*;
import com.powsybl.openloadflow.network.impl.LfShuntImpl;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.mutable.MutableObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

/**
* @author Hadrien Godard <hadrien.godard at artelys.com>
*/

public class IncrementalShuntVoltageControlOuterLoop implements OuterLoop {

private static final Logger LOGGER = LoggerFactory.getLogger(IncrementalShuntVoltageControlOuterLoop.class);

private static final int MAX_DIRECTION_CHANGE = 2;
annetill marked this conversation as resolved.
Show resolved Hide resolved

private static final class ControllerContext {

private final MutableInt directionChangeCount = new MutableInt();

private LfShuntImpl.Controller.AllowedDirection allowedDirection = LfShuntImpl.Controller.AllowedDirection.BOTH;

public MutableInt getDirectionChangeCount() {
return directionChangeCount;
}

private LfShuntImpl.Controller.AllowedDirection getAllowedDirection() {
return allowedDirection;
}

private void setAllowedDirection(LfShuntImpl.Controller.AllowedDirection allowedDirection) {
this.allowedDirection = Objects.requireNonNull(allowedDirection);
}
}

private static final class ContextData {

private final Map<String, ControllerContext> controllersContexts = new HashMap<>();

private Map<String, ControllerContext> getControllersContexts() {
return controllersContexts;
}
}

private static List<LfShunt> getControllerShunts(LfNetwork network) {
return network.getBuses().stream()
.flatMap(bus -> bus.getControllerShunt().stream())
.filter(controllerShunt -> !controllerShunt.isDisabled() && controllerShunt.hasVoltageControlCapability())
.collect(Collectors.toList());
}

protected static boolean checkTargetDeadband(Double targetDeadband, double difference) {
return (targetDeadband != null && Math.abs(difference) > targetDeadband / 2) || targetDeadband == null;
}

@Override
public String getType() {
return "Incremental Shunt voltage control";
}

@Override
public void initialize(OuterLoopContext context) {
var contextData = new ContextData();
context.setData(contextData);

// All shunt voltage control are disabled for the first equation system resolution.
for (LfShunt shunt : getControllerShunts(context.getNetwork())) {
shunt.getVoltageControl().ifPresent(voltageControl -> shunt.setVoltageControlEnabled(false));
// FIX ME: not safe casting
for (LfShuntImpl.Controller lfShuntController : ((LfShuntImpl) shunt).getControllers()) {
contextData.getControllersContexts().put(lfShuntController.getId(), new ControllerContext());
}
}
}

private static void updateAllowedDirection(ControllerContext controllerContext, LfShuntImpl.Controller.Direction direction) {
if (controllerContext.getDirectionChangeCount().getValue() <= MAX_DIRECTION_CHANGE) {
if (!controllerContext.getAllowedDirection().equals(direction.getAllowedDirection())) {
// both vs increase or decrease
// increase vs decrease or decrease vs increase
controllerContext.getDirectionChangeCount().increment();
}
controllerContext.setAllowedDirection(direction.getAllowedDirection());
}
}

private void adjustB(List<LfShunt> sortedControllerShunts, LfBus controlledBus, ContextData contextData, int[] controllerShuntIndex,
DenseMatrix sensitivities, double diffV, MutableObject<OuterLoopStatus> status) {
// several shunts control the same bus
double remainingDiffV = diffV;
boolean hasChanged = true;
while (hasChanged) {
hasChanged = false;

Choose a reason for hiding this comment

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

This is not a conform code, there has to be a better way to do it

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes sure. But please explain what is the best way to avoid it, or link to documents that describe it.

for (LfShunt controllerShunt : sortedControllerShunts) {
double sensitivity = ((EquationTerm<AcVariableType, AcEquationType>) controlledBus.getCalculatedV())
.calculateSensi(sensitivities, controllerShuntIndex[controllerShunt.getNum()]);
// FIX ME: Not safe casting
LfShuntImpl controllerShuntImpl = (LfShuntImpl) controllerShunt;
double targetDeadband = controllerShuntImpl.getShuntVoltageControlTargetDeadband().orElse(null);
// Not very efficient because sorting is performed at each iteration. However, in practical should not be an issue.
// Considering storing the controlers sorted already as same order is used everywhere else
for (LfShuntImpl.Controller controller : controllerShuntImpl.getControllers().stream().sorted(Comparator.comparing(LfShuntImpl.Controller::getBMagnitude)).collect(Collectors.toList())) {
var controllerContext = contextData.getControllersContexts().get(controller.getId());
if (checkTargetDeadband(targetDeadband, remainingDiffV)) {
double previousB = controller.getB();
double deltaB = remainingDiffV / sensitivity;
LfShuntImpl.Controller.Direction direction = controller.updateTapPositionB(deltaB, 1, controllerContext.getAllowedDirection()).orElse(null);
if (direction != null) {
updateAllowedDirection(controllerContext, direction);
remainingDiffV -= (controller.getB() - previousB) * sensitivity;
hasChanged = true;
status.setValue(OuterLoopStatus.UNSTABLE);
LOGGER.debug("Increment shunt susceptance of '{}': {} -> {}", controller.getId(), previousB, controller.getB());

Choose a reason for hiding this comment

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

Susceptence value

Copy link
Member Author

Choose a reason for hiding this comment

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

Just to check, this comment means that it is more proper to write "Increment shunt susceptance value of" in the log?

Choose a reason for hiding this comment

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

yes

}
} else {
LOGGER.trace("Controller shunt '{}' is in its deadband: deadband {} vs voltage difference {}", controllerShunt.getId(), targetDeadband, Math.abs(diffV));
}
}
if (hasChanged) {
controllerShuntImpl.updateB();
controllerShuntImpl.updateG();
for (LfNetworkListener listener : controllerShunt.getNetwork().getListeners()) {
listener.onShuntTargetBChange(controllerShunt, controllerShunt.getB());
}
}
}
}
}

@Override
public OuterLoopStatus check(OuterLoopContext context, Reporter reporter) {
MutableObject<OuterLoopStatus> status = new MutableObject<>(OuterLoopStatus.STABLE);

LfNetwork network = context.getNetwork();
List<LfShunt> controllerShunts = getControllerShunts(network);
int[] controllerShuntIndex = new int[network.getShunts().size()];
for (int i = 0; i < controllerShunts.size(); i++) {
LfShunt controllerShunt = controllerShunts.get(i);
controllerShuntIndex[controllerShunt.getNum()] = i;
}

DenseMatrix sensitivities = calculateSensitivityValues(controllerShunts, controllerShuntIndex,
context.getAcLoadFlowContext().getEquationSystem(),
context.getAcLoadFlowContext().getJacobianMatrix());

var contextData = (IncrementalShuntVoltageControlOuterLoop.ContextData) context.getData();

network.getBuses().stream()
.filter(LfBus::isShuntVoltageControlled)
.forEach(controlledBus -> {
ShuntVoltageControl voltageControl = controlledBus.getShuntVoltageControl().orElseThrow();
double targetV = voltageControl.getTargetValue();
double v = voltageControl.getControlled().getV();
double diffV = targetV - v;
List<LfShunt> sortedControllers = voltageControl.getControllers().stream().sorted(Comparator.comparing(LfShunt::getBMagnitude)).collect(Collectors.toList());
Copy link
Member

Choose a reason for hiding this comment

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

@Hadrien-Godard do we need a .reversed() here? What do you want?

adjustB(sortedControllers, controlledBus, contextData, controllerShuntIndex, sensitivities, diffV, status);
});
return status.getValue();
}

private static DenseMatrix calculateSensitivityValues(List<LfShunt> controllerShunts, int[] controllerShuntIndex,
EquationSystem<AcVariableType, AcEquationType> equationSystem,
JacobianMatrix<AcVariableType, AcEquationType> j) {
DenseMatrix rhs = new DenseMatrix(equationSystem.getIndex().getSortedEquationsToSolve().size(), controllerShunts.size());
for (LfShunt controllerShunt : controllerShunts) {
equationSystem.getEquation(controllerShunt.getNum(), AcEquationType.SHUNT_TARGET_B)
.ifPresent(equation -> rhs.set(equation.getColumn(), controllerShuntIndex[controllerShunt.getNum()], 1d));
}
j.solveTransposed(rhs);
return rhs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ private void adjustWithOneController(LfBranch controllerBranch, LfBus controlled
double deltaR1 = diffV / sensitivity;
controllerBranch.getPiModel().updateTapPositionR1(deltaR1, MAX_TAP_SHIFT, controllerContext.getAllowedDirection()).ifPresent(direction -> {
updateAllowedDirection(controllerContext, direction);
LOGGER.debug("Round voltage ratio of '{}': {} -> {}", controllerBranch.getId(), previousR1, controllerBranch.getPiModel().getR1());
LOGGER.debug("Increment voltage ratio of '{}': {} -> {}", controllerBranch.getId(), previousR1, controllerBranch.getPiModel().getR1());
status.setValue(OuterLoopStatus.UNSTABLE);
});
} else {
Expand Down Expand Up @@ -134,7 +134,7 @@ private void adjustWithSeveralControllers(List<LfBranch> controllerBranches, LfB
remainingDiffV -= (controllerBranch.getPiModel().getR1() - previousR1) * sensitivity;
hasChanged = true;
status.setValue(OuterLoopStatus.UNSTABLE);
LOGGER.debug("[Shared control] round voltage ratio of '{}': {} -> {}", controllerBranch.getId(), previousR1, controllerBranch.getPiModel().getR1());
LOGGER.debug("[Shared control] increment voltage ratio of '{}': {} -> {}", controllerBranch.getId(), previousR1, controllerBranch.getPiModel().getR1());
}
} else {
LOGGER.trace("Controller branch '{}' is in its deadband: deadband {} vs voltage difference {}", controllerBranch.getId(), targetDeadband, Math.abs(diffV));
Expand Down
Loading