diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorContingencyScenarios.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorContingencyScenarios.java index fa77cdfa38..938ca7424a 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorContingencyScenarios.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/castor/algorithm/CastorContingencyScenarios.java @@ -120,7 +120,8 @@ private Object runScenario(PrePerimeterResult prePerimeterSensitivityOutput, boo AutomatonPerimeterResultImpl automatonResult = automatonSimulator.simulateAutomatonState(automatonState.get(), curativeStates, networkClone, stateTree, automatonTreeParameters); if (automatonResult.getComputationStatus() == ComputationStatus.FAILURE) { autoStateSensiFailed = true; - contingencyScenarioResults.put(automatonState.get(), new SkippedOptimizationResultImpl(automatonState.get(), automatonResult.getActivatedNetworkActions(), automatonResult.getActivatedRangeActions(automatonState.get()), ComputationStatus.FAILURE, sensitivityFailureOvercost)); + //contingencyScenarioResults.put(automatonState.get(), new SkippedOptimizationResultImpl(automatonState.get(), automatonResult.getActivatedNetworkActions(), automatonResult.getActivatedRangeActions(automatonState.get()), ComputationStatus.FAILURE, sensitivityFailureOvercost)); + contingencyScenarioResults.put(automatonState.get(), automatonResult); } else { contingencyScenarioResults.put(automatonState.get(), automatonResult); preCurativeResult = automatonResult.getPostAutomatonSensitivityAnalysisOutput(); 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 efa5c45046..336ca79c69 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 @@ -188,7 +188,12 @@ public CompletableFuture run() { BUSINESS_LOGS.info("Merging preventive and post-contingency RAO results:"); RaoLogger.logMostLimitingElementsResults(BUSINESS_LOGS, stateTree.getBasecaseScenario(), preventiveResult, stateTree.getContingencyScenarios(), postContingencyResults, raoParameters.getObjectiveFunctionParameters().getType(), NUMBER_LOGGED_ELEMENTS_END_RAO); } - + RaoLogger.logSensitivityAnalysisResults(String.format("Iteration %d: sensitivity analysis: ", 1), + prePerimeterSensitivityAnalysis.getObjectiveFunction(), + null, //TODO: Find the right remedialActionActivationResult if we want to use costly objective function not needed otherwise + preCurativeSensitivityAnalysisOutput, + raoParameters, + NUMBER_LOGGED_ELEMENTS_DURING_RAO); return postCheckResults(mergedRaoResults, initialOutput, raoParameters.getObjectiveFunctionParameters()); } catch (RuntimeException e) { BUSINESS_LOGS.error("{} \n {}", e.getMessage(), ExceptionUtils.getStackTrace(e)); diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/commons/RaoLogger.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/commons/RaoLogger.java index 1c9dc61516..5a35ecec7f 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/commons/RaoLogger.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/commons/RaoLogger.java @@ -70,6 +70,30 @@ public static void logSensitivityAnalysisResults(String prefix, numberOfLoggedLimitingElements); } + public static void logObjectifFunctionResult(String prefix, + ObjectiveFunctionResult objectiveFunctionResult, + PrePerimeterResult sensitivityAnalysisResult, + RaoParameters raoParameters, + int numberOfLoggedLimitingElements) { + + if (!BUSINESS_LOGS.isInfoEnabled()) { + return; + } + + Map virtualCostDetailed = getVirtualCostDetailed(objectiveFunctionResult); + + BUSINESS_LOGS.info(prefix + "cost = {} (functional: {}, virtual: {}{})", + formatDoubleBasedOnMargin(objectiveFunctionResult.getCost(), -objectiveFunctionResult.getCost()), + formatDoubleBasedOnMargin(objectiveFunctionResult.getFunctionalCost(), -objectiveFunctionResult.getCost()), + formatDoubleBasedOnMargin(objectiveFunctionResult.getVirtualCost(), -objectiveFunctionResult.getCost()), + virtualCostDetailed.isEmpty() ? "" : " " + virtualCostDetailed); + + RaoLogger.logMostLimitingElementsResults(BUSINESS_LOGS, + sensitivityAnalysisResult, + raoParameters.getObjectiveFunctionParameters().getType(), + numberOfLoggedLimitingElements); + } + public static void logRangeActions(OpenRaoLogger logger, Leaf leaf, OptimizationPerimeter diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/commons/marginevaluator/MarginEvaluatorWithMarginDecreaseUnoptimizedCnecs.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/commons/marginevaluator/MarginEvaluatorWithMarginDecreaseUnoptimizedCnecs.java index 58565ca43c..a38aa6acad 100644 --- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/commons/marginevaluator/MarginEvaluatorWithMarginDecreaseUnoptimizedCnecs.java +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/commons/marginevaluator/MarginEvaluatorWithMarginDecreaseUnoptimizedCnecs.java @@ -50,7 +50,7 @@ public double getMargin(FlowResult flowResult, FlowCnec flowCnec, TwoSides side, } private double computeMargin(FlowCnec flowCnec, double newMargin, double prePerimeterMargin) { - if (countriesNotToOptimize.contains(flowCnec.getOperator()) && newMargin > prePerimeterMargin - .0001 * Math.abs(prePerimeterMargin)) { + if (countriesNotToOptimize.contains(flowCnec.getOperator()) && newMargin > prePerimeterMargin - .0001 * Math.abs(prePerimeterMargin) && flowCnec.getState().getInstant().isCurative()) { return Double.MAX_VALUE; } return newMargin; diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/faorao/FastRao.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/faorao/FastRao.java new file mode 100644 index 0000000000..224bbb7f08 --- /dev/null +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/faorao/FastRao.java @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2020, 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 com.powsybl.openrao.searchtreerao.faorao; + +import com.google.auto.service.AutoService; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VariantManager; +import com.powsybl.openrao.commons.OpenRaoException; +import com.powsybl.openrao.commons.Unit; +import com.powsybl.openrao.data.crac.api.*; +import com.powsybl.openrao.data.crac.api.cnec.FlowCnec; +import com.powsybl.openrao.data.crac.api.parameters.CracCreationParameters; +import com.powsybl.openrao.data.crac.io.json.JsonExport; +import com.powsybl.openrao.data.crac.io.json.JsonImport; +import com.powsybl.openrao.data.raoresult.api.ComputationStatus; +import com.powsybl.openrao.data.raoresult.api.RaoResult; +import com.powsybl.openrao.raoapi.RaoInput; +import com.powsybl.openrao.raoapi.RaoProvider; +import com.powsybl.openrao.raoapi.parameters.RaoParameters; +import com.powsybl.openrao.raoapi.parameters.extensions.LoopFlowParametersExtension; +import com.powsybl.openrao.raoapi.parameters.extensions.RelativeMarginsParametersExtension; +import com.powsybl.openrao.searchtreerao.castor.algorithm.CastorFullOptimization; +import com.powsybl.openrao.searchtreerao.castor.algorithm.PrePerimeterSensitivityAnalysis; +import com.powsybl.openrao.searchtreerao.castor.algorithm.StateTree; +import com.powsybl.openrao.searchtreerao.commons.RaoLogger; +import com.powsybl.openrao.searchtreerao.commons.RaoUtil; +import com.powsybl.openrao.searchtreerao.commons.SensitivityComputer; +import com.powsybl.openrao.searchtreerao.commons.ToolProvider; +import com.powsybl.openrao.searchtreerao.commons.objectivefunction.ObjectiveFunction; +import com.powsybl.openrao.searchtreerao.result.api.*; +import com.powsybl.openrao.searchtreerao.result.impl.*; +import com.powsybl.openrao.sensitivityanalysis.AppliedRemedialActions; +import com.powsybl.openrao.util.AbstractNetworkPool; + +import java.io.*; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; + +import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.BUSINESS_LOGS; + +/** + * @author Joris Mancini {@literal } + * @author Philippe Edwards {@literal } + * @author Peter Mitri {@literal } + * @author Godelaine De-Montmorillon {@literal } + * @author Baptiste Seguinot {@literal } + */ +@AutoService(RaoProvider.class) +public class FastRao implements RaoProvider { + private static final String FAST_RAO = "FastRao"; + private static final int NUMBER_LOGGED_ELEMENTS_DURING_RAO = 2; + private static final int NUMBER_LOGGED_ELEMENTS_END_RAO = 10; + private static final int NUMBER_OF_CNECS_TO_ADD = 20; + private static final boolean ADD_UNSECURE_CNECS = false; + private static final double MARGIN_LIMIT = 5; + // Do not store any big object in this class as it is a static RaoProvider + // Objects stored in memory will not be released at the end of the RAO run + + @Override + public String getName() { + return FAST_RAO; + } + + @Override + public String getVersion() { + return "1.0.0"; + } + + @Override + public CompletableFuture run(RaoInput raoInput, RaoParameters parameters) { + return run(raoInput, parameters, null); + } + + @Override + public CompletableFuture run(RaoInput raoInput, RaoParameters parameters, Instant targetEndInstant) { + RaoUtil.initData(raoInput, parameters); + return CompletableFuture.completedFuture(launchFilteredRao(raoInput, parameters, targetEndInstant, new HashSet<>())); + } + + public static RaoResult launchFilteredRao(RaoInput raoInput, RaoParameters parameters, Instant targetEndInstant, Set consideredCnecs) { + + try { + // 1. Retrieve input data + Crac crac = raoInput.getCrac(); + Collection initialNetworkVariants = new HashSet<>(raoInput.getNetwork().getVariantManager().getVariantIds()); + + ToolProvider toolProvider = ToolProvider.buildFromRaoInputAndParameters(raoInput, parameters); + + PrePerimeterSensitivityAnalysis prePerimeterSensitivityAnalysis = new PrePerimeterSensitivityAnalysis( + crac.getFlowCnecs(), + crac.getRangeActions(), + parameters, + toolProvider); + + // 3. Run initial sensi (for initial values, and to know which cnecs to put in the first rao) + PrePerimeterResult initialResult = prePerimeterSensitivityAnalysis.runInitialSensitivityAnalysis(raoInput.getNetwork(), raoInput.getCrac()); + + if (initialResult.getSensitivityStatus() == ComputationStatus.FAILURE) { + BUSINESS_LOGS.error("Initial sensitivity analysis failed"); + return new FailedRaoResultImpl("Initial sensitivity analysis failed"); + } + + RaoLogger.logSensitivityAnalysisResults("Initial sensitivity analysis: ", + prePerimeterSensitivityAnalysis.getObjectiveFunction(), + RemedialActionActivationResultImpl.empty(initialResult), + initialResult, + parameters, + NUMBER_LOGGED_ELEMENTS_DURING_RAO); + + PrePerimeterResult stepResult = initialResult; + //computeAvailableRangeActions(initialResult, crac, network, parameters); + + FlowCnec worstCnec; + FastRaoResultImpl raoResult; + + com.powsybl.openrao.data.crac.api.Instant lastInstant = raoInput.getCrac().getLastInstant(); + AbstractNetworkPool networkPool = AbstractNetworkPool.create(raoInput.getNetwork(), raoInput.getNetworkVariantId(), 3, true); + int counter = 1; + do { + addWorstCnecs(consideredCnecs, NUMBER_OF_CNECS_TO_ADD, stepResult); + if (ADD_UNSECURE_CNECS) { + consideredCnecs.addAll(getUnsecureFunctionalCnecs(stepResult, parameters.getObjectiveFunctionParameters().getType().getUnit())); + } + consideredCnecs.addAll(getCostlyVirtualCnecs(stepResult)); + consideredCnecs.add(getWorstPreventiveCnec(stepResult, crac)); + cleanVariants(raoInput.getNetwork(), initialNetworkVariants); + + raoResult = runFilteredRao(raoInput, parameters, targetEndInstant, consideredCnecs, toolProvider, initialResult, networkPool, counter); + stepResult = raoResult.getAppropriateResult(lastInstant); + + RaoLogger.logObjectifFunctionResult(String.format("Iteration %d: sensitivity analysis: ", counter), + stepResult, //TODO: Find the right remedialActionActivationResult if we want to use costly objective function not needed otherwise + stepResult, + parameters, + NUMBER_LOGGED_ELEMENTS_DURING_RAO); + + worstCnec = stepResult.getMostLimitingElements(1).get(0); + counter++; + } while (!(consideredCnecs.contains(worstCnec) && consideredCnecs.containsAll(getCostlyVirtualCnecs(stepResult)))); + networkPool.shutdownAndAwaitTermination(24, TimeUnit.HOURS); + + RaoLogger.logObjectifFunctionResult("Final Result: ", + stepResult, //TODO: Find the right remedialActionActivationResult if we want to use costly objective function not needed otherwise + stepResult, + parameters, + NUMBER_LOGGED_ELEMENTS_DURING_RAO); + + return raoResult; + } catch (IOException e) { + throw new UncheckedIOException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private static void addWorstCnecs(Set consideredCnecs, int numberOfCnecsToAdd, PrePerimeterResult ofResult) { + List orderedCnecs = ofResult.getMostLimitingElements(Integer.MAX_VALUE); + int counter = 0; + for (FlowCnec cnec : orderedCnecs) { + if (counter >= numberOfCnecsToAdd) { + return; + } + if (consideredCnecs.add(cnec)) { + counter++; + } + } + } + + private static void cleanVariants(Network network, Collection initialNetworkVariants) { + VariantManager variantManager = network.getVariantManager(); + Set variantsToRemove = new HashSet<>(); + variantManager.getVariantIds().stream() + .filter(id -> !initialNetworkVariants.contains(id)) + .forEach(variantsToRemove::add); + variantsToRemove.forEach(variantManager::removeVariant); + } + + private static FlowCnec getWorstPreventiveCnec(ObjectiveFunctionResult ofResult, Crac crac) { + List orderedCnecs = ofResult.getMostLimitingElements(Integer.MAX_VALUE); + return orderedCnecs.stream().filter(cnec -> cnec.getState().isPreventive()).findFirst().orElse( + // If only MNECs are present previous list will be empty + crac.getFlowCnecs(crac.getPreventiveState()).stream().findFirst().orElseThrow() + ); + } + + private static Set getUnsecureFunctionalCnecs(PrePerimeterResult prePerimeterResult, Unit unit) { + List orderedCnecs = prePerimeterResult.getMostLimitingElements(Integer.MAX_VALUE); + Set flowCnecs = new HashSet<>(); + for (FlowCnec cnec : orderedCnecs) { + if (prePerimeterResult.getMargin(cnec, unit) < MARGIN_LIMIT) { + flowCnecs.add(cnec); + } + } + return flowCnecs; + } + + private static Set getCostlyVirtualCnecs(ObjectiveFunctionResult ofResult) { + Set flowCnecs = new HashSet<>(); + ofResult.getVirtualCostNames().forEach(name -> flowCnecs.addAll(ofResult.getCostlyElements(name, Integer.MAX_VALUE))); + return flowCnecs; + } + + private static FastRaoResultImpl runFilteredRao(RaoInput raoInput, RaoParameters parameters, Instant targetEndInstant, Set flowCnecsToKeep, ToolProvider toolProvider, PrePerimeterResult initialResult, AbstractNetworkPool networkPool, int counter) throws IOException, InterruptedException { + Crac crac = raoInput.getCrac(); + // 4. Filter CRAC to only keep the worst CNECs + Crac filteredCrac = copyCrac(crac, raoInput.getNetwork()); + removeFlowCnecsFromCrac(filteredCrac, flowCnecsToKeep); + + BUSINESS_LOGS.info("***** Iteration {}: Run filtered RAO [start]", counter); + + RaoInput filteredRaoInput = createFilteredRaoInput(raoInput, filteredCrac); + RaoResult raoResult; + try { + raoResult = new CastorFullOptimization(filteredRaoInput, parameters, targetEndInstant).run().get(); + List preventiveNetworkActions = raoResult.getActivatedNetworkActionsDuringState(crac.getPreventiveState()).stream() + .map(Identifiable::getId) + .toList(); + if (preventiveNetworkActions.size() >= 2) { + List> predefinedCombinations = parameters.getTopoOptimizationParameters().getPredefinedCombinations(); + if (!preventiveNetworkActions.contains(preventiveNetworkActions)) { + predefinedCombinations.add(preventiveNetworkActions); + } + parameters.getTopoOptimizationParameters().setPredefinedCombinations(predefinedCombinations); + } + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + + BUSINESS_LOGS.info("***** Iteration {}: Run filtered RAO [end]", counter); + + String finalVariantId = raoInput.getNetwork().getVariantManager().getWorkingVariantId(); + raoInput.getNetwork().getVariantManager().setWorkingVariant(raoInput.getNetworkVariantId()); + // 6. Apply / Force optimal RAs found on filter RAO + // 7. Run RAO with applied/forced RAs + BUSINESS_LOGS.info("***** Iteration {}: Run full sensitivity analysis [start]", counter); + //TODO: Semaphores won't quite work with the current implementation to parallelize the loadflows: + // eg if we applied PRAs, we need to give the post PRA result to the autoSensi etc, so the sensitivities are not parallelized + // Some options: + // - have a runBasedOnInitialAndPrePerimResults that takes a semaphore to be able to run a sensi, and then wait for the semaphore to build the result + // - ideally use the security analysis for faster runs anyways => rewrite the method completely + // (for now option 1) + // Solution selected : Do the three Sensitivity computation in parallel, wait for all of them to finish then compute objective function sequentially + + AtomicReference postPraSensi = new AtomicReference<>(); + AtomicReference postAraSensi = new AtomicReference<>(); + AtomicReference postCraSensi = new AtomicReference<>(); + + ForkJoinTask task1 = networkPool.submit(() -> { + try { + Network networkCopy = networkPool.getAvailableNetwork(); + applyOptimalPreventiveRemedialActions(networkCopy, filteredCrac.getPreventiveState(), raoResult); + postPraSensi.set(runBasedOnInitialResults(toolProvider, raoInput, networkCopy, parameters, crac.getFlowCnecs(), new AppliedRemedialActions(), initialResult)); + networkPool.releaseUsedNetwork(networkCopy); + + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + + // TODO: Use list of tasks like everywhere else in the codebase + ForkJoinTask task2 = networkPool.submit(() -> { + try { + Network networkCopy = networkPool.getAvailableNetwork(); + applyOptimalPreventiveRemedialActions(networkCopy, filteredCrac.getPreventiveState(), raoResult); + AppliedRemedialActions appliedAutoRemedialActions = createAutoAppliedRemedialActionsFromRaoResult(filteredCrac, raoResult); + postAraSensi.set(runBasedOnInitialResults(toolProvider, raoInput, networkCopy, parameters, crac.getFlowCnecs(), appliedAutoRemedialActions, initialResult)); + networkPool.releaseUsedNetwork(networkCopy); + + } catch (OpenRaoException | InterruptedException e) { + throw new RuntimeException(e); + } + }); + + ForkJoinTask task3 = networkPool.submit(() -> { + try { + Network networkCopy = networkPool.getAvailableNetwork(); + applyOptimalPreventiveRemedialActions(networkCopy, filteredCrac.getPreventiveState(), raoResult); + AppliedRemedialActions appliedRemedialActions = createAppliedRemedialActionsFromRaoResult(filteredCrac, raoResult); + postCraSensi.set(runBasedOnInitialResults(toolProvider, raoInput, networkCopy, parameters, raoInput.getCrac().getFlowCnecs(), appliedRemedialActions, initialResult)); + networkPool.releaseUsedNetwork(networkCopy); + } catch (InterruptedException | OpenRaoException e) { + throw new RuntimeException(e); + } + }); + + task1.join(); + task2.join(); + task3.join(); + + raoInput.getNetwork().getVariantManager().setWorkingVariant(finalVariantId); + + postPraSensi.set(getCompletePrePerimeterSensitivityResultImpl(toolProvider, raoInput, parameters, raoInput.getCrac().getFlowCnecs(), initialResult, initialResult, postPraSensi.get())); + postAraSensi.set(getCompletePrePerimeterSensitivityResultImpl(toolProvider, raoInput, parameters, raoInput.getCrac().getFlowCnecs(), initialResult, postPraSensi.get(), postAraSensi.get())); + postCraSensi.set(getCompletePrePerimeterSensitivityResultImpl(toolProvider, raoInput, parameters, raoInput.getCrac().getFlowCnecs(), initialResult, postAraSensi.get(), postCraSensi.get())); + + BUSINESS_LOGS.info("***** Iteration {}: Run full sensitivity analysis [end]", counter); + + return new FastRaoResultImpl(initialResult, postPraSensi.get(), postAraSensi.get(), postCraSensi.get(), raoResult, raoInput.getCrac()); + } + + private static RaoInput createFilteredRaoInput(RaoInput raoInput, Crac filteredCrac) { + return RaoInput.build(raoInput.getNetwork(), filteredCrac) + .withPerimeter(raoInput.getPerimeter()) + .withGlskProvider(raoInput.getGlskProvider()) + .withRefProg(raoInput.getReferenceProgram()) + .withNetworkVariantId(raoInput.getNetworkVariantId()) + .build(); + } + + public static Crac copyCrac(Crac crac, Network network) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + new JsonExport().exportData(crac, outputStream); + ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + return new JsonImport().importData(inputStream, new CracCreationParameters(), network, null).getCrac(); + } + + // Caution: We might remove CNECs associated to RAs with onConstraint usageRule + public static void removeFlowCnecsFromCrac(Crac crac, Collection flowCnecsToKeep) { + List flowCnecsToRemove = crac.getFlowCnecs().stream().filter(fc -> !flowCnecsToKeep.contains(fc)).toList(); + // Remove FlowCNECs + Set flowCnecsToRemoveIds = new HashSet<>(); + flowCnecsToRemove.forEach(cnec -> flowCnecsToRemoveIds.add(cnec.getId())); + crac.removeFlowCnecs(flowCnecsToRemoveIds); + } + + private static void applyOptimalPreventiveRemedialActions(Network networkCopy, State state, RaoResult raoResult) { + raoResult.getActivatedRangeActionsDuringState(state).forEach(rangeAction -> rangeAction.apply(networkCopy, raoResult.getOptimizedSetPointOnState(state, rangeAction))); + raoResult.getActivatedNetworkActionsDuringState(state).forEach(networkAction -> networkAction.apply(networkCopy)); + } + + private static AppliedRemedialActions createAppliedRemedialActionsFromRaoResult(Crac crac, RaoResult raoResult) { + if (raoResult instanceof OneStateOnlyRaoResultImpl) { + return new AppliedRemedialActions(); + } + AppliedRemedialActions appliedRemedialActions = new AppliedRemedialActions(); + crac.getStates().stream().filter(state -> !state.isPreventive() && !state.getInstant().getKind().equals(InstantKind.OUTAGE)).forEach(state -> { + appliedRemedialActions.addAppliedNetworkActions(state, raoResult.getActivatedNetworkActionsDuringState(state)); + if (!raoResult.getActivatedRangeActionsDuringState(state).isEmpty()) { + appliedRemedialActions.addAppliedRangeActions(state, raoResult.getOptimizedSetPointsOnState(state)); + } + } + ); + return appliedRemedialActions; + } + + private static AppliedRemedialActions createAutoAppliedRemedialActionsFromRaoResult(Crac crac, RaoResult raoResult) { + if (raoResult instanceof OneStateOnlyRaoResultImpl) { + return new AppliedRemedialActions(); + } + AppliedRemedialActions appliedRemedialActions = new AppliedRemedialActions(); + + crac.getStates().stream().filter(state -> state.getInstant().getKind().equals(InstantKind.AUTO)).forEach(state -> { + appliedRemedialActions.addAppliedNetworkActions(state, raoResult.getActivatedNetworkActionsDuringState(state)); + appliedRemedialActions.addAppliedRangeActions(state, raoResult.getOptimizedSetPointsOnState(state)); + }); + return appliedRemedialActions; + } + + private static PrePerimeterSensitivityResultImpl runBasedOnInitialResults(ToolProvider toolProvider, + RaoInput raoInput, + Network network, + RaoParameters raoParameters, + Set flowCnecs, + AppliedRemedialActions appliedRemedialActions, + PrePerimeterResult initialFlowResult) throws InterruptedException { + Crac crac = raoInput.getCrac(); + SensitivityComputer.SensitivityComputerBuilder sensitivityComputerBuilder = SensitivityComputer.create() + .withToolProvider(toolProvider) + .withCnecs(flowCnecs) + .withRangeActions(crac.getRangeActions()) + .withOutageInstant(raoInput.getCrac().getOutageInstant()); + + if (raoParameters.hasExtension(LoopFlowParametersExtension.class)) { + if (raoParameters.getExtension(LoopFlowParametersExtension.class).getPtdfApproximation().shouldUpdatePtdfWithTopologicalChange()) { + sensitivityComputerBuilder.withCommercialFlowsResults(toolProvider.getLoopFlowComputation(), toolProvider.getLoopFlowCnecs(flowCnecs)); + } else { + sensitivityComputerBuilder.withCommercialFlowsResults(initialFlowResult); + } + } + if (raoParameters.getObjectiveFunctionParameters().getType().relativePositiveMargins()) { + if (raoParameters.getExtension(RelativeMarginsParametersExtension.class).getPtdfApproximation().shouldUpdatePtdfWithTopologicalChange()) { + sensitivityComputerBuilder.withPtdfsResults(toolProvider.getAbsolutePtdfSumsComputation(), flowCnecs); + } else { + sensitivityComputerBuilder.withPtdfsResults(initialFlowResult); + } + } + if (appliedRemedialActions != null) { + // for 2nd preventive initial sensi + sensitivityComputerBuilder.withAppliedRemedialActions(appliedRemedialActions); + } + SensitivityComputer sensitivityComputer = sensitivityComputerBuilder.build(); + sensitivityComputer.compute(network); + + FlowResult flowResult = sensitivityComputer.getBranchResult(network); + SensitivityResult sensitivityResult = sensitivityComputer.getSensitivityResult(); + RangeActionSetpointResult rangeActionSetpointResult = RangeActionSetpointResultImpl.buildWithSetpointsFromNetwork(network, crac.getRangeActions()); + + return new PrePerimeterSensitivityResultImpl( + flowResult, + sensitivityResult, + rangeActionSetpointResult, + null //complete later + ); + } + + private static PrePerimeterResult getCompletePrePerimeterSensitivityResultImpl(ToolProvider toolProvider, + RaoInput raoInput, + RaoParameters raoParameters, + Set flowCnecs, + PrePerimeterResult initialFlowResult, + PrePerimeterResult prePerimeterResult, + PrePerimeterResult currentPrePerimeterResult) { + + Crac crac = raoInput.getCrac(); + ObjectiveFunction objectiveFunction = ObjectiveFunction.build(flowCnecs, + toolProvider.getLoopFlowCnecs(flowCnecs), + initialFlowResult, + prePerimeterResult.getFlowResult(), + new StateTree(crac).getOperatorsNotSharingCras(), + raoParameters, + Set.of()); //TODO: To complete later if we want to use costly objective function not needed otherwise + + ObjectiveFunctionResult objectiveFunctionResult = objectiveFunction.evaluate( + currentPrePerimeterResult.getFlowResult(), + null //TODO: Find the right remedialActionActivationResult if we want to use costly objective function not needed otherwise + ); + + return new PrePerimeterSensitivityResultImpl( + currentPrePerimeterResult.getFlowResult(), + currentPrePerimeterResult.getSensitivityResult(), + currentPrePerimeterResult.getRangeActionSetpointResult(), + objectiveFunctionResult + ); + } + + private static boolean anyActionActivatedDuringInstantKind(RaoResult raoResult, InstantKind instantKind, Crac crac) { + if (instantKind.equals(InstantKind.PREVENTIVE)) { + State preventiveState = crac.getPreventiveState(); + return !(raoResult.getActivatedNetworkActionsDuringState(preventiveState).isEmpty() && raoResult.getActivatedRangeActionsDuringState(preventiveState).isEmpty()); + } + if (raoResult instanceof OneStateOnlyRaoResultImpl) { + return false; + } + + return crac.getStates().stream() + .filter(state -> state.getInstant().getKind().equals(instantKind)) + .anyMatch(state -> + !(raoResult.getActivatedNetworkActionsDuringState(state).isEmpty() && raoResult.getActivatedRangeActionsDuringState(state).isEmpty()) + ); + } +} diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/FastRaoResultImpl.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/FastRaoResultImpl.java new file mode 100644 index 0000000000..10028dec50 --- /dev/null +++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/FastRaoResultImpl.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2021, 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 com.powsybl.openrao.searchtreerao.result.impl; + +import com.powsybl.openrao.commons.OpenRaoException; +import com.powsybl.openrao.commons.PhysicalParameter; +import com.powsybl.openrao.commons.Unit; +import com.powsybl.openrao.data.crac.api.*; +import com.powsybl.openrao.data.crac.api.cnec.FlowCnec; +import com.powsybl.openrao.data.crac.api.networkaction.NetworkAction; +import com.powsybl.openrao.data.crac.api.rangeaction.PstRangeAction; +import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction; +import com.powsybl.openrao.data.raoresult.api.ComputationStatus; +import com.powsybl.openrao.data.raoresult.api.RaoResult; +import com.powsybl.openrao.searchtreerao.result.api.*; + +import com.powsybl.iidm.network.TwoSides; + +import java.util.*; + +import static com.powsybl.openrao.data.raoresult.api.ComputationStatus.*; + +/** + * @author Philippe Edwards {@literal } + */ +public class FastRaoResultImpl implements RaoResult { + private final PrePerimeterResult initialResult; + private final PrePerimeterResult afterPraResult; + private final PrePerimeterResult afterAraResult; + private final PrePerimeterResult finalResult; + private final RaoResult filteredRaoResult; + private final Crac crac; + private String executionDetails; + + public FastRaoResultImpl(PrePerimeterResult initialResult, + PrePerimeterResult afterPraResult, + PrePerimeterResult afterAraResult, + PrePerimeterResult finalResult, + RaoResult filteredRaoResult, + Crac crac) { + this.initialResult = initialResult; + this.afterPraResult = afterPraResult; + this.afterAraResult = afterAraResult; + this.finalResult = finalResult; + this.filteredRaoResult = filteredRaoResult; + this.crac = crac; + executionDetails = filteredRaoResult.getExecutionDetails(); + removeFailingContingencies(initialResult, afterPraResult, afterAraResult, finalResult, crac); + } + + private static void removeFailingContingencies(PrePerimeterResult initialResult, PrePerimeterResult afterPraResult, PrePerimeterResult afterAraResult, PrePerimeterResult finalResult, Crac crac) { + Set failingContingencies = new HashSet<>(); + crac.getStates().stream().filter(state -> initialResult.getComputationStatus(state) == FAILURE && !state.isPreventive()) + .forEach(state -> failingContingencies.add(state.getContingency().get().getId())); + crac.getStates().stream().filter(state -> afterPraResult.getComputationStatus(state) == FAILURE && !state.isPreventive()) + .forEach(state -> failingContingencies.add(state.getContingency().get().getId())); + crac.getStates().stream().filter(state -> afterAraResult.getComputationStatus(state) == FAILURE && !state.isPreventive()) + .forEach(state -> failingContingencies.add(state.getContingency().get().getId())); + crac.getStates().stream().filter(state -> finalResult.getComputationStatus(state) == FAILURE && !state.isPreventive()) + .forEach(state -> failingContingencies.add(state.getContingency().get().getId())); + initialResult.excludeContingencies(failingContingencies); + afterPraResult.excludeContingencies(failingContingencies); + afterAraResult.excludeContingencies(failingContingencies); + finalResult.excludeContingencies(failingContingencies); + } + + @Override + public ComputationStatus getComputationStatus() { + //TODO: PreventivAndCurativesRaoResult has a postContingencyResults object that we go through to evaluate the PARTIAL_FAILURE status. Understand why this object is not defined here. + if (initialResult.getSensitivityStatus() == FAILURE) { + return FAILURE; + } + if (initialResult.getSensitivityStatus() == PARTIAL_FAILURE || + finalResult == null || finalResult.getSensitivityStatus() != DEFAULT || + afterPraResult == null || afterPraResult.getSensitivityStatus() != DEFAULT || + afterAraResult == null || afterAraResult.getSensitivityStatus() != DEFAULT + ) { + return PARTIAL_FAILURE; + } + return DEFAULT; + + } + + @Override + public ComputationStatus getComputationStatus(State state) { + return getAppropriateResult(state.getInstant()).getComputationStatus(state); + } + + public PrePerimeterResult getAppropriateResult(Instant optimizedInstant) { + if (optimizedInstant == null) { + return initialResult; + } + if (optimizedInstant.isPreventive() || optimizedInstant.isOutage()) { + return afterPraResult; + } + if (optimizedInstant.isAuto()) { + return afterAraResult; + } + if (optimizedInstant.isCurative()) { + return finalResult; + } + throw new OpenRaoException(String.format("Optimized instant %s was not recognized", optimizedInstant)); + } + + public PrePerimeterResult getAppropriateResult(Instant optimizedInstant, FlowCnec flowCnec) { + if (Objects.isNull(optimizedInstant)) { + return initialResult; + } + Instant minInstant = optimizedInstant.comesBefore(flowCnec.getState().getInstant()) ? + optimizedInstant : flowCnec.getState().getInstant(); + return getAppropriateResult(minInstant); + } + + @Override + public double getMargin(Instant optimizedInstant, FlowCnec flowCnec, Unit unit) { + return getAppropriateResult(optimizedInstant, flowCnec).getMargin(flowCnec, unit); + } + + @Override + public double getRelativeMargin(Instant optimizedInstant, FlowCnec flowCnec, Unit unit) { + return getAppropriateResult(optimizedInstant, flowCnec).getRelativeMargin(flowCnec, unit); + } + + @Override + public double getFlow(Instant optimizedInstant, FlowCnec flowCnec, TwoSides side, Unit unit) { + return getAppropriateResult(optimizedInstant, flowCnec).getFlow(flowCnec, side, unit); + } + + @Override + public double getCommercialFlow(Instant optimizedInstant, FlowCnec flowCnec, TwoSides side, Unit unit) { + return getAppropriateResult(optimizedInstant, flowCnec).getCommercialFlow(flowCnec, side, unit); + } + + @Override + public double getLoopFlow(Instant optimizedInstant, FlowCnec flowCnec, TwoSides side, Unit unit) { + return getAppropriateResult(optimizedInstant, flowCnec).getLoopFlow(flowCnec, side, unit); + } + + @Override + public double getPtdfZonalSum(Instant optimizedInstant, FlowCnec flowCnec, TwoSides side) { + return getAppropriateResult(optimizedInstant, flowCnec).getPtdfZonalSum(flowCnec, side); + } + + @Override + public double getFunctionalCost(Instant optimizedInstant) { + return getAppropriateResult(optimizedInstant).getFunctionalCost(); + } + + public List getMostLimitingElements(Instant optimizedInstant, int number) { + return getAppropriateResult(optimizedInstant).getMostLimitingElements(number); + } + + @Override + public double getVirtualCost(Instant optimizedInstant) { + return getAppropriateResult(optimizedInstant).getVirtualCost(); + } + + @Override + public Set getVirtualCostNames() { + Set virtualCostNames = new HashSet<>(); + if (initialResult.getVirtualCostNames() != null) { + virtualCostNames.addAll(initialResult.getVirtualCostNames()); + } + if (finalResult.getVirtualCostNames() != null) { + virtualCostNames.addAll(finalResult.getVirtualCostNames()); + } + return virtualCostNames; + } + + @Override + public double getVirtualCost(Instant optimizedInstant, String virtualCostName) { + return getAppropriateResult(optimizedInstant).getVirtualCost(virtualCostName); + } + + public List getCostlyElements(Instant optimizedInstant, String virtualCostName, int number) { + return getAppropriateResult(optimizedInstant).getCostlyElements(virtualCostName, number); + } + + @Override + public boolean isActivatedDuringState(State state, RemedialAction remedialAction) { + if (remedialAction instanceof NetworkAction networkAction) { + return isActivatedDuringState(state, networkAction); + } else if (remedialAction instanceof RangeAction rangeAction) { + return isActivatedDuringState(state, rangeAction); + } else { + throw new OpenRaoException("Unrecognized remedial action type"); + } + } + + @Override + public boolean wasActivatedBeforeState(State state, NetworkAction networkAction) { + return filteredRaoResult.wasActivatedBeforeState(state, networkAction); + } + + @Override + public boolean isActivatedDuringState(State state, NetworkAction networkAction) { + return filteredRaoResult.isActivatedDuringState(state, networkAction); + } + + @Override + public Set getActivatedNetworkActionsDuringState(State state) { + return filteredRaoResult.getActivatedNetworkActionsDuringState(state); + } + + @Override + public boolean isActivatedDuringState(State state, RangeAction rangeAction) { + return filteredRaoResult.isActivatedDuringState(state, rangeAction); + } + + @Override + public int getPreOptimizationTapOnState(State state, PstRangeAction pstRangeAction) { + return filteredRaoResult.getPreOptimizationTapOnState(state, pstRangeAction); + } + + @Override + public int getOptimizedTapOnState(State state, PstRangeAction pstRangeAction) { + return filteredRaoResult.getOptimizedTapOnState(state, pstRangeAction); + } + + @Override + public double getPreOptimizationSetPointOnState(State state, RangeAction rangeAction) { + return filteredRaoResult.getPreOptimizationSetPointOnState(state, rangeAction); + } + + @Override + public double getOptimizedSetPointOnState(State state, RangeAction rangeAction) { + return filteredRaoResult.getOptimizedSetPointOnState(state, rangeAction); + } + + @Override + public Set> getActivatedRangeActionsDuringState(State state) { + return filteredRaoResult.getActivatedRangeActionsDuringState(state); + } + + @Override + public Map getOptimizedTapsOnState(State state) { + return filteredRaoResult.getOptimizedTapsOnState(state); + + } + + @Override + public Map, Double> getOptimizedSetPointsOnState(State state) { + return filteredRaoResult.getOptimizedSetPointsOnState(state); + } + + @Override + public String getExecutionDetails() { + return executionDetails; + } + + @Override + public void setExecutionDetails(String executionDetails) { + this.executionDetails = executionDetails; + } + + @Override + public boolean isSecure(Instant optimizedInstant, PhysicalParameter... u) { + if (ComputationStatus.FAILURE.equals(getComputationStatus())) { + return false; + } + return getFunctionalCost(optimizedInstant) < 0; + } + + @Override + public boolean isSecure(PhysicalParameter... u) { + return isSecure(crac.getLastInstant(), u); + } + +} diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/SearchTreeRaoSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/SearchTreeRaoSteps.java index a2d2af8bec..d16532ff66 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/SearchTreeRaoSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/SearchTreeRaoSteps.java @@ -52,6 +52,7 @@ public class SearchTreeRaoSteps { private static final String SEARCH_TREE_RAO = "SearchTreeRao"; + private static final String FAST_RAO = "FastRao"; private static final double TOLERANCE_FLOW_IN_AMPERE = 5.0; private static final double TOLERANCE_FLOW_IN_MEGAWATT = 5.0; private static final double TOLERANCE_FLOW_RELATIVE = 1.5 / 100; @@ -84,7 +85,7 @@ public void iLaunchSearchTreeRaoWithTimeLimit(int timeLimit) { @When("I launch search_tree_rao at {string}") public void iLaunchSearchTreeRao(String timestamp) { - launchRao(null, null, timestamp, SEARCH_TREE_RAO); + launchRao(null, null, timestamp, FAST_RAO); } @When("I launch search_tree_rao at {string} on {string}") @@ -114,12 +115,12 @@ public void iLaunchSearchTreeRao(String contingencyId, String instantKind) { @When("I launch loopflow search_tree_rao with default loopflow limit as {double} percent of pmax") public void iLaunchSearchTreeRaoWithDefaultLoopflowLimit(double percentage) { - launchRao(null, null, null, percentage, SEARCH_TREE_RAO, null); + launchRao(null, null, null, percentage, FAST_RAO, null); } @When("I launch loopflow search_tree_rao at {string} with default loopflow limit as {double} percent of pmax") public void iLaunchSearchTreeRaoWithDefaultLoopflowLimit(String timestamp, double percentage) { - launchRao(null, null, timestamp, percentage, SEARCH_TREE_RAO, null); + launchRao(null, null, timestamp, percentage, FAST_RAO, null); } @When("I launch loopflow_computation with OpenLoadFlow") @@ -619,7 +620,7 @@ public void absPtdfSumAfterInstant(String cnecId, String instantKind, Double exp } private void launchRao(int timeLimit) { - launchRao(null, null, null, null, SEARCH_TREE_RAO, timeLimit); + launchRao(null, null, null, null, FAST_RAO, timeLimit); } private void launchRao(String contingencyId, InstantKind instantKind, String timestamp, String raoType) {