Skip to content

Commit e9f18ad

Browse files
committed
add more checks for isCalculatedBusValid for updates and code improvements from reviews
Signed-off-by: HARPER Jon <[email protected]>
1 parent 9c17fe0 commit e9f18ad

File tree

4 files changed

+96
-62
lines changed

4 files changed

+96
-62
lines changed

network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/AbstractTopology.java

+32-22
Original file line numberDiff line numberDiff line change
@@ -362,21 +362,26 @@ private void setCalculatedBuses(Resource<VoltageLevelAttributes> voltageLevelRes
362362

363363
private CalculatedBusAttributes findFirstMatchingNodeBreakerCalculatedBusAttributes(Resource<VoltageLevelAttributes> voltageLevelResource,
364364
ConnectedSetResult<T> connectedSet, boolean isBusView) {
365-
// TODO Some day we may decide to start preserving values in nodebreaker topology after invalidating the views,
366-
// so we could remove this check for isCalculatedBusesValid. Here it controls preserving
367-
// the value accross the other view, we have it to be consistent with preserving values
368-
// from the same view (currently not preserved)
369-
if (voltageLevelResource.getAttributes().isCalculatedBusesValid()) {
370-
List<CalculatedBusAttributes> calculatedBusAttributes = isBusView ? voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView() : voltageLevelResource.getAttributes().getCalculatedBusesForBusView();
371-
if (!CollectionUtils.isEmpty(calculatedBusAttributes)) {
372-
Map<Integer, Integer> nodesToCalculatedBuses = isBusView ? voltageLevelResource.getAttributes().getNodeToCalculatedBusForBusBreakerView() : voltageLevelResource.getAttributes().getNodeToCalculatedBusForBusView();
373-
if (!MapUtils.isEmpty(nodesToCalculatedBuses)) {
374-
Integer node = (Integer) connectedSet.getConnectedNodesOrBuses().iterator().next(); // non deterministic
375-
Integer busNum = nodesToCalculatedBuses.get(node);
376-
if (busNum != null) {
377-
return calculatedBusAttributes.get(busNum);
378-
}
379-
}
365+
// TODO Some day we may decide to start preserving phase/angle values
366+
// in nodebreaker topology even after invalidating the views, so we
367+
// could remove the check for isCalculatedBusesValid. Here it controls
368+
// whether we preserve or not the phase/angle values accross the other
369+
// view. For now we do not preserve to be consistent with the behavior
370+
// of not preserving values from the same view after invalidation.
371+
List<CalculatedBusAttributes> calculatedBusAttributesInOtherView = isBusView ? voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView() : voltageLevelResource.getAttributes().getCalculatedBusesForBusView();
372+
Map<Integer, Integer> nodesToCalculatedBusesInOtherView = isBusView ? voltageLevelResource.getAttributes().getNodeToCalculatedBusForBusBreakerView() : voltageLevelResource.getAttributes().getNodeToCalculatedBusForBusView();
373+
Set<Integer> nodes = (Set<Integer>) connectedSet.getConnectedNodesOrBuses();
374+
if (voltageLevelResource.getAttributes().isCalculatedBusesValid()
375+
&& !CollectionUtils.isEmpty(calculatedBusAttributesInOtherView)
376+
&& !MapUtils.isEmpty(nodesToCalculatedBusesInOtherView)
377+
&& !nodes.isEmpty()) {
378+
// busNumInOtherView is deterministic for the busbreakerview because all busbreakerviewbuses correspond
379+
// to the same busviewbus. For the busview, busNumInOtherView will be non deterministic, it will
380+
// be one of the busbreakerbuses of this busviewbus.
381+
Integer node = (Integer) nodes.iterator().next();
382+
Integer busNumInOtherView = nodesToCalculatedBusesInOtherView.get(node);
383+
if (busNumInOtherView != null) {
384+
return calculatedBusAttributesInOtherView.get(busNumInOtherView);
380385
}
381386
}
382387
return null;
@@ -395,13 +400,18 @@ private CalculatedBusAttributes createCalculatedBusAttributesWithVAndAngle(Netwo
395400
angle = busAttributes.getAngle();
396401
}
397402
} else { // BUS_BREAKER
398-
// currently for busbreakertopology the values are always preserved
399-
// when using only the busbreakerview. So mimic the behavior and always preserve them
400-
// when using the busview: get V and Angle values from configured buses
401-
String configuredBusId = ((ConnectedSetResult<String>) connectedSet).getConnectedNodesOrBuses().iterator().next(); // nondeterministic
402-
Bus b = index.getConfiguredBus(configuredBusId).orElseThrow();
403-
v = b.getV();
404-
angle = b.getAngle();
403+
// currently for busbreakertopology the phase/angle values are preserved
404+
// when set in the busbreakerview which is in a sense always valid.
405+
// So mimic the behavior and always preserve them also in the busview
406+
// by *not* testing for isCalculatedBusesValid.
407+
Set<String> configuredBusesIds = (Set<String>) connectedSet.getConnectedNodesOrBuses();
408+
if (!configuredBusesIds.isEmpty()) {
409+
// nondeterministic, chooses a random configuredbus in this busviewbus
410+
String configuredBusId = configuredBusesIds.iterator().next();
411+
Bus b = index.getConfiguredBus(configuredBusId).orElseThrow(IllegalStateException::new);
412+
v = b.getV();
413+
angle = b.getAngle();
414+
}
405415
}
406416
return new CalculatedBusAttributes(connectedSet.getConnectedVertices(), null, null, v, angle);
407417
}

network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CalculatedBus.java

+34-15
Original file line numberDiff line numberDiff line change
@@ -511,27 +511,39 @@ public int getCalculatedBusNum() {
511511
}
512512

513513
private void updateBusesAttributes(double value, ObjDoubleConsumer<CalculatedBusAttributes> setValue) {
514-
// busnum of this bus -> nodes in this bus -> busnums in the other view -> buses of the other view
514+
// Use the busnum of this bus to get the nodes in this bus to get the
515+
// busnums in the other view to get the buses of the other view to
516+
// update them all. For the busbreakerview, there is only one matching bus in the busview so return early.
517+
// We only update when isCalculatedBusesValid is true, there is no point in updating stale bus objects and
518+
// in when isCalculatedBusesValid is not true, we may even update the wrong buses (but not much of a problem
519+
// because they are stale objects).
520+
// TODO add tests for updates with isCalculatedBusesValid=false
521+
// NOTE: we don't maintain a mapping from busnum to nodes so we iterate
522+
// all the nodes and filter but it should be ok, the number is small. TODO, is this really ok ?
515523
VoltageLevelAttributes vlAttributes = ((VoltageLevelImpl) getVoltageLevel()).getResource().getAttributes();
516524
Map<Integer, Integer> nodesToCalculatedBuses = isBusView
517525
? vlAttributes.getNodeToCalculatedBusForBusView()
518526
: vlAttributes.getNodeToCalculatedBusForBusBreakerView();
519527
Map<Integer, Integer> nodesToCalculatedBusesInOtherView = isBusView
520528
? vlAttributes.getNodeToCalculatedBusForBusBreakerView()
521529
: vlAttributes.getNodeToCalculatedBusForBusView();
522-
if (!MapUtils.isEmpty(nodesToCalculatedBuses) && !MapUtils.isEmpty(nodesToCalculatedBusesInOtherView)) {
530+
List<CalculatedBusAttributes> calculatedBusAttributes = isBusView
531+
? vlAttributes.getCalculatedBusesForBusBreakerView()
532+
: vlAttributes.getCalculatedBusesForBusView();
533+
if (vlAttributes.isCalculatedBusesValid() && !CollectionUtils.isEmpty(calculatedBusAttributes)
534+
&& !MapUtils.isEmpty(nodesToCalculatedBuses) && !MapUtils.isEmpty(nodesToCalculatedBusesInOtherView)) {
535+
Set<Integer> seen = new HashSet<>();
523536
for (Entry<Integer, Integer> entry : nodesToCalculatedBuses.entrySet()) {
524537
if (getCalculatedBusNum() == entry.getValue()) {
525538
int node = entry.getKey();
526-
if (nodesToCalculatedBusesInOtherView.containsKey(node)) {
527-
int busNumInOtherView = nodesToCalculatedBusesInOtherView.get(node);
528-
List<CalculatedBusAttributes> calculatedBusAttributes = isBusView
529-
? vlAttributes.getCalculatedBusesForBusBreakerView()
530-
: vlAttributes.getCalculatedBusesForBusView();
531-
if (!CollectionUtils.isEmpty(calculatedBusAttributes)) {
532-
setValue.accept(calculatedBusAttributes.get(busNumInOtherView), value);
533-
index.updateVoltageLevelResource(voltageLevelResource, AttributeFilter.SV);
534-
}
539+
Integer busNumInOtherView = nodesToCalculatedBusesInOtherView.get(node);
540+
if (busNumInOtherView != null && !seen.contains(busNumInOtherView)) {
541+
setValue.accept(calculatedBusAttributes.get(busNumInOtherView), value);
542+
index.updateVoltageLevelResource(voltageLevelResource, AttributeFilter.SV);
543+
seen.add(busNumInOtherView);
544+
}
545+
if (!isBusView) {
546+
return;
535547
}
536548
}
537549
}
@@ -540,11 +552,18 @@ private void updateBusesAttributes(double value, ObjDoubleConsumer<CalculatedBus
540552

541553
private void updateConfiguredBuses(double newValue,
542554
ObjDoubleConsumer<ConfiguredBusImpl> setValue) {
555+
// update all the configured buses
556+
// NOTE: we don't maintain a mapping from busnum to bus so we iterate
557+
// all the buses and filter but it should be ok, the number is small. TODO, is this really ok ?
558+
// We only update when isCalculatedBusesValid is true, otherwise we may update the wrong configured bus
559+
// TODO add tests for updates with isCalculatedBusesValid=false
543560
VoltageLevelAttributes vlAttributes = ((VoltageLevelImpl) getVoltageLevel()).getResource().getAttributes();
544-
for (Entry<String, Integer> entry : vlAttributes.getBusToCalculatedBusForBusView().entrySet()) {
545-
if (getCalculatedBusNum() == entry.getValue()) {
546-
Bus bus = getVoltageLevel().getBusBreakerView().getBus(entry.getKey());
547-
setValue.accept((ConfiguredBusImpl) bus, newValue);
561+
if (vlAttributes.isCalculatedBusesValid()) {
562+
for (Entry<String, Integer> entry : vlAttributes.getBusToCalculatedBusForBusView().entrySet()) {
563+
if (getCalculatedBusNum() == entry.getValue()) {
564+
ConfiguredBusImpl bus = index.getConfiguredBus(entry.getKey()).orElseThrow(IllegalStateException::new);
565+
setValue.accept(bus, newValue);
566+
}
548567
}
549568
}
550569
}

network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/ConfiguredBusImpl.java

+17-12
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
import com.powsybl.network.store.model.AttributeFilter;
2929
import com.powsybl.network.store.model.CalculatedBusAttributes;
3030
import com.powsybl.network.store.model.ConfiguredBusAttributes;
31+
import com.powsybl.network.store.model.VoltageLevelAttributes;
3132
import com.powsybl.network.store.model.Resource;
3233
import org.apache.commons.collections4.MapUtils;
34+
import org.apache.commons.collections4.CollectionUtils;
3335

3436
import java.util.List;
3537
import java.util.Map;
@@ -96,8 +98,8 @@ private void setV(double v, boolean updateCalculatedBus) {
9698
}
9799
}
98100

99-
// update without the part setting values in calculated buses otherwise
100-
// it leads to infinite loops because calculated buses also update configured buses
101+
// update without the part setting values in calculated buses otherwise it
102+
// leads to infinite loops because calculated buses also update configured buses
101103
void setConfiguredBusV(double v) {
102104
setV(v, false);
103105
}
@@ -148,23 +150,26 @@ private void setAngleInCalculatedBus(CalculatedBusAttributes calculatedBusAttrib
148150
private void updateCalculatedBusAttributes(double newValue,
149151
String voltageLevelId,
150152
ObjDoubleConsumer<CalculatedBusAttributes> setValue) {
151-
VoltageLevelImpl voltageLevel = index.getVoltageLevel(voltageLevelId).orElseThrow();
152-
Map<String, Integer> calculatedBuses = voltageLevel.getResource().getAttributes().getBusToCalculatedBusForBusView();
153-
// TODO, do we want to update the busview values if the view is invalid ?
154-
// if invalid, values will be restored from the configuredbuses on the next
155-
// busview computation anyway in the new bus objects and attributes,
156-
// but the previous bus objects and attributes may still be used somewhere
157-
// so there is a visible effect to choosing to update invalid views or not.
158-
if (!MapUtils.isEmpty(calculatedBuses)) {
153+
VoltageLevelImpl voltageLevel = index.getVoltageLevel(voltageLevelId).orElseThrow(IllegalArgumentException::new);
154+
VoltageLevelAttributes vlAttributes = voltageLevel.getResource().getAttributes();
155+
Map<String, Integer> calculatedBuses = vlAttributes.getBusToCalculatedBusForBusView();
156+
List<CalculatedBusAttributes> busViewCalculatedBusesAttributes = vlAttributes.getCalculatedBusesForBusView();
157+
// We only update when isCalculatedBusesValid is true, there is no point in updating stale bus objects and
158+
// in when isCalculatedBusesValid is not true, we may even update the wrong buses (but not much of a problem
159+
// because they are stale objects).
160+
// TODO add tests for updates with isCalculatedBusesValid=false
161+
if (vlAttributes.isCalculatedBusesValid()
162+
&& !CollectionUtils.isEmpty(busViewCalculatedBusesAttributes)
163+
&& !MapUtils.isEmpty(calculatedBuses)) {
159164
Integer busviewnum = calculatedBuses.get(getId());
160165
if (busviewnum != null) {
161-
CalculatedBusAttributes busviewattributes = voltageLevel.getResource().getAttributes().getCalculatedBusesForBusView().get(busviewnum);
166+
CalculatedBusAttributes busViewCalculatedBusAttributes = busViewCalculatedBusesAttributes.get(busviewnum);
162167
// Same code as the iidm impl for CalculatedBus setV / setAngle
163168
// (without the part setting values in configured buses otherwise
164169
// it would be an infinite loop), but copy paste here
165170
// to avoid creating the object (calculated buses are created on when computing
166171
// the bus view, but we want to only update if the busview exist, not force its creation)
167-
setValue.accept(busviewattributes, newValue);
172+
setValue.accept(busViewCalculatedBusAttributes, newValue);
168173
index.updateVoltageLevelResource(voltageLevel.getResource(), AttributeFilter.SV);
169174
}
170175
}

0 commit comments

Comments
 (0)