CostCoreProblemFiller.java
/*
* 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.raoapi.parameters.extensions.SearchTreeRaoRangeActionsOptimizationParameters;
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.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;
/**
* @author Thomas Bouquet {@literal <thomas.bouquet at rte-france.com>}
*/
public class CostCoreProblemFiller extends AbstractCoreProblemFiller {
public CostCoreProblemFiller(OptimizationPerimeter optimizationContext,
RangeActionSetpointResult prePerimeterRangeActionSetpoints,
RangeActionsOptimizationParameters rangeActionParameters,
SearchTreeRaoRangeActionsOptimizationParameters rangeActionParametersExtension,
Unit unit,
boolean raRangeShrinking,
SearchTreeRaoRangeActionsOptimizationParameters.PstModel pstModel,
OffsetDateTime timestamp) {
super(optimizationContext, prePerimeterRangeActionSetpoints, rangeActionParameters, rangeActionParametersExtension, unit, raRangeShrinking, pstModel, timestamp);
if (SearchTreeRaoRangeActionsOptimizationParameters.PstModel.CONTINUOUS.equals(pstModel)) {
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<Double> activationCost = rangeAction.getActivationCost();
if (activationCost.isPresent() && activationCost.get() > 0) {
linearProblem.addRangeActionVariationBinary(rangeAction, state);
}
}
@Override
protected void buildConstraintsForRangeActionAndState(LinearProblem linearProblem, RangeAction<?> rangeAction, State state) {
addSetPointConstraints(linearProblem, rangeAction, state);
addIsVariationConstraint(linearProblem, rangeAction, state);
}
/**
* Link the activation binary variable of a RangeAction r to its variation variables.
* If one of the variation variables is non-null, the total variation is necessarily
* lower than maxReachableSetPoint[r] - minReachableSetPoint[r] and r is activated.
* isVariation[r] * (maxReachableSetPoint[r] - minReachableSetPoint[r]) >= upwardVariation[r] + downwardVariation[r]
*/
private void addIsVariationConstraint(LinearProblem linearProblem, RangeAction<?> rangeAction, State state) {
Optional<Double> 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<RangeAction<?>, 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<Double> 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:
* <ul>
* <li>An activation cost if the range action is used;</li>
* <li>Variation costs that depend on how much the set-point was shifted.</li>
* </ul>
*/
@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);
// pst costs are considered in the discreteTapFiller
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());
}
}
));
}
}