All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.powsybl.openrao.searchtreerao.castor.algorithm.StateTree Maven / Gradle / Ivy

The newest version!
/*
 * 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.castor.algorithm;

import com.powsybl.contingency.Contingency;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.Instant;
import com.powsybl.openrao.data.crac.api.InstantKind;
import com.powsybl.openrao.data.crac.api.RemedialAction;
import com.powsybl.openrao.data.crac.api.State;
import com.powsybl.openrao.data.crac.api.cnec.Cnec;
import org.apache.commons.lang3.tuple.Pair;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author Thomas Bouquet {@literal }
 * @author Philippe Edwards {@literal }
 * @author Joris Mancini {@literal }
 */
public class StateTree {

    private final Set operatorsNotSharingCras;
    private final Perimeter preventivePerimeter;
    private final Set contingencyScenarios = new HashSet<>();

    public StateTree(Crac crac) {
        preventivePerimeter = new Perimeter(crac.getPreventiveState(), null);

        for (Contingency contingency : crac.getContingencies()) {
            processOutageInstant(contingency, crac);
            processAutoAndCurativeInstants(contingency, crac);
        }

        this.operatorsNotSharingCras = findOperatorsNotSharingCras(crac);
    }

    /**
     * Process OUTAGE state for a given contingency.
     * If the state has RAs, the case is not supported by Open RAO.
     * Else, the state is optimized in basecase RAO.
     */
    private void processOutageInstant(Contingency contingency, Crac crac) {
        State outageState = crac.getState(contingency.getId(), crac.getOutageInstant());
        if (outageState != null) {
            if (anyAvailableRemedialAction(crac, outageState)) {
                throw new OpenRaoException(String.format("Outage state %s has available RAs. This is not supported.", outageState));
            } else {
                preventivePerimeter.addOtherState(outageState);
            }
        }
    }

    /**
     * Process AUTO and CURATIVE states for a given contingency.
     * If the state has RAs in AUTO but not in CURATIVE, the case is not supported by Open RAO.
     * If the state has AUTO and CURATIVE RAs, both states will be treated in a dedicated scenario.
     * If the AUTO has no RA but the CURATIVE has RAs, the AUTO will be optimized in basecase RAO and the CURATIVE in a dedicated scenario.
     * If neither AUTO nor CURATIVE states have RAs, they will be optimized in basecase RAO.
     * 

* If AUTO or CURATIVE state does not exist, it will not be optimized. */ private void processAutoAndCurativeInstants(Contingency contingency, Crac crac) { ContingencyScenario.ContingencyScenarioBuilder contingencyScenarioBuilder = ContingencyScenario.create().withContingency(contingency); Pair autoInstantHasCnecsAndRemedialActions = processAutoInstant(contingency, crac, contingencyScenarioBuilder); Perimeter defaultPerimeter = getDefaultPerimeter(contingency, crac, autoInstantHasCnecsAndRemedialActions.getRight()); boolean scenarioHasCurativeStates = false; if (defaultPerimeter != null) { scenarioHasCurativeStates = processCurativeInstants(contingency, crac, contingencyScenarioBuilder, defaultPerimeter, autoInstantHasCnecsAndRemedialActions.getLeft()); } if (Boolean.TRUE.equals(autoInstantHasCnecsAndRemedialActions.getLeft()) && Boolean.TRUE.equals(autoInstantHasCnecsAndRemedialActions.getRight()) || scenarioHasCurativeStates) { contingencyScenarios.add(contingencyScenarioBuilder.build()); } } /** * Returns the default perimeter to which all curative CNECs that have no associated CRAs must be added * @param contingency: the scenario's contingency * @param crac: the input CRAC * @param automatonRemedialActionsExist: whether auto remedial actions were added to the CRAC or not * @return *

    *
  • preventivePerimeter if no ARAs exist
  • *
  • a perimeter with an optimisation state corresponding to the first curative instant
  • *
  • null if no curative instant is defined
  • *
*/ private Perimeter getDefaultPerimeter(Contingency contingency, Crac crac, boolean automatonRemedialActionsExist) { if (!automatonRemedialActionsExist) { return preventivePerimeter; } return crac.getStates(contingency) .stream() .filter(state -> state.getInstant().isCurative()) .filter(state -> anyCnec(crac, state)) .sorted() .findFirst() .map(state -> new Perimeter(state, new HashSet<>())) .orElse(null); } private Pair processAutoInstant(Contingency contingency, Crac crac, ContingencyScenario.ContingencyScenarioBuilder contingencyScenarioBuilder) { State automatonState = crac.hasAutoInstant() ? crac.getState(contingency.getId(), crac.getInstant(InstantKind.AUTO)) : null; Pair autoInstantHasCnecsAndRemedialActions = stateHasCnecsAndRemedialActions(crac, automatonState); boolean autoCnecsExist = autoInstantHasCnecsAndRemedialActions.getLeft(); boolean autoRemedialActionsExist = autoInstantHasCnecsAndRemedialActions.getRight(); if (autoCnecsExist && !autoRemedialActionsExist) { // the auto CNECs must be added to the preventive perimeter because no ARAs can affect them preventivePerimeter.addOtherState(automatonState); } else if (autoRemedialActionsExist) { contingencyScenarioBuilder.withAutomatonState(automatonState); } return autoInstantHasCnecsAndRemedialActions; } private Pair stateHasCnecsAndRemedialActions(Crac crac, State state) { return state != null ? Pair.of(anyCnec(crac, state), anyAvailableRemedialAction(crac, state)) : Pair.of(false, false); } /** * Process the CURATIVE instants. *

* For each curative instant with CNECs, the nearest previous curative instant with CRAs is used as the optimisation * instant for these CNECs. If no such instant exists, the CNECs are added to the default perimeter. *

* If an instant has CRAs but not CNECs and occurs before instants with CNECs, the CRAs can affect them so a * curative perimeter must be built for this instant as well. *

* The method returns whether curative perimeters were added to the contingency scenario or not. **/ private boolean processCurativeInstants(Contingency contingency, Crac crac, ContingencyScenario.ContingencyScenarioBuilder contingencyScenarioBuilder, Perimeter defaultPerimeter, boolean automatonCnecsExist) { Set instantsWithCnecs = crac.getInstants(InstantKind.CURATIVE).stream().filter(instant -> anyCnec(crac, crac.getState(contingency, instant))).collect(Collectors.toSet()); if (!automatonCnecsExist && instantsWithCnecs.isEmpty()) { OpenRaoLoggerProvider.BUSINESS_WARNS.warn("Contingency {} has an automaton or a curative remedial action but no CNECs associated.", contingency.getId()); return false; } // retrieve the nearest curative instant with CRAs for each curative instant with CNECs Map associatedOptimizationInstant = new HashMap<>(); instantsWithCnecs.forEach(instant -> associatedOptimizationInstant.put(instant, getLastCurativeInstantWithCraBeforeGivenInstant(contingency, crac, instant).orElse(null))); // create a perimeter for each instant with CRAs associated to an instant with CNECs Map curativePerimeters = new HashMap<>(); associatedOptimizationInstant.values().stream().distinct().filter(Objects::nonNull) .forEach(instant -> curativePerimeters.put(instant, new Perimeter(crac.getState(contingency, instant), new HashSet<>()))); // add the CNECs of the curative instants to the different perimeters: // - if the associated instant is null, the CNECs are added to the default perimeter // - otherwise, they are added to the perimeter corresponding to their associated optimization instant instantsWithCnecs.forEach(cnecInstant -> curativePerimeters.getOrDefault(associatedOptimizationInstant.get(cnecInstant), defaultPerimeter).addOtherState(crac.getState(contingency, cnecInstant))); // add the curative perimeters to the contingency scenario builder if (!defaultPerimeter.equals(preventivePerimeter) && associatedOptimizationInstant.containsValue(null)) { // add the default perimeter to the curative perimeters if it is curative and contains CNECs of several instants curativePerimeters.put(defaultPerimeter.getRaOptimisationState().getInstant(), defaultPerimeter); } curativePerimeters.values().forEach(contingencyScenarioBuilder::withCurativePerimeter); return !curativePerimeters.isEmpty(); } /** * Get the nearest previous curative instant with CRAs for a given curative instant. * @param contingency: the contingency of the scenario * @param crac: the CRAC data * @param instant: the curative instant * @return nearest previous curative instant with CRAs (Optional.empty() is none) */ private Optional getLastCurativeInstantWithCraBeforeGivenInstant(Contingency contingency, Crac crac, Instant instant) { return crac.getInstants(InstantKind.CURATIVE) .stream() .filter(otherInstant -> !otherInstant.comesAfter(instant)) .filter(otherInstant -> Objects.nonNull(crac.getState(contingency, otherInstant))) .filter(otherInstant -> anyAvailableRemedialAction(crac, crac.getState(contingency, otherInstant))) .max(Instant::compareTo); } public Perimeter getBasecaseScenario() { return preventivePerimeter; } public Set getContingencyScenarios() { return contingencyScenarios; } public Set getOperatorsNotSharingCras() { return operatorsNotSharingCras; } private boolean anyCnec(Crac crac, State state) { return !crac.getFlowCnecs(state).isEmpty(); } private static boolean anyAvailableRemedialAction(Crac crac, State state) { return !crac.getPotentiallyAvailableNetworkActions(state).isEmpty() || !crac.getPotentiallyAvailableRangeActions(state).isEmpty(); } static Set findOperatorsNotSharingCras(Crac crac) { Set tsos = crac.getFlowCnecs().stream().map(Cnec::getOperator).collect(Collectors.toSet()); tsos.addAll(crac.getRemedialActions().stream().map(RemedialAction::getOperator).collect(Collectors.toSet())); // If a CNEC's operator is not null, filter it out of the list of operators not sharing CRAs return tsos.stream().filter(tso -> Objects.nonNull(tso) && !tsoHasCra(tso, crac)).collect(Collectors.toSet()); } static boolean tsoHasCra(String tso, Crac crac) { Set optimizedCurativeStates = crac.getCurativeStates(); return optimizedCurativeStates.stream().anyMatch(state -> crac.getPotentiallyAvailableNetworkActions(state).stream().map(RemedialAction::getOperator).anyMatch(tso::equals) || crac.getPotentiallyAvailableRangeActions(state).stream().map(RemedialAction::getOperator).anyMatch(tso::equals) ); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy