diff --git a/src/main/java/org/gridsuite/modification/dto/TemporaryLimitModificationType.java b/src/main/java/org/gridsuite/modification/dto/TemporaryLimitModificationType.java index 5eb5d0a3..b23ccc41 100644 --- a/src/main/java/org/gridsuite/modification/dto/TemporaryLimitModificationType.java +++ b/src/main/java/org/gridsuite/modification/dto/TemporaryLimitModificationType.java @@ -12,6 +12,7 @@ public enum TemporaryLimitModificationType { ADD, MODIFY, + MODIFY_OR_ADD, DELETE, REPLACE } diff --git a/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java b/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java index 6c2fe162..06dec973 100644 --- a/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java +++ b/src/main/java/org/gridsuite/modification/modifications/AbstractBranchModification.java @@ -11,6 +11,7 @@ import com.powsybl.commons.report.TypedValue; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.*; +import jakarta.validation.constraints.NotNull; import org.gridsuite.modification.NetworkModificationException; import org.gridsuite.modification.dto.*; import org.gridsuite.modification.utils.ModificationUtils; @@ -380,7 +381,10 @@ public boolean isThisLimitDeleted(List t .anyMatch(temporaryLimit -> temporaryLimit.getAcceptableDuration() == acceptableDuration && temporaryLimit.getModificationType() == TemporaryLimitModificationType.DELETE); } - protected void modifyTemporaryLimits(OperationalLimitsGroupModificationInfos operationalLimitsGroupModificationInfos, + /** + * This function removes all the temporary limits of the 'currentLimits' concerned and recreates them (except in case of deletion) + */ + protected void modifyTemporaryLimits(@NotNull OperationalLimitsGroupModificationInfos operationalLimitsGroupModificationInfos, CurrentLimitsAdder limitsAdder, CurrentLimits currentLimits, List limitsReports) { @@ -388,12 +392,12 @@ protected void modifyTemporaryLimits(OperationalLimitsGroupModificationInfos ope // we create a mutable list of temporary limits to be able to remove the limits that are modified in current modification List branchTemporaryLimits = new ArrayList<>(); - boolean areLimitsReplaced = operationalLimitsGroupModificationInfos != null && TemporaryLimitModificationType.REPLACE.equals(operationalLimitsGroupModificationInfos.getTemporaryLimitsModificationType()); + boolean areLimitsReplaced = TemporaryLimitModificationType.REPLACE.equals(operationalLimitsGroupModificationInfos.getTemporaryLimitsModificationType()); if (currentLimits != null && !areLimitsReplaced) { branchTemporaryLimits.addAll(currentLimits.getTemporaryLimits()); } List temporaryLimitsReports = new ArrayList<>(); - if (operationalLimitsGroupModificationInfos != null && TemporaryLimitModificationType.REPLACE.equals(operationalLimitsGroupModificationInfos.getTemporaryLimitsModificationType())) { + if (areLimitsReplaced) { temporaryLimitsReports.add(ReportNode.newRootReportNode() .withAllResourceBundlesFromClasspath() .withMessageTemplate("network.modification.temporaryLimitsReplaced") @@ -403,98 +407,24 @@ protected void modifyTemporaryLimits(OperationalLimitsGroupModificationInfos ope if (currentLimitsInfos != null && currentLimitsInfos.getTemporaryLimits() != null) { for (CurrentTemporaryLimitModificationInfos limit : currentLimitsInfos.getTemporaryLimits()) { - int limitAcceptableDuration = limit.getAcceptableDuration() == null ? Integer.MAX_VALUE : limit.getAcceptableDuration(); - double limitValue = limit.getValue() == null ? Double.MAX_VALUE : limit.getValue(); - String limitDurationToReport = limitAcceptableDuration == Integer.MAX_VALUE ? " " : String.valueOf(limitAcceptableDuration); - String limitValueToReport = limitValue == Double.MAX_VALUE ? "no value" : String.valueOf(limitValue); - LoadingLimits.TemporaryLimit limitToModify = null; - if (currentLimits != null) { - limitToModify = currentLimits.getTemporaryLimit(limitAcceptableDuration); - if (limitToModify != null && !limitToModify.getName().equals(limit.getName())) { - boolean isThisLimitDeleted = isThisLimitDeleted(currentLimitsInfos.getTemporaryLimits(), limitAcceptableDuration); - if (isThisLimitDeleted) { - limitToModify = null; - } else if (TemporaryLimitModificationType.ADD.equals(limit.getModificationType())) { - throw new PowsyblException("2 temporary limits have the same duration " + limitAcceptableDuration); - } - } - - //Additional check for limit sets tabular modifications - if (operationalLimitsGroupModificationInfos != null && TemporaryLimitModificationType.ADD.equals(operationalLimitsGroupModificationInfos.getTemporaryLimitsModificationType())) { - currentLimits.getTemporaryLimits().stream().filter(temporaryLimit -> temporaryLimit.getName().equals(limit.getName())).findFirst().ifPresent(temporaryLimit -> { - throw new PowsyblException("2 temporary limits have the same name " + limit.getName()); - }); - } - // we remove the limit to modify from the list of temporary limits so we can get the list of temporary limits coming from previous modifications - branchTemporaryLimits.removeIf(temporaryLimit -> temporaryLimit.getAcceptableDuration() == limitAcceptableDuration); - } - if (limitToModify == null && limit.getModificationType() == TemporaryLimitModificationType.ADD || limit.getModificationType() == TemporaryLimitModificationType.REPLACE) { - temporaryLimitsReports.add(ReportNode.newRootReportNode() - .withAllResourceBundlesFromClasspath() - .withMessageTemplate("network.modification.temporaryLimitAdded.name") - .withUntypedValue(NAME, limit.getName()) - .withUntypedValue(DURATION, limitDurationToReport) - .withUntypedValue(VALUE, limitValueToReport) - .withSeverity(TypedValue.INFO_SEVERITY) - .build()); - - } else if (limitToModify != null) { - if (limit.getModificationType() == TemporaryLimitModificationType.DELETE) { - temporaryLimitsReports.add(ReportNode.newRootReportNode() - .withAllResourceBundlesFromClasspath() - .withMessageTemplate("network.modification.temporaryLimitDeleted.name") - .withUntypedValue(NAME, limit.getName()) - .withUntypedValue(DURATION, limitDurationToReport) - .withSeverity(TypedValue.INFO_SEVERITY) - .build()); - continue; - } else if (Double.compare(limitToModify.getValue(), limitValue) != 0 && limit.getModificationType() != null) { - temporaryLimitsReports.add(ReportNode.newRootReportNode() - .withAllResourceBundlesFromClasspath() - .withMessageTemplate("network.modification.temporaryLimitModified.name") - .withUntypedValue(NAME, limit.getName()) - .withUntypedValue(DURATION, limitDurationToReport) - .withUntypedValue(VALUE, limitValueToReport) - .withUntypedValue("oldValue", - limitToModify.getValue() == Double.MAX_VALUE ? "no value" - : String.valueOf(limitToModify.getValue())) - .withSeverity(TypedValue.INFO_SEVERITY) - .build()); - } else { - limitValue = limitToModify.getValue(); - } - } else if (limit.getModificationType() == TemporaryLimitModificationType.MODIFY) { - temporaryLimitsReports.add(ReportNode.newRootReportNode() - .withAllResourceBundlesFromClasspath() - .withMessageTemplate("network.modification.temporaryLimitsNoMatch") - .withUntypedValue(LIMIT_ACCEPTABLE_DURATION, limitAcceptableDuration) - .withSeverity(TypedValue.WARN_SEVERITY) - .build()); - continue; - } else { - continue; - } - limitsAdder - .beginTemporaryLimit() - .setName(limit.getName()) - .setValue(limitValue) - .setAcceptableDuration(limitAcceptableDuration) - .endTemporaryLimit(); + applyTemporaryLimitModification( + operationalLimitsGroupModificationInfos, + limitsAdder, + currentLimits, + limit, + branchTemporaryLimits, + temporaryLimitsReports + ); } } // we add the temporary limits comming from previous modifications if (!branchTemporaryLimits.isEmpty()) { for (LoadingLimits.TemporaryLimit limit : branchTemporaryLimits) { - limitsAdder - .beginTemporaryLimit() - .setName(limit.getName()) - .setValue(limit.getValue()) - .setAcceptableDuration(limit.getAcceptableDuration()) - .endTemporaryLimit(); + addTemporaryLimit(limitsAdder, limit.getName(), limit.getValue(), limit.getAcceptableDuration()); } } if (!temporaryLimitsReports.isEmpty()) { - temporaryLimitsReports.add(0, ReportNode.newRootReportNode() + temporaryLimitsReports.addFirst(ReportNode.newRootReportNode() .withAllResourceBundlesFromClasspath() .withMessageTemplate("network.modification.temporaryLimitsModification") .withSeverity(TypedValue.INFO_SEVERITY) @@ -503,6 +433,139 @@ protected void modifyTemporaryLimits(OperationalLimitsGroupModificationInfos ope } } + private static boolean mayCreateALimit(TemporaryLimitModificationType modificationType) { + return modificationType == TemporaryLimitModificationType.ADD + || modificationType == TemporaryLimitModificationType.REPLACE + || modificationType == TemporaryLimitModificationType.MODIFY_OR_ADD; + } + + private void applyTemporaryLimitModification( + OperationalLimitsGroupModificationInfos operationalLimitsGroupModificationInfos, + CurrentLimitsAdder limitsAdder, + CurrentLimits networkCurrentLimits, + CurrentTemporaryLimitModificationInfos limit, + List branchTemporaryLimits, + List temporaryLimitsReports) { + CurrentLimitsModificationInfos currentLimitsInfos = operationalLimitsGroupModificationInfos.getCurrentLimits(); + int limitAcceptableDuration = limit.getAcceptableDuration() == null ? Integer.MAX_VALUE : limit.getAcceptableDuration(); + double limitValue = limit.getValue() == null ? Double.MAX_VALUE : limit.getValue(); + String limitDurationToReport = limitAcceptableDuration == Integer.MAX_VALUE ? " " : String.valueOf(limitAcceptableDuration); + String limitValueToReport = limitValue == Double.MAX_VALUE ? "no value" : String.valueOf(limitValue); + LoadingLimits.TemporaryLimit limitToModify = null; + if (networkCurrentLimits != null) { + limitToModify = getTemporaryLimitToModify(networkCurrentLimits, limit, currentLimitsInfos, operationalLimitsGroupModificationInfos.getTemporaryLimitsModificationType()); + // we remove the limit to modify from the list of temporary limits so we can get the list of temporary limits coming from previous modifications + branchTemporaryLimits.removeIf(temporaryLimit -> temporaryLimit.getAcceptableDuration() == limitAcceptableDuration); + } + if (limitToModify == null && mayCreateALimit(limit.getModificationType())) { + createTemporaryLimit(limitsAdder, limit, temporaryLimitsReports, limitDurationToReport, limitValueToReport, limitValue, limitAcceptableDuration); + } else if (limitToModify != null) { + // the limit already exists + if (limit.getModificationType() == TemporaryLimitModificationType.DELETE) { + // the limit has been removed previously + temporaryLimitsReports.add(ReportNode.newRootReportNode() + .withAllResourceBundlesFromClasspath() + .withMessageTemplate("network.modification.temporaryLimitDeleted.name") + .withUntypedValue(NAME, limit.getName()) + .withUntypedValue(DURATION, limitDurationToReport) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + } else { + modifyTemporaryLimit(limitsAdder, limit, temporaryLimitsReports, limitToModify, limitValue, limitDurationToReport, limitValueToReport, limitAcceptableDuration); + } + } else if (limit.getModificationType() == TemporaryLimitModificationType.MODIFY || limit.getModificationType() == TemporaryLimitModificationType.MODIFY_OR_ADD) { + // invalid modification + temporaryLimitsReports.add(ReportNode.newRootReportNode() + .withAllResourceBundlesFromClasspath() + .withMessageTemplate("network.modification.temporaryLimitsNoMatch") + .withUntypedValue(LIMIT_ACCEPTABLE_DURATION, limitAcceptableDuration) + .withSeverity(TypedValue.WARN_SEVERITY) + .build()); + } + } + + private static void modifyTemporaryLimit( + CurrentLimitsAdder limitsAdder, + CurrentTemporaryLimitModificationInfos limitModificationInfos, + List temporaryLimitsReports, + LoadingLimits.TemporaryLimit limitToModify, + double limitValue, + String limitDurationToReport, + String limitValueToReport, + int limitAcceptableDuration) { + if (Double.compare(limitToModify.getValue(), limitValue) != 0 && limitModificationInfos.getModificationType() != null) { + temporaryLimitsReports.add(ReportNode.newRootReportNode() + .withAllResourceBundlesFromClasspath() + .withMessageTemplate("network.modification.temporaryLimitModified.name") + .withUntypedValue(NAME, limitModificationInfos.getName()) + .withUntypedValue(DURATION, limitDurationToReport) + .withUntypedValue(VALUE, limitValueToReport) + .withUntypedValue("oldValue", + limitToModify.getValue() == Double.MAX_VALUE ? "no value" + : String.valueOf(limitToModify.getValue())) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + addTemporaryLimit(limitsAdder, limitModificationInfos.getName(), limitValue, limitAcceptableDuration); + } else { + // no real modification + addTemporaryLimit(limitsAdder, limitModificationInfos.getName(), limitToModify.getValue(), limitAcceptableDuration); + } + } + + private static void createTemporaryLimit( + CurrentLimitsAdder limitsAdder, + CurrentTemporaryLimitModificationInfos limit, + List temporaryLimitsReports, + String limitDurationToReport, + String limitValueToReport, + double limitValue, + int limitAcceptableDuration) { + temporaryLimitsReports.add(ReportNode.newRootReportNode() + .withAllResourceBundlesFromClasspath() + .withMessageTemplate("network.modification.temporaryLimitAdded.name") + .withUntypedValue(NAME, limit.getName()) + .withUntypedValue(DURATION, limitDurationToReport) + .withUntypedValue(VALUE, limitValueToReport) + .withSeverity(TypedValue.INFO_SEVERITY) + .build()); + addTemporaryLimit(limitsAdder, limit.getName(), limitValue, limitAcceptableDuration); + } + + private static void addTemporaryLimit(CurrentLimitsAdder limitsAdder, String limit, double limitValue, int limitAcceptableDuration) { + limitsAdder + .beginTemporaryLimit() + .setName(limit) + .setValue(limitValue) + .setAcceptableDuration(limitAcceptableDuration) + .endTemporaryLimit(); + } + + private LoadingLimits.TemporaryLimit getTemporaryLimitToModify( + CurrentLimits networkCurrentLimits, + CurrentTemporaryLimitModificationInfos limit, + CurrentLimitsModificationInfos currentLimitsInfos, + TemporaryLimitModificationType temporaryLimitsModificationType) { + int limitAcceptableDuration = limit.getAcceptableDuration() == null ? Integer.MAX_VALUE : limit.getAcceptableDuration(); + LoadingLimits.TemporaryLimit limitToModify; + limitToModify = networkCurrentLimits.getTemporaryLimit(limitAcceptableDuration); + if (limitToModify != null && !limitToModify.getName().equals(limit.getName())) { + boolean isThisLimitDeleted = isThisLimitDeleted(currentLimitsInfos.getTemporaryLimits(), limitAcceptableDuration); + if (isThisLimitDeleted) { + limitToModify = null; + } else if (TemporaryLimitModificationType.ADD.equals(limit.getModificationType())) { + throw new PowsyblException("2 temporary limits have the same duration " + limitAcceptableDuration); + } + } + + //Additional check for limit sets tabular modifications + if (TemporaryLimitModificationType.ADD.equals(temporaryLimitsModificationType)) { + networkCurrentLimits.getTemporaryLimits().stream().filter(temporaryLimit -> temporaryLimit.getName().equals(limit.getName())).findFirst().ifPresent(temporaryLimit -> { + throw new PowsyblException("2 temporary limits have the same name " + limit.getName()); + }); + } + return limitToModify; + } + protected boolean characteristicsModified(BranchModificationInfos branchModificationInfos) { return branchModificationInfos.getX() != null && branchModificationInfos.getX().getValue() != null diff --git a/src/test/java/org/gridsuite/modification/modifications/tabularmodifications/LimitSetModificationsTest.java b/src/test/java/org/gridsuite/modification/modifications/tabularmodifications/LimitSetModificationsTest.java index cfae1abc..efafe6ee 100644 --- a/src/test/java/org/gridsuite/modification/modifications/tabularmodifications/LimitSetModificationsTest.java +++ b/src/test/java/org/gridsuite/modification/modifications/tabularmodifications/LimitSetModificationsTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Test; import java.util.List; +import java.util.Objects; import java.util.UUID; import static org.gridsuite.modification.utils.TestUtils.assertLogMessageWithoutRank; @@ -103,12 +104,20 @@ protected ModificationInfos buildModification() { .temporaryLimitsModificationType(TemporaryLimitModificationType.MODIFY) .currentLimits(CurrentLimitsModificationInfos.builder() .temporaryLimits(List.of( + // throws a warning CurrentTemporaryLimitModificationInfos.builder() .modificationType(TemporaryLimitModificationType.MODIFY) .name("test1") .acceptableDuration(3) .value(10.) .build(), + // valid modification + CurrentTemporaryLimitModificationInfos.builder() + .modificationType(TemporaryLimitModificationType.MODIFY) + .name("test1") + .acceptableDuration(2) + .value(50.) + .build(), CurrentTemporaryLimitModificationInfos.builder() .modificationType(TemporaryLimitModificationType.ADD) .name("test2_plus") @@ -200,24 +209,27 @@ public void testApply() { @Override protected void assertAfterNetworkModificationApplication() { Line line1 = getNetwork().getLine("line1"); - CurrentLimits line1CurrentLimits = getNetwork().getLine("line1").getOperationalLimitsGroup1("DEFAULT").orElse(null).getCurrentLimits().orElse(null); + CurrentLimits line1CurrentLimits = Objects.requireNonNull(getNetwork().getLine("line1").getOperationalLimitsGroup1("DEFAULT").orElse(null)).getCurrentLimits().orElse(null); + Assertions.assertNotNull(line1CurrentLimits); assertEquals(2, line1CurrentLimits.getTemporaryLimits().size()); assertEquals("test2_plus", line1CurrentLimits.getTemporaryLimit(1).getName()); assertEquals("test1", line1CurrentLimits.getTemporaryLimit(2).getName()); + assertEquals(50, line1CurrentLimits.getTemporaryLimit(2).getValue()); assertNull(line1.getSelectedOperationalLimitsGroupId1().orElse(null)); Line line2 = getNetwork().getLine("line2"); - CurrentLimits line2DefaultCurrentLimitsSide1 = line2.getOperationalLimitsGroup1("DEFAULT").orElse(null).getCurrentLimits().orElse(null); + CurrentLimits line2DefaultCurrentLimitsSide1 = Objects.requireNonNull(line2.getOperationalLimitsGroup1("DEFAULT").orElse(null)).getCurrentLimits().orElse(null); Assertions.assertNotNull(line2DefaultCurrentLimitsSide1); assertEquals(1, line2DefaultCurrentLimitsSide1.getTemporaryLimits().size()); assertEquals("test1", line2DefaultCurrentLimitsSide1.getTemporaryLimit(1).getName()); assertEquals("DEFAULT", line2.getSelectedOperationalLimitsGroupId1().orElse(null)); assertEquals("group0", line2.getSelectedOperationalLimitsGroupId2().orElse(null)); - CurrentLimits line2Group0CurrentLimitsSide2 = line2.getOperationalLimitsGroup2("group0").orElse(null).getCurrentLimits().orElse(null); + CurrentLimits line2Group0CurrentLimitsSide2 = Objects.requireNonNull(line2.getOperationalLimitsGroup2("group0").orElse(null)).getCurrentLimits().orElse(null); Assertions.assertNotNull(line2Group0CurrentLimitsSide2); assertEquals(100, line2Group0CurrentLimitsSide2.getPermanentLimit()); - CurrentLimits line2CurrentLimitsSide2 = line2.getOperationalLimitsGroup2("group0").orElse(null).getCurrentLimits().orElse(null); + CurrentLimits line2CurrentLimitsSide2 = Objects.requireNonNull(line2.getOperationalLimitsGroup2("group0").orElse(null)).getCurrentLimits().orElse(null); + Assertions.assertNotNull(line2CurrentLimitsSide2); assertEquals(1, line2CurrentLimitsSide2.getTemporaryLimits().size()); assertEquals("test1", line2CurrentLimitsSide2.getTemporaryLimit(1).getName()); assertEquals("group0", line2.getSelectedOperationalLimitsGroupId2().orElse(null)); @@ -237,5 +249,6 @@ private void assertAfterNetworkModificationApplication(ReportNode reportNode) { @Override protected void checkModification() { + // abstract method that has to be implemented even if there is nothing to check } }