Skip to content

Commit e50ffe2

Browse files
authored
Add limit reductions to security analysis (#134)
Signed-off-by: Slimane AMAR <[email protected]>
1 parent 7adbfbe commit e50ffe2

18 files changed

+601
-94
lines changed

src/main/java/org/gridsuite/securityanalysis/server/RestResponseEntityExceptionHandler.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,27 @@
77
package org.gridsuite.securityanalysis.server;
88

99
import org.gridsuite.securityanalysis.server.util.SecurityAnalysisException;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
1012
import org.springframework.http.HttpStatus;
1113
import org.springframework.http.ResponseEntity;
1214
import org.springframework.web.bind.annotation.ControllerAdvice;
1315
import org.springframework.web.bind.annotation.ExceptionHandler;
16+
1417
/**
1518
* @author Kevin Le Saulnier <kevin.lesaulnier at rte-france.com>
1619
*/
1720

1821
@ControllerAdvice
1922
public class RestResponseEntityExceptionHandler {
2023

24+
private static final Logger LOGGER = LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class);
25+
2126
@ExceptionHandler(SecurityAnalysisException.class)
2227
protected ResponseEntity<Object> handleStudyException(SecurityAnalysisException exception) {
28+
if (LOGGER.isErrorEnabled()) {
29+
LOGGER.error(exception.getMessage());
30+
}
2331
switch (exception.getType()) {
2432
case RESULT_NOT_FOUND, PARAMETERS_NOT_FOUND:
2533
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getType());
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright (c) 2024, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
package org.gridsuite.securityanalysis.server.dto;
8+
9+
import lombok.*;
10+
11+
import java.util.List;
12+
13+
@Data
14+
@Builder
15+
@NoArgsConstructor
16+
@AllArgsConstructor
17+
public class LimitReductionsByVoltageLevel {
18+
19+
@Setter
20+
@Getter
21+
@NoArgsConstructor
22+
public static class LimitDuration {
23+
private Integer lowBound;
24+
private boolean lowClosed;
25+
private Integer highBound;
26+
private boolean highClosed;
27+
}
28+
29+
@Setter
30+
@Getter
31+
@NoArgsConstructor
32+
public static class VoltageLevel {
33+
private double nominalV;
34+
private double lowBound;
35+
private double highBound;
36+
}
37+
38+
@Builder
39+
@Setter
40+
@Getter
41+
@NoArgsConstructor
42+
@AllArgsConstructor
43+
public static class LimitReduction {
44+
double reduction;
45+
LimitDuration limitDuration;
46+
}
47+
48+
private VoltageLevel voltageLevel;
49+
private double permanentLimitReduction;
50+
private List<LimitReduction> temporaryLimitReductions;
51+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Copyright (c) 2023, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
package org.gridsuite.securityanalysis.server.dto;
8+
9+
import com.powsybl.security.SecurityAnalysisParameters;
10+
import lombok.Builder;
11+
12+
import java.util.List;
13+
14+
@Builder
15+
public record SecurityAnalysisParametersDTO(
16+
SecurityAnalysisParameters securityAnalysisParameters,
17+
List<List<Double>> limitReductions
18+
) { }

src/main/java/org/gridsuite/securityanalysis/server/dto/SecurityAnalysisParametersValues.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,23 @@
66
*/
77
package org.gridsuite.securityanalysis.server.dto;
88

9+
import com.fasterxml.jackson.annotation.JsonIgnore;
910
import lombok.AllArgsConstructor;
1011
import lombok.Builder;
1112
import lombok.Getter;
1213
import lombok.NoArgsConstructor;
1314
import org.gridsuite.securityanalysis.server.entities.SecurityAnalysisParametersEntity;
1415

16+
import java.util.ArrayList;
17+
import java.util.List;
18+
1519
/**
1620
* @author Abdelsalem Hedhili <abdelsalem.hedhili at rte-france.com>
1721
*/
18-
@Getter
22+
@NoArgsConstructor
1923
@AllArgsConstructor
24+
@Getter
2025
@Builder
21-
@NoArgsConstructor
2226
public class SecurityAnalysisParametersValues {
2327
private String provider;
2428

@@ -32,7 +36,20 @@ public class SecurityAnalysisParametersValues {
3236

3337
private double flowProportionalThreshold;
3438

39+
private List<LimitReductionsByVoltageLevel> limitReductions;
40+
3541
public SecurityAnalysisParametersEntity toEntity() {
3642
return new SecurityAnalysisParametersEntity(this);
3743
}
44+
45+
@JsonIgnore
46+
public List<List<Double>> getLimitReductionsValues() {
47+
// Only for some providers
48+
return limitReductions == null ? null : limitReductions.stream().map(reductionsByVL -> {
49+
List<Double> values = new ArrayList<>(reductionsByVL.getTemporaryLimitReductions().size() + 1);
50+
values.add(reductionsByVL.getPermanentLimitReduction());
51+
reductionsByVL.getTemporaryLimitReductions().forEach(l -> values.add(l.getReduction()));
52+
return values;
53+
}).toList();
54+
}
3855
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Copyright (c) 2023, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
package org.gridsuite.securityanalysis.server.entities;
8+
9+
import jakarta.persistence.*;
10+
import lombok.Getter;
11+
import lombok.NoArgsConstructor;
12+
13+
import java.util.List;
14+
import java.util.UUID;
15+
16+
@Getter
17+
@Entity
18+
@NoArgsConstructor
19+
@Table(name = "limitReductionEntity", indexes = {@Index(name = "idx_security_analysis_parameters_id", columnList = "security_analysis_parameters_id")})
20+
public class LimitReductionEntity {
21+
@Id
22+
@GeneratedValue(strategy = GenerationType.AUTO)
23+
private UUID id;
24+
25+
@ElementCollection(fetch = FetchType.EAGER)
26+
@CollectionTable(
27+
name = "limit_reduction_entity_reductions",
28+
joinColumns = @JoinColumn(name = "limit_reduction_entity_id"),
29+
foreignKey = @ForeignKey(name = "limitReductionEntity_limitReductionEntityReductions_fk")
30+
)
31+
@OrderColumn(name = "index")
32+
@Column(name = "reductions")
33+
private List<Double> reductions;
34+
35+
public LimitReductionEntity(List<Double> reductions) {
36+
this.reductions = reductions;
37+
}
38+
}

src/main/java/org/gridsuite/securityanalysis/server/entities/SecurityAnalysisParametersEntity.java

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
*/
77
package org.gridsuite.securityanalysis.server.entities;
88

9-
import lombok.*;
10-
119
import jakarta.persistence.*;
10+
import lombok.*;
1211
import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisParametersValues;
12+
import org.springframework.lang.Nullable;
1313

14+
import java.util.ArrayList;
15+
import java.util.List;
1416
import java.util.UUID;
17+
import java.util.stream.Collectors;
1518

1619
/**
1720
* @author Abdelsalem HEDHILI <[email protected]>
@@ -26,13 +29,7 @@
2629
public class SecurityAnalysisParametersEntity {
2730

2831
public SecurityAnalysisParametersEntity(SecurityAnalysisParametersValues securityAnalysisParametersValues) {
29-
this(null,
30-
securityAnalysisParametersValues.getProvider(),
31-
securityAnalysisParametersValues.getLowVoltageAbsoluteThreshold(),
32-
securityAnalysisParametersValues.getLowVoltageProportionalThreshold(),
33-
securityAnalysisParametersValues.getHighVoltageAbsoluteThreshold(),
34-
securityAnalysisParametersValues.getHighVoltageProportionalThreshold(),
35-
securityAnalysisParametersValues.getFlowProportionalThreshold());
32+
assignAttributes(securityAnalysisParametersValues);
3633
}
3734

3835
@Id
@@ -58,24 +55,40 @@ public SecurityAnalysisParametersEntity(SecurityAnalysisParametersValues securit
5855
@Column(name = "flowProportionalThreshold")
5956
private double flowProportionalThreshold;
6057

61-
public SecurityAnalysisParametersValues toSecurityAnalysisParametersValues() {
62-
return SecurityAnalysisParametersValues.builder()
63-
.provider(this.provider)
64-
.flowProportionalThreshold(this.flowProportionalThreshold)
65-
.highVoltageAbsoluteThreshold(this.highVoltageAbsoluteThreshold)
66-
.highVoltageProportionalThreshold(this.highVoltageProportionalThreshold)
67-
.lowVoltageAbsoluteThreshold(this.lowVoltageAbsoluteThreshold)
68-
.lowVoltageProportionalThreshold(this.lowVoltageProportionalThreshold)
69-
.build();
58+
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
59+
@JoinColumn(name = "security_analysis_parameters_id", foreignKey = @ForeignKey(name = "securityAnalysisParametersEntity_limitReductions_fk"))
60+
@OrderColumn(name = "index")
61+
private List<LimitReductionEntity> limitReductions;
62+
63+
public List<List<Double>> toLimitReductionsValues() {
64+
return this.limitReductions.stream().map(LimitReductionEntity::getReductions).map(ArrayList::new).collect(Collectors.toList());
7065
}
7166

7267
public void update(@NonNull SecurityAnalysisParametersValues securityAnalysisParametersValues) {
73-
updateProvider(securityAnalysisParametersValues.getProvider());
68+
assignAttributes(securityAnalysisParametersValues);
69+
}
70+
71+
private void assignAttributes(SecurityAnalysisParametersValues securityAnalysisParametersValues) {
72+
this.provider = securityAnalysisParametersValues.getProvider();
7473
this.flowProportionalThreshold = securityAnalysisParametersValues.getFlowProportionalThreshold();
7574
this.highVoltageAbsoluteThreshold = securityAnalysisParametersValues.getHighVoltageAbsoluteThreshold();
7675
this.highVoltageProportionalThreshold = securityAnalysisParametersValues.getHighVoltageProportionalThreshold();
7776
this.lowVoltageAbsoluteThreshold = securityAnalysisParametersValues.getLowVoltageAbsoluteThreshold();
7877
this.lowVoltageProportionalThreshold = securityAnalysisParametersValues.getLowVoltageProportionalThreshold();
78+
assignLimitReductions(securityAnalysisParametersValues.getLimitReductionsValues());
79+
}
80+
81+
private void assignLimitReductions(@Nullable List<List<Double>> values) {
82+
if (values == null) {
83+
return;
84+
}
85+
List<LimitReductionEntity> entities = values.stream().map(LimitReductionEntity::new).toList();
86+
if (limitReductions == null) {
87+
limitReductions = entities;
88+
} else {
89+
limitReductions.clear();
90+
limitReductions.addAll(entities);
91+
}
7992
}
8093

8194
public void updateProvider(String provider) {
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Copyright (c) 2024, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
package org.gridsuite.securityanalysis.server.service;
8+
9+
import lombok.Getter;
10+
import lombok.Setter;
11+
import org.apache.commons.lang3.Range;
12+
import org.gridsuite.securityanalysis.server.dto.LimitReductionsByVoltageLevel;
13+
import org.gridsuite.securityanalysis.server.util.SecurityAnalysisException;
14+
import org.springframework.boot.context.properties.ConfigurationProperties;
15+
import org.springframework.stereotype.Service;
16+
17+
import java.util.ArrayList;
18+
import java.util.List;
19+
import java.util.Set;
20+
import java.util.concurrent.atomic.AtomicInteger;
21+
22+
@Setter
23+
@Getter
24+
@Service
25+
@ConfigurationProperties(prefix = "security-analysis.default-limit-reductions")
26+
public class LimitReductionService {
27+
private Set<String> providers;
28+
private List<LimitReductionsByVoltageLevel.VoltageLevel> voltageLevels;
29+
private List<LimitReductionsByVoltageLevel.LimitDuration> limitDurations;
30+
private List<List<Double>> defaultValues;
31+
32+
public List<LimitReductionsByVoltageLevel> createDefaultLimitReductions() {
33+
return createLimitReductions(defaultValues);
34+
}
35+
36+
public List<LimitReductionsByVoltageLevel> createLimitReductions(List<List<Double>> values) {
37+
assertValidConfig(values);
38+
List<LimitReductionsByVoltageLevel> limitReductions = new ArrayList<>(voltageLevels.size());
39+
AtomicInteger index = new AtomicInteger(0);
40+
voltageLevels.forEach(vl -> {
41+
LimitReductionsByVoltageLevel.LimitReductionsByVoltageLevelBuilder builder = LimitReductionsByVoltageLevel.builder().voltageLevel(vl);
42+
List<Double> valuesByVl = values.get(index.getAndIncrement());
43+
builder.permanentLimitReduction(valuesByVl.get(0));
44+
builder.temporaryLimitReductions(getLimitReductionsByDuration(valuesByVl));
45+
limitReductions.add(builder.build());
46+
});
47+
48+
return limitReductions;
49+
}
50+
51+
private List<LimitReductionsByVoltageLevel.LimitReduction> getLimitReductionsByDuration(List<Double> values) {
52+
List<LimitReductionsByVoltageLevel.LimitReduction> limitReductions = new ArrayList<>(limitDurations.size());
53+
AtomicInteger index = new AtomicInteger(1);
54+
limitDurations.forEach(limitDuration ->
55+
limitReductions.add(
56+
LimitReductionsByVoltageLevel.LimitReduction.builder()
57+
.limitDuration(limitDuration)
58+
.reduction(values.get(index.getAndIncrement()))
59+
.build()
60+
)
61+
);
62+
return limitReductions;
63+
}
64+
65+
private void assertValidConfig(List<List<Double>> values) {
66+
if (voltageLevels.isEmpty()) {
67+
throw new SecurityAnalysisException(SecurityAnalysisException.Type.LIMIT_REDUCTION_CONFIG_ERROR, "No configuration for voltage levels");
68+
}
69+
70+
if (limitDurations.isEmpty()) {
71+
throw new SecurityAnalysisException(SecurityAnalysisException.Type.LIMIT_REDUCTION_CONFIG_ERROR, "No configuration for limit durations");
72+
}
73+
74+
if (values.isEmpty() || values.get(0).isEmpty()) {
75+
throw new SecurityAnalysisException(SecurityAnalysisException.Type.LIMIT_REDUCTION_CONFIG_ERROR, "No values provided");
76+
}
77+
78+
int nbValuesByVl = values.get(0).size();
79+
if (values.stream().anyMatch(valuesByVl -> valuesByVl.size() != nbValuesByVl)) {
80+
throw new SecurityAnalysisException(SecurityAnalysisException.Type.LIMIT_REDUCTION_CONFIG_ERROR, "Number of values for a voltage level is incorrect");
81+
}
82+
83+
if (voltageLevels.size() < values.size()) {
84+
throw new SecurityAnalysisException(SecurityAnalysisException.Type.LIMIT_REDUCTION_CONFIG_ERROR, "Too many values provided for voltage levels");
85+
}
86+
87+
if (voltageLevels.size() > values.size()) {
88+
throw new SecurityAnalysisException(SecurityAnalysisException.Type.LIMIT_REDUCTION_CONFIG_ERROR, "Not enough values provided for voltage levels");
89+
}
90+
91+
if (limitDurations.size() < nbValuesByVl - 1) {
92+
throw new SecurityAnalysisException(SecurityAnalysisException.Type.LIMIT_REDUCTION_CONFIG_ERROR, "Too many values provided for limit durations");
93+
}
94+
95+
if (limitDurations.size() > nbValuesByVl - 1) {
96+
throw new SecurityAnalysisException(SecurityAnalysisException.Type.LIMIT_REDUCTION_CONFIG_ERROR, "Not enough values provided for limit durations");
97+
}
98+
99+
values.forEach(valuesByVl -> {
100+
if (valuesByVl.stream().anyMatch(v -> !Range.of(0.0, 1.0).contains(v))) {
101+
throw new SecurityAnalysisException(SecurityAnalysisException.Type.LIMIT_REDUCTION_CONFIG_ERROR, "Value not between 0 and 1");
102+
}
103+
});
104+
}
105+
}

src/main/java/org/gridsuite/securityanalysis/server/service/SecurityAnalysisObserver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@
88
package org.gridsuite.securityanalysis.server.service;
99

1010
import com.powsybl.loadflow.LoadFlowResult;
11-
import com.powsybl.security.SecurityAnalysisParameters;
1211
import com.powsybl.security.SecurityAnalysisResult;
1312
import io.micrometer.core.instrument.MeterRegistry;
1413
import io.micrometer.observation.ObservationRegistry;
1514
import lombok.NonNull;
1615
import com.powsybl.ws.commons.computation.service.AbstractComputationObserver;
16+
import org.gridsuite.securityanalysis.server.dto.SecurityAnalysisParametersDTO;
1717
import org.springframework.stereotype.Service;
1818

1919
/**
2020
* @author Kevin Le Saulnier <kevin.lesaulnier at rte-france.com>
2121
*/
2222
@Service
23-
public class SecurityAnalysisObserver extends AbstractComputationObserver<SecurityAnalysisResult, SecurityAnalysisParameters> {
23+
public class SecurityAnalysisObserver extends AbstractComputationObserver<SecurityAnalysisResult, SecurityAnalysisParametersDTO> {
2424

2525
private static final String COMPUTATION_TYPE = "sa";
2626

0 commit comments

Comments
 (0)