diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/AbstractTopology.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/AbstractTopology.java index 4210c0c12..cc1e0f5e3 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/AbstractTopology.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/AbstractTopology.java @@ -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; /** @@ -358,6 +362,59 @@ private void setCalculatedBuses(Resource voltageLevelRes } } + private void getVAndAngleFromConfiguredBus(NetworkObjectIndex index, + Resource voltageLevelResource, + ConnectedSetResult connectedSet, + AtomicDouble v, + AtomicDouble angle) { + index.getConfiguredBuses(voltageLevelResource.getId()).forEach(bus -> { + ConfiguredBusImpl configuredBus = (ConfiguredBusImpl) bus; + AtomicReference 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 voltageLevelResource, + ConnectedSetResult connectedSet, + AtomicDouble v, + AtomicDouble angle, + boolean isBusView) { + AtomicBoolean foundInCalculatedBuses = new AtomicBoolean(false); + List calculatedBusAttributes = isBusView ? + voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView() : + voltageLevelResource.getAttributes().getCalculatedBusesForBusView(); + if (!CollectionUtils.isEmpty(calculatedBusAttributes)) { + connectedSet.getConnectedVertices().forEach(vertex -> + calculatedBusAttributes.stream().filter(attr -> attr.getVertices().contains(vertex)).forEach(b -> { + if (!Double.isNaN(b.getV())) { + v.set(b.getV()); + foundInCalculatedBuses.set(true); + } + if (!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 getCalculatedBusAttributesList(NetworkObjectIndex index, Resource voltageLevelResource, boolean isBusView) { List calculatedBusAttributesList; Map nodeOrBusToCalculatedBusNum; @@ -365,11 +422,16 @@ private CalculationResult getCalculatedBusAttributesList(NetworkObjectIndex i calculatedBusAttributesList = isBusView ? voltageLevelResource.getAttributes().getCalculatedBusesForBusView() : voltageLevelResource.getAttributes().getCalculatedBusesForBusBreakerView(); nodeOrBusToCalculatedBusNum = getNodeOrBusToCalculatedBusNum(voltageLevelResource, isBusView); } else { - // calculate buses List> connectedSetList = findConnectedSetList(index, voltageLevelResource, isBusView); + AtomicDouble v = new AtomicDouble(Double.NaN); + AtomicDouble angle = new AtomicDouble(Double.NaN); 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); diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CalculatedBus.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CalculatedBus.java index 8b2c8b196..4ff2e8d3f 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CalculatedBus.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/CalculatedBus.java @@ -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; @@ -48,6 +51,9 @@ public final class CalculatedBus implements BaseBus { private final Function getBusFromTerminal; + private static final String VOLTAGE = "v"; + private static final String ANGLE = "angle"; + CalculatedBus(NetworkObjectIndex index, String voltageLevelId, String id, String name, Resource voltageLevelResource, int calculatedBusNum, boolean isBusView) { this.index = Objects.requireNonNull(index); @@ -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 calculatedBusAttributesList, + CalculatedBusAttributes sourceBusAttributes, + ObjDoubleConsumer 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 calculatedBusAttributes, + String attributeName, + ToDoubleFunction getValue, + ObjDoubleConsumer setValue) { + List busesIds = calculatedBusAttributes.getVertices().stream() + .map(Vertex::getBus) + .toList(); + + List buses = getVoltageLevel().getBusBreakerView().getBusStream() + .filter(bus -> busesIds.contains(bus.getId()) && !Objects.equals(getValue.applyAsDouble(bus), newValue)) + .toList(); + + Map> 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(); @@ -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; } @@ -423,6 +523,10 @@ public Stream getAllTerminalsStream() { .filter(t -> t.getVoltageLevel().getId().equals(getVoltageLevel().getId()) && pred.test(t)); } + public int getCalculatedBusNum() { + return calculatedBusNum; + } + @Override public > void addExtension(Class aClass, E e) { throw new UnsupportedOperationException("Adding an extension on calculated bus is not authorized"); @@ -467,8 +571,4 @@ public , B extends ExtensionAdder> B newExtensi ExtensionAdderProvider provider = ExtensionAdderProviders.findCachedProvider(getImplementationName(), type); return (B) provider.newAdder(this); } - - public int getCalculatedBusNum() { - return calculatedBusNum; - } } diff --git a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/ConfiguredBusImpl.java b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/ConfiguredBusImpl.java index f5c835937..c2c75eda5 100644 --- a/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/ConfiguredBusImpl.java +++ b/network-store-iidm-impl/src/main/java/com/powsybl/network/store/iidm/impl/ConfiguredBusImpl.java @@ -25,11 +25,15 @@ 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.Stream; @@ -77,6 +81,37 @@ 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 setValue) { + Optional voltageLevelOpt = index.getVoltageLevel(voltageLevelId); + + voltageLevelOpt.ifPresent(voltageLevel -> { + List calculatedBusAttributesList = voltageLevel.getResource() + .getAttributes() + .getCalculatedBusesForBusView(); + + if (CollectionUtils.isNotEmpty(calculatedBusAttributesList)) { + getAllTerminals().stream() + .filter(Terminal::isConnected) + .map(t -> ((CalculatedBus) t.getBusView().getBus()).getCalculatedBusNum()) + .findFirst() + .ifPresent(calculatedBusNum -> { + setValue.accept(calculatedBusAttributesList.get(calculatedBusNum), newValue); + index.updateVoltageLevelResource(voltageLevel.getResource(), AttributeFilter.SV); + }); + } + }); + } + @Override public Bus setV(double v) { if (v < 0) { @@ -87,6 +122,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; } @@ -98,6 +136,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; } diff --git a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/LineTest.java b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/LineTest.java index c054defbb..0cac6d2f4 100644 --- a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/LineTest.java +++ b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/LineTest.java @@ -10,20 +10,32 @@ import com.powsybl.cgmes.conformity.CgmesConformity1Catalog; import com.powsybl.cgmes.conversion.CgmesImport; import com.powsybl.commons.PowsyblException; -import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.ActivePowerLimits; +import com.powsybl.iidm.network.ApparentPowerLimits; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.CurrentLimits; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.DanglingLineFilter; +import com.powsybl.iidm.network.Importer; +import com.powsybl.iidm.network.LimitType; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Terminal; +import com.powsybl.iidm.network.TieLine; +import com.powsybl.iidm.network.TwoSides; +import com.powsybl.iidm.network.VoltageLevel; import com.powsybl.iidm.network.extensions.ConnectablePosition; import com.powsybl.iidm.network.extensions.ConnectablePositionAdder; - -import com.powsybl.network.store.model.LimitsAttributes; -import com.powsybl.network.store.model.TemporaryLimitAttributes; import org.junit.Test; import java.util.List; import java.util.Properties; -import java.util.TreeMap; import java.util.stream.Collectors; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -33,58 +45,6 @@ * @author Etienne Homer */ public class LineTest { - //TODO: there is a similar test in the TCK tests. A CurrentLimitsTest extends AbstractCurrentLimitsTest should be created and this test can be deleted. - // The TCK test doesn't pass yet. As is, the network-store implementation of setV(v) on buses is not consistent. We have problems with the views we are working on (BusBreakerView or BusView). - @Test - public void isOverloadedTest() { - Network network = CreateNetworksUtil.createNodeBreakerNetworkWithLine(); - LineImpl l1 = (LineImpl) network.getLine("L1"); - l1.getTerminal1().setP(10); - l1.getTerminal1().setQ(0); - l1.getTerminal1().getBusView().getBus().setV(400.0); - assertFalse(l1.isOverloaded()); - - l1.getTerminal1().setP(400); - - l1.setCurrentLimits(TwoSides.ONE, new LimitsAttributes("PermaLimit1", 600, null), "PermaLimit1"); - assertNull(l1.getNullableCurrentLimits1()); - l1.setSelectedOperationalLimitsGroup1("PermaLimit1"); - assertTrue(l1.getNullableCurrentLimits1().getTemporaryLimits().isEmpty()); - assertFalse(l1.isOverloaded()); - - l1.newCurrentLimits1().setPermanentLimit(50).add(); - assertTrue(l1.isOverloaded()); - - TreeMap temporaryLimits = new TreeMap<>(); - temporaryLimits.put(5, TemporaryLimitAttributes.builder().name("TempLimit5").value(1000).acceptableDuration(5).fictitious(false).build()); - l1.setCurrentLimits(TwoSides.ONE, new LimitsAttributes("PermaLimit1", 40, temporaryLimits), "PermaLimit1"); - l1.setCurrentLimits(TwoSides.TWO, new LimitsAttributes("PermaLimit1", 40, temporaryLimits), "PermaLimit1"); - l1.setSelectedOperationalLimitsGroup1("PermaLimit1"); - l1.setSelectedOperationalLimitsGroup2("PermaLimit1"); - assertEquals(5, l1.getOverloadDuration()); - - assertTrue(l1.checkPermanentLimit(TwoSides.ONE, LimitType.CURRENT)); - assertTrue(l1.checkPermanentLimit1(LimitType.CURRENT)); - assertFalse(l1.checkPermanentLimit(TwoSides.TWO, LimitType.CURRENT)); - assertFalse(l1.checkPermanentLimit2(LimitType.CURRENT)); - assertFalse(l1.checkPermanentLimit(TwoSides.ONE, LimitType.APPARENT_POWER)); - assertFalse(l1.checkPermanentLimit(TwoSides.TWO, LimitType.ACTIVE_POWER)); - assertThrows(UnsupportedOperationException.class, () -> l1.checkPermanentLimit(TwoSides.TWO, LimitType.VOLTAGE)); - - Overload overload = l1.checkTemporaryLimits(TwoSides.ONE, LimitType.CURRENT); - assertEquals("TempLimit5", overload.getTemporaryLimit().getName()); - assertEquals(40.0, overload.getPreviousLimit(), 0); - assertEquals(5, overload.getTemporaryLimit().getAcceptableDuration()); - assertNull(l1.checkTemporaryLimits(TwoSides.TWO, LimitType.CURRENT)); - - temporaryLimits.put(5, TemporaryLimitAttributes.builder().name("TempLimit5").value(20).acceptableDuration(5).fictitious(false).build()); - assertEquals(Integer.MAX_VALUE, l1.getOverloadDuration()); - - temporaryLimits.put(10, TemporaryLimitAttributes.builder().name("TempLimit10").value(8).acceptableDuration(10).fictitious(false).build()); - // check duration sorting order: first entry has the highest duration - assertEquals(10., l1.getNullableCurrentLimits1().getTemporaryLimits().iterator().next().getAcceptableDuration(), 0); - } - @Test public void testAddConnectablePositionExtensionToLine() { Network network = CreateNetworksUtil.createNodeBreakerNetworkWithLine(); diff --git a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/VoltageLevelTest.java b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/VoltageLevelTest.java index 1ffe9b351..33df20173 100644 --- a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/VoltageLevelTest.java +++ b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/VoltageLevelTest.java @@ -9,6 +9,8 @@ import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.BusbarSectionPositionAdder; import org.junit.Test; +import java.util.*; +import java.util.stream.*; import static org.junit.Assert.*; @@ -17,6 +19,78 @@ */ public class VoltageLevelTest { + @Test + public void testBusBreakerSetVUpdateVoltageLevel() { + Network network = CreateNetworksUtil.createBusBreakerNetworkWithLine(); + LineImpl l1 = (LineImpl) network.getLine("L1"); + + // Update voltage using BusView + l1.getTerminal1().getBusView().getBus().setV(222); + + // Verify the voltage update in BusBreakerView + assertEquals("Voltage should match in BusBreakerView after update in BusView", 222, l1.getTerminal1().getBusBreakerView().getBus().getV(), 0.0); + + // Set voltage using BusBreakerView + l1.getTerminal1().getBusBreakerView().getBus().setV(400.0); + + // Verify voltage update in BusView + assertEquals("Voltage should match in BusView after update in BusBreakerView", 400.0, l1.getTerminal1().getBusView().getBus().getV(), 0.0); + } + + @Test + public void testNodeBreakerSetVUpdateVoltageLevel() { + Network network = CreateNetworksUtil.createNodeBreakerNetworkWithLine(); + LineImpl l1 = (LineImpl) network.getLine("L1"); + + // Update voltage using BusBreakerView + l1.getTerminal1().getBusBreakerView().getBus().setV(222); + + // Verify the voltage update in BusView + assertEquals("Voltage should match in BusBreakerView after second update in BusView", 222, l1.getTerminal1().getBusView().getBus().getV(), 0.0); + + // Set voltage using BusView + l1.getTerminal1().getBusView().getBus().setV(400.0); + + // Verify voltage update in BusBreakerView + assertEquals("Voltage should match in BusBreakerView after update in BusView", 400.0, l1.getTerminal1().getBusBreakerView().getBus().getV(), 0.0); + } + + @Test + public void testBusBreakerSetAngleUpdateVoltageLevel() { + Network network = CreateNetworksUtil.createBusBreakerNetworkWithLine(); + LineImpl l1 = (LineImpl) network.getLine("L1"); + + // Update angle using BusView + l1.getTerminal1().getBusView().getBus().setAngle(111); + + // Verify the angle update in BusBreakerView + assertEquals("Angle should match in BusView after update in BusBreakerView", 111, l1.getTerminal1().getBusBreakerView().getBus().getAngle(), 0.0); + + // Set angle using BusBreakerView + l1.getTerminal1().getBusBreakerView().getBus().setAngle(400.0); + + // Verify Angle update in BusView + assertEquals("Angle should match in BusView after update in BusBreakerView", 400.0, l1.getTerminal1().getBusView().getBus().getAngle(), 0.0); + } + + @Test + public void testNodeBreakerSetAngleUpdateVoltageLevel() { + Network network = CreateNetworksUtil.createNodeBreakerNetworkWithLine(); + LineImpl l1 = (LineImpl) network.getLine("L1"); + + // Update angle using BusBreakerView + l1.getTerminal1().getBusBreakerView().getBus().setAngle(222); + + // Verify the angle update in BusView + assertEquals("Angle should match in BusBreakerView after second update in BusView", 222, l1.getTerminal1().getBusView().getBus().getAngle(), 0.0); + + // Set angle using BusView + l1.getTerminal1().getBusView().getBus().setAngle(400.0); + + // Verify angle update in BusBreakerView + assertEquals("Angle should match in BusBreakerView after update in BusView", 400.0, l1.getTerminal1().getBusBreakerView().getBus().getAngle(), 0.0); + } + @Test public void testBusBreakerConnectables() { Network network = CreateNetworksUtil.createBusBreakerNetworkWithLine(); @@ -84,4 +158,284 @@ public void testBusbarSectionPositions() { assertEquals("Busbar section 'idBBS': Busbar index has to be greater or equals to zero", assertThrows(ValidationException.class, busbarSectionPositionAdder::add).getMessage()); } + + @Test + //TODO move all this to a separate file it's huge. + public void testWithMultipleBusInBusBreakerAndBusView() { + Network network = Network.create("test_mcc", "test"); + + VoltageLevelImpl vl1 = createNodeBreaker(network); + VoltageLevelImpl vl2 = createBusBreaker(network); + + testSetVMultipleBusAcrossViews(vl1, NBVL_BUSBREAKERVIEWBUS_TO_BUSVIEWBUS, NBVL_BUSVIEWBUS_TO_BUSBREAKERVIEWBUS); + testSetVMultipleBusAcrossViews(vl2, BBVL_CONFIGUREDBUS_TO_BUSVIEWBUS, BBVL_BUSVIEWBUS_TO_CONFIGUREDBUS); + } + + private static final Map NBVL_BUSBREAKERVIEWBUS_TO_BUSVIEWBUS = new TreeMap<>(Map.of( + "VL1_0", "VL1_0", + "VL1_2", "VL1_0", + "VL1_3", "VL1_3", + "VL1_4", "VL1_4", + "VL1_10", "", + "VL1_11", "VL1_0", + "VL1_5", "" + )); + private static final Map> NBVL_BUSVIEWBUS_TO_BUSBREAKERVIEWBUS = invertMap(NBVL_BUSBREAKERVIEWBUS_TO_BUSVIEWBUS); + + private static VoltageLevelImpl createNodeBreaker(Network network) { + // nodebreaker topology voltage level + VoltageLevel vl1 = network.newVoltageLevel() + .setId("VL1") + .setNominalV(225.0) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + vl1.getNodeBreakerView().newBusbarSection() + .setId("BBS1") + .setNode(0) + .add(); + vl1.getNodeBreakerView().newSwitch() + .setId("VL1SW1") + .setKind(SwitchKind.BREAKER) + .setRetained(false) + .setOpen(false) + .setNode1(0) + .setNode2(1) + .add(); + vl1.getNodeBreakerView().newBusbarSection() + .setId("BBS2") + .setNode(1) + .add(); + vl1.getNodeBreakerView().newSwitch() + .setId("VL1SW2") + .setKind(SwitchKind.BREAKER) + .setRetained(true) + .setOpen(false) + .setNode1(1) + .setNode2(2) + .add(); + vl1.getNodeBreakerView().newBusbarSection() + .setId("BBS3") + .setNode(2) + .add(); + vl1.getNodeBreakerView().newSwitch() + .setId("VL1SW3") + .setKind(SwitchKind.BREAKER) + .setRetained(true) + .setOpen(true) + .setNode1(2) + .setNode2(3) + .add(); + vl1.getNodeBreakerView().newBusbarSection() + .setId("BBS4") + .setNode(3) + .add(); + vl1.getNodeBreakerView().newSwitch() + .setId("VL1SW4") + .setKind(SwitchKind.BREAKER) + .setRetained(false) + .setOpen(true) + .setNode1(3) + .setNode2(4) + .add(); + vl1.getNodeBreakerView().newBusbarSection() + .setId("BBS5") + .setNode(4) + .add(); + vl1.getNodeBreakerView().newSwitch() + .setId("VL1SW5") + .setKind(SwitchKind.BREAKER) + .setRetained(true) + .setOpen(true) + .setNode1(4) + .setNode2(5) + .add(); + vl1.getNodeBreakerView().newBusbarSection() + .setId("BBS6") + .setNode(5) + .add(); + + // add loads so that buses are not pruned + vl1.getNodeBreakerView().newInternalConnection() + .setNode1(0) + .setNode2(6) + .add(); + vl1.newLoad().setId("VL1L1").setNode(6).setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + + vl1.getNodeBreakerView().newInternalConnection() + .setNode1(2) + .setNode2(7) + .add(); + vl1.newLoad().setId("VL1L2").setNode(7).setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + + vl1.getNodeBreakerView().newInternalConnection() + .setNode1(3) + .setNode2(8) + .add(); + vl1.newLoad().setId("VL1L3").setNode(8).setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + + vl1.getNodeBreakerView().newInternalConnection() + .setNode1(4) + .setNode2(9) + .add(); + vl1.newLoad().setId("VL1L4").setNode(9).setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + + //no load on BBS6, it will be pruned + + //add a load on bbs1 but not connected, it will be in a separate bus in the busbreakerview and pruned in the busview + vl1.getNodeBreakerView().newSwitch() + .setId("VL1SW6") + .setKind(SwitchKind.BREAKER) + .setRetained(false) + .setOpen(true) + .setNode1(0) + .setNode2(10) + .add(); + vl1.newLoad().setId("VL1L5").setNode(10).setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + + //add a load on bbs1 but with retained switch, it will be in a separate bus in the busbreakerview but connected in the busview + vl1.getNodeBreakerView().newSwitch() + .setId("VL1SW7") + .setKind(SwitchKind.BREAKER) + .setRetained(true) + .setOpen(false) + .setNode1(0) + .setNode2(11) + .add(); + vl1.newLoad().setId("VL1L6").setNode(11).setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + + return (VoltageLevelImpl) vl1; + } + + private static final Map BBVL_CONFIGUREDBUS_TO_BUSVIEWBUS = new TreeMap<>(Map.of( + "BUS1", "VL2_0", + "BUS2", "VL2_0", + "BUS3", "VL2_1", + "BUS4", "VL2_2", + "BUS5", "" + )); + + private static final Map> BBVL_BUSVIEWBUS_TO_CONFIGUREDBUS = invertMap(BBVL_CONFIGUREDBUS_TO_BUSVIEWBUS); + + private static VoltageLevelImpl createBusBreaker(Network network) { + + // busbreaker topology voltage level + VoltageLevel vl2 = network.newVoltageLevel() + .setId("VL2") + .setNominalV(225.0) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + vl2.getBusBreakerView().newBus() + .setId("BUS1") + .add(); + vl2.getBusBreakerView().newSwitch() + .setId("VL2SW1") + .setOpen(false) + .setBus1("BUS1") + .setBus2("BUS2") + .add(); + vl2.getBusBreakerView().newBus() + .setId("BUS2") + .add(); + vl2.getBusBreakerView().newSwitch() + .setId("VL2SW2") + .setOpen(true) + .setBus1("BUS2") + .setBus2("BUS3") + .add(); + vl2.getBusBreakerView().newBus() + .setId("BUS3") + .add(); + vl2.getBusBreakerView().newBus() + .setId("BUS4") + .add(); + vl2.getBusBreakerView().newBus() + .setId("BUS5") + .add(); + + // loads to avoid pruning + vl2.newLoad().setId("VL2L1").setBus("BUS1").setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + vl2.newLoad().setId("VL2L2").setBus("BUS3").setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + vl2.newLoad().setId("VL2L3").setBus("BUS4").setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + vl2.newLoad().setId("VL2L4").setConnectableBus("BUS5").setP0(1).setQ0(1).add().getTerminal().setP(0.001).setQ(1); + // BUS6 has no load it will be pruned + + return (VoltageLevelImpl) vl2; + } + + private void testSetVMultipleBusAcrossViews(VoltageLevelImpl vl, Map busBreakerViewBusToBusViewBus, Map> busViewBusToBusBreakerViewBus) { + for (Map.Entry entry : busBreakerViewBusToBusViewBus.entrySet()) { + String busbreakerviewbusid = entry.getKey(); + String busviewbusid = entry.getValue(); + + // should we replace with new network for each test ?? + vl.invalidateCalculatedBuses(); // but this keeps previous v values + // TODO this forces a computation of all busviews + // need to test when views are not initialized + for (Bus bbvb : vl.getBusBreakerView().getBuses()) { + bbvb.setV(Double.NaN); + } + for (Bus bvb : vl.getBusView().getBuses()) { + bvb.setV(Double.NaN); + } + + vl.getBusBreakerView().getBus(busbreakerviewbusid).setV(1.); + if (!busviewbusid.isEmpty()) { + assertEquals("case " + busbreakerviewbusid + " (busbreakerviewbus) is set, " + busviewbusid + " (busviewbus) should have been set", + 1, vl.getBusView().getBus(busviewbusid).getV(), 0); + } + for (Bus bbvb : vl.getBusBreakerView().getBuses()) { + if (!busbreakerviewbusid.equals(bbvb.getId())) { + assertTrue("case " + busbreakerviewbusid + " (busbreakerviewbus) is set, " + bbvb.getId() + " (busbreakerviewbus) should not have been set", + Double.isNaN(bbvb.getV())); + } + } + for (Bus bvb : vl.getBusView().getBuses()) { + if (!busviewbusid.equals(bvb.getId())) { + assertTrue("case " + busbreakerviewbusid + "(busbreakerviewbus) is set, " + bvb.getId() + " (busviewbus) should not have been set", + Double.isNaN(bvb.getV())); + } + } + } + + for (Map.Entry > entry : busViewBusToBusBreakerViewBus.entrySet()) { + String busviewbusid = entry.getKey(); + List busbreakerviewbusids = entry.getValue(); + + // should we replace with new network for each test ?? + vl.invalidateCalculatedBuses(); // but this keeps previous v values + // TODO this forces a computation of all busviews + // need to test when views are not initialized + for (Bus bbvb : vl.getBusBreakerView().getBuses()) { + bbvb.setV(Double.NaN); + } + for (Bus bvb : vl.getBusView().getBuses()) { + bvb.setV(Double.NaN); + } + + vl.getBusView().getBus(busviewbusid).setV(1.); + for (String busbreakerviewbusid : busbreakerviewbusids) { + assertEquals("case " + busviewbusid + " (busviewbus) is set, " + busbreakerviewbusid + " (busbreakerviewbus) should have been set", + 1, vl.getBusBreakerView().getBus(busbreakerviewbusid).getV(), 0); + } + for (Bus bvb : vl.getBusView().getBuses()) { + if (!busviewbusid.equals(bvb.getId())) { + assertTrue("case " + busviewbusid + " (busviewbus) is set, " + bvb.getId() + " (busviewbus) should not have been set", + Double.isNaN(bvb.getV())); + } + } + for (Bus bbvb : vl.getBusBreakerView().getBuses()) { + if (!busbreakerviewbusids.contains(bbvb.getId())) { + assertTrue("case " + busviewbusid + " (busviewbus) is set, " + bbvb.getId() + " (busbreakerviewbus) should not have been set", + Double.isNaN(bbvb.getV())); + } + } + } + } + + private static Map> invertMap(Map map) { + return map + .entrySet().stream() + .filter(x -> !x.getValue().isEmpty()) + .collect(Collectors.groupingBy(Map.Entry::getValue, TreeMap::new, Collectors.mapping(Map.Entry::getKey, Collectors.toList()))); + } } diff --git a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/tck/CurrentLimitsTest.java b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/tck/CurrentLimitsTest.java index d8644f85b..241ed0831 100644 --- a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/tck/CurrentLimitsTest.java +++ b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/tck/CurrentLimitsTest.java @@ -12,33 +12,4 @@ * @author Geoffroy Jamgotchian */ public class CurrentLimitsTest extends AbstractCurrentLimitsTest { - @Override - public void test() { - // FIXME delete this test when we fix Bus.getV/setV not getting/updating correctly the V in all views - } - - @Override - public void testForThreeWindingsTransformerLeg1() { - // FIXME delete this test when we fix Bus.getV/setV not getting/updating correctly the V in all views - } - - @Override - public void testForThreeWindingsTransformerLeg2() { - // FIXME delete this test when we fix Bus.getV/setV not getting/updating correctly the V in all views - } - - @Override - public void testForThreeWindingsTransformerLeg3() { - // FIXME delete this test when we fix Bus.getV/setV not getting/updating correctly the V in all views - } - - @Override - public void testLimitWithoutTempLimit() { - // FIXME delete this test when we fix Bus.getV/setV not getting/updating correctly the V in all views - } - - @Override - public void testSetterGetter() { - // FIXME delete this test when we fix Bus.getV/setV not getting/updating correctly the V in all views - } } diff --git a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/tck/MainConnectedComponentWithSwitchTest.java b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/tck/MainConnectedComponentWithSwitchTest.java index 9e01d1315..883428e0f 100644 --- a/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/tck/MainConnectedComponentWithSwitchTest.java +++ b/network-store-iidm-impl/src/test/java/com/powsybl/network/store/iidm/impl/tck/MainConnectedComponentWithSwitchTest.java @@ -12,9 +12,4 @@ * @author Geoffroy Jamgotchian */ public class MainConnectedComponentWithSwitchTest extends AbstractMainConnectedComponentWithSwitchTest { - - @Override - public void test() { - // FIXME remove this test when we stop losing the v of buses / use the right views - } }