> getAvailableRangeActionsOnSameAction(RangeAction>
return rangeActions;
}
- /**
- * Add in the objective function a penalty cost associated to the RangeAction
- * activations. This penalty cost prioritizes the solutions which change as little
- * as possible the set points of the RangeActions.
- *
- * min( sum{r in RangeAction} penaltyCost[r] - AV[r] )
- */
- private void fillObjectiveWithRangeActionPenaltyCost(LinearProblem linearProblem) {
- optimizationContext.getRangeActionsPerState().forEach((state, rangeActions) -> rangeActions.forEach(ra -> {
- OpenRaoMPVariable absoluteVariationVariable = linearProblem.getAbsoluteRangeActionVariationVariable(ra, state);
-
- // If the range action has been filtered out, then absoluteVariationVariable is null
- if (absoluteVariationVariable != null && ra instanceof PstRangeAction) {
- linearProblem.getObjective().setCoefficient(absoluteVariationVariable, rangeActionParameters.getPstPenaltyCost());
- } else if (absoluteVariationVariable != null && ra instanceof HvdcRangeAction) {
- linearProblem.getObjective().setCoefficient(absoluteVariationVariable, rangeActionParameters.getHvdcPenaltyCost());
- } else if (absoluteVariationVariable != null && ra instanceof InjectionRangeAction) {
- linearProblem.getObjective().setCoefficient(absoluteVariationVariable, rangeActionParameters.getInjectionRaPenaltyCost());
- }
- }
- ));
+ protected abstract void fillObjective(LinearProblem linearProblem);
+
+ protected static double getRangeActionPenaltyCost(RangeAction> rangeAction, RangeActionsOptimizationParameters rangeActionParameters) {
+ if (rangeAction instanceof PstRangeAction) {
+ return rangeActionParameters.getPstPenaltyCost();
+ } else if (rangeAction instanceof HvdcRangeAction) {
+ return rangeActionParameters.getHvdcPenaltyCost();
+ } else if (rangeAction instanceof InjectionRangeAction) {
+ return rangeActionParameters.getInjectionRaPenaltyCost();
+ } else {
+ throw new OpenRaoException("Unexpected type of range action: '%s'.".formatted(rangeAction.getClass().getSimpleName()));
+ }
}
}
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/CostCoreProblemFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/CostCoreProblemFiller.java
new file mode 100644
index 0000000000..c77a10ee49
--- /dev/null
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/CostCoreProblemFiller.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2024, 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.linearoptimisation.algorithms.fillers;
+
+import com.powsybl.openrao.commons.OpenRaoException;
+import com.powsybl.openrao.commons.Unit;
+import com.powsybl.openrao.data.crac.api.State;
+import com.powsybl.openrao.data.crac.api.rangeaction.PstRangeAction;
+import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction;
+import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection;
+import com.powsybl.openrao.raoapi.parameters.RangeActionsOptimizationParameters;
+import com.powsybl.openrao.searchtreerao.commons.RaoUtil;
+import com.powsybl.openrao.searchtreerao.commons.optimizationperimeters.OptimizationPerimeter;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.LinearProblem;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPConstraint;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPVariable;
+import com.powsybl.openrao.searchtreerao.result.api.RangeActionSetpointResult;
+import org.apache.commons.lang3.tuple.Pair;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Thomas Bouquet {@literal }
+ */
+public class CostCoreProblemFiller extends AbstractCoreProblemFiller {
+
+ public CostCoreProblemFiller(OptimizationPerimeter optimizationContext,
+ RangeActionSetpointResult prePerimeterRangeActionSetpoints,
+ RangeActionsOptimizationParameters rangeActionParameters,
+ Unit unit,
+ boolean raRangeShrinking,
+ RangeActionsOptimizationParameters.PstModel pstModel) {
+ super(optimizationContext, prePerimeterRangeActionSetpoints, rangeActionParameters, unit, raRangeShrinking, pstModel);
+ if (pstModel.equals(RangeActionsOptimizationParameters.PstModel.CONTINUOUS)) {
+ throw new OpenRaoException("Costly remedial action optimization is only available for the APPROXIMATED_INTEGERS mode of PST range actions.");
+ }
+ }
+
+ @Override
+ protected void addAllRangeActionVariables(LinearProblem linearProblem, RangeAction> rangeAction, State state) {
+ super.addAllRangeActionVariables(linearProblem, rangeAction, state);
+ Optional activationCost = rangeAction.getActivationCost();
+ if (activationCost.isPresent() && activationCost.get() > 0) {
+ linearProblem.addRangeActionVariationBinary(rangeAction, state);
+ }
+ }
+
+ /**
+ * Build range action constraints for each RangeAction r.
+ * These constraints link the set-point variable of the RangeAction with its
+ * variation variables, and bounds the set-point in an admissible range.
+ * S[r] = initialSetPoint[r] + upwardVariation[r] - downwardVariation[r]
+ */
+ @Override
+ protected void buildConstraintsForRangeActionAndState(LinearProblem linearProblem, RangeAction> rangeAction, State state) {
+ addSetPointConstraints(linearProblem, rangeAction, state);
+ addIsVariationConstraint(linearProblem, rangeAction, state);
+ }
+
+ private void addIsVariationConstraint(LinearProblem linearProblem, RangeAction> rangeAction, State state) {
+ Optional activationCost = rangeAction.getActivationCost();
+ if (activationCost.isPresent() && activationCost.get() > 0) {
+ OpenRaoMPConstraint activationConstraint = linearProblem.addIsVariationConstraint(0, linearProblem.infinity(), rangeAction, state);
+ OpenRaoMPVariable upwardVariationVariable = linearProblem.getRangeActionVariationVariable(rangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD);
+ OpenRaoMPVariable downwardVariationVariable = linearProblem.getRangeActionVariationVariable(rangeAction, state, LinearProblem.VariationDirectionExtension.DOWNWARD);
+ OpenRaoMPVariable variationBinaryVariable = linearProblem.getRangeActionVariationBinary(rangeAction, state);
+
+ double minSetPoint;
+ double maxSetPoint;
+
+ Pair, State> lastAvailableRangeAction = RaoUtil.getLastAvailableRangeActionOnSameNetworkElement(optimizationContext, rangeAction, state);
+
+ if (lastAvailableRangeAction == null) {
+ // if state is equal to masterState,
+ // or if rangeAction is not available for a previous state
+ // then, rangeAction could not have been activated in a previous instant
+
+ double prePerimeterSetPoint = prePerimeterRangeActionSetpoints.getSetpoint(rangeAction);
+ minSetPoint = rangeAction.getMinAdmissibleSetpoint(prePerimeterSetPoint);
+ maxSetPoint = rangeAction.getMaxAdmissibleSetpoint(prePerimeterSetPoint);
+ } else {
+ // range action have been activated in a previous instant
+ // getRangeActionSetpointVariable from previous instant
+ List minAndMaxAbsoluteAndRelativeSetpoints = getMinAndMaxAbsoluteAndRelativeSetpoints(rangeAction, linearProblem.infinity());
+ minSetPoint = minAndMaxAbsoluteAndRelativeSetpoints.get(0);
+ maxSetPoint = minAndMaxAbsoluteAndRelativeSetpoints.get(1);
+ }
+
+ activationConstraint.setCoefficient(variationBinaryVariable, maxSetPoint - minSetPoint + RANGE_ACTION_SETPOINT_EPSILON);
+ activationConstraint.setCoefficient(upwardVariationVariable, -1.0);
+ activationConstraint.setCoefficient(downwardVariationVariable, -1.0);
+ }
+ }
+
+ /**
+ * Add in the objective function the costs associated to the range actions that
+ * is the sum of two factors:
+ *
+ * - An activation cost if the range action is used;
+ * - Variation costs that depend on how much the set-point was shifted.
+ *
+ */
+ @Override
+ protected void fillObjective(LinearProblem linearProblem) {
+ optimizationContext.getRangeActionsPerState().forEach((state, rangeActions) -> rangeActions.forEach(ra -> {
+ OpenRaoMPVariable upwardVariationVariable = linearProblem.getRangeActionVariationVariable(ra, state, LinearProblem.VariationDirectionExtension.UPWARD);
+ OpenRaoMPVariable downwardVariationVariable = linearProblem.getRangeActionVariationVariable(ra, state, LinearProblem.VariationDirectionExtension.DOWNWARD);
+
+ double defaultVariationCost = getRangeActionPenaltyCost(ra, rangeActionParameters);
+ if (!(ra instanceof PstRangeAction)) {
+ linearProblem.getObjective().setCoefficient(upwardVariationVariable, ra.getVariationCost(VariationDirection.UP).orElse(defaultVariationCost));
+ linearProblem.getObjective().setCoefficient(downwardVariationVariable, ra.getVariationCost(VariationDirection.DOWN).orElse(defaultVariationCost));
+ }
+
+ if (ra.getActivationCost().isPresent() && ra.getActivationCost().get() > 0) {
+ OpenRaoMPVariable activationVariable = linearProblem.getRangeActionVariationBinary(ra, state);
+ linearProblem.getObjective().setCoefficient(activationVariable, ra.getActivationCost().get());
+ }
+ }
+ ));
+ }
+}
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstTapFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstTapFiller.java
index 0c073a6c34..c9e5c32aaa 100644
--- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstTapFiller.java
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstTapFiller.java
@@ -12,9 +12,12 @@
import com.powsybl.openrao.data.crac.api.range.TapRange;
import com.powsybl.openrao.data.crac.api.rangeaction.PstRangeAction;
import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction;
+import com.powsybl.openrao.data.crac.api.rangeaction.VariationDirection;
+import com.powsybl.openrao.raoapi.parameters.RangeActionsOptimizationParameters;
import com.powsybl.openrao.searchtreerao.commons.RaoUtil;
import com.powsybl.openrao.searchtreerao.commons.optimizationperimeters.OptimizationPerimeter;
import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPConstraint;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPObjective;
import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPVariable;
import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.LinearProblem;
import com.powsybl.openrao.searchtreerao.result.api.FlowResult;
@@ -33,14 +36,20 @@ public class DiscretePstTapFiller implements ProblemFiller {
private final OptimizationPerimeter optimizationPerimeter;
private final Map> rangeActions;
private final RangeActionSetpointResult prePerimeterRangeActionSetpoints;
+ private final RangeActionsOptimizationParameters rangeActionsParameters;
+ private final boolean costOptimization;
private int iteration = 0;
public DiscretePstTapFiller(OptimizationPerimeter optimizationPerimeter,
Map> rangeActions,
- RangeActionSetpointResult prePerimeterRangeActionSetpoints) {
+ RangeActionSetpointResult prePerimeterRangeActionSetpoints,
+ RangeActionsOptimizationParameters rangeActionsParameters,
+ boolean costOptimization) {
this.optimizationPerimeter = optimizationPerimeter;
this.rangeActions = rangeActions;
this.prePerimeterRangeActionSetpoints = prePerimeterRangeActionSetpoints;
+ this.rangeActionsParameters = rangeActionsParameters;
+ this.costOptimization = costOptimization;
}
@Override
@@ -52,6 +61,7 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity
if (iteration > 1) {
update(linearProblem, rangeActionActivationResult);
}
+ fillObjective(linearProblem);
}
@Override
@@ -69,12 +79,12 @@ private void update(LinearProblem linearProblem, RangeActionActivationResult ran
}
private void buildPstTapVariablesAndConstraints(LinearProblem linearProblem, PstRangeAction pstRangeAction, State state, RangeActionActivationResult rangeActionActivationResult) {
+ Pair, State> lastAvailableRangeAction = RaoUtil.getLastAvailableRangeActionOnSameNetworkElement(optimizationPerimeter, pstRangeAction, state);
// compute a few values on PST taps and angle
double currentAngle = rangeActionActivationResult.getOptimizedSetpoint(pstRangeAction, state);
int currentTap = rangeActionActivationResult.getOptimizedTap(pstRangeAction, state);
- Pair, State> lastAvailableRangeAction = RaoUtil.getLastAvailableRangeActionOnSameNetworkElement(optimizationPerimeter, pstRangeAction, state);
Pair admissibleTaps = getMinAndMaxAdmissibleTaps(pstRangeAction, lastAvailableRangeAction);
int minAdmissibleTap = admissibleTaps.getLeft();
int maxAdmissibleTap = admissibleTaps.getRight();
@@ -89,7 +99,7 @@ private void buildPstTapVariablesAndConstraints(LinearProblem linearProblem, Pst
OpenRaoMPVariable pstTapDownwardVariationBinary = linearProblem.addPstTapVariationBinary(pstRangeAction, state, LinearProblem.VariationDirectionExtension.DOWNWARD);
OpenRaoMPVariable pstTapUpwardVariationBinary = linearProblem.addPstTapVariationBinary(pstRangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD);
- // build integer constraint as it wasn't built in CoreProblemFiller
+ // build integer constraint as it wasn't built in MarginCoreProblemFiller
if (lastAvailableRangeAction != null) {
RangeAction> preventiveRangeAction = lastAvailableRangeAction.getKey();
Pair pstLimits = getMinAndMaxRelativeTaps(pstRangeAction, linearProblem.infinity());
@@ -145,6 +155,33 @@ private void buildPstTapVariablesAndConstraints(LinearProblem linearProblem, Pst
OpenRaoMPConstraint upAuthorizationConstraint = linearProblem.addIsVariationInDirectionConstraint(-linearProblem.infinity(), 0, pstRangeAction, state, LinearProblem.VariationReferenceExtension.PREVIOUS_ITERATION, LinearProblem.VariationDirectionExtension.UPWARD);
upAuthorizationConstraint.setCoefficient(pstTapUpwardVariationVariable, 1);
upAuthorizationConstraint.setCoefficient(pstTapUpwardVariationBinary, -maxUpwardTapVariation);
+
+ int initialTap = prePerimeterRangeActionSetpoints.getTap(pstRangeAction);
+
+ OpenRaoMPVariable tapVariable = linearProblem.addTapVariable(pstRangeAction, state);
+ OpenRaoMPConstraint tapConstraint = linearProblem.addTapConstraint(pstRangeAction, state);
+ tapConstraint.setCoefficient(tapVariable, 1.0);
+ tapConstraint.setCoefficient(pstTapUpwardVariationVariable, -1.0);
+ tapConstraint.setCoefficient(pstTapDownwardVariationVariable, 1.0);
+ tapConstraint.setLb(currentTap);
+ tapConstraint.setUb(currentTap);
+
+ if (costOptimization) {
+ OpenRaoMPConstraint totalTapVariationConstraint = linearProblem.addTotalPstRangeActionTapVariationConstraint(pstRangeAction, state);
+ OpenRaoMPVariable totalUpwardTapVariationVariable = linearProblem.addTotalPstRangeActionTapVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD);
+ OpenRaoMPVariable totalDownwardTapVariationVariable = linearProblem.addTotalPstRangeActionTapVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.DOWNWARD);
+
+ totalTapVariationConstraint.setCoefficient(totalUpwardTapVariationVariable, 1.0);
+ totalTapVariationConstraint.setCoefficient(totalDownwardTapVariationVariable, -1.0);
+ totalTapVariationConstraint.setCoefficient(tapVariable, -1.0);
+
+ if (lastAvailableRangeAction == null) {
+ totalTapVariationConstraint.setLb(-initialTap);
+ totalTapVariationConstraint.setUb(-initialTap);
+ } else {
+ totalTapVariationConstraint.setCoefficient(linearProblem.getTapVariable((PstRangeAction) lastAvailableRangeAction.getLeft(), lastAvailableRangeAction.getRight()), 1.0);
+ }
+ }
}
private void updateRelativeRangeConstraints(LinearProblem linearProblem, PstRangeAction pstRangeAction, State state, RangeActionActivationResult rangeActionActivationResult) {
@@ -165,12 +202,12 @@ private void updateRelativeRangeConstraints(LinearProblem linearProblem, PstRang
}
private void refineTapToAngleConversionCoefficientAndUpdateBounds(LinearProblem linearProblem, PstRangeAction pstRangeAction, RangeActionActivationResult rangeActionActivationResult, State state) {
+ Pair, State> lastAvailableRangeAction = RaoUtil.getLastAvailableRangeActionOnSameNetworkElement(optimizationPerimeter, pstRangeAction, state);
// compute a few values on PST taps and angle
double newAngle = rangeActionActivationResult.getOptimizedSetpoint(pstRangeAction, state);
int newTapPosition = rangeActionActivationResult.getOptimizedTap(pstRangeAction, state);
- Pair, State> lastAvailableRangeAction = RaoUtil.getLastAvailableRangeActionOnSameNetworkElement(optimizationPerimeter, pstRangeAction, state);
Pair admissibleTaps = getMinAndMaxAdmissibleTaps(pstRangeAction, lastAvailableRangeAction);
int minAdmissibleTap = admissibleTaps.getLeft();
int maxAdmissibleTap = admissibleTaps.getRight();
@@ -207,6 +244,11 @@ private void refineTapToAngleConversionCoefficientAndUpdateBounds(LinearProblem
// update the coefficient on the min/max tap variations of the isVariationInDirectionConstraints
downAuthorizationConstraint.setCoefficient(pstTapDownwardVariationBinary, -maxDownwardTapVariation);
upAuthorizationConstraint.setCoefficient(pstTapUpwardVariationBinary, -maxUpwardTapVariation);
+
+ // update tap position constraint bounds
+ OpenRaoMPConstraint tapConstraint = linearProblem.getTapConstraint(pstRangeAction, state);
+ tapConstraint.setLb(newTapPosition);
+ tapConstraint.setUb(newTapPosition);
}
/**
@@ -246,4 +288,15 @@ private Pair getMinAndMaxRelativeTaps(PstRangeAction pstRangeAct
maxRelativeTap = Math.max(0, maxRelativeTap);
return Pair.of(minRelativeTap, maxRelativeTap);
}
+
+ private void fillObjective(LinearProblem linearProblem) {
+ if (costOptimization) {
+ double defaultCost = rangeActionsParameters.getPstPenaltyCost();
+ OpenRaoMPObjective objective = linearProblem.getObjective();
+ rangeActions.forEach((state, pstRangeActions) -> pstRangeActions.forEach(pstRangeAction -> {
+ objective.setCoefficient(linearProblem.getTotalPstRangeActionTapVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD), pstRangeAction.getVariationCost(VariationDirection.UP).orElse(defaultCost));
+ objective.setCoefficient(linearProblem.getTotalPstRangeActionTapVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.DOWNWARD), pstRangeAction.getVariationCost(VariationDirection.DOWN).orElse(defaultCost));
+ }));
+ }
+ }
}
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MarginCoreProblemFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MarginCoreProblemFiller.java
new file mode 100644
index 0000000000..b1013af755
--- /dev/null
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MarginCoreProblemFiller.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2024, 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.linearoptimisation.algorithms.fillers;
+
+import com.powsybl.openrao.commons.Unit;
+import com.powsybl.openrao.data.crac.api.State;
+import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction;
+import com.powsybl.openrao.raoapi.parameters.RangeActionsOptimizationParameters;
+import com.powsybl.openrao.searchtreerao.commons.optimizationperimeters.OptimizationPerimeter;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPVariable;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.LinearProblem;
+import com.powsybl.openrao.searchtreerao.result.api.RangeActionSetpointResult;
+
+/**
+ * @author Thomas Bouquet {@literal }
+ */
+public class MarginCoreProblemFiller extends AbstractCoreProblemFiller {
+
+ public MarginCoreProblemFiller(OptimizationPerimeter optimizationContext,
+ RangeActionSetpointResult prePerimeterRangeActionSetpoints,
+ RangeActionsOptimizationParameters rangeActionParameters,
+ Unit unit,
+ boolean raRangeShrinking,
+ RangeActionsOptimizationParameters.PstModel pstModel) {
+ super(optimizationContext, prePerimeterRangeActionSetpoints, rangeActionParameters, unit, raRangeShrinking, pstModel);
+ }
+
+ /**
+ * Build range action constraints for each RangeAction r.
+ * These constraints link the set-point variable of the RangeAction with its
+ * variation variables, and bounds the set-point in an admissible range.
+ * S[r] = initialSetPoint[r] + upwardVariation[r] - downwardVariation[r]
+ */
+ @Override
+ protected void buildConstraintsForRangeActionAndState(LinearProblem linearProblem, RangeAction> rangeAction, State state) {
+ addSetPointConstraints(linearProblem, rangeAction, state);
+ }
+
+ /**
+ * Add in the objective function a penalty cost associated to the RangeAction
+ * activations. This penalty cost prioritizes the solutions which change as little
+ * as possible the set points of the RangeActions.
+ *
+ * min( sum{r in RangeAction} penaltyCost[r] - AV[r] )
+ */
+ @Override
+ protected void fillObjective(LinearProblem linearProblem) {
+ optimizationContext.getRangeActionsPerState().forEach((state, rangeActions) -> rangeActions.forEach(ra -> {
+ OpenRaoMPVariable absoluteVariationVariable = linearProblem.getAbsoluteRangeActionVariationVariable(ra, state);
+ if (absoluteVariationVariable != null) {
+ linearProblem.getObjective().setCoefficient(absoluteVariationVariable, getRangeActionPenaltyCost(ra, rangeActionParameters));
+ }
+ }));
+ }
+}
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinMarginFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinMarginFiller.java
index e30d22509c..12c2785f5a 100644
--- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinMarginFiller.java
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinMarginFiller.java
@@ -30,14 +30,17 @@
* @author Baptiste Seguinot {@literal }
*/
public class MaxMinMarginFiller implements ProblemFiller {
+ private static final double OVERLOAD_PENALTY = 10000.0; // TODO: put this in Rao Parameters and mutualize with evaluator
protected final Set optimizedCnecs;
private final Unit unit;
+ private final boolean costOptimization;
public MaxMinMarginFiller(Set optimizedCnecs,
- Unit unit) {
+ Unit unit, boolean costOptimization) {
this.optimizedCnecs = new TreeSet<>(Comparator.comparing(Identifiable::getId));
this.optimizedCnecs.addAll(optimizedCnecs);
this.unit = unit;
+ this.costOptimization = costOptimization;
}
@Override
@@ -49,11 +52,18 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity
// build constraints
buildMinimumMarginConstraints(linearProblem, validFlowCnecs);
+ if (costOptimization) {
+ forceMinMarginToBeNegative(linearProblem);
+ }
// complete objective
fillObjectiveWithMinMargin(linearProblem);
}
+ private static void forceMinMarginToBeNegative(LinearProblem linearProblem) {
+ linearProblem.getMinimumMarginVariable().setUb(0.0);
+ }
+
@Override
public void updateBetweenMipIteration(LinearProblem linearProblem, RangeActionActivationResult rangeActionActivationResult) {
// Objective does not change, nothing to do
@@ -122,8 +132,7 @@ private void buildMinimumMarginConstraints(LinearProblem linearProblem, Set optimizedCnecs,
FlowResult preOptimFlowResult,
Unit unit,
RelativeMarginsParametersExtension maxMinRelativeMarginParameters) {
- super(optimizedCnecs, unit);
+ super(optimizedCnecs, unit, false);
this.preOptimFlowResult = preOptimFlowResult;
this.ptdfApproximationLevel = maxMinRelativeMarginParameters.getPtdfApproximation();
this.unit = unit;
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/RaUsageLimitsFiller.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/RaUsageLimitsFiller.java
index 69a03eb206..2807f5b13e 100644
--- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/RaUsageLimitsFiller.java
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/RaUsageLimitsFiller.java
@@ -44,17 +44,19 @@ public class RaUsageLimitsFiller implements ProblemFiller {
private final boolean arePstSetpointsApproximated;
private static final double RANGE_ACTION_SETPOINT_EPSILON = 1e-4;
private final Network network;
+ private final boolean costOptimization;
public RaUsageLimitsFiller(Map>> rangeActions,
RangeActionSetpointResult prePerimeterRangeActionSetpoints,
RangeActionLimitationParameters rangeActionLimitationParameters,
boolean arePstSetpointsApproximated,
- Network network) {
+ Network network, boolean costOptimization) {
this.rangeActions = rangeActions;
this.prePerimeterRangeActionSetpoints = prePerimeterRangeActionSetpoints;
this.rangeActionLimitationParameters = rangeActionLimitationParameters;
this.arePstSetpointsApproximated = arePstSetpointsApproximated;
this.network = network;
+ this.costOptimization = costOptimization;
}
@Override
@@ -63,7 +65,10 @@ public void fill(LinearProblem linearProblem, FlowResult flowResult, Sensitivity
if (!rangeActionLimitationParameters.areRangeActionLimitedForState(state)) {
return;
}
- rangeActionSet.forEach(ra -> buildIsVariationVariableAndConstraints(linearProblem, ra, state));
+ // if cost optimization, variation variables are already defined
+ if (!costOptimization) {
+ rangeActionSet.forEach(ra -> buildIsVariationVariableAndConstraints(linearProblem, ra, state));
+ }
if (rangeActionLimitationParameters.getMaxRangeActions(state) != null) {
addMaxRaConstraint(linearProblem, state);
}
@@ -129,19 +134,21 @@ private double getInitialSetpointRelaxation(RangeAction> rangeAction) {
}
private void buildIsVariationVariableAndConstraints(LinearProblem linearProblem, RangeAction> rangeAction, State state) {
- OpenRaoMPVariable isVariationVariable = linearProblem.addRangeActionVariationBinary(rangeAction, state);
+ if (!costOptimization) {
+ OpenRaoMPVariable isVariationVariable = linearProblem.addRangeActionVariationBinary(rangeAction, state);
- OpenRaoMPVariable absoluteVariationVariable = linearProblem.getAbsoluteRangeActionVariationVariable(rangeAction, state);
+ OpenRaoMPVariable absoluteVariationVariable = linearProblem.getAbsoluteRangeActionVariationVariable(rangeAction, state);
- double initialSetpointRelaxation = getInitialSetpointRelaxation(rangeAction);
+ double initialSetpointRelaxation = getInitialSetpointRelaxation(rangeAction);
- // range action absolute variation <= isVariationVariable * (max setpoint - min setpoint) + initialSetpointRelaxation
- // RANGE_ACTION_SETPOINT_EPSILON is used to mitigate rounding issues, ensuring that the maximum setpoint is feasible
- // initialSetpointRelaxation is used to ensure that the initial setpoint is feasible
- OpenRaoMPConstraint constraint = linearProblem.addIsVariationConstraint(-linearProblem.infinity(), initialSetpointRelaxation, rangeAction, state);
- constraint.setCoefficient(absoluteVariationVariable, 1);
- double initialSetpoint = prePerimeterRangeActionSetpoints.getSetpoint(rangeAction);
- constraint.setCoefficient(isVariationVariable, -(rangeAction.getMaxAdmissibleSetpoint(initialSetpoint) + RANGE_ACTION_SETPOINT_EPSILON - rangeAction.getMinAdmissibleSetpoint(initialSetpoint)));
+ // range action absolute variation <= isVariationVariable * (max setpoint - min setpoint) + initialSetpointRelaxation
+ // RANGE_ACTION_SETPOINT_EPSILON is used to mitigate rounding issues, ensuring that the maximum setpoint is feasible
+ // initialSetpointRelaxation is used to ensure that the initial setpoint is feasible
+ OpenRaoMPConstraint constraint = linearProblem.addIsVariationConstraint(-linearProblem.infinity(), initialSetpointRelaxation, rangeAction, state);
+ constraint.setCoefficient(absoluteVariationVariable, 1);
+ double initialSetpoint = prePerimeterRangeActionSetpoints.getSetpoint(rangeAction);
+ constraint.setCoefficient(isVariationVariable, -(rangeAction.getMaxAdmissibleSetpoint(initialSetpoint) + RANGE_ACTION_SETPOINT_EPSILON - rangeAction.getMinAdmissibleSetpoint(initialSetpoint)));
+ }
}
private void addMaxRaConstraint(LinearProblem linearProblem, State state) {
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java
index 7c01284a28..0c868cb51f 100644
--- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblem.java
@@ -468,6 +468,40 @@ public OpenRaoMPConstraint getRangeActionAbsoluteVariationConstraint(RangeAction
return solver.getConstraint(rangeActionAbsoluteVariationConstraintId(rangeAction, state));
}
+ public OpenRaoMPVariable addTotalPstRangeActionTapVariationVariable(PstRangeAction pstRangeAction, State state, LinearProblem.VariationDirectionExtension variationDirection) {
+ return solver.makeIntVar(0, infinity(), totalPstRangeActionTapVariationVariableId(pstRangeAction, state, variationDirection));
+ }
+
+ public OpenRaoMPVariable getTotalPstRangeActionTapVariationVariable(PstRangeAction pstRangeAction, State state, LinearProblem.VariationDirectionExtension variationDirection) {
+ return solver.getVariable(totalPstRangeActionTapVariationVariableId(pstRangeAction, state, variationDirection));
+ }
+
+ public OpenRaoMPConstraint addTotalPstRangeActionTapVariationConstraint(PstRangeAction pstRangeAction, State state) {
+ return solver.makeConstraint(0, 0, totalPstRangeActionTapVariationConstraintId(pstRangeAction, state));
+ }
+
+ public OpenRaoMPConstraint getTotalPstRangeActionTapVariationConstraint(PstRangeAction pstRangeAction, State state) {
+ return solver.getConstraint(totalPstRangeActionTapVariationConstraintId(pstRangeAction, state));
+ }
+
+ public OpenRaoMPVariable addTapVariable(PstRangeAction pstRangeAction, State state) {
+ int minTap = pstRangeAction.getTapToAngleConversionMap().keySet().stream().min(Integer::compareTo).orElseThrow();
+ int maxTap = pstRangeAction.getTapToAngleConversionMap().keySet().stream().max(Integer::compareTo).orElseThrow();
+ return solver.makeIntVar(minTap, maxTap, tapVariableId(pstRangeAction, state));
+ }
+
+ public OpenRaoMPVariable getTapVariable(PstRangeAction pstRangeAction, State state) {
+ return solver.getVariable(tapVariableId(pstRangeAction, state));
+ }
+
+ public OpenRaoMPConstraint addTapConstraint(PstRangeAction pstRangeAction, State state) {
+ return solver.makeConstraint(0, 0, tapConstraintId(pstRangeAction, state));
+ }
+
+ public OpenRaoMPConstraint getTapConstraint(PstRangeAction pstRangeAction, State state) {
+ return solver.getConstraint(tapConstraintId(pstRangeAction, state));
+ }
+
public double infinity() {
return solver.infinity();
}
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblemBuilder.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblemBuilder.java
index 91e94a828e..d5ea4cc2db 100644
--- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblemBuilder.java
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblemBuilder.java
@@ -118,10 +118,17 @@ public LinearProblemBuilder withInitialRangeActionActivationResult(RangeActionAc
}
private ProblemFiller buildCoreProblemFiller() {
- return new CoreProblemFiller(
+ return parameters.getObjectiveFunction().costOptimization() ? new CostCoreProblemFiller(
inputs.optimizationPerimeter(),
inputs.prePerimeterSetpoints(),
- parameters.getRangeActionParameters(),
+ parameters.getRangeActionParameters(),
+ parameters.getObjectiveFunctionUnit(),
+ parameters.getRaRangeShrinking(),
+ parameters.getRangeActionParameters().getPstModel()
+ ) : new MarginCoreProblemFiller(
+ inputs.optimizationPerimeter(),
+ inputs.prePerimeterSetpoints(),
+ parameters.getRangeActionParameters(),
parameters.getObjectiveFunctionUnit(),
parameters.getRaRangeShrinking(),
parameters.getRangeActionParameters().getPstModel()
@@ -140,8 +147,8 @@ private ProblemFiller buildMaxMinRelativeMarginFiller() {
private ProblemFiller buildMaxMinMarginFiller() {
return new MaxMinMarginFiller(
inputs.optimizationPerimeter().getOptimizedFlowCnecs(),
- parameters.getObjectiveFunctionUnit()
- );
+ parameters.getObjectiveFunctionUnit(),
+ parameters.getObjectiveFunction().costOptimization());
}
private ProblemFiller buildMnecFiller() {
@@ -163,9 +170,9 @@ private ProblemFiller buildLoopFlowFiller() {
private ProblemFiller buildUnoptimizedCnecFiller() {
return new UnoptimizedCnecFiller(
- inputs.optimizationPerimeter().getFlowCnecs(),
- inputs.prePerimeterFlowResult(),
- parameters.getUnoptimizedCnecParameters()
+ inputs.optimizationPerimeter().getFlowCnecs(),
+ inputs.prePerimeterFlowResult(),
+ parameters.getUnoptimizedCnecParameters()
);
}
@@ -173,7 +180,9 @@ private ProblemFiller buildIntegerPstTapFiller(Map> p
return new DiscretePstTapFiller(
inputs.optimizationPerimeter(),
pstRangeActions,
- inputs.prePerimeterSetpoints()
+ inputs.prePerimeterSetpoints(),
+ parameters.getRangeActionParameters(),
+ parameters.getObjectiveFunction().costOptimization()
);
}
@@ -194,7 +203,7 @@ private ProblemFiller buildRaUsageLimitsFiller() {
inputs.prePerimeterSetpoints(),
parameters.getRaLimitationParameters(),
parameters.getRangeActionParameters().getPstModel() == RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS,
- inputs.network());
+ inputs.network(), parameters.getObjectiveFunction().costOptimization());
}
private Map>> copyWithoutPstRangeActions(Map>> inRangeActions) {
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblemIdGenerator.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblemIdGenerator.java
index 35345d76ed..e21ebd1bb0 100644
--- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblemIdGenerator.java
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/linearproblem/LinearProblemIdGenerator.java
@@ -49,9 +49,9 @@ public final class LinearProblemIdGenerator {
private static final String PST_ABSOLUTE_VARIATION_FROM_INITIAL_TAP = "pstabsolutevariationfrominitialtap";
private static final String MAX_ELEMENTARY_ACTIONS_PER_TSO = "maxelementaryactionspertso";
private static final String RANGE_ACTION_VARIATION = "rangeactionvariation";
- private static final String RANGE_ACTION_ACTIVATION = "rangeactionactivation";
private static final String RANGE_ACTION_SET_POINT_VARIATION = "rangeactionsetpointvariation";
private static final String RANGE_ACTION_ABSOLUTE_VARIATION = "rangeactionabsolutevariation";
+ private static final String TOTAL_PST_RANGE_ACTION_TAP_VARIATION = "totalpstrangeactiontapvariation";
private LinearProblemIdGenerator() {
// Should not be instantiated
@@ -228,4 +228,20 @@ public static String rangeActionSetPointVariationConstraintId(RangeAction> ran
public static String rangeActionAbsoluteVariationConstraintId(RangeAction> rangeAction, State state) {
return RANGE_ACTION_ABSOLUTE_VARIATION + SEPARATOR + rangeAction.getId() + SEPARATOR + state.getId() + SEPARATOR + CONSTRAINT_SUFFIX;
}
+
+ public static String totalPstRangeActionTapVariationVariableId(PstRangeAction pstRangeAction, State state, LinearProblem.VariationDirectionExtension variationDirection) {
+ return TOTAL_PST_RANGE_ACTION_TAP_VARIATION + SEPARATOR + pstRangeAction.getId() + SEPARATOR + state.getId() + SEPARATOR + VARIABLE_SUFFIX + SEPARATOR + variationDirection;
+ }
+
+ public static String totalPstRangeActionTapVariationConstraintId(PstRangeAction pstRangeAction, State state) {
+ return TOTAL_PST_RANGE_ACTION_TAP_VARIATION + SEPARATOR + pstRangeAction.getId() + SEPARATOR + state.getId() + SEPARATOR + CONSTRAINT_SUFFIX;
+ }
+
+ public static String tapVariableId(PstRangeAction pstRangeAction, State state) {
+ return TAP + SEPARATOR + pstRangeAction.getId() + SEPARATOR + state.getId() + SEPARATOR + VARIABLE_SUFFIX;
+ }
+
+ public static String tapConstraintId(PstRangeAction pstRangeAction, State state) {
+ return TAP + SEPARATOR + pstRangeAction.getId() + SEPARATOR + state.getId() + SEPARATOR + CONSTRAINT_SUFFIX;
+ }
}
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/AbstractFunctionalCostComputer.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/AbstractFunctionalCostComputer.java
new file mode 100644
index 0000000000..bfcdb039a5
--- /dev/null
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/AbstractFunctionalCostComputer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2024, 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.functionalcostcomputer;
+
+import com.powsybl.openrao.data.crac.api.*;
+import com.powsybl.openrao.searchtreerao.result.api.OptimizationResult;
+
+import java.util.Map;
+import java.util.stream.DoubleStream;
+
+/**
+ * @author Thomas Bouquet {@literal }
+ */
+public abstract class AbstractFunctionalCostComputer {
+ protected final OptimizationResult secondPreventivePerimeterResult;
+ protected final Map postContingencyResults;
+
+ protected AbstractFunctionalCostComputer(OptimizationResult secondPreventivePerimeterResult, Map postContingencyResults) {
+ this.secondPreventivePerimeterResult = secondPreventivePerimeterResult;
+ this.postContingencyResults = postContingencyResults;
+ }
+
+ protected DoubleStream streamPostContingencyResultsBeforeInstant(Instant instant) {
+ return postContingencyResults.entrySet().stream()
+ .filter(entry -> !entry.getKey().getInstant().comesAfter(instant))
+ .map(Map.Entry::getValue)
+ .filter(AbstractFunctionalCostComputer::hasActualFunctionalCost)
+ .mapToDouble(OptimizationResult::getFunctionalCost);
+ }
+
+ /**
+ * Returns true if the perimeter has an actual functional cost, ie has CNECs
+ * (as opposed to a perimeter with pure MNECs only)
+ */
+ private static boolean hasActualFunctionalCost(OptimizationResult perimeterResult) {
+ return !perimeterResult.getMostLimitingElements(1).isEmpty();
+ }
+
+ public abstract double computeFunctionalCost(Instant instant);
+}
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/MaximumFunctionalCostComputer.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/MaximumFunctionalCostComputer.java
new file mode 100644
index 0000000000..b28272c014
--- /dev/null
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/MaximumFunctionalCostComputer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2024, 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.functionalcostcomputer;
+
+import com.powsybl.openrao.data.crac.api.Instant;
+import com.powsybl.openrao.data.crac.api.State;
+import com.powsybl.openrao.searchtreerao.result.api.OptimizationResult;
+
+import java.util.Map;
+
+/**
+ * @author Thomas Bouquet {@literal }
+ */
+public class MaximumFunctionalCostComputer extends AbstractFunctionalCostComputer {
+ public MaximumFunctionalCostComputer(OptimizationResult secondPreventivePerimeterResult, Map postContingencyResults) {
+ super(secondPreventivePerimeterResult, postContingencyResults);
+ }
+
+ @Override
+ public double computeFunctionalCost(Instant instant) {
+ return Math.max(secondPreventivePerimeterResult.getFunctionalCost(), streamPostContingencyResultsBeforeInstant(instant).max().orElse(-Double.MAX_VALUE));
+ }
+}
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/TotalFunctionalCostComputer.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/TotalFunctionalCostComputer.java
new file mode 100644
index 0000000000..0ea9ef1731
--- /dev/null
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/TotalFunctionalCostComputer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2024, 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.functionalcostcomputer;
+
+import com.powsybl.openrao.data.crac.api.Instant;
+import com.powsybl.openrao.data.crac.api.State;
+import com.powsybl.openrao.searchtreerao.result.api.OptimizationResult;
+
+import java.util.Map;
+
+/**
+ * @author Thomas Bouquet {@literal }
+ */
+public class TotalFunctionalCostComputer extends AbstractFunctionalCostComputer {
+ public TotalFunctionalCostComputer(OptimizationResult secondPreventivePerimeterResult, Map postContingencyResults) {
+ super(secondPreventivePerimeterResult, postContingencyResults);
+ }
+
+ @Override
+ public double computeFunctionalCost(Instant instant) {
+ return instant == null ? 0.0 : secondPreventivePerimeterResult.getFunctionalCost() + streamPostContingencyResultsBeforeInstant(instant).sum();
+ }
+}
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/CurativeWithSecondPraoResult.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/CurativeWithSecondPraoResult.java
index 5dd6246b47..b28d9c7cf1 100644
--- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/CurativeWithSecondPraoResult.java
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/CurativeWithSecondPraoResult.java
@@ -40,8 +40,9 @@ public class CurativeWithSecondPraoResult implements OptimizationResult {
private final FlowResult postCraSensitivityFlowResult; // contains final flows
private final ObjectiveFunctionResult postCraSensitivityObjectiveResult; // contains final flows
private final SensitivityResult postCraSensitivitySensitivityResult; // contains final flows
+ private final boolean costOptimization;
- private CurativeWithSecondPraoResult(State state, OptimizationResult firstCraoResult, OptimizationResult secondPraoResult, Set> remedialActionsExcludedFromSecondPreventive, FlowResult postCraSensitivityFlowResult, ObjectiveFunctionResult postCraSensitivityObjectiveResult, SensitivityResult postCraSensitivitySensitivityResult) {
+ private CurativeWithSecondPraoResult(State state, OptimizationResult firstCraoResult, OptimizationResult secondPraoResult, Set> remedialActionsExcludedFromSecondPreventive, FlowResult postCraSensitivityFlowResult, ObjectiveFunctionResult postCraSensitivityObjectiveResult, SensitivityResult postCraSensitivitySensitivityResult, boolean costOptimization) {
this.state = state;
this.firstCraoResult = firstCraoResult;
this.secondPraoResult = secondPraoResult;
@@ -49,10 +50,11 @@ private CurativeWithSecondPraoResult(State state, OptimizationResult firstCraoRe
this.postCraSensitivityFlowResult = postCraSensitivityFlowResult;
this.postCraSensitivityObjectiveResult = postCraSensitivityObjectiveResult;
this.postCraSensitivitySensitivityResult = postCraSensitivitySensitivityResult;
+ this.costOptimization = costOptimization;
}
- public CurativeWithSecondPraoResult(State state, OptimizationResult firstCraoResult, OptimizationResult secondPraoResult, Set> remedialActionsExcludedFromSecondPreventive, PrePerimeterResult postCraPrePerimeterResult) {
- this(state, firstCraoResult, secondPraoResult, remedialActionsExcludedFromSecondPreventive, postCraPrePerimeterResult, postCraPrePerimeterResult, postCraPrePerimeterResult);
+ public CurativeWithSecondPraoResult(State state, OptimizationResult firstCraoResult, OptimizationResult secondPraoResult, Set> remedialActionsExcludedFromSecondPreventive, PrePerimeterResult postCraPrePerimeterResult, boolean costOptimization) {
+ this(state, firstCraoResult, secondPraoResult, remedialActionsExcludedFromSecondPreventive, postCraPrePerimeterResult, postCraPrePerimeterResult, postCraPrePerimeterResult, costOptimization);
}
private void checkState(State stateToCheck) {
@@ -123,9 +125,13 @@ public Set getActivatedNetworkActions() {
@Override
public double getFunctionalCost() {
- // Careful : this returns functional cost over all curative perimeters, but it should be enough for normal use
- // since we never really need functional cost per perimeter at the end of the RAO
- return postCraSensitivityObjectiveResult.getFunctionalCost();
+ if (costOptimization) {
+ return getActivatedNetworkActions().stream().mapToDouble(networkAction -> networkAction.getActivationCost().orElse(0.0)).sum();
+ } else {
+ // Careful : this returns functional cost over all curative perimeters, but it should be enough for normal use
+ // since we never really need functional cost per perimeter at the end of the RAO
+ return postCraSensitivityObjectiveResult.getFunctionalCost();
+ }
}
@Override
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/LinearProblemResult.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/LinearProblemResult.java
index d1e1852808..3da997ce45 100644
--- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/LinearProblemResult.java
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/LinearProblemResult.java
@@ -22,8 +22,8 @@ public LinearProblemResult(LinearProblem linearProblem, RangeActionSetpointResul
optimizationContext.getRangeActionsPerState().forEach((state, rangeActions) ->
rangeActions.forEach(rangeAction -> {
if (linearProblem.getAbsoluteRangeActionVariationVariable(rangeAction, state).solutionValue() > 1e-6) {
- double setpoint = linearProblem.getRangeActionSetpointVariable(rangeAction, state).solutionValue();
- putResult(rangeAction, state, setpoint);
+ double setPoint = linearProblem.getRangeActionSetpointVariable(rangeAction, state).solutionValue();
+ putResult(rangeAction, state, setPoint);
}
})
);
diff --git a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/PreventiveAndCurativesRaoResultImpl.java b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/PreventiveAndCurativesRaoResultImpl.java
index a192c51bfc..903259a4e1 100644
--- a/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/PreventiveAndCurativesRaoResultImpl.java
+++ b/ra-optimisation/search-tree-rao/src/main/java/com/powsybl/openrao/searchtreerao/result/impl/PreventiveAndCurativesRaoResultImpl.java
@@ -26,6 +26,8 @@
import com.powsybl.openrao.searchtreerao.castor.algorithm.Perimeter;
import com.powsybl.openrao.searchtreerao.result.api.*;
import com.powsybl.openrao.searchtreerao.castor.algorithm.StateTree;
+import com.powsybl.openrao.searchtreerao.result.functionalcostcomputer.MaximumFunctionalCostComputer;
+import com.powsybl.openrao.searchtreerao.result.functionalcostcomputer.TotalFunctionalCostComputer;
import java.util.*;
@@ -242,6 +244,9 @@ public OptimizationResult getOptimizationResult(Instant optimizedInstant, State
@Override
public double getFunctionalCost(Instant optimizedInstant) {
+ if (objectiveFunctionParameters.getType().costOptimization()) {
+ return new TotalFunctionalCostComputer(secondPreventivePerimeterResult, postContingencyResults).computeFunctionalCost(optimizedInstant);
+ }
if (optimizedInstant == null) {
return initialResult.getFunctionalCost();
} else if (optimizedInstant.isPreventive() || optimizedInstant.isOutage() || postContingencyResults.isEmpty() ||
@@ -254,7 +259,7 @@ public double getFunctionalCost(Instant optimizedInstant) {
} else {
// No second preventive was run => use CRAO1 results
// OR ARA
- return objectiveFunctionParameters.getType().costOptimization() ? getTotalFunctionalCostForInstant(optimizedInstant) : getHighestFunctionalForInstant(optimizedInstant);
+ return new MaximumFunctionalCostComputer(secondPreventivePerimeterResult, postContingencyResults).computeFunctionalCost(optimizedInstant);
}
}
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/LinearProblemBuilderTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/LinearProblemBuilderTest.java
index c9d0aa100d..3fcc6e2c46 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/LinearProblemBuilderTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/LinearProblemBuilderTest.java
@@ -75,7 +75,7 @@ void testBuildMaxMarginContinuous() {
assertNotNull(linearProblem);
List fillers = linearProblem.getFillers();
assertEquals(3, fillers.size());
- assertInstanceOf(CoreProblemFiller.class, fillers.get(0));
+ assertInstanceOf(MarginCoreProblemFiller.class, fillers.get(0));
assertInstanceOf(MaxMinMarginFiller.class, fillers.get(1));
assertInstanceOf(ContinuousRangeActionGroupFiller.class, fillers.get(2));
}
@@ -89,7 +89,7 @@ void testBuildMaxMarginDiscrete() {
assertNotNull(linearProblem);
List fillers = linearProblem.getFillers();
assertEquals(5, fillers.size());
- assertInstanceOf(CoreProblemFiller.class, fillers.get(0));
+ assertInstanceOf(MarginCoreProblemFiller.class, fillers.get(0));
assertInstanceOf(MaxMinMarginFiller.class, fillers.get(1));
assertInstanceOf(DiscretePstTapFiller.class, fillers.get(2));
assertInstanceOf(DiscretePstGroupFiller.class, fillers.get(3));
@@ -105,7 +105,7 @@ void testBuildMaxRelativeMarginContinuous() {
assertNotNull(linearProblem);
List fillers = linearProblem.getFillers();
assertEquals(3, fillers.size());
- assertInstanceOf(CoreProblemFiller.class, fillers.get(0));
+ assertInstanceOf(MarginCoreProblemFiller.class, fillers.get(0));
assertInstanceOf(MaxMinRelativeMarginFiller.class, fillers.get(1));
assertInstanceOf(ContinuousRangeActionGroupFiller.class, fillers.get(2));
}
@@ -122,7 +122,7 @@ void testBuildMaxMarginContinuousMnecLoopflowUnoptimized() {
assertNotNull(linearProblem);
List fillers = linearProblem.getFillers();
assertEquals(6, fillers.size());
- assertInstanceOf(CoreProblemFiller.class, fillers.get(0));
+ assertInstanceOf(MarginCoreProblemFiller.class, fillers.get(0));
assertInstanceOf(MaxMinMarginFiller.class, fillers.get(1));
assertInstanceOf(MnecFiller.class, fillers.get(2));
assertInstanceOf(MaxLoopFlowFiller.class, fillers.get(3));
@@ -143,7 +143,7 @@ void testBuildMaxMarginContinuousRaLimitation() {
assertNotNull(linearProblem);
List fillers = linearProblem.getFillers();
assertEquals(4, fillers.size());
- assertInstanceOf(CoreProblemFiller.class, fillers.get(0));
+ assertInstanceOf(MarginCoreProblemFiller.class, fillers.get(0));
assertInstanceOf(MaxMinMarginFiller.class, fillers.get(1));
assertInstanceOf(ContinuousRangeActionGroupFiller.class, fillers.get(2));
assertInstanceOf(RaUsageLimitsFiller.class, fillers.get(3));
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/ContinuousRangeActionGroupFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/ContinuousRangeActionGroupFillerTest.java
index 897456d3e0..9b8d62d4d6 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/ContinuousRangeActionGroupFillerTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/ContinuousRangeActionGroupFillerTest.java
@@ -58,7 +58,7 @@ void testFillAndUpdateMethods() throws IOException {
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(new RaoParameters());
- CoreProblemFiller coreProblemFiller = new CoreProblemFiller(
+ MarginCoreProblemFiller coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
rangeActionParameters,
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/CostCoreProblemFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/CostCoreProblemFillerTest.java
new file mode 100644
index 0000000000..4e27cd3036
--- /dev/null
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/CostCoreProblemFillerTest.java
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2024, 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.linearoptimisation.algorithms.fillers;
+
+import com.powsybl.iidm.network.TwoSides;
+import com.powsybl.openrao.commons.OpenRaoException;
+import com.powsybl.openrao.commons.Unit;
+import com.powsybl.openrao.data.crac.api.State;
+import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
+import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction;
+import com.powsybl.openrao.data.raoresult.api.ComputationStatus;
+import com.powsybl.openrao.raoapi.parameters.RangeActionsOptimizationParameters;
+import com.powsybl.openrao.raoapi.parameters.RaoParameters;
+import com.powsybl.openrao.searchtreerao.commons.optimizationperimeters.OptimizationPerimeter;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.LinearProblem;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.LinearProblemBuilder;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPConstraint;
+import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.OpenRaoMPVariable;
+import com.powsybl.openrao.searchtreerao.result.api.RangeActionSetpointResult;
+import com.powsybl.openrao.searchtreerao.result.impl.RangeActionActivationResultImpl;
+import com.powsybl.openrao.searchtreerao.result.impl.RangeActionSetpointResultImpl;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author Thomas Bouquet {@literal }
+ */
+class CostCoreProblemFillerTest extends AbstractFillerTest {
+ private LinearProblem linearProblem;
+ private CostCoreProblemFiller coreProblemFiller;
+ private RangeActionSetpointResult initialRangeActionSetpointResult;
+ // some additional data
+ private double minAlpha;
+ private double maxAlpha;
+ private double initialAlpha;
+
+ @BeforeEach
+ public void setUp() throws IOException {
+ init();
+ // arrange some additional data
+ network.getTwoWindingsTransformer(RANGE_ACTION_ELEMENT_ID).getPhaseTapChanger().setTapPosition(TAP_INITIAL);
+ minAlpha = crac.getRangeAction(RANGE_ACTION_ID).getMinAdmissibleSetpoint(0);
+ maxAlpha = crac.getRangeAction(RANGE_ACTION_ID).getMaxAdmissibleSetpoint(0);
+ initialAlpha = pstRangeAction.convertTapToAngle(network.getTwoWindingsTransformer(RANGE_ACTION_ELEMENT_ID).getPhaseTapChanger().getTapPosition());
+
+ initialRangeActionSetpointResult = new RangeActionSetpointResultImpl(Map.of(pstRangeAction, initialAlpha));
+ }
+
+ private void buildLinearProblem() {
+ linearProblem = new LinearProblemBuilder()
+ .withProblemFiller(coreProblemFiller)
+ .withSolver(RangeActionsOptimizationParameters.Solver.SCIP)
+ .withInitialRangeActionActivationResult(getInitialRangeActionActivationResult())
+ .build();
+ linearProblem.fill(flowResult, sensitivityResult);
+ }
+
+ private void initializeForPreventive(double pstSensitivityThreshold, double hvdcSensitivityThreshold, double injectionSensitivityThreshold) {
+ initialize(Set.of(cnec1), pstSensitivityThreshold, hvdcSensitivityThreshold, injectionSensitivityThreshold, crac.getPreventiveState(), false, RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+ }
+
+ private void initializeForGlobal(RangeActionsOptimizationParameters.PstModel pstModel) {
+ initialize(Set.of(cnec1, cnec2), 1e-6, 1e-6, 1e-6, crac.getPreventiveState(), false, pstModel);
+ }
+
+ private void initialize(Set cnecs, double pstSensitivityThreshold, double hvdcSensitivityThreshold, double injectionSensitivityThreshold, State mainState, boolean raRangeShrinking, RangeActionsOptimizationParameters.PstModel pstModel) {
+ OptimizationPerimeter optimizationPerimeter = Mockito.mock(OptimizationPerimeter.class);
+ Mockito.when(optimizationPerimeter.getFlowCnecs()).thenReturn(cnecs);
+ Mockito.when(optimizationPerimeter.getMainOptimizationState()).thenReturn(mainState);
+
+ Map>> rangeActions = new HashMap<>();
+ cnecs.forEach(cnec -> rangeActions.put(cnec.getState(), Set.of(pstRangeAction)));
+ Mockito.when(optimizationPerimeter.getRangeActionsPerState()).thenReturn(rangeActions);
+
+ RaoParameters raoParameters = new RaoParameters();
+ raoParameters.getRangeActionsOptimizationParameters().setPstSensitivityThreshold(pstSensitivityThreshold);
+ raoParameters.getRangeActionsOptimizationParameters().setHvdcSensitivityThreshold(hvdcSensitivityThreshold);
+ raoParameters.getRangeActionsOptimizationParameters().setInjectionRaSensitivityThreshold(injectionSensitivityThreshold);
+ RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(raoParameters);
+
+ coreProblemFiller = new CostCoreProblemFiller(
+ optimizationPerimeter,
+ initialRangeActionSetpointResult,
+ rangeActionParameters,
+ Unit.MEGAWATT, raRangeShrinking, pstModel);
+ buildLinearProblem();
+ }
+
+ @Test
+ void fillTestOnPreventive() {
+ initializeForPreventive(1e-6, 1e-6, 1e-6);
+ State state = cnec1.getState();
+
+ // check range action setpoint variable
+ OpenRaoMPVariable setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, state);
+ assertNotNull(setPointVariable);
+ assertEquals(minAlpha, setPointVariable.lb(), DOUBLE_TOLERANCE);
+ assertEquals(maxAlpha, setPointVariable.ub(), DOUBLE_TOLERANCE);
+
+ // check upward variation variable
+ OpenRaoMPVariable upwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD);
+ assertNotNull(upwardVariationVariable);
+ assertEquals(0, upwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), upwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check downward variation variable
+ OpenRaoMPVariable downwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.DOWNWARD);
+ assertNotNull(downwardVariationVariable);
+ assertEquals(0, downwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), downwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check binary activation variable
+ OpenRaoMPVariable activationVariable = linearProblem.getRangeActionVariationBinary(pstRangeAction, state);
+ assertNotNull(activationVariable);
+ assertEquals(0, activationVariable.lb(), 0.01);
+ assertEquals(1, activationVariable.ub(), 0.01);
+
+ // check flow variable for cnec1
+ OpenRaoMPVariable flowVariable = linearProblem.getFlowVariable(cnec1, TwoSides.ONE);
+ assertNotNull(flowVariable);
+ assertEquals(-linearProblem.infinity(), flowVariable.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check flow constraint for cnec1
+ OpenRaoMPConstraint flowConstraint = linearProblem.getFlowConstraint(cnec1, TwoSides.ONE);
+ assertNotNull(flowConstraint);
+ assertEquals(REF_FLOW_CNEC1_IT1 - initialAlpha * SENSI_CNEC1_IT1, flowConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(REF_FLOW_CNEC1_IT1 - initialAlpha * SENSI_CNEC1_IT1, flowConstraint.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, flowConstraint.getCoefficient(flowVariable), 0.1);
+ assertEquals(-SENSI_CNEC1_IT1, flowConstraint.getCoefficient(setPointVariable), DOUBLE_TOLERANCE);
+
+ // check flow variable for cnec2 does not exist
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowVariable(cnec2, TwoSides.TWO));
+ assertEquals("Variable Tieline BE FR - Defaut - N-1 NL1-NL3_two_flow_variable has not been created yet", e.getMessage());
+
+ // check flow constraint for cnec2 does not exist
+ e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowConstraint(cnec2, TwoSides.TWO));
+ assertEquals("Constraint Tieline BE FR - Defaut - N-1 NL1-NL3_two_flow_constraint has not been created yet", e.getMessage());
+
+ // check set-point variation constraint
+ OpenRaoMPConstraint setPointVariationConstraint = linearProblem.getRangeActionSetPointVariationConstraint(pstRangeAction, state);
+ assertNotNull(setPointVariationConstraint);
+ assertEquals(1.9479, setPointVariationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(1.9479, setPointVariationConstraint.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, setPointVariationConstraint.getCoefficient(setPointVariable));
+ assertEquals(-1, setPointVariationConstraint.getCoefficient(upwardVariationVariable));
+ assertEquals(1, setPointVariationConstraint.getCoefficient(downwardVariationVariable));
+
+ // check activation constraint
+ OpenRaoMPConstraint activationConstraint = linearProblem.getIsVariationConstraint(pstRangeAction, state);
+ assertNotNull(activationConstraint);
+ assertEquals(0, activationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(linearProblem.infinity(), activationConstraint.ub(), linearProblem.infinity() * 1e-3);
+ assertEquals(-1, activationConstraint.getCoefficient(upwardVariationVariable));
+ assertEquals(-1, activationConstraint.getCoefficient(downwardVariationVariable));
+ assertEquals(11.6782, activationConstraint.getCoefficient(activationVariable), DOUBLE_TOLERANCE);
+
+ // check the number of variables and constraints
+ // total number of variables 4 :
+ // - 1 per CNEC (flow)
+ // - 5 per range action (set-point, absolute variation, activation and variation up/down)
+ // total number of constraints 4 :
+ // - 1 per CNEC (flow constraint)
+ // - 3 per range action (activation and set-point + absolute variation)
+ assertEquals(6, linearProblem.numVariables());
+ assertEquals(4, linearProblem.numConstraints());
+
+ // check objective
+ assertEquals(15.0, linearProblem.getObjective().getCoefficient(activationVariable));
+ }
+
+ @Test
+ void fillTestOnPreventiveFiltered() {
+ initializeForPreventive(2.5, 2.5, 2.5);
+ State state = cnec1.getState();
+
+ // check range action setpoint variable
+ OpenRaoMPVariable setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, state);
+ assertNotNull(setPointVariable);
+ assertEquals(minAlpha, setPointVariable.lb(), DOUBLE_TOLERANCE);
+ assertEquals(maxAlpha, setPointVariable.ub(), DOUBLE_TOLERANCE);
+
+ // check upward variation variable
+ OpenRaoMPVariable upwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD);
+ assertNotNull(upwardVariationVariable);
+ assertEquals(0, upwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), upwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check downward variation variable
+ OpenRaoMPVariable downwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.DOWNWARD);
+ assertNotNull(downwardVariationVariable);
+ assertEquals(0, downwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), downwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check binary activation variable
+ OpenRaoMPVariable activationVariable = linearProblem.getRangeActionVariationBinary(pstRangeAction, state);
+ assertNotNull(activationVariable);
+ assertEquals(0, activationVariable.lb(), 0.01);
+ assertEquals(1, activationVariable.ub(), 0.01);
+
+ // check flow variable for cnec1
+ OpenRaoMPVariable flowVariable = linearProblem.getFlowVariable(cnec1, TwoSides.ONE);
+ assertNotNull(flowVariable);
+ assertEquals(-linearProblem.infinity(), flowVariable.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check flow constraint for cnec1
+ OpenRaoMPConstraint flowConstraint = linearProblem.getFlowConstraint(cnec1, TwoSides.ONE);
+ assertNotNull(flowConstraint);
+ assertEquals(REF_FLOW_CNEC1_IT1 - initialAlpha * 0, flowConstraint.lb(), DOUBLE_TOLERANCE); // sensitivity filtered (= 0)
+ assertEquals(REF_FLOW_CNEC1_IT1 - initialAlpha * 0, flowConstraint.ub(), DOUBLE_TOLERANCE); // sensitivity filtered (= 0)
+ assertEquals(1, flowConstraint.getCoefficient(flowVariable), 0.1);
+ assertEquals(0, flowConstraint.getCoefficient(setPointVariable), DOUBLE_TOLERANCE); // sensitivity filtered (= 0)
+
+ // check flow variable for cnec2 does not exist
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowVariable(cnec2, TwoSides.TWO));
+ assertEquals("Variable Tieline BE FR - Defaut - N-1 NL1-NL3_two_flow_variable has not been created yet", e.getMessage());
+
+ // check flow constraint for cnec2 does not exist
+ e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowConstraint(cnec2, TwoSides.TWO));
+ assertEquals("Constraint Tieline BE FR - Defaut - N-1 NL1-NL3_two_flow_constraint has not been created yet", e.getMessage());
+
+ // check set-point variation constraint
+ OpenRaoMPConstraint setPointVariationConstraint = linearProblem.getRangeActionSetPointVariationConstraint(pstRangeAction, state);
+ assertNotNull(setPointVariationConstraint);
+ assertEquals(1.9479, setPointVariationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(1.9479, setPointVariationConstraint.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, setPointVariationConstraint.getCoefficient(setPointVariable));
+ assertEquals(-1, setPointVariationConstraint.getCoefficient(upwardVariationVariable));
+ assertEquals(1, setPointVariationConstraint.getCoefficient(downwardVariationVariable));
+
+ // check activation constraint
+ OpenRaoMPConstraint activationConstraint = linearProblem.getIsVariationConstraint(pstRangeAction, state);
+ assertNotNull(activationConstraint);
+ assertEquals(0, activationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(linearProblem.infinity(), activationConstraint.ub(), linearProblem.infinity() * 1e-3);
+ assertEquals(-1, activationConstraint.getCoefficient(upwardVariationVariable));
+ assertEquals(-1, activationConstraint.getCoefficient(downwardVariationVariable));
+ assertEquals(11.6782, activationConstraint.getCoefficient(activationVariable), DOUBLE_TOLERANCE);
+
+ // check the number of variables and constraints
+ // total number of variables 4 :
+ // - 1 per CNEC (flow)
+ // - 5 per range action (set-point, absolute variation, activation and variation up/down)
+ // total number of constraints 4 :
+ // - 1 per CNEC (flow constraint)
+ // - 3 per range action (activation and set-point + absolute variation)
+ assertEquals(6, linearProblem.numVariables());
+ assertEquals(4, linearProblem.numConstraints());
+
+ // check objective
+ assertEquals(15.0, linearProblem.getObjective().getCoefficient(activationVariable));
+ }
+
+ @Test
+ void fillTestOnCurative() {
+ initialize(Set.of(cnec2), 1e-6, 1e-6, 1e-6, cnec2.getState(), false, RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+ State state = cnec2.getState();
+
+ // check range action setpoint variable
+ OpenRaoMPVariable setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, state);
+ assertNotNull(setPointVariable);
+ assertEquals(minAlpha, setPointVariable.lb(), DOUBLE_TOLERANCE);
+ assertEquals(maxAlpha, setPointVariable.ub(), DOUBLE_TOLERANCE);
+
+ // check upward variation variable
+ OpenRaoMPVariable upwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD);
+ assertNotNull(upwardVariationVariable);
+ assertEquals(0, upwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), upwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check downward variation variable
+ OpenRaoMPVariable downwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.DOWNWARD);
+ assertNotNull(downwardVariationVariable);
+ assertEquals(0, downwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), downwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check binary activation variable
+ OpenRaoMPVariable activationVariable = linearProblem.getRangeActionVariationBinary(pstRangeAction, state);
+ assertNotNull(activationVariable);
+ assertEquals(0, activationVariable.lb(), 0.01);
+ assertEquals(1, activationVariable.ub(), 0.01);
+
+ // check flow variable for cnec1 does not exist
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowVariable(cnec1, TwoSides.ONE));
+ assertEquals("Variable Tieline BE FR - N - preventive_one_flow_variable has not been created yet", e.getMessage());
+
+ // check flow constraint for cnec1 does not exist
+ e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowConstraint(cnec1, TwoSides.ONE));
+ assertEquals("Constraint Tieline BE FR - N - preventive_one_flow_constraint has not been created yet", e.getMessage());
+
+ // check flow variable for cnec2
+ OpenRaoMPVariable flowVariable2 = linearProblem.getFlowVariable(cnec2, TwoSides.TWO);
+ assertNotNull(flowVariable2);
+ assertEquals(-linearProblem.infinity(), flowVariable2.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable2.ub(), linearProblem.infinity() * 1e-3);
+
+ // check flow constraint for cnec2
+ OpenRaoMPConstraint flowConstraint2 = linearProblem.getFlowConstraint(cnec2, TwoSides.TWO);
+ assertNotNull(flowConstraint2);
+ assertEquals(REF_FLOW_CNEC2_IT1 - initialAlpha * SENSI_CNEC2_IT1, flowConstraint2.lb(), DOUBLE_TOLERANCE);
+ assertEquals(REF_FLOW_CNEC2_IT1 - initialAlpha * SENSI_CNEC2_IT1, flowConstraint2.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, flowConstraint2.getCoefficient(flowVariable2), DOUBLE_TOLERANCE);
+ assertEquals(-SENSI_CNEC2_IT1, flowConstraint2.getCoefficient(setPointVariable), DOUBLE_TOLERANCE);
+
+ // check set-point variation constraint
+ OpenRaoMPConstraint setPointVariationConstraint = linearProblem.getRangeActionSetPointVariationConstraint(pstRangeAction, state);
+ assertNotNull(setPointVariationConstraint);
+ assertEquals(1.9479, setPointVariationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(1.9479, setPointVariationConstraint.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, setPointVariationConstraint.getCoefficient(setPointVariable));
+ assertEquals(-1, setPointVariationConstraint.getCoefficient(upwardVariationVariable));
+ assertEquals(1, setPointVariationConstraint.getCoefficient(downwardVariationVariable));
+
+ // check activation constraint
+ OpenRaoMPConstraint activationConstraint = linearProblem.getIsVariationConstraint(pstRangeAction, state);
+ assertNotNull(activationConstraint);
+ assertEquals(0, activationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(linearProblem.infinity(), activationConstraint.ub(), linearProblem.infinity() * 1e-3);
+ assertEquals(-1, activationConstraint.getCoefficient(upwardVariationVariable));
+ assertEquals(-1, activationConstraint.getCoefficient(downwardVariationVariable));
+ assertEquals(11.6782, activationConstraint.getCoefficient(activationVariable), DOUBLE_TOLERANCE);
+
+ // check the number of variables and constraints
+ // total number of variables 4 :
+ // - 1 per CNEC (flow)
+ // - 5 per range action (set-point, absolute variation, activation and variation up/down)
+ // total number of constraints 4 :
+ // - 1 per CNEC (flow constraint)
+ // - 3 per range action (activation and set-point + absolute variation)
+ assertEquals(6, linearProblem.numVariables());
+ assertEquals(4, linearProblem.numConstraints());
+
+ // check objective
+ assertEquals(15.0, linearProblem.getObjective().getCoefficient(activationVariable));
+ }
+
+ @Test
+ void testContinuousPstMode() {
+ OpenRaoException exception = assertThrows(OpenRaoException.class, () -> initializeForGlobal(RangeActionsOptimizationParameters.PstModel.CONTINUOUS));
+ assertEquals("Costly remedial action optimization is only available for the APPROXIMATED_INTEGERS mode of PST range actions.", exception.getMessage());
+ }
+
+ @Test
+ void fillTestOnGlobal() {
+ initializeForGlobal(RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+ State prevState = cnec1.getState();
+ State curState = cnec2.getState();
+
+ // check relative setpoint constraint for PRA_PST_BE has not been created in curative.
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getRangeActionRelativeSetpointConstraint(pstRangeAction, curState, LinearProblem.RaRangeShrinking.FALSE));
+ assertEquals("Constraint PRA_PST_BE_N-1 NL1-NL3 - curative_relative_setpoint_constraint has not been created yet", e.getMessage());
+
+ // check range action setpoint variable for preventive state
+ OpenRaoMPVariable prevSetPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, prevState);
+ assertNotNull(prevSetPointVariable);
+ assertEquals(minAlpha, prevSetPointVariable.lb(), DOUBLE_TOLERANCE);
+ assertEquals(maxAlpha, prevSetPointVariable.ub(), DOUBLE_TOLERANCE);
+
+ // check upward variation variable for preventive state
+ OpenRaoMPVariable prevUpwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, prevState, LinearProblem.VariationDirectionExtension.UPWARD);
+ assertNotNull(prevUpwardVariationVariable);
+ assertEquals(0, prevUpwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), prevUpwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check downward variation variable for preventive state
+ OpenRaoMPVariable prevDownwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, prevState, LinearProblem.VariationDirectionExtension.DOWNWARD);
+ assertNotNull(prevDownwardVariationVariable);
+ assertEquals(0, prevDownwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), prevDownwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check binary activation variable for preventive state
+ OpenRaoMPVariable prevActivationVariable = linearProblem.getRangeActionVariationBinary(pstRangeAction, prevState);
+ assertNotNull(prevActivationVariable);
+ assertEquals(0, prevActivationVariable.lb(), 0.01);
+ assertEquals(1, prevActivationVariable.ub(), 0.01);
+
+ // check range action setpoint variable for curative state
+ OpenRaoMPVariable curSetPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, curState);
+ assertNotNull(curSetPointVariable);
+ assertEquals(minAlpha, curSetPointVariable.lb(), DOUBLE_TOLERANCE);
+ assertEquals(maxAlpha, curSetPointVariable.ub(), DOUBLE_TOLERANCE);
+
+ // check upward variation variable for curative state
+ OpenRaoMPVariable curUpwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, curState, LinearProblem.VariationDirectionExtension.UPWARD);
+ assertNotNull(curUpwardVariationVariable);
+ assertEquals(0, curUpwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), curUpwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check downward variation variable for curative state
+ OpenRaoMPVariable curDownwardVariationVariable = linearProblem.getRangeActionVariationVariable(pstRangeAction, curState, LinearProblem.VariationDirectionExtension.DOWNWARD);
+ assertNotNull(curDownwardVariationVariable);
+ assertEquals(0, curDownwardVariationVariable.lb(), 0.01);
+ assertEquals(linearProblem.infinity(), curDownwardVariationVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check binary activation variable for curative state
+ OpenRaoMPVariable curActivationVariable = linearProblem.getRangeActionVariationBinary(pstRangeAction, curState);
+ assertNotNull(curActivationVariable);
+ assertEquals(0, curActivationVariable.lb(), 0.01);
+ assertEquals(1, curActivationVariable.ub(), 0.01);
+
+ // check flow variable for cnec1
+ OpenRaoMPVariable flowVariable = linearProblem.getFlowVariable(cnec1, TwoSides.ONE);
+ assertNotNull(flowVariable);
+ assertEquals(-linearProblem.infinity(), flowVariable.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check flow constraint for cnec1
+ OpenRaoMPConstraint flowConstraint = linearProblem.getFlowConstraint(cnec1, TwoSides.ONE);
+ assertNotNull(flowConstraint);
+ assertEquals(REF_FLOW_CNEC1_IT1 - initialAlpha * SENSI_CNEC1_IT1, flowConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(REF_FLOW_CNEC1_IT1 - initialAlpha * SENSI_CNEC1_IT1, flowConstraint.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, flowConstraint.getCoefficient(flowVariable), 0.1);
+ assertEquals(-SENSI_CNEC1_IT1, flowConstraint.getCoefficient(prevSetPointVariable), DOUBLE_TOLERANCE);
+
+ // check flow variable for cnec2
+ OpenRaoMPVariable flowVariable2 = linearProblem.getFlowVariable(cnec2, TwoSides.TWO);
+ assertNotNull(flowVariable2);
+ assertEquals(-linearProblem.infinity(), flowVariable2.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable2.ub(), linearProblem.infinity() * 1e-3);
+
+ // check flow constraint for cnec2
+ OpenRaoMPConstraint flowConstraint2 = linearProblem.getFlowConstraint(cnec2, TwoSides.TWO);
+ assertNotNull(flowConstraint2);
+ assertEquals(REF_FLOW_CNEC2_IT1 - initialAlpha * SENSI_CNEC2_IT1, flowConstraint2.lb(), DOUBLE_TOLERANCE);
+ assertEquals(REF_FLOW_CNEC2_IT1 - initialAlpha * SENSI_CNEC2_IT1, flowConstraint2.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, flowConstraint2.getCoefficient(flowVariable2), DOUBLE_TOLERANCE);
+ assertEquals(-SENSI_CNEC2_IT1, flowConstraint2.getCoefficient(curSetPointVariable), DOUBLE_TOLERANCE);
+
+ // check set-point variation constraint for preventive state
+ OpenRaoMPConstraint prevSetPointVariationConstraint = linearProblem.getRangeActionSetPointVariationConstraint(pstRangeAction, prevState);
+ assertNotNull(prevSetPointVariationConstraint);
+ assertEquals(1.9479, prevSetPointVariationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(1.9479, prevSetPointVariationConstraint.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, prevSetPointVariationConstraint.getCoefficient(prevSetPointVariable));
+ assertEquals(-1, prevSetPointVariationConstraint.getCoefficient(prevUpwardVariationVariable));
+ assertEquals(1, prevSetPointVariationConstraint.getCoefficient(prevDownwardVariationVariable));
+
+ // check activation constraint for preventive state
+ OpenRaoMPConstraint prevActivationConstraint = linearProblem.getIsVariationConstraint(pstRangeAction, prevState);
+ assertNotNull(prevActivationConstraint);
+ assertEquals(0, prevActivationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(linearProblem.infinity(), prevActivationConstraint.ub(), linearProblem.infinity() * 1e-3);
+ assertEquals(-1, prevActivationConstraint.getCoefficient(prevUpwardVariationVariable));
+ assertEquals(-1, prevActivationConstraint.getCoefficient(prevDownwardVariationVariable));
+ assertEquals(11.6782, prevActivationConstraint.getCoefficient(prevActivationVariable), DOUBLE_TOLERANCE);
+
+ // check set-point variation constraint for curative state
+ OpenRaoMPConstraint curSetPointVariationConstraint = linearProblem.getRangeActionSetPointVariationConstraint(pstRangeAction, curState);
+ assertNotNull(curSetPointVariationConstraint);
+ assertEquals(0.0, curSetPointVariationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(0.0, curSetPointVariationConstraint.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, curSetPointVariationConstraint.getCoefficient(curSetPointVariable));
+ assertEquals(-1, curSetPointVariationConstraint.getCoefficient(curUpwardVariationVariable));
+ assertEquals(1, curSetPointVariationConstraint.getCoefficient(curDownwardVariationVariable));
+ assertEquals(-1, curSetPointVariationConstraint.getCoefficient(prevSetPointVariable));
+
+ // check activation constraint for curative state
+ OpenRaoMPConstraint curActivationConstraint = linearProblem.getIsVariationConstraint(pstRangeAction, curState);
+ assertNotNull(curActivationConstraint);
+ assertEquals(0, curActivationConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(linearProblem.infinity(), curActivationConstraint.ub(), linearProblem.infinity() * 1e-3);
+ assertEquals(-1, curActivationConstraint.getCoefficient(curUpwardVariationVariable));
+ assertEquals(-1, curActivationConstraint.getCoefficient(curDownwardVariationVariable));
+ assertEquals(11.6782, curActivationConstraint.getCoefficient(curActivationVariable), DOUBLE_TOLERANCE);
+
+ // check the number of variables and constraints
+ // total number of variables 10 :
+ // - 1 per CNEC (flow)
+ // - 5 per range action (set-point, absolute variation, activation and variation up/down)
+ // total number of constraints 6 or 7:
+ // - 1 per CNEC (flow constraint)
+ // - 3 per range action (activation and set-point + absolute variation)
+ // - 0 or 1 for curative range action (relative variation constraint)
+ assertEquals(12, linearProblem.numVariables());
+ assertEquals(8, linearProblem.numConstraints());
+ }
+
+ private void updateLinearProblem() {
+ // arrange some additional data
+ network.getTwoWindingsTransformer(RANGE_ACTION_ELEMENT_ID).getPhaseTapChanger().setTapPosition(TAP_IT2);
+ initialAlpha = network.getTwoWindingsTransformer(RANGE_ACTION_ELEMENT_ID).getPhaseTapChanger().getCurrentStep().getAlpha();
+
+ when(flowResult.getFlow(cnec1, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(REF_FLOW_CNEC1_IT2);
+ when(flowResult.getFlow(cnec2, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(REF_FLOW_CNEC2_IT2);
+ when(sensitivityResult.getSensitivityValue(cnec1, TwoSides.ONE, pstRangeAction, Unit.MEGAWATT)).thenReturn(SENSI_CNEC1_IT2);
+ when(sensitivityResult.getSensitivityValue(cnec2, TwoSides.TWO, pstRangeAction, Unit.MEGAWATT)).thenReturn(SENSI_CNEC2_IT2);
+
+ // update the problem
+ RangeActionSetpointResult rangeActionSetpointResult = new RangeActionSetpointResultImpl(Map.of(pstRangeAction, initialAlpha));
+ linearProblem.updateBetweenSensiIteration(flowResult, sensitivityResult, new RangeActionActivationResultImpl(rangeActionSetpointResult));
+ }
+
+ @Test
+ void updateTestOnPreventive() {
+ initializeForPreventive(1e-6, 1e-6, 1e-6);
+ State state = cnec1.getState();
+ // update the problem with new data
+ updateLinearProblem();
+
+ // some additional data
+ final double currentAlpha = pstRangeAction.convertTapToAngle(network.getTwoWindingsTransformer(RANGE_ACTION_ELEMENT_ID).getPhaseTapChanger().getTapPosition());
+
+ OpenRaoMPVariable setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, state);
+
+ // check flow variable for cnec1
+ OpenRaoMPVariable flowVariable = linearProblem.getFlowVariable(cnec1, TwoSides.ONE);
+ assertNotNull(flowVariable);
+ assertEquals(-linearProblem.infinity(), flowVariable.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable.ub(), linearProblem.infinity() * 1e-3);
+
+ // check flow constraint for cnec1
+ OpenRaoMPConstraint flowConstraint = linearProblem.getFlowConstraint(cnec1, TwoSides.ONE);
+ assertNotNull(flowConstraint);
+ assertEquals(REF_FLOW_CNEC1_IT2 - currentAlpha * SENSI_CNEC1_IT2, flowConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(REF_FLOW_CNEC1_IT2 - currentAlpha * SENSI_CNEC1_IT2, flowConstraint.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, flowConstraint.getCoefficient(flowVariable), DOUBLE_TOLERANCE);
+ assertEquals(-SENSI_CNEC1_IT2, flowConstraint.getCoefficient(setPointVariable), DOUBLE_TOLERANCE);
+
+ // check flow variable for cnec2 does not exist
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowVariable(cnec2, TwoSides.TWO));
+ assertEquals("Variable Tieline BE FR - Defaut - N-1 NL1-NL3_two_flow_variable has not been created yet", e.getMessage());
+
+ // check flow constraint for cnec2 does not exist
+ e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowConstraint(cnec2, TwoSides.TWO));
+ assertEquals("Constraint Tieline BE FR - Defaut - N-1 NL1-NL3_two_flow_constraint has not been created yet", e.getMessage());
+
+ // check the number of variables and constraints
+ // No iterative relative variation constraint should be created since MarginCoreProblemFiller.raRangeShrinking = false
+ // total number of variables 3 :
+ // - 1 per CNEC (flow)
+ // - 5 per range action (set-point, absolute variation, activation and variation up/down)
+ // total number of constraints 3 :
+ // - 1 per CNEC (flow constraint)
+ // - 3 per range action (activation and set-point + absolute variation)
+
+ assertEquals(6, linearProblem.numVariables());
+ assertEquals(4, linearProblem.numConstraints());
+ }
+
+ @Test
+ void updateTestOnPreventiveWithRaRangeShrinking() {
+ initialize(Set.of(cnec1), 1e-6, 1e-6, 1e-6, crac.getPreventiveState(), true, RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+ State state = cnec1.getState();
+
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getRangeActionRelativeSetpointConstraint(pstRangeAction, state, LinearProblem.RaRangeShrinking.TRUE));
+ assertEquals("Constraint PRA_PST_BE_preventive_relative_setpoint_iterative-shrinkconstraint has not been created yet", e.getMessage());
+
+ // 1st update
+ updateLinearProblem();
+
+ assertEquals(6, linearProblem.numVariables());
+ assertEquals(5, linearProblem.numConstraints());
+
+ OpenRaoMPVariable setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, state);
+ OpenRaoMPConstraint shrinkingConstraint = linearProblem.getRangeActionRelativeSetpointConstraint(pstRangeAction, state, LinearProblem.RaRangeShrinking.TRUE);
+ assertNotNull(shrinkingConstraint);
+ assertEquals(1, shrinkingConstraint.getCoefficient(setPointVariable));
+ assertEquals(-10.5161, shrinkingConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(5.0626, shrinkingConstraint.ub(), DOUBLE_TOLERANCE);
+
+ // 2nd update
+ updateLinearProblem();
+
+ assertEquals(6, linearProblem.numVariables());
+ assertEquals(5, linearProblem.numConstraints());
+
+ setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, state);
+ shrinkingConstraint = linearProblem.getRangeActionRelativeSetpointConstraint(pstRangeAction, state, LinearProblem.RaRangeShrinking.TRUE);
+ assertNotNull(shrinkingConstraint);
+ assertEquals(1, shrinkingConstraint.getCoefficient(setPointVariable));
+ assertEquals(-7.9222, shrinkingConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(2.4687, shrinkingConstraint.ub(), DOUBLE_TOLERANCE);
+ }
+
+ @Test
+ void updateTestOnCurativeWithRaRangeShrinking() {
+ initialize(Set.of(cnec2), 1e-6, 1e-6, 1e-6, cnec2.getState(), true, RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+ State state = cnec2.getState();
+ // update the problem with new data
+ updateLinearProblem();
+
+ // some additional data
+ final double currentAlpha = pstRangeAction.convertTapToAngle(network.getTwoWindingsTransformer(RANGE_ACTION_ELEMENT_ID).getPhaseTapChanger().getTapPosition());
+
+ OpenRaoMPVariable setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, state);
+
+ // check flow variable for cnec1 does not exist
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowVariable(cnec1, TwoSides.ONE));
+ assertEquals("Variable Tieline BE FR - N - preventive_one_flow_variable has not been created yet", e.getMessage());
+
+ // check flow constraint for cnec1 does not exist
+ e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowConstraint(cnec1, TwoSides.ONE));
+ assertEquals("Constraint Tieline BE FR - N - preventive_one_flow_constraint has not been created yet", e.getMessage());
+
+ // check flow variable for cnec2
+ OpenRaoMPVariable flowVariable2 = linearProblem.getFlowVariable(cnec2, TwoSides.TWO);
+ assertNotNull(flowVariable2);
+ assertEquals(-linearProblem.infinity(), flowVariable2.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable2.ub(), linearProblem.infinity() * 1e-3);
+
+ // check flow constraint for cnec2
+ OpenRaoMPConstraint flowConstraint2 = linearProblem.getFlowConstraint(cnec2, TwoSides.TWO);
+ assertNotNull(flowConstraint2);
+ assertEquals(REF_FLOW_CNEC2_IT2 - currentAlpha * SENSI_CNEC2_IT2, flowConstraint2.lb(), DOUBLE_TOLERANCE);
+ assertEquals(REF_FLOW_CNEC2_IT2 - currentAlpha * SENSI_CNEC2_IT2, flowConstraint2.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, flowConstraint2.getCoefficient(flowVariable2), DOUBLE_TOLERANCE);
+ assertEquals(-SENSI_CNEC2_IT2, flowConstraint2.getCoefficient(setPointVariable), DOUBLE_TOLERANCE);
+
+ // check the number of variables and constraints
+ // total number of variables 3 :
+ // - 1 per CNEC (flow)
+ // - 5 per range action (set-point, absolute variation, activation and variation up/down)
+ // total number of constraints 4 :
+ // - 1 per CNEC (flow constraint)
+ // - 3 per range action (activation, absolute variation, set-point variation and iterative relative variation constraint: created before 2nd iteration)
+ assertEquals(6, linearProblem.numVariables());
+ assertEquals(5, linearProblem.numConstraints());
+
+ // assert that no other constraint is created after 2nd iteration
+ updateLinearProblem();
+ assertEquals(5, linearProblem.numConstraints());
+ }
+
+ @Test
+ void testSensitivityFilter1() {
+ OpenRaoMPConstraint flowConstraint;
+ OpenRaoMPVariable rangeActionSetpoint;
+ when(flowResult.getPtdfZonalSum(cnec1, TwoSides.ONE)).thenReturn(0.5);
+
+ // (sensi = 2) < 2.5 should be filtered
+ when(flowResult.getMargin(cnec1, Unit.MEGAWATT)).thenReturn(-1.0);
+ initialize(Set.of(cnec1), 2.5, 2.5, 2.5, crac.getPreventiveState(), false, RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+ flowConstraint = linearProblem.getFlowConstraint(cnec1, TwoSides.ONE);
+ rangeActionSetpoint = linearProblem.getRangeActionSetpointVariable(pstRangeAction, cnec1.getState());
+ assertEquals(0, flowConstraint.getCoefficient(rangeActionSetpoint), DOUBLE_TOLERANCE);
+ assertEquals(500., flowConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(500., flowConstraint.ub(), DOUBLE_TOLERANCE);
+ }
+
+ @Test
+ void testSensitivityFilter2() {
+ OpenRaoMPConstraint flowConstraint;
+ OpenRaoMPVariable rangeActionSetpoint;
+ when(flowResult.getPtdfZonalSum(cnec1, TwoSides.ONE)).thenReturn(0.5);
+ Map tapToAngle = pstRangeAction.getTapToAngleConversionMap();
+
+ // (sensi = 2) > 1/.5 should not be filtered
+ when(flowResult.getMargin(cnec1, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(-1.0);
+ initialize(Set.of(cnec1), 1.5, 1.5, 1.5, crac.getPreventiveState(), false, RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+ flowConstraint = linearProblem.getFlowConstraint(cnec1, TwoSides.ONE);
+ rangeActionSetpoint = linearProblem.getRangeActionSetpointVariable(pstRangeAction, cnec1.getState());
+ assertEquals(-2, flowConstraint.getCoefficient(rangeActionSetpoint), DOUBLE_TOLERANCE);
+ assertEquals(500. - 2 * tapToAngle.get(TAP_INITIAL), flowConstraint.lb(), DOUBLE_TOLERANCE);
+ assertEquals(500. - 2 * tapToAngle.get(TAP_INITIAL), flowConstraint.ub(), DOUBLE_TOLERANCE);
+ }
+
+ @Test
+ void testFilterCnecWithSensiFailure() {
+ // cnec1 has a failed state, cnec2 has a succeeded state
+ // only cnec2's flow variables & constraints must be added to MIP
+ when(sensitivityResult.getSensitivityStatus(cnec1.getState())).thenReturn(ComputationStatus.FAILURE);
+ when(sensitivityResult.getSensitivityStatus(cnec2.getState())).thenReturn(ComputationStatus.DEFAULT);
+ initialize(Set.of(cnec1, cnec2), 1e-6, 1e-6, 1e-6, cnec1.getState(), false, RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+
+ OpenRaoMPVariable setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, cnec2.getState());
+
+ // check flow variable for cnec1 does not exist
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowVariable(cnec1, TwoSides.ONE));
+ assertEquals("Variable Tieline BE FR - N - preventive_one_flow_variable has not been created yet", e.getMessage());
+
+ // check flow constraint for cnec1 does not exist
+ e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowConstraint(cnec1, TwoSides.ONE));
+ assertEquals("Constraint Tieline BE FR - N - preventive_one_flow_constraint has not been created yet", e.getMessage());
+
+ // check flow variable for cnec2
+ OpenRaoMPVariable flowVariable2 = linearProblem.getFlowVariable(cnec2, TwoSides.TWO);
+ assertNotNull(flowVariable2);
+ assertEquals(-linearProblem.infinity(), flowVariable2.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable2.ub(), linearProblem.infinity() * 1e-3);
+
+ // check flow constraint for cnec2
+ OpenRaoMPConstraint flowConstraint2 = linearProblem.getFlowConstraint(cnec2, TwoSides.TWO);
+ assertNotNull(flowConstraint2);
+ assertEquals(REF_FLOW_CNEC2_IT1 - initialAlpha * SENSI_CNEC2_IT1, flowConstraint2.lb(), DOUBLE_TOLERANCE);
+ assertEquals(REF_FLOW_CNEC2_IT1 - initialAlpha * SENSI_CNEC2_IT1, flowConstraint2.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, flowConstraint2.getCoefficient(flowVariable2), DOUBLE_TOLERANCE);
+ assertEquals(-SENSI_CNEC2_IT1, flowConstraint2.getCoefficient(setPointVariable), DOUBLE_TOLERANCE);
+ }
+
+ @Test
+ void testFilterCnecWithSensiFailureAndUpdateWithoutChange() {
+ // cnec1 has a failed state, cnec2 has a succeeded state
+ // only cnec2's flow variables & constraints must be added to MIP
+ when(sensitivityResult.getSensitivityStatus(cnec1.getState())).thenReturn(ComputationStatus.FAILURE);
+ when(sensitivityResult.getSensitivityStatus(cnec2.getState())).thenReturn(ComputationStatus.DEFAULT);
+ initialize(Set.of(cnec1, cnec2), 1e-6, 1e-6, 1e-6, cnec1.getState(), false, RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+
+ updateLinearProblem();
+
+ OpenRaoMPVariable setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, cnec2.getState());
+
+ // check flow variable for cnec1 does not exist
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowVariable(cnec1, TwoSides.ONE));
+ assertEquals("Variable Tieline BE FR - N - preventive_one_flow_variable has not been created yet", e.getMessage());
+
+ // check flow constraint for cnec1 does not exist
+ e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowConstraint(cnec1, TwoSides.ONE));
+ assertEquals("Constraint Tieline BE FR - N - preventive_one_flow_constraint has not been created yet", e.getMessage());
+
+ // check flow variable for cnec2
+ OpenRaoMPVariable flowVariable2 = linearProblem.getFlowVariable(cnec2, TwoSides.TWO);
+ assertNotNull(flowVariable2);
+ assertEquals(-linearProblem.infinity(), flowVariable2.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable2.ub(), linearProblem.infinity() * 1e-3);
+
+ // check flow constraint for cnec2
+ final double currentAlpha = pstRangeAction.convertTapToAngle(network.getTwoWindingsTransformer(RANGE_ACTION_ELEMENT_ID).getPhaseTapChanger().getTapPosition());
+ OpenRaoMPConstraint flowConstraint2 = linearProblem.getFlowConstraint(cnec2, TwoSides.TWO);
+ assertEquals(REF_FLOW_CNEC2_IT2 - currentAlpha * SENSI_CNEC2_IT2, flowConstraint2.lb(), DOUBLE_TOLERANCE);
+ assertEquals(REF_FLOW_CNEC2_IT2 - currentAlpha * SENSI_CNEC2_IT2, flowConstraint2.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, flowConstraint2.getCoefficient(flowVariable2), DOUBLE_TOLERANCE);
+ assertEquals(-SENSI_CNEC2_IT2, flowConstraint2.getCoefficient(setPointVariable), DOUBLE_TOLERANCE);
+ }
+
+ @Test
+ void testFilterCnecWithSensiFailureAndUpdateWithChange() {
+ // cnec1 has a failed state, cnec2 has a succeeded state
+ // only cnec2's flow variables & constraints must be added to MIP
+ when(sensitivityResult.getSensitivityStatus(cnec1.getState())).thenReturn(ComputationStatus.FAILURE);
+ when(sensitivityResult.getSensitivityStatus(cnec2.getState())).thenReturn(ComputationStatus.DEFAULT);
+ initialize(Set.of(cnec1, cnec2), 1e-6, 1e-6, 1e-6, cnec1.getState(), false, RangeActionsOptimizationParameters.PstModel.APPROXIMATED_INTEGERS);
+
+ // invert sensitivity failure statuses & update
+ // only cnec1's flow variables & constraints must be added to MIP
+ when(sensitivityResult.getSensitivityStatus(cnec1.getState())).thenReturn(ComputationStatus.DEFAULT);
+ when(sensitivityResult.getSensitivityStatus(cnec2.getState())).thenReturn(ComputationStatus.FAILURE);
+ updateLinearProblem();
+
+ // check flow variable for cnec2 does not exist
+ Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowVariable(cnec2, TwoSides.TWO));
+ assertEquals("Variable Tieline BE FR - Defaut - N-1 NL1-NL3_two_flow_variable has not been created yet", e.getMessage());
+
+ // check flow constraint for cnec2 does not exist
+ e = assertThrows(OpenRaoException.class, () -> linearProblem.getFlowConstraint(cnec2, TwoSides.TWO));
+ assertEquals("Constraint Tieline BE FR - Defaut - N-1 NL1-NL3_two_flow_constraint has not been created yet", e.getMessage());
+
+ // check flow variable for cnec1
+ OpenRaoMPVariable flowVariable1 = linearProblem.getFlowVariable(cnec1, TwoSides.ONE);
+ assertNotNull(flowVariable1);
+ assertEquals(-linearProblem.infinity(), flowVariable1.lb(), linearProblem.infinity() * 1e-3);
+ assertEquals(linearProblem.infinity(), flowVariable1.ub(), linearProblem.infinity() * 1e-3);
+
+ final double currentAlpha = pstRangeAction.convertTapToAngle(network.getTwoWindingsTransformer(RANGE_ACTION_ELEMENT_ID).getPhaseTapChanger().getTapPosition());
+ OpenRaoMPVariable setPointVariable = linearProblem.getRangeActionSetpointVariable(pstRangeAction, cnec1.getState());
+
+ // check flow constraint for cnec1
+ OpenRaoMPConstraint flowConstraint1 = linearProblem.getFlowConstraint(cnec1, TwoSides.ONE);
+ assertNotNull(flowConstraint1);
+ assertEquals(REF_FLOW_CNEC1_IT2 - currentAlpha * SENSI_CNEC1_IT2, flowConstraint1.lb(), DOUBLE_TOLERANCE);
+ assertEquals(REF_FLOW_CNEC1_IT2 - currentAlpha * SENSI_CNEC1_IT2, flowConstraint1.ub(), DOUBLE_TOLERANCE);
+ assertEquals(1, flowConstraint1.getCoefficient(flowVariable1), DOUBLE_TOLERANCE);
+ assertEquals(-SENSI_CNEC1_IT2, flowConstraint1.getCoefficient(setPointVariable), DOUBLE_TOLERANCE);
+ }
+}
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstGroupFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstGroupFillerTest.java
index cfa799f4d6..1d98db43fe 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstGroupFillerTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstGroupFillerTest.java
@@ -60,7 +60,7 @@ void testFillAndUpdateMethods() throws IOException {
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(new RaoParameters());
- CoreProblemFiller coreProblemFiller = new CoreProblemFiller(
+ MarginCoreProblemFiller coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
rangeActionParameters,
@@ -72,7 +72,9 @@ void testFillAndUpdateMethods() throws IOException {
DiscretePstTapFiller discretePstTapFiller = new DiscretePstTapFiller(
optimizationPerimeter,
pstRangeActions,
- initialRangeActionSetpointResult);
+ initialRangeActionSetpointResult,
+ rangeActionParameters,
+ false);
DiscretePstGroupFiller discretePstGroupFiller = new DiscretePstGroupFiller(
state,
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstTapFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstTapFillerTest.java
index d67fc18686..f076ae88d4 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstTapFillerTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/DiscretePstTapFillerTest.java
@@ -21,8 +21,9 @@
import com.powsybl.openrao.searchtreerao.result.api.RangeActionSetpointResult;
import com.powsybl.openrao.searchtreerao.result.impl.RangeActionActivationResultImpl;
import com.powsybl.openrao.searchtreerao.result.impl.RangeActionSetpointResultImpl;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;
import java.io.IOException;
@@ -48,8 +49,7 @@ class DiscretePstTapFillerTest extends AbstractFillerTest {
private PstRangeAction pra;
private PstRangeAction cra;
- @BeforeEach
- void setUpAndFill() throws IOException {
+ void setUpAndFill(boolean costOptimization) throws IOException {
// prepare data
init();
preventiveState = crac.getPreventiveState();
@@ -61,6 +61,7 @@ void setUpAndFill() throws IOException {
.withNetworkElement("BBE2AA1 BBE3AA1 1")
.newOnContingencyStateUsageRule().withUsageMethod(AVAILABLE).withContingency("N-1 NL1-NL3").withInstant("curative").add()
.withInitialTap(0)
+ .withActivationCost(10.0)
.withTapToAngleConversionMap(tapToAngle)
.newTapRange()
.withMinTap(-10)
@@ -81,7 +82,7 @@ void setUpAndFill() throws IOException {
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(new RaoParameters());
- CoreProblemFiller coreProblemFiller = new CoreProblemFiller(
+ MarginCoreProblemFiller coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
rangeActionParameters,
@@ -94,7 +95,9 @@ void setUpAndFill() throws IOException {
discretePstTapFiller = new DiscretePstTapFiller(
optimizationPerimeter,
pstRangeActions,
- initialRangeActionSetpointResult);
+ initialRangeActionSetpointResult,
+ new RangeActionsOptimizationParameters(),
+ true);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
@@ -188,8 +191,10 @@ private void checkPstRelativeTapConstraint(double expectedLb, double expectedUb)
assertEquals(1, craRelativeTapC.getCoefficient(variationDownV));
}
- @Test
- void testFillAndUpdateMethods() {
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testFillAndUpdateMethods(boolean costOptimization) throws IOException {
+ setUpAndFill(costOptimization);
checkContent(pstRangeAction, preventiveState, 0, -15, 15, true);
checkContent(cra, curativeState, 0, -16, 16, true);
checkPstRelativeTapConstraint(-10, 7);
@@ -206,10 +211,18 @@ void testFillAndUpdateMethods() {
checkContent(pra, preventiveState, -4, -15, 15, false);
checkContent(cra, curativeState, -6, -16, 16, false);
checkPstRelativeTapConstraint(-8, 9);
+
+ if (costOptimization) {
+ assertEquals(10.0, linearProblem.getObjective().getCoefficient(linearProblem.getTotalPstRangeActionTapVariationVariable(pra, preventiveState, LinearProblem.VariationDirectionExtension.UPWARD)));
+ assertEquals(10.0, linearProblem.getObjective().getCoefficient(linearProblem.getTotalPstRangeActionTapVariationVariable(pra, preventiveState, LinearProblem.VariationDirectionExtension.UPWARD)));
+ assertEquals(0.01, linearProblem.getObjective().getCoefficient(linearProblem.getTotalPstRangeActionTapVariationVariable(cra, curativeState, LinearProblem.VariationDirectionExtension.UPWARD)), 1e-2);
+ assertEquals(0.01, linearProblem.getObjective().getCoefficient(linearProblem.getTotalPstRangeActionTapVariationVariable(cra, curativeState, LinearProblem.VariationDirectionExtension.DOWNWARD)), 1e-2);
+ }
}
@Test
- void testUpdateBetweenMipIteration() {
+ void testUpdateBetweenMipIteration() throws IOException {
+ setUpAndFill(false);
RangeActionActivationResultImpl rangeActionActivationResult =
new RangeActionActivationResultImpl(new RangeActionSetpointResultImpl(Map.of(pra, 0., cra, 0.)));
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/CoreProblemFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MarginCoreProblemFillerTest.java
similarity index 99%
rename from ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/CoreProblemFillerTest.java
rename to ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MarginCoreProblemFillerTest.java
index 9c1dab5b58..5d98b53ea0 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/CoreProblemFillerTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MarginCoreProblemFillerTest.java
@@ -41,9 +41,9 @@
* @author Joris Mancini {@literal }
* @author Baptiste Seguinot {@literal }
*/
-class CoreProblemFillerTest extends AbstractFillerTest {
+class MarginCoreProblemFillerTest extends AbstractFillerTest {
private LinearProblem linearProblem;
- private CoreProblemFiller coreProblemFiller;
+ private MarginCoreProblemFiller coreProblemFiller;
private RangeActionSetpointResult initialRangeActionSetpointResult;
// some additional data
private double minAlpha;
@@ -94,7 +94,7 @@ private void initialize(Set cnecs, double pstSensitivityThreshold, dou
raoParameters.getRangeActionsOptimizationParameters().setInjectionRaSensitivityThreshold(injectionSensitivityThreshold);
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(raoParameters);
- coreProblemFiller = new CoreProblemFiller(
+ coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
rangeActionParameters,
@@ -529,7 +529,7 @@ void updateTestOnPreventive() {
assertEquals("Constraint Tieline BE FR - Defaut - N-1 NL1-NL3_two_flow_constraint has not been created yet", e.getMessage());
// check the number of variables and constraints
- // No iterative relative variation constraint should be created since CoreProblemFiller.raRangeShrinking = false
+ // No iterative relative variation constraint should be created since MarginCoreProblemFiller.raRangeShrinking = false
// total number of variables 5 :
// - 1 per CNEC (flow)
// - 4 per range action (set-point 3 variations [up, down, absolute])
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxLoopFlowFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxLoopFlowFillerTest.java
index 694c068751..145b01a514 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxLoopFlowFillerTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxLoopFlowFillerTest.java
@@ -45,7 +45,7 @@
*/
class MaxLoopFlowFillerTest extends AbstractFillerTest {
private LinearProblem linearProblem;
- private CoreProblemFiller coreProblemFiller;
+ private MarginCoreProblemFiller coreProblemFiller;
private MaxLoopFlowFiller maxLoopFlowFiller;
private LoopFlowParametersExtension loopFlowParameters;
private FlowCnec cnecOn2sides;
@@ -74,7 +74,7 @@ public void setUp() throws IOException {
Mockito.when(optimizationPerimeter.getRangeActionsPerState()).thenReturn(rangeActions);
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(new RaoParameters());
- coreProblemFiller = new CoreProblemFiller(
+ coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
rangeActionParameters,
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinMarginFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinMarginFillerTest.java
index dc38aec76f..d4fe0fbe1b 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinMarginFillerTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinMarginFillerTest.java
@@ -38,7 +38,7 @@
*/
class MaxMinMarginFillerTest extends AbstractFillerTest {
private LinearProblem linearProblem;
- private CoreProblemFiller coreProblemFiller;
+ private MarginCoreProblemFiller coreProblemFiller;
private MaxMinMarginFiller maxMinMarginFiller;
@BeforeEach
@@ -61,7 +61,7 @@ public void setUp() throws IOException {
raoParameters.getRangeActionsOptimizationParameters().setInjectionRaPenaltyCost(0.01);
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(raoParameters);
- coreProblemFiller = new CoreProblemFiller(
+ coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
rangeActionParameters,
@@ -70,7 +70,7 @@ public void setUp() throws IOException {
}
private void createMaxMinMarginFiller(Unit unit) {
- maxMinMarginFiller = new MaxMinMarginFiller(Set.of(cnec1), unit);
+ maxMinMarginFiller = new MaxMinMarginFiller(Set.of(cnec1), unit, false);
}
private void buildLinearProblem() {
@@ -186,7 +186,7 @@ void fillWithMissingRangeActionVariables() {
.build();
// FlowVariables present , but not the absoluteRangeActionVariables present,
- // This should work since range actions can be filtered out by the CoreProblemFiller if their number
+ // This should work since range actions can be filtered out by the MarginCoreProblemFiller if their number
// exceeds the max-pst-per-tso parameter
linearProblem.addFlowVariable(0.0, 0.0, cnec1, TwoSides.ONE);
linearProblem.addFlowVariable(0.0, 0.0, cnec2, TwoSides.TWO);
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinRelativeMarginFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinRelativeMarginFillerTest.java
index 3e741bdd1d..d8a39774df 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinRelativeMarginFillerTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MaxMinRelativeMarginFillerTest.java
@@ -48,7 +48,7 @@ class MaxMinRelativeMarginFillerTest extends AbstractFillerTest {
private static final double PRECISE_DOUBLE_TOLERANCE = 1e-9;
private LinearProblem linearProblem;
- private CoreProblemFiller coreProblemFiller;
+ private MarginCoreProblemFiller coreProblemFiller;
private MaxMinRelativeMarginFiller maxMinRelativeMarginFiller;
private RelativeMarginsParametersExtension parameters;
private RangeActionSetpointResult initialRangeActionSetpointResult;
@@ -77,7 +77,7 @@ public void setUp() throws IOException {
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(raoParameters);
parameters = raoParameters.getExtension(RelativeMarginsParametersExtension.class);
- coreProblemFiller = new CoreProblemFiller(
+ coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
rangeActionParameters,
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MnecFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MnecFillerTest.java
index 250072c307..a2440d81bd 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MnecFillerTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/MnecFillerTest.java
@@ -39,7 +39,7 @@
*/
class MnecFillerTest extends AbstractFillerTest {
private LinearProblem linearProblem;
- private CoreProblemFiller coreProblemFiller;
+ private MarginCoreProblemFiller coreProblemFiller;
private FlowCnec mnec1;
private FlowCnec mnec2;
private FlowCnec mnec3;
@@ -104,7 +104,7 @@ public void setUp() throws IOException {
raoParameters.getRangeActionsOptimizationParameters().setInjectionRaPenaltyCost(0.01);
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(raoParameters);
- coreProblemFiller = new CoreProblemFiller(
+ coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
rangeActionParameters,
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/RaUsageLimitsFillerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/RaUsageLimitsFillerTest.java
index 61258064ea..88aa302dd8 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/RaUsageLimitsFillerTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/RaUsageLimitsFillerTest.java
@@ -50,7 +50,7 @@ class RaUsageLimitsFillerTest extends AbstractFillerTest {
private State state;
private LinearProblem linearProblem;
- private CoreProblemFiller coreProblemFiller;
+ private MarginCoreProblemFiller coreProblemFiller;
@BeforeEach
public void setup() throws IOException {
@@ -107,7 +107,7 @@ public void setup() throws IOException {
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(new RaoParameters());
- coreProblemFiller = new CoreProblemFiller(
+ coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
prePerimeterRangeActionSetpointResult,
rangeActionParameters,
@@ -123,7 +123,7 @@ void testSkipFiller() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(raUsageLimitsFiller)
@@ -146,7 +146,7 @@ void testVariationVariableAndConstraints() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(raUsageLimitsFiller)
@@ -179,7 +179,7 @@ void testVariationVariableAndConstraintsApproxPsts() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
true,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(raUsageLimitsFiller)
@@ -212,7 +212,7 @@ void testSkipConstraints1() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(raUsageLimitsFiller)
@@ -245,7 +245,7 @@ void testSkipConstraints2() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(raUsageLimitsFiller)
@@ -266,7 +266,7 @@ void testMaxRa() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
@@ -293,7 +293,7 @@ void testSkipLargeMaxRa1() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
@@ -316,7 +316,7 @@ void testSkipLargeMaxRa2() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
@@ -347,7 +347,7 @@ void testMaxTso() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(raUsageLimitsFiller)
@@ -380,7 +380,7 @@ void testSkipLargeMaxTso1() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(raUsageLimitsFiller)
@@ -402,7 +402,7 @@ void testSkipLargeMaxTso2() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(raUsageLimitsFiller)
@@ -424,7 +424,7 @@ void testMaxTsoWithExclusion() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(raUsageLimitsFiller)
@@ -451,7 +451,7 @@ void testMaxRaPerTso() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
@@ -492,7 +492,7 @@ void testMaxPstPerTso() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
false,
- network);
+ network, false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
@@ -539,7 +539,7 @@ void testMaxElementaryActionsPerTsoConstraint() {
prePerimeterRangeActionSetpointResult,
raLimitationParameters,
true,
- network);
+ network, false);
Map> pstRangeActionsPerState = new HashMap<>();
rangeActionsPerState.forEach((s, rangeActionSet) -> rangeActionSet.stream().filter(PstRangeAction.class::isInstance).map(PstRangeAction.class::cast).forEach(pstRangeAction -> pstRangeActionsPerState.computeIfAbsent(s, e -> new HashSet<>()).add(pstRangeAction)));
@@ -548,7 +548,7 @@ void testMaxElementaryActionsPerTsoConstraint() {
when(optimizationPerimeter.getMainOptimizationState()).thenReturn(state);
when(optimizationPerimeter.getRangeActionsPerState()).thenReturn(rangeActionsPerState);
- DiscretePstTapFiller discretePstTapFiller = new DiscretePstTapFiller(optimizationPerimeter, pstRangeActionsPerState, prePerimeterRangeActionSetpointResult);
+ DiscretePstTapFiller discretePstTapFiller = new DiscretePstTapFiller(optimizationPerimeter, pstRangeActionsPerState, prePerimeterRangeActionSetpointResult, new RangeActionsOptimizationParameters(), false);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/UnoptimizedCnecFillerMarginDecreaseRuleTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/UnoptimizedCnecFillerMarginDecreaseRuleTest.java
index f0624a714c..8dbcac76b1 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/UnoptimizedCnecFillerMarginDecreaseRuleTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/linearoptimisation/algorithms/fillers/UnoptimizedCnecFillerMarginDecreaseRuleTest.java
@@ -44,7 +44,7 @@ class UnoptimizedCnecFillerMarginDecreaseRuleTest extends AbstractFillerTest {
private static final double MAX_ABS_THRESHOLD = 1000;
private LinearProblem linearProblem;
- private CoreProblemFiller coreProblemFiller;
+ private MarginCoreProblemFiller coreProblemFiller;
private UnoptimizedCnecFiller unoptimizedCnecFiller;
private FlowCnec cnecNl;
private FlowCnec cnecFr;
@@ -82,7 +82,7 @@ public void setUp() throws IOException {
raoParameters.getRangeActionsOptimizationParameters().setInjectionRaPenaltyCost(0.01);
RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(raoParameters);
- coreProblemFiller = new CoreProblemFiller(
+ coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
rangeActionParameters,
@@ -96,7 +96,7 @@ private void buildLinearProblemWithMaxMinMargin() {
private void buildLinearProblemWithMaxMinMargin(boolean initialFlowsAreNan) {
UnoptimizedCnecParameters unoptimizedCnecParameters = new UnoptimizedCnecParameters(Set.of("NL"));
- MaxMinMarginFiller maxMinMarginFiller = new MaxMinMarginFiller(Set.of(cnecNl, cnecFr), Unit.MEGAWATT);
+ MaxMinMarginFiller maxMinMarginFiller = new MaxMinMarginFiller(Set.of(cnecNl, cnecFr), Unit.MEGAWATT, false);
FlowResult initialFlowResult = Mockito.mock(FlowResult.class);
when(initialFlowResult.getMargin(cnecNl, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(400.);
when(initialFlowResult.getMargin(cnecFr, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(600.);
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/FunctionalCostComputerTestUtils.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/FunctionalCostComputerTestUtils.java
new file mode 100644
index 0000000000..5084f191d2
--- /dev/null
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/FunctionalCostComputerTestUtils.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2024, 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.functionalcostcomputer;
+
+import com.powsybl.openrao.data.crac.api.Crac;
+import com.powsybl.openrao.data.crac.api.CracFactory;
+import com.powsybl.openrao.data.crac.api.InstantKind;
+import com.powsybl.openrao.data.crac.api.State;
+import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
+import com.powsybl.openrao.data.crac.impl.FlowCnecImpl;
+import com.powsybl.openrao.searchtreerao.result.api.OptimizationResult;
+import org.mockito.Mockito;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Thomas Bouquet {@literal }
+ */
+public class FunctionalCostComputerTestUtils {
+ protected Crac crac;
+ protected State autoStateCo1;
+ protected State autoStateCo2;
+ protected State curativeStateCo1;
+ protected State curativeStateCo2;
+ protected OptimizationResult secondPreventivePerimeterResult;
+ protected Map postContingencyResults;
+
+ protected void init() {
+ crac = CracFactory.findDefault().create("crac");
+ crac.newInstant("preventive", InstantKind.PREVENTIVE);
+ crac.newInstant("outage", InstantKind.OUTAGE);
+ crac.newInstant("auto", InstantKind.AUTO);
+ crac.newInstant("curative", InstantKind.CURATIVE);
+
+ // mock states
+
+ autoStateCo1 = Mockito.mock(State.class);
+ Mockito.when(autoStateCo1.getInstant()).thenReturn(crac.getInstant("auto"));
+
+ autoStateCo2 = Mockito.mock(State.class);
+ Mockito.when(autoStateCo2.getInstant()).thenReturn(crac.getInstant("auto"));
+
+ curativeStateCo1 = Mockito.mock(State.class);
+ Mockito.when(curativeStateCo1.getInstant()).thenReturn(crac.getInstant("curative"));
+
+ curativeStateCo2 = Mockito.mock(State.class);
+ Mockito.when(curativeStateCo2.getInstant()).thenReturn(crac.getInstant("curative"));
+
+ // mock flow cnecs
+
+ FlowCnec autoFlowCnec = Mockito.mock(FlowCnecImpl.class);
+ FlowCnec curativeFlowCnec = Mockito.mock(FlowCnecImpl.class);
+
+ // mock optimization results
+
+ secondPreventivePerimeterResult = Mockito.mock(OptimizationResult.class);
+ Mockito.when(secondPreventivePerimeterResult.getFunctionalCost()).thenReturn(100.0);
+
+ OptimizationResult autoPerimeterResultCo1 = Mockito.mock(OptimizationResult.class);
+ Mockito.when(autoPerimeterResultCo1.getFunctionalCost()).thenReturn(30.0);
+ Mockito.when(autoPerimeterResultCo1.getMostLimitingElements(1)).thenReturn(List.of(autoFlowCnec));
+
+ OptimizationResult autoPerimeterResultCo2 = Mockito.mock(OptimizationResult.class);
+ Mockito.when(autoPerimeterResultCo2.getFunctionalCost()).thenReturn(17.0);
+ Mockito.when(autoPerimeterResultCo2.getMostLimitingElements(1)).thenReturn(List.of(autoFlowCnec));
+
+ OptimizationResult curativePerimeterResultCo1 = Mockito.mock(OptimizationResult.class);
+ Mockito.when(curativePerimeterResultCo1.getFunctionalCost()).thenReturn(250.0);
+ Mockito.when(curativePerimeterResultCo1.getMostLimitingElements(1)).thenReturn(List.of(curativeFlowCnec));
+
+ OptimizationResult curativePerimeterResultCo2 = Mockito.mock(OptimizationResult.class);
+ Mockito.when(curativePerimeterResultCo2.getFunctionalCost()).thenReturn(110.0);
+ Mockito.when(curativePerimeterResultCo2.getMostLimitingElements(1)).thenReturn(List.of(curativeFlowCnec));
+
+ // mock post-contingency results
+
+ postContingencyResults = Map.of(autoStateCo1, autoPerimeterResultCo1, autoStateCo2, autoPerimeterResultCo2, curativeStateCo1, curativePerimeterResultCo1, curativeStateCo2, curativePerimeterResultCo2);
+ }
+}
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/MaximumFunctionalCostComputerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/MaximumFunctionalCostComputerTest.java
new file mode 100644
index 0000000000..48773d14b5
--- /dev/null
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/MaximumFunctionalCostComputerTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2024, 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.functionalcostcomputer;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Thomas Bouquet {@literal }
+ */
+class MaximumFunctionalCostComputerTest extends FunctionalCostComputerTestUtils {
+ private MaximumFunctionalCostComputer functionalCostComputer;
+
+ @BeforeEach
+ void setUp() {
+ init();
+ functionalCostComputer = new MaximumFunctionalCostComputer(secondPreventivePerimeterResult, postContingencyResults);
+ }
+
+ @Test
+ void testPreventiveTotalFunctionalCost() {
+ assertEquals(100.0, functionalCostComputer.computeFunctionalCost(crac.getInstant("preventive")));
+ }
+
+ @Test
+ void testOutageTotalFunctionalCost() {
+ assertEquals(100.0, functionalCostComputer.computeFunctionalCost(crac.getInstant("outage")));
+ }
+
+ @Test
+ void testAutoTotalFunctionalCost() {
+ assertEquals(100.0, functionalCostComputer.computeFunctionalCost(crac.getInstant("auto")));
+ }
+
+ @Test
+ void testCurativeTotalFunctionalCost() {
+ assertEquals(250.0, functionalCostComputer.computeFunctionalCost(crac.getInstant("curative")));
+ }
+}
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/TotalFunctionalCostComputerTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/TotalFunctionalCostComputerTest.java
new file mode 100644
index 0000000000..d3e1a49954
--- /dev/null
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/functionalcostcomputer/TotalFunctionalCostComputerTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2024, 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.functionalcostcomputer;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Thomas Bouquet {@literal }
+ */
+class TotalFunctionalCostComputerTest extends FunctionalCostComputerTestUtils {
+ private TotalFunctionalCostComputer functionalCostComputer;
+
+ @BeforeEach
+ void setUp() {
+ init();
+ functionalCostComputer = new TotalFunctionalCostComputer(secondPreventivePerimeterResult, postContingencyResults);
+ }
+
+ @Test
+ void testPreventiveTotalFunctionalCost() {
+ assertEquals(100.0, functionalCostComputer.computeFunctionalCost(crac.getInstant("preventive")));
+ }
+
+ @Test
+ void testOutageTotalFunctionalCost() {
+ assertEquals(100.0, functionalCostComputer.computeFunctionalCost(crac.getInstant("outage")));
+ }
+
+ @Test
+ void testAutoTotalFunctionalCost() {
+ assertEquals(147.0, functionalCostComputer.computeFunctionalCost(crac.getInstant("auto")));
+ }
+
+ @Test
+ void testCurativeTotalFunctionalCost() {
+ assertEquals(507.0, functionalCostComputer.computeFunctionalCost(crac.getInstant("curative")));
+ }
+
+ @Test
+ void testTotalFunctionalCostForNullInstant() {
+ assertEquals(0.0, functionalCostComputer.computeFunctionalCost(null));
+ }
+}
diff --git a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/impl/CurativeWithSecondPraoResultTest.java b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/impl/CurativeWithSecondPraoResultTest.java
index 87ddf9718c..36759eb3ff 100644
--- a/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/impl/CurativeWithSecondPraoResultTest.java
+++ b/ra-optimisation/search-tree-rao/src/test/java/com/powsybl/openrao/searchtreerao/result/impl/CurativeWithSecondPraoResultTest.java
@@ -54,7 +54,7 @@ void testGetFlow() {
when(postCraPrePerimeterResult.getFlow(eq(cnec1), any(), any())).thenReturn(135.4);
when(postCraPrePerimeterResult.getFlow(eq(cnec1), any(), any(), any())).thenReturn(135.4);
- CurativeWithSecondPraoResult result = new CurativeWithSecondPraoResult(state1, null, null, null, postCraPrePerimeterResult);
+ CurativeWithSecondPraoResult result = new CurativeWithSecondPraoResult(state1, null, null, null, postCraPrePerimeterResult, false);
assertEquals(135.4, result.getFlow(cnec1, TwoSides.TWO, Unit.MEGAWATT), DOUBLE_TOLERANCE);
assertEquals(135.4, result.getFlow(cnec1, TwoSides.ONE, Unit.AMPERE, mock(Instant.class)), DOUBLE_TOLERANCE);
diff --git a/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac.json b/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac.json
index 3421c1d85b..bbe459afe0 100644
--- a/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac.json
+++ b/ra-optimisation/search-tree-rao/src/test/resources/crac/small-crac.json
@@ -1,8 +1,26 @@
{
"type" : "CRAC",
- "version" : "1.0",
+ "version" : "2.6",
"id": "LS_unsecure",
"name": "LS_unsecure",
+ "instants": [
+ {
+ "id": "preventive",
+ "kind": "PREVENTIVE"
+ },
+ {
+ "id": "outage",
+ "kind": "OUTAGE"
+ },
+ {
+ "id": "auto",
+ "kind": "AUTO"
+ },
+ {
+ "id": "curative",
+ "kind": "CURATIVE"
+ }
+ ],
"networkElementsNamePerId": { },
"contingencies": [
{
@@ -22,7 +40,7 @@
"unit": "megawatt",
"max": 750,
"min" : -750,
- "rule" : "onRightSide"
+ "side" : 2
} ],
"contingencyId": "N-1 NL1-NL3",
"instant" : "curative",
@@ -38,7 +56,7 @@
"unit": "megawatt",
"max": 750,
"min" : -750,
- "rule" : "onLeftSide"
+ "side" : 1
} ],
"instant": "preventive",
"optimized": true,
@@ -52,6 +70,11 @@
"operator": "BE",
"initialTap": 0,
"speed": 1,
+ "activationCost": 15.0,
+ "variationCosts": {
+ "up": 10.0,
+ "down": 10.0
+ },
"tapToAngleConversionMap" : {
"-1" : -0.3896097993971608,
"0" : 0.0,
@@ -87,13 +110,13 @@
"15" : 5.839110508104064,
"16" : 6.2276423729910535
},
- "freeToUseUsageRules": [
+ "onInstantUsageRules": [
{
"usageMethod": "available",
"instant": "preventive"
}
],
- "onStateUsageRules" : [ {
+ "onContingencyStateUsageRules" : [ {
"instant" : "curative",
"contingencyId" : "N-1 NL1-NL3",
"usageMethod" : "available"
@@ -148,13 +171,13 @@
"15" : 5.839110508104064,
"16" : 6.2276423729910535
},
- "freeToUseUsageRules": [
+ "onInstantUsageRules": [
{
"usageMethod": "available",
"instant": "preventive"
}
],
- "onStateUsageRules" : [ {
+ "onContingencyStateUsageRules" : [ {
"instant" : "auto",
"contingencyId" : "N-1 NL1-NL3",
"usageMethod" : "available"
diff --git a/tests/src/test/resources/com/powsybl/openrao/tests/features/epic92_costly_rao/US92_2.feature b/tests/src/test/resources/com/powsybl/openrao/tests/features/epic92_costly_rao/US92_2.feature
new file mode 100644
index 0000000000..29de68b40f
--- /dev/null
+++ b/tests/src/test/resources/com/powsybl/openrao/tests/features/epic92_costly_rao/US92_2.feature
@@ -0,0 +1,90 @@
+# Copyright (c) 2024, 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/.
+
+Feature: US 92.2: Costly range actions optimization - APPROXIMATED_INTEGERS PSTs
+
+ @fast @preventive-only @costly @rao
+ Scenario: US 92.2.1: Change only necessary taps on preventive PST
+ The RAO can increase the minimum margin by setting the tap of the PST on position -10
+ but stops at position -5 because the network is secure and this saves expenses.
+ Given network file is "epic92/2Nodes2ParallelLinesPST.uct"
+ Given crac file is "epic92/crac-92-2-1.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst.json"
+ When I launch search_tree_rao
+ Then the worst margin is 45.46 MW
+ And the value of the objective function initially should be 2000000.0
+ And 1 remedial actions are used in preventive
+ And the remedial action "pstBeFr2" is used in preventive
+ And the tap of PstRangeAction "pstBeFr2" should be -5 in preventive
+ And the value of the objective function after PRA should be 55.0
+
+ @fast @preventive-only @costly @rao
+ Scenario: US 92.2.2: Two PSTs
+ PST 1 has cheaper variation costs (5 per tap) but a higher activation price (100) so moving the 9 required taps would
+ require a cost of 145. PST 2 is cheaper to activate (5) and more expensive to use (15 per tap) but leads to a total
+ cost of 140 so it is chosen.
+ Given network file is "epic92/2Nodes3ParallelLines2PSTs.uct"
+ Given crac file is "epic92/crac-92-2-2.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst.json"
+ When I launch search_tree_rao
+ Then the worst margin is 31.15 MW
+ And the value of the objective function initially should be 2633333.33
+ And 1 remedial actions are used in preventive
+ And the remedial action "pstBeFr3" is used in preventive
+ And the tap of PstRangeAction "pstBeFr2" should be 0 in preventive
+ And the tap of PstRangeAction "pstBeFr3" should be -9 in preventive
+ And the value of the objective function after PRA should be 140.0
+
+ @fast @costly @rao
+ Scenario: US 92.2.3: Costly PST in preventive and curative
+ Given network file is "epic92/2Nodes3ParallelLinesPST.uct"
+ Given crac file is "epic92/crac-92-2-3.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst.json"
+ When I launch search_tree_rao
+ Then the worst margin is 11.73 MW
+ And the value of the objective function initially should be 4300000.0
+ And 1 remedial actions are used in preventive
+ And the remedial action "pstBeFr3" is used in preventive
+ And the tap of PstRangeAction "pstBeFr3" should be -3 in preventive
+ # Activation of pstBeFr3 (20) + 3 taps moved (3 * 7.5) + overload penalty (282.71 * 10000)
+ And the value of the objective function after PRA should be 2827226.08
+ And 1 remedial actions are used after "coBeFr2" at "curative"
+ And the tap of PstRangeAction "pstBeFr3" should be -9 after "coBeFr2" at "curative"
+ # Activation of pstBeFr3 twice (2 * 20) + 9 taps moved in total (3 * 7.5 + 6 * 7.5)
+ And the value of the objective function after CRA should be 107.5
+
+ @fast @costly @rao
+ Scenario: US 92.2.4: Free PST in preventive and curative
+ Given network file is "epic92/2Nodes3ParallelLinesPST.uct"
+ Given crac file is "epic92/crac-92-2-4.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst.json"
+ When I launch search_tree_rao
+ Then the worst margin is 11.73 MW
+ And the value of the objective function initially should be 4300000.0
+ And 1 remedial actions are used in preventive
+ And the remedial action "pstBeFr3" is used in preventive
+ And the tap of PstRangeAction "pstBeFr3" should be -3 in preventive
+ # Overload penalty (282.71 * 10000)
+ And the value of the objective function after PRA should be 2827171.08
+ And 1 remedial actions are used after "coBeFr2" at "curative"
+ And the tap of PstRangeAction "pstBeFr3" should be -9 after "coBeFr2" at "curative"
+ And the value of the objective function after CRA should be 0
+
+ @fast @costly @rao @second-preventive
+ Scenario: US 92.2.5: PST in 2nd preventive optimization
+ PST is moved to tap -9 straight from preventive optimization to cut curative activation costs.
+ Given network file is "epic92/2Nodes3ParallelLinesPST.uct"
+ Given crac file is "epic92/crac-92-2-3.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst_2P.json"
+ When I launch search_tree_rao
+ Then the worst margin is 11.73 MW
+ And the value of the objective function initially should be 4300000.0
+ And 1 remedial actions are used in preventive
+ And the remedial action "pstBeFr3" is used in preventive
+ And the tap of PstRangeAction "pstBeFr3" should be -9 in preventive
+ # Activation of pstBeFr3 (20) + 9 taps moved (9 * 7.5)
+ And the value of the objective function after PRA should be 87.5
+ And 0 remedial actions are used after "coBeFr2" at "curative"
+ And the value of the objective function after CRA should be 87.5
\ No newline at end of file
diff --git a/tests/src/test/resources/com/powsybl/openrao/tests/features/epic92_costly_rao/US92_3.feature b/tests/src/test/resources/com/powsybl/openrao/tests/features/epic92_costly_rao/US92_3.feature
new file mode 100644
index 0000000000..b134d78d36
--- /dev/null
+++ b/tests/src/test/resources/com/powsybl/openrao/tests/features/epic92_costly_rao/US92_3.feature
@@ -0,0 +1,116 @@
+# Copyright (c) 2024, 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/.
+
+
+Feature: US 92.3: Exhaustive costly optimization - APPROXIMATED_INTEGERS PSTs
+
+ @fast @preventive-only @costly @rao
+ Scenario: US 92.3.1: Activate one topological action and one PST in preventive
+ Two ways to secure the network:
+ 1. move PST to tap -5 => cost of 25
+ 2. (optimal) close line BE-FR 2 and move PST to tap -2 => cost of 20
+ Given network file is "epic92/2Nodes3ParallelLinesPST2LinesClosed.uct"
+ Given crac file is "epic92/crac-92-3-1.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst.json"
+ When I launch search_tree_rao
+ Then the worst margin is 32.13 MW
+ And the value of the objective function initially should be 2000000.0
+ And 2 remedial actions are used in preventive
+ And the remedial action "pstBeFr3" is used in preventive
+ And the tap of PstRangeAction "pstBeFr3" should be -2 in preventive
+ And the remedial action "closeBeFr2" is used in preventive
+ And the value of the objective function after PRA should be 20.0
+
+ @fast @costly @rao
+ Scenario: US 92.3.2: Preventive and curative PST + curative topological action
+ The PST is moved to tap -5 to secure the preventive perimeter for a total cost of 95
+ (20 for activation + 5 * 15 for variation). Then, there are two ways to secure the curative perimeter:
+ 1. move PST to tap -7 => cost of 20 + 2 * 15 = 50
+ 2. (optimal) close line BE-FR 3 and move PST to tap -6 => cost of 10 + 20 + 1 * 15 = 45
+ Given network file is "epic92/2Nodes4ParallelLinesPST3LinesClosed.uct"
+ Given crac file is "epic92/crac-92-3-2.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst.json"
+ When I launch search_tree_rao
+ # Worst margin on preventive CNEC
+ Then the worst margin is 5.3 MW
+ And the value of the objective function initially should be 3500000.0
+ And 1 remedial actions are used in preventive
+ And the remedial action "pstBeFr4" is used in preventive
+ And the tap of PstRangeAction "pstBeFr4" should be -5 in preventive
+ # Activation of pstBeFr4 (20) + 5 taps moved (5 * 15) + overload penalty (282.71 * 10000)
+ And the value of the objective function after PRA should be 1045531.44
+ And 2 remedial actions are used after "coBeFr2" at "curative"
+ And the remedial action "pstBeFr4" is used after "coBeFr2" at "curative"
+ And the tap of PstRangeAction "pstBeFr4" should be -6 after "coBeFr2" at "curative"
+ And the remedial action "closeBeFr3" is used after "coBeFr2" at "curative"
+ # Activation of pstBeFr4 twice (2 * 20) + 6 taps moved in total (6 * 15) + activation of closeBeFr3 (10)
+ And the value of the objective function after CRA should be 140.0
+
+ @fast @costly @rao @second-preventive
+ Scenario: US 92.3.3: Preventive and curative PST + curative topological action with 2nd preventive optimization
+ The PST is moved to tap -6 straight from preventive to only activate the remedial action once.
+ Given network file is "epic92/2Nodes4ParallelLinesPST3LinesClosed.uct"
+ Given crac file is "epic92/crac-92-3-2.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst_2P.json"
+ When I launch search_tree_rao
+ # Worst margin on curative CNEC
+ Then the worst margin is 13.02 MW
+ And the value of the objective function initially should be 3500000.0
+ And 1 remedial actions are used in preventive
+ And the remedial action "pstBeFr4" is used in preventive
+ And the tap of PstRangeAction "pstBeFr4" should be -6 in preventive
+ # Activation of pstBeFr4 (20) + 6 taps moved (6 * 15) + overload penalty (55.46 * 10000)
+ And the value of the objective function after PRA should be 554758.53
+ And 1 remedial actions are used after "coBeFr2" at "curative"
+ And the remedial action "closeBeFr3" is used after "coBeFr2" at "curative"
+ # Activation of pstBeFr4 (20) + 6 taps moved in total (6 * 15) + activation of closeBeFr3 (10)
+ And the value of the objective function after CRA should be 120.0
+
+ @fast @costly @rao
+ Scenario: US 92.3.4: Preventive and curative PST + curative topological action - 2 scenarios
+ Given network file is "epic92/2Nodes5ParallelLinesPST4LinesClosed.uct"
+ Given crac file is "epic92/crac-92-3-4.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst.json"
+ When I launch search_tree_rao
+ # Worst margin on preventive CNEC
+ Then the worst margin is 2.73 MW
+ And the value of the objective function initially should be 2333333.33
+ And 1 remedial actions are used in preventive
+ And the remedial action "pstBeFr5" is used in preventive
+ And the tap of PstRangeAction "pstBeFr5" should be -5 in preventive
+ # Activation of pstBeFr4 (20) + 5 taps moved (5 * 15) + overload penalty (69.7 * 10000)
+ And the value of the objective function after PRA should be 696982.63
+ And 2 remedial actions are used after "coBeFr2" at "curative"
+ And the remedial action "pstBeFr5" is used after "coBeFr2" at "curative"
+ And the tap of PstRangeAction "pstBeFr5" should be -6 after "coBeFr2" at "curative"
+ And the remedial action "closeBeFr4" is used after "coBeFr2" at "curative"
+ And 2 remedial actions are used after "coBeFr3" at "curative"
+ And the remedial action "pstBeFr5" is used after "coBeFr3" at "curative"
+ And the tap of PstRangeAction "pstBeFr5" should be -7 after "coBeFr3" at "curative"
+ And the remedial action "closeBeFr4" is used after "coBeFr3" at "curative"
+ # Activation of pstBeFr4 three times (3 * 20) + 8 taps moved in total (8 * 15) + activation of closeBeFr3 twice (2 * 10)
+ And the value of the objective function after CRA should be 200.0
+
+ @fast @costly @rao @second-preventive
+ Scenario: US 92.3.5: Preventive and curative PST + curative topological action - 2 scenarios with 2nd preventive optimization
+ To cut activation cost expenses, the PST is moved to tap -7 straight from preventive optimization.
+ Given network file is "epic92/2Nodes5ParallelLinesPST4LinesClosed.uct"
+ Given crac file is "epic92/crac-92-3-4.json"
+ Given configuration file is "epic92/RaoParameters_dc_minObjective_discretePst_2P.json"
+ When I launch search_tree_rao
+ # Worst margin on curative CNEC
+ Then the worst margin is 21.8 MW
+ And the value of the objective function initially should be 2333333.33
+ And 1 remedial actions are used in preventive
+ And the remedial action "pstBeFr5" is used in preventive
+ And the tap of PstRangeAction "pstBeFr5" should be -7 in preventive
+ # Activation of pstBeFr4 (20) + 5 taps moved (7 * 15) + overload penalty (4.26 * 10000)
+ And the value of the objective function after PRA should be 42744.12
+ And 1 remedial actions are used after "coBeFr2" at "curative"
+ And the remedial action "closeBeFr4" is used after "coBeFr2" at "curative"
+ And 1 remedial actions are used after "coBeFr3" at "curative"
+ And the remedial action "closeBeFr4" is used after "coBeFr3" at "curative"
+ # Activation of pstBeFr4 (20) + 7 taps moved in total (7 * 15) + activation of closeBeFr3 twice (2 * 10)
+ And the value of the objective function after CRA should be 145.0
\ No newline at end of file
diff --git a/tests/src/test/resources/files/cases/epic92/2Nodes2ParallelLinesPST.uct b/tests/src/test/resources/files/cases/epic92/2Nodes2ParallelLinesPST.uct
new file mode 100644
index 0000000000..c4b652e668
--- /dev/null
+++ b/tests/src/test/resources/files/cases/epic92/2Nodes2ParallelLinesPST.uct
@@ -0,0 +1,12 @@
+##C 2007.05.01
+##N
+##ZBE
+BBE1AA1 BE1 0 2 400.00 0.00000 0.00000 -1000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##ZFR
+FFR1AA1 FR1 0 2 400.00 1000.00 0.00000 00000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##L
+BBE1AA1 FFR1AA1 1 0 0.0000 10.000 0.000000 5000
+##T
+BBE1AA1 FFR1AA1 2 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST
+##R
+BBE1AA1 FFR1AA1 2 -0.68 90.00 16 0 SYMM
diff --git a/tests/src/test/resources/files/cases/epic92/2Nodes3ParallelLines2PSTs.uct b/tests/src/test/resources/files/cases/epic92/2Nodes3ParallelLines2PSTs.uct
new file mode 100644
index 0000000000..1942010820
--- /dev/null
+++ b/tests/src/test/resources/files/cases/epic92/2Nodes3ParallelLines2PSTs.uct
@@ -0,0 +1,14 @@
+##C 2007.05.01
+##N
+##ZBE
+BBE1AA1 BE1 0 2 400.00 0.00000 0.00000 -1000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##ZFR
+FFR1AA1 FR1 0 2 400.00 1000.00 0.00000 00000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##L
+BBE1AA1 FFR1AA1 1 0 0.0000 10.000 0.000000 5000
+##T
+BBE1AA1 FFR1AA1 2 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST
+BBE1AA1 FFR1AA1 3 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST
+##R
+BBE1AA1 FFR1AA1 2 -0.68 90.00 16 0 SYMM
+BBE1AA1 FFR1AA1 3 -0.68 90.00 16 0 SYMM
diff --git a/tests/src/test/resources/files/cases/epic92/2Nodes3ParallelLinesPST.uct b/tests/src/test/resources/files/cases/epic92/2Nodes3ParallelLinesPST.uct
new file mode 100644
index 0000000000..82298057cb
--- /dev/null
+++ b/tests/src/test/resources/files/cases/epic92/2Nodes3ParallelLinesPST.uct
@@ -0,0 +1,13 @@
+##C 2007.05.01
+##N
+##ZBE
+BBE1AA1 BE1 0 2 400.00 0.00000 0.00000 -1000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##ZFR
+FFR1AA1 FR1 0 2 400.00 1000.00 0.00000 00000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##L
+BBE1AA1 FFR1AA1 1 0 0.0000 10.000 0.000000 5000
+BBE1AA1 FFR1AA1 2 0 0.0000 10.000 0.000000 5000
+##T
+BBE1AA1 FFR1AA1 3 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST
+##R
+BBE1AA1 FFR1AA1 3 -0.68 90.00 16 0 SYMM
diff --git a/tests/src/test/resources/files/cases/epic92/2Nodes3ParallelLinesPST2LinesClosed.uct b/tests/src/test/resources/files/cases/epic92/2Nodes3ParallelLinesPST2LinesClosed.uct
new file mode 100644
index 0000000000..8607f65a3a
--- /dev/null
+++ b/tests/src/test/resources/files/cases/epic92/2Nodes3ParallelLinesPST2LinesClosed.uct
@@ -0,0 +1,13 @@
+##C 2007.05.01
+##N
+##ZBE
+BBE1AA1 BE1 0 2 400.00 0.00000 0.00000 -1000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##ZFR
+FFR1AA1 FR1 0 2 400.00 1000.00 0.00000 00000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##L
+BBE1AA1 FFR1AA1 1 0 0.0000 10.000 0.000000 5000
+BBE1AA1 FFR1AA1 2 8 0.0000 10.000 0.000000 5000
+##T
+BBE1AA1 FFR1AA1 3 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST
+##R
+BBE1AA1 FFR1AA1 3 -0.68 90.00 16 0 SYMM
diff --git a/tests/src/test/resources/files/cases/epic92/2Nodes4ParallelLinesPST3LinesClosed.uct b/tests/src/test/resources/files/cases/epic92/2Nodes4ParallelLinesPST3LinesClosed.uct
new file mode 100644
index 0000000000..86891741bd
--- /dev/null
+++ b/tests/src/test/resources/files/cases/epic92/2Nodes4ParallelLinesPST3LinesClosed.uct
@@ -0,0 +1,14 @@
+##C 2007.05.01
+##N
+##ZBE
+BBE1AA1 BE1 0 2 400.00 0.00000 0.00000 -1000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##ZFR
+FFR1AA1 FR1 0 2 400.00 1000.00 0.00000 00000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##L
+BBE1AA1 FFR1AA1 1 0 0.0000 10.000 0.000000 5000
+BBE1AA1 FFR1AA1 2 0 0.0000 10.000 0.000000 5000
+BBE1AA1 FFR1AA1 3 8 0.0000 10.000 0.000000 5000
+##T
+BBE1AA1 FFR1AA1 4 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST
+##R
+BBE1AA1 FFR1AA1 4 -0.68 90.00 16 0 SYMM
diff --git a/tests/src/test/resources/files/cases/epic92/2Nodes5ParallelLinesPST4LinesClosed.uct b/tests/src/test/resources/files/cases/epic92/2Nodes5ParallelLinesPST4LinesClosed.uct
new file mode 100644
index 0000000000..e83c5a9072
--- /dev/null
+++ b/tests/src/test/resources/files/cases/epic92/2Nodes5ParallelLinesPST4LinesClosed.uct
@@ -0,0 +1,15 @@
+##C 2007.05.01
+##N
+##ZBE
+BBE1AA1 BE1 0 2 400.00 0.00000 0.00000 -1000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##ZFR
+FFR1AA1 FR1 0 2 400.00 1000.00 0.00000 00000.0 0.00000 9000.00 -9000.0 9000.00 -9000.0
+##L
+BBE1AA1 FFR1AA1 1 0 0.0000 10.000 0.000000 5000
+BBE1AA1 FFR1AA1 2 0 0.0000 10.000 0.000000 5000
+BBE1AA1 FFR1AA1 3 0 0.0000 10.000 0.000000 5000
+BBE1AA1 FFR1AA1 4 8 0.0000 10.000 0.000000 5000
+##T
+BBE1AA1 FFR1AA1 5 0 400.0 400.0 1000. 0.0000 10.000 0.000000 0.0 5000 PST
+##R
+BBE1AA1 FFR1AA1 5 -0.68 90.00 16 0 SYMM
diff --git a/tests/src/test/resources/files/configurations/epic92/RaoParameters_dc_minObjective_2P.json b/tests/src/test/resources/files/configurations/epic92/RaoParameters_dc_minObjective_2P.json
new file mode 100644
index 0000000000..7798599dcf
--- /dev/null
+++ b/tests/src/test/resources/files/configurations/epic92/RaoParameters_dc_minObjective_2P.json
@@ -0,0 +1,134 @@
+{
+ "version" : "2.4",
+ "objective-function" : {
+ "type" : "MIN_COST_IN_MEGAWATT",
+ "forbid-cost-increase" : false,
+ "curative-min-obj-improvement" : 0.0,
+ "preventive-stop-criterion" : "MIN_OBJECTIVE",
+ "curative-stop-criterion" : "MIN_OBJECTIVE"
+ },
+ "range-actions-optimization" : {
+ "max-mip-iterations" : 5,
+ "pst-penalty-cost" : 0.01,
+ "pst-sensitivity-threshold" : 1.0E-6,
+ "pst-model" : "CONTINUOUS",
+ "hvdc-penalty-cost" : 0.001,
+ "hvdc-sensitivity-threshold" : 1.0E-6,
+ "injection-ra-penalty-cost" : 0.001,
+ "injection-ra-sensitivity-threshold" : 1.0E-6,
+ "ra-range-shrinking" : "DISABLED",
+ "linear-optimization-solver" : {
+ "solver" : "CBC",
+ "relative-mip-gap" : 0.001,
+ "solver-specific-parameters" : null
+ }
+ },
+ "topological-actions-optimization" : {
+ "max-preventive-search-tree-depth" : 5,
+ "max-auto-search-tree-depth" : 5,
+ "max-curative-search-tree-depth" : 5,
+ "predefined-combinations" : [ ],
+ "relative-minimum-impact-threshold" : 0.0,
+ "absolute-minimum-impact-threshold" : 0.0,
+ "skip-actions-far-from-most-limiting-element" : false,
+ "max-number-of-boundaries-for-skipping-actions" : 0
+ },
+ "multi-threading" : {
+ "contingency-scenarios-in-parallel" : 2,
+ "preventive-leaves-in-parallel" : 4,
+ "curative-leaves-in-parallel" : 2
+ },
+ "second-preventive-rao" : {
+ "execution-condition" : "POSSIBLE_CURATIVE_IMPROVEMENT",
+ "re-optimize-curative-range-actions" : true,
+ "hint-from-first-preventive-rao" : false
+ },
+ "not-optimized-cnecs" : {
+ "do-not-optimize-curative-cnecs-for-tsos-without-cras" : false
+ },
+ "load-flow-and-sensitivity-computation" : {
+ "load-flow-provider" : "OpenLoadFlow",
+ "sensitivity-provider" : "OpenLoadFlow",
+ "sensitivity-failure-overcost" : 10000.0,
+ "sensitivity-parameters" : {
+ "version" : "1.0",
+ "load-flow-parameters" : {
+ "version" : "1.9",
+ "voltageInitMode" : "UNIFORM_VALUES",
+ "transformerVoltageControlOn" : false,
+ "phaseShifterRegulationOn" : false,
+ "useReactiveLimits" : true,
+ "twtSplitShuntAdmittance" : true,
+ "shuntCompensatorVoltageControlOn" : false,
+ "readSlackBus" : false,
+ "writeSlackBus" : true,
+ "dc" : true,
+ "distributedSlack" : true,
+ "balanceType" : "PROPORTIONAL_TO_GENERATION_P",
+ "dcUseTransformerRatio" : false,
+ "countriesToBalance" : [ "GR", "BE", "SK", "TR", "CH", "RS", "PL", "UA", "BG", "ES", "ME", "CZ", "HR", "AL", "RO", "HU", "AT", "FR", "PT", "DE", "MK", "BA", "SI", "IT", "NL" ],
+ "connectedComponentMode" : "MAIN",
+ "hvdcAcEmulation" : true,
+ "dcPowerFactor" : 1.0,
+ "extensions" : {
+ "open-load-flow-parameters" : {
+ "slackBusSelectionMode" : "MOST_MESHED",
+ "slackBusesIds" : [ ],
+ "slackDistributionFailureBehavior" : "LEAVE_ON_SLACK_BUS",
+ "lowImpedanceBranchMode" : "REPLACE_BY_ZERO_IMPEDANCE_LINE",
+ "loadPowerFactorConstant" : false,
+ "plausibleActivePowerLimit" : 10000.0,
+ "newtonRaphsonStoppingCriteriaType" : "UNIFORM_CRITERIA",
+ "maxActivePowerMismatch" : 0.01,
+ "maxReactivePowerMismatch" : 0.01,
+ "maxVoltageMismatch" : 1.0E-4,
+ "maxAngleMismatch" : 1.0E-5,
+ "maxRatioMismatch" : 1.0E-5,
+ "maxSusceptanceMismatch" : 1.0E-4,
+ "slackBusPMaxMismatch" : 1.0,
+ "voltagePerReactivePowerControl" : false,
+ "maxNewtonRaphsonIterations" : 30,
+ "maxOuterLoopIterations" : 20,
+ "newtonRaphsonConvEpsPerEq" : 1.0E-4,
+ "voltageInitModeOverride" : "NONE",
+ "transformerVoltageControlMode" : "WITH_GENERATOR_VOLTAGE_CONTROL",
+ "shuntVoltageControlMode" : "WITH_GENERATOR_VOLTAGE_CONTROL",
+ "minPlausibleTargetVoltage" : 0.8,
+ "maxPlausibleTargetVoltage" : 1.2,
+ "minNominalVoltageTargetVoltageCheck" : 20.0,
+ "minRealisticVoltage" : 0.5,
+ "maxRealisticVoltage" : 1.5,
+ "lowImpedanceThreshold" : 1.0E-8,
+ "reactiveRangeCheckMode" : "MAX",
+ "networkCacheEnabled" : false,
+ "svcVoltageMonitoring" : true,
+ "stateVectorScalingMode" : "NONE",
+ "maxSlackBusCount" : 1,
+ "debugDir" : null,
+ "incrementalTransformerRatioTapControlOuterLoopMaxTapShift" : 3,
+ "secondaryVoltageControl" : false,
+ "reactiveLimitsMaxPqPvSwitch" : 3,
+ "phaseShifterControlMode" : "CONTINUOUS_WITH_DISCRETISATION",
+ "alwaysUpdateNetwork" : false,
+ "mostMeshedSlackBusSelectorMaxNominalVoltagePercentile" : 95.0,
+ "reportedFeatures" : [ ],
+ "slackBusCountryFilter" : [ ],
+ "actionableSwitchesIds" : [ ],
+ "asymmetrical" : false,
+ "reactivePowerDispatchMode" : "Q_EQUAL_PROPORTION",
+ "outerLoopNames" : null,
+ "useActiveLimits" : true,
+ "lineSearchStateVectorScalingMaxIteration" : 10,
+ "lineSearchStateVectorScalingStepFold" : 1.3333333333333333,
+ "maxVoltageChangeStateVectorScalingMaxDv" : 0.1,
+ "maxVoltageChangeStateVectorScalingMaxDphi" : 0.17453292519943295,
+ "linePerUnitMode" : "IMPEDANCE",
+ "useLoadModel" : false,
+ "dcApproximationType" : "IGNORE_R",
+ "simulateAutomationSystems" : false
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/src/test/resources/files/configurations/epic92/RaoParameters_dc_minObjective_discretePst.json b/tests/src/test/resources/files/configurations/epic92/RaoParameters_dc_minObjective_discretePst.json
new file mode 100644
index 0000000000..96c7381fb3
--- /dev/null
+++ b/tests/src/test/resources/files/configurations/epic92/RaoParameters_dc_minObjective_discretePst.json
@@ -0,0 +1,134 @@
+{
+ "version" : "2.4",
+ "objective-function" : {
+ "type" : "MIN_COST_IN_MEGAWATT",
+ "forbid-cost-increase" : false,
+ "curative-min-obj-improvement" : 0.0,
+ "preventive-stop-criterion" : "MIN_OBJECTIVE",
+ "curative-stop-criterion" : "MIN_OBJECTIVE"
+ },
+ "range-actions-optimization" : {
+ "max-mip-iterations" : 5,
+ "pst-penalty-cost" : 0.01,
+ "pst-sensitivity-threshold" : 1.0E-6,
+ "pst-model" : "APPROXIMATED_INTEGERS",
+ "hvdc-penalty-cost" : 0.001,
+ "hvdc-sensitivity-threshold" : 1.0E-6,
+ "injection-ra-penalty-cost" : 0.001,
+ "injection-ra-sensitivity-threshold" : 1.0E-6,
+ "ra-range-shrinking" : "DISABLED",
+ "linear-optimization-solver" : {
+ "solver" : "CBC",
+ "relative-mip-gap" : 0.001,
+ "solver-specific-parameters" : null
+ }
+ },
+ "topological-actions-optimization" : {
+ "max-preventive-search-tree-depth" : 5,
+ "max-auto-search-tree-depth" : 5,
+ "max-curative-search-tree-depth" : 5,
+ "predefined-combinations" : [ ],
+ "relative-minimum-impact-threshold" : 0.0,
+ "absolute-minimum-impact-threshold" : 0.0,
+ "skip-actions-far-from-most-limiting-element" : false,
+ "max-number-of-boundaries-for-skipping-actions" : 0
+ },
+ "multi-threading" : {
+ "contingency-scenarios-in-parallel" : 2,
+ "preventive-leaves-in-parallel" : 4,
+ "curative-leaves-in-parallel" : 2
+ },
+ "second-preventive-rao" : {
+ "execution-condition" : "DISABLED",
+ "re-optimize-curative-range-actions" : false,
+ "hint-from-first-preventive-rao" : false
+ },
+ "not-optimized-cnecs" : {
+ "do-not-optimize-curative-cnecs-for-tsos-without-cras" : false
+ },
+ "load-flow-and-sensitivity-computation" : {
+ "load-flow-provider" : "OpenLoadFlow",
+ "sensitivity-provider" : "OpenLoadFlow",
+ "sensitivity-failure-overcost" : 10000.0,
+ "sensitivity-parameters" : {
+ "version" : "1.0",
+ "load-flow-parameters" : {
+ "version" : "1.9",
+ "voltageInitMode" : "UNIFORM_VALUES",
+ "transformerVoltageControlOn" : false,
+ "phaseShifterRegulationOn" : false,
+ "useReactiveLimits" : true,
+ "twtSplitShuntAdmittance" : true,
+ "shuntCompensatorVoltageControlOn" : false,
+ "readSlackBus" : false,
+ "writeSlackBus" : true,
+ "dc" : true,
+ "distributedSlack" : true,
+ "balanceType" : "PROPORTIONAL_TO_GENERATION_P",
+ "dcUseTransformerRatio" : false,
+ "countriesToBalance" : [ "GR", "BE", "SK", "TR", "CH", "RS", "PL", "UA", "BG", "ES", "ME", "CZ", "HR", "AL", "RO", "HU", "AT", "FR", "PT", "DE", "MK", "BA", "SI", "IT", "NL" ],
+ "connectedComponentMode" : "MAIN",
+ "hvdcAcEmulation" : true,
+ "dcPowerFactor" : 1.0,
+ "extensions" : {
+ "open-load-flow-parameters" : {
+ "slackBusSelectionMode" : "MOST_MESHED",
+ "slackBusesIds" : [ ],
+ "slackDistributionFailureBehavior" : "LEAVE_ON_SLACK_BUS",
+ "lowImpedanceBranchMode" : "REPLACE_BY_ZERO_IMPEDANCE_LINE",
+ "loadPowerFactorConstant" : false,
+ "plausibleActivePowerLimit" : 10000.0,
+ "newtonRaphsonStoppingCriteriaType" : "UNIFORM_CRITERIA",
+ "maxActivePowerMismatch" : 0.01,
+ "maxReactivePowerMismatch" : 0.01,
+ "maxVoltageMismatch" : 1.0E-4,
+ "maxAngleMismatch" : 1.0E-5,
+ "maxRatioMismatch" : 1.0E-5,
+ "maxSusceptanceMismatch" : 1.0E-4,
+ "slackBusPMaxMismatch" : 1.0,
+ "voltagePerReactivePowerControl" : false,
+ "maxNewtonRaphsonIterations" : 30,
+ "maxOuterLoopIterations" : 20,
+ "newtonRaphsonConvEpsPerEq" : 1.0E-4,
+ "voltageInitModeOverride" : "NONE",
+ "transformerVoltageControlMode" : "WITH_GENERATOR_VOLTAGE_CONTROL",
+ "shuntVoltageControlMode" : "WITH_GENERATOR_VOLTAGE_CONTROL",
+ "minPlausibleTargetVoltage" : 0.8,
+ "maxPlausibleTargetVoltage" : 1.2,
+ "minNominalVoltageTargetVoltageCheck" : 20.0,
+ "minRealisticVoltage" : 0.5,
+ "maxRealisticVoltage" : 1.5,
+ "lowImpedanceThreshold" : 1.0E-8,
+ "reactiveRangeCheckMode" : "MAX",
+ "networkCacheEnabled" : false,
+ "svcVoltageMonitoring" : true,
+ "stateVectorScalingMode" : "NONE",
+ "maxSlackBusCount" : 1,
+ "debugDir" : null,
+ "incrementalTransformerRatioTapControlOuterLoopMaxTapShift" : 3,
+ "secondaryVoltageControl" : false,
+ "reactiveLimitsMaxPqPvSwitch" : 3,
+ "phaseShifterControlMode" : "CONTINUOUS_WITH_DISCRETISATION",
+ "alwaysUpdateNetwork" : false,
+ "mostMeshedSlackBusSelectorMaxNominalVoltagePercentile" : 95.0,
+ "reportedFeatures" : [ ],
+ "slackBusCountryFilter" : [ ],
+ "actionableSwitchesIds" : [ ],
+ "asymmetrical" : false,
+ "reactivePowerDispatchMode" : "Q_EQUAL_PROPORTION",
+ "outerLoopNames" : null,
+ "useActiveLimits" : true,
+ "lineSearchStateVectorScalingMaxIteration" : 10,
+ "lineSearchStateVectorScalingStepFold" : 1.3333333333333333,
+ "maxVoltageChangeStateVectorScalingMaxDv" : 0.1,
+ "maxVoltageChangeStateVectorScalingMaxDphi" : 0.17453292519943295,
+ "linePerUnitMode" : "IMPEDANCE",
+ "useLoadModel" : false,
+ "dcApproximationType" : "IGNORE_R",
+ "simulateAutomationSystems" : false
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/src/test/resources/files/configurations/epic92/RaoParameters_dc_minObjective_discretePst_2P.json b/tests/src/test/resources/files/configurations/epic92/RaoParameters_dc_minObjective_discretePst_2P.json
new file mode 100644
index 0000000000..8d3b175f5f
--- /dev/null
+++ b/tests/src/test/resources/files/configurations/epic92/RaoParameters_dc_minObjective_discretePst_2P.json
@@ -0,0 +1,134 @@
+{
+ "version" : "2.4",
+ "objective-function" : {
+ "type" : "MIN_COST_IN_MEGAWATT",
+ "forbid-cost-increase" : false,
+ "curative-min-obj-improvement" : 0.0,
+ "preventive-stop-criterion" : "MIN_OBJECTIVE",
+ "curative-stop-criterion" : "MIN_OBJECTIVE"
+ },
+ "range-actions-optimization" : {
+ "max-mip-iterations" : 5,
+ "pst-penalty-cost" : 0.01,
+ "pst-sensitivity-threshold" : 1.0E-6,
+ "pst-model" : "APPROXIMATED_INTEGERS",
+ "hvdc-penalty-cost" : 0.001,
+ "hvdc-sensitivity-threshold" : 1.0E-6,
+ "injection-ra-penalty-cost" : 0.001,
+ "injection-ra-sensitivity-threshold" : 1.0E-6,
+ "ra-range-shrinking" : "DISABLED",
+ "linear-optimization-solver" : {
+ "solver" : "CBC",
+ "relative-mip-gap" : 0.001,
+ "solver-specific-parameters" : null
+ }
+ },
+ "topological-actions-optimization" : {
+ "max-preventive-search-tree-depth" : 5,
+ "max-auto-search-tree-depth" : 5,
+ "max-curative-search-tree-depth" : 5,
+ "predefined-combinations" : [ ],
+ "relative-minimum-impact-threshold" : 0.0,
+ "absolute-minimum-impact-threshold" : 0.0,
+ "skip-actions-far-from-most-limiting-element" : false,
+ "max-number-of-boundaries-for-skipping-actions" : 0
+ },
+ "multi-threading" : {
+ "contingency-scenarios-in-parallel" : 2,
+ "preventive-leaves-in-parallel" : 4,
+ "curative-leaves-in-parallel" : 2
+ },
+ "second-preventive-rao" : {
+ "execution-condition" : "POSSIBLE_CURATIVE_IMPROVEMENT",
+ "re-optimize-curative-range-actions" : true,
+ "hint-from-first-preventive-rao" : false
+ },
+ "not-optimized-cnecs" : {
+ "do-not-optimize-curative-cnecs-for-tsos-without-cras" : false
+ },
+ "load-flow-and-sensitivity-computation" : {
+ "load-flow-provider" : "OpenLoadFlow",
+ "sensitivity-provider" : "OpenLoadFlow",
+ "sensitivity-failure-overcost" : 10000.0,
+ "sensitivity-parameters" : {
+ "version" : "1.0",
+ "load-flow-parameters" : {
+ "version" : "1.9",
+ "voltageInitMode" : "UNIFORM_VALUES",
+ "transformerVoltageControlOn" : false,
+ "phaseShifterRegulationOn" : false,
+ "useReactiveLimits" : true,
+ "twtSplitShuntAdmittance" : true,
+ "shuntCompensatorVoltageControlOn" : false,
+ "readSlackBus" : false,
+ "writeSlackBus" : true,
+ "dc" : true,
+ "distributedSlack" : true,
+ "balanceType" : "PROPORTIONAL_TO_GENERATION_P",
+ "dcUseTransformerRatio" : false,
+ "countriesToBalance" : [ "GR", "BE", "SK", "TR", "CH", "RS", "PL", "UA", "BG", "ES", "ME", "CZ", "HR", "AL", "RO", "HU", "AT", "FR", "PT", "DE", "MK", "BA", "SI", "IT", "NL" ],
+ "connectedComponentMode" : "MAIN",
+ "hvdcAcEmulation" : true,
+ "dcPowerFactor" : 1.0,
+ "extensions" : {
+ "open-load-flow-parameters" : {
+ "slackBusSelectionMode" : "MOST_MESHED",
+ "slackBusesIds" : [ ],
+ "slackDistributionFailureBehavior" : "LEAVE_ON_SLACK_BUS",
+ "lowImpedanceBranchMode" : "REPLACE_BY_ZERO_IMPEDANCE_LINE",
+ "loadPowerFactorConstant" : false,
+ "plausibleActivePowerLimit" : 10000.0,
+ "newtonRaphsonStoppingCriteriaType" : "UNIFORM_CRITERIA",
+ "maxActivePowerMismatch" : 0.01,
+ "maxReactivePowerMismatch" : 0.01,
+ "maxVoltageMismatch" : 1.0E-4,
+ "maxAngleMismatch" : 1.0E-5,
+ "maxRatioMismatch" : 1.0E-5,
+ "maxSusceptanceMismatch" : 1.0E-4,
+ "slackBusPMaxMismatch" : 1.0,
+ "voltagePerReactivePowerControl" : false,
+ "maxNewtonRaphsonIterations" : 30,
+ "maxOuterLoopIterations" : 20,
+ "newtonRaphsonConvEpsPerEq" : 1.0E-4,
+ "voltageInitModeOverride" : "NONE",
+ "transformerVoltageControlMode" : "WITH_GENERATOR_VOLTAGE_CONTROL",
+ "shuntVoltageControlMode" : "WITH_GENERATOR_VOLTAGE_CONTROL",
+ "minPlausibleTargetVoltage" : 0.8,
+ "maxPlausibleTargetVoltage" : 1.2,
+ "minNominalVoltageTargetVoltageCheck" : 20.0,
+ "minRealisticVoltage" : 0.5,
+ "maxRealisticVoltage" : 1.5,
+ "lowImpedanceThreshold" : 1.0E-8,
+ "reactiveRangeCheckMode" : "MAX",
+ "networkCacheEnabled" : false,
+ "svcVoltageMonitoring" : true,
+ "stateVectorScalingMode" : "NONE",
+ "maxSlackBusCount" : 1,
+ "debugDir" : null,
+ "incrementalTransformerRatioTapControlOuterLoopMaxTapShift" : 3,
+ "secondaryVoltageControl" : false,
+ "reactiveLimitsMaxPqPvSwitch" : 3,
+ "phaseShifterControlMode" : "CONTINUOUS_WITH_DISCRETISATION",
+ "alwaysUpdateNetwork" : false,
+ "mostMeshedSlackBusSelectorMaxNominalVoltagePercentile" : 95.0,
+ "reportedFeatures" : [ ],
+ "slackBusCountryFilter" : [ ],
+ "actionableSwitchesIds" : [ ],
+ "asymmetrical" : false,
+ "reactivePowerDispatchMode" : "Q_EQUAL_PROPORTION",
+ "outerLoopNames" : null,
+ "useActiveLimits" : true,
+ "lineSearchStateVectorScalingMaxIteration" : 10,
+ "lineSearchStateVectorScalingStepFold" : 1.3333333333333333,
+ "maxVoltageChangeStateVectorScalingMaxDv" : 0.1,
+ "maxVoltageChangeStateVectorScalingMaxDphi" : 0.17453292519943295,
+ "linePerUnitMode" : "IMPEDANCE",
+ "useLoadModel" : false,
+ "dcApproximationType" : "IGNORE_R",
+ "simulateAutomationSystems" : false
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/src/test/resources/files/crac/epic92/crac-92-2-1.json b/tests/src/test/resources/files/crac/epic92/crac-92-2-1.json
new file mode 100644
index 0000000000..0cbfacc315
--- /dev/null
+++ b/tests/src/test/resources/files/crac/epic92/crac-92-2-1.json
@@ -0,0 +1,116 @@
+{
+ "type": "CRAC",
+ "version": "2.6",
+ "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/",
+ "id": "crac-92.2.1",
+ "name": "crac-92.2.1",
+ "instants": [
+ {
+ "id": "preventive",
+ "kind": "PREVENTIVE"
+ },
+ {
+ "id": "outage",
+ "kind": "OUTAGE"
+ },
+ {
+ "id": "curative",
+ "kind": "CURATIVE"
+ }
+ ],
+ "networkElementsNamePerId": {},
+ "flowCnecs": [
+ {
+ "id": "cnecBeFrPreventive",
+ "name": "cnecBeFrPreventive",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "preventive",
+ "contingencyId": null,
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -300.0,
+ "max": 300.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -300.0,
+ "max": 300.0,
+ "side": 2
+ }
+ ]
+ }
+ ],
+ "pstRangeActions": [
+ {
+ "id": "pstBeFr2",
+ "name": "pstBeFr2",
+ "operator": "BE",
+ "activationCost": 5.0,
+ "variationCosts": {
+ "up": 10.0,
+ "down": 10.0
+ },
+ "onInstantUsageRules": [
+ {
+ "instant": "preventive",
+ "usageMethod": "available"
+ }
+ ],
+ "networkElementId": "BBE1AA1 FFR1AA1 2",
+ "initialTap": 0,
+ "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
+ },
+ "ranges": [
+ {
+ "min": -16,
+ "max": 16,
+ "rangeType": "absolute"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/src/test/resources/files/crac/epic92/crac-92-2-2.json b/tests/src/test/resources/files/crac/epic92/crac-92-2-2.json
new file mode 100644
index 0000000000..511f46ee1e
--- /dev/null
+++ b/tests/src/test/resources/files/crac/epic92/crac-92-2-2.json
@@ -0,0 +1,176 @@
+{
+ "type": "CRAC",
+ "version": "2.6",
+ "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/",
+ "id": "crac-92.2.2",
+ "name": "crac-92.2.2",
+ "instants": [
+ {
+ "id": "preventive",
+ "kind": "PREVENTIVE"
+ },
+ {
+ "id": "outage",
+ "kind": "OUTAGE"
+ },
+ {
+ "id": "curative",
+ "kind": "CURATIVE"
+ }
+ ],
+ "networkElementsNamePerId": {},
+ "flowCnecs": [
+ {
+ "id": "cnecBeFrPreventive",
+ "name": "cnecBeFrPreventive",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "preventive",
+ "contingencyId": null,
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -70.0,
+ "max": 70.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -70.0,
+ "max": 70.0,
+ "side": 2
+ }
+ ]
+ }
+ ],
+ "pstRangeActions": [
+ {
+ "id": "pstBeFr2",
+ "name": "pstBeFr2",
+ "operator": "BE",
+ "activationCost": 100.0,
+ "variationCosts": {
+ "up": 5.0,
+ "down": 5.0
+ },
+ "onInstantUsageRules": [
+ {
+ "instant": "preventive",
+ "usageMethod": "available"
+ }
+ ],
+ "networkElementId": "BBE1AA1 FFR1AA1 2",
+ "initialTap": 0,
+ "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
+ },
+ "ranges": [
+ {
+ "min": -16,
+ "max": 16,
+ "rangeType": "absolute"
+ }
+ ]
+ },
+ {
+ "id": "pstBeFr3",
+ "name": "pstBeFr3",
+ "operator": "BE",
+ "activationCost": 5.0,
+ "variationCosts": {
+ "up": 15.0,
+ "down": 15.0
+ },
+ "onInstantUsageRules": [
+ {
+ "instant": "preventive",
+ "usageMethod": "available"
+ }
+ ],
+ "networkElementId": "BBE1AA1 FFR1AA1 3",
+ "initialTap": 0,
+ "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
+ },
+ "ranges": [
+ {
+ "min": -16,
+ "max": 16,
+ "rangeType": "absolute"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/src/test/resources/files/crac/epic92/crac-92-2-3.json b/tests/src/test/resources/files/crac/epic92/crac-92-2-3.json
new file mode 100644
index 0000000000..a47d8371db
--- /dev/null
+++ b/tests/src/test/resources/files/crac/epic92/crac-92-2-3.json
@@ -0,0 +1,161 @@
+{
+ "type": "CRAC",
+ "version": "2.6",
+ "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/",
+ "id": "crac-92.2.3",
+ "name": "crac-92.2.3",
+ "instants": [
+ {
+ "id": "preventive",
+ "kind": "PREVENTIVE"
+ },
+ {
+ "id": "outage",
+ "kind": "OUTAGE"
+ },
+ {
+ "id": "curative",
+ "kind": "CURATIVE"
+ }
+ ],
+ "networkElementsNamePerId": {},
+ "contingencies": [
+ {
+ "id": "coBeFr2",
+ "networkElementsIds": [
+ "BBE1AA1 FFR1AA1 2"
+ ]
+ }
+ ],
+ "flowCnecs": [
+ {
+ "id": "cnecBeFrPreventive",
+ "name": "cnecBeFrPreventive",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "preventive",
+ "contingencyId": null,
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -250.0,
+ "max": 250.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -250.0,
+ "max": 250.0,
+ "side": 2
+ }
+ ]
+ },
+ {
+ "id": "cnecBeFrCurative",
+ "name": "cnecBeFrCurative",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -70.0,
+ "max": 70.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -70.0,
+ "max": 70.0,
+ "side": 2
+ }
+ ]
+ }
+ ],
+ "pstRangeActions": [
+ {
+ "id": "pstBeFr3",
+ "name": "pstBeFr3",
+ "operator": "BE",
+ "activationCost": 20.0,
+ "variationCosts": {
+ "up": 7.5,
+ "down": 7.5
+ },
+ "onInstantUsageRules": [
+ {
+ "instant": "preventive",
+ "usageMethod": "available"
+ }
+ ],
+ "onContingencyStateUsageRules": [
+ {
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "usageMethod": "available"
+ }
+ ],
+ "networkElementId": "BBE1AA1 FFR1AA1 3",
+ "initialTap": 0,
+ "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
+ },
+ "ranges": [
+ {
+ "min": -16,
+ "max": 16,
+ "rangeType": "absolute"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/src/test/resources/files/crac/epic92/crac-92-2-4.json b/tests/src/test/resources/files/crac/epic92/crac-92-2-4.json
new file mode 100644
index 0000000000..ec6c3fcfb6
--- /dev/null
+++ b/tests/src/test/resources/files/crac/epic92/crac-92-2-4.json
@@ -0,0 +1,156 @@
+{
+ "type": "CRAC",
+ "version": "2.6",
+ "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/",
+ "id": "crac-92.2.4",
+ "name": "crac-92.2.4",
+ "instants": [
+ {
+ "id": "preventive",
+ "kind": "PREVENTIVE"
+ },
+ {
+ "id": "outage",
+ "kind": "OUTAGE"
+ },
+ {
+ "id": "curative",
+ "kind": "CURATIVE"
+ }
+ ],
+ "networkElementsNamePerId": {},
+ "contingencies": [
+ {
+ "id": "coBeFr2",
+ "networkElementsIds": [
+ "BBE1AA1 FFR1AA1 2"
+ ]
+ }
+ ],
+ "flowCnecs": [
+ {
+ "id": "cnecBeFrPreventive",
+ "name": "cnecBeFrPreventive",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "preventive",
+ "contingencyId": null,
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -250.0,
+ "max": 250.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -250.0,
+ "max": 250.0,
+ "side": 2
+ }
+ ]
+ },
+ {
+ "id": "cnecBeFrCurative",
+ "name": "cnecBeFrCurative",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -70.0,
+ "max": 70.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -70.0,
+ "max": 70.0,
+ "side": 2
+ }
+ ]
+ }
+ ],
+ "pstRangeActions": [
+ {
+ "id": "pstBeFr3",
+ "name": "pstBeFr3",
+ "operator": "BE",
+ "onInstantUsageRules": [
+ {
+ "instant": "preventive",
+ "usageMethod": "available"
+ }
+ ],
+ "onContingencyStateUsageRules": [
+ {
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "usageMethod": "available"
+ }
+ ],
+ "networkElementId": "BBE1AA1 FFR1AA1 3",
+ "initialTap": 0,
+ "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
+ },
+ "ranges": [
+ {
+ "min": -16,
+ "max": 16,
+ "rangeType": "absolute"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/src/test/resources/files/crac/epic92/crac-92-3-1.json b/tests/src/test/resources/files/crac/epic92/crac-92-3-1.json
new file mode 100644
index 0000000000..bf2c007f32
--- /dev/null
+++ b/tests/src/test/resources/files/crac/epic92/crac-92-3-1.json
@@ -0,0 +1,135 @@
+{
+ "type": "CRAC",
+ "version": "2.6",
+ "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/",
+ "id": "crac-92.3.1",
+ "name": "crac-92.3.1",
+ "instants": [
+ {
+ "id": "preventive",
+ "kind": "PREVENTIVE"
+ },
+ {
+ "id": "outage",
+ "kind": "OUTAGE"
+ },
+ {
+ "id": "curative",
+ "kind": "CURATIVE"
+ }
+ ],
+ "networkElementsNamePerId": {},
+ "flowCnecs": [
+ {
+ "id": "cnecBeFrPreventive",
+ "name": "cnecBeFrPreventive",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "preventive",
+ "contingencyId": null,
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -300.0,
+ "max": 300.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -300.0,
+ "max": 300.0,
+ "side": 2
+ }
+ ]
+ }
+ ],
+ "pstRangeActions": [
+ {
+ "id": "pstBeFr3",
+ "name": "pstBeFr3",
+ "operator": "BE",
+ "variationCosts": {
+ "up": 5.0,
+ "down": 5.0
+ },
+ "onInstantUsageRules": [
+ {
+ "instant": "preventive",
+ "usageMethod": "available"
+ }
+ ],
+ "networkElementId": "BBE1AA1 FFR1AA1 3",
+ "initialTap": 0,
+ "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
+ },
+ "ranges": [
+ {
+ "min": -16,
+ "max": 16,
+ "rangeType": "absolute"
+ }
+ ]
+ }
+ ],
+ "networkActions": [
+ {
+ "id": "closeBeFr2",
+ "name": "closeBeFr2",
+ "operator": "FR",
+ "activationCost": 10.0,
+ "onInstantUsageRules": [
+ {
+ "instant": "preventive",
+ "usageMethod": "available"
+ }
+ ],
+ "terminalsConnectionActions": [
+ {
+ "networkElementId": "BBE1AA1 FFR1AA1 2",
+ "actionType": "close"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/src/test/resources/files/crac/epic92/crac-92-3-2.json b/tests/src/test/resources/files/crac/epic92/crac-92-3-2.json
new file mode 100644
index 0000000000..0368e1b095
--- /dev/null
+++ b/tests/src/test/resources/files/crac/epic92/crac-92-3-2.json
@@ -0,0 +1,182 @@
+{
+ "type": "CRAC",
+ "version": "2.6",
+ "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/",
+ "id": "crac-92.3.1",
+ "name": "crac-92.3.1",
+ "instants": [
+ {
+ "id": "preventive",
+ "kind": "PREVENTIVE"
+ },
+ {
+ "id": "outage",
+ "kind": "OUTAGE"
+ },
+ {
+ "id": "curative",
+ "kind": "CURATIVE"
+ }
+ ],
+ "networkElementsNamePerId": {},
+ "contingencies": [
+ {
+ "id": "coBeFr2",
+ "networkElementsIds": [
+ "BBE1AA1 FFR1AA1 2"
+ ]
+ }
+ ],
+ "flowCnecs": [
+ {
+ "id": "cnecBeFrPreventive",
+ "name": "cnecBeFrPreventive",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "preventive",
+ "contingencyId": null,
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -175.0,
+ "max": 175.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -175.0,
+ "max": 175.0,
+ "side": 2
+ }
+ ]
+ },
+ {
+ "id": "cnecBeFrCurative",
+ "name": "cnecBeFrCurative",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -150.0,
+ "max": 150.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -150.0,
+ "max": 150.0,
+ "side": 2
+ }
+ ]
+ }
+ ],
+ "pstRangeActions": [
+ {
+ "id": "pstBeFr4",
+ "name": "pstBeFr4",
+ "operator": "BE",
+ "activationCost": 20.0,
+ "variationCosts": {
+ "up": 15.0,
+ "down": 15.0
+ },
+ "onInstantUsageRules": [
+ {
+ "instant": "preventive",
+ "usageMethod": "available"
+ }
+ ],
+ "onContingencyStateUsageRules": [
+ {
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "usageMethod": "available"
+ }
+ ],
+ "networkElementId": "BBE1AA1 FFR1AA1 4",
+ "initialTap": 0,
+ "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
+ },
+ "ranges": [
+ {
+ "min": -16,
+ "max": 16,
+ "rangeType": "absolute"
+ }
+ ]
+ }
+ ],
+ "networkActions": [
+ {
+ "id": "closeBeFr3",
+ "name": "closeBeFr3",
+ "operator": "FR",
+ "activationCost": 10.0,
+ "onContingencyStateUsageRules": [
+ {
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "usageMethod": "available"
+ }
+ ],
+ "terminalsConnectionActions": [
+ {
+ "networkElementId": "BBE1AA1 FFR1AA1 3",
+ "actionType": "close"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/src/test/resources/files/crac/epic92/crac-92-3-4.json b/tests/src/test/resources/files/crac/epic92/crac-92-3-4.json
new file mode 100644
index 0000000000..d8e29a0df5
--- /dev/null
+++ b/tests/src/test/resources/files/crac/epic92/crac-92-3-4.json
@@ -0,0 +1,228 @@
+{
+ "type": "CRAC",
+ "version": "2.6",
+ "info": "Generated by PowSyBl OpenRAO https://powsybl.readthedocs.io/projects/openrao/",
+ "id": "crac-92.3.4",
+ "name": "crac-92.3.4",
+ "instants": [
+ {
+ "id": "preventive",
+ "kind": "PREVENTIVE"
+ },
+ {
+ "id": "outage",
+ "kind": "OUTAGE"
+ },
+ {
+ "id": "curative",
+ "kind": "CURATIVE"
+ }
+ ],
+ "networkElementsNamePerId": {},
+ "contingencies": [
+ {
+ "id": "coBeFr2",
+ "networkElementsIds": [
+ "BBE1AA1 FFR1AA1 2"
+ ]
+ },
+ {
+ "id": "coBeFr3",
+ "networkElementsIds": [
+ "BBE1AA1 FFR1AA1 3"
+ ]
+ }
+ ],
+ "flowCnecs": [
+ {
+ "id": "cnecBeFrPreventive",
+ "name": "cnecBeFrPreventive",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "preventive",
+ "contingencyId": null,
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -130.0,
+ "max": 130.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -130.0,
+ "max": 130.0,
+ "side": 2
+ }
+ ]
+ },
+ {
+ "id": "cnecBeFrCurative - coBeFr2",
+ "name": "cnecBeFrCurative - coBeFr2",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -110.0,
+ "max": 110.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -110.0,
+ "max": 110.0,
+ "side": 2
+ }
+ ]
+ },
+ {
+ "id": "cnecBeFrCurative - coBeFr3",
+ "name": "cnecBeFrCurative - coBeFr3",
+ "networkElementId": "BBE1AA1 FFR1AA1 1",
+ "operator": "FR",
+ "instant": "curative",
+ "contingencyId": "coBeFr3",
+ "optimized": true,
+ "monitored": false,
+ "iMax": [
+ NaN
+ ],
+ "nominalV": [
+ 400.0
+ ],
+ "thresholds": [
+ {
+ "unit": "megawatt",
+ "min": -100.0,
+ "max": 100.0,
+ "side": 1
+ },
+ {
+ "unit": "megawatt",
+ "min": -100.0,
+ "max": 100.0,
+ "side": 2
+ }
+ ]
+ }
+ ],
+ "pstRangeActions": [
+ {
+ "id": "pstBeFr5",
+ "name": "pstBeFr5",
+ "operator": "BE",
+ "activationCost": 20.0,
+ "variationCosts": {
+ "up": 15.0,
+ "down": 15.0
+ },
+ "onInstantUsageRules": [
+ {
+ "instant": "preventive",
+ "usageMethod": "available"
+ }
+ ],
+ "onContingencyStateUsageRules": [
+ {
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "usageMethod": "available"
+ },
+ {
+ "instant": "curative",
+ "contingencyId": "coBeFr3",
+ "usageMethod": "available"
+ }
+ ],
+ "networkElementId": "BBE1AA1 FFR1AA1 5",
+ "initialTap": 0,
+ "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
+ },
+ "ranges": [
+ {
+ "min": -16,
+ "max": 16,
+ "rangeType": "absolute"
+ }
+ ]
+ }
+ ],
+ "networkActions": [
+ {
+ "id": "closeBeFr4",
+ "name": "closeBeFr4",
+ "operator": "FR",
+ "activationCost": 10.0,
+ "onContingencyStateUsageRules": [
+ {
+ "instant": "curative",
+ "contingencyId": "coBeFr2",
+ "usageMethod": "available"
+ },
+ {
+ "instant": "curative",
+ "contingencyId": "coBeFr3",
+ "usageMethod": "available"
+ }
+ ],
+ "terminalsConnectionActions": [
+ {
+ "networkElementId": "BBE1AA1 FFR1AA1 4",
+ "actionType": "close"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file