Skip to content

Commit b242f52

Browse files
vmouradianjeandemanged
authored andcommitted
Balance type PROPORTIONAL_TO_REMAINING_MARGIN checks active mismatch sign for participation factors (#1189)
Signed-off-by: vmouradian <valentin.mouradian@artelys.com> Co-authored-by: jeandemanged <damien.jeandemange@artelys.com> Signed-off-by: Didier Vidal <didier.vidal_externe@rte-france.com>
1 parent 34dc05c commit b242f52

File tree

5 files changed

+82
-10
lines changed

5 files changed

+82
-10
lines changed

src/main/java/com/powsybl/openloadflow/network/util/ActivePowerDistribution.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public interface Step {
3939

4040
String getElementType();
4141

42-
List<ParticipatingElement> getParticipatingElements(Collection<LfBus> buses);
42+
List<ParticipatingElement> getParticipatingElements(Collection<LfBus> buses, OptionalDouble mismatch);
4343

4444
double run(List<ParticipatingElement> participatingElements, int iteration, double remainingMismatch);
4545
}
@@ -61,7 +61,7 @@ public Result run(LfNetwork network, double activePowerMismatch) {
6161
}
6262

6363
public Result run(LfGenerator referenceGenerator, Collection<LfBus> buses, double activePowerMismatch) {
64-
List<ParticipatingElement> participatingElements = step.getParticipatingElements(buses);
64+
List<ParticipatingElement> participatingElements = step.getParticipatingElements(buses, OptionalDouble.of(activePowerMismatch));
6565
final Map<ParticipatingElement, Double> initialP = participatingElements.stream()
6666
.collect(Collectors.toUnmodifiableMap(Function.identity(), ParticipatingElement::getTargetP));
6767

src/main/java/com/powsybl/openloadflow/network/util/GenerationActivePowerDistributionStep.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
package com.powsybl.openloadflow.network.util;
1010

11+
import com.powsybl.commons.PowsyblException;
1112
import com.powsybl.openloadflow.network.LfBus;
1213
import com.powsybl.openloadflow.network.LfGenerator;
1314
import com.powsybl.openloadflow.util.PerUnit;
@@ -18,6 +19,8 @@
1819
import java.util.Iterator;
1920
import java.util.LinkedList;
2021
import java.util.List;
22+
import java.util.Objects;
23+
import java.util.OptionalDouble;
2124
import java.util.stream.Collectors;
2225

2326
/**
@@ -50,12 +53,19 @@ public String getElementType() {
5053
}
5154

5255
@Override
53-
public List<ParticipatingElement> getParticipatingElements(Collection<LfBus> buses) {
56+
public List<ParticipatingElement> getParticipatingElements(Collection<LfBus> buses, OptionalDouble mismatch) {
57+
Boolean positiveMismatch = mismatch.isPresent() ? mismatch.getAsDouble() > 0 : null;
5458
return buses.stream()
5559
.filter(bus -> bus.isParticipating() && !bus.isDisabled() && !bus.isFictitious())
5660
.flatMap(bus -> bus.getGenerators().stream())
57-
.filter(generator -> isParticipating(generator) && getParticipationFactor(generator) != 0)
58-
.map(generator -> new ParticipatingElement(generator, getParticipationFactor(generator)))
61+
.map(gen -> {
62+
double factor = getParticipationFactor(gen, positiveMismatch);
63+
if (isParticipating(gen) && factor != 0) {
64+
return new ParticipatingElement(gen, factor);
65+
}
66+
return null;
67+
})
68+
.filter(Objects::nonNull)
5969
.collect(Collectors.toCollection(LinkedList::new));
6070
}
6171

@@ -124,12 +134,17 @@ public double run(List<ParticipatingElement> participatingElements, int iteratio
124134
return done;
125135
}
126136

127-
private double getParticipationFactor(LfGenerator generator) {
137+
private double getParticipationFactor(LfGenerator generator, Boolean positiveMismatch) {
128138
return switch (participationType) {
129139
case MAX -> generator.getMaxP() / generator.getDroop();
130140
case TARGET -> Math.abs(generator.getTargetP());
131141
case PARTICIPATION_FACTOR -> generator.getParticipationFactor();
132-
case REMAINING_MARGIN -> Math.max(0.0, generator.getMaxP() - generator.getTargetP());
142+
case REMAINING_MARGIN -> {
143+
if (positiveMismatch == null) {
144+
throw new PowsyblException("The sign of the active power mismatch is unknown, it is mandatory for REMAINING_MARGIN participation type");
145+
}
146+
yield Boolean.TRUE.equals(positiveMismatch) ? Math.max(0.0, generator.getMaxP() - generator.getTargetP()) : Math.max(0.0, generator.getTargetP() - generator.getMinP());
147+
}
133148
};
134149
}
135150

src/main/java/com/powsybl/openloadflow/network/util/LoadActivePowerDistributionStep.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.Iterator;
1818
import java.util.LinkedList;
1919
import java.util.List;
20+
import java.util.OptionalDouble;
2021
import java.util.stream.Collectors;
2122

2223
/**
@@ -38,7 +39,7 @@ public String getElementType() {
3839
}
3940

4041
@Override
41-
public List<ParticipatingElement> getParticipatingElements(Collection<LfBus> buses) {
42+
public List<ParticipatingElement> getParticipatingElements(Collection<LfBus> buses, OptionalDouble mismatch) {
4243
return buses.stream()
4344
.filter(bus -> bus.isParticipating() && !bus.isDisabled() && !bus.isFictitious())
4445
.flatMap(bus -> bus.getLoads().stream())

src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ protected SensitivityFactorGroupList<V, E> createFactorGroups(List<LfSensitivity
626626

627627
protected List<ParticipatingElement> getParticipatingElements(Collection<LfBus> buses, LoadFlowParameters.BalanceType balanceType, OpenLoadFlowParameters openLoadFlowParameters) {
628628
ActivePowerDistribution.Step step = ActivePowerDistribution.getStep(balanceType, openLoadFlowParameters.isLoadPowerFactorConstant(), openLoadFlowParameters.isUseActiveLimits());
629-
List<ParticipatingElement> participatingElements = step.getParticipatingElements(buses);
629+
List<ParticipatingElement> participatingElements = step.getParticipatingElements(buses, OptionalDouble.empty()); // The value of the mismatch cannot be known here. It is not needed for the distribution types supported in sensitivity analysis and checked in #checkLoadFlowParameters
630630
ParticipatingElement.normalizeParticipationFactors(participatingElements);
631631
return participatingElements;
632632
}

src/test/java/com/powsybl/openloadflow/ac/DistributedSlackOnGenerationTest.java

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99
package com.powsybl.openloadflow.ac;
1010

11+
import com.powsybl.commons.PowsyblException;
1112
import com.powsybl.commons.report.ReportNode;
1213
import com.powsybl.computation.local.LocalComputationManager;
1314
import com.powsybl.iidm.network.*;
@@ -23,13 +24,21 @@
2324
import com.powsybl.openloadflow.OpenLoadFlowProvider;
2425
import com.powsybl.openloadflow.network.DistributedSlackNetworkFactory;
2526
import com.powsybl.openloadflow.network.EurostagFactory;
27+
import com.powsybl.openloadflow.network.FirstSlackBusSelector;
28+
import com.powsybl.openloadflow.network.LfBus;
29+
import com.powsybl.openloadflow.network.LfNetwork;
2630
import com.powsybl.openloadflow.network.ReferenceBusSelectionMode;
2731
import com.powsybl.openloadflow.network.SlackBusSelectionMode;
32+
import com.powsybl.openloadflow.network.impl.LfNetworkLoaderImpl;
33+
import com.powsybl.openloadflow.network.util.ActivePowerDistribution;
2834
import com.powsybl.openloadflow.util.LoadFlowAssert;
2935
import org.junit.jupiter.api.BeforeEach;
3036
import org.junit.jupiter.api.Test;
3137

3238
import java.util.EnumSet;
39+
import java.util.List;
40+
import java.util.OptionalDouble;
41+
import java.util.Set;
3342
import java.util.concurrent.CompletionException;
3443

3544
import static com.powsybl.openloadflow.util.LoadFlowAssert.*;
@@ -187,7 +196,7 @@ void testProportionalToParticipationFactor() {
187196
}
188197

189198
@Test
190-
void testProportionalToRemainingMargin() {
199+
void testProportionalToRemainingMarginUp() {
191200
// decrease g1 max limit power, so that distributed slack algo reach the g1 max
192201
g1.setMaxP(105);
193202

@@ -201,6 +210,53 @@ void testProportionalToRemainingMargin() {
201210
assertActivePowerEquals(-122.0, g4.getTerminal());
202211
}
203212

213+
@Test
214+
void testProportionalToRemainingMarginDown() {
215+
// Decrease load P0, so that active mismatch is negative
216+
network.getLoad("l1").setP0(400);
217+
218+
parameters.setBalanceType(LoadFlowParameters.BalanceType.PROPORTIONAL_TO_GENERATION_REMAINING_MARGIN);
219+
LoadFlowResult result = loadFlowRunner.run(network, parameters);
220+
221+
assertTrue(result.isFullyConverged());
222+
assertActivePowerEquals(-71.428, g1.getTerminal());
223+
assertActivePowerEquals(-171.428, g2.getTerminal());
224+
assertActivePowerEquals(-78.571, g3.getTerminal());
225+
assertActivePowerEquals(-78.571, g4.getTerminal());
226+
}
227+
228+
@Test
229+
void testGetParticipatingElementsWithMismatch() {
230+
LfNetwork lfNetwork = LfNetwork.load(network, new LfNetworkLoaderImpl(), new FirstSlackBusSelector(Set.of())).get(0);
231+
final OptionalDouble mismatch = OptionalDouble.of(30);
232+
final List<LfBus> buses = lfNetwork.getBuses();
233+
for (LoadFlowParameters.BalanceType balanceType : LoadFlowParameters.BalanceType.values()) {
234+
ActivePowerDistribution.Step step = ActivePowerDistribution.getStep(balanceType, parametersExt.isLoadPowerFactorConstant(), parametersExt.isUseActiveLimits());
235+
switch (balanceType) {
236+
case PROPORTIONAL_TO_GENERATION_P_MAX, PROPORTIONAL_TO_GENERATION_P, PROPORTIONAL_TO_GENERATION_REMAINING_MARGIN -> assertEquals(4, step.getParticipatingElements(buses, mismatch).size());
237+
case PROPORTIONAL_TO_LOAD, PROPORTIONAL_TO_CONFORM_LOAD -> assertEquals(1, step.getParticipatingElements(buses, mismatch).size());
238+
case PROPORTIONAL_TO_GENERATION_PARTICIPATION_FACTOR -> assertEquals(0, step.getParticipatingElements(buses, mismatch).size());
239+
}
240+
}
241+
}
242+
243+
@Test
244+
void testGetParticipatingElementsWithoutMismatch() {
245+
LfNetwork lfNetwork = LfNetwork.load(network, new LfNetworkLoaderImpl(), new FirstSlackBusSelector(Set.of())).get(0);
246+
final OptionalDouble emptyMismatch = OptionalDouble.empty();
247+
final List<LfBus> buses = lfNetwork.getBuses();
248+
for (LoadFlowParameters.BalanceType balanceType : LoadFlowParameters.BalanceType.values()) {
249+
ActivePowerDistribution.Step step = ActivePowerDistribution.getStep(balanceType, parametersExt.isLoadPowerFactorConstant(), parametersExt.isUseActiveLimits());
250+
switch (balanceType) {
251+
case PROPORTIONAL_TO_GENERATION_P_MAX, PROPORTIONAL_TO_GENERATION_P -> assertEquals(4, step.getParticipatingElements(buses, emptyMismatch).size());
252+
case PROPORTIONAL_TO_LOAD, PROPORTIONAL_TO_CONFORM_LOAD -> assertEquals(1, step.getParticipatingElements(buses, emptyMismatch).size());
253+
case PROPORTIONAL_TO_GENERATION_PARTICIPATION_FACTOR -> assertEquals(0, step.getParticipatingElements(buses, emptyMismatch).size());
254+
case PROPORTIONAL_TO_GENERATION_REMAINING_MARGIN -> assertThrows(PowsyblException.class, () -> step.getParticipatingElements(buses, emptyMismatch),
255+
"The sign of the active power mismatch is unknown, it is mandatory for REMAINING_MARGIN participation type");
256+
}
257+
}
258+
}
259+
204260
@Test
205261
void testProportionalToRemainingMarginPmaxBelowTargetP() {
206262
// decrease g1 max limit power below target P

0 commit comments

Comments
 (0)