-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Network modification to set generator regulation to local instead of …
…remote (#3192) * Network modification to force generator to regulate locally instead of remotely * Add javadoc for the class * Network modification constructor takes now a String generatorId as argument, and test the impact on network. * Replace tests on references by tests on IDs * Use throwException boolean argument to log a message or throw an error in case of incorrect input data Signed-off-by: Romain Courtier <romain.courtier@rte-france.com>
- Loading branch information
Showing
3 changed files
with
255 additions
and
0 deletions.
There are no files selected for viewing
88 changes: 88 additions & 0 deletions
88
...dification/src/main/java/com/powsybl/iidm/modification/SetGeneratorToLocalRegulation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* Copyright (c) 2024, 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.report.ReportNode; | ||
import com.powsybl.computation.ComputationManager; | ||
import com.powsybl.iidm.modification.topology.NamingStrategy; | ||
import com.powsybl.iidm.network.Generator; | ||
import com.powsybl.iidm.network.Network; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.Objects; | ||
|
||
import static com.powsybl.iidm.modification.util.ModificationReports.generatorLocalRegulationReport; | ||
|
||
/** | ||
* <p>Network modification to set a generator regulation to local instead of remote.</p> | ||
* <ul> | ||
* <li>Generator's RegulatingTerminal is set to the generator's own Terminal.</li> | ||
* <li>TargetV engineering unit value is adapted but the per unit value remains the same.</li> | ||
* </ul> | ||
* | ||
* @author Romain Courtier {@literal <romain.courtier at rte-france.com>} | ||
*/ | ||
public class SetGeneratorToLocalRegulation extends AbstractNetworkModification { | ||
|
||
private final String generatorId; | ||
private static final Logger LOG = LoggerFactory.getLogger(SetGeneratorToLocalRegulation.class); | ||
|
||
public SetGeneratorToLocalRegulation(String generatorId) { | ||
this.generatorId = Objects.requireNonNull(generatorId); | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return "SetGeneratorToLocalRegulation"; | ||
} | ||
|
||
@Override | ||
public void apply(Network network, NamingStrategy namingStrategy, boolean throwException, ComputationManager computationManager, ReportNode reportNode) { | ||
Generator generator = network.getGenerator(generatorId); | ||
if (generator == null) { | ||
logOrThrow(throwException, "Generator '" + generatorId + "' not found"); | ||
} else if (!generator.getId().equals(generator.getRegulatingTerminal().getConnectable().getId())) { | ||
setLocalRegulation(generator, reportNode); | ||
} | ||
} | ||
|
||
/** | ||
* Change the regulatingTerminal and the targetV of a generator to make it regulate locally. | ||
* @param generator The Generator that should regulate locally. | ||
* @param reportNode The ReportNode for functional logs. | ||
*/ | ||
private void setLocalRegulation(Generator generator, ReportNode reportNode) { | ||
// Calculate the (new) local targetV which should be the same value in per unit as the (old) remote targetV | ||
double remoteTargetV = generator.getTargetV(); | ||
double remoteNominalV = generator.getRegulatingTerminal().getVoltageLevel().getNominalV(); | ||
double localNominalV = generator.getTerminal().getVoltageLevel().getNominalV(); | ||
double localTargetV = localNominalV * remoteTargetV / remoteNominalV; | ||
|
||
// Change the regulation (local instead of remote) | ||
generator.setRegulatingTerminal(generator.getTerminal()); | ||
generator.setTargetV(localTargetV); | ||
|
||
// Notify the change | ||
LOG.info("Changed regulation for generator: {} to local instead of remote", generator.getId()); | ||
generatorLocalRegulationReport(reportNode, generator.getId()); | ||
} | ||
|
||
@Override | ||
public NetworkModificationImpact hasImpactOnNetwork(Network network) { | ||
Generator generator = network.getGenerator(generatorId); | ||
if (generator == null) { | ||
impact = NetworkModificationImpact.CANNOT_BE_APPLIED; | ||
} else if (generator.getId().equals(generator.getRegulatingTerminal().getConnectable().getId())) { | ||
impact = NetworkModificationImpact.NO_IMPACT_ON_NETWORK; | ||
} else { | ||
impact = DEFAULT_IMPACT; | ||
} | ||
return impact; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
159 changes: 159 additions & 0 deletions
159
...cation/src/test/java/com/powsybl/iidm/modification/SetGeneratorToLocalRegulationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
/** | ||
* Copyright (c) 2024, 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.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"); | ||
|
||
// 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(21.0, gen2.getTargetV()); | ||
|
||
ReportNode reportNode = ReportNode.newRootReportNode() | ||
.withMessageTemplate("rootReportNode", "Set generators to local regulation").build(); | ||
new SetGeneratorToLocalRegulation("GEN1").apply(network, reportNode); | ||
new SetGeneratorToLocalRegulation("GEN2").apply(network, reportNode); | ||
SetGeneratorToLocalRegulation modification = new SetGeneratorToLocalRegulation("WRONG_ID"); | ||
PowsyblException e = assertThrows(PowsyblException.class, () -> modification.apply(network, true, reportNode)); | ||
assertEquals("Generator 'WRONG_ID' not found", e.getMessage()); | ||
|
||
// After applying the network modification, both generators regulate locally at 1.05 pu (21 kV). | ||
assertEquals(gen1.getId(), gen1.getRegulatingTerminal().getConnectable().getId()); | ||
assertEquals(21.0, gen1.getTargetV()); | ||
assertEquals(gen2.getId(), gen2.getRegulatingTerminal().getConnectable().getId()); | ||
assertEquals(21.0, gen2.getTargetV()); | ||
|
||
// Report node has been updated with the change for gen1 (no impact for gen2). | ||
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())); | ||
} | ||
|
||
@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.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(21) | ||
// No regulatingTerminal set == use its own terminal for regulation | ||
.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(); | ||
|
||
vl400.getNodeBreakerView().newInternalConnection().setNode1(0).setNode2(1); | ||
vl20.getNodeBreakerView().newInternalConnection().setNode1(2).setNode2(3); | ||
vl20.getNodeBreakerView().newInternalConnection().setNode1(2).setNode2(4); | ||
|
||
return n; | ||
} | ||
} |