RaoResult.java
/*
* 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.data.raoresult.api;
import com.powsybl.commons.util.ServiceLoaderCache;
import com.powsybl.openrao.commons.MinOrMax;
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.Crac;
import com.powsybl.openrao.data.crac.api.CracCreationContext;
import com.powsybl.openrao.data.crac.api.Instant;
import com.powsybl.openrao.data.crac.api.RemedialAction;
import com.powsybl.openrao.data.crac.api.State;
import com.powsybl.openrao.data.crac.api.cnec.AngleCnec;
import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.openrao.data.crac.api.cnec.VoltageCnec;
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.io.Exporter;
import com.powsybl.openrao.data.raoresult.api.io.Importer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* This interface will provide complete results that a user could expect after a RAO. It enables to access physical
* and computational values along different {@link Instant} which represents the different states of the
* optimization (initial situation, after PRA, after CRA).
*
* @author Joris Mancini {@literal <joris.mancini at rte-france.com>}
*/
public interface RaoResult {
String INITIAL_INSTANT_ID = "initial";
/**
* Get the overall sensitivity computation status of the RAO
*/
ComputationStatus getComputationStatus();
/**
* Get the sensitivity computation status for a given state
*/
ComputationStatus getComputationStatus(State state);
/**
* It gives the flow on a {@link FlowCnec} after a given {@link Instant} and in a
* given {@link Unit}.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param flowCnec: The branch to be studied.
* @param side: The side of the branch to be queried.
* @param unit: The unit in which the flow is queried. Only accepted values are MEGAWATT or AMPERE.
* @return The flow on the branch at the optimization state in the given unit.
*/
double getFlow(Instant optimizedInstant, FlowCnec flowCnec, TwoSides side, Unit unit);
/**
* It gives the angle on an {@link AngleCnec} at a given {@link Instant} and in a
* given {@link Unit}.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param angleCnec: The angle cnec to be studied.
* @param unit: The unit in which the flow is queried. Only accepted value for now is DEGREE.
* @return The angle on the cnec at the optimization state in the given unit.
*/
default double getAngle(Instant optimizedInstant, AngleCnec angleCnec, Unit unit) {
throw new OpenRaoException("Angle cnecs are not computed in the rao");
}
/**
* It gives the voltage on a {@link VoltageCnec} at a given {@link Instant} and in a
* given {@link Unit}.
*
* @param optimizedInstant The optimized instant to be studied (set to null to access initial results)
* @param voltageCnec The voltage cnec to be studied.
* @param minOrMax minimum or maximum voltage value on the voltage CNEC
* @param unit The unit in which the voltage is queried. Only accepted value for now is KILOVOLT.
* @return The min or max voltage on the cnec at the optimization state in the given unit.
*/
default double getVoltage(Instant optimizedInstant, VoltageCnec voltageCnec, MinOrMax minOrMax, Unit unit) {
throw new OpenRaoException("Voltage cnecs are not computed in the rao");
}
default double getMinVoltage(Instant optimizedInstant, VoltageCnec voltageCnec, MinOrMax minOrMax, Unit unit) {
throw new OpenRaoException("Voltage cnecs are not computed in the rao");
}
default double getMaxVoltage(Instant optimizedInstant, VoltageCnec voltageCnec, MinOrMax minOrMax, Unit unit) {
throw new OpenRaoException("Voltage cnecs are not computed in the rao");
}
/**
* It gives the margin on a {@link FlowCnec} at a given {@link Instant} and in a
* given {@link Unit}. It is basically the difference between the flow and the most constraining threshold in the
* flow direction of the given branch. If it is negative the branch is under constraint.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param flowCnec: The branch to be studied.
* @param unit: The unit in which the margin is queried. Only accepted values are MEGAWATT or AMPERE.
* @return The margin on the branch at the optimization state in the given unit.
*/
double getMargin(Instant optimizedInstant, FlowCnec flowCnec, Unit unit);
/**
* It gives the margin on an {@link AngleCnec} at a given {@link Instant} and in a
* given {@link Unit}. It is basically the difference between the angle and the most constraining threshold in the
* angle direction of the given branch. If it is negative the cnec is under constraint.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param angleCnec: The angle cnec to be studied.
* @param unit: The unit in which the margin is queried. Only accepted for now is DEGREE.
* @return The margin on the angle cnec at the optimization state in the given unit.
*/
default double getMargin(Instant optimizedInstant, AngleCnec angleCnec, Unit unit) {
throw new OpenRaoException("Angle cnecs are not computed in the rao");
}
/**
* It gives the margin on a {@link VoltageCnec} at a given {@link Instant} and in a
* given {@link Unit}. It is basically the difference between the voltage and the most constraining threshold in the
* of the given voltage level. If it is negative the cnec is under constraint.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param voltageCnec: The voltage cnec to be studied.
* @param unit: The unit in which the margin is queried. Only accepted for now is KILOVOLT.
* @return The margin on the voltage cnec at the optimization state in the given unit.
*/
default double getMargin(Instant optimizedInstant, VoltageCnec voltageCnec, Unit unit) {
throw new OpenRaoException("Voltage cnecs are not computed in the rao");
}
/**
* It gives the relative margin (according to CORE D-2 CC methodology) on a {@link FlowCnec} at a given
* {@link Instant} and in a given {@link Unit}. If the margin is negative it gives it directly (same
* value as {@code getMargin} method. If the margin is positive it gives this value divided by the sum of the zonal
* PTDFs on this branch of the studied zone. Zones to include in this computation are defined in the
* RAO. If it is negative the branch is under constraint. If the PTDFs are not defined in the
* computation or the sum of them is null, this method could return {@code Double.NaN} values.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param flowCnec: The branch to be studied.
* @param unit: The unit in which the relative margin is queried. Only accepted values are MEGAWATT or AMPERE.
* @return The relative margin on the branch at the optimization state in the given unit.
*/
double getRelativeMargin(Instant optimizedInstant, FlowCnec flowCnec, Unit unit);
/**
* It gives the value of commercial flow (according to CORE D-2 CC methodology) on a {@link FlowCnec} at a given
* {@link Instant} and in a given {@link Unit}. If the branch is not considered as a branch on which the
* loop flows are monitored, this method could return {@code Double.NaN} values.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param flowCnec: The branch to be studied.
* @param unit: The unit in which the commercial flow is queried. Only accepted values are MEGAWATT or AMPERE.
* @return The commercial flow on the branch at the optimization state in the given unit.
*/
double getCommercialFlow(Instant optimizedInstant, FlowCnec flowCnec, TwoSides side, Unit unit);
/**
* It gives the value of loop flow (according to CORE D-2 CC methodology) on a {@link FlowCnec} at a given
* {@link Instant} and in a given {@link Unit}. If the branch is not considered as a branch on which the
* loop flows are monitored, this method could return {@code Double.NaN} values.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param flowCnec: The branch to be studied.
* @param unit: The unit in which the loop flow is queried. Only accepted values are MEGAWATT or AMPERE.
* @return The loop flow on the branch at the optimization state in the given unit.
*/
double getLoopFlow(Instant optimizedInstant, FlowCnec flowCnec, TwoSides side, Unit unit);
/**
* It gives the sum of the computation areas' zonal PTDFs on a {@link FlowCnec} at a given
* {@link Instant}. If the computation does not consider PTDF values or if the RAO does
* not define any list of considered areas, this method could return {@code Double.NaN} values.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param flowCnec: The branch to be studied.
* @return The sum of the computation areas' zonal PTDFs on the branch at the optimization state.
*/
double getPtdfZonalSum(Instant optimizedInstant, FlowCnec flowCnec, TwoSides side);
/**
* It gives the global cost of the situation at a given {@link Instant} according to the objective
* function defined in the RAO.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @return The global cost of the situation state.
*/
default double getCost(Instant optimizedInstant) {
return getFunctionalCost(optimizedInstant) + getVirtualCost(optimizedInstant);
}
/**
* It gives the functional cost of the situation at a given {@link Instant} according to the objective
* function defined in the RAO. It represents the main part of the objective function.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @return The functional cost of the situation state.
*/
double getFunctionalCost(Instant optimizedInstant);
/**
* It gives the sum of virtual costs of the situation at a given {@link Instant} according to the
* objective function defined in the RAO. It represents the secondary parts of the objective
* function.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @return The global virtual cost of the situation state.
*/
double getVirtualCost(Instant optimizedInstant);
/**
* It gives the names of the different virtual cost implied in the objective function defined in
* the RAO.
*
* @return The set of virtual cost names.
*/
Set<String> getVirtualCostNames();
/**
* It gives the specified virtual cost of the situation at a given {@link Instant}. It represents the
* secondary parts of the objective. If the specified name is not part of the virtual costs defined in the
* objective function, this method could return {@code Double.NaN} values.
*
* @param optimizedInstant: The optimized instant to be studied (set to null to access initial results)
* @param virtualCostName: The name of the virtual cost.
* @return The specific virtual cost of the situation state.
*/
double getVirtualCost(Instant optimizedInstant, String virtualCostName);
/**
* It states if the {@link RemedialAction} is activated on a specific {@link State}.
*
* @param state: The state of the state tree to be studied.
* @param remedialAction: The remedial action to be studied.
* @return True if the remedial action is chosen by the optimizer during the specified state.
*/
default 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");
}
}
/**
* It states if the {@link NetworkAction} was already activated when a specific {@link State} is studied. Meaning
* the network action has not been chosen by the optimizer on this state, but this action is already effective in
* the network due to previous optimizations.
*
* @param state: The state of the state tree to be studied.
* @param networkAction: The network action to be studied.
* @return True if the network action is already active but has not been activated during the specified state.
*/
boolean wasActivatedBeforeState(State state, NetworkAction networkAction);
/**
* It states if the {@link NetworkAction} is activated on a specific {@link State}.
*
* @param state: The state of the state tree to be studied.
* @param networkAction: The network action to be studied.
* @return True if the network action is chosen by the optimizer during the specified state.
*/
boolean isActivatedDuringState(State state, NetworkAction networkAction);
/**
* It states if the {@link NetworkAction} is or was activated when a specific {@link State} is studied.
*
* @param state: The state of the state tree to be studied.
* @param networkAction: The network action to be studied.
* @return True if the network action is active during the specified state.
*/
default boolean isActivated(State state, NetworkAction networkAction) {
return wasActivatedBeforeState(state, networkAction) || isActivatedDuringState(state, networkAction);
}
/**
* It gathers the {@link NetworkAction} that are activated during the specified {@link State}.
*
* @param state: The state of the state tree to be studied.
* @return The set of activated network action during the specified state.
*/
Set<NetworkAction> getActivatedNetworkActionsDuringState(State state);
/**
* It states if a {@link RangeAction} is activated during a specified {@link State}. It is the case only if the set
* point of the range action is different in the specified state compared to the previous state. The previous
* "state" is the initial situation in the case of the preventive state.
*
* @param state: The state of the state tree to be studied.
* @param rangeAction: The range action to be studied.
* @return True if the set point of the range action has been changed during the specified state.
*/
boolean isActivatedDuringState(State state, RangeAction<?> rangeAction);
/**
* It gives the tap position of the PST on which the {@link PstRangeAction} is pointing at before it is optimized
* on the specified {@link State}. So, in the specific case of a PST range action that would be defined several
* times for the same PST (but available on different states), the final result would always be the situation of
* the PST on the state before its optimization. For example, if two PST range actions are defined :
* - RA1 : on "pst-element" only available in preventive state
* - RA2 : on "pst-element" only available on curative state after contingency "co-example"
* <p>
* Let's say tap of "pst-element" is initially at 0 in the network. During preventive optimization RA1 is activated
* and the PST tap goes to 5. During curative optimization RA2 is activated and the PST tap goes to 10. So when the
* method is called, we would get the following results :
* - getPreOptimizationTapOnState(preventiveState, RA1) = getPreOptimizationTapOnState(preventiveState, RA2) = 0
* - getPreOptimizationTapOnState(curativeState, RA1) = getPreOptimizationTapOnState(curativeState, RA2) = 5
* So we will still get 0 in preventive even if RA2 has not been activated during preventive optimization. And we
* will still get 5 in curative even if RA1 has not been activated during curative optimization.
*
* @param state: The state of the state tree to be studied.
* @param pstRangeAction: The PST range action to be studied.
* @return The tap of the PST defined in the PST range action at the specified state before its optimization.
*/
int getPreOptimizationTapOnState(State state, PstRangeAction pstRangeAction);
/**
* It gives the tap position of the PST on which the {@link PstRangeAction} is pointing at after it is optimized
* on the specified {@link State}. So, in the specific case of a PST range action that would be defined several
* times for the same PST (but available on different states), the final result would always be the optimized
* situation of the PST on the state. For example, if two range actions are defined :
* - RA1 : on "pst-element" only available in preventive state
* - RA2 : on "pst-element" only available on curative state after contingency "co-example"
* <p>
* Let's say tap of "pst-element" is initially at 0 in the network. During preventive optimization RA1 is activated
* and the PST tap goes to 5. During curative optimization RA2 is activated and the PST tap goes to 10. So when the
* method is called, we would get the following results :
* - getOptimizedTapOnState(preventiveState, RA1) = getOptimizedTapOnState(preventiveState, RA2) = 5
* - getOptimizedTapOnState(curativeState, RA1) = getOptimizedTapOnState(curativeState, RA2) = 10
* So we will still get 5 in preventive even if RA2 has not been activated during preventive optimization. And we
* will still get 10 in curative even if RA1 has not been activated during curative optimization.
*
* @param state: The state of the state tree to be studied.
* @param pstRangeAction: The PST range action to be studied.
* @return The tap of the PST defined in the PST range action at the specified state after its optimization.
*/
int getOptimizedTapOnState(State state, PstRangeAction pstRangeAction);
/**
* It gives the set point of the element on which the {@link RangeAction} is pointing at before it is optimized
* on the specified {@link State}. So, in the specific case of a range action that would be defined several
* times for the same network element (but available on different states), the final result would always be the
* set point of the network element on the state before its optimization. For example, if two range actions are
* defined :
* - RA1 : on "pst-element" only available in preventive state
* - RA2 : on "pst-element" only available on curative state after contingency "co-example"
* <p>
* Let's say the set point of "pst-element" is initially at 0. in the network. During preventive optimization RA1
* is activated and the PST set point goes to 3.2. During curative optimization RA2 is activated and the PST tap
* goes to 5.6. So when the method is called, we would get the following results :
* - getOptimizedSetPointOnState(preventiveState, RA1) = getOptimizedSetPointOnState(preventiveState, RA2) = 0.
* - getOptimizedSetPointOnState(curativeState, RA1) = getOptimizedSetPointOnState(curativeState, RA2) = 3.2
* So we will still get 0. in preventive even if RA2 has not been activated during preventive optimization. And we
* will still get 3.2 in curative even if RA1 has not been activated during curative optimization.
*
* @param state: The state of the state tree to be studied.
* @param rangeAction: The range action to be studied.
* @return The set point of the network element defined in the range action at the specified state before its
* optimization.
*/
double getPreOptimizationSetPointOnState(State state, RangeAction<?> rangeAction);
/**
* It gives the set point of the element on which the {@link RangeAction} is pointing at after it is optimized
* on the specified {@link State}. So, in the specific case of a range action that would be defined several
* times for the same network element (but available on different states), the final result would always be the
* optimized situation of the network element on the state. For example, if two PST range actions are defined :
* - RA1 : on "pst-element" only available in preventive state
* - RA2 : on "pst-element" only available on curative state after contingency "co-example"
* <p>
* Let's say the set point of "pst-element" is initially at 0. in the network. During preventive optimization RA1
* is activated and the PST set point goes to 3.2. During curative optimization RA2 is activated and the PST tap
* goes to 5.6. So when the method is called, we would get the following results :
* - getOptimizedSetPointOnState(preventiveState, RA1) = getOptimizedSetPointOnState(preventiveState, RA2) = 3.2
* - getOptimizedSetPointOnState(curativeState, RA1) = getOptimizedSetPointOnState(curativeState, RA2) = 5.6
* So we will still get 3.2 in preventive even if RA2 has not been activated during preventive optimization. And we
* will still get 5.6 in curative even if RA1 has not been activated during curative optimization.
*
* @param state: The state of the state tree to be studied.
* @param rangeAction: The range action to be studied.
* @return The set point of the network element defined in the range action at the specified state after its
* optimization.
*/
double getOptimizedSetPointOnState(State state, RangeAction<?> rangeAction);
/**
* It gathers the {@link RangeAction} that are activated during the specified {@link State}.
*
* @param state: The state of the state tree to be studied.
* @return The set of activated range action during the specified state.
*/
Set<RangeAction<?>> getActivatedRangeActionsDuringState(State state);
/**
* It gives a summary of all the optimized taps of the {@link PstRangeAction} present in the {@link Crac} for a
* specific {@link State}.
*
* @param state: The state of the state tree to be studied.
* @return The map of the PST range actions associated to their optimized tap of the specified state.
*/
Map<PstRangeAction, Integer> getOptimizedTapsOnState(State state);
/**
* It gives a summary of all the optimized set points of the {@link RangeAction} present in the {@link Crac} for a
* specific {@link State}.
*
* @param state: The state of the state tree to be studied.
* @return The map of the range actions associated to their optimized set points of the specified state.
*/
Map<RangeAction<?>, Double> getOptimizedSetPointsOnState(State state);
/**
* Know which RAO steps were executed by the RAO
*/
String getExecutionDetails();
void setExecutionDetails(String executionDetails);
/**
* Indicates whether the all the CNECs of a given type at a given instant are secure.
*
* @param optimizedInstant: The instant to assess
* @param u: The types of CNECs to check (FLOW -> FlowCNECs, ANGLE -> AngleCNECs, VOLTAGE -> VoltageCNECs). 1 to 3 arguments can be provided.
* @return whether all the CNECs of the given type(s) are secure at the optimized instant.
*/
boolean isSecure(Instant optimizedInstant, PhysicalParameter... u);
/**
* Indicates whether all the CNECs of a given type are secure at last instant (i.e. after RAO)..
*
* @param u: The types of CNECs to check (FLOW -> FlowCNECs, ANGLE -> AngleCNECs, VOLTAGE -> VoltageCNECs). 1 to 3 arguments can be provided.
* @return whether all the CNECs of the given type(s) are secure at last instant (i.e. after RAO)..
*/
boolean isSecure(PhysicalParameter... u);
/**
* Indicates whether all the CNECs are secure at last instant (i.e. after RAO)..
*
* @return whether all the CNECs are secure at last instant (i.e. after RAO)..
*/
default boolean isSecure() {
return isSecure(PhysicalParameter.FLOW, PhysicalParameter.ANGLE, PhysicalParameter.VOLTAGE);
}
/**
* Import RaoResult from a file
*
* @param importers candidates RaoResult importers to process the data
* @param inputStream RaoResult data
* @param crac the crac on which the RaoResult data is based
* @return RaoResult object
*/
private static RaoResult read(List<Importer> importers, InputStream inputStream, Crac crac) throws IOException {
byte[] bytes = getBytesFromInputStream(inputStream);
return importers.stream()
.filter(importer -> importer.exists(new ByteArrayInputStream(bytes)))
.findAny()
.orElseThrow(() -> new OpenRaoException("No suitable RaoResult importer found."))
.importData(new ByteArrayInputStream(bytes), crac);
}
/**
* Import RaoResult from a file
*
* @param inputStream RaoResult data
* @param crac the crac on which the RaoResult data is based
* @return RaoResult object
*/
static RaoResult read(InputStream inputStream, Crac crac) throws IOException {
return read(new ServiceLoaderCache<>(Importer.class).getServices(), inputStream, crac);
}
private static byte[] getBytesFromInputStream(InputStream inputStream) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
org.apache.commons.io.IOUtils.copy(inputStream, baos);
return baos.toByteArray();
}
/**
* Write RAO Result data into a file
*
* @param exporters candidate RAO Result exporters
* @param format desired output RAO Result data type
* @param cracCreationContext CRAC creation context that contains the original CRAC
* @param properties specific information needed for export
* @param outputStream file where to write the RAO Result data
*/
private void write(List<Exporter> exporters, String format, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) {
exporters.stream()
.filter(ex -> format.equals(ex.getFormat()))
.findAny()
.orElseThrow(() -> new OpenRaoException("Export format " + format + " not supported"))
.exportData(this, cracCreationContext, properties, outputStream);
}
/**
* Write RAO Result data into a file
*
* @param format desired output RAO Result data type
* @param cracCreationContext CRAC creation context that contains the original CRAC
* @param properties specific information needed for export
* @param outputStream file where to write the RAO Result data
*/
default void write(String format, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) {
write(new ServiceLoaderCache<>(Exporter.class).getServices(), format, cracCreationContext, properties, outputStream);
}
/**
* Write RAO Result data into a file
*
* @param exporters candidate RAO Result exporters
* @param format desired output RAO Result data type
* @param crac CRAC data
* @param properties specific information needed for export
* @param outputStream file where to write the RAO Result data
*/
private void write(List<Exporter> exporters, String format, Crac crac, Properties properties, OutputStream outputStream) {
exporters.stream()
.filter(ex -> format.equals(ex.getFormat()))
.findAny()
.orElseThrow(() -> new OpenRaoException("Export format " + format + " not supported"))
.exportData(this, crac, properties, outputStream);
}
/**
* Write RAO Result data into a file
*
* @param format desired output RAO Result data type
* @param crac CRAC data
* @param properties specific information needed for export
* @param outputStream file where to write the RAO Result data
*/
default void write(String format, Crac crac, Properties properties, OutputStream outputStream) {
write(new ServiceLoaderCache<>(Exporter.class).getServices(), format, crac, properties, outputStream);
}
}