-
Notifications
You must be signed in to change notification settings - Fork 8
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
[WIP] Automatic fix of incompatible target voltages #1115
base: main
Are you sure you want to change the base?
Changes from 14 commits
b6d59c7
6aa5b8a
e62d1b8
965ae71
ea0be1f
b211915
ab8e4b1
50dd365
021766d
fe48f54
486201c
f03e043
bf367f8
499e6fb
ba46ea8
a4563b1
660c2e4
e978193
14acfb3
7ea9f6f
9488317
cb87f85
70c4326
ac807c9
6fa1020
1557c2c
bf94e9c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
/** | ||
* 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; | ||
|
||
import com.google.common.base.Stopwatch; | ||
import com.powsybl.openloadflow.adm.AdmittanceEquationSystem; | ||
import com.powsybl.openloadflow.adm.AdmittanceMatrix; | ||
import com.powsybl.openloadflow.equations.VariableSet; | ||
import com.powsybl.openloadflow.network.LfBranch; | ||
import com.powsybl.openloadflow.network.LfBus; | ||
import com.powsybl.openloadflow.network.LfNetwork; | ||
import org.apache.commons.lang3.mutable.MutableInt; | ||
import org.apache.commons.lang3.tuple.Pair; | ||
import org.jgrapht.Graph; | ||
import org.jgrapht.graph.Pseudograph; | ||
import org.jgrapht.traverse.BreadthFirstIterator; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>} | ||
*/ | ||
public class TargetVoltageCompatibilityChecker { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(TargetVoltageCompatibilityChecker.class); | ||
|
||
private final LfNetwork network; | ||
|
||
vidaldid-rte marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public TargetVoltageCompatibilityChecker(LfNetwork network) { | ||
this.network = Objects.requireNonNull(network); | ||
} | ||
|
||
private static Graph<LfBus, LfBranch> createGraph(LfNetwork lfNetwork) { | ||
Graph<LfBus, LfBranch> graph = new Pseudograph<>(LfBranch.class); | ||
for (LfBranch branch : lfNetwork.getBranches()) { | ||
LfBus bus1 = branch.getBus1(); | ||
LfBus bus2 = branch.getBus2(); | ||
if (bus1 != null && bus2 != null && !branch.isDisabled()) { | ||
if (!graph.containsVertex(bus1)) { | ||
graph.addVertex(bus1); | ||
} | ||
if (!graph.containsVertex(bus2)) { | ||
graph.addVertex(bus2); | ||
} | ||
graph.addEdge(bus1, bus2, branch); | ||
} | ||
} | ||
return graph; | ||
} | ||
|
||
private static Set<LfBus> exploreNeighbors(Graph<LfBus, LfBranch> graph, LfBus bus, int maxDepth) { | ||
var it = new BreadthFirstIterator<>(graph, bus); | ||
Set<LfBus> neighbors = new HashSet<>(); | ||
while (it.hasNext()) { | ||
LfBus b = it.next(); | ||
int currentDepth = it.getDepth(b); | ||
if (currentDepth > maxDepth) { | ||
break; | ||
} | ||
if (b != bus) { | ||
neighbors.add(b); | ||
} | ||
} | ||
return neighbors; | ||
} | ||
|
||
public List<Pair<LfBus, LfBus>> check(TargetVoltageCompatibilityCheckerParameters parameters) { | ||
Stopwatch stopwatch = Stopwatch.createStarted(); | ||
|
||
Graph<LfBus, LfBranch> graph = createGraph(network); | ||
|
||
Set<LfBus> controlledBuses = network.getBuses().stream() | ||
.filter(bus -> !bus.isDisabled() | ||
&& bus.isVoltageControlled() | ||
&& bus.getVoltageControls().stream().anyMatch(vc -> !vc.isDisabled())) | ||
.collect(Collectors.toSet()); | ||
|
||
List<Pair<LfBus, LfBus>> incompatibleControlledBuses = new ArrayList<>(); | ||
|
||
var ySystem = AdmittanceEquationSystem.create(network, new VariableSet<>()); | ||
try (var y = AdmittanceMatrix.create(ySystem, parameters.getMatrixFactory())) { | ||
for (LfBus controlledBus : controlledBuses) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't you compute the impedance between each pair of bus twice ? if you iterate on all buses then on all neighbours ? Also, would it accerlerate things if there was an option to check a pair only if at least one of the two buses is remotely controlled ? [This is where we have convergence issues] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we are checking potentially twice. It could be improved indeed. |
||
Set<LfBus> neighborControlledBuses = exploreNeighbors(graph, controlledBus, parameters.getControlledBusNeighborsExplorationDepth()) | ||
.stream().filter(controlledBuses::contains) | ||
.collect(Collectors.toSet()); | ||
if (!neighborControlledBuses.isEmpty()) { | ||
for (LfBus neighborControlledBus : neighborControlledBuses) { | ||
double z = y.getZ(controlledBus, neighborControlledBus).abs(); | ||
double dv = Math.abs(controlledBus.getHighestPriorityTargetV().orElseThrow() - neighborControlledBus.getHighestPriorityTargetV().orElseThrow()); | ||
double targetVoltagePlausibility = dv / z; | ||
if (targetVoltagePlausibility > parameters.getTargetVoltagePlausibilityThreshold()) { | ||
incompatibleControlledBuses.add(Pair.of(controlledBus, neighborControlledBus)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
LOGGER.debug("Target voltage compatibility checked in {} ms", stopwatch.elapsed().toMillis()); | ||
|
||
return incompatibleControlledBuses; | ||
} | ||
|
||
public void fix(TargetVoltageCompatibilityCheckerParameters parameters) { | ||
List<Pair<LfBus, LfBus>> incompatibleControlledBuses = check(parameters); | ||
// some buses could be part of multiple target v incompatible buses couples | ||
// fix the most referenced ones | ||
Map<LfBus, MutableInt> incompatibleControlledBusRefCount = new HashMap<>(); | ||
for (Pair<LfBus, LfBus> p : incompatibleControlledBuses) { | ||
incompatibleControlledBusRefCount.computeIfAbsent(p.getLeft(), k -> new MutableInt(0)).increment(); | ||
incompatibleControlledBusRefCount.computeIfAbsent(p.getRight(), k -> new MutableInt(0)).increment(); | ||
} | ||
Set<LfBus> fixedControlledBuses = new HashSet<>(); | ||
for (Pair<LfBus, LfBus> p : incompatibleControlledBuses) { | ||
LfBus controlledBus1 = p.getLeft(); | ||
LfBus controlledBus2 = p.getRight(); | ||
if (fixedControlledBuses.contains(controlledBus1) || fixedControlledBuses.contains(controlledBus2)) { | ||
continue; | ||
} | ||
LfBus controlledBusToFix = incompatibleControlledBusRefCount.get(controlledBus1).intValue() > incompatibleControlledBusRefCount.get(controlledBus2).intValue() | ||
? controlledBus1 : controlledBus2; | ||
for (var voltageControl : controlledBusToFix.getVoltageControls()) { | ||
LOGGER.warn("Controlled buses '{}' and '{}' have incompatible target voltages ({} and {}): disable voltage control of '{}'", | ||
controlledBus1.getId(), controlledBus2.getId(), controlledBus1.getHighestPriorityTargetV().orElseThrow() * controlledBus1.getNominalV(), | ||
controlledBus2.getHighestPriorityTargetV().orElseThrow() * controlledBus2.getNominalV(), controlledBusToFix.getId()); | ||
voltageControl.setDisabled(true); | ||
fixedControlledBuses.add(controlledBusToFix); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/** | ||
* 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; | ||
|
||
import com.powsybl.math.matrix.MatrixFactory; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>} | ||
*/ | ||
public class TargetVoltageCompatibilityCheckerParameters { | ||
|
||
public static final int CONTROLLED_BUS_NEIGHBORS_EXPLORATION_DEPTH_DEFAULT_VALUE = 2; | ||
|
||
public static final double TARGET_VOLTAGE_PLAUSIBILITY_THRESHOLD_DEFAULT_VALUE = 10; | ||
|
||
private MatrixFactory matrixFactory; | ||
|
||
private int controlledBusNeighborsExplorationDepth = CONTROLLED_BUS_NEIGHBORS_EXPLORATION_DEPTH_DEFAULT_VALUE; | ||
|
||
private double targetVoltagePlausibilityThreshold = TARGET_VOLTAGE_PLAUSIBILITY_THRESHOLD_DEFAULT_VALUE; | ||
|
||
public MatrixFactory getMatrixFactory() { | ||
return matrixFactory; | ||
} | ||
|
||
public void setMatrixFactory(MatrixFactory matrixFactory) { | ||
this.matrixFactory = Objects.requireNonNull(matrixFactory); | ||
} | ||
|
||
public TargetVoltageCompatibilityCheckerParameters(MatrixFactory matrixFactory) { | ||
this.matrixFactory = Objects.requireNonNull(matrixFactory); | ||
} | ||
|
||
public int getControlledBusNeighborsExplorationDepth() { | ||
return controlledBusNeighborsExplorationDepth; | ||
} | ||
|
||
public TargetVoltageCompatibilityCheckerParameters setControlledBusNeighborsExplorationDepth(int controlledBusNeighborsExplorationDepth) { | ||
this.controlledBusNeighborsExplorationDepth = controlledBusNeighborsExplorationDepth; | ||
return this; | ||
} | ||
|
||
public double getTargetVoltagePlausibilityThreshold() { | ||
return targetVoltagePlausibilityThreshold; | ||
} | ||
|
||
public TargetVoltageCompatibilityCheckerParameters setTargetVoltagePlausibilityThreshold(double targetVoltagePlausibilityThreshold) { | ||
this.targetVoltagePlausibilityThreshold = targetVoltagePlausibilityThreshold; | ||
return this; | ||
} | ||
|
||
public String toString() { | ||
return "TargetVoltageCompatibilityCheckerParameters(" + | ||
"controlledBusNeighborsExplorationDepth=" + controlledBusNeighborsExplorationDepth + | ||
", targetVoltagePlausibilityThreshold=" + targetVoltagePlausibilityThreshold + | ||
", matrixFactory=" + matrixFactory.getClass().getSimpleName() + | ||
")"; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be documented.
And maybe addiotional parameters given like TARGET_VOLTAGE_PLAUSIBILITY_THRESHOLD
However in my test networks, increasing CONTROLLED_BUS_NEIGHBORS_EXPLORATION_DEPTH had a terrible algotirhmic cost and didn't improve quality (defined as more convergence)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"And maybe addiotional parameters given like TARGET_VOLTAGE_PLAUSIBILITY_THRESHOLD" => yes
for CONTROLLED_BUS_NEIGHBORS_EXPLORATION_DEPTH indeed it should not be configurable, a value of 2 or 3 is probably enough.