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

Do not allow switches with the same node or bus at both ends. #1991

Merged
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public Switch add() {
if (busId2 == null) {
throw new ValidationException(this, "second connection bus is not set");
}
if (busId1.equals(busId2)) {
throw new ValidationException(this, "same bus at both ends");
}

SwitchImpl aSwitch = new SwitchImpl(BusBreakerVoltageLevel.this, id, getName(), isFictitious(), SwitchKind.BREAKER, open, true);
addSwitch(aSwitch, busId1, busId2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ public Switch add() {
if (node2 == null) {
throw new ValidationException(this, "second connection node is not set");
}
if (node1.equals(node2)) {
throw new ValidationException(this, "same node at both ends");
}
if (kind == null) {
throw new ValidationException(this, "kind is not set");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.iidm.network.impl.tck;

import com.powsybl.iidm.network.tck.AbstractSwitchBusBreakerTest;

public class SwitchBusBreakerTest extends AbstractSwitchBusBreakerTest { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.iidm.network.impl.tck;

import com.powsybl.iidm.network.tck.AbstractSwitchNodeBreakerTest;

public class SwitchNodeBreakerTest extends AbstractSwitchNodeBreakerTest { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.iidm.network.tck;

import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.test.BatteryNetworkFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public abstract class AbstractSwitchBusBreakerTest {

@Rule
public ExpectedException thrown = ExpectedException.none();

private Network network;
private VoltageLevel voltageLevel;

@Before
public void initNetwork() {
network = BatteryNetworkFactory.create();
voltageLevel = network.getVoltageLevel("VLGEN");
}

@Test
public void addSwitchWithSameBusAtBothEnds() {
thrown.expect(ValidationException.class);
thrown.expectMessage("Switch 'Sw1': same bus at both ends");
Bus bus = voltageLevel.getBusBreakerView().getBus("NGEN");
voltageLevel.getBusBreakerView().newSwitch()
.setId("Sw1")
.setBus1(bus.getId())
.setBus2(bus.getId())
.add();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.iidm.network.tck;

import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.test.FictitiousSwitchFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public abstract class AbstractSwitchNodeBreakerTest {

@Rule
public ExpectedException thrown = ExpectedException.none();

private Network network;
private VoltageLevel voltageLevel;

@Before
public void initNetwork() {
network = FictitiousSwitchFactory.create();
voltageLevel = network.getVoltageLevel("C");
}

@Test
public void addSwitchWithSameNodeAtBothEnds() {
thrown.expect(ValidationException.class);
thrown.expectMessage("Switch 'Sw1': same node at both ends");
int newNode = voltageLevel.getNodeBreakerView().getMaximumNodeIndex() + 1;
voltageLevel.getNodeBreakerView().newSwitch()
.setId("Sw1")
.setNode1(newNode)
.setNode2(newNode)
.setKind(SwitchKind.BREAKER)
.add();
}

@Test
public void addSwitchWithNullKind() {
thrown.expect(ValidationException.class);
thrown.expectMessage("Switch 'Sw1': kind is not set");
int newNode1 = voltageLevel.getNodeBreakerView().getMaximumNodeIndex() + 1;
int newNode2 = newNode1 + 1;
voltageLevel.getNodeBreakerView().newSwitch()
.setId("Sw1")
.setNode1(newNode1)
.setNode2(newNode2)
.add();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,19 @@ protected boolean hasSubElements(T identifiable, NetworkXmlWriterContext context
return hasSubElements(identifiable);
}

protected boolean isValid(T identifiable, P parent) {
return true;
}

protected abstract void writeRootElementAttributes(T identifiable, P parent, NetworkXmlWriterContext context) throws XMLStreamException;

protected void writeSubElements(T identifiable, P parent, NetworkXmlWriterContext context) throws XMLStreamException {
}

public final void write(T identifiable, P parent, NetworkXmlWriterContext context) throws XMLStreamException {
if (!isValid(identifiable, parent)) {
return;
}
boolean isNotEmptyElement = hasSubElements(identifiable, context) || identifiable.hasProperty() || identifiable.hasAliases();
if (isNotEmptyElement) {
context.getWriter().writeStartElement(context.getVersion().getNamespaceURI(context.isValid()), getRootElementName());
Expand Down Expand Up @@ -94,7 +101,9 @@ protected void readSubElements(T identifiable, NetworkXmlReaderContext context)

protected void readElement(String id, A adder, NetworkXmlReaderContext context) throws XMLStreamException {
T identifiable = readRootElementAttributes(adder, context);
readSubElements(identifiable, context);
if (identifiable != null) {
readSubElements(identifiable, context);
}
}

public final void read(P parent, NetworkXmlReaderContext context) throws XMLStreamException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,26 @@

import javax.xml.stream.XMLStreamException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com>
*/
public class BusBreakerViewSwitchXml extends AbstractSwitchXml<VoltageLevel.BusBreakerView.SwitchAdder> {

static final BusBreakerViewSwitchXml INSTANCE = new BusBreakerViewSwitchXml();

@Override
protected boolean isValid(Switch s, VoltageLevel vl) {
VoltageLevel.BusBreakerView v = vl.getBusBreakerView();
if (v.getBus1(s.getId()).getId().equals(v.getBus2(s.getId()).getId())) {
LOGGER.warn("Discard switch with same bus at both ends. Id: {}", s.getId());
return false;
}
return true;
}

@Override
protected void writeRootElementAttributes(Switch s, VoltageLevel vl, NetworkXmlWriterContext context) throws XMLStreamException {
super.writeRootElementAttributes(s, vl, context);
Expand All @@ -45,9 +58,17 @@ protected Switch readRootElementAttributes(VoltageLevel.BusBreakerView.SwitchAdd
});
String bus1 = context.getAnonymizer().deanonymizeString(context.getReader().getAttributeValue(null, "bus1"));
String bus2 = context.getAnonymizer().deanonymizeString(context.getReader().getAttributeValue(null, "bus2"));
return adder.setOpen(open)
// Discard switches with same bus at both ends
if (bus1.equals(bus2)) {
LOGGER.warn("Discard switch with same bus at both ends. Id: {}", context.getReader().getAttributeValue(null, "id"));
return null;
} else {
return adder.setOpen(open)
.setBus1(bus1)
.setBus2(bus2)
.add();
}
}

private static final Logger LOGGER = LoggerFactory.getLogger(BusBreakerViewSwitchXml.class);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,26 @@

import javax.xml.stream.XMLStreamException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com>
*/
public class NodeBreakerViewSwitchXml extends AbstractSwitchXml<VoltageLevel.NodeBreakerView.SwitchAdder> {

static final NodeBreakerViewSwitchXml INSTANCE = new NodeBreakerViewSwitchXml();

@Override
protected boolean isValid(Switch s, VoltageLevel vl) {
VoltageLevel.NodeBreakerView v = vl.getNodeBreakerView();
if (v.getNode1(s.getId()) == v.getNode2(s.getId())) {
LOGGER.warn("Discard switch with same node at both ends. Id: {}", s.getId());
return false;
}
return true;
}

@Override
protected void writeRootElementAttributes(Switch s, VoltageLevel vl, NetworkXmlWriterContext context) throws XMLStreamException {
super.writeRootElementAttributes(s, vl, context);
Expand All @@ -45,11 +58,19 @@ protected Switch readRootElementAttributes(VoltageLevel.NodeBreakerView.SwitchAd
});
int node1 = XmlUtil.readIntAttribute(context.getReader(), "node1");
int node2 = XmlUtil.readIntAttribute(context.getReader(), "node2");
return adder.setKind(kind)
// Discard switches with same node at both ends
if (node1 == node2) {
LOGGER.warn("Discard switch with same node at both ends. Id: {}", context.getReader().getAttributeValue(null, "id"));
return null;
} else {
return adder.setKind(kind)
.setRetained(retained)
.setOpen(open)
.setNode1(node1)
.setNode2(node2)
.add();
}
}

private static final Logger LOGGER = LoggerFactory.getLogger(NodeBreakerViewSwitchXml.class);
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ protected InputStream getVersionedNetworkAsStream(String fileName, IidmXmlVersio
return getClass().getResourceAsStream(getVersionedNetworkPath(fileName, version));
}

/**
* Return an input stream of the test resource IIDM-XML file with a given file name.
*/
protected InputStream getNetworkAsStream(String fileName) {
return getClass().getResourceAsStream(fileName);
}

/**
* Execute a round trip test on the test resource IIDM-XML file with a given file name for the given IIDM-XML versions.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.iidm.xml;

import com.powsybl.iidm.network.Network;
import org.junit.Test;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

/**
* @author Luma Zamarreño <zamarrenolm at aia.es>
* @author José Antonio Marqués <marquesja at aia.es>
*/
public class SwitchTest extends AbstractXmlConverterTest {

@Test
public void readAndDiscardSwitchWithSameNodeAtBothEnds() {
//Network n = NetworkXml.read(getVersionedNetworkAsStream("switchWithSameNodeAtBothEnds.xml", CURRENT_IIDM_XML_VERSION));
Network n = NetworkXml.read(getNetworkAsStream("/switches/switchWithSameNodeAtBothEnds.xiidm"));

// Check that the "looped-switch" has been discarded
assertEquals(2, n.getSwitchCount());
assertNull(n.getSwitch("looped-switch"));
}

@Test
public void readAndDiscardSwitchWithSameBusAtBothEnds() {
Network n = NetworkXml.read(getNetworkAsStream("/switches/switchWithSameBusAtBothEnds.xiidm"));

// Check that the "looped-switch" has been discarded
assertEquals(0, n.getSwitchCount());
assertNull(n.getSwitch("looped-switch"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_7" xmlns:ld="http://www.itesla_project.eu/schema/iidm/ext/load_detail/1_0" id="test" caseDate="2016-06-27T12:27:58.535+02:00" forecastDistance="0" sourceFormat="test" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:substation id="S" country="FR">
<iidm:voltageLevel id="VL" nominalV="400.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="B"/>
<iidm:switch id="looped-switch" name="BK" kind="BREAKER" retained="true" open="false" bus1="B" bus2="B"/>
</iidm:busBreakerTopology>
<iidm:load id="L" loadType="UNDEFINED" p0="100.0" q0="50.0" bus="B" connectableBus="B"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:extension id="L">
<ld:detail fixedActivePower="40.0" fixedReactivePower="20.0" variableActivePower="60.0" variableReactivePower="30.0"/>
</iidm:extension>
</iidm:network>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_7" id="internal-connections" caseDate="2018-11-08T12:33:26.208+01:00" forecastDistance="0" sourceFormat="test" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:substation id="s1" country="ES">
<iidm:voltageLevel id="vl1" nominalV="400.0" topologyKind="NODE_BREAKER">
<iidm:nodeBreakerTopology>
<iidm:busbarSection id="b1" node="2"/>
<iidm:switch id="br1" kind="BREAKER" retained="false" open="false" node1="1" node2="2"/>
<iidm:switch id="looped-switch" kind="BREAKER" retained="false" open="false" node1="1" node2="1"/>
<iidm:internalConnection node1="0" node2="1"/>
<iidm:internalConnection node1="3" node2="4"/>
</iidm:nodeBreakerTopology>
<iidm:generator id="g1" energySource="OTHER" minP="0.0" maxP="100.0" voltageRegulatorOn="true" targetP="10.0" targetV="400.0" node="0">
<iidm:minMaxReactiveLimits minQ="-1.7976931348623157E308" maxQ="1.7976931348623157E308"/>
</iidm:generator>
</iidm:voltageLevel>
</iidm:substation>
<iidm:substation id="s2" country="ES">
<iidm:voltageLevel id="vl2" nominalV="400.0" topologyKind="NODE_BREAKER">
<iidm:nodeBreakerTopology>
<iidm:busbarSection id="b2" node="2"/>
<iidm:switch id="br2" kind="BREAKER" retained="false" open="false" node1="1" node2="2"/>
<iidm:internalConnection node1="0" node2="1"/>
<iidm:internalConnection node1="3" node2="4"/>
</iidm:nodeBreakerTopology>
<iidm:load id="l2" loadType="UNDEFINED" p0="10.0" q0="1.0" node="0"/>
</iidm:voltageLevel>
</iidm:substation>
<iidm:line id="line1-2" r="0.1" x="10.0" g1="0.0" b1="0.0" g2="0.0" b2="0.0" node1="4" voltageLevelId1="vl1" node2="4" voltageLevelId2="vl2"/>
</iidm:network>