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 performance of incremental outer loops #895

Merged
merged 22 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from 21 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
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();
Copy link
Member

Choose a reason for hiding this comment

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

@jeandemanged and @geofjamg bug fix here. We have to check the voltage difference against the voltage control of highest priority, most of the time those of the generator and not those of the transformer.

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()
annetill marked this conversation as resolved.
Show resolved Hide resolved
.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(33.0);
Copy link
Member

Choose a reason for hiding this comment

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

Change this targetV to 30.0 to show the benefit of the bug fix.


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
Loading