diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractOperationalLimitsGroupsTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractOperationalLimitsGroupsTest.java index cf1e495b09d..cfde13d97f1 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractOperationalLimitsGroupsTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractOperationalLimitsGroupsTest.java @@ -831,8 +831,8 @@ private static Stream provideUtilCheckTemporaryLimitsArguments() { ThreeWindingsTransformer transformer = network3wt.getThreeWindingsTransformer(EurostagTutorialExample1Factory.NGEN_V2_NHV1); return Stream.of( - Arguments.of(l, ThreeSides.ONE, List.of(1., 0.95), LimitType.CURRENT, 299, List.of()), // below any permanent limit - Arguments.of(l, ThreeSides.ONE, List.of(1., 0.8), LimitType.CURRENT, 310, List.of( + Arguments.of(l, ThreeSides.ONE, List.of(1., 0.95, 1.12), LimitType.CURRENT, 299, List.of()), // below any permanent limit + Arguments.of(l, ThreeSides.ONE, List.of(1., 0.8, 1.37), LimitType.CURRENT, 310, List.of( new ExpectedOverload( LimitViolationUtils.PERMANENT_LIMIT_NAME, EurostagTutorialExample1Factory.ACTIVATED_ONE_TWO, @@ -840,7 +840,7 @@ private static Stream provideUtilCheckTemporaryLimitsArguments() { 60 * 40 ) )), // above permanent of activated_1_2 - Arguments.of(l, ThreeSides.ONE, List.of(1., 0.82), LimitType.CURRENT, 510, List.of( + Arguments.of(l, ThreeSides.ONE, List.of(1., 0.82, 1.23), LimitType.CURRENT, 510, List.of( new ExpectedOverload( LimitViolationUtils.PERMANENT_LIMIT_NAME, EurostagTutorialExample1Factory.ACTIVATED_ONE_TWO, @@ -848,7 +848,7 @@ private static Stream provideUtilCheckTemporaryLimitsArguments() { 60 * 40 ) )), // above permanent of Default, but no temporary above. `checkAllTemporaryLimits` doesn't detect anything in this case. It is the responsibility of `checkPermanentLimit` - Arguments.of(l, ThreeSides.ONE, List.of(1., 0.7), LimitType.CURRENT, 701, List.of( + Arguments.of(l, ThreeSides.ONE, List.of(1., 0.7, 1.5), LimitType.CURRENT, 701, List.of( new ExpectedOverload( "40'", EurostagTutorialExample1Factory.ACTIVATED_ONE_TWO, @@ -856,7 +856,7 @@ private static Stream provideUtilCheckTemporaryLimitsArguments() { 30 ) )), // above first temporary of 1_2 - Arguments.of(l, ThreeSides.ONE, List.of(1., 0.92), LimitType.CURRENT, 1122, List.of( + Arguments.of(l, ThreeSides.ONE, List.of(1., 0.92, 2.), LimitType.CURRENT, 1122, List.of( new ExpectedOverload( "40'", EurostagTutorialExample1Factory.ACTIVATED_ONE_TWO, @@ -870,7 +870,7 @@ private static Stream provideUtilCheckTemporaryLimitsArguments() { 60 * 10 ) )), // above permanent of 1_1 - Arguments.of(l, ThreeSides.ONE, List.of(1., 0.87), LimitType.CURRENT, 1450, List.of( + Arguments.of(l, ThreeSides.ONE, List.of(1., 0.87, 1.43), LimitType.CURRENT, 1450, List.of( new ExpectedOverload( "40'", EurostagTutorialExample1Factory.ACTIVATED_ONE_TWO, @@ -884,7 +884,7 @@ private static Stream provideUtilCheckTemporaryLimitsArguments() { 60 ) )), // above first temporary of 1_1 - Arguments.of(l, ThreeSides.ONE, List.of(1., 0.3), LimitType.CURRENT, 1500, List.of( + Arguments.of(l, ThreeSides.ONE, List.of(1., 0.3, 3.1), LimitType.CURRENT, 1500, List.of( new ExpectedOverload( "40'", EurostagTutorialExample1Factory.ACTIVATED_ONE_TWO, @@ -898,7 +898,7 @@ private static Stream provideUtilCheckTemporaryLimitsArguments() { 0 ) )), // above last temporary of 1_1 - Arguments.of(l, ThreeSides.ONE, List.of(1., 0.84), LimitType.CURRENT, 1601, List.of( + Arguments.of(l, ThreeSides.ONE, List.of(1., 0.84, 1.75), LimitType.CURRENT, 1601, List.of( new ExpectedOverload( "0.5'", EurostagTutorialExample1Factory.ACTIVATED_ONE_TWO, @@ -912,9 +912,9 @@ private static Stream provideUtilCheckTemporaryLimitsArguments() { 0 ) )), // above last temporary of 1_2 - Arguments.of(transformer, ThreeSides.THREE, List.of(1., 0.99), LimitType.ACTIVE_POWER, 200, List.of()), //under all limits - Arguments.of(transformer, ThreeSides.THREE, List.of(1., 0.96), LimitType.ACTIVE_POWER, 275, List.of()), // above permanent of Default, but no temporary above. `checkAllTemporaryLimits` doesn't detect anything in this case. It is the responsibility of `checkPermanentLimit` - Arguments.of(transformer, ThreeSides.THREE, List.of(1., 0.88), LimitType.ACTIVE_POWER, 375, List.of( + Arguments.of(transformer, ThreeSides.THREE, List.of(1., 0.99, 1.01), LimitType.ACTIVE_POWER, 200, List.of()), //under all limits + Arguments.of(transformer, ThreeSides.THREE, List.of(1., 0.96, 1.87), LimitType.ACTIVE_POWER, 275, List.of()), // above permanent of Default, but no temporary above. `checkAllTemporaryLimits` doesn't detect anything in this case. It is the responsibility of `checkPermanentLimit` + Arguments.of(transformer, ThreeSides.THREE, List.of(1., 0.88, 1.64), LimitType.ACTIVE_POWER, 375, List.of( new ExpectedOverload( LimitViolationUtils.PERMANENT_LIMIT_NAME, EurostagTutorialExample1Factory.ACTIVATED_THREE_ONE, @@ -922,7 +922,7 @@ private static Stream provideUtilCheckTemporaryLimitsArguments() { 45 * 60 ) )), // above permanent of activated_3_1 - Arguments.of(transformer, ThreeSides.THREE, List.of(1., 0.77), LimitType.ACTIVE_POWER, 405, List.of( + Arguments.of(transformer, ThreeSides.THREE, List.of(1., 0.77, 1.67), LimitType.ACTIVE_POWER, 405, List.of( new ExpectedOverload( "45'", EurostagTutorialExample1Factory.ACTIVATED_THREE_ONE, diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/LimitReduction.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/LimitReduction.java index 05c1c4c5e86..5f26de5c163 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/LimitReduction.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/LimitReduction.java @@ -231,8 +231,8 @@ private LimitReduction(LimitType limitType, double value, boolean monitoringOnly } else { throw new PowsyblException(limitType + " is not a supported limit type for limit reduction"); } - if (value > 1. || value < 0.) { - throw new PowsyblException("Limit reduction value should be in [0;1]"); + if (value < 0.) { + throw new PowsyblException("Limit reduction value should be equal or greater than 0"); } this.value = value; this.monitoringOnly = monitoringOnly; diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/LimitReductionList.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/LimitReductionList.java index 119212e1c78..5bccc8d9fd8 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/LimitReductionList.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/LimitReductionList.java @@ -19,7 +19,8 @@ */ public class LimitReductionList { - public static final String VERSION = "1.1"; + // v1.2 introduces limit increase (reduction with value above 1) + public static final String VERSION = "1.2"; private final List limitReductions; public LimitReductionList(List limitReductions) { diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationDetectionTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationDetectionTest.java index 7dfc444e581..dd3753e2848 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationDetectionTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationDetectionTest.java @@ -125,6 +125,67 @@ void testVoltageViolationDetection() { }); } + @Test + void testSingleTempLimitIncrease() { + Network network = EurostagTutorialExample1Factory.createWithMultipleSelectedFixedCurrentLimits(); + Line line = network.getLine(EurostagTutorialExample1Factory.NHV1_NHV2_1); + DefaultLimitReductionsApplier applier = new DefaultLimitReductionsApplier( + List.of(LimitReduction.builder(LimitType.CURRENT, 1.1) + .withOperationalLimitsGroupIdSelection(List.of(EurostagTutorialExample1Factory.ACTIVATED_ONE_ONE)) + .withLimitDurationCriteria(new EqualityTemporaryDurationCriterion(600)) + .build() + ) + ); + line.setSelectedOperationalLimitsGroup1(EurostagTutorialExample1Factory.ACTIVATED_ONE_ONE); + LimitViolationDetection.checkLimitViolation( + line, TwoSides.ONE, 1300., LimitType.CURRENT, Set.of(LoadingLimitType.TATL, LoadingLimitType.PATL), applier, violationsCollector::add + ); + assertEquals(1, violationsCollector.size()); + LimitViolation violation = violationsCollector.getFirst(); + // the limit at 1200 is raised to 1320 + assertEquals(LimitViolationUtils.PERMANENT_LIMIT_NAME, violation.getLimitName()); + assertEquals(600, violation.getAcceptableDuration()); + assertEquals(1100, violation.getLimit()); + assertEquals(1, violation.getLimitReduction()); + } + + @Test + void testTempLimitIntervertedWithIncrease() { + Network network = EurostagTutorialExample1Factory.createWithMultipleSelectedFixedCurrentLimits(); + Line line = network.getLine(EurostagTutorialExample1Factory.NHV1_NHV2_1); + DefaultLimitReductionsApplier applier = new DefaultLimitReductionsApplier( + List.of(LimitReduction.builder(LimitType.CURRENT, 1.3) + .withOperationalLimitsGroupIdSelection(List.of(EurostagTutorialExample1Factory.ACTIVATED_ONE_ONE)) + .withLimitDurationCriteria(new EqualityTemporaryDurationCriterion(600)) + .build() + ) + ); + line.setSelectedOperationalLimitsGroup1(EurostagTutorialExample1Factory.ACTIVATED_ONE_ONE); + //Check detection between the original at 1200 (now raised to 1560) and the IT1 at 1500 + LimitViolationDetection.checkLimitViolation( + line, TwoSides.ONE, 1300., LimitType.CURRENT, Set.of(LoadingLimitType.TATL, LoadingLimitType.PATL), applier, violationsCollector::add + ); + assertEquals(1, violationsCollector.size()); + LimitViolation violation = violationsCollector.getFirst(); + // the limit at 1200 is raised to 1560 above the limit at 1500 + assertEquals(LimitViolationUtils.PERMANENT_LIMIT_NAME, violation.getLimitName()); + assertEquals(60, violation.getAcceptableDuration()); + assertEquals(1100, violation.getLimit()); + assertEquals(1, violation.getLimitReduction()); + + violationsCollector.clear(); + //Check above both limits + LimitViolationDetection.checkLimitViolation( + line, TwoSides.ONE, 1600., LimitType.CURRENT, Set.of(LoadingLimitType.TATL, LoadingLimitType.PATL), applier, violationsCollector::add + ); + assertEquals(1, violationsCollector.size()); + violation = violationsCollector.getFirst(); + assertEquals("1'", violation.getLimitName()); + assertEquals(0, violation.getAcceptableDuration()); + assertEquals(1500, violation.getLimit()); + assertEquals(1, violation.getLimitReduction()); + } + @Test void testVoltageViolationDetectionWithDetailLimitViolationId() { Network network = EurostagTutorialExample1Factory.createWithFixedCurrentLimits(); diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/limitreduction/LimitReductionModuleTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/limitreduction/LimitReductionModuleTest.java index faf7f746e6f..65151914303 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/limitreduction/LimitReductionModuleTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/limitreduction/LimitReductionModuleTest.java @@ -50,6 +50,21 @@ void limitReductionReadV10() { compareLimitReductionList(expectedReductions, limitReductionList); } + @Test + void limitReductionReadV11() { + LimitReductionList limitReductionList = LimitReductionListSerDeUtil.read(getClass().getResourceAsStream("/LimitReductionsV1.1.json")); + LimitReductionList expectedReductions = new LimitReductionList( + List.of( + getLimitReduction1(), + getLimitReduction2(), + getLimitReduction3(), + getLimitReduction4(), + getLimitReduction5() + ) + ); + compareLimitReductionList(expectedReductions, limitReductionList); + } + private void compareLimitReductionList(LimitReductionList expected, LimitReductionList actual) { Assertions.assertThat(actual.getLimitReductions()) .hasSize(expected.getLimitReductions().size()) @@ -79,11 +94,19 @@ private void compareLimitReductionList(LimitReductionList expected, LimitReducti @Test void roundTripTest() throws IOException { - LimitReductionList limitReductionList = new LimitReductionList(List.of(getLimitReduction1(), getLimitReduction2(), getLimitReduction3(), getLimitReduction4(), getLimitReduction5())); + LimitReductionList limitReductionList = new LimitReductionList( + List.of( + getLimitReduction1(), + getLimitReduction2(), + getLimitReduction3(), + getLimitReduction4(), + getLimitReduction5(), + getLimitReduction6() + )); roundTripTest(limitReductionList, LimitReductionListSerDeUtil::write, LimitReductionListSerDeUtil::read, - "/LimitReductionsV1.1.json"); + "/LimitReductionsV1.2.json"); } @Test @@ -91,7 +114,7 @@ void compatibilityWithOldCriterion() throws IOException { LimitReductionList reductionList = LimitReductionListSerDeUtil.read(getClass().getResourceAsStream("/LimitReductionsV1.0.json")); try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { LimitReductionListSerDeUtil.write(reductionList, bos); - ComparisonUtils.assertTxtEquals(getClass().getResourceAsStream("/LimitReductions_no_limits_groupV1.1.json"), new ByteArrayInputStream(bos.toByteArray())); + ComparisonUtils.assertTxtEquals(getClass().getResourceAsStream("/LimitReductions_no_limits_groupV1.2.json"), new ByteArrayInputStream(bos.toByteArray())); } catch (Exception e) { // Should not happen fail(); @@ -151,4 +174,10 @@ private LimitReduction getLimitReduction5() { .withOperationalLimitsGroupIdSelection("DEFAULT", "activated_1_3", "activated_2_1") .build(); } + + private LimitReduction getLimitReduction6() { + return LimitReduction.builder(LimitType.CURRENT, 1.13) + .withLimitDurationCriteria(IntervalTemporaryDurationCriterion.builder().setLowBound(60, true).build()) + .build(); + } } diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/limitreduction/LimitReductionListTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/limitreduction/LimitReductionListTest.java index 7dc3e130b6b..287363223d1 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/limitreduction/LimitReductionListTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/limitreduction/LimitReductionListTest.java @@ -126,11 +126,8 @@ void unsupportedLimitType() { @Test void unsupportedLimitReductionValues() { - String expectedMessage = "Limit reduction value should be in [0;1]"; + String expectedMessage = "Limit reduction value should be equal or greater than 0"; Exception e = assertThrows(PowsyblException.class, () -> new LimitReduction(LimitType.CURRENT, -0.5, true)); assertEquals(expectedMessage, e.getMessage()); - - e = assertThrows(PowsyblException.class, () -> new LimitReduction(LimitType.CURRENT, 1.3)); - assertEquals(expectedMessage, e.getMessage()); } } diff --git a/security-analysis/security-analysis-api/src/test/resources/LimitReductionsV1.2.json b/security-analysis/security-analysis-api/src/test/resources/LimitReductionsV1.2.json new file mode 100644 index 00000000000..a2e7b8b24e8 --- /dev/null +++ b/security-analysis/security-analysis-api/src/test/resources/LimitReductionsV1.2.json @@ -0,0 +1,164 @@ +{ + "version" : "1.2", + "limitReductions" : [ { + "value" : 0.9, + "limitType" : "CURRENT", + "monitoringOnly" : false, + "contingencyContext" : { + "contextType" : "SPECIFIC", + "contingencyId" : "contingency1" + }, + "equipmentCriteria" : [ { + "type" : "identifierCriterion", + "version" : "1.1", + "identifiers" : [ "NHV1_NHV2_1" ] + }, { + "type" : "lineCriterion", + "version" : "1.1", + "countryCriterion" : { + "type" : "TWO_COUNTRY", + "countries1" : [ "FR" ], + "countries2" : [ ] + }, + "nominalVoltageCriterion" : { + "type" : "TWO_NOMINAL_VOLTAGE", + "voltageInterval1" : { + "nominalVoltageLowBound" : 350.0, + "lowClosed" : true, + "nominalVoltageHighBound" : 410.0, + "highClosed" : false + } + } + }, { + "type" : "twoWindingsTransformerCriterion", + "version" : "1.1", + "countryCriterion" : { + "type" : "SINGLE_COUNTRY", + "countries" : [ "FR", "BE" ] + } + }, { + "type" : "threeWindingsTransformerCriterion", + "version" : "1.1", + "countryCriterion" : { + "type" : "SINGLE_COUNTRY", + "countries" : [ "FR", "BE" ] + } + }, { + "type" : "tieLineCriterion", + "version" : "1.1", + "nominalVoltageCriterion" : { + "type" : "TWO_NOMINAL_VOLTAGE", + "voltageInterval1" : { + "nominalVoltageLowBound" : 350.0, + "lowClosed" : true, + "nominalVoltageHighBound" : 410.0, + "highClosed" : false + } + } + }, { + "type" : "boundaryLineCriterion", + "version" : "1.1", + "nominalVoltageCriterion" : { + "type" : "SINGLE_NOMINAL_VOLTAGE", + "voltageInterval" : { + "nominalVoltageLowBound" : 80.0, + "lowClosed" : true, + "nominalVoltageHighBound" : 100.0, + "highClosed" : false + } + } + } ], + "durationCriteria" : [ { + "type" : "PERMANENT", + "version" : "1.0" + }, { + "type" : "TEMPORARY_ALL", + "version" : "1.0" + } ] + }, { + "value" : 0.5, + "limitType" : "APPARENT_POWER", + "monitoringOnly" : false, + "contingencyContext" : { + "contextType" : "ALL" + }, + "equipmentCriteria" : [ { + "type" : "identifierCriterion", + "version" : "1.1", + "identifiers" : [ "NHV1_NHV2_2" ] + } ], + "durationCriteria" : [ { + "type" : "TEMPORARY_INTERVAL", + "version" : "1.0", + "lowBound" : 600, + "lowClosed" : true, + "highBound" : 1200, + "highClosed" : true + } ] + }, { + "value" : 0.8, + "limitType" : "ACTIVE_POWER", + "monitoringOnly" : true, + "contingencyContext" : { + "contextType" : "ALL" + } + }, { + "value" : 0.9, + "limitType" : "CURRENT", + "monitoringOnly" : false, + "contingencyContext" : { + "contextType" : "ALL" + }, + "equipmentCriteria" : [ { + "type" : "identifiableCriterion", + "version" : "1.1", + "countryCriterion" : { + "type" : "AT_LEAST_ONE_COUNTRY", + "countries" : [ "FR" ] + }, + "nominalVoltageCriterion" : { + "type" : "AT_LEAST_ONE_NOMINAL_VOLTAGE", + "voltageInterval" : { + "nominalVoltageLowBound" : 380.0, + "lowClosed" : true, + "nominalVoltageHighBound" : 410.0, + "highClosed" : true + } + } + } ], + "durationCriteria" : [ { + "type" : "TEMPORARY_INTERVAL", + "version" : "1.0", + "lowBound" : 300, + "lowClosed" : true, + "highBound" : 600, + "highClosed" : false + } ] + }, { + "value" : 0.88, + "limitType" : "ACTIVE_POWER", + "monitoringOnly" : false, + "contingencyContext" : { + "contextType" : "ALL" + }, + "equipmentCriteria" : [ { + "type" : "identifierCriterion", + "version" : "1.1", + "identifiers" : [ "NHV1_NHV2_1", "NHV1_NHV2_2" ] + } ], + "operationalLimitsGroupIdsSelection" : [ "DEFAULT", "activated_1_3", "activated_2_1" ] + }, { + "value" : 1.13, + "limitType" : "CURRENT", + "monitoringOnly" : false, + "contingencyContext" : { + "contextType" : "ALL" + }, + "durationCriteria" : [ { + "type" : "TEMPORARY_INTERVAL", + "version" : "1.0", + "lowBound" : 60, + "lowClosed" : true + } ] + } ] +} \ No newline at end of file diff --git a/security-analysis/security-analysis-api/src/test/resources/LimitReductions_no_limits_groupV1.1.json b/security-analysis/security-analysis-api/src/test/resources/LimitReductions_no_limits_groupV1.2.json similarity index 99% rename from security-analysis/security-analysis-api/src/test/resources/LimitReductions_no_limits_groupV1.1.json rename to security-analysis/security-analysis-api/src/test/resources/LimitReductions_no_limits_groupV1.2.json index c41c4b18d65..ce94971fda5 100644 --- a/security-analysis/security-analysis-api/src/test/resources/LimitReductions_no_limits_groupV1.1.json +++ b/security-analysis/security-analysis-api/src/test/resources/LimitReductions_no_limits_groupV1.2.json @@ -1,5 +1,5 @@ { - "version" : "1.1", + "version" : "1.2", "limitReductions" : [ { "value" : 0.9, "limitType" : "CURRENT",