Skip to content

Commit

Permalink
Improve exhaustive test, make nodebreaker consistent with invalid vie…
Browse files Browse the repository at this point in the history
…ws, simplify code, throw exceptions instead of silently doing nothing

Signed-off-by: HARPER Jon <[email protected]>
  • Loading branch information
jonenst committed Dec 4, 2024
1 parent 6853516 commit 9c17fe0
Show file tree
Hide file tree
Showing 7 changed files with 834 additions and 516 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
package com.powsybl.network.store.iidm.impl;

import com.google.common.util.concurrent.AtomicDouble;
import com.powsybl.commons.config.PlatformConfig;
import com.powsybl.iidm.network.*;
import com.powsybl.network.store.model.*;
Expand All @@ -17,7 +16,6 @@
import org.jgrapht.graph.DirectedPseudograph;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -362,58 +360,50 @@ private void setCalculatedBuses(Resource<VoltageLevelAttributes> voltageLevelRes
}
}

private void getVAndAngleFromConfiguredBus(NetworkObjectIndex index,
Resource<VoltageLevelAttributes> voltageLevelResource,
ConnectedSetResult<T> connectedSet,
AtomicDouble v,
AtomicDouble angle) {
AtomicReference<ConfiguredBusImpl> foundConfiguredBus = new AtomicReference<>();
index.getConfiguredBuses(voltageLevelResource.getId()).forEach(bus -> {
if (foundConfiguredBus.get() == null) {
ConfiguredBusImpl configuredBus = (ConfiguredBusImpl) bus;
configuredBus.getAllTerminals().stream()
.filter(Terminal::isConnected)
.forEach(t -> {
if (foundConfiguredBus.get() == null) {
connectedSet.getConnectedVertices().stream().filter(vertex ->
vertex.getId().equals(t.getConnectable().getId())
).findFirst().ifPresent(vertex -> foundConfiguredBus.set(configuredBus));
}
});
}
});
if (foundConfiguredBus.get() != null) {
v.set(foundConfiguredBus.get().getResource().getAttributes().getV());
angle.set(foundConfiguredBus.get().getResource().getAttributes().getAngle());
}
}

private void getVAndAngleFromOtherView(NetworkObjectIndex index,
Resource<VoltageLevelAttributes> voltageLevelResource,
ConnectedSetResult<T> connectedSet,
AtomicDouble v,
AtomicDouble angle,
boolean isBusView) {
if (voltageLevelResource.getAttributes().getTopologyKind() == TopologyKind.NODE_BREAKER) {
private CalculatedBusAttributes findFirstMatchingNodeBreakerCalculatedBusAttributes(Resource<VoltageLevelAttributes> voltageLevelResource,
ConnectedSetResult<T> connectedSet, boolean isBusView) {
// TODO Some day we may decide to start preserving values in nodebreaker topology after invalidating the views,
// so we could remove this check for isCalculatedBusesValid. Here it controls preserving
// the value accross the other view, we have it to be consistent with preserving values
// from the same view (currently not preserved)
if (voltageLevelResource.getAttributes().isCalculatedBusesValid()) {
List<CalculatedBusAttributes> calculatedBusAttributes = isBusView ? voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView() : voltageLevelResource.getAttributes().getCalculatedBusesForBusView();
if (!CollectionUtils.isEmpty(calculatedBusAttributes)) {
Map<Integer, Integer> nodesToCalculatedBuses = isBusView ? voltageLevelResource.getAttributes().getNodeToCalculatedBusForBusBreakerView() : voltageLevelResource.getAttributes().getNodeToCalculatedBusForBusView();
if (!MapUtils.isEmpty(nodesToCalculatedBuses)) {
Integer node = (Integer) connectedSet.getConnectedNodesOrBuses().iterator().next();
Integer node = (Integer) connectedSet.getConnectedNodesOrBuses().iterator().next(); // non deterministic
Integer busNum = nodesToCalculatedBuses.get(node);
if (busNum != null) {
CalculatedBusAttributes busAttributes = calculatedBusAttributes.get(busNum);
if (busAttributes != null) {
v.set(busAttributes.getV());
angle.set(busAttributes.getAngle());
}
return calculatedBusAttributes.get(busNum);
}
}
}
} else {
// get V and Angle values from configured buses
getVAndAngleFromConfiguredBus(index, voltageLevelResource, connectedSet, v, angle);
}
return null;
}

private CalculatedBusAttributes createCalculatedBusAttributesWithVAndAngle(NetworkObjectIndex index,
Resource<VoltageLevelAttributes> voltageLevelResource,
ConnectedSetResult<T> connectedSet,
boolean isBusView) {
double v = Double.NaN;
double angle = Double.NaN;
if (voltageLevelResource.getAttributes().getTopologyKind() == TopologyKind.NODE_BREAKER) {
CalculatedBusAttributes busAttributes = findFirstMatchingNodeBreakerCalculatedBusAttributes(voltageLevelResource, connectedSet, isBusView);
if (busAttributes != null) {
v = busAttributes.getV();
angle = busAttributes.getAngle();
}
} else { // BUS_BREAKER
// currently for busbreakertopology the values are always preserved
// when using only the busbreakerview. So mimic the behavior and always preserve them
// when using the busview: get V and Angle values from configured buses
String configuredBusId = ((ConnectedSetResult<String>) connectedSet).getConnectedNodesOrBuses().iterator().next(); // nondeterministic
Bus b = index.getConfiguredBus(configuredBusId).orElseThrow();
v = b.getV();
angle = b.getAngle();
}
return new CalculatedBusAttributes(connectedSet.getConnectedVertices(), null, null, v, angle);
}

private CalculationResult<T> getCalculatedBusAttributesList(NetworkObjectIndex index, Resource<VoltageLevelAttributes> voltageLevelResource, boolean isBusView) {
Expand All @@ -423,16 +413,17 @@ private CalculationResult<T> getCalculatedBusAttributesList(NetworkObjectIndex i
calculatedBusAttributesList = isBusView ? voltageLevelResource.getAttributes().getCalculatedBusesForBusView() : voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView();
nodeOrBusToCalculatedBusNum = getNodeOrBusToCalculatedBusNum(voltageLevelResource, isBusView);
} else {
// calculate buses
List<ConnectedSetResult<T>> connectedSetList = findConnectedSetList(index, voltageLevelResource, isBusView);
calculatedBusAttributesList = connectedSetList
.stream()
.map(connectedSet -> {
AtomicDouble v = new AtomicDouble(Double.NaN);
AtomicDouble angle = new AtomicDouble(Double.NaN);
// get V and Angle values from other view if available
getVAndAngleFromOtherView(index, voltageLevelResource, connectedSet, v, angle, isBusView);
return new CalculatedBusAttributes(connectedSet.getConnectedVertices(), null, null, v.get(), angle.get());
})
//TODO in this case in nodebreaker topology we currently don't preserve any values from
//the same view if it was already computed but is invalidated.
//we could do it some day (we need to define good heuristics to
//match previous values to new buses).
//NOTE: We chose to have the same behavior when getting the values from the other view
// get V and Angle values from other view if available
.map(connectedSet -> createCalculatedBusAttributesWithVAndAngle(index, voltageLevelResource, connectedSet, isBusView))
.collect(Collectors.toList());
setCalculatedBuses(voltageLevelResource, isBusView, calculatedBusAttributesList);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
import org.apache.commons.collections4.MapUtils;

import java.util.*;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.function.ObjDoubleConsumer;
import java.util.function.Predicate;
import java.util.function.ToDoubleFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -52,9 +52,6 @@ public final class CalculatedBus implements BaseBus {

private final Function<Terminal, Bus> getBusFromTerminal;

private static final String VOLTAGE = "v";
private static final String ANGLE = "angle";

CalculatedBus(NetworkObjectIndex index, String voltageLevelId, String id, String name, Resource<VoltageLevelAttributes> voltageLevelResource,
int calculatedBusNum, boolean isBusView) {
this.index = Objects.requireNonNull(index);
Expand Down Expand Up @@ -142,24 +139,16 @@ private void setVInCalculatedBus(CalculatedBusAttributes calculatedBusAttributes
calculatedBusAttributes.setV(value);
}

private void setVInConfiguredBus(ConfiguredBusAttributes configuredBusAttributes, double value) {
configuredBusAttributes.setV(value);
}

private double getVInBus(Bus bus) {
return bus.getV();
private void setVInConfiguredBus(ConfiguredBusImpl configuredBus, double value) {
configuredBus.setConfiguredBusV(value);
}

private void setAngleInCalculatedBus(CalculatedBusAttributes calculatedBusAttributes, double value) {
calculatedBusAttributes.setAngle(value);
}

private void setAngleInConfiguredBus(ConfiguredBusAttributes configuredBusAttributes, double value) {
configuredBusAttributes.setAngle(value);
}

private double getAngleInBus(Bus bus) {
return bus.getAngle();
private void setAngleInConfiguredBus(ConfiguredBusImpl configuredBus, double value) {
configuredBus.setConfiguredBusAngle(value);
}

@Override
Expand All @@ -169,70 +158,15 @@ public Bus setV(double v) {

if (getVoltageLevel().getTopologyKind() == TopologyKind.BUS_BREAKER) {
// update V in configured buses
updateConfiguredBuses(v, VOLTAGE, this::getVInBus, this::setVInConfiguredBus);
// this triggers network notifications for 'v' for each configured bus
updateConfiguredBuses(v, this::setVInConfiguredBus);
} else {
// update V for buses in the other view (busView/busBreakerView)
updateBusesAttributes(v, this::setVInCalculatedBus);
}
return this;
}

private void updateBusesAttributes(double value, ObjDoubleConsumer<CalculatedBusAttributes> setValue) {
VoltageLevelAttributes vlAttributes = ((VoltageLevelImpl) getVoltageLevel()).getResource().getAttributes();
Map<Integer, Integer> nodesToCalculatedBuses = isBusView
? vlAttributes.getNodeToCalculatedBusForBusView()
: vlAttributes.getNodeToCalculatedBusForBusBreakerView();
if (!MapUtils.isEmpty(nodesToCalculatedBuses)) {
nodesToCalculatedBuses.entrySet().stream()
.filter(entry -> getCalculatedBusNum() == entry.getValue())
.map(Map.Entry::getKey)
.forEach(node -> {
Map<Integer, Integer> nodesToCalculatedBusesInOtherView = isBusView
? vlAttributes.getNodeToCalculatedBusForBusBreakerView()
: vlAttributes.getNodeToCalculatedBusForBusView();
if (!MapUtils.isEmpty(nodesToCalculatedBusesInOtherView) &&
nodesToCalculatedBusesInOtherView.containsKey(node)) {
int busNumInOtherView = nodesToCalculatedBusesInOtherView.get(node);
List<CalculatedBusAttributes> calculatedBusAttributes = isBusView
? vlAttributes.getCalculatedBusesForBusBreakerView()
: vlAttributes.getCalculatedBusesForBusView();
if (!CollectionUtils.isEmpty(calculatedBusAttributes)) {
setValue.accept(calculatedBusAttributes.get(busNumInOtherView), value);
index.updateVoltageLevelResource(voltageLevelResource, AttributeFilter.SV);
}
}
});
}
}

private void updateConfiguredBuses(double newValue,
String attributeName,
ToDoubleFunction<Bus> getValue,
ObjDoubleConsumer<ConfiguredBusAttributes> setValue) {
List<Bus> buses = ((VoltageLevelImpl) getVoltageLevel()).getResource().getAttributes()
.getBusToCalculatedBusForBusView().entrySet().stream()
.filter(entry -> getCalculatedBusNum() == entry.getValue())
.map(Map.Entry::getKey)
.map(getVoltageLevel().getBusBreakerView()::getBus)
.toList();

Map<Bus, Map.Entry<Double, Double>> oldNewValues = buses.stream()
.collect(Collectors.toMap(
bus -> bus,
bus -> new AbstractMap.SimpleEntry<>(getValue.applyAsDouble(bus), newValue)
));

buses.forEach(bus -> {
setValue.accept(((ConfiguredBusImpl) bus).getResource().getAttributes(), newValue);
index.updateConfiguredBusResource(((ConfiguredBusImpl) bus).getResource(), null);
});

String variantId = index.getNetwork().getVariantManager().getWorkingVariantId();
oldNewValues.forEach((bus, oldNewValue) ->
index.notifyUpdate(bus, attributeName, variantId, oldNewValue.getKey(), oldNewValue.getValue())
);
}

@Override
public double getAngle() {
return getAttributes().getAngle();
Expand All @@ -245,7 +179,8 @@ public Bus setAngle(double angle) {

if (getVoltageLevel().getTopologyKind() == TopologyKind.BUS_BREAKER) {
// update angle in configuredBus
updateConfiguredBuses(angle, ANGLE, this::getAngleInBus, this::setAngleInConfiguredBus);
// this triggers network notifications for 'angle' for each configured bus
updateConfiguredBuses(angle, this::setAngleInConfiguredBus);
} else {
// update angle for buses in the other view (busView/busBreakerView)
updateBusesAttributes(angle, this::setAngleInCalculatedBus);
Expand Down Expand Up @@ -526,10 +461,6 @@ public Stream<Terminal> getAllTerminalsStream() {
.filter(t -> t.getVoltageLevel().getId().equals(getVoltageLevel().getId()) && pred.test(t));
}

public int getCalculatedBusNum() {
return calculatedBusNum;
}

@Override
public <E extends Extension<Bus>> void addExtension(Class<? super E> aClass, E e) {
throw new UnsupportedOperationException("Adding an extension on calculated bus is not authorized");
Expand Down Expand Up @@ -574,4 +505,47 @@ public <E extends Extension<Bus>, B extends ExtensionAdder<Bus, E>> B newExtensi
ExtensionAdderProvider provider = ExtensionAdderProviders.findCachedProvider(getImplementationName(), type);
return (B) provider.newAdder(this);
}

public int getCalculatedBusNum() {
return calculatedBusNum;
}

private void updateBusesAttributes(double value, ObjDoubleConsumer<CalculatedBusAttributes> setValue) {
// busnum of this bus -> nodes in this bus -> busnums in the other view -> buses of the other view
VoltageLevelAttributes vlAttributes = ((VoltageLevelImpl) getVoltageLevel()).getResource().getAttributes();
Map<Integer, Integer> nodesToCalculatedBuses = isBusView
? vlAttributes.getNodeToCalculatedBusForBusView()
: vlAttributes.getNodeToCalculatedBusForBusBreakerView();
Map<Integer, Integer> nodesToCalculatedBusesInOtherView = isBusView
? vlAttributes.getNodeToCalculatedBusForBusBreakerView()
: vlAttributes.getNodeToCalculatedBusForBusView();
if (!MapUtils.isEmpty(nodesToCalculatedBuses) && !MapUtils.isEmpty(nodesToCalculatedBusesInOtherView)) {
for (Entry<Integer, Integer> entry : nodesToCalculatedBuses.entrySet()) {
if (getCalculatedBusNum() == entry.getValue()) {
int node = entry.getKey();
if (nodesToCalculatedBusesInOtherView.containsKey(node)) {
int busNumInOtherView = nodesToCalculatedBusesInOtherView.get(node);
List<CalculatedBusAttributes> calculatedBusAttributes = isBusView
? vlAttributes.getCalculatedBusesForBusBreakerView()
: vlAttributes.getCalculatedBusesForBusView();
if (!CollectionUtils.isEmpty(calculatedBusAttributes)) {
setValue.accept(calculatedBusAttributes.get(busNumInOtherView), value);
index.updateVoltageLevelResource(voltageLevelResource, AttributeFilter.SV);
}
}
}
}
}
}

private void updateConfiguredBuses(double newValue,
ObjDoubleConsumer<ConfiguredBusImpl> setValue) {
VoltageLevelAttributes vlAttributes = ((VoltageLevelImpl) getVoltageLevel()).getResource().getAttributes();
for (Entry<String, Integer> entry : vlAttributes.getBusToCalculatedBusForBusView().entrySet()) {
if (getCalculatedBusNum() == entry.getValue()) {
Bus bus = getVoltageLevel().getBusBreakerView().getBus(entry.getKey());
setValue.accept((ConfiguredBusImpl) bus, newValue);
}
}
}
}
Loading

0 comments on commit 9c17fe0

Please sign in to comment.