Skip to content

Commit 879e8f7

Browse files
Support having multiple active operational limits group (#1345)
Signed-off-by: PRABAKARAN Sylvestre <sylvestre.prabakaran@rte-france.com>
1 parent e17dc47 commit 879e8f7

10 files changed

Lines changed: 277 additions & 153 deletions

File tree

src/main/java/com/powsybl/openloadflow/network/LfBranch.java

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.powsybl.iidm.network.util.LimitViolationUtils;
1616
import com.powsybl.openloadflow.sa.LimitReductionManager;
1717
import com.powsybl.openloadflow.util.Evaluable;
18+
import com.powsybl.openloadflow.util.PerUnit;
1819
import com.powsybl.security.results.BranchResult;
1920

2021
import java.util.*;
@@ -38,6 +39,73 @@ enum BranchType {
3839
record LfBranchResults(double p1, double p2, double q1, double q2, double i1, double i2) {
3940
}
4041

42+
class LfLimitsGroup {
43+
44+
private final List<LfLimit> sortedLimits;
45+
46+
private final String operationalLimitsGroupId;
47+
48+
public LfLimitsGroup(List<LfLimit> sortedLimits, String operationalLimitsGroupId) {
49+
this.sortedLimits = sortedLimits;
50+
this.operationalLimitsGroupId = operationalLimitsGroupId;
51+
}
52+
53+
public String getOperationalLimitsGroupId() {
54+
return operationalLimitsGroupId;
55+
}
56+
57+
public List<LfLimit> getSortedLimits() {
58+
return sortedLimits;
59+
}
60+
61+
private static double getScaleForLimitType(LimitType type, LfBus bus) {
62+
return switch (type) {
63+
case ACTIVE_POWER, APPARENT_POWER -> 1.0 / PerUnit.SB;
64+
case CURRENT -> 1.0 / PerUnit.ib(bus.getNominalV());
65+
default ->
66+
throw new UnsupportedOperationException(String.format("Getting scale for limit type %s is not supported.", type));
67+
};
68+
}
69+
70+
/**
71+
* Create the list of LfLimits from a LoadingLimits and a list of reductions.
72+
* The resulting list will contain the permanent limit
73+
* This list is returned in a LfLimitsGroup object
74+
*/
75+
public static LfLimitsGroup createSortedLimitsList(LoadingLimits loadingLimits, String operationalLimitsGroupId, LfBus bus, double[] limitReductions) {
76+
List<LfLimit> sortedLimits = new ArrayList<>(3);
77+
if (loadingLimits != null) {
78+
double toPerUnit = getScaleForLimitType(loadingLimits.getLimitType(), bus);
79+
80+
int i = 0;
81+
for (LoadingLimits.TemporaryLimit temporaryLimit : loadingLimits.getTemporaryLimits()) {
82+
if (temporaryLimit.getAcceptableDuration() != 0) {
83+
// it is not useful to add a limit with acceptable duration equal to zero as the only value plausible
84+
// for this limit is infinity.
85+
// https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/CurrentLimits.html
86+
double reduction = limitReductions.length == 0 ? 1d : limitReductions[i + 1]; // Temporary limit's reductions are stored starting from index 1 in `limitReductions`
87+
double originalValuePerUnit = temporaryLimit.getValue() * toPerUnit;
88+
sortedLimits.add(0, LfLimit.createTemporaryLimit(temporaryLimit.getName(), temporaryLimit.getAcceptableDuration(),
89+
originalValuePerUnit, reduction));
90+
}
91+
i++;
92+
}
93+
double reduction = limitReductions.length == 0 ? 1d : limitReductions[0];
94+
sortedLimits.add(LfLimit.createPermanentLimit(loadingLimits.getPermanentLimit() * toPerUnit, reduction));
95+
}
96+
if (sortedLimits.size() > 1) {
97+
// we only make that fix if there is more than a permanent limit attached to the branch.
98+
for (int i = sortedLimits.size() - 1; i > 0; i--) {
99+
// From the permanent limit to the most serious temporary limit.
100+
sortedLimits.get(i).setAcceptableDuration(sortedLimits.get(i - 1).getAcceptableDuration());
101+
}
102+
sortedLimits.get(0).setAcceptableDuration(0);
103+
}
104+
return new LfLimitsGroup(sortedLimits, operationalLimitsGroupId);
105+
}
106+
107+
}
108+
41109
class LfLimit {
42110

43111
private final String name;
@@ -211,9 +279,9 @@ static int[] createIndex(LfNetwork network, List<LfBranch> branches) {
211279

212280
List<Evaluable> getAdditionalClosedQ2();
213281

214-
List<LfLimit> getLimits1(LimitType type, LimitReductionManager limitReductionManager);
282+
List<LfLimitsGroup> getLimits1(LimitType type, LimitReductionManager limitReductionManager);
215283

216-
default List<LfLimit> getLimits2(LimitType type, LimitReductionManager limitReductionManager) {
284+
default List<LfLimitsGroup> getLimits2(LimitType type, LimitReductionManager limitReductionManager) {
217285
return Collections.emptyList();
218286
}
219287

src/main/java/com/powsybl/openloadflow/network/impl/AbstractLfBranch.java

Lines changed: 30 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import com.powsybl.openloadflow.network.*;
1212
import com.powsybl.openloadflow.sa.LimitReductionManager;
1313
import com.powsybl.openloadflow.util.Evaluable;
14-
import com.powsybl.openloadflow.util.PerUnit;
1514
import net.jafama.FastMath;
1615
import org.slf4j.Logger;
1716
import org.slf4j.LoggerFactory;
@@ -30,17 +29,17 @@ public abstract class AbstractLfBranch extends AbstractElement implements LfBran
3029

3130
protected final LfBus bus2;
3231

33-
private List<LfLimit> currentLimits1;
32+
private List<LfLimitsGroup> currentLimits1;
3433

35-
private List<LfLimit> activePowerLimits1;
34+
private List<LfLimitsGroup> activePowerLimits1;
3635

37-
private List<LfLimit> apparentPowerLimits1;
36+
private List<LfLimitsGroup> apparentPowerLimits1;
3837

39-
private List<LfLimit> currentLimits2;
38+
private List<LfLimitsGroup> currentLimits2;
4039

41-
private List<LfLimit> activePowerLimits2;
40+
private List<LfLimitsGroup> activePowerLimits2;
4241

43-
private List<LfLimit> apparentPowerLimits2;
42+
private List<LfLimitsGroup> apparentPowerLimits2;
4443

4544
protected final PiModel piModel;
4645

@@ -92,42 +91,6 @@ public Optional<ThreeSides> getOriginalSide() {
9291
return Optional.empty();
9392
}
9493

95-
/**
96-
* Create the list of LfLimits from a LoadingLimits and a list of reductions.
97-
* The resulting list will contain the permanent limit
98-
*/
99-
protected static List<LfLimit> createSortedLimitsList(LoadingLimits loadingLimits, LfBus bus, double[] limitReductions) {
100-
List<LfLimit> sortedLimits = new ArrayList<>(3);
101-
if (loadingLimits != null) {
102-
double toPerUnit = getScaleForLimitType(loadingLimits.getLimitType(), bus);
103-
104-
int i = 0;
105-
for (LoadingLimits.TemporaryLimit temporaryLimit : loadingLimits.getTemporaryLimits()) {
106-
if (temporaryLimit.getAcceptableDuration() != 0) {
107-
// it is not useful to add a limit with acceptable duration equal to zero as the only value plausible
108-
// for this limit is infinity.
109-
// https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/CurrentLimits.html
110-
double reduction = limitReductions.length == 0 ? 1d : limitReductions[i + 1]; // Temporary limit's reductions are stored starting from index 1 in `limitReductions`
111-
double originalValuePerUnit = temporaryLimit.getValue() * toPerUnit;
112-
sortedLimits.add(0, LfLimit.createTemporaryLimit(temporaryLimit.getName(), temporaryLimit.getAcceptableDuration(),
113-
originalValuePerUnit, reduction));
114-
}
115-
i++;
116-
}
117-
double reduction = limitReductions.length == 0 ? 1d : limitReductions[0];
118-
sortedLimits.add(LfLimit.createPermanentLimit(loadingLimits.getPermanentLimit() * toPerUnit, reduction));
119-
}
120-
if (sortedLimits.size() > 1) {
121-
// we only make that fix if there is more than a permanent limit attached to the branch.
122-
for (int i = sortedLimits.size() - 1; i > 0; i--) {
123-
// From the permanent limit to the most serious temporary limit.
124-
sortedLimits.get(i).setAcceptableDuration(sortedLimits.get(i - 1).getAcceptableDuration());
125-
}
126-
sortedLimits.get(0).setAcceptableDuration(0);
127-
}
128-
return sortedLimits;
129-
}
130-
13194
@Override
13295
public ElementType getType() {
13396
return ElementType.BRANCH;
@@ -143,7 +106,7 @@ public LfBus getBus2() {
143106
return bus2;
144107
}
145108

146-
private List<LfLimit> getLimits1(LimitType type) {
109+
private List<LfLimitsGroup> getLimits1(LimitType type) {
147110
switch (type) {
148111
case ACTIVE_POWER -> {
149112
return activePowerLimits1;
@@ -158,7 +121,7 @@ private List<LfLimit> getLimits1(LimitType type) {
158121
}
159122
}
160123

161-
private void setLimits1(LimitType type, List<LfLimit> limits) {
124+
private void setLimits1(LimitType type, List<LfLimitsGroup> limits) {
162125
switch (type) {
163126
case ACTIVE_POWER -> activePowerLimits1 = limits;
164127
case APPARENT_POWER -> apparentPowerLimits1 = limits;
@@ -167,19 +130,31 @@ private void setLimits1(LimitType type, List<LfLimit> limits) {
167130
}
168131
}
169132

170-
public <T extends LoadingLimits> List<LfLimit> getLimits1(LimitType type, Supplier<Optional<T>> loadingLimitsSupplier, LimitReductionManager limitReductionManager) {
171-
var limits = getLimits1(type);
133+
private <T extends LoadingLimits> List<LfLimitsGroup> createLimits(Supplier<Map<String, T>> loadingLimitsSupplier, LimitReductionManager limitReductionManager, TwoSides side) {
134+
// It is possible to apply the reductions here since the only supported ContingencyContext for LimitReduction is ALL.
135+
Map<String, T> allSelectedLoadingLimits = loadingLimitsSupplier.get(); // Map of all selected loading limits indexed by their operational limits group id
136+
List<LfLimitsGroup> limits = new ArrayList<>();
137+
for (Map.Entry<String, T> loadingLimitsEntry : allSelectedLoadingLimits.entrySet()) {
138+
T loadingLimits = loadingLimitsEntry.getValue();
139+
String operationalLimitsGroupId = loadingLimitsEntry.getKey();
140+
limits.add(LfLimitsGroup.createSortedLimitsList(loadingLimits,
141+
operationalLimitsGroupId,
142+
side == TwoSides.ONE ? bus1 : bus2,
143+
getLimitReductions(side, limitReductionManager, loadingLimits)));
144+
}
145+
return limits;
146+
}
147+
148+
public <T extends LoadingLimits> List<LfLimitsGroup> getLimits1(LimitType type, Supplier<Map<String, T>> loadingLimitsSupplier, LimitReductionManager limitReductionManager) {
149+
List<LfLimitsGroup> limits = getLimits1(type);
172150
if (limits == null) {
173-
// It is possible to apply the reductions here since the only supported ContingencyContext for LimitReduction is ALL.
174-
var loadingLimits = loadingLimitsSupplier.get().orElse(null);
175-
limits = createSortedLimitsList(loadingLimits, bus1,
176-
getLimitReductions(TwoSides.ONE, limitReductionManager, loadingLimits));
151+
limits = createLimits(loadingLimitsSupplier, limitReductionManager, TwoSides.ONE);
177152
setLimits1(type, limits);
178153
}
179154
return limits;
180155
}
181156

182-
private List<LfLimit> getLimits2(LimitType type) {
157+
private List<LfLimitsGroup> getLimits2(LimitType type) {
183158
switch (type) {
184159
case ACTIVE_POWER -> {
185160
return activePowerLimits2;
@@ -194,7 +169,7 @@ private List<LfLimit> getLimits2(LimitType type) {
194169
}
195170
}
196171

197-
private void setLimits2(LimitType type, List<LfLimit> limits) {
172+
private void setLimits2(LimitType type, List<LfLimitsGroup> limits) {
198173
switch (type) {
199174
case ACTIVE_POWER -> activePowerLimits2 = limits;
200175
case APPARENT_POWER -> apparentPowerLimits2 = limits;
@@ -203,13 +178,10 @@ private void setLimits2(LimitType type, List<LfLimit> limits) {
203178
}
204179
}
205180

206-
public <T extends LoadingLimits> List<LfLimit> getLimits2(LimitType type, Supplier<Optional<T>> loadingLimitsSupplier, LimitReductionManager limitReductionManager) {
181+
public <T extends LoadingLimits> List<LfLimitsGroup> getLimits2(LimitType type, Supplier<Map<String, T>> loadingLimitsSupplier, LimitReductionManager limitReductionManager) {
207182
var limits = getLimits2(type);
208183
if (limits == null) {
209-
// It is possible to apply the reductions here since the only supported ContingencyContext for LimitReduction is ALL.
210-
var loadingLimits = loadingLimitsSupplier.get().orElse(null);
211-
limits = createSortedLimitsList(loadingLimits, bus2,
212-
getLimitReductions(TwoSides.TWO, limitReductionManager, loadingLimits));
184+
limits = createLimits(loadingLimitsSupplier, limitReductionManager, TwoSides.TWO);
213185
setLimits2(type, limits);
214186
}
215187
return limits;
@@ -264,19 +236,6 @@ protected void updateSolvedTapPosition(RatioTapChanger rtc, double ptcRho, doubl
264236
Transformers.findTapPosition(rtc, ptcRho, rho).ifPresent(rtc::setSolvedTapPosition);
265237
}
266238

267-
protected static double getScaleForLimitType(LimitType type, LfBus bus) {
268-
switch (type) {
269-
case ACTIVE_POWER,
270-
APPARENT_POWER:
271-
return 1.0 / PerUnit.SB;
272-
case CURRENT:
273-
return 1.0 / PerUnit.ib(bus.getNominalV());
274-
case VOLTAGE:
275-
default:
276-
throw new UnsupportedOperationException(String.format("Getting scale for limit type %s is not supported.", type));
277-
}
278-
}
279-
280239
@Override
281240
public Optional<TransformerVoltageControl> getVoltageControl() {
282241
return Optional.ofNullable(voltageControl);

src/main/java/com/powsybl/openloadflow/network/impl/LfBoundaryLineBranch.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@
77
*/
88
package com.powsybl.openloadflow.network.impl;
99

10-
import com.powsybl.iidm.network.BoundaryLine;
11-
import com.powsybl.iidm.network.LimitType;
12-
import com.powsybl.iidm.network.LoadingLimits;
13-
import com.powsybl.iidm.network.TwoSides;
10+
import com.powsybl.iidm.network.*;
1411
import com.powsybl.openloadflow.network.*;
1512
import com.powsybl.openloadflow.sa.LimitReductionManager;
1613
import com.powsybl.openloadflow.util.PerUnit;
@@ -19,6 +16,10 @@
1916
import java.util.List;
2017
import java.util.Map;
2118
import java.util.Objects;
19+
import java.util.Optional;
20+
import java.util.function.Function;
21+
import java.util.function.Supplier;
22+
import java.util.stream.Collectors;
2223

2324
/**
2425
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
@@ -80,15 +81,23 @@ public List<BranchResult> createBranchResult(double preContingencyBranchP1, doub
8081
return List.of(buildBranchResult(loadFlowModel, zeroImpedanceFlows, currentScale, currentScale, Double.NaN, Double.NaN));
8182
}
8283

84+
private <T extends LoadingLimits> Supplier<Map<String, T>> toMapIndexedByOperationalLimitsGroupId(Function<OperationalLimitsGroup, Optional<T>> limitsGetter) {
85+
return () -> getBoundaryLine()
86+
.getAllSelectedOperationalLimitsGroups()
87+
.stream()
88+
.filter(o -> limitsGetter.apply(o).isPresent())
89+
.collect(Collectors.toMap(OperationalLimitsGroup::getId, o -> limitsGetter.apply(o).orElseThrow()));
90+
}
91+
8392
@Override
84-
public List<LfLimit> getLimits1(final LimitType type, LimitReductionManager limitReductionManager) {
93+
public List<LfLimitsGroup> getLimits1(final LimitType type, LimitReductionManager limitReductionManager) {
8594
switch (type) {
8695
case ACTIVE_POWER:
87-
return getLimits1(type, () -> getBoundaryLine().getActivePowerLimits(), limitReductionManager);
96+
return getLimits1(type, toMapIndexedByOperationalLimitsGroupId(OperationalLimitsGroup::getActivePowerLimits), limitReductionManager);
8897
case APPARENT_POWER:
89-
return getLimits1(type, () -> getBoundaryLine().getApparentPowerLimits(), limitReductionManager);
98+
return getLimits1(type, toMapIndexedByOperationalLimitsGroupId(OperationalLimitsGroup::getApparentPowerLimits), limitReductionManager);
9099
case CURRENT:
91-
return getLimits1(type, () -> getBoundaryLine().getCurrentLimits(), limitReductionManager);
100+
return getLimits1(type, toMapIndexedByOperationalLimitsGroupId(OperationalLimitsGroup::getCurrentLimits), limitReductionManager);
92101
case VOLTAGE:
93102
default:
94103
throw new UnsupportedOperationException(String.format("Getting %s limits is not supported.", type.name()));

src/main/java/com/powsybl/openloadflow/network/impl/LfBranchImpl.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
import org.slf4j.LoggerFactory;
1919

2020
import java.util.*;
21+
import java.util.function.Function;
22+
import java.util.function.Supplier;
23+
import java.util.stream.Collectors;
2124

2225
/**
2326
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
@@ -228,30 +231,38 @@ public List<BranchResult> createBranchResult(double preContingencyBranchP1, doub
228231
return List.of(branchResult);
229232
}
230233

234+
private <T extends LoadingLimits> Supplier<Map<String, T>> toMapIndexedByOperationalLimitsGroupId(Function<OperationalLimitsGroup, Optional<T>> limitsGetter, TwoSides side) {
235+
return () -> getBranch()
236+
.getAllSelectedOperationalLimitsGroups(side)
237+
.stream()
238+
.filter(o -> limitsGetter.apply(o).isPresent())
239+
.collect(Collectors.toMap(OperationalLimitsGroup::getId, o -> limitsGetter.apply(o).orElseThrow()));
240+
}
241+
231242
@Override
232-
public List<LfLimit> getLimits1(final LimitType type, LimitReductionManager limitReductionManager) {
243+
public List<LfLimitsGroup> getLimits1(final LimitType type, LimitReductionManager limitReductionManager) {
233244
switch (type) {
234245
case ACTIVE_POWER:
235-
return getLimits1(type, () -> getBranch().getActivePowerLimits1(), limitReductionManager);
246+
return getLimits1(type, toMapIndexedByOperationalLimitsGroupId(OperationalLimitsGroup::getActivePowerLimits, TwoSides.ONE), limitReductionManager);
236247
case APPARENT_POWER:
237-
return getLimits1(type, () -> getBranch().getApparentPowerLimits1(), limitReductionManager);
248+
return getLimits1(type, toMapIndexedByOperationalLimitsGroupId(OperationalLimitsGroup::getApparentPowerLimits, TwoSides.ONE), limitReductionManager);
238249
case CURRENT:
239-
return getLimits1(type, () -> getBranch().getCurrentLimits1(), limitReductionManager);
250+
return getLimits1(type, toMapIndexedByOperationalLimitsGroupId(OperationalLimitsGroup::getCurrentLimits, TwoSides.ONE), limitReductionManager);
240251
case VOLTAGE:
241252
default:
242253
throw new UnsupportedOperationException(String.format("Getting %s limits is not supported.", type.name()));
243254
}
244255
}
245256

246257
@Override
247-
public List<LfLimit> getLimits2(final LimitType type, LimitReductionManager limitReductionManager) {
258+
public List<LfLimitsGroup> getLimits2(final LimitType type, LimitReductionManager limitReductionManager) {
248259
switch (type) {
249260
case ACTIVE_POWER:
250-
return getLimits2(type, () -> getBranch().getActivePowerLimits2(), limitReductionManager);
261+
return getLimits2(type, toMapIndexedByOperationalLimitsGroupId(OperationalLimitsGroup::getActivePowerLimits, TwoSides.TWO), limitReductionManager);
251262
case APPARENT_POWER:
252-
return getLimits2(type, () -> getBranch().getApparentPowerLimits2(), limitReductionManager);
263+
return getLimits2(type, toMapIndexedByOperationalLimitsGroupId(OperationalLimitsGroup::getApparentPowerLimits, TwoSides.TWO), limitReductionManager);
253264
case CURRENT:
254-
return getLimits2(type, () -> getBranch().getCurrentLimits2(), limitReductionManager);
265+
return getLimits2(type, toMapIndexedByOperationalLimitsGroupId(OperationalLimitsGroup::getCurrentLimits, TwoSides.TWO), limitReductionManager);
255266
case VOLTAGE:
256267
default:
257268
throw new UnsupportedOperationException(String.format("Getting %s limits is not supported.", type.name()));

0 commit comments

Comments
 (0)