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

Synchronization between views for v and angle bus values. #452

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
*/
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.*;
import org.apache.commons.collections4.CollectionUtils;
import org.jgrapht.Graph;
import org.jgrapht.alg.connectivity.ConnectivityInspector;
import org.jgrapht.graph.DirectedPseudograph;

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

/**
Expand Down Expand Up @@ -358,18 +362,80 @@ private void setCalculatedBuses(Resource<VoltageLevelAttributes> voltageLevelRes
}
}

private void getVAndAngleFromConfiguredBus(NetworkObjectIndex index,
Resource<VoltageLevelAttributes> voltageLevelResource,
ConnectedSetResult<T> connectedSet,
AtomicDouble v,
AtomicDouble angle) {
index.getConfiguredBuses(voltageLevelResource.getId()).forEach(bus -> {
ConfiguredBusImpl configuredBus = (ConfiguredBusImpl) bus;
AtomicReference<ConfiguredBusImpl> foundConfiguredBus = new AtomicReference<>();
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) {
AtomicBoolean foundInCalculatedBuses = new AtomicBoolean(false);
List<CalculatedBusAttributes> calculatedBusAttributes = isBusView ?
voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView() :
voltageLevelResource.getAttributes().getCalculatedBusesForBusView();
if (!CollectionUtils.isEmpty(calculatedBusAttributes)) {
connectedSet.getConnectedVertices().forEach(vertex -> {
List<CalculatedBusAttributes> busesInOtherView = calculatedBusAttributes.stream().filter(attr -> attr.getVertices().contains(vertex)).toList();
if (!CollectionUtils.isEmpty(busesInOtherView)) {
busesInOtherView.forEach(b -> {
if (Double.isNaN(v.get()) && !Double.isNaN(b.getV())) {
v.set(b.getV());
foundInCalculatedBuses.set(true);
}
if (Double.isNaN(angle.get()) && !Double.isNaN(b.getAngle())) {
angle.set(b.getAngle());
foundInCalculatedBuses.set(true);
}
});
}
});
}
if (isBusView && !foundInCalculatedBuses.get()) {
// get V and Angle values from configured buses
getVAndAngleFromConfiguredBus(index, voltageLevelResource, connectedSet, v, angle);
}
}

private CalculationResult<T> getCalculatedBusAttributesList(NetworkObjectIndex index, Resource<VoltageLevelAttributes> voltageLevelResource, boolean isBusView) {
List<CalculatedBusAttributes> calculatedBusAttributesList;
Map<T, Integer> nodeOrBusToCalculatedBusNum;
if (isCalculatedBusesValid(voltageLevelResource, isBusView)) {
calculatedBusAttributesList = isBusView ? voltageLevelResource.getAttributes().getCalculatedBusesForBusView() : voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView();
nodeOrBusToCalculatedBusNum = getNodeOrBusToCalculatedBusNum(voltageLevelResource, isBusView);
} else {
// calculate buses
List<ConnectedSetResult<T>> connectedSetList = findConnectedSetList(index, voltageLevelResource, isBusView);
AtomicDouble v = new AtomicDouble(Double.NaN);
AtomicDouble angle = new AtomicDouble(Double.NaN);
Copy link
Collaborator

Choose a reason for hiding this comment

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

v set in all remaining buses

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah I see this bug made the AbstractMainConnectedComponentWithSwitchTest work because there is a bus without any vertex which would be unset, but since it's setting v in all remaining buses it sets it anyway.

This is all to do with the fact that we can not use the equipment Vertex (=non switch equipment) to match buses, we need to use the topology vertex (nodes/configuredbus)

calculatedBusAttributesList = connectedSetList
.stream()
.map(connectedSet -> new CalculatedBusAttributes(connectedSet.getConnectedVertices(), null, null, Double.NaN, Double.NaN))
.map(connectedSet -> {
// 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());
})
.collect(Collectors.toList());
setCalculatedBuses(voltageLevelResource, isBusView, calculatedBusAttributesList);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
import com.powsybl.iidm.network.util.Networks;
import com.powsybl.network.store.model.*;
import lombok.EqualsAndHashCode;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
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 @@ -48,6 +51,9 @@ 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 @@ -131,13 +137,94 @@ public double getV() {
return getAttributes().getV();
}

private void setVInCalculatedBus(CalculatedBusAttributes calculatedBusAttributes, double value) {
calculatedBusAttributes.setV(value);
}

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

private double getVInBus(Bus bus) {
return bus.getV();
}

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();
}

@Override
public Bus setV(double v) {
getAttributes().setV(v);
index.updateVoltageLevelResource(voltageLevelResource, AttributeFilter.SV);

if (getVoltageLevel().getTopologyKind() == TopologyKind.BUS_BREAKER) {
// update V in configured buses
updateConfiguredBuses(v, getAttributes(), VOLTAGE, this::getVInBus, this::setVInConfiguredBus);
} else {
if (isBusView) {
// update V for buses in BusBreakerView
updateBusesAttributes(v, voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView(), getAttributes(), this::setVInCalculatedBus);
} else {
// update V for buses in BusView
updateBusesAttributes(v, voltageLevelResource.getAttributes().getCalculatedBusesForBusView(), getAttributes(), this::setVInCalculatedBus);
}
}
return this;
}

private void updateBusesAttributes(double value,
List<CalculatedBusAttributes> calculatedBusAttributesList,
CalculatedBusAttributes sourceBusAttributes,
ObjDoubleConsumer<CalculatedBusAttributes> setValue) {
if (!CollectionUtils.isEmpty(calculatedBusAttributesList)) {
calculatedBusAttributesList.forEach(busToUpdate -> busToUpdate.getVertices().forEach(vertex1 ->
sourceBusAttributes.getVertices().stream().filter(v -> v.getId().equals(vertex1.getId())).findFirst().ifPresent(vertex2 -> {
setValue.accept(busToUpdate, value);
index.updateVoltageLevelResource(voltageLevelResource, AttributeFilter.SV);
})
));
}
}

private void updateConfiguredBuses(double newValue,
CalculatedBusAttributes calculatedBusAttributesBus,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
CalculatedBusAttributes calculatedBusAttributesBus,
CalculatedBusAttributes calculatedBusAttributes,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

String attributeName,
ToDoubleFunction<Bus> getValue,
ObjDoubleConsumer<ConfiguredBusAttributes> setValue) {
List<String> busesIds = calculatedBusAttributesBus.getVertices().stream()
.map(Vertex::getBus)
.toList();

List<Bus> buses = index.getConfiguredBuses().stream()
Copy link
Collaborator

Choose a reason for hiding this comment

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

all configured bus of the network ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right.
We use now only the buses in voltage level bus breaker view ...

.filter(bus -> busesIds.contains(bus.getId()) && !Objects.equals(getValue.applyAsDouble(bus), newValue))
.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 @@ -147,6 +234,19 @@ public double getAngle() {
public Bus setAngle(double angle) {
getAttributes().setAngle(angle);
index.updateVoltageLevelResource(voltageLevelResource, AttributeFilter.SV);

if (getVoltageLevel().getTopologyKind() == TopologyKind.BUS_BREAKER) {
// update angle in configuredBus
updateConfiguredBuses(angle, getAttributes(), ANGLE, this::getAngleInBus, this::setAngleInConfiguredBus);
} else {
if (isBusView) {
// update angle for Bus in BusBreakerView
updateBusesAttributes(angle, voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView(), getAttributes(), this::setAngleInCalculatedBus);
} else {
// update angle for Bus in BusView
updateBusesAttributes(angle, voltageLevelResource.getAttributes().getCalculatedBusesForBusView(), getAttributes(), this::setAngleInCalculatedBus);
}
}
return this;
}

Expand Down Expand Up @@ -423,6 +523,10 @@ 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 @@ -467,8 +571,4 @@ 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@
import com.powsybl.iidm.network.ValidationException;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.iidm.network.VscConverterStation;
import com.powsybl.network.store.model.AttributeFilter;
import com.powsybl.network.store.model.CalculatedBusAttributes;
import com.powsybl.network.store.model.ConfiguredBusAttributes;
import com.powsybl.network.store.model.Resource;
import org.apache.commons.collections4.CollectionUtils;

import java.util.List;
import java.util.Optional;
import java.util.function.ObjDoubleConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
Expand Down Expand Up @@ -77,6 +82,40 @@ public double getAngle() {
return getResource().getAttributes().getAngle();
}

private void setVInCalculatedBus(CalculatedBusAttributes calculatedBusAttributes, double value) {
calculatedBusAttributes.setV(value);
}

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

private void updateCalculatedBusAttributes(double newValue,
String voltageLevelId,
ObjDoubleConsumer<CalculatedBusAttributes> setValue) {
Optional<VoltageLevelImpl> voltageLevelOpt = index.getVoltageLevel(voltageLevelId);

voltageLevelOpt.ifPresent(voltageLevel -> {
List<CalculatedBusAttributes> calculatedBusAttributesList = voltageLevel.getResource()
.getAttributes()
.getCalculatedBusesForBusView();

if (CollectionUtils.isNotEmpty(calculatedBusAttributesList)) {
List<Integer> calculatedBusesNum = getAllTerminals().stream()
.filter(Terminal::isConnected)
.map(t -> ((CalculatedBus) t.getBusView().getBus()).getCalculatedBusNum()).distinct().toList();
Copy link
Collaborator

Choose a reason for hiding this comment

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

all terminals in one configured bus always map to the same busviewbus no ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right.
We consider now only the first bus in bus view ...

List<CalculatedBusAttributes> busesToUpdateList = IntStream.range(0, calculatedBusAttributesList.size())
.filter(calculatedBusesNum::contains)
.mapToObj(calculatedBusAttributesList::get)
.toList();
if (CollectionUtils.isNotEmpty(busesToUpdateList)) {
busesToUpdateList.forEach(b -> setValue.accept(b, newValue));
index.updateVoltageLevelResource(voltageLevel.getResource(), AttributeFilter.SV);
}
}
});
}

@Override
public Bus setV(double v) {
if (v < 0) {
Expand All @@ -87,6 +126,9 @@ public Bus setV(double v) {
updateResource(res -> res.getAttributes().setV(v));
String variantId = index.getNetwork().getVariantManager().getWorkingVariantId();
index.notifyUpdate(this, "v", variantId, oldValue, v);

// update V for bus in BusView
updateCalculatedBusAttributes(v, getResource().getAttributes().getVoltageLevelId(), this::setVInCalculatedBus);
}
return this;
}
Expand All @@ -98,6 +140,9 @@ public Bus setAngle(double angle) {
updateResource(res -> res.getAttributes().setAngle(angle));
String variantId = index.getNetwork().getVariantManager().getWorkingVariantId();
index.notifyUpdate(this, "angle", variantId, oldValue, angle);

// update angle for bus in BusView
updateCalculatedBusAttributes(angle, getResource().getAttributes().getVoltageLevelId(), this::setAngleInCalculatedBus);
}
return this;
}
Expand Down
Loading
Loading