Skip to content

Commit

Permalink
Fix ReferenceTerminals and SlackTerminal iIDM extension with merged n…
Browse files Browse the repository at this point in the history
…etworks and subnetworks (#3076)

* Fix ReferenceTerminals and SlackTerminal iIDM extension with merged networks and subnetworks

Signed-off-by: Damien Jeandemange <damien.jeandemange@artelys.com>
(cherry picked from commit 6401496)
  • Loading branch information
jeandemanged authored and olperr1 committed Jun 24, 2024
1 parent 76e2392 commit d29a7ca
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ default String getName() {
ReferenceTerminals addReferenceTerminal(Terminal terminal);

/**
* Deletes all defined reference terminals in the network for the current variant
* Deletes all defined reference terminals in the network and all its subnetworks for the current variant
* @param network network whose reference terminals should be deleted
*/
static void reset(Network network) {
Expand All @@ -51,10 +51,13 @@ static void reset(Network network) {
.add();
}
ext.reset();
// reset also all subnetworks
network.getSubnetworks().forEach(ReferenceTerminals::reset);
}

/**
* Defines/add a terminal as reference in the network for the current variant
* Defines/add a terminal as reference in the network for the current variant.
* In case of a merged network with subnetwork, the extension is placed on the root/merged network.
* @param terminal terminal to be added as reference terminal
*/
static void addTerminal(Terminal terminal) {
Expand All @@ -70,7 +73,10 @@ static void addTerminal(Terminal terminal) {
}

/**
* Gets the reference terminals in the network for the current variant
* Gets the reference terminals in the network for the current variant.
* <p> Note: This method returns only the terminal from the extension attached to the provided network.
* In case of a merged network with subnetworks, be careful whether you want the extension
* of the merged network or of a subnetwork.</p>
* @param network network to get reference terminals from
*/
static Set<Terminal> getTerminals(Network network) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ public interface SlackTerminal extends Extension<VoltageLevel> {
String NAME = "slackTerminal";

/**
* Set the terminal of all SlackTerminal extensions from the given network to null. If the extension is empty,
* meaning that for each variant the terminal is null, this method automatically remove the extension.
* Set the terminal of all SlackTerminal extensions from the given network and all its subnetworks to null.
* If the extension is empty, meaning that for each variant the terminal is null, this method automatically
* remove the extension.
*
* @param network A network to cleanup
*/
static void reset(Network network) {
network.getVoltageLevels().forEach(vl -> reset(vl, null));
// reset also all subnetworks
network.getSubnetworks().forEach(sn -> sn.getVoltageLevels().forEach(vl -> reset(vl, null)));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class ReferenceTerminalsImpl extends AbstractMultiVariantIdentifiableExtension<N

private final class ReferenceTerminalsListener extends DefaultNetworkListener {
@Override
public void beforeRemoval(Identifiable identifiable) {
public void beforeRemoval(Identifiable<?> identifiable) {
if (identifiable instanceof Connectable<?> connectable) {
// if connectable removed from network, remove its terminals from this extension
terminalsPerVariant.forEach(referenceTerminals -> connectable.getTerminals().forEach(referenceTerminals::remove));
Expand All @@ -39,12 +39,21 @@ public ReferenceTerminalsImpl(Network network, Set<Terminal> terminals) {
Collections.nCopies(getVariantManagerHolder().getVariantManager().getVariantArraySize(), new LinkedHashSet<>()));
setReferenceTerminals(terminals);
this.referenceTerminalsListener = new ReferenceTerminalsListener();
network.addListener(this.referenceTerminalsListener);
}

@Override
public void setExtendable(Network extendable) {
super.setExtendable(extendable);
if (extendable != null) {
// Add the listener, this will be done both extension creation, but also on extension transfer when merging and detaching.
extendable.getNetwork().addListener(this.referenceTerminalsListener);
}
}

@Override
protected void cleanup() {
getExtendable().removeListener(this.referenceTerminalsListener);
// when extension removed from extendable, remove the listener. This will happen when merging and detaching.
getExtendable().getNetwork().removeListener(this.referenceTerminalsListener);
}

@Override
Expand Down Expand Up @@ -103,9 +112,19 @@ public void allocateVariantArrayElement(int[] indexes, int sourceIndex) {
}

private static void checkTerminalInNetwork(Terminal terminal, Network network) {
if (!terminal.getVoltageLevel().getNetwork().equals(network)) {
throw new PowsyblException("Terminal given is not in the right Network ("
+ terminal.getVoltageLevel().getNetwork().getId() + " instead of " + network.getId() + ")");
final boolean extendableIsRootNetwork = network.getNetwork().equals(network);
if (extendableIsRootNetwork) {
// it is all fine as long as the terminal belongs to the merged network
if (!terminal.getVoltageLevel().getNetwork().equals(network)) {
throw new PowsyblException("Terminal given is not in the right Network ("
+ terminal.getVoltageLevel().getNetwork().getId() + " instead of " + network.getId() + ")");
}
} else {
// subnetwork: the terminal must be in the subnetwork
if (!terminal.getVoltageLevel().getParentNetwork().equals(network)) {
throw new PowsyblException("Terminal given is not in the right Network ("
+ terminal.getVoltageLevel().getParentNetwork().getId() + " instead of " + network.getId() + ")");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ public void testVariantsCloning() {
public void testWrongNetwork() {
Network other = EurostagTutorialExample1Factory.create();
Terminal terminal = other.getBusBreakerView().getBus("NHV1").getConnectedTerminals().iterator().next();
PowsyblException ex1 = assertThrows(PowsyblException.class, () -> network.newExtension(ReferenceTerminalsAdder.class)
.withTerminals(Set.of(terminal))
.add());
ReferenceTerminalsAdder referenceTerminalsAdder = network.newExtension(ReferenceTerminalsAdder.class)
.withTerminals(Set.of(terminal));
PowsyblException ex1 = assertThrows(PowsyblException.class, referenceTerminalsAdder::add);
assertEquals("Terminal given is not in the right Network (sim1 instead of fourSubstations)", ex1.getMessage());
network.newExtension(ReferenceTerminalsAdder.class)
.withTerminals(Set.of())
Expand All @@ -196,6 +196,77 @@ public void testWrongNetwork() {
assertEquals("Terminal given is not in the right Network (sim1 instead of fourSubstations)", ex2.getMessage());
}

@Test
public void testWithSubnetwork() {
Network merged = Network.merge(network, EurostagTutorialExample1Factory.create());
Network subnetwork = merged.getSubnetwork("fourSubstations");
Network sim1subnetwork = merged.getSubnetwork("sim1");
gh1 = merged.getGenerator("GH1");
gh2 = merged.getGenerator("GH2");
Terminal gh1Terminal = gh1.getTerminal();
Terminal gh2Terminal = gh2.getTerminal();

// gh1 is in subnetwork and can be added to subnetwork extension
subnetwork.newExtension(ReferenceTerminalsAdder.class)
.withTerminals(Set.of(gh1Terminal))
.add();
ReferenceTerminals extSubnetwork = subnetwork.getExtension(ReferenceTerminals.class);
assertEquals(1, extSubnetwork.getReferenceTerminals().size());
assertTrue(extSubnetwork.getReferenceTerminals().contains(gh1Terminal));

// gh2 is in subnetwork and can be added to root network extension
merged.newExtension(ReferenceTerminalsAdder.class)
.withTerminals(Set.of(gh2Terminal))
.add();
ReferenceTerminals extMergedNetwork = merged.getExtension(ReferenceTerminals.class);
assertEquals(1, extMergedNetwork.getReferenceTerminals().size());
assertTrue(extMergedNetwork.getReferenceTerminals().contains(gh2Terminal));

// we can reset everything via this method
ReferenceTerminals.reset(merged);
assertTrue(ReferenceTerminals.getTerminals(merged).isEmpty());

// we can add easily to merged/root network via this method
ReferenceTerminals.addTerminal(gh1Terminal);
extMergedNetwork = merged.getExtension(ReferenceTerminals.class);
extSubnetwork = subnetwork.getExtension(ReferenceTerminals.class);
assertEquals(1, extMergedNetwork.getReferenceTerminals().size());
assertEquals(0, extSubnetwork.getReferenceTerminals().size()); // not added to subnetwork
// same as above, but using the more user-friendly static methods from ReferenceTerminals.
assertEquals(1, ReferenceTerminals.getTerminals(merged).size());
assertEquals(0, ReferenceTerminals.getTerminals(subnetwork).size());

// we can't add gh1 to sim1
sim1subnetwork.newExtension(ReferenceTerminalsAdder.class)
.withTerminals(Set.of())
.add();
ReferenceTerminals extSim1 = sim1subnetwork.getExtension(ReferenceTerminals.class);
PowsyblException ex = assertThrows(PowsyblException.class, () -> extSim1.addReferenceTerminal(gh1Terminal));
assertEquals("Terminal given is not in the right Network (fourSubstations instead of sim1)", ex.getMessage());
}

@Test
public void testListenersTransferOnMergeAndDetach() {
Network network1 = FourSubstationsNodeBreakerFactory.create();
Network network2 = EurostagTutorialExample1Factory.create();
ReferenceTerminals.addTerminal(network1.getGenerator("GH1").getTerminal());
ReferenceTerminals.addTerminal(network1.getGenerator("GH2").getTerminal());

Network merged = Network.merge(network1, network2);
network1 = merged.getSubnetwork("fourSubstations");

// check listener is now effective on merged network
assertEquals(2, ReferenceTerminals.getTerminals(network1).size());
merged.getGenerator("GH1").remove();
assertEquals(1, ReferenceTerminals.getTerminals(network1).size());

Network network1detached = network1.detach();
// check listener is now effective on detached network
assertEquals(1, ReferenceTerminals.getTerminals(network1detached).size());
network1detached.getGenerator("GH2").remove();
assertEquals(0, ReferenceTerminals.getTerminals(network1detached).size());
}

@Test
public void testRemoveEquipment() {
network.newExtension(ReferenceTerminalsAdder.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ static Network createBusBreakerNetwork() {
.setQ0(50)
.add();

Substation s1 = network.newSubstation()
.setId("S1")
.setCountry(Country.FR)
.add();
VoltageLevel vl1 = s.newVoltageLevel()
.setId("VL1")
.setNominalV(400)
Expand Down Expand Up @@ -260,4 +256,32 @@ public void variantsResetTest() {
assertNull(vlhv1.getExtension(SlackTerminal.class));

}

@Test
public void testWithSubnetwork() {
Network network1 = createBusBreakerNetwork();
SlackTerminal.attach(network1.getBusBreakerView().getBus("B"));
Network network2 = EurostagTutorialExample1Factory.create();
SlackTerminal.attach(network2.getBusBreakerView().getBus("NHV1"));

Network merged = Network.merge(network1, network2);
network1 = merged.getSubnetwork("test");
network2 = merged.getSubnetwork("sim1");

// still there after merge
assertNotNull(merged.getVoltageLevel("VL").getExtension(SlackTerminal.class));
assertNotNull(merged.getVoltageLevel("VLHV1").getExtension(SlackTerminal.class));

// we can reset everything (including subnetworks)
SlackTerminal.reset(merged);
assertNull(merged.getVoltageLevel("VL").getExtension(SlackTerminal.class));
assertNull(merged.getVoltageLevel("VLHV1").getExtension(SlackTerminal.class));

// we can reset only a subnetwork
SlackTerminal.attach(network1.getBusBreakerView().getBus("B"));
SlackTerminal.attach(network2.getBusBreakerView().getBus("NHV1"));
SlackTerminal.reset(network1);
assertNull(merged.getVoltageLevel("VL").getExtension(SlackTerminal.class)); // reset
assertNotNull(merged.getVoltageLevel("VLHV1").getExtension(SlackTerminal.class)); // untouched
}
}

0 comments on commit d29a7ca

Please sign in to comment.