Skip to content

Commit

Permalink
Improve performance of incremental outer loops (#895)
Browse files Browse the repository at this point in the history
Signed-off-by: Caio Luke <caioluke97@gmail.com>
Co-authored-by: Anne Tilloy <anne.tilloy@rte-france.com>
  • Loading branch information
caioluke and annetill authored Nov 24, 2023
1 parent efe503c commit 3321a02
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Comparator;
import java.util.List;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
Expand All @@ -47,8 +47,17 @@ public String getName() {
return NAME;
}

public static List<LfBus> getControlledBuses(IncrementalContextData contextData) {
return IncrementalContextData.getControlledBuses(contextData.getCandidateControlledBuses(), VoltageControl.Type.SHUNT);
public static List<LfBus> getControlledBusesOutOfDeadband(IncrementalContextData contextData) {
return IncrementalContextData.getControlledBuses(contextData.getCandidateControlledBuses(), VoltageControl.Type.SHUNT).stream()
.filter(bus -> isOutOfDeadband(bus.getShuntVoltageControl().orElseThrow()))
.collect(Collectors.toList());
}

public static List<LfShunt> getControllerElementsOutOfDeadband(List<LfBus> controlledBusesOutOfDeadband) {
return controlledBusesOutOfDeadband.stream()
.flatMap(bus -> bus.getShuntVoltageControl().orElseThrow().getMergedControllerElements().stream())
.filter(Predicate.not(LfShunt::isDisabled))
.collect(Collectors.toList());
}

public static List<LfShunt> getControllerElements(IncrementalContextData contextData) {
Expand Down Expand Up @@ -94,7 +103,10 @@ private static int[] createControllerShuntIndex(LfNetwork network, List<LfShunt>
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());
int nRows = equationSystem.getIndex().getSortedEquationsToSolve().size();
int nColumns = controllerShunts.size();

DenseMatrix rhs = new DenseMatrix(nRows, nColumns);
for (LfShunt controllerShunt : controllerShunts) {
equationSystem.getEquation(controllerShunt.getNum(), AcEquationType.SHUNT_TARGET_B)
.ifPresent(equation -> rhs.set(equation.getColumn(), controllerShuntIndex[controllerShunt.getNum()], 1d));
Expand Down Expand Up @@ -148,6 +160,27 @@ private void adjustB(ShuntVoltageControl voltageControl, List<LfShunt> sortedCon
}
}

private static double getDiffV(ShuntVoltageControl voltageControl) {
double targetV = voltageControl.getControlledBus().getHighestPriorityMainVoltageControl().orElseThrow().getTargetValue();
double v = voltageControl.getControlledBus().getV();
return targetV - v;
}

private static boolean isOutOfDeadband(ShuntVoltageControl voltageControl) {
double diffV = getDiffV(voltageControl);
double halfTargetDeadband = getHalfTargetDeadband(voltageControl);
boolean outOfDeadband = Math.abs(diffV) > halfTargetDeadband;
if (outOfDeadband) {
List<LfShunt> controllers = voltageControl.getMergedControllerElements().stream()
.filter(shunt -> !shunt.isDisabled())
.collect(Collectors.toList());
LOGGER.trace("Controlled bus '{}' ({} controllers) is outside of its deadband (half is {} kV) and could need a voltage adjustment of {} kV",
voltageControl.getControlledBus().getId(), controllers.size(), halfTargetDeadband * voltageControl.getControlledBus().getNominalV(),
diffV * voltageControl.getControlledBus().getNominalV());
}
return outOfDeadband;
}

@Override
public OuterLoopStatus check(AcOuterLoopContext context, Reporter reporter) {
MutableObject<OuterLoopStatus> status = new MutableObject<>(OuterLoopStatus.STABLE);
Expand All @@ -156,20 +189,27 @@ public OuterLoopStatus check(AcOuterLoopContext context, Reporter reporter) {
AcLoadFlowContext loadFlowContext = context.getLoadFlowContext();
var contextData = (IncrementalContextData) context.getData();

List<LfShunt> controllerShunts = getControllerElements(contextData);
SensitivityContext sensitivityContext = new SensitivityContext(network, controllerShunts,
// check which shunts are not within their deadbands
List<LfBus> controlledBusesOutOfDeadband = getControlledBusesOutOfDeadband(contextData);
List<LfShunt> controllerShuntsOutOfDeadband = getControllerElementsOutOfDeadband(controlledBusesOutOfDeadband);

// all shunts are within their deadbands
if (controllerShuntsOutOfDeadband.isEmpty()) {
return status.getValue();
}

SensitivityContext sensitivityContext = new SensitivityContext(network, controllerShuntsOutOfDeadband,
loadFlowContext.getEquationSystem(), loadFlowContext.getJacobianMatrix());

getControlledBuses(contextData)
.forEach(controlledBus -> {
ShuntVoltageControl voltageControl = controlledBus.getShuntVoltageControl().orElseThrow();
double diffV = controlledBus.getHighestPriorityMainVoltageControl().orElseThrow().getTargetValue() - voltageControl.getControlledBus().getV();
List<LfShunt> sortedControllers = voltageControl.getMergedControllerElements().stream()
.filter(shunt -> !shunt.isDisabled())
.sorted(Comparator.comparingDouble(LfShunt::getBMagnitude).reversed())
.collect(Collectors.toList());
adjustB(voltageControl, sortedControllers, controlledBus, contextData, sensitivityContext, diffV, status);
});
controlledBusesOutOfDeadband.forEach(controlledBus -> {
ShuntVoltageControl voltageControl = controlledBus.getShuntVoltageControl().orElseThrow();
double diffV = getDiffV(voltageControl);
List<LfShunt> sortedControllers = voltageControl.getMergedControllerElements().stream()
.filter(shunt -> !shunt.isDisabled())
.sorted(Comparator.comparingDouble(LfShunt::getBMagnitude).reversed())
.collect(Collectors.toList());
adjustB(voltageControl, sortedControllers, controlledBus, contextData, sensitivityContext, diffV, status);
});
return status.getValue();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
Expand All @@ -56,8 +54,17 @@ public String getName() {
return NAME;
}

public static List<LfBus> getControlledBuses(IncrementalContextData contextData) {
return IncrementalContextData.getControlledBuses(contextData.getCandidateControlledBuses(), VoltageControl.Type.TRANSFORMER);
public static List<LfBus> getControlledBusesOutOfDeadband(IncrementalContextData contextData) {
return IncrementalContextData.getControlledBuses(contextData.getCandidateControlledBuses(), VoltageControl.Type.TRANSFORMER).stream()
.filter(bus -> isOutOfDeadband(bus.getTransformerVoltageControl().orElseThrow()))
.collect(Collectors.toList());
}

public static List<LfBranch> getControllerElementsOutOfDeadband(List<LfBus> controlledBusesOutOfDeadband) {
return controlledBusesOutOfDeadband.stream()
.flatMap(bus -> bus.getTransformerVoltageControl().orElseThrow().getMergedControllerElements().stream())
.filter(Predicate.not(LfBranch::isDisabled))
.collect(Collectors.toList());
}

public static List<LfBranch> getControllerElements(IncrementalContextData contextData) {
Expand Down Expand Up @@ -93,7 +100,10 @@ public SensitivityContext(LfNetwork network, List<LfBranch> controllerBranches,
private static DenseMatrix calculateSensitivityValues(List<LfBranch> controllerBranches, int[] controllerBranchIndex,
EquationSystem<AcVariableType, AcEquationType> equationSystem,
JacobianMatrix<AcVariableType, AcEquationType> j) {
DenseMatrix rhs = new DenseMatrix(equationSystem.getIndex().getSortedEquationsToSolve().size(), controllerBranches.size());
int nRows = equationSystem.getIndex().getSortedEquationsToSolve().size();
int nColumns = controllerBranches.size();

DenseMatrix rhs = new DenseMatrix(nRows, nColumns);
for (LfBranch controllerBranch : controllerBranches) {
equationSystem.getEquation(controllerBranch.getNum(), AcEquationType.BRANCH_TARGET_RHO1)
.ifPresent(equation -> rhs.set(equation.getColumn(), controllerBranchIndex[controllerBranch.getNum()], 1d));
Expand Down Expand Up @@ -191,11 +201,26 @@ private boolean adjustWithSeveralControllers(List<LfBranch> controllerBranches,
}

private static double getDiffV(TransformerVoltageControl voltageControl) {
double targetV = voltageControl.getTargetValue();
double targetV = voltageControl.getControlledBus().getHighestPriorityMainVoltageControl().orElseThrow().getTargetValue();
double v = voltageControl.getControlledBus().getV();
return targetV - v;
}

private static boolean isOutOfDeadband(TransformerVoltageControl voltageControl) {
double diffV = getDiffV(voltageControl);
double halfTargetDeadband = getHalfTargetDeadband(voltageControl);
boolean outOfDeadband = Math.abs(diffV) > halfTargetDeadband;
if (outOfDeadband) {
List<LfBranch> controllers = voltageControl.getMergedControllerElements().stream()
.filter(b -> !b.isDisabled())
.collect(Collectors.toList());
LOGGER.trace("Controlled bus '{}' ({} controllers) is outside of its deadband (half is {} kV) and could need a voltage adjustment of {} kV",
voltageControl.getControlledBus().getId(), controllers.size(), halfTargetDeadband * voltageControl.getControlledBus().getNominalV(),
diffV * voltageControl.getControlledBus().getNominalV());
}
return outOfDeadband;
}

@Override
public OuterLoopStatus check(AcOuterLoopContext context, Reporter reporter) {
MutableObject<OuterLoopStatus> status = new MutableObject<>(OuterLoopStatus.STABLE);
Expand All @@ -204,49 +229,49 @@ public OuterLoopStatus check(AcOuterLoopContext context, Reporter reporter) {
AcLoadFlowContext loadFlowContext = context.getLoadFlowContext();
var contextData = (IncrementalContextData) context.getData();

List<LfBranch> controllerBranches = getControllerElements(contextData);
SensitivityContext sensitivityContext = new SensitivityContext(network, controllerBranches,
// filter out buses/branches which are outside their deadbands
List<LfBus> controlledBusesOutOfDeadband = getControlledBusesOutOfDeadband(contextData);
List<LfBranch> controllerBranchesOutOfDeadband = getControllerElementsOutOfDeadband(controlledBusesOutOfDeadband);

// all branches are within their deadbands
if (controllerBranchesOutOfDeadband.isEmpty()) {
return status.getValue();
}

SensitivityContext sensitivityContext = new SensitivityContext(network, controllerBranchesOutOfDeadband,
loadFlowContext.getEquationSystem(), loadFlowContext.getJacobianMatrix());

// for synthetics logs
List<String> controlledBusesOutsideOfDeadband = new ArrayList<>();
List<String> controlledBusesAdjusted = new ArrayList<>();
List<String> controlledBusesWithAllItsControllersToLimit = new ArrayList<>();

List<LfBus> controlledBuses = getControlledBuses(contextData);

controlledBuses.forEach(controlledBus -> {
controlledBusesOutOfDeadband.forEach(controlledBus -> {
TransformerVoltageControl voltageControl = controlledBus.getTransformerVoltageControl().orElseThrow();
double diffV = getDiffV(voltageControl);
double halfTargetDeadband = getHalfTargetDeadband(voltageControl);
if (Math.abs(diffV) > halfTargetDeadband) {
controlledBusesOutsideOfDeadband.add(controlledBus.getId());
List<LfBranch> controllers = voltageControl.getMergedControllerElements().stream()
.filter(b -> !b.isDisabled())
.collect(Collectors.toList());
LOGGER.trace("Controlled bus '{}' ({} controllers) is outside of its deadband (half is {} kV) and could need a voltage adjustment of {} kV",
controlledBus.getId(), controllers.size(), halfTargetDeadband * controlledBus.getNominalV(), diffV * controlledBus.getNominalV());
boolean adjusted;
if (controllers.size() == 1) {
adjusted = adjustWithOneController(controllers.get(0), controlledBus, contextData, sensitivityContext, diffV, controlledBusesWithAllItsControllersToLimit);
} else {
adjusted = adjustWithSeveralControllers(controllers, controlledBus, contextData, sensitivityContext, diffV, halfTargetDeadband, controlledBusesWithAllItsControllersToLimit);
}
if (adjusted) {
controlledBusesAdjusted.add(controlledBus.getId());
status.setValue(OuterLoopStatus.UNSTABLE);
}
List<LfBranch> controllers = voltageControl.getMergedControllerElements().stream()
.filter(b -> !b.isDisabled())
.collect(Collectors.toList());
boolean adjusted;
if (controllers.size() == 1) {
adjusted = adjustWithOneController(controllers.get(0), controlledBus, contextData, sensitivityContext, diffV, controlledBusesWithAllItsControllersToLimit);
} else {
adjusted = adjustWithSeveralControllers(controllers, controlledBus, contextData, sensitivityContext, diffV, halfTargetDeadband, controlledBusesWithAllItsControllersToLimit);
}
if (adjusted) {
controlledBusesAdjusted.add(controlledBus.getId());
status.setValue(OuterLoopStatus.UNSTABLE);
}
});

if (!controlledBusesOutsideOfDeadband.isEmpty() && LOGGER.isInfoEnabled()) {
Map<String, Double> largestMismatches = controlledBuses.stream()
if (!controlledBusesOutOfDeadband.isEmpty() && LOGGER.isInfoEnabled()) {
Map<String, Double> largestMismatches = controlledBusesOutOfDeadband.stream()
.map(controlledBus -> Pair.of(controlledBus.getId(), Math.abs(getDiffV(controlledBus.getTransformerVoltageControl().orElseThrow()) * controlledBus.getNominalV())))
.sorted((p1, p2) -> Double.compare(p2.getRight(), p1.getRight()))
.limit(3) // 3 largest
.collect(Collectors.toMap(Pair::getLeft, Pair::getRight, (key1, key2) -> key1, LinkedHashMap::new));
LOGGER.info("{} controlled bus voltages are outside of their target deadband, largest ones are: {}",
controlledBusesOutsideOfDeadband.size(), largestMismatches);
controlledBusesOutOfDeadband.size(), largestMismatches);
}
if (!controlledBusesAdjusted.isEmpty()) {
LOGGER.info("{} controlled bus voltages have been adjusted by changing at least one tap",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,85 @@ void remoteVoltageControlT2wtTest2() {
assertEquals(3, t2wt.getRatioTapChanger().getTapPosition());
}

@Test
void testIncrementalVoltageControlWithGenerator() {
selectNetwork(VoltageControlNetworkFactory.createNetworkWithT2wt());

Substation substation = network.newSubstation()
.setId("SUBSTATION4")
.setCountry(Country.FR)
.add();
VoltageLevel vl4 = substation.newVoltageLevel()
.setId("VL_4")
.setNominalV(33.0)
.setLowVoltageLimit(0)
.setHighVoltageLimit(100)
.setTopologyKind(TopologyKind.BUS_BREAKER)
.add();
Bus bus4 = vl4.getBusBreakerView().newBus()
.setId("BUS_4")
.add();
vl4.newLoad()
.setId("LOAD_4")
.setBus("BUS_4")
.setP0(2.)
.setQ0(0.5)
.add();

Line line34 = network.newLine()
.setId("LINE_34")
.setBus1("BUS_3")
.setBus2("BUS_4")
.setR(1.05)
.setX(10.0)
.setG1(0.0000005)
.add();

parameters.setTransformerVoltageControlOn(true);
parametersExt.setTransformerVoltageControlMode(OpenLoadFlowParameters.TransformerVoltageControlMode.INCREMENTAL_VOLTAGE_CONTROL);

t2wt.getRatioTapChanger()
.setTargetDeadband(0)
.setRegulating(true)
.setTapPosition(0)
.setRegulationTerminal(line34.getTerminal2())
.setTargetV(30.0);

Generator g4 = vl4.newGenerator()
.setId("GEN_4")
.setBus("BUS_4")
.setMinP(0.0)
.setMaxP(30)
.setTargetP(5)
.setTargetV(33)
.setVoltageRegulatorOn(true)
.add();

// Generator reactive capability is enough to hold voltage target
LoadFlowResult result = loadFlowRunner.run(network, parameters);
assertTrue(result.isOk());
assertVoltageEquals(33, bus4);
assertEquals(0, t2wt.getRatioTapChanger().getTapPosition());
assertReactivePowerEquals(-7.110, g4.getTerminal());

g4.newMinMaxReactiveLimits().setMinQ(-3.5).setMaxQ(3.5).add();
// Generator reactive capability is not enough to hold voltage target and rtc is deactivated
t2wt.getRatioTapChanger().setRegulating(false);
LoadFlowResult result2 = loadFlowRunner.run(network, parameters);
assertTrue(result2.isOk());
assertVoltageEquals(31.032, bus4);
assertEquals(0, t2wt.getRatioTapChanger().getTapPosition());
assertReactivePowerEquals(-3.5, g4.getTerminal());

// Generator reactive capability is not enough to hold voltage alone but with rtc it is ok
t2wt.getRatioTapChanger().setRegulating(true);
LoadFlowResult result3 = loadFlowRunner.run(network, parameters);
assertTrue(result3.isOk());
assertVoltageEquals(33, bus4);
assertEquals(1, t2wt.getRatioTapChanger().getTapPosition());
assertReactivePowerEquals(-1.172, g4.getTerminal());
}

@Test
void nonSupportedVoltageControlT2wtTest() {
selectNetwork(VoltageControlNetworkFactory.createNetworkWithT2wt());
Expand Down

0 comments on commit 3321a02

Please sign in to comment.