diff --git a/src/main/java/org/gridsuite/securityanalysis/server/entities/PreContingencyLimitViolationEntity.java b/src/main/java/org/gridsuite/securityanalysis/server/entities/PreContingencyLimitViolationEntity.java index 710c6626..01b75e8a 100644 --- a/src/main/java/org/gridsuite/securityanalysis/server/entities/PreContingencyLimitViolationEntity.java +++ b/src/main/java/org/gridsuite/securityanalysis/server/entities/PreContingencyLimitViolationEntity.java @@ -43,17 +43,22 @@ public static List toEntityList(Network netw } public static PreContingencyLimitViolationEntity toEntity(Network network, LimitViolation limitViolation, SubjectLimitViolationEntity subjectLimitViolation) { + Double patlLimit = getPatlLimit(limitViolation, network); return PreContingencyLimitViolationEntity.builder() .subjectLimitViolation(subjectLimitViolation) .limit(limitViolation.getLimit()) .limitName(limitViolation.getLimitName()) .limitType(limitViolation.getLimitType()) - .acceptableDuration(limitViolation.getAcceptableDuration()) + .acceptableDuration(calculateActualOverloadDuration(limitViolation, network)) + .upcomingAcceptableDuration(calculateUpcomingOverloadDuration(limitViolation)) .limitReduction(limitViolation.getLimitReduction()) .value(limitViolation.getValue()) .side(limitViolation.getSide()) + .patlLimit(patlLimit) .loading(computeLoading(limitViolation, limitViolation.getLimit())) + .patlLoading(computeLoading(limitViolation, patlLimit)) .locationId(ComputationResultUtils.getViolationLocationId(limitViolation, network)) + .nextLimitName(getNextLimitName(limitViolation, network)) .build(); } } diff --git a/src/test/java/org/gridsuite/securityanalysis/server/PreContingencyLimitViolationTest.java b/src/test/java/org/gridsuite/securityanalysis/server/PreContingencyLimitViolationTest.java new file mode 100644 index 00000000..17580b5d --- /dev/null +++ b/src/test/java/org/gridsuite/securityanalysis/server/PreContingencyLimitViolationTest.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2025, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.securityanalysis.server; + +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.iidm.network.util.LimitViolationUtils; +import com.powsybl.security.LimitViolation; +import com.powsybl.security.LimitViolationType; +import org.gridsuite.securityanalysis.server.entities.PreContingencyLimitViolationEntity; +import org.gridsuite.securityanalysis.server.entities.SubjectLimitViolationEntity; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import static com.powsybl.iidm.network.test.EurostagTutorialExample1Factory.NGEN_NHV1; +import static com.powsybl.iidm.network.test.EurostagTutorialExample1Factory.NHV1_NHV2_1; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SpringBootTest +class PreContingencyLimitViolationTest { + @Test + void testPreContingencyLimitViolationEntityNewFields() { + testPreContingencyLimitViolationMapping("10'", 10 * 60, 1200, 1, 1250, TwoSides.TWO, "1'", 1100, 10 * 60, null); + } + + @Test + void testPreContingencyLimitViolationEntityNewFieldsWithPermanentLimitReached() { + testPreContingencyLimitViolationMapping(LimitViolationUtils.PERMANENT_LIMIT_NAME, Integer.MAX_VALUE, 1100, 1, 1150, TwoSides.TWO, "10'", 1100, Integer.MAX_VALUE, null); + } + + @Test + void testPreContingencyLimitViolationEntityNewFieldsWithPermanentLimitReachedAndNoTemporaryLimit() { + testPreContingencyLimitViolationMapping(LimitViolationUtils.PERMANENT_LIMIT_NAME, Integer.MAX_VALUE, 500, 1, 1000, TwoSides.ONE, null, 500, Integer.MAX_VALUE, null); + } + + @Test + void testPreContingencyLimitViolationEntityNewFieldsWithLastLimitReached() { + testPreContingencyLimitViolationMapping("N/A", 0, 1100, 1, 3000, TwoSides.TWO, null, 1100, 0, null); + } + + @Test + void testPreContingencyLimitViolationEntityNewFieldsWithLimitReductionEffective() { + // for this test to be relevant, "value" needs to be less that "limit" + testPreContingencyLimitViolationMapping("10'", 60, 1200, 0.8, 1150, TwoSides.TWO, "1'", 1100, 10 * 60, 60); + } + + @Test + void test2wtPreContingencyLimitViolationEntityNewFieldsWithLimitReductionEffective() { + // for this test to be relevant, "value" needs to be less that "limit" + Network network = EurostagTutorialExample1Factory.createWithFixedCurrentLimits(); + // create limit set for two windings transformer + network.getTwoWindingsTransformer(NGEN_NHV1).getOrCreateSelectedOperationalLimitsGroup1().newCurrentLimits() + .setPermanentLimit(100) + .beginTemporaryLimit() + .setName("10'") + .setValue(200) + .setAcceptableDuration(60 * 10) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("1'") + .setValue(300) + .setAcceptableDuration(60) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("N/A") + .setValue(Double.MAX_VALUE) + .setAcceptableDuration(0) + .endTemporaryLimit() + .add(); + + LimitViolation limitViolation = new LimitViolation(NGEN_NHV1, "NGEN_NHV1_name", LimitViolationType.CURRENT, "10'", 60, 200, 0.8, 180, TwoSides.ONE); + + SubjectLimitViolationEntity subjectLimitViolationEntity = new SubjectLimitViolationEntity(NGEN_NHV1, "NGEN_NHV1_name"); + + PreContingencyLimitViolationEntity preContingencyLimitViolationEntity = PreContingencyLimitViolationEntity.toEntity(network, limitViolation, subjectLimitViolationEntity); + + assertEquals("1'", preContingencyLimitViolationEntity.getNextLimitName()); + assertEquals(100, preContingencyLimitViolationEntity.getPatlLimit()); + assertEquals(60 * 10, preContingencyLimitViolationEntity.getAcceptableDuration()); + assertEquals(60, preContingencyLimitViolationEntity.getUpcomingAcceptableDuration()); + assertEquals(100 * limitViolation.getValue() / preContingencyLimitViolationEntity.getPatlLimit(), preContingencyLimitViolationEntity.getPatlLoading()); + } + + private void testPreContingencyLimitViolationMapping(String limitName, int acceptableDuration, double limit, double limitReduction, double value, TwoSides side, String expectedNextLimitName, long expectedPatlLimit, Integer expectedAcceptableDuration, Integer expectedUpcomingAcceptableDuration) { + Network network = EurostagTutorialExample1Factory.createWithFixedCurrentLimits(); + LimitViolation limitViolation = new LimitViolation(NHV1_NHV2_1, "NHV1_NHV2_1_name", LimitViolationType.CURRENT, limitName, acceptableDuration, limit, limitReduction, value, side); + + SubjectLimitViolationEntity subjectLimitViolationEntity = new SubjectLimitViolationEntity(NHV1_NHV2_1, "NHV1_NHV2_1_name"); + + PreContingencyLimitViolationEntity preContingencyLimitViolationEntity = PreContingencyLimitViolationEntity.toEntity(network, limitViolation, subjectLimitViolationEntity); + + assertEquals(expectedNextLimitName, preContingencyLimitViolationEntity.getNextLimitName()); + assertEquals(expectedPatlLimit, preContingencyLimitViolationEntity.getPatlLimit()); + assertEquals(expectedAcceptableDuration, preContingencyLimitViolationEntity.getAcceptableDuration()); + assertEquals(expectedUpcomingAcceptableDuration, preContingencyLimitViolationEntity.getUpcomingAcceptableDuration()); + assertEquals(100 * limitViolation.getValue() / preContingencyLimitViolationEntity.getPatlLimit(), preContingencyLimitViolationEntity.getPatlLoading()); + } +}