SetGeneratorToLocalRegulationTest.java
/**
* Copyright (c) 2024-2025, 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/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.iidm.modification;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.PowsyblCoreReportResourceBundle;
import com.powsybl.commons.test.PowsyblCoreTestReportResourceBundle;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.commons.test.TestUtil;
import com.powsybl.iidm.network.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.StringWriter;
import java.time.ZonedDateTime;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Romain Courtier {@literal <romain.courtier at rte-france.com>}
*/
class SetGeneratorToLocalRegulationTest {
Network network;
@BeforeEach
void setUp() {
network = createTestNetwork();
}
@Test
void setLocalRegulationTest() throws IOException {
assertNotNull(network);
Generator gen1 = network.getGenerator("GEN1");
Generator gen2 = network.getGenerator("GEN2");
Generator gen3 = network.getGenerator("GEN3");
Generator gen4 = network.getGenerator("GEN4");
// Before applying the network modification,
// gen1 regulates remotely at 1.05 pu (420 kV) and gen2 regulates locally at 1.05 pu (21 kV).
assertNotEquals(gen1.getId(), gen1.getRegulatingTerminal().getConnectable().getId());
assertEquals(420.0, gen1.getTargetV());
assertEquals(gen2.getId(), gen2.getRegulatingTerminal().getConnectable().getId());
assertEquals(25.0, gen2.getTargetV());
assertEquals(gen3.getId(), gen3.getRegulatingTerminal().getConnectable().getId());
assertEquals(22.0, gen3.getTargetV());
assertNotEquals(gen4.getId(), gen4.getRegulatingTerminal().getConnectable().getId());
assertEquals(21.0, gen4.getTargetV());
ReportNode reportNode = ReportNode.newRootReportNode()
.withResourceBundles(PowsyblCoreTestReportResourceBundle.TEST_BASE_NAME, PowsyblCoreReportResourceBundle.BASE_NAME)
.withMessageTemplate("rootReportNode")
.build();
new SetGeneratorToLocalRegulation("GEN1").apply(network, reportNode);
new SetGeneratorToLocalRegulation("GEN2").apply(network, reportNode);
SetGeneratorToLocalRegulation modification = new SetGeneratorToLocalRegulation("WRONG_ID");
assertDoesNotThrow(() -> modification.apply(network, false, ReportNode.NO_OP));
PowsyblException e = assertThrows(PowsyblException.class, () -> modification.apply(network, true, reportNode));
assertEquals("Generator 'WRONG_ID' not found", e.getMessage());
// After applying the network modification, GEN1 generator regulates locally at same targetV of GEN3 (closest to nominal V).
assertEquals(gen1.getId(), gen1.getRegulatingTerminal().getConnectable().getId());
assertEquals(22.0, gen1.getTargetV());
assertEquals(gen2.getId(), gen2.getRegulatingTerminal().getConnectable().getId());
assertEquals(25.0, gen2.getTargetV());
assertEquals(gen3.getId(), gen3.getRegulatingTerminal().getConnectable().getId());
assertEquals(22.0, gen3.getTargetV());
// Report node has been updated with the change for gen1.
StringWriter sw = new StringWriter();
reportNode.print(sw);
assertEquals("""
+ Set generators to local regulation
Changed regulation for generator GEN1 to local instead of remote
""", TestUtil.normalizeLineSeparator(sw.toString()));
new SetGeneratorToLocalRegulation("GEN4").apply(network, reportNode);
// After applying the network modification, GEN4 generator regulates locally at voltage level nominal V
assertEquals(gen4.getId(), gen4.getRegulatingTerminal().getConnectable().getId());
assertEquals(420.0, gen4.getTargetV());
}
@Test
void hasImpactTest() {
SetGeneratorToLocalRegulation modification;
modification = new SetGeneratorToLocalRegulation("WRONG_ID");
assertEquals(NetworkModificationImpact.CANNOT_BE_APPLIED, modification.hasImpactOnNetwork(network));
modification = new SetGeneratorToLocalRegulation("GEN1");
assertEquals(NetworkModificationImpact.HAS_IMPACT_ON_NETWORK, modification.hasImpactOnNetwork(network));
modification = new SetGeneratorToLocalRegulation("GEN2");
assertEquals(NetworkModificationImpact.NO_IMPACT_ON_NETWORK, modification.hasImpactOnNetwork(network));
}
@Test
void getNameTest() {
SetGeneratorToLocalRegulation modification = new SetGeneratorToLocalRegulation("GEN1");
assertEquals("SetGeneratorToLocalRegulation", modification.getName());
}
private Network createTestNetwork() {
Network n = Network.create("test_network", "test");
n.setCaseDate(ZonedDateTime.parse("2021-12-07T18:45:00.000+02:00"));
Substation st = n.newSubstation()
.setId("ST")
.setCountry(Country.FR)
.add();
VoltageLevel vl400 = st.newVoltageLevel()
.setId("VL400")
.setNominalV(400)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();
vl400.getNodeBreakerView().newBusbarSection()
.setId("BBS")
.setNode(0)
.add();
VoltageLevel vl20 = st.newVoltageLevel()
.setId("VL20")
.setNominalV(20)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();
vl20.getNodeBreakerView().newBusbarSection()
.setId("BBS20")
.setNode(0)
.add();
vl20.newGenerator()
.setId("GEN1")
.setNode(3)
.setEnergySource(EnergySource.NUCLEAR)
.setMinP(100)
.setMaxP(200)
.setTargetP(200)
.setVoltageRegulatorOn(true)
.setTargetV(420)
.setRegulatingTerminal(n.getBusbarSection("BBS").getTerminal())
.add();
vl20.newGenerator()
.setId("GEN2")
.setNode(4)
.setEnergySource(EnergySource.NUCLEAR)
.setMinP(100)
.setMaxP(200)
.setTargetP(200)
.setVoltageRegulatorOn(true)
.setTargetV(25)
// No regulatingTerminal set == use its own terminal for regulation
.add();
vl20.newGenerator()
.setId("GEN3")
.setNode(6)
.setEnergySource(EnergySource.NUCLEAR)
.setMinP(100)
.setMaxP(200)
.setTargetP(200)
.setVoltageRegulatorOn(true)
.setTargetV(22)
// No regulatingTerminal set == use its own terminal for regulation
.add();
vl400.newGenerator()
.setId("GEN4")
.setNode(7)
.setEnergySource(EnergySource.NUCLEAR)
.setMinP(100)
.setMaxP(200)
.setTargetP(200)
.setVoltageRegulatorOn(true)
.setTargetV(21)
.setRegulatingTerminal(n.getBusbarSection("BBS20").getTerminal())
.add();
st.newTwoWindingsTransformer()
.setId("T2W")
.setName("T2W")
.setR(1.0)
.setX(10.0)
.setG(0.0)
.setB(0.0)
.setRatedU1(20.0)
.setRatedU2(400.0)
.setRatedS(250.0)
.setVoltageLevel1("VL400")
.setVoltageLevel2("VL20")
.setNode1(1)
.setNode2(2)
.add();
createSwitch(vl20, "BBS20_DISCONNECTOR", SwitchKind.DISCONNECTOR, false, 0, 1);
createSwitch(vl20, "BBS20_BREAKER_1_2", SwitchKind.BREAKER, false, 1, 2);
createSwitch(vl20, "BBS20_BREAKER_2_3", SwitchKind.BREAKER, false, 2, 3);
createSwitch(vl20, "BBS20_BREAKER_3_4", SwitchKind.BREAKER, false, 3, 4);
createSwitch(vl20, "BBS20_BREAKER_1_4", SwitchKind.BREAKER, false, 1, 4);
createSwitch(vl20, "BBS20_BREAKER_2_6", SwitchKind.BREAKER, false, 2, 6);
Load load1 = vl20.newLoad()
.setId("LD1")
.setLoadType(LoadType.UNDEFINED)
.setP0(80)
.setQ0(10)
.setNode(5)
.add();
load1.getTerminal().setP(80.0).setQ(10.0);
vl20.getNodeBreakerView().getBusbarSection("BBS20").getTerminal().getBusView().getBus()
.setV(224.6139)
.setAngle(2.2822);
return n;
}
private void createSwitch(VoltageLevel vl, String id, SwitchKind kind, boolean open, int node1, int node2) {
vl.getNodeBreakerView().newSwitch()
.setId(id)
.setName(id)
.setKind(kind)
.setRetained(kind.equals(SwitchKind.BREAKER))
.setOpen(open)
.setFictitious(false)
.setNode1(node1)
.setNode2(node2)
.add();
}
}