Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2019, 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.searchtree.algorithms;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.commons.logs.OpenRaoLogger;
import com.powsybl.openrao.data.crac.api.State;
import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.openrao.data.crac.api.networkaction.NetworkAction;
import com.powsybl.openrao.searchtreerao.commons.NetworkActionCombination;
import com.powsybl.openrao.searchtreerao.commons.RaoLogger;
import com.powsybl.openrao.searchtreerao.commons.SensitivityComputer;
import com.powsybl.openrao.searchtreerao.commons.optimizationperimeters.GlobalOptimizationPerimeter;
import com.powsybl.openrao.searchtreerao.commons.optimizationperimeters.OptimizationPerimeter;
import com.powsybl.openrao.searchtreerao.commons.parameters.TreeParameters;
import com.powsybl.openrao.searchtreerao.result.api.OptimizationResult;
import com.powsybl.openrao.searchtreerao.result.api.PrePerimeterResult;
import com.powsybl.openrao.searchtreerao.result.api.RangeActionActivationResult;
import com.powsybl.openrao.searchtreerao.result.impl.RangeActionActivationResultImpl;
import com.powsybl.openrao.searchtreerao.searchtree.inputs.SearchTreeInput;
import com.powsybl.openrao.searchtreerao.searchtree.parameters.SearchTreeParameters;
import com.powsybl.openrao.sensitivityanalysis.AppliedRemedialActions;
import com.powsybl.openrao.util.AbstractNetworkPool;
import com.google.common.hash.Hashing;
import com.powsybl.iidm.network.Network;
import org.apache.commons.lang3.NotImplementedException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider.*;
import static com.powsybl.openrao.searchtreerao.castor.algorithm.AutomatonSimulator.getRangeActionsAndTheirTapsAppliedOnState;
/**
* The "tree" is one of the core object of the search-tree algorithm.
* It aims at finding a good combination of Network Actions.
*
* The tree is composed of leaves which evaluate the impact of Network Actions,
* one by one. The tree is orchestrating the leaves : it looks for a smart
* routing among the leaves in order to converge as quickly as possible to a local
* minimum of the objective function.
*
* The leaves of a same depth can be evaluated simultaneously.
*
* @author Joris Mancini {@literal }
* @author Baptiste Seguinot {@literal }
*/
public class SearchTree {
private static final int NUMBER_LOGGED_ELEMENTS_DURING_TREE = 2;
private static final int NUMBER_LOGGED_ELEMENTS_END_TREE = 5;
private static final int NUMBER_LOGGED_VIRTUAL_COSTLY_ELEMENTS = 10;
/**
* attribute defined in constructor of the search tree class
*/
private final SearchTreeInput input;
private final SearchTreeParameters parameters;
private final OpenRaoLogger topLevelLogger;
/**
* attribute defined and used within the class
*/
private final boolean purelyVirtual;
private final SearchTreeBloomer bloomer;
private Leaf rootLeaf;
private Leaf optimalLeaf;
private Leaf previousDepthOptimalLeaf;
private Optional combinationFulfillingStopCriterion = Optional.empty();
public SearchTree(SearchTreeInput input,
SearchTreeParameters parameters,
boolean verbose) {
// inputs
this.input = input;
this.parameters = parameters;
this.topLevelLogger = verbose ? BUSINESS_LOGS : TECHNICAL_LOGS;
// build from inputs
this.purelyVirtual = input.getOptimizationPerimeter().getOptimizedFlowCnecs().isEmpty();
this.bloomer = new SearchTreeBloomer(input, parameters);
}
public CompletableFuture run() {
initLeaves(input);
TECHNICAL_LOGS.debug("Evaluating root leaf");
rootLeaf.evaluate(input.getObjectiveFunction(), getSensitivityComputerForEvaluation(true));
if (rootLeaf.getStatus().equals(Leaf.Status.ERROR)) {
topLevelLogger.info("Could not evaluate leaf: {}", rootLeaf);
logOptimizationSummary(rootLeaf);
rootLeaf.finalizeOptimization();
return CompletableFuture.completedFuture(rootLeaf);
} else if (stopCriterionReached(rootLeaf)) {
topLevelLogger.info("Stop criterion reached on {}", rootLeaf);
RaoLogger.logMostLimitingElementsResults(topLevelLogger, rootLeaf, parameters.getObjectiveFunction(), NUMBER_LOGGED_ELEMENTS_END_TREE);
logOptimizationSummary(rootLeaf);
rootLeaf.finalizeOptimization();
return CompletableFuture.completedFuture(rootLeaf);
}
TECHNICAL_LOGS.info("{}", rootLeaf);
RaoLogger.logMostLimitingElementsResults(TECHNICAL_LOGS, rootLeaf, parameters.getObjectiveFunction(), NUMBER_LOGGED_ELEMENTS_DURING_TREE);
TECHNICAL_LOGS.info("Linear optimization on root leaf");
optimizeLeaf(rootLeaf);
topLevelLogger.info("{}", rootLeaf);
RaoLogger.logRangeActions(TECHNICAL_LOGS, optimalLeaf, input.getOptimizationPerimeter(), null);
RaoLogger.logMostLimitingElementsResults(topLevelLogger, optimalLeaf, parameters.getObjectiveFunction(), NUMBER_LOGGED_ELEMENTS_DURING_TREE);
logVirtualCostInformation(rootLeaf, "");
if (stopCriterionReached(rootLeaf)) {
logOptimizationSummary(rootLeaf);
rootLeaf.finalizeOptimization();
return CompletableFuture.completedFuture(rootLeaf);
}
iterateOnTree();
TECHNICAL_LOGS.info("Search-tree RAO completed with status {}", optimalLeaf.getSensitivityStatus());
TECHNICAL_LOGS.info("Best leaf: {}", optimalLeaf);
RaoLogger.logRangeActions(TECHNICAL_LOGS, optimalLeaf, input.getOptimizationPerimeter(), "Best leaf: ");
RaoLogger.logMostLimitingElementsResults(TECHNICAL_LOGS, optimalLeaf, parameters.getObjectiveFunction(), NUMBER_LOGGED_ELEMENTS_END_TREE);
logOptimizationSummary(optimalLeaf);
optimalLeaf.finalizeOptimization();
return CompletableFuture.completedFuture(optimalLeaf);
}
void initLeaves(SearchTreeInput input) {
rootLeaf = makeLeaf(input.getOptimizationPerimeter(), input.getNetwork(), input.getPrePerimeterResult(), input.getPreOptimizationAppliedRemedialActions());
optimalLeaf = rootLeaf;
previousDepthOptimalLeaf = rootLeaf;
}
Leaf makeLeaf(OptimizationPerimeter optimizationPerimeter, Network network, PrePerimeterResult prePerimeterOutput, AppliedRemedialActions appliedRemedialActionsInSecondaryStates) {
return new Leaf(optimizationPerimeter, network, prePerimeterOutput, appliedRemedialActionsInSecondaryStates);
}
private void logOptimizationSummary(Leaf optimalLeaf) {
State state = input.getOptimizationPerimeter().getMainOptimizationState();
RaoLogger.logOptimizationSummary(BUSINESS_LOGS, state, optimalLeaf.getActivatedNetworkActions(), getRangeActionsAndTheirTapsAppliedOnState(optimalLeaf, state), rootLeaf.getPreOptimObjectiveFunctionResult(), optimalLeaf);
logVirtualCostInformation(optimalLeaf, "");
}
private void iterateOnTree() {
int depth = 0;
boolean hasImproved = true;
if (input.getOptimizationPerimeter().getNetworkActions().isEmpty()) {
topLevelLogger.info("No network action available");
return;
}
int leavesInParallel = Math.min(input.getOptimizationPerimeter().getNetworkActions().size(), parameters.getTreeParameters().leavesInParallel());
TECHNICAL_LOGS.debug("Evaluating {} leaves in parallel", leavesInParallel);
try (AbstractNetworkPool networkPool = makeOpenRaoNetworkPool(input.getNetwork(), leavesInParallel)) {
while (depth < parameters.getTreeParameters().maximumSearchDepth() && hasImproved && !stopCriterionReached(optimalLeaf)) {
TECHNICAL_LOGS.info("Search depth {} [start]", depth + 1);
previousDepthOptimalLeaf = optimalLeaf;
updateOptimalLeafWithNextDepthBestLeaf(networkPool);
hasImproved = previousDepthOptimalLeaf != optimalLeaf; // It means this depth evaluation has improved the global cost
if (hasImproved) {
TECHNICAL_LOGS.info("Search depth {} [end]", depth + 1);
topLevelLogger.info("Search depth {} best leaf: {}", depth + 1, optimalLeaf);
RaoLogger.logRangeActions(TECHNICAL_LOGS, optimalLeaf, input.getOptimizationPerimeter(), String.format("Search depth %s best leaf: ", depth + 1));
RaoLogger.logMostLimitingElementsResults(topLevelLogger, optimalLeaf, parameters.getObjectiveFunction(), NUMBER_LOGGED_ELEMENTS_DURING_TREE);
} else {
topLevelLogger.info("No better result found in search depth {}, exiting search tree", depth + 1);
}
depth += 1;
if (depth >= parameters.getTreeParameters().maximumSearchDepth()) {
topLevelLogger.info("maximum search depth has been reached, exiting search tree");
}
}
networkPool.shutdownAndAwaitTermination(24, TimeUnit.HOURS);
} catch (InterruptedException e) {
TECHNICAL_LOGS.warn("A computation thread was interrupted");
Thread.currentThread().interrupt();
}
}
/**
* Evaluate all the leaves. We use OpenRaoNetworkPool to parallelize the computation
*/
private void updateOptimalLeafWithNextDepthBestLeaf(AbstractNetworkPool networkPool) throws InterruptedException {
TreeSet naCombinationsSorted = new TreeSet<>(this::deterministicNetworkActionCombinationComparison);
naCombinationsSorted.addAll(bloomer.bloom(optimalLeaf, input.getOptimizationPerimeter().getNetworkActions()));
int numberOfCombinations = naCombinationsSorted.size();
networkPool.initClones(numberOfCombinations);
if (naCombinationsSorted.isEmpty()) {
TECHNICAL_LOGS.info("No more network action available");
return;
} else {
TECHNICAL_LOGS.info("Leaves to evaluate: {}", numberOfCombinations);
}
AtomicInteger remainingLeaves = new AtomicInteger(numberOfCombinations);
List> tasks = naCombinationsSorted.stream().map(naCombination ->
networkPool.submit(() -> optimizeOneLeaf(networkPool, naCombination, remainingLeaves))
).toList();
for (ForkJoinTask