Skip to content

Commit

Permalink
Fast restart for active power target change (#1085)
Browse files Browse the repository at this point in the history
Signed-off-by: Geoffroy Jamgotchian <geoffroy.jamgotchian@rte-france.com>
  • Loading branch information
geofjamg authored Sep 13, 2024
1 parent e7ff398 commit c4393ca
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 8 deletions.
29 changes: 28 additions & 1 deletion src/main/java/com/powsybl/openloadflow/NetworkCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.powsybl.openloadflow.network.impl.AbstractLfGenerator;
import com.powsybl.openloadflow.network.impl.LfLegBranch;
import com.powsybl.openloadflow.network.util.PreviousValueVoltageInitializer;
import com.powsybl.openloadflow.util.PerUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -156,6 +157,16 @@ private CacheUpdateResult onInjectionUpdate(Injection<?> injection, BiFunction<A
return CacheUpdateResult.elementNotFound();
}

private static CacheUpdateResult updateLfGeneratorTargetP(String id, double oldValue, double newValue, AcLoadFlowContext context, LfBus lfBus) {
double valueShift = newValue - oldValue;
LfGenerator lfGenerator = lfBus.getNetwork().getGeneratorById(id);
double newTargetP = lfGenerator.getInitialTargetP() + valueShift / PerUnit.SB;
lfGenerator.setTargetP(newTargetP);
lfGenerator.setInitialTargetP(newTargetP);
lfGenerator.reApplyActivePowerControlChecks(context.getParameters().getNetworkParameters(), null);
return CacheUpdateResult.elementUpdated(context);
}

private CacheUpdateResult onGeneratorUpdate(Generator generator, String attribute, Object oldValue, Object newValue) {
return onInjectionUpdate(generator, (context, lfBus) -> {
if (attribute.equals("targetV")) {
Expand All @@ -174,6 +185,17 @@ private CacheUpdateResult onGeneratorUpdate(Generator generator, String attribut
}
context.getNetwork().validate(LoadFlowModel.AC, null);
return CacheUpdateResult.elementUpdated(context);
} else if (attribute.equals("targetP")) {
return updateLfGeneratorTargetP(generator.getId(), (double) oldValue, (double) newValue, context, lfBus);
}
return CacheUpdateResult.unsupportedUpdate();
});
}

private CacheUpdateResult onBatteryUpdate(Battery battery, String attribute, Object oldValue, Object newValue) {
return onInjectionUpdate(battery, (context, lfBus) -> {
if (attribute.equals("targetP")) {
return updateLfGeneratorTargetP(battery.getId(), (double) oldValue, (double) newValue, context, lfBus);
}
return CacheUpdateResult.unsupportedUpdate();
});
Expand Down Expand Up @@ -277,9 +299,14 @@ public void onUpdate(Identifiable identifiable, String attribute, String variant
default -> {
if (identifiable.getType() == IdentifiableType.GENERATOR) {
Generator generator = (Generator) identifiable;
if (attribute.equals("targetV")) {
if (attribute.equals("targetV") || attribute.equals("targetP")) {
result = onGeneratorUpdate(generator, attribute, oldValue, newValue);
}
} else if (identifiable.getType() == IdentifiableType.BATTERY) {
Battery battery = (Battery) identifiable;
if (attribute.equals("targetP")) {
result = onBatteryUpdate(battery, attribute, oldValue, newValue);
}
} else if (identifiable.getType() == IdentifiableType.SHUNT_COMPENSATOR) {
ShuntCompensator shunt = (ShuntCompensator) identifiable;
if (attribute.equals("sectionCount")) {
Expand Down
6 changes: 1 addition & 5 deletions src/main/java/com/powsybl/openloadflow/network/LfAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.Terminal;
import com.powsybl.openloadflow.graph.GraphConnectivity;
import com.powsybl.openloadflow.network.impl.AbstractLfGenerator;
import com.powsybl.openloadflow.network.impl.LfLegBranch;
import com.powsybl.openloadflow.network.impl.LfShuntImpl;
import com.powsybl.openloadflow.network.impl.Networks;
Expand Down Expand Up @@ -335,10 +334,7 @@ private void applyGeneratorChange(LfNetworkParameters networkParameters) {
double newTargetP = generatorChange.isRelative() ? generator.getTargetP() + generatorChange.activePowerValue() : generatorChange.activePowerValue();
generator.setTargetP(newTargetP);
generator.setInitialTargetP(newTargetP);
if (!AbstractLfGenerator.checkActivePowerControl(generator.getId(), generator.getTargetP(), generator.getMaxP(), generator.getMinTargetP(), generator.getMaxTargetP(),
networkParameters.getPlausibleActivePowerLimit(), networkParameters.isUseActiveLimits(), null)) {
generator.setParticipating(false);
}
generator.reApplyActivePowerControlChecks(networkParameters, null);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.powsybl.openloadflow.network;

import com.powsybl.iidm.network.TwoSides;
import com.powsybl.openloadflow.network.impl.LfNetworkLoadingReport;

import java.util.OptionalDouble;

Expand Down Expand Up @@ -143,4 +144,6 @@ default boolean isDisabled() {
LfAsymGenerator getAsym();

void setAsym(LfAsymGenerator asym);

void reApplyActivePowerControlChecks(LfNetworkParameters parameters, LfNetworkLoadingReport report);
}
Original file line number Diff line number Diff line change
Expand Up @@ -456,4 +456,9 @@ public boolean isReference() {
public void setReference(boolean reference) {
this.reference = reference;
}

@Override
public void reApplyActivePowerControlChecks(LfNetworkParameters parameters, LfNetworkLoadingReport report) {
// nothing to do
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public final class LfBatteryImpl extends AbstractLfGenerator {

private final Ref<Battery> batteryRef;

private boolean initialParticipating;

private boolean participating;

private final double droop;
Expand All @@ -39,7 +41,8 @@ private LfBatteryImpl(Battery battery, LfNetwork network, LfNetworkParameters pa
super(network, battery.getTargetP() / PerUnit.SB);
this.batteryRef = Ref.create(battery, parameters.isCacheEnabled());
var apcHelper = ActivePowerControlHelper.create(battery, battery.getMinP(), battery.getMaxP());
participating = apcHelper.participating();
initialParticipating = apcHelper.participating();
participating = initialParticipating;
participationFactor = apcHelper.participationFactor();
droop = apcHelper.droop();
minTargetP = apcHelper.minTargetP();
Expand Down Expand Up @@ -131,4 +134,14 @@ public void updateState(LfNetworkStateUpdateParameters parameters) {
.setP(-targetP * PerUnit.SB)
.setQ(Double.isNaN(calculatedQ) ? -getTargetQ() * PerUnit.SB : -calculatedQ * PerUnit.SB);
}

@Override
public void reApplyActivePowerControlChecks(LfNetworkParameters parameters, LfNetworkLoadingReport report) {
participating = initialParticipating;
var battery = getBattery();
if (!checkActivePowerControl(battery.getId(), targetP * PerUnit.SB, battery.getMaxP(), minTargetP, maxTargetP,
parameters.getPlausibleActivePowerLimit(), parameters.isUseActiveLimits(), report)) {
participating = false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public final class LfGeneratorImpl extends AbstractLfGenerator {

private final Ref<Generator> generatorRef;

private final boolean initialParticipating;

private boolean participating;

private final double droop;
Expand All @@ -53,7 +55,8 @@ private LfGeneratorImpl(Generator generator, LfNetwork network, LfNetworkParamet
// we force voltage control of generators tagged as condensers or tagged as fictitious if the dedicated mode is activated.
forceVoltageControl = generator.isCondenser() || generator.isFictitious() && parameters.getFictitiousGeneratorVoltageControlCheckMode() == OpenLoadFlowParameters.FictitiousGeneratorVoltageControlCheckMode.FORCED;
var apcHelper = ActivePowerControlHelper.create(generator, generator.getMinP(), generator.getMaxP());
participating = apcHelper.participating();
initialParticipating = apcHelper.participating();
participating = initialParticipating;
participationFactor = apcHelper.participationFactor();
droop = apcHelper.droop();
minTargetP = apcHelper.minTargetP();
Expand Down Expand Up @@ -87,6 +90,16 @@ private LfGeneratorImpl(Generator generator, LfNetwork network, LfNetworkParamet
}
}

@Override
public void reApplyActivePowerControlChecks(LfNetworkParameters parameters, LfNetworkLoadingReport report) {
participating = initialParticipating;
var generator = getGenerator();
if (!checkActivePowerControl(generator.getId(), targetP * PerUnit.SB, generator.getMaxP(), minTargetP, maxTargetP,
parameters.getPlausibleActivePowerLimit(), parameters.isUseActiveLimits(), report)) {
participating = false;
}
}

private static void createAsym(Generator generator, LfGeneratorImpl lfGenerator) {
var extension = generator.getExtension(GeneratorFortescue.class);
if (extension != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,78 @@ void testTargetV() {
assertEquals(0, result.getComponentResults().get(0).getIterationCount());
}

@Test
void testGeneratorTargetP() {
var network = DistributedSlackNetworkFactory.create();
var g1 = network.getGenerator("g1");
var g2 = network.getGenerator("g2");
var g3 = network.getGenerator("g3");
var g4 = network.getGenerator("g4");
// align active power control of the 3 generators to have understandable results
g1.setMaxP(300);
g2.setMaxP(300);
g3.setMaxP(300);
g4.setMaxP(300);
g1.getExtension(ActivePowerControl.class).setDroop(1);
g2.getExtension(ActivePowerControl.class).setDroop(1);
g3.getExtension(ActivePowerControl.class).setDroop(1);
g4.getExtension(ActivePowerControl.class).setDroop(1);

var result = loadFlowRunner.run(network, parameters);
assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus());
assertEquals(3, result.getComponentResults().get(0).getIterationCount());
// mismatch 120 -> + 30 each
assertActivePowerEquals(-130.0, g1.getTerminal()); // 100 -> 130
assertActivePowerEquals(-230.0, g2.getTerminal()); // 200 -> 230
assertActivePowerEquals(-120.0, g3.getTerminal()); // 90 -> 120
assertActivePowerEquals(-120.0, g4.getTerminal()); // 90 -> 120

g1.setTargetP(120); // 100 -> 120
assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); // check cache has not been invalidated

result = loadFlowRunner.run(network, parameters);
assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus());
assertEquals(2, result.getComponentResults().get(0).getIterationCount());
// mismatch 100 -> + 25 each
assertActivePowerEquals(-145.0, g1.getTerminal()); // 120 -> 125
assertActivePowerEquals(-225.0, g2.getTerminal()); // 220 -> 225
assertActivePowerEquals(-115.0, g3.getTerminal()); // 90 -> 115
assertActivePowerEquals(-115.0, g4.getTerminal()); // 90 -> 115

// check that if target_p > map_p the generator is discarded from active power control
g1.setTargetP(310);
result = loadFlowRunner.run(network, parameters);
assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus());
assertEquals(2, result.getComponentResults().get(0).getIterationCount());
// mismatch 90 -> + 60 each
assertActivePowerEquals(-310.0, g1.getTerminal()); // unchanged
assertActivePowerEquals(-170.0, g2.getTerminal()); // 200 -> 170
assertActivePowerEquals(-60.0, g3.getTerminal()); // 90 -> 60
assertActivePowerEquals(-60.0, g4.getTerminal()); // 90 -> 60
}

@Test
void testBatteryTargetP() {
var network = DistributedSlackNetworkFactory.createWithBattery();
var b1 = network.getBattery("bat1");
var b2 = network.getBattery("bat2");

var result = loadFlowRunner.run(network, parameters);
assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus());
assertEquals(3, result.getComponentResults().get(0).getIterationCount());
assertActivePowerEquals(-2.0, b1.getTerminal());
assertActivePowerEquals(2.983, b2.getTerminal());

b1.setTargetP(4);
assertNotNull(NetworkCache.INSTANCE.findEntry(network).orElseThrow().getContexts()); // check cache has not been invalidated

result = loadFlowRunner.run(network, parameters);
assertEquals(LoadFlowResult.ComponentResult.Status.CONVERGED, result.getComponentResults().get(0).getStatus());
assertEquals(2, result.getComponentResults().get(0).getIterationCount());
assertActivePowerEquals(-4.0, b1.getTerminal());
assertActivePowerEquals(3.016, b2.getTerminal());
}

@Test
void testParameterChange() {
var network = EurostagFactory.fix(EurostagTutorialExample1Factory.create());
Expand Down

0 comments on commit c4393ca

Please sign in to comment.