Skip to content

Commit 1db9dfb

Browse files
bassecheEtienneLt
andauthored
Manage change of applicability on branch modification (#108)
Signed-off-by: basseche <[email protected]> Signed-off-by: Etienne LESOT <[email protected]> Co-authored-by: EtienneLt <[email protected]>
1 parent 880bf2a commit 1db9dfb

File tree

4 files changed

+268
-12
lines changed

4 files changed

+268
-12
lines changed

src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public abstract class AbstractBranchModification extends AbstractModification {
3737
private static final String LIMIT_ACCEPTABLE_DURATION = "limitAcceptableDuration";
3838
private static final String OPERATIONAL_LIMITS_GROUP_NAME = "operationalLimitsGroupName";
3939
private static final String SIDE = "side";
40+
private static final String APPLICABILITY = "applicability";
4041

4142
protected final BranchModificationInfos modificationInfos;
4243

@@ -82,6 +83,113 @@ protected void modifyBranch(Branch<?> branch, BranchModificationInfos branchModi
8283
updateConnections(branch, branchModificationInfos);
8384
}
8485

86+
private void copyOperationalLimitsGroup(CurrentLimitsAdder limitsAdder, OperationalLimitsGroup opLimitGroupToCopy) {
87+
// Copy all limits of the other side
88+
opLimitGroupToCopy.getCurrentLimits().ifPresent(currentLimits -> {
89+
limitsAdder.setPermanentLimit(currentLimits.getPermanentLimit());
90+
91+
for (LoadingLimits.TemporaryLimit tempLimit : currentLimits.getTemporaryLimits()) {
92+
addTemporaryLimit(limitsAdder, tempLimit.getName(), tempLimit.getValue(), tempLimit.getAcceptableDuration());
93+
}
94+
limitsAdder.add();
95+
});
96+
}
97+
98+
private void moveLimitSetToTheOtherSide(Branch<?> branch, List<OperationalLimitsGroupModificationInfos> modificationInfos,
99+
OperationalLimitsGroup limitsGroupToCopy, String modifiedLimitSet,
100+
boolean isSide1, List<ReportNode> olgReports) {
101+
// if we have only one limit set with the same name but applicability is not good
102+
// we should copy existing limit set on the right side and removed it from the other side
103+
if (modificationInfos.stream().filter(limitSet -> limitSet.getId().equals(modifiedLimitSet)).toList().size() == 1) {
104+
// Copy operational limits group to the other side
105+
OperationalLimitsGroup limitsGroup = isSide1 ? branch.newOperationalLimitsGroup1(limitsGroupToCopy.getId())
106+
: branch.newOperationalLimitsGroup2(limitsGroupToCopy.getId());
107+
copyOperationalLimitsGroup(limitsGroup.newCurrentLimits(), limitsGroupToCopy);
108+
109+
olgReports.add(ReportNode.newRootReportNode().withMessageTemplate("network.modification.applicabilityChanged")
110+
.withUntypedValue(OPERATIONAL_LIMITS_GROUP_NAME, limitsGroupToCopy.getId())
111+
.withUntypedValue(APPLICABILITY, isSide1 ? SIDE1.toString() : SIDE2.toString())
112+
.withSeverity(TypedValue.INFO_SEVERITY)
113+
.build());
114+
// Remove copied operational limits group
115+
if (isSide1) {
116+
branch.removeOperationalLimitsGroup2(modifiedLimitSet);
117+
} else {
118+
branch.removeOperationalLimitsGroup1(modifiedLimitSet);
119+
}
120+
}
121+
}
122+
123+
private boolean shouldDeletedOtherSide(Branch<?> branch, List<OperationalLimitsGroupModificationInfos> modificationInfos,
124+
OperationalLimitsGroupModificationInfos limitsModifInfos) {
125+
boolean hasModificationOnSideOne = !modificationInfos.stream().filter(opLimitModifInfo ->
126+
opLimitModifInfo.getId().equals(limitsModifInfos.getId()) && opLimitModifInfo.getApplicability().equals(SIDE1))
127+
.toList().isEmpty();
128+
129+
boolean hasModificationOnSideTwo = !modificationInfos.stream().filter(opLimitModifInfo ->
130+
opLimitModifInfo.getId().equals(limitsModifInfos.getId()) && opLimitModifInfo.getApplicability().equals(SIDE2))
131+
.toList().isEmpty();
132+
133+
switch (limitsModifInfos.getApplicability()) {
134+
case SIDE1 -> {
135+
return !hasModificationOnSideTwo && branch.getOperationalLimitsGroup2(limitsModifInfos.getId()).isPresent();
136+
}
137+
case SIDE2 -> {
138+
return !hasModificationOnSideOne && branch.getOperationalLimitsGroup1(limitsModifInfos.getId()).isPresent();
139+
}
140+
default -> {
141+
return false;
142+
}
143+
}
144+
}
145+
146+
// If we are changing applicability we may not find operational limits group where we should so check both sides
147+
private void detectApplicabilityChange(Branch<?> branch, List<OperationalLimitsGroupModificationInfos> modificationInfos,
148+
OperationalLimitsGroupModificationInfos modifiedLimitSetInfos, List<ReportNode> olgReports) {
149+
150+
OperationalLimitsGroup limitsGroup1 = branch.getOperationalLimitsGroup1(modifiedLimitSetInfos.getId()).orElse(null);
151+
OperationalLimitsGroup limitsGroup2 = branch.getOperationalLimitsGroup2(modifiedLimitSetInfos.getId()).orElse(null);
152+
if (limitsGroup1 == null && modifiedLimitSetInfos.getApplicability().equals(SIDE2)
153+
|| limitsGroup2 == null && modifiedLimitSetInfos.getApplicability().equals(SIDE1)) {
154+
return;
155+
} else if (limitsGroup1 != null && limitsGroup2 != null && (modifiedLimitSetInfos.getApplicability().equals(SIDE1)
156+
|| modifiedLimitSetInfos.getApplicability().equals(SIDE2))) {
157+
if (shouldDeletedOtherSide(branch, modificationInfos, modifiedLimitSetInfos)) {
158+
if (modifiedLimitSetInfos.getApplicability().equals(SIDE1)) {
159+
branch.removeOperationalLimitsGroup2(modifiedLimitSetInfos.getId());
160+
} else {
161+
branch.removeOperationalLimitsGroup1(modifiedLimitSetInfos.getId());
162+
}
163+
}
164+
return;
165+
}
166+
167+
switch (modifiedLimitSetInfos.getApplicability()) {
168+
case SIDE1 -> moveLimitSetToTheOtherSide(branch, modificationInfos, limitsGroup2, modifiedLimitSetInfos.getId(), true, olgReports);
169+
case SIDE2 -> moveLimitSetToTheOtherSide(branch, modificationInfos, limitsGroup1, modifiedLimitSetInfos.getId(), false, olgReports);
170+
case EQUIPMENT -> {
171+
boolean applicabilityChanged = false;
172+
if (limitsGroup1 == null && limitsGroup2 != null) {
173+
limitsGroup1 = branch.newOperationalLimitsGroup1(limitsGroup2.getId());
174+
copyOperationalLimitsGroup(limitsGroup1.newCurrentLimits(), limitsGroup2);
175+
applicabilityChanged = true;
176+
}
177+
if (limitsGroup2 == null && limitsGroup1 != null) {
178+
limitsGroup2 = branch.newOperationalLimitsGroup2(limitsGroup1.getId());
179+
copyOperationalLimitsGroup(limitsGroup2.newCurrentLimits(), limitsGroup1);
180+
applicabilityChanged = true;
181+
}
182+
if (applicabilityChanged) {
183+
olgReports.add(ReportNode.newRootReportNode().withMessageTemplate("network.modification.applicabilityChanged")
184+
.withUntypedValue(OPERATIONAL_LIMITS_GROUP_NAME, limitsGroup1.getId())
185+
.withUntypedValue(APPLICABILITY, EQUIPMENT.toString())
186+
.withSeverity(TypedValue.INFO_SEVERITY)
187+
.build());
188+
}
189+
}
190+
}
191+
}
192+
85193
private void modifyOperationalLimitsGroups(Branch<?> branch, List<OperationalLimitsGroupModificationInfos> operationalLimitsInfos, List<ReportNode> olgReports) {
86194
for (OperationalLimitsGroupModificationInfos opLGModifInfos : operationalLimitsInfos) {
87195
if (opLGModifInfos.getModificationType() == null) {
@@ -90,13 +198,16 @@ private void modifyOperationalLimitsGroups(Branch<?> branch, List<OperationalLim
90198
OperationalLimitsGroupInfos.Applicability applicability = opLGModifInfos.getApplicability();
91199
// here the modifications on an applicability EQUIPMENT are separated into two separate applications of both sides
92200
// because iidm has two separated sets of opLGs on the network object (and for better logs)
201+
202+
detectApplicabilityChange(branch, operationalLimitsInfos, opLGModifInfos, olgReports);
203+
93204
if (applicability == SIDE1
94-
|| applicability == OperationalLimitsGroupInfos.Applicability.EQUIPMENT) {
205+
|| applicability == EQUIPMENT) {
95206
OperationalLimitsGroup operationalLimitsGroup1 = branch.getOperationalLimitsGroup1(opLGModifInfos.getId()).orElse(null);
96207
applyModificationToOperationalLimitsGroup(branch::newOperationalLimitsGroup1, opLGModifInfos, operationalLimitsGroup1, olgReports, SIDE1);
97208
}
98209
if (applicability == SIDE2
99-
|| applicability == OperationalLimitsGroupInfos.Applicability.EQUIPMENT) {
210+
|| applicability == EQUIPMENT) {
100211
OperationalLimitsGroup operationalLimitsGroup2 = branch.getOperationalLimitsGroup2(opLGModifInfos.getId()).orElse(null);
101212
applyModificationToOperationalLimitsGroup(branch::newOperationalLimitsGroup2, opLGModifInfos, operationalLimitsGroup2, olgReports, SIDE2);
102213
}

src/main/resources/org/gridsuite/modification/reports.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ network.modification.noLimitSetSelectedOnSide1 = No limit set selected on side 1
184184
network.modification.noLimitSetSelectedOnSide2 = No limit set selected on side 2
185185
network.modification.limitSetAbsentOnSide1 = limit set '${selectedOperationalLimitsGroup}' on side 1 does not exist
186186
network.modification.limitSetAbsentOnSide2 = limit set '${selectedOperationalLimitsGroup}' on side 2 does not exist
187+
network.modification.applicabilityChanged = limit set ${operationalLimitsGroupName} applicability changed to ${applicability}
187188
network.modification.limits = Limits
188189
network.modification.activeLimitsSets = Active limits sets
189190
network.modification.limitsSets = Limits sets

src/test/java/org/gridsuite/modification/modifications/LineModificationTest.java

Lines changed: 151 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
package org.gridsuite.modification.modifications;
88

99
import com.fasterxml.jackson.core.type.TypeReference;
10-
import com.powsybl.iidm.network.Line;
11-
import com.powsybl.iidm.network.Network;
12-
import com.powsybl.iidm.network.ThreeSides;
10+
import com.powsybl.iidm.network.*;
1311
import com.powsybl.iidm.network.extensions.ConnectablePosition;
1412
import com.powsybl.iidm.network.extensions.Measurement;
1513
import com.powsybl.iidm.network.extensions.Measurements;
@@ -20,13 +18,12 @@
2018
import org.gridsuite.modification.utils.NetworkCreation;
2119
import org.junit.jupiter.api.Test;
2220

23-
import java.util.Collection;
24-
import java.util.List;
25-
import java.util.Map;
26-
import java.util.UUID;
21+
import java.util.*;
2722

2823
import static org.assertj.core.api.Assertions.assertThat;
2924
import static org.gridsuite.modification.NetworkModificationException.Type.LINE_NOT_FOUND;
25+
import static org.gridsuite.modification.dto.OperationalLimitsGroupInfos.Applicability.*;
26+
import static org.gridsuite.modification.dto.OperationalLimitsGroupModificationType.MODIFY_OR_ADD;
3027
import static org.junit.jupiter.api.Assertions.*;
3128

3229
/**
@@ -163,6 +160,153 @@ void testConnection() {
163160
changeLineConnectionState(getNetwork().getLine("line1"), true);
164161
}
165162

163+
@Test
164+
void testApplicabilityChangeFromSide1ToSide2() {
165+
Line line = getNetwork().getLine("line1");
166+
OperationalLimitsGroup limitsGroup = line.newOperationalLimitsGroup1("NewLimitsGroup1");
167+
limitsGroup.newCurrentLimits()
168+
.setPermanentLimit(10.0)
169+
.beginTemporaryLimit().setName("IT10").setValue(15.0).setAcceptableDuration(600).endTemporaryLimit()
170+
.add();
171+
172+
assertNotNull(line.getOperationalLimitsGroup1("NewLimitsGroup1"));
173+
assertTrue(line.getOperationalLimitsGroup2("NewLimitsGroup1").isEmpty());
174+
175+
// Change from Side 1 -> Side 2
176+
OperationalLimitsGroupModificationInfos opLimitsGroupInfos = OperationalLimitsGroupModificationInfos.builder()
177+
.id("NewLimitsGroup1").applicability(SIDE2).modificationType(MODIFY_OR_ADD).build();
178+
opLimitsGroupInfos.setCurrentLimits(new CurrentLimitsModificationInfos());
179+
LineModificationInfos lineModificationInfos = LineModificationInfos.builder()
180+
.equipmentId("line1")
181+
.enableOLGModification(true)
182+
.operationalLimitsGroups(Collections.singletonList(opLimitsGroupInfos)).build();
183+
184+
lineModificationInfos.toModification().apply(getNetwork());
185+
assertTrue(line.getOperationalLimitsGroup1("NewLimitsGroup1").isEmpty());
186+
assertNotNull(line.getOperationalLimitsGroup2("NewLimitsGroup1"));
187+
188+
// Test on the copy of the limit set
189+
OperationalLimitsGroup opLimitsGroupOnLine = line.getOperationalLimitsGroup2("NewLimitsGroup1").get();
190+
assertTrue(opLimitsGroupOnLine.getCurrentLimits().isPresent());
191+
CurrentLimits currentLimits = opLimitsGroupOnLine.getCurrentLimits().get();
192+
assertEquals(10.0, currentLimits.getPermanentLimit());
193+
assertNotNull(currentLimits.getTemporaryLimit(600));
194+
LoadingLimits.TemporaryLimit temporaryLimit = currentLimits.getTemporaryLimit(600);
195+
assertEquals(15.0, temporaryLimit.getValue());
196+
assertEquals(600, temporaryLimit.getAcceptableDuration());
197+
assertEquals("IT10", temporaryLimit.getName());
198+
}
199+
200+
@Test
201+
void testApplicabilityChangeFromSide2ToEquipment() {
202+
Line line = getNetwork().getLine("line1");
203+
// Change from Side 2 -> Equipment
204+
line.newOperationalLimitsGroup2("NewLimitsGroup2").newCurrentLimits()
205+
.setPermanentLimit(10.0)
206+
.beginTemporaryLimit().setName("IT10").setValue(15.0).setAcceptableDuration(600).endTemporaryLimit()
207+
.add();
208+
209+
OperationalLimitsGroupModificationInfos opLimitsGroupInfos2 = OperationalLimitsGroupModificationInfos.builder()
210+
.id("NewLimitsGroup2").applicability(EQUIPMENT).modificationType(MODIFY_OR_ADD).build();
211+
opLimitsGroupInfos2.setCurrentLimits(new CurrentLimitsModificationInfos());
212+
213+
LineModificationInfos lineModificationInfos2 = LineModificationInfos.builder()
214+
.equipmentId("line1")
215+
.enableOLGModification(true)
216+
.operationalLimitsGroups(Collections.singletonList(opLimitsGroupInfos2)).build();
217+
lineModificationInfos2.toModification().apply(getNetwork());
218+
219+
lineModificationInfos2.toModification().apply(getNetwork());
220+
assertNotNull(line.getOperationalLimitsGroup1("NewLimitsGroup2"));
221+
assertNotNull(line.getOperationalLimitsGroup2("NewLimitsGroup2"));
222+
}
223+
224+
@Test
225+
void testApplicabilityChangeFromEquipmentToSide2() {
226+
Line line = getNetwork().getLine("line1");
227+
line.newOperationalLimitsGroup1("NewLimitsGroup3").newCurrentLimits()
228+
.setPermanentLimit(10.0)
229+
.add();
230+
line.newOperationalLimitsGroup2("NewLimitsGroup3").newCurrentLimits()
231+
.setPermanentLimit(10.0)
232+
.add();
233+
OperationalLimitsGroupModificationInfos opLimitsGroupInfos3 = OperationalLimitsGroupModificationInfos.builder()
234+
.id("NewLimitsGroup3").applicability(SIDE2).modificationType(MODIFY_OR_ADD).build();
235+
opLimitsGroupInfos3.setCurrentLimits(new CurrentLimitsModificationInfos());
236+
LineModificationInfos lineModificationInfos3 = LineModificationInfos.builder()
237+
.enableOLGModification(true)
238+
.equipmentId("line1")
239+
.operationalLimitsGroups(Collections.singletonList(opLimitsGroupInfos3)).build();
240+
241+
lineModificationInfos3.toModification().apply(getNetwork());
242+
assertNotNull(line.getOperationalLimitsGroup2("NewLimitsGroup3"));
243+
assertTrue(line.getOperationalLimitsGroup1("NewLimitsGroup3").isEmpty());
244+
}
245+
246+
@Test
247+
void testApplicabilityChangeFromSide2ToSide1() {
248+
Line line = getNetwork().getLine("line1");
249+
line.newOperationalLimitsGroup2("NewLimitsGroup4").newCurrentLimits()
250+
.setPermanentLimit(10.0)
251+
.add();
252+
OperationalLimitsGroupModificationInfos opLimitsGroupInfos4 = OperationalLimitsGroupModificationInfos.builder()
253+
.id("NewLimitsGroup4").applicability(SIDE2).modificationType(MODIFY_OR_ADD).build();
254+
opLimitsGroupInfos4.setCurrentLimits(new CurrentLimitsModificationInfos());
255+
LineModificationInfos lineModificationInfos4 = LineModificationInfos.builder()
256+
.enableOLGModification(true)
257+
.equipmentId("line1")
258+
.operationalLimitsGroups(Collections.singletonList(opLimitsGroupInfos4)).build();
259+
260+
lineModificationInfos4.toModification().apply(getNetwork());
261+
assertNotNull(line.getOperationalLimitsGroup1("NewLimitsGroup4"));
262+
assertTrue(line.getOperationalLimitsGroup1("NewLimitsGroup4").isEmpty());
263+
}
264+
265+
@Test
266+
void testApplicabilityChangeFromSide1ToEquipment() {
267+
Line line = getNetwork().getLine("line1");
268+
line.newOperationalLimitsGroup1("NewLimitsGroup5").newCurrentLimits()
269+
.setPermanentLimit(10.0)
270+
.add();
271+
OperationalLimitsGroupModificationInfos opLimitsGroupInfos5 = OperationalLimitsGroupModificationInfos.builder()
272+
.id("NewLimitsGroup5").applicability(EQUIPMENT).modificationType(MODIFY_OR_ADD).build();
273+
opLimitsGroupInfos5.setCurrentLimits(new CurrentLimitsModificationInfos());
274+
275+
LineModificationInfos lineModificationInfos5 = LineModificationInfos.builder()
276+
.equipmentId("line1")
277+
.enableOLGModification(true)
278+
.operationalLimitsGroups(Collections.singletonList(opLimitsGroupInfos5)).build();
279+
lineModificationInfos5.toModification().apply(getNetwork());
280+
281+
lineModificationInfos5.toModification().apply(getNetwork());
282+
assertNotNull(line.getOperationalLimitsGroup1("NewLimitsGroup5"));
283+
assertNotNull(line.getOperationalLimitsGroup2("NewLimitsGroup5"));
284+
}
285+
286+
@Test
287+
void testApplicabilityChangeFromEquipmentToSide1() {
288+
Line line = getNetwork().getLine("line1");
289+
line.newOperationalLimitsGroup1("NewLimitsGroup5").newCurrentLimits()
290+
.setPermanentLimit(10.0)
291+
.add();
292+
line.newOperationalLimitsGroup2("NewLimitsGroup5").newCurrentLimits()
293+
.setPermanentLimit(10.0)
294+
.add();
295+
OperationalLimitsGroupModificationInfos opLimitsGroupInfos5 = OperationalLimitsGroupModificationInfos.builder()
296+
.id("NewLimitsGroup5").applicability(SIDE1).modificationType(MODIFY_OR_ADD).build();
297+
opLimitsGroupInfos5.setCurrentLimits(new CurrentLimitsModificationInfos());
298+
299+
LineModificationInfos lineModificationInfos5 = LineModificationInfos.builder()
300+
.equipmentId("line1")
301+
.enableOLGModification(true)
302+
.operationalLimitsGroups(Collections.singletonList(opLimitsGroupInfos5)).build();
303+
lineModificationInfos5.toModification().apply(getNetwork());
304+
305+
lineModificationInfos5.toModification().apply(getNetwork());
306+
assertNotNull(line.getOperationalLimitsGroup1("NewLimitsGroup5"));
307+
assertTrue(line.getOperationalLimitsGroup2("NewLimitsGroup5").isEmpty());
308+
}
309+
166310
@Test
167311
void testConnectWhenNoSwitchesOpened() {
168312
getNetwork().getSwitch("v3dl1").setOpen(true);

0 commit comments

Comments
 (0)