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

Allow use of bus-breaker voltage levels #169

Merged
merged 12 commits into from
Nov 8, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public class MetrixNetwork {
private final Map<String, String> mappedSwitchMap = new HashMap<>();

protected MetrixNetwork(Network network) {
// TODO: switch T3T for 3xTWT in the network using a network modification
this.network = Objects.requireNonNull(network);
}

Expand Down Expand Up @@ -612,35 +613,66 @@ private void createRetainedBreakersList(Set<String> breakerList) {
private void addElementToRetainedBreakersList(Switch sw) {
String switchId = sw.getId();

VoltageLevel.NodeBreakerView nodeBreakerView = sw.getVoltageLevel().getNodeBreakerView();
Terminal terminal1 = nodeBreakerView.getTerminal1(switchId);
Terminal terminal2 = nodeBreakerView.getTerminal2(switchId);
VoltageLevel voltageLevel = sw.getVoltageLevel();

if (terminal1 == null || terminal1.getConnectable().getType() == IdentifiableType.BUSBAR_SECTION) {
terminal1 = terminal2;
if (voltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER) {
// Get the terminals on both sides of the switch
VoltageLevel.NodeBreakerView nodeBreakerView = voltageLevel.getNodeBreakerView();
Terminal terminal1 = nodeBreakerView.getTerminal1(switchId);
Terminal terminal2 = nodeBreakerView.getTerminal2(switchId);

// Check on both sides of the switch to find a connectable that is not another switch nor a bus bar section
if (isSwitchNotConnectedToOtherSwitchOrBbs(terminal1)) {
addElementToRetainedBreakersList(sw, terminal1, true);
} else if (isSwitchNotConnectedToOtherSwitchOrBbs(terminal2)) {
addElementToRetainedBreakersList(sw, terminal2, true);
} else {
// Both sides have either a switch or a bus bar section : the switch is registered by itself
addElementToRetainedBreakersList(sw, switchId, true);
}
} else if (voltageLevel.getTopologyKind() == TopologyKind.BUS_BREAKER) {
// Get the terminals on both sides of the switch
VoltageLevel.BusBreakerView busBreakerView = voltageLevel.getBusBreakerView();
List<? extends Terminal> terminalsBus1 = busBreakerView.getBus1(switchId).getConnectedTerminalStream().toList();
List<? extends Terminal> terminalsBus2 = busBreakerView.getBus2(switchId).getConnectedTerminalStream().toList();

// Check on both sides of the switch to check if there is one and only one connectable
if (terminalsBus1.size() == 1) {
addElementToRetainedBreakersList(sw, terminalsBus1.get(0), false);
} else if (terminalsBus2.size() == 1) {
addElementToRetainedBreakersList(sw, terminalsBus2.get(0), false);
} else {
addElementToRetainedBreakersList(sw, switchId, false);
}
}
}

if (terminal1 == null || terminal1.getConnectable().getType() == IdentifiableType.BUSBAR_SECTION) {
sw.setRetained(true);
mappedSwitchMap.put(switchId, switchId);
} else {
switch (terminal1.getConnectable().getType()) {
case LINE, TWO_WINDINGS_TRANSFORMER -> {
sw.setRetained(true);
mappedSwitchMap.put(switchId, terminal1.getConnectable().getId());
}
case LOAD, GENERATOR -> {
sw.setRetained(true);
mappedSwitchMap.put(switchId, switchId);
}
case DANGLING_LINE, HVDC_CONVERTER_STATION, SHUNT_COMPENSATOR, STATIC_VAR_COMPENSATOR, THREE_WINDINGS_TRANSFORMER -> {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn(String.format("Unsupported connectable type (%s) for switch '%s'", terminal1.getConnectable().getType(), switchId));
}
private boolean isSwitchNotConnectedToOtherSwitchOrBbs(Terminal terminal) {
return terminal != null && terminal.getConnectable().getType() != IdentifiableType.BUSBAR_SECTION;
}

private void addElementToRetainedBreakersList(Switch sw, Terminal terminal, boolean setRetained) {
String switchId = sw.getId();
switch (terminal.getConnectable().getType()) {
// Since switches connected to lines and TWT are "replaced" by those connectables, no need to set them retained
case LINE, TWO_WINDINGS_TRANSFORMER -> addElementToRetainedBreakersList(sw, terminal.getConnectable().getId(), false);
case LOAD, GENERATOR -> addElementToRetainedBreakersList(sw, switchId, setRetained);
case DANGLING_LINE, HVDC_CONVERTER_STATION, SHUNT_COMPENSATOR, STATIC_VAR_COMPENSATOR,
THREE_WINDINGS_TRANSFORMER -> {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Unsupported connectable type ({}) for switch '{}'", terminal.getConnectable().getType(), switchId);
}
default -> throw new PowsyblException("Unexpected connectable type : " + terminal1.getConnectable().getType());
}
default ->
throw new PowsyblException("Unexpected connectable type : " + terminal.getConnectable().getType());
}
}

private void addElementToRetainedBreakersList(Switch sw, String id, boolean setRetained) {
if (setRetained) {
sw.setRetained(true);
}
mappedSwitchMap.put(sw.getId(), id);
}

private void createOpenedBranchesList(Set<String> openedBranches) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,11 @@ private void createMetrixInputs() {

trnbgrou = metrixNetwork.getGeneratorList().size();

cqnbquad = metrixNetwork.getLineList().size() + metrixNetwork.getTwoWindingsTransformerList().size() + 3 * metrixNetwork.getThreeWindingsTransformerList().size() + metrixNetwork.getDanglingLineList().size() + metrixNetwork.getSwitchList().size();
// Quadripoles are lines, transformers and switches
cqnbquad = metrixNetwork.getLineList().size() + metrixNetwork.getTwoWindingsTransformerList().size() + 3 * metrixNetwork.getThreeWindingsTransformerList().size() + metrixNetwork.getSwitchList().size();
dtnbtrde = metrixNetwork.getPhaseTapChangerList().size();

// Loads are loads and dangling lines
ecnbcons = metrixNetwork.getLoadList().size() + metrixNetwork.getDanglingLineList().size();

dcnblies = metrixNetwork.getHvdcLineList().size();
Expand Down Expand Up @@ -391,7 +393,7 @@ private void writeBranches(boolean constantLossFactor, MetrixDie die) {

// Three Windings Transformers
metrixNetwork.getThreeWindingsTransformerList().forEach(twt -> {
throw new UnsupportedOperationException("TODO");
throw new PowsyblException("Three Windings Transformers are not yet supported in metrix");
});

// Switches
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
import com.google.common.collect.Range;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.powsybl.commons.PowsyblException;
import com.powsybl.contingency.*;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.test.ThreeWindingsTransformerNetworkFactory;
import com.powsybl.iidm.serde.NetworkSerDe;
import com.powsybl.metrix.integration.dataGenerator.MetrixInputData;
import com.powsybl.metrix.integration.metrix.MetrixChunkParam;
Expand Down Expand Up @@ -156,6 +158,17 @@ void metrixDefaultInputTest() throws IOException {
new ByteArrayInputStream(actual.getBytes(StandardCharsets.UTF_8))));
}

@Test
void metrixInputDataWithT3TTest() throws IOException {
Network n = ThreeWindingsTransformerNetworkFactory.create();
MetrixInputData metrixInputData = new MetrixInputData(MetrixNetwork.create(n), null, new MetrixParameters());
try (StringWriter writer = new StringWriter()) {
assertThrows(PowsyblException.class,
() -> metrixInputData.writeJson(writer),
"Three Windings Transformers are not yet supported in metrix");
}
}

@Test
void metrixInputTest() throws IOException {
Network n = NetworkSerDe.read(getClass().getResourceAsStream("/simpleNetwork.xml"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.powsybl.contingency.Contingency;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.test.FourSubstationsNodeBreakerFactory;
import com.powsybl.iidm.serde.ExportOptions;
import com.powsybl.iidm.serde.NetworkSerDe;
import com.powsybl.metrix.integration.contingency.Probability;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -21,6 +23,8 @@
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* @author Nicolas Rol {@literal <nicolas.rol at rte-france.com>}
Expand All @@ -44,8 +48,11 @@ void testNetworkElementsLists() {
Network network = createNetwork();

// Set some switches as retained
Set<String> mappedSwitches = Set.of("S1VL2_GH2_BREAKER", "S3VL1_LINES3S4_BREAKER", "S1VL1_LD1_BREAKER");
List<Switch> switchList = mappedSwitches.stream()
Set<String> mappedSwitches = Set.of("S1VL2_GH2_BREAKER", "S3VL1_LINES3S4_BREAKER", "S1VL1_LD1_BREAKER", "S1VL3_DL_BREAKER", "S1VL2_BBS1_BBS3");

// Expected switch list in MetrixNetwork: switches next to branches (lines, two windings transformers) are not present
List<Switch> switchList = Set.of("S1VL2_GH2_BREAKER", "S1VL1_LD1_BREAKER", "S1VL2_BBS1_BBS3")
.stream()
.map(network::getSwitch).toList();

// Contingencies
Expand Down Expand Up @@ -77,13 +84,79 @@ void testNetworkElementsLists() {
assertThat(metrixNetwork.getHvdcLineList()).containsExactlyInAnyOrderElementsOf(network.getHvdcLines());
assertThat(metrixNetwork.getBusList()).containsExactlyInAnyOrderElementsOf(network.getBusBreakerView().getBuses());
assertThat(metrixNetwork.getContingencyList()).containsExactlyInAnyOrderElementsOf(List.of(a, b));

assertTrue(metrixNetwork.isMapped(network.getIdentifiable("S1VL2_GH2_BREAKER")));
assertTrue(metrixNetwork.isMapped(network.getIdentifiable("DL")));
assertTrue(metrixNetwork.isMapped(network.getIdentifiable("GH2")));
assertTrue(metrixNetwork.isMapped(network.getIdentifiable("HVDC1")));
assertTrue(metrixNetwork.isMapped(network.getIdentifiable("LINE_S2S3")));
assertTrue(metrixNetwork.isMapped(network.getIdentifiable("LD5")));
assertFalse(metrixNetwork.isMapped(network.getIdentifiable("S2VL1_BBS")));
}

@Test
void testNetworkBusBreakerElementsLists() {
// Mapped switches
Set<String> mappedSwitches = Set.of("S1VL2_GH2_BREAKER", "S3VL1_LINES3S4_BREAKER", "S1VL1_LD1_BREAKER",
"S1VL3_DL_BREAKER", "S1VL2_BBS1_BBS3", "S1VL3_3WT_BREAKER");

// Network
Network network = createBusBreakerNetwork(mappedSwitches);

// Expected switch list in MetrixNetwork: switches next to branches (lines, two windings transformers) are not present
List<Switch> switchList = mappedSwitches.stream().map(network::getSwitch).toList();

// Contingencies
Contingency a = new Contingency("a", Collections.singletonList(new BranchContingency("LINE_S2S3")));
Contingency b = new Contingency("b", Arrays.asList(
new BranchContingency("LINE_S2S3"),
new BranchContingency("LINE_S3S4")));

// Create a contingency provider
ContingenciesProvider contingenciesProvider = networkLocal -> {
a.addExtension(Probability.class, new Probability(0.002d, null));
b.addExtension(Probability.class, new Probability(null, "variable_ts1"));
return Arrays.asList(a, b);
};

// Initialize the MetrixNetwork
MetrixNetwork metrixNetwork = MetrixNetwork.create(network, contingenciesProvider, mappedSwitches, new MetrixParameters(), (Path) null);

// Check the lists
assertThat(metrixNetwork.getCountryList()).containsExactlyInAnyOrderElementsOf(Collections.singletonList("Undefined"));
assertThat(metrixNetwork.getLoadList()).containsExactlyInAnyOrderElementsOf(network.getLoads());
assertThat(metrixNetwork.getGeneratorList()).containsExactlyInAnyOrderElementsOf(network.getGenerators());
assertThat(metrixNetwork.getGeneratorTypeList()).containsExactlyInAnyOrderElementsOf(List.of("HYDRO", "THERMAL"));
assertThat(metrixNetwork.getLineList()).containsExactlyInAnyOrderElementsOf(network.getLines());
assertThat(metrixNetwork.getTwoWindingsTransformerList()).containsExactlyInAnyOrderElementsOf(network.getTwoWindingsTransformers());
assertThat(metrixNetwork.getThreeWindingsTransformerList()).containsExactlyInAnyOrderElementsOf(network.getThreeWindingsTransformers());
assertThat(metrixNetwork.getDanglingLineList()).containsExactlyInAnyOrderElementsOf(network.getDanglingLines());
assertThat(metrixNetwork.getSwitchList()).containsExactlyInAnyOrderElementsOf(switchList);
assertThat(metrixNetwork.getHvdcLineList()).containsExactlyInAnyOrderElementsOf(network.getHvdcLines());
assertThat(metrixNetwork.getBusList()).containsExactlyInAnyOrderElementsOf(network.getBusBreakerView().getBuses());
assertThat(metrixNetwork.getContingencyList()).containsExactlyInAnyOrderElementsOf(List.of(a, b));
}

private Network createBusBreakerNetwork(Set<String> mappedSwitches) {
// Initial network
Network network = createNetwork();

// Set some switches as retained
List<Switch> retainedSwitches = mappedSwitches.stream().map(network::getSwitch).toList();
network.getSwitchStream()
.forEach(sw -> sw.setRetained(retainedSwitches.contains(sw)));

// Export the network as BusBreaker
Path exportedFile = fileSystem.getPath("./network.xiidm");
NetworkSerDe.write(network, new ExportOptions().setTopologyLevel(TopologyLevel.BUS_BREAKER), exportedFile);
return NetworkSerDe.read(exportedFile);
}

private Network createNetwork() {
// Initial network
Network network = FourSubstationsNodeBreakerFactory.create();

// We add a substation, a ThreeWindingsTransformer and a DanglingLine
// We add a voltage level, a ThreeWindingsTransformer and a DanglingLine
VoltageLevel s1vl3 = network.getSubstation("S1").newVoltageLevel()
.setId("S1VL3")
.setNominalV(225.0)
Expand Down Expand Up @@ -137,7 +210,7 @@ private Network createNetwork() {

// Dangling line
createSwitch(s1vl3, "S1VL3_BBS_DL_DISCONNECTOR", SwitchKind.DISCONNECTOR, 0, 3);
createSwitch(s1vl3, "S1VL3_DL_BREAKER", SwitchKind.BREAKER, 3, 4);
createSwitch(s1vl3, "S1VL3_DL_BREAKER", SwitchKind.BREAKER, 4, 3);
s1vl3.newDanglingLine()
.setId("DL")
.setR(10.0)
Expand All @@ -149,6 +222,16 @@ private Network createNetwork() {
.setNode(4)
.add();

// We add another bus bar section and link it to the others with a breaker
network.getVoltageLevel("S1VL2").getNodeBreakerView().newBusbarSection()
.setId("S1VL2_BBS3")
.setName("S1VL2_BBS3")
.setNode(90)
.add();
createSwitch(network.getVoltageLevel("S1VL2"), "S1VL2_BBS1_DISCONNECTOR", SwitchKind.DISCONNECTOR, 0, 91);
createSwitch(network.getVoltageLevel("S1VL2"), "S1VL2_BBS3_DISCONNECTOR", SwitchKind.DISCONNECTOR, 92, 90);
createSwitch(network.getVoltageLevel("S1VL2"), "S1VL2_BBS1_BBS3", SwitchKind.BREAKER, 91, 92);

return network;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@ class SimpleMappingData extends FilteredData {
// for each filtered equipment, compute the distribution key and add it to the config
if (!filteredEquipments.isEmpty()) {

if (((Switch) filteredEquipments[0]).voltageLevel.topologyKind == TopologyKind.BUS_BREAKER) {
throw new TimeSeriesMappingException("Bus breaker topology not supported for switch mapping")
}

filteredEquipments.forEach({ Identifiable identifiable ->
configLoader.addEquipmentMapping(breakerType, spec.timeSeriesName, identifiable.id, NumberDistributionKey.ONE, EquipmentVariable.OPEN)
})
Expand Down
Loading