Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,30 @@
public class LimitViolationDTO {
private LimitViolationType limitType;
private String limitName;
private String nextLimitName;
private ThreeSides side;
private int acceptableDuration;
private double limit;
private Double patlLimit;
private double limitReduction;
private double value;
private Double loading;
private Double patlLoading;
private String locationId;

public static LimitViolationDTO toDto(AbstractLimitViolationEntity limitViolation) {
return LimitViolationDTO.builder()
.limitType(limitViolation.getLimitType())
.limitName(limitViolation.getLimitName())
.nextLimitName(limitViolation.getNextLimitName())
.side(limitViolation.getSide())
.acceptableDuration((int) limitViolation.getAcceptableDuration())
.limit(limitViolation.getLimit())
.patlLimit(limitViolation.getPatlLimit())
.limitReduction(limitViolation.getLimitReduction())
.value(limitViolation.getValue())
.loading(limitViolation.getLoading())
.patlLoading(limitViolation.getPatlLoading())
.locationId(limitViolation.getLocationId())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
*/
package org.gridsuite.securityanalysis.server.entities;

import com.powsybl.iidm.network.ThreeSides;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.util.LimitViolationUtils;
import com.powsybl.security.LimitViolation;
import com.powsybl.security.LimitViolationType;
import jakarta.persistence.*;
Expand All @@ -16,6 +17,8 @@
import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder;

import java.util.Iterator;
import java.util.Optional;
import java.util.UUID;

/**
Expand All @@ -40,8 +43,12 @@ public abstract class AbstractLimitViolationEntity {
@Column(name = "limitValue")
private double limit;

private Double patlLimit;

private String limitName;

private String nextLimitName;

@Enumerated(EnumType.STRING)
private LimitViolationType limitType;

Expand All @@ -58,12 +65,67 @@ public abstract class AbstractLimitViolationEntity {
@Column(name = "loading")
private Double loading;

private Double patlLoading;

@Column
private String locationId;

public static Double computeLoading(LimitViolation limitViolation) {
return LimitViolationType.CURRENT.equals(limitViolation.getLimitType())
? 100 * limitViolation.getValue() / limitViolation.getLimit()
protected static Double computeLoading(LimitViolation limitViolation, Double limit) {
return LimitViolationType.CURRENT.equals(limitViolation.getLimitType()) && limit != null
? 100 * limitViolation.getValue() / limit
: null;
}

protected static Double getPatlLimit(LimitViolation limitViolation, Network network) {
String equipmentId = limitViolation.getSubjectId();
Branch<?> branch = network.getBranch(equipmentId);
ThreeSides limitViolationSide = limitViolation.getSide();
Copy link
Contributor

@antoinebhs antoinebhs Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: if you're dealing with branches, you could have used TwoSides

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

if (branch == null || limitViolationSide == null) {
return null;
}

Optional<CurrentLimits> currentLimits = limitViolationSide.name().equals(TwoSides.ONE.name()) ? branch.getCurrentLimits1() : branch.getCurrentLimits2();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you use :
default Optional getCurrentLimits(TwoSides side) {
of branch directly? it's a good helper method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

if (currentLimits.isPresent()) {
return currentLimits.get().getPermanentLimit();
}
return null;
}

protected static String getNextLimitName(LimitViolation limitViolation, Network network) {
String equipmentId = limitViolation.getSubjectId();
Branch<?> branch = network.getBranch(equipmentId);
if (branch == null) {
return null;
}
LoadingLimits.TemporaryLimit temporaryLimit = getNextTemporaryLimit(branch, limitViolation);
return temporaryLimit != null ? temporaryLimit.getName() : null;
}

private static LoadingLimits.TemporaryLimit getNextTemporaryLimit(Branch<?> branch, LimitViolation limitViolation) {
// limits are returned from the store by DESC duration / ASC value
ThreeSides limitViolationSide = limitViolation.getSide();
String limitName = limitViolation.getLimitName();
if (limitViolationSide == null || limitName == null) {
return null;
}

Optional<CurrentLimits> currentLimits = limitViolationSide.name().equals(TwoSides.ONE.name()) ? branch.getCurrentLimits1() : branch.getCurrentLimits2();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment to fetch the side

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or compare directly enum instead of names at least?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

if (currentLimits.isEmpty()) {
return null;
}

if (limitName.equals(LimitViolationUtils.PERMANENT_LIMIT_NAME)) {
return currentLimits.get().getTemporaryLimits().stream().findFirst().orElse(null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: temporaryLimits = currentLimits.get().getTemporaryLimits() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}

Iterator<LoadingLimits.TemporaryLimit> temporaryLimitIterator = currentLimits.get().getTemporaryLimits().iterator();
while (temporaryLimitIterator.hasNext()) {
LoadingLimits.TemporaryLimit currentTemporaryLimit = temporaryLimitIterator.next();
if (currentTemporaryLimit.getName().equals(limitViolation.getLimitName())) {
return temporaryLimitIterator.hasNext() ? temporaryLimitIterator.next() : null;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
*/
package org.gridsuite.securityanalysis.server.entities;

import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.*;
import com.powsybl.security.LimitViolation;
import org.gridsuite.computation.utils.ComputationResultUtils;
import jakarta.persistence.*;
import lombok.*;
import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder;



/**
* @author Kevin Le Saulnier <kevin.lesaulnier at rte-france.com>
*/
Expand All @@ -33,17 +31,22 @@ public class ContingencyLimitViolationEntity extends AbstractLimitViolationEntit
private ContingencyEntity contingency;

public static ContingencyLimitViolationEntity toEntity(Network network, LimitViolation limitViolation, SubjectLimitViolationEntity subjectLimitViolation) {
Double patlLimit = getPatlLimit(limitViolation, network);

ContingencyLimitViolationEntity contingencyLimitViolationEntity = ContingencyLimitViolationEntity.builder()
.limit(limitViolation.getLimit())
.limitName(limitViolation.getLimitName())
.limitType(limitViolation.getLimitType())
.acceptableDuration(limitViolation.getAcceptableDuration())
.limitReduction(limitViolation.getLimitReduction())
.value(limitViolation.getValue())
.side(limitViolation.getSide())
.loading(computeLoading(limitViolation))
.loading(computeLoading(limitViolation, limitViolation.getLimit()))
.locationId(ComputationResultUtils.getViolationLocationId(limitViolation, network))
.subjectLimitViolation(subjectLimitViolation)
.patlLimit(patlLimit)
.patlLoading(computeLoading(limitViolation, patlLimit))
.nextLimitName(getNextLimitName(limitViolation, network))
.acceptableDuration(limitViolation.getAcceptableDuration())
.build();

subjectLimitViolation.addContingencyLimitViolation(contingencyLimitViolationEntity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static PreContingencyLimitViolationEntity toEntity(Network network, Limit
.limitReduction(limitViolation.getLimitReduction())
.value(limitViolation.getValue())
.side(limitViolation.getSide())
.loading(computeLoading(limitViolation))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't want the same changes for N results?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

N results will be chaged on other US

.loading(computeLoading(limitViolation, limitViolation.getLimit()))
.locationId(ComputationResultUtils.getViolationLocationId(limitViolation, network))
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="lesaulnierkev (generated)" id="1756904408172-1">
<addColumn tableName="contingency_limit_violation">
<column name="next_limit_name" type="varchar(255)"/>
</addColumn>
</changeSet>
<changeSet author="lesaulnierkev (generated)" id="1756904408172-2">
<addColumn tableName="pre_contingency_limit_violation">
<column name="next_limit_name" type="varchar(255)"/>
</addColumn>
</changeSet>
<changeSet author="lesaulnierkev (generated)" id="1756904408172-3">
<addColumn tableName="contingency_limit_violation">
<column name="patl_limit" type="float(53)"/>
</addColumn>
</changeSet>
<changeSet author="lesaulnierkev (generated)" id="1756904408172-4">
<addColumn tableName="pre_contingency_limit_violation">
<column name="patl_limit" type="float(53)"/>
</addColumn>
</changeSet>
<changeSet author="lesaulnierkev (generated)" id="1756904408172-7">
<addColumn tableName="contingency_limit_violation">
<column name="patl_loading" type="float(53)"/>
</addColumn>
</changeSet>
<changeSet author="lesaulnierkev (generated)" id="1756904408172-8">
<addColumn tableName="pre_contingency_limit_violation">
<column name="patl_loading" type="float(53)"/>
</addColumn>
</changeSet>
</databaseChangeLog>
3 changes: 3 additions & 0 deletions src/main/resources/db/changelog/db.changelog-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ databaseChangeLog:
- include:
file: changesets/changelog_20250827T095600Z.xml
relativeToChangelogFile: true
- include:
file: changesets/changelog_20250903T125946Z.xml
relativeToChangelogFile: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* Copyright (c) 2023, RTE (http://www.rte-france.com)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2025

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

* 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.ContingencyLimitViolationEntity;
import org.gridsuite.securityanalysis.server.entities.SubjectLimitViolationEntity;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* @author Kevin Le Saulnier <kevin.lesaulnier at rte-france.com>
*/
@SpringBootTest
class ContingencyLimitViolationTest {

@Test
void testContingencyLimitViolationEntityNewFields() {
testContingencyLimitViolationMapping("10'", 10 * 60, 1200, 1250, TwoSides.TWO, "1'", 1100);
}

@Test
void testContingencyLimitViolationEntityNewFieldsWithPermanentLimitReached() {
testContingencyLimitViolationMapping(LimitViolationUtils.PERMANENT_LIMIT_NAME, Integer.MAX_VALUE, 1100, 1150, TwoSides.TWO, "10'", 1100);
}

@Test
void testContingencyLimitViolationEntityNewFieldsWithPermanentLimitReachedAndNoTemporaryLimit() {
testContingencyLimitViolationMapping(LimitViolationUtils.PERMANENT_LIMIT_NAME, Integer.MAX_VALUE, 500, 1000, TwoSides.ONE, null, 500);
}

@Test
void testContingencyLimitViolationEntityNewFieldsWithLastLimitReached() {
testContingencyLimitViolationMapping("N/A", 0, 1100, 3000, TwoSides.TWO, null, 1100);
}

private void testContingencyLimitViolationMapping(String limitName, int acceptableDuration, double limit, double value, TwoSides side, String expectedNextLimitName, long expectedPatlLimit) {
Network network = EurostagTutorialExample1Factory.createWithFixedCurrentLimits();
LimitViolation limitViolation = new LimitViolation("NHV1_NHV2_1", "NHV1_NHV2_1_name", LimitViolationType.CURRENT, limitName, acceptableDuration, limit, 1, value, side);

SubjectLimitViolationEntity subjectLimitViolationEntity = new SubjectLimitViolationEntity("NHV1_NHV2_1", "NHV1_NHV2_1_name");

ContingencyLimitViolationEntity contingencyLimitViolationEntity = ContingencyLimitViolationEntity.toEntity(network, limitViolation, subjectLimitViolationEntity);

assertEquals(expectedNextLimitName, contingencyLimitViolationEntity.getNextLimitName());
assertEquals(expectedPatlLimit, contingencyLimitViolationEntity.getPatlLimit());
assertEquals(100 * limitViolation.getValue() / contingencyLimitViolationEntity.getPatlLimit(), contingencyLimitViolationEntity.getPatlLoading());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -231,17 +231,17 @@ private static SubjectLimitViolationDTO toSubjectLimitViolationDTO(LimitViolatio

return new SubjectLimitViolationDTO(
limitViolation.getSubjectId(),
new LimitViolationDTO(
limitViolation.getLimitType(),
limitViolation.getLimitName(),
limitViolation.getSide(),
limitViolation.getAcceptableDuration(),
limitViolation.getLimit(),
limitViolation.getLimitReduction(),
limitViolation.getValue(),
computedLoading,
ComputationResultUtils.getViolationLocationId(limitViolation, getNetwork())
)
LimitViolationDTO.builder()
.limitType(limitViolation.getLimitType())
.limitName(limitViolation.getLimitName())
.side(limitViolation.getSide())
.acceptableDuration(limitViolation.getAcceptableDuration())
.limit(limitViolation.getLimit())
.limitReduction(limitViolation.getLimitReduction())
.value(limitViolation.getValue())
.loading(computedLoading)
.locationId(ComputationResultUtils.getViolationLocationId(limitViolation, getNetwork()))
.build()
);
}

Expand All @@ -258,17 +258,17 @@ private static PreContingencyLimitViolationResultDTO toPreContingencyResultDTO(L
return new PreContingencyLimitViolationResultDTO(
limitViolation.getSubjectId(),
status.name(),
new LimitViolationDTO(
limitViolation.getLimitType(),
limitViolation.getLimitName(),
limitViolation.getSide(),
limitViolation.getAcceptableDuration(),
limitViolation.getLimit(),
limitViolation.getLimitReduction(),
limitViolation.getValue(),
computedLoading,
ComputationResultUtils.getViolationLocationId(limitViolation, getNetwork())
));
LimitViolationDTO.builder()
.limitType(limitViolation.getLimitType())
.limitName(limitViolation.getLimitName())
.side(limitViolation.getSide())
.acceptableDuration(limitViolation.getAcceptableDuration())
.limit(limitViolation.getLimit())
.limitReduction(limitViolation.getLimitReduction())
.value(limitViolation.getValue())
.loading(computedLoading)
.locationId(ComputationResultUtils.getViolationLocationId(limitViolation, getNetwork()))
.build());
}

private static ContingencyResultDTO toContingencyResultDTO(Contingency contingency, String status, List<LimitViolation> limitViolations) {
Expand All @@ -295,17 +295,17 @@ private static ContingencyLimitViolationDTO toContingencyLimitViolationDTO(Conti
status,
contingency.getElements().stream().map(e -> new ContingencyElementDTO(e.getId(), e.getType())).toList()
),
new LimitViolationDTO(
limitViolation.getLimitType(),
limitViolation.getLimitName(),
limitViolation.getSide(),
limitViolation.getAcceptableDuration(),
limitViolation.getLimit(),
limitViolation.getLimitReduction(),
limitViolation.getValue(),
computedLoading,
ComputationResultUtils.getViolationLocationId(limitViolation, getNetwork())
)
LimitViolationDTO.builder()
.limitType(limitViolation.getLimitType())
.limitName(limitViolation.getLimitName())
.side(limitViolation.getSide())
.acceptableDuration(limitViolation.getAcceptableDuration())
.limit(limitViolation.getLimit())
.limitReduction(limitViolation.getLimitReduction())
.value(limitViolation.getValue())
.loading(computedLoading)
.locationId(ComputationResultUtils.getViolationLocationId(limitViolation, getNetwork()))
.build()
);
}

Expand Down