diff --git a/data/crac/crac-api/src/main/java/com/powsybl/openrao/data/cracapi/RaUsageLimits.java b/data/crac/crac-api/src/main/java/com/powsybl/openrao/data/cracapi/RaUsageLimits.java index c8bfb1a987..936e20e7ec 100644 --- a/data/crac/crac-api/src/main/java/com/powsybl/openrao/data/cracapi/RaUsageLimits.java +++ b/data/crac/crac-api/src/main/java/com/powsybl/openrao/data/cracapi/RaUsageLimits.java @@ -24,6 +24,7 @@ public class RaUsageLimits { private static final Map DEFAULT_MAX_RA_PER_TSO = new HashMap<>(); private int maxRa = DEFAULT_MAX_RA; private int maxTso = DEFAULT_MAX_TSO; + private final Set maxTsoExclusion = new HashSet<>(); private Map maxTopoPerTso = DEFAULT_MAX_TOPO_PER_TSO; private Map maxPstPerTso = DEFAULT_MAX_PST_PER_TSO; private Map maxRaPerTso = DEFAULT_MAX_RA_PER_TSO; @@ -96,6 +97,10 @@ public Map getMaxRaPerTso() { return maxRaPerTso; } + public Set getMaxTsoExclusion() { + return maxTsoExclusion; + } + private Map replaceNegativeValues(Map limitsPerTso) { limitsPerTso.forEach((tso, limit) -> { if (limit < 0) { @@ -123,6 +128,10 @@ private static void crossCheckMaxCraPerTsoParameters(Map maxRaP }); } + public void addTsoToExclude(String tso) { + maxTsoExclusion.add(tso); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/docs/input-data/crac/json.md b/docs/input-data/crac/json.md index 1a13c3cc83..8fb9527c93 100644 --- a/docs/input-data/crac/json.md +++ b/docs/input-data/crac/json.md @@ -1346,3 +1346,21 @@ crac.newRaUsageLimits("curative") ::: :::: +If several instants of the same kind are defined in the CRAC, the usage limits are **cumulative** among these instants. + +For instance, let us consider a CRAC with 3 curative instants and the following usage limits: + +```json +"ra-usage-limits-per-instant" : [ { + "instant": "curative 1", + "max-ra" : 1, +}, { + "instant": "curative 2", + "max-ra" : 3, +}, { + "instant": "curative 3", + "max-ra" : 7, +} ] +``` + +The maximum number of applicable remedial actions defined for the second curative instant (3) is a cumulated value that includes the maximum number of applicable remedial actions during the first curative instant (1). Thus, if 1 remedial action was applied during the first curative instant, only 2 remedial actions can actually be applied during the second curative instant. Likewise, the maximum number of remedial actions for the third curative instant includes the remedial actions applied at curative 1 and 2 instants. Depending on the number of previously applied remedial actions, the number of actually applicable remedial actions during the third curative instant can vary between 4 and 7. diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorFullOptimization.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorFullOptimization.java index 828ace7dc5..5ea3acabc8 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorFullOptimization.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorFullOptimization.java @@ -362,7 +362,8 @@ private Object runScenario(Crac crac, RaoParameters raoParameters, StateTree sta } else if (!automatonsOnly) { boolean allPreviousPerimetersSucceded = true; PrePerimeterResult previousPerimeterResult = preCurativeResult; - // Optimize curative instant + // Optimize curative perimeters + Map resultsPerPerimeter = new HashMap<>(); for (Perimeter curativePerimeter : optimizedScenario.getCurativePerimeters()) { State curativeState = curativePerimeter.getRaOptimisationState(); if (previousPerimeterResult == null) { @@ -370,11 +371,14 @@ private Object runScenario(Crac crac, RaoParameters raoParameters, StateTree sta } if (allPreviousPerimetersSucceded) { OptimizationResult curativeResult = optimizeCurativePerimeter(curativePerimeter, crac, networkClone, - raoParameters, stateTree, toolProvider, curativeTreeParameters, initialSensitivityOutput, previousPerimeterResult); + raoParameters, stateTree, toolProvider, curativeTreeParameters, initialSensitivityOutput, previousPerimeterResult, resultsPerPerimeter); allPreviousPerimetersSucceded = curativeResult.getSensitivityStatus() == DEFAULT; contingencyScenarioResults.put(curativeState, curativeResult); applyRemedialActions(networkClone, curativeResult, curativeState); previousPerimeterResult = null; + if (allPreviousPerimetersSucceded) { + resultsPerPerimeter.put(curativePerimeter.getRaOptimisationState(), curativeResult); + } } else { contingencyScenarioResults.put(curativeState, new SkippedOptimizationResultImpl(curativeState, new HashSet<>(), new HashSet<>(), ComputationStatus.FAILURE, sensitivityFailureOvercost)); } @@ -402,7 +406,8 @@ private OptimizationResult optimizeCurativePerimeter(Perimeter curativePerimeter ToolProvider toolProvider, TreeParameters curativeTreeParameters, PrePerimeterResult initialSensitivityOutput, - PrePerimeterResult prePerimeterSensitivityOutput) { + PrePerimeterResult prePerimeterSensitivityOutput, + Map resultsPerPerimeter) { State curativeState = curativePerimeter.getRaOptimisationState(); TECHNICAL_LOGS.info("Optimizing curative state {}.", curativeState.getId()); @@ -414,6 +419,8 @@ private OptimizationResult optimizeCurativePerimeter(Perimeter curativePerimeter .withUnoptimizedCnecParameters(UnoptimizedCnecParameters.build(raoParameters.getNotOptimizedCnecsParameters(), stateTree.getOperatorsNotSharingCras(), raoInput.getCrac())) .build(); + searchTreeParameters.decreaseRemedialActionUsageLimits(resultsPerPerimeter); + SearchTreeInput searchTreeInput = SearchTreeInput.create() .withNetwork(network) .withOptimizationPerimeter(optPerimeter) diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/searchtree/parameters/SearchTreeParameters.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/searchtree/parameters/SearchTreeParameters.java index 167104bbcb..52c7744860 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/searchtree/parameters/SearchTreeParameters.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/searchtree/parameters/SearchTreeParameters.java @@ -9,6 +9,7 @@ import com.powsybl.openrao.data.cracapi.Crac; import com.powsybl.openrao.data.cracapi.Instant; import com.powsybl.openrao.data.cracapi.RaUsageLimits; +import com.powsybl.openrao.data.cracapi.State; import com.powsybl.openrao.data.cracapi.rangeaction.RangeAction; import com.powsybl.openrao.raoapi.parameters.ObjectiveFunctionParameters; import com.powsybl.openrao.raoapi.parameters.RangeActionsOptimizationParameters; @@ -17,7 +18,9 @@ import com.powsybl.openrao.raoapi.parameters.extensions.MnecParametersExtension; import com.powsybl.openrao.raoapi.parameters.extensions.RelativeMarginsParametersExtension; import com.powsybl.openrao.searchtreerao.commons.parameters.*; +import com.powsybl.openrao.searchtreerao.result.api.OptimizationResult; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -32,7 +35,7 @@ public class SearchTreeParameters { // required for the search tree algorithm private final TreeParameters treeParameters; private final NetworkActionParameters networkActionParameters; - private Map raLimitationParameters; + private final Map raLimitationParameters; // required for sub-module iterating linear optimizer private final RangeActionsOptimizationParameters rangeActionParameters; @@ -134,6 +137,63 @@ public void setRaLimitationsForSecondPreventive(RaUsageLimits raUsageLimits, Set this.raLimitationParameters.put(preventiveInstant, raUsageLimits); } + public void decreaseRemedialActionUsageLimits(Map resultsPerOptimizationState) { + resultsPerOptimizationState.forEach((optimizedState, result) -> + raLimitationParameters.keySet().forEach( + otherInstant -> { + // Cumulative behaviour of constraints only applies to instants of the same kind + if (!otherInstant.comesBefore(optimizedState.getInstant()) && optimizedState.getInstant().getKind().equals(otherInstant.getKind())) { + RaUsageLimits raUsageLimits = raLimitationParameters.get(otherInstant); + int decreasedMaxRa = decreaseMaxRemedialAction(raUsageLimits, optimizedState, result); + Map decreasedMaxRaPerTso = decreaseMaxRemedialActionPerTso(raUsageLimits, optimizedState, result); + Map decreasedMaxTopoPerTso = decreaseMaxTopoPerTso(raUsageLimits, result, decreasedMaxRaPerTso); + Map decreasedMaxPstPerTso = decreaseMaxPstPerTso(raUsageLimits, optimizedState, result, decreasedMaxRaPerTso); + int decreasedMaxTso = decreaseMaxTso(raUsageLimits, optimizedState, result); + + RaUsageLimits decreasedRaUsageLimits = new RaUsageLimits(); + decreasedRaUsageLimits.setMaxRa(decreasedMaxRa); + decreasedRaUsageLimits.setMaxRaPerTso(decreasedMaxRaPerTso); + decreasedRaUsageLimits.setMaxTopoPerTso(decreasedMaxTopoPerTso); + decreasedRaUsageLimits.setMaxPstPerTso(decreasedMaxPstPerTso); + raUsageLimits.getMaxTsoExclusion().forEach(decreasedRaUsageLimits::addTsoToExclude); + decreasedRaUsageLimits.setMaxTso(decreasedMaxTso); + + raLimitationParameters.put(otherInstant, decreasedRaUsageLimits); + } + } + ) + ); + } + + private static int decreaseMaxRemedialAction(RaUsageLimits raUsageLimits, State optimizedState, OptimizationResult result) { + return raUsageLimits.getMaxRa() - result.getActivatedNetworkActions().size() - result.getActivatedRangeActions(optimizedState).size(); + } + + private static Map decreaseMaxTopoPerTso(RaUsageLimits raUsageLimits, OptimizationResult result, Map decreasedMaxRaPerTso) { + Map decreasedMaxTopoPerTso = new HashMap<>(); + raUsageLimits.getMaxTopoPerTso().forEach((key, value) -> decreasedMaxTopoPerTso.put(key, Math.min(value - (int) result.getActivatedNetworkActions().stream().filter(networkAction -> key.equals(networkAction.getOperator())).count(), decreasedMaxRaPerTso.get(key)))); + return decreasedMaxTopoPerTso; + } + + private static Map decreaseMaxPstPerTso(RaUsageLimits raUsageLimits, State optimizedState, OptimizationResult result, Map decreasedMaxRaPerTso) { + Map decreasedMaxPstPerTso = new HashMap<>(); + raUsageLimits.getMaxPstPerTso().forEach((key, value) -> decreasedMaxPstPerTso.put(key, Math.min(value - (int) result.getActivatedRangeActions(optimizedState).stream().filter(networkAction -> key.equals(networkAction.getOperator())).count(), decreasedMaxRaPerTso.get(key)))); + return decreasedMaxPstPerTso; + } + + private static Map decreaseMaxRemedialActionPerTso(RaUsageLimits raUsageLimits, State optimizedState, OptimizationResult result) { + Map decreasedMaxRaPerTso = new HashMap<>(); + raUsageLimits.getMaxRaPerTso().forEach((key, value) -> decreasedMaxRaPerTso.put(key, value - (int) result.getActivatedNetworkActions().stream().filter(networkAction -> key.equals(networkAction.getOperator())).count() - (int) result.getActivatedRangeActions(optimizedState).stream().filter(networkAction -> key.equals(networkAction.getOperator())).count())); + return decreasedMaxRaPerTso; + } + + private static int decreaseMaxTso(RaUsageLimits raUsageLimits, State optimizedState, OptimizationResult result) { + int tsosWithPreviouslyAppliedRas = raUsageLimits.getMaxTsoExclusion().size(); + result.getActivatedNetworkActions().forEach(networkAction -> raUsageLimits.addTsoToExclude(networkAction.getOperator())); + result.getActivatedRangeActions(optimizedState).forEach(rangeAction -> raUsageLimits.addTsoToExclude(rangeAction.getOperator())); + return raUsageLimits.getMaxTso() - (raUsageLimits.getMaxTsoExclusion().size() - tsosWithPreviouslyAppliedRas); + } + public static SearchTreeParametersBuilder create() { return new SearchTreeParametersBuilder(); } @@ -180,7 +240,7 @@ public SearchTreeParametersBuilder withNetworkActionParameters(NetworkActionPara } public SearchTreeParametersBuilder withGlobalRemedialActionLimitationParameters(Map raLimitationParameters) { - this.raLimitationParameters = raLimitationParameters; + this.raLimitationParameters = new HashMap<>(raLimitationParameters); return this; } diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorFullOptimizationTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorFullOptimizationTest.java index 4ea2954894..c7a0bb5750 100644 --- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorFullOptimizationTest.java +++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorFullOptimizationTest.java @@ -41,6 +41,8 @@ import org.mockito.Mockito; import org.slf4j.LoggerFactory; +import java.io.File; +import java.nio.file.Paths; import java.util.*; import java.util.stream.Collectors; @@ -918,6 +920,45 @@ void optimizationWithAutoSearchTreeAndAutoPsts() { assertEquals(-131.0, raoResult.getFlow(crac.getInstant("curative"), crac.getFlowCnec("NNL2AA1 BBE3AA1 1 - Contingency DE2 NL3 1 - curative"), Side.LEFT, Unit.MEGAWATT), 1.); } + @Test + void threeCurativeInstantsWithCumulativeMaximumNumberOfApplicableRemedialActions() { + Network network = Network.read("12Nodes_4ParallelLines.uct", getClass().getResourceAsStream("/network/12Nodes_4ParallelLines.uct")); + Crac crac = CracImporters.importCrac( + Paths.get(new File(Objects.requireNonNull(CastorFullOptimizationTest.class.getResource("/crac/small-crac-ra-limits-per-instant.json")).getFile()).toString()), + network + ); + + RaoInput raoInput = RaoInput.build(network, crac).build(); + RaoParameters raoParameters = JsonRaoParameters.read(getClass().getResourceAsStream("/parameters/RaoParameters_DC.json")); + + RaoResult raoResult = new CastorFullOptimization(raoInput, raoParameters, null).run().join(); + + // SearchTree stop criterion is MIN_OBJECTIVE so all 3 remedial actions should be applied during the first curative instant + // Yet, the number of RAs that can be applied is restricted to 1 (resp. 2) in total for curative1 (resp. curative2) + assertEquals(1, raoResult.getActivatedNetworkActionsDuringState(crac.getState("contingency", crac.getInstant("curative1"))).size()); + assertEquals(1, raoResult.getActivatedNetworkActionsDuringState(crac.getState("contingency", crac.getInstant("curative2"))).size()); + } + + @Test + void threeCurativeInstantsWithCumulativeMaximumNumberOfTsos() { + Network network = Network.read("12Nodes_4ParallelLines.uct", getClass().getResourceAsStream("/network/12Nodes_4ParallelLines.uct")); + Crac crac = CracImporters.importCrac( + Paths.get(new File(Objects.requireNonNull(CastorFullOptimizationTest.class.getResource("/crac/small-crac-ra-limits-per-instant-3-tsos.json")).getFile()).toString()), + network + ); + + RaoInput raoInput = RaoInput.build(network, crac).build(); + RaoParameters raoParameters = JsonRaoParameters.read(getClass().getResourceAsStream("/parameters/RaoParameters_DC.json")); + + RaoResult raoResult = new CastorFullOptimization(raoInput, raoParameters, null).run().join(); + + // SearchTree stop criterion is MIN_OBJECTIVE so all 3 remedial actions should be applied during the first curative instant + // Yet, the number of RAs that can be applied is restricted to 2 (resp. 1) in total for curative1 (resp. curative2) + assertEquals(2, raoResult.getActivatedNetworkActionsDuringState(crac.getState("contingency", crac.getInstant("curative1"))).size()); + assertEquals(0, raoResult.getActivatedNetworkActionsDuringState(crac.getState("contingency", crac.getInstant("curative2"))).size()); + assertEquals(1, raoResult.getActivatedNetworkActionsDuringState(crac.getState("contingency", crac.getInstant("curative3"))).size()); + } + @Test void curativeOptimizationShouldNotBeDoneIfPreventiveUnsecure() { Network network = Network.read("small-network-2P.uct", getClass().getResourceAsStream("/network/small-network-2P.uct")); diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/searchtree/parameters/SearchTreeParametersTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/searchtree/parameters/SearchTreeParametersTest.java index 7c9ccb70e5..efe5829fcc 100644 --- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/searchtree/parameters/SearchTreeParametersTest.java +++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/searchtree/parameters/SearchTreeParametersTest.java @@ -6,10 +6,12 @@ */ package com.powsybl.openrao.searchtreerao.searchtree.parameters; +import com.powsybl.iidm.network.Network; import com.powsybl.openrao.data.cracapi.Crac; import com.powsybl.openrao.data.cracapi.Instant; import com.powsybl.openrao.data.cracapi.RaUsageLimits; import com.powsybl.openrao.data.cracapi.rangeaction.RangeAction; +import com.powsybl.openrao.data.cracioapi.CracImporters; import com.powsybl.openrao.raoapi.parameters.ObjectiveFunctionParameters; import com.powsybl.openrao.raoapi.parameters.RangeActionsOptimizationParameters; import com.powsybl.openrao.raoapi.parameters.RaoParameters; @@ -17,12 +19,16 @@ import com.powsybl.openrao.raoapi.parameters.extensions.MnecParametersExtension; import com.powsybl.openrao.raoapi.parameters.extensions.RelativeMarginsParametersExtension; import com.powsybl.openrao.searchtreerao.commons.parameters.*; +import com.powsybl.openrao.searchtreerao.result.api.OptimizationResult; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import java.io.File; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; import static org.junit.jupiter.api.Assertions.*; @@ -145,4 +151,54 @@ void testRaLimitsSetter() { assertEquals(10, maxPstPerTso.get("BE")); assertEquals(0, maxPstPerTso.get("FR")); } + + @Test + void testDecreaseRemedialActionUsageLimits() { + Crac crac = CracImporters.importCrac( + Paths.get(new File(Objects.requireNonNull(SearchTreeParametersTest.class.getResource("/crac/small-crac-with-comprehensive-usage-limits.json")).getFile()).toString()), + Network.read(Paths.get(new File(Objects.requireNonNull(SearchTreeParametersTest.class.getResource("/network/small-network-2P.uct")).getFile()).toString())) + ); + + SearchTreeParameters parameters = SearchTreeParameters.create() + .withGlobalRemedialActionLimitationParameters( + Map.of( + crac.getInstant("preventive"), crac.getRaUsageLimits(crac.getInstant("preventive")), + crac.getInstant("curative1"), crac.getRaUsageLimits(crac.getInstant("curative1")), + crac.getInstant("curative2"), crac.getRaUsageLimits(crac.getInstant("curative2")) + ) + ) + .build(); + + OptimizationResult preventiveOptimizationResult = Mockito.mock(OptimizationResult.class); + when(preventiveOptimizationResult.getActivatedNetworkActions()).thenReturn(Set.of(crac.getNetworkAction("prev-open-be-1"), crac.getNetworkAction("prev-close-be-2"))); + when(preventiveOptimizationResult.getActivatedRangeActions(crac.getPreventiveState())).thenReturn(Set.of(crac.getPstRangeAction("prev-pst-fr"))); + + OptimizationResult curative1OptimizationResult = Mockito.mock(OptimizationResult.class); + when(curative1OptimizationResult.getActivatedNetworkActions()).thenReturn(Set.of(crac.getNetworkAction("cur1-open-fr-1"))); + when(curative1OptimizationResult.getActivatedRangeActions(crac.getState("contingency", crac.getInstant("curative1")))).thenReturn(Set.of(crac.getPstRangeAction("cur1-pst-be"))); + + parameters.decreaseRemedialActionUsageLimits(Map.of(crac.getPreventiveState(), preventiveOptimizationResult, crac.getState("contingency", crac.getInstant("curative1")), curative1OptimizationResult)); + + RaUsageLimits preventiveRaUsageLimits = parameters.getRaLimitationParameters().get(crac.getInstant("preventive")); + assertEquals(0, preventiveRaUsageLimits.getMaxRa()); + assertEquals(Map.of("FR", 2, "BE", 1), preventiveRaUsageLimits.getMaxTopoPerTso()); + assertEquals(Map.of("FR", 4, "BE", 1), preventiveRaUsageLimits.getMaxPstPerTso()); + assertEquals(Map.of("FR", 4, "BE", 1), preventiveRaUsageLimits.getMaxRaPerTso()); + assertEquals(0, preventiveRaUsageLimits.getMaxTso()); + + RaUsageLimits curative1RaUsageLimits = parameters.getRaLimitationParameters().get(crac.getInstant("curative1")); + assertEquals(0, curative1RaUsageLimits.getMaxRa()); + assertEquals(Map.of("FR", 0, "BE", 0), curative1RaUsageLimits.getMaxTopoPerTso()); + assertEquals(Map.of("FR", 1, "BE", 0), curative1RaUsageLimits.getMaxPstPerTso()); + assertEquals(Map.of("FR", 1, "BE", 0), curative1RaUsageLimits.getMaxRaPerTso()); + assertEquals(0, curative1RaUsageLimits.getMaxTso()); + + // results from curative1 should impact limits for curative2 + RaUsageLimits curative2RaUsageLimits = parameters.getRaLimitationParameters().get(crac.getInstant("curative2")); + assertEquals(3, curative2RaUsageLimits.getMaxRa()); + assertEquals(Map.of("FR", 2, "BE", 2), curative2RaUsageLimits.getMaxTopoPerTso()); + assertEquals(Map.of("FR", 4, "BE", 4), curative2RaUsageLimits.getMaxPstPerTso()); + assertEquals(Map.of("FR", 5, "BE", 4), curative2RaUsageLimits.getMaxRaPerTso()); + assertEquals(1, curative2RaUsageLimits.getMaxTso()); + } } diff --git a/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac-ra-limits-per-instant-3-tsos.json b/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac-ra-limits-per-instant-3-tsos.json new file mode 100644 index 0000000000..4f3c638004 --- /dev/null +++ b/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac-ra-limits-per-instant-3-tsos.json @@ -0,0 +1,211 @@ +{ + "type" : "CRAC", + "version" : "2.1", + "info" : "Generated by Open RAO http://farao-community.github.io", + "id" : "crac", + "name" : "crac", + "instants" : [ { + "id" : "preventive", + "kind" : "PREVENTIVE" + }, { + "id" : "outage", + "kind" : "OUTAGE" + }, { + "id" : "curative1", + "kind" : "CURATIVE" + }, { + "id" : "curative2", + "kind" : "CURATIVE" + }, { + "id" : "curative3", + "kind" : "CURATIVE" + } ], + "ra-usage-limits-per-instant" : [ { + "instant" : "curative3", + "max-ra" : 2147483647, + "max-tso" : 3, + "max-topo-per-tso" : { }, + "max-pst-per-tso" : { }, + "max-ra-per-tso" : { } + }, { + "instant" : "curative2", + "max-ra" : 2147483647, + "max-tso" : 2, + "max-topo-per-tso" : { }, + "max-pst-per-tso" : { }, + "max-ra-per-tso" : { } + }, { + "instant" : "curative1", + "max-ra" : 2147483647, + "max-tso" : 2, + "max-topo-per-tso" : { }, + "max-pst-per-tso" : { }, + "max-ra-per-tso" : { } + } ], + "networkElementsNamePerId" : { }, + "contingencies" : [ { + "id" : "contingency", + "name" : "CO", + "networkElementsIds" : [ "NNL2AA1 BBE3AA1 1" ] + } ], + "flowCnecs" : [ { + "id" : "FR2-DE3 - contingency - curative1", + "name" : "FR2-DE3 - contingency - curative1", + "networkElementId" : "FFR2AA1 DDE3AA1 1", + "operator" : null, + "instant" : "curative1", + "contingencyId" : "contingency", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -800.0, + "max" : 800.0, + "side" : "left" + } ] + }, { + "id" : "FR2-DE3 - contingency - curative2", + "name" : "FR2-DE3 - contingency - curative2", + "networkElementId" : "FFR2AA1 DDE3AA1 1", + "operator" : null, + "instant" : "curative2", + "contingencyId" : "contingency", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -400.0, + "max" : 400.0, + "side" : "left" + } ] + }, { + "id" : "FR2-DE3 - contingency - curative3", + "name" : "FR2-DE3 - contingency - curative3", + "networkElementId" : "FFR2AA1 DDE3AA1 1", + "operator" : null, + "instant" : "curative3", + "contingencyId" : "contingency", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -400.0, + "max" : 400.0, + "side" : "left" + } ] + }, { + "id" : "FR2-DE3 - contingency - outage", + "name" : "FR2-DE3 - contingency - outage", + "networkElementId" : "FFR2AA1 DDE3AA1 1", + "operator" : null, + "instant" : "outage", + "contingencyId" : "contingency", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -1000.0, + "max" : 1000.0, + "side" : "left" + } ] + }, { + "id" : "FR2-DE3 - preventive", + "name" : "FR2-DE3 - preventive", + "networkElementId" : "FFR2AA1 DDE3AA1 1", + "operator" : null, + "instant" : "preventive", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -600.0, + "max" : 600.0, + "side" : "left" + } ] + } ], + "angleCnecs" : [ ], + "voltageCnecs" : [ ], + "pstRangeActions" : [ ], + "hvdcRangeActions" : [ ], + "injectionRangeActions" : [ ], + "counterTradeRangeActions" : [ ], + "networkActions" : [ { + "id" : "close-fr2-de3-2", + "name" : "close-fr2-de3-2", + "operator" : "BE", + "onContingencyStateUsageRules" : [ { + "instant" : "curative1", + "contingencyId" : "contingency", + "usageMethod" : "available" + }, { + "instant" : "curative2", + "contingencyId" : "contingency", + "usageMethod" : "available" + }, { + "instant" : "curative3", + "contingencyId" : "contingency", + "usageMethod" : "available" + } ], + "topologicalActions" : [ { + "networkElementId" : "FFR2AA1 DDE3AA1 2", + "actionType" : "close" + } ] + }, { + "id" : "close-fr2-de3-3", + "name" : "close-fr2-de3-3", + "operator" : "DE", + "onContingencyStateUsageRules" : [ { + "instant" : "curative1", + "contingencyId" : "contingency", + "usageMethod" : "available" + }, { + "instant" : "curative2", + "contingencyId" : "contingency", + "usageMethod" : "available" + }, { + "instant" : "curative3", + "contingencyId" : "contingency", + "usageMethod" : "available" + } ], + "topologicalActions" : [ { + "networkElementId" : "FFR2AA1 DDE3AA1 3", + "actionType" : "close" + } ] + }, { + "id" : "close-fr2-de3-4", + "name" : "close-fr2-de3-4", + "operator" : "FR", + "onContingencyStateUsageRules" : [ { + "instant" : "curative1", + "contingencyId" : "contingency", + "usageMethod" : "available" + }, { + "instant" : "curative2", + "contingencyId" : "contingency", + "usageMethod" : "available" + }, { + "instant" : "curative3", + "contingencyId" : "contingency", + "usageMethod" : "available" + } ], + "topologicalActions" : [ { + "networkElementId" : "FFR2AA1 DDE3AA1 4", + "actionType" : "close" + } ] + } ] +} \ No newline at end of file diff --git a/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac-ra-limits-per-instant.json b/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac-ra-limits-per-instant.json new file mode 100644 index 0000000000..53bea5fcf1 --- /dev/null +++ b/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac-ra-limits-per-instant.json @@ -0,0 +1,171 @@ +{ + "type" : "CRAC", + "version" : "2.1", + "info" : "Generated by Open RAO http://farao-community.github.io", + "id" : "crac", + "name" : "crac", + "instants" : [ { + "id" : "preventive", + "kind" : "PREVENTIVE" + }, { + "id" : "outage", + "kind" : "OUTAGE" + }, { + "id" : "curative1", + "kind" : "CURATIVE" + }, { + "id" : "curative2", + "kind" : "CURATIVE" + } ], + "ra-usage-limits-per-instant" : [ { + "instant" : "curative2", + "max-ra" : 2, + "max-tso" : 2147483647, + "max-topo-per-tso" : { }, + "max-pst-per-tso" : { }, + "max-ra-per-tso" : { } + }, { + "instant" : "curative1", + "max-ra" : 1, + "max-tso" : 2147483647, + "max-topo-per-tso" : { }, + "max-pst-per-tso" : { }, + "max-ra-per-tso" : { } + } ], + "networkElementsNamePerId" : { }, + "contingencies" : [ { + "id" : "contingency", + "name" : "CO", + "networkElementsIds" : [ "NNL2AA1 BBE3AA1 1" ] + } ], + "flowCnecs" : [ { + "id" : "FR2-DE3 - contingency - curative1", + "name" : "FR2-DE3 - contingency - curative1", + "networkElementId" : "FFR2AA1 DDE3AA1 1", + "operator" : null, + "instant" : "curative1", + "contingencyId" : "contingency", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -800.0, + "max" : 800.0, + "side" : "left" + } ] + }, { + "id" : "FR2-DE3 - contingency - curative2", + "name" : "FR2-DE3 - contingency - curative2", + "networkElementId" : "FFR2AA1 DDE3AA1 1", + "operator" : null, + "instant" : "curative2", + "contingencyId" : "contingency", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -400.0, + "max" : 400.0, + "side" : "left" + } ] + }, { + "id" : "FR2-DE3 - contingency - outage", + "name" : "FR2-DE3 - contingency - outage", + "networkElementId" : "FFR2AA1 DDE3AA1 1", + "operator" : null, + "instant" : "outage", + "contingencyId" : "contingency", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -1000.0, + "max" : 1000.0, + "side" : "left" + } ] + }, { + "id" : "FR2-DE3 - preventive", + "name" : "FR2-DE3 - preventive", + "networkElementId" : "FFR2AA1 DDE3AA1 1", + "operator" : null, + "instant" : "preventive", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -600.0, + "max" : 600.0, + "side" : "left" + } ] + } ], + "angleCnecs" : [ ], + "voltageCnecs" : [ ], + "pstRangeActions" : [ ], + "hvdcRangeActions" : [ ], + "injectionRangeActions" : [ ], + "counterTradeRangeActions" : [ ], + "networkActions" : [ { + "id" : "close-fr2-de3-2", + "name" : "close-fr2-de3-2", + "operator" : null, + "onContingencyStateUsageRules" : [ { + "instant" : "curative1", + "contingencyId" : "contingency", + "usageMethod" : "available" + }, { + "instant" : "curative2", + "contingencyId" : "contingency", + "usageMethod" : "available" + } ], + "topologicalActions" : [ { + "networkElementId" : "FFR2AA1 DDE3AA1 2", + "actionType" : "close" + } ] + }, { + "id" : "close-fr2-de3-3", + "name" : "close-fr2-de3-3", + "operator" : null, + "onContingencyStateUsageRules" : [ { + "instant" : "curative1", + "contingencyId" : "contingency", + "usageMethod" : "available" + }, { + "instant" : "curative2", + "contingencyId" : "contingency", + "usageMethod" : "available" + } ], + "topologicalActions" : [ { + "networkElementId" : "FFR2AA1 DDE3AA1 3", + "actionType" : "close" + } ] + }, { + "id" : "close-fr2-de3-4", + "name" : "close-fr2-de3-4", + "operator" : null, + "onContingencyStateUsageRules" : [ { + "instant" : "curative1", + "contingencyId" : "contingency", + "usageMethod" : "available" + }, { + "instant" : "curative2", + "contingencyId" : "contingency", + "usageMethod" : "available" + } ], + "topologicalActions" : [ { + "networkElementId" : "FFR2AA1 DDE3AA1 4", + "actionType" : "close" + } ] + } ] +} \ No newline at end of file diff --git a/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac-with-comprehensive-usage-limits.json b/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac-with-comprehensive-usage-limits.json new file mode 100644 index 0000000000..8cb66335c2 --- /dev/null +++ b/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac-with-comprehensive-usage-limits.json @@ -0,0 +1,249 @@ +{ + "type" : "CRAC", + "version" : "2.1", + "info" : "Generated by Open RAO http://farao-community.github.io", + "id" : "crac", + "name" : "crac", + "instants" : [ { + "id" : "preventive", + "kind" : "PREVENTIVE" + }, { + "id" : "outage", + "kind" : "OUTAGE" + }, { + "id" : "curative1", + "kind" : "CURATIVE" + }, { + "id" : "curative2", + "kind" : "CURATIVE" + } ], + "ra-usage-limits-per-instant" : [ { + "instant" : "curative2", + "max-ra" : 5, + "max-tso" : 3, + "max-topo-per-tso" : { + "FR" : 3, + "BE" : 2 + }, + "max-pst-per-tso" : { + "FR" : 4, + "BE" : 5 + }, + "max-ra-per-tso" : { + "FR" : 6, + "BE" : 5 + } + }, { + "instant" : "curative1", + "max-ra" : 2, + "max-tso" : 2, + "max-topo-per-tso" : { + "FR" : 1, + "BE" : 1 + }, + "max-pst-per-tso" : { + "FR" : 2, + "BE" : 1 + }, + "max-ra-per-tso" : { + "FR" : 2, + "BE" : 1 + } + }, { + "instant" : "preventive", + "max-ra" : 3, + "max-tso" : 2, + "max-topo-per-tso" : { + "FR" : 2, + "BE" : 3 + }, + "max-pst-per-tso" : { + "FR" : 5, + "BE" : 2 + }, + "max-ra-per-tso" : { + "FR" : 5, + "BE" : 3 + } + } ], + "networkElementsNamePerId" : { }, + "contingencies" : [ { + "id" : "contingency", + "name" : "CO", + "networkElementsIds" : [ "FFR1AA1 FFR2AA1 1" ] + } ], + "flowCnecs" : [ { + "id" : "network-element - contingency - curative1", + "name" : "network-element - contingency - curative1", + "networkElementId" : "FFR2AA1 FFR3AA1 1", + "operator" : null, + "instant" : "curative1", + "contingencyId" : "contingency", + "optimized" : true, + "monitored" : false, + "reliabilityMargin" : 0.0, + "iMax" : [ NaN ], + "nominalV" : [ 400.0 ], + "thresholds" : [ { + "unit" : "ampere", + "min" : -800.0, + "max" : 800.0, + "side" : "left" + } ] + } ], + "angleCnecs" : [ ], + "voltageCnecs" : [ ], + "pstRangeActions" : [ + { + "id": "prev-pst-fr", + "name": "prev-pst-fr", + "operator": "FR", + "initialTap": 0, + "speed": 1, + "tapToAngleConversionMap" : { + "-1" : -0.3896097993971608, + "0" : 0.0, + "-2" : -0.7792105912934298, + "1" : 0.3896097993971608, + "-3" : -1.1687933694373345, + "2" : 0.7792105912934298, + "-4" : -1.5583491300758083, + "3" : 1.1687933694373345, + "-5" : -1.9478688732023104, + "4" : 1.5583491300758083, + "-6" : -2.337343603803646, + "5" : 1.9478688732023104, + "-7" : -2.7267643331050597, + "6" : 2.337343603803646, + "-8" : -3.1161220798131644, + "7" : 2.7267643331050597, + "-9" : -3.505407871356285, + "8" : 3.1161220798131644, + "-10" : -3.894612745121778, + "9" : 3.505407871356285, + "-11" : -4.283727749689918, + "10" : 3.894612745121778, + "-12" : -4.672743946063913, + "11" : 4.283727749689918, + "-13" : -5.061652408895631, + "12" : 4.672743946063913, + "-14" : -5.4504442277066305, + "13" : 5.061652408895631, + "-15" : -5.839110508104064, + "14" : 5.4504442277066305, + "-16" : -6.2276423729910535, + "15" : 5.839110508104064, + "16" : 6.2276423729910535 + }, + "onInstantUsageRules": [ + { + "usageMethod": "available", + "instant": "preventive" + } + ], + "ranges": [ + { + "min": -15, + "max": 15, + "rangeType": "absolute" + } + ], + "networkElementId": "FFR2AA1 FFR4AA1 1" + }, { + "id": "cur1-pst-be", + "name": "cur1-pst-be", + "operator": "BE", + "initialTap": 0, + "speed": 1, + "tapToAngleConversionMap" : { + "-1" : -0.3896097993971608, + "0" : 0.0, + "-2" : -0.7792105912934298, + "1" : 0.3896097993971608, + "-3" : -1.1687933694373345, + "2" : 0.7792105912934298, + "-4" : -1.5583491300758083, + "3" : 1.1687933694373345, + "-5" : -1.9478688732023104, + "4" : 1.5583491300758083, + "-6" : -2.337343603803646, + "5" : 1.9478688732023104, + "-7" : -2.7267643331050597, + "6" : 2.337343603803646, + "-8" : -3.1161220798131644, + "7" : 2.7267643331050597, + "-9" : -3.505407871356285, + "8" : 3.1161220798131644, + "-10" : -3.894612745121778, + "9" : 3.505407871356285, + "-11" : -4.283727749689918, + "10" : 3.894612745121778, + "-12" : -4.672743946063913, + "11" : 4.283727749689918, + "-13" : -5.061652408895631, + "12" : 4.672743946063913, + "-14" : -5.4504442277066305, + "13" : 5.061652408895631, + "-15" : -5.839110508104064, + "14" : 5.4504442277066305, + "-16" : -6.2276423729910535, + "15" : 5.839110508104064, + "16" : 6.2276423729910535 + }, + "onInstantUsageRules": [ + { + "usageMethod": "available", + "instant": "curative1" + } + ], + "ranges": [ + { + "min": -15, + "max": 15, + "rangeType": "absolute" + } + ], + "networkElementId": "BBE2AA1 BBE3AA1 1" + } + ], + "hvdcRangeActions" : [ ], + "injectionRangeActions" : [ ], + "counterTradeRangeActions" : [ ], + "networkActions" : [ { + "id" : "prev-open-be-1", + "name" : "prev-open-be-1", + "operator" : "BE", + "onInstantUsageRules" : [ { + "instant" : "preventive", + "usageMethod" : "available" + } ], + "topologicalActions" : [ { + "networkElementId" : "BBE1AA1 BBE2AA1 1", + "actionType" : "open" + } ] + }, { + "id" : "prev-close-be-2", + "name" : "prev-close-be-2", + "operator" : "BE", + "onInstantUsageRules" : [ { + "instant" : "preventive", + "usageMethod" : "available" + } ], + "topologicalActions" : [ { + "networkElementId" : "BBE2AA1 BBE3AA1 1", + "actionType" : "close" + } ] + }, { + "id" : "cur1-open-fr-1", + "name" : "cur1-open-fr-1", + "operator" : "FR", + "onInstantUsageRules" : [ { + "instant" : "curative1", + "usageMethod" : "available" + } ], + "topologicalActions" : [ { + "networkElementId" : "FFR1AA1 FFR3AA1 1", + "actionType" : "open" + } ] + } ] +} \ No newline at end of file diff --git a/ra-optimisation/search-tree-rao/src/test/resources/network/12Nodes_4ParallelLines.uct b/ra-optimisation/search-tree-rao/src/test/resources/network/12Nodes_4ParallelLines.uct new file mode 100644 index 0000000000..069c81ee79 --- /dev/null +++ b/ra-optimisation/search-tree-rao/src/test/resources/network/12Nodes_4ParallelLines.uct @@ -0,0 +1,41 @@ +##C 2007.05.01 +##N +##ZBE +BBE1AA1 BE1 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +BBE2AA1 BE2 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +BBE3AA1 BE3 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##ZDE +DDE1AA1 DE1 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +DDE2AA1 DE2 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +DDE3AA1 DE3 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##ZFR +FFR1AA1 FR1 0 2 400.00 -1000.0 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +FFR2AA1 FR2 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +FFR3AA1 FR3 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##ZNL +NNL1AA1 NL1 0 2 400.00 1000.00 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +NNL2AA1 NL2 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +NNL3AA1 NL3 0 2 400.00 0.00000 0.00000 0.00000 0.00000 9000.00 -9000.0 9000.00 -9000.0 +##L +BBE1AA1 BBE2AA1 1 0 0.0000 10.000 0.000000 5000 +BBE1AA1 BBE3AA1 1 0 0.0000 10.000 0.000000 5000 +FFR1AA1 FFR2AA1 1 0 0.0000 10.000 0.000000 5000 +FFR1AA1 FFR3AA1 1 0 0.0000 10.000 0.000000 5000 +FFR2AA1 FFR3AA1 1 0 0.0000 10.000 0.000000 5000 +DDE1AA1 DDE2AA1 1 0 0.0000 10.000 0.000000 5000 +DDE1AA1 DDE3AA1 1 0 0.0000 10.000 0.000000 5000 +DDE2AA1 DDE3AA1 1 0 0.0000 10.000 0.000000 5000 +NNL1AA1 NNL2AA1 1 0 0.0000 10.000 0.000000 5000 +NNL1AA1 NNL3AA1 1 0 0.0000 10.000 0.000000 5000 +NNL2AA1 NNL3AA1 1 0 0.0000 10.000 0.000000 5000 +FFR2AA1 DDE3AA1 1 0 0.0000 10.000 0.000000 5000 +FFR2AA1 DDE3AA1 2 8 0.0000 10.000 0.000000 5000 +FFR2AA1 DDE3AA1 3 8 0.0000 10.000 0.000000 5000 +FFR2AA1 DDE3AA1 4 8 0.0000 10.000 0.000000 5000 +DDE2AA1 NNL3AA1 1 0 0.0000 10.000 0.000000 5000 +NNL2AA1 BBE3AA1 1 0 0.0000 10.000 0.000000 5000 +BBE2AA1 FFR3AA1 1 0 0.0000 10.000 0.000000 5000 +##T +BBE2AA1 BBE3AA1 1 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST +##R +BBE2AA1 BBE3AA1 1 -0.68 90.00 16 0 SYMM