Skip to content

Commit

Permalink
SetGeneratorToLocalRegulation: set same targetV on all locally regula…
Browse files Browse the repository at this point in the history
…ting generators (powsybl#3287)

* SetGeneratorToLocalRegulation : set same targetV on all locally regulating generators
* SetGeneratorToLocalRegulation: fix bus detection
* SceGeneratorToLocalRegulations: targetV determined as the closest value to VL nominal voltage among the regulating terminals

Signed-off-by: Giovanni Ferrari <[email protected]>
  • Loading branch information
quinarygio authored Feb 4, 2025
1 parent b11f078 commit c4d300e
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* 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/.
Expand All @@ -10,20 +10,24 @@
import com.powsybl.commons.report.ReportNode;
import com.powsybl.computation.ComputationManager;
import com.powsybl.iidm.modification.topology.NamingStrategy;
import com.powsybl.iidm.network.Bus;
import com.powsybl.iidm.network.Generator;
import com.powsybl.iidm.network.Network;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;

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>
* <li>In case other generators are already regulating locally on the same bus, targetV value is determined by being the closest value to the voltage level nominal voltage among the regulating terminals.</li>
* <li>If no other generator is regulating on the same bus, targetV engineering unit value is adapted to the voltage level nominal voltage, but the per unit value remains the same.</li>
* </ul>
*
* @author Romain Courtier {@literal <romain.courtier at rte-france.com>}
Expand All @@ -47,7 +51,7 @@ public void apply(Network network, NamingStrategy namingStrategy, boolean throwE
Generator generator = network.getGenerator(generatorId);
if (generator == null) {
logOrThrow(throwException, "Generator '" + generatorId + "' not found");
} else if (!generator.getId().equals(generator.getRegulatingTerminal().getConnectable().getId())) {
} else if (!isGeneratorRegulatingLocally(generator)) {
setLocalRegulation(generator, reportNode);
}
}
Expand All @@ -58,27 +62,56 @@ public void apply(Network network, NamingStrategy namingStrategy, boolean throwE
* @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.setTargetV(calculateTargetVoltage(generator));
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());
}

private double calculateTargetVoltage(Generator generator) {
double localNominalV = generator.getTerminal().getVoltageLevel().getNominalV();
Bus bus = generator.getTerminal().getBusView().getBus();
if (bus != null) {
Optional<Generator> referenceGenerator = getReferenceGenerator(bus, localNominalV);
if (referenceGenerator.isPresent()) {
double targetV = referenceGenerator.get().getTargetV();
checkLocalGeneratorsWithWrongTargetV(bus, targetV);
return targetV;
}
}
// 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();
return localNominalV * remoteTargetV / remoteNominalV;
}

private boolean isGeneratorRegulatingLocally(Generator generator) {
return generator.getId().equals(generator.getRegulatingTerminal().getConnectable().getId());
}

private Optional<Generator> getReferenceGenerator(Bus bus, double localNominalV) {
return bus.getGeneratorStream()
.filter(g -> !g.getId().equals(generatorId) && isGeneratorRegulatingLocally(g))
.min(Comparator.comparing(g -> Math.abs(g.getTargetV() - localNominalV)));
}

private void checkLocalGeneratorsWithWrongTargetV(Bus bus, double targetV) {
bus.getGeneratorStream()
.filter(g -> !g.getId().equals(generatorId) && isGeneratorRegulatingLocally(g)
&& g.getTargetV() != targetV)
.forEach(gen -> LOG.warn("Generator {} has wrong target voltage {}", gen.getId(), gen.getTargetV()));
}

@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())) {
} else if (isGeneratorRegulatingLocally(generator)) {
impact = NetworkModificationImpact.NO_IMPACT_ON_NETWORK;
} else {
impact = DEFAULT_IMPACT;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* 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/.
Expand Down Expand Up @@ -37,13 +37,19 @@ 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(21.0, gen2.getTargetV());
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()
.withMessageTemplate("rootReportNode", "Set generators to local regulation").build();
Expand All @@ -53,19 +59,26 @@ void setLocalRegulationTest() throws IOException {
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).
// 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(21.0, gen1.getTargetV());
assertEquals(22.0, gen1.getTargetV());
assertEquals(gen2.getId(), gen2.getRegulatingTerminal().getConnectable().getId());
assertEquals(21.0, gen2.getTargetV());
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 (no impact for gen2).
// 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
Expand Down Expand Up @@ -111,6 +124,10 @@ private Network createTestNetwork() {
.setNominalV(20)
.setTopologyKind(TopologyKind.NODE_BREAKER)
.add();
vl20.getNodeBreakerView().newBusbarSection()
.setId("BBS20")
.setNode(0)
.add();
vl20.newGenerator()
.setId("GEN1")
.setNode(3)
Expand All @@ -122,6 +139,7 @@ private Network createTestNetwork() {
.setTargetV(420)
.setRegulatingTerminal(n.getBusbarSection("BBS").getTerminal())
.add();

vl20.newGenerator()
.setId("GEN2")
.setNode(4)
Expand All @@ -130,10 +148,34 @@ private Network createTestNetwork() {
.setMaxP(200)
.setTargetP(200)
.setVoltageRegulatorOn(true)
.setTargetV(21)
.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")
Expand All @@ -150,10 +192,39 @@ private Network createTestNetwork() {
.setNode2(2)
.add();

vl400.getNodeBreakerView().newInternalConnection().setNode1(0).setNode2(1);
vl20.getNodeBreakerView().newInternalConnection().setNode1(2).setNode2(3);
vl20.getNodeBreakerView().newInternalConnection().setNode1(2).setNode2(4);
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();
}
}

0 comments on commit c4d300e

Please sign in to comment.