Skip to content

Commit

Permalink
fix bug on 2P Global Optimization with PST modeled as `APPROXIMATED_I…
Browse files Browse the repository at this point in the history
…NTEGERS` (#1068)

fix bug on 2P Global Optimization with PST modeled as `APPROXIMATED_INTEGERS`

Signed-off-by: belthlemar <[email protected]>
  • Loading branch information
MartinBelthle authored Jun 28, 2024
1 parent a30659b commit cc4df17
Show file tree
Hide file tree
Showing 10 changed files with 682 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@ private List<Double> getMinAndMaxAbsoluteAndRelativeSetpoints(RangeAction<?> ran
minRelativeTap = Math.max(minRelativeTap, range.getMinTap());
maxRelativeTap = Math.min(maxRelativeTap, range.getMaxTap());
break;
default:
throw new OpenRaoException(String.format("Unsupported range type %s", rangeType));
}
}
// The taps are not necessarily in order of increasing angle.
Expand Down Expand Up @@ -398,6 +400,8 @@ private List<Double> getMinAndMaxAbsoluteAndRelativeSetpoints(RangeAction<?> ran
minRelativeSetpoint = Math.max(minRelativeSetpoint, range.getMin());
maxRelativeSetpoint = Math.min(maxRelativeSetpoint, range.getMax());
break;
default:
throw new OpenRaoException(String.format("Unsupported range type %s", rangeType));
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

import com.powsybl.openrao.data.cracapi.State;
import com.powsybl.openrao.data.cracapi.rangeaction.PstRangeAction;
import com.powsybl.openrao.data.cracapi.rangeaction.RangeAction;
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.OpenRaoMPVariable;
import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.LinearProblem;
Expand All @@ -17,7 +20,9 @@
import com.powsybl.openrao.searchtreerao.result.api.RangeActionSetpointResult;
import com.powsybl.openrao.searchtreerao.result.api.SensitivityResult;
import com.powsybl.iidm.network.Network;
import org.apache.commons.lang3.tuple.Pair;

import java.util.IntSummaryStatistics;
import java.util.Map;
import java.util.Set;

Expand All @@ -27,16 +32,16 @@
public class DiscretePstTapFiller implements ProblemFiller {

private final Network network;
private final State optimizedState;
private final OptimizationPerimeter optimizationPerimeter;
private final Map<State, Set<PstRangeAction>> rangeActions;
private final RangeActionSetpointResult prePerimeterRangeActionSetpoints;

public DiscretePstTapFiller(Network network,
State optimizedState,
OptimizationPerimeter optimizationPerimeter,
Map<State, Set<PstRangeAction>> rangeActions,
RangeActionSetpointResult prePerimeterRangeActionSetpoints) {
this.network = network;
this.optimizedState = optimizedState;
this.optimizationPerimeter = optimizationPerimeter;
this.rangeActions = rangeActions;
this.prePerimeterRangeActionSetpoints = prePerimeterRangeActionSetpoints;
}
Expand Down Expand Up @@ -65,12 +70,12 @@ public void updateBetweenMipIteration(LinearProblem linearProblem, RangeActionAc
private void buildPstTapVariablesAndConstraints(LinearProblem linearProblem, PstRangeAction pstRangeAction, State state) {

// compute a few values on PST taps and angle
double prePerimeterAngle = prePerimeterRangeActionSetpoints.getSetpoint(pstRangeAction);
double currentAngle = pstRangeAction.getCurrentSetpoint(network);
int currentTap = pstRangeAction.getCurrentTapPosition(network);

int minAdmissibleTap = Math.min(pstRangeAction.convertAngleToTap(pstRangeAction.getMinAdmissibleSetpoint(prePerimeterAngle)), pstRangeAction.convertAngleToTap(pstRangeAction.getMaxAdmissibleSetpoint(prePerimeterAngle)));
int maxAdmissibleTap = Math.max(pstRangeAction.convertAngleToTap(pstRangeAction.getMinAdmissibleSetpoint(prePerimeterAngle)), pstRangeAction.convertAngleToTap(pstRangeAction.getMaxAdmissibleSetpoint(prePerimeterAngle)));
Pair<Integer, Integer> admissibleTaps = getMinAndMaxAdmissibleTaps(pstRangeAction, state);
int minAdmissibleTap = admissibleTaps.getLeft();
int maxAdmissibleTap = admissibleTaps.getRight();

int maxDownwardTapVariation = Math.max(0, currentTap - minAdmissibleTap);
int maxUpwardTapVariation = Math.max(0, maxAdmissibleTap - currentTap);
Expand Down Expand Up @@ -126,18 +131,16 @@ private void buildPstTapVariablesAndConstraints(LinearProblem linearProblem, Pst
private void refineTapToAngleConversionCoefficientAndUpdateBounds(LinearProblem linearProblem, PstRangeAction pstRangeAction, RangeActionActivationResult rangeActionActivationResult, State state) {

// compute a few values on PST taps and angle
double newAngle = rangeActionActivationResult.getOptimizedSetpoint(pstRangeAction, optimizedState);
int newTapPosition = rangeActionActivationResult.getOptimizedTap(pstRangeAction, optimizedState);
double newAngle = rangeActionActivationResult.getOptimizedSetpoint(pstRangeAction, state);
int newTapPosition = rangeActionActivationResult.getOptimizedTap(pstRangeAction, state);

double prePerimeterAngle = prePerimeterRangeActionSetpoints.getSetpoint(pstRangeAction);
int minAdmissibleTap = Math.min(pstRangeAction.convertAngleToTap(pstRangeAction.getMinAdmissibleSetpoint(prePerimeterAngle)), pstRangeAction.convertAngleToTap(pstRangeAction.getMaxAdmissibleSetpoint(prePerimeterAngle)));
int maxAdmissibleTap = Math.max(pstRangeAction.convertAngleToTap(pstRangeAction.getMinAdmissibleSetpoint(prePerimeterAngle)), pstRangeAction.convertAngleToTap(pstRangeAction.getMaxAdmissibleSetpoint(prePerimeterAngle)));
Pair<Integer, Integer> admissibleTaps = getMinAndMaxAdmissibleTaps(pstRangeAction, state);
int minAdmissibleTap = admissibleTaps.getLeft();
int maxAdmissibleTap = admissibleTaps.getRight();

int maxDownwardTapVariation = Math.max(0, newTapPosition - minAdmissibleTap);
int maxUpwardTapVariation = Math.max(0, maxAdmissibleTap - newTapPosition);

Map<Integer, Double> tapToAngleConversionMap = pstRangeAction.getTapToAngleConversionMap();

// get variables and constraints
OpenRaoMPConstraint tapToAngleConversionConstraint = linearProblem.getTapToAngleConversionConstraint(pstRangeAction, state);
OpenRaoMPVariable pstTapUpwardVariationVariable = linearProblem.getPstTapVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD);
Expand All @@ -153,6 +156,7 @@ private void refineTapToAngleConversionCoefficientAndUpdateBounds(LinearProblem

// update coefficients of the constraint with newly calculated ones, except if the tap is already at the limit of the PST range
// when updating the MIP, the factors are calibrated on a change of one tap
Map<Integer, Double> tapToAngleConversionMap = pstRangeAction.getTapToAngleConversionMap();
if (tapToAngleConversionMap.containsKey(newTapPosition + 1)) {
double angleToTapUpwardConversionFactor = tapToAngleConversionMap.get(newTapPosition + 1) - tapToAngleConversionMap.get(newTapPosition);
tapToAngleConversionConstraint.setCoefficient(pstTapUpwardVariationVariable, -angleToTapUpwardConversionFactor);
Expand All @@ -167,4 +171,28 @@ private void refineTapToAngleConversionCoefficientAndUpdateBounds(LinearProblem
downAuthorizationConstraint.setCoefficient(pstTapDownwardVariationBinary, -maxDownwardTapVariation);
upAuthorizationConstraint.setCoefficient(pstTapUpwardVariationBinary, -maxUpwardTapVariation);
}

/**
* Returns min and max admissible taps for a given PST in a given state.
* In the nominal case, it computes these values with the PST ranges and its pre-perimeter setpoint.
* However, in Second Preventive with Global Optimization, we can optimize a PST in both preventive and curative.
* If so, we can't predict the curative limits as they depend on the preventive ones.
* In such a case, we return the network limits.
*/
private Pair<Integer, Integer> getMinAndMaxAdmissibleTaps(PstRangeAction pstRangeAction, State state) {
double prePerimeterAngle = prePerimeterRangeActionSetpoints.getSetpoint(pstRangeAction);
int minTap = pstRangeAction.convertAngleToTap(pstRangeAction.getMinAdmissibleSetpoint(prePerimeterAngle));
int maxTap = pstRangeAction.convertAngleToTap(pstRangeAction.getMaxAdmissibleSetpoint(prePerimeterAngle));
int minAdmissibleTap = Math.min(maxTap, minTap);
int maxAdmissibleTap = Math.max(maxTap, minTap);

Pair<RangeAction<?>, State> lastAvailableRangeAction = RaoUtil.getLastAvailableRangeActionOnSameNetworkElement(optimizationPerimeter, pstRangeAction, state);
if (lastAvailableRangeAction != null) {
Set<Integer> pstTapsSet = pstRangeAction.getTapToAngleConversionMap().keySet();
IntSummaryStatistics tapStats = pstTapsSet.stream().mapToInt(k -> k).summaryStatistics();
minAdmissibleTap = tapStats.getMin();
maxAdmissibleTap = tapStats.getMax();
}
return Pair.of(minAdmissibleTap, maxAdmissibleTap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ private ProblemFiller buildUnoptimizedCnecFiller() {
private ProblemFiller buildIntegerPstTapFiller(Map<State, Set<PstRangeAction>> pstRangeActions) {
return new DiscretePstTapFiller(
inputs.getNetwork(),
inputs.getOptimizationPerimeter().getMainOptimizationState(),
inputs.getOptimizationPerimeter(),
pstRangeActions,
inputs.getPrePerimeterSetpoints()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void testFillAndUpdateMethods() throws IOException {
pstRangeActions.put(state, Set.of(pstRa1, pstRa2));
DiscretePstTapFiller discretePstTapFiller = new DiscretePstTapFiller(
network,
state,
optimizationPerimeter,
pstRangeActions,
initialRangeActionSetpointResult);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.data.cracapi.State;
import com.powsybl.openrao.data.cracapi.range.RangeType;
import com.powsybl.openrao.data.cracapi.rangeaction.PstRangeAction;
import com.powsybl.openrao.data.cracapi.rangeaction.RangeAction;
import com.powsybl.openrao.raoapi.parameters.RangeActionsOptimizationParameters;
Expand All @@ -29,6 +30,7 @@
import java.util.Map;
import java.util.Set;

import static com.powsybl.openrao.data.cracapi.usagerule.UsageMethod.AVAILABLE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

Expand All @@ -43,15 +45,30 @@ void testFillAndUpdateMethods() throws IOException {
// prepare data
init();
State state = crac.getPreventiveState();
PstRangeAction pstRangeAction = crac.getPstRangeAction(RANGE_ACTION_ID);
State curativeState = crac.getCurativeStates().iterator().next();
Map<Integer, Double> tapToAngle = pstRangeAction.getTapToAngleConversionMap();
PstRangeAction cra = crac.newPstRangeAction()
.withId("cra")
.withNetworkElement("BBE2AA1 BBE3AA1 1")
.newOnContingencyStateUsageRule().withUsageMethod(AVAILABLE).withContingency("N-1 NL1-NL3").withInstant("curative").add()
.withInitialTap(0)
.withTapToAngleConversionMap(tapToAngle)
.newTapRange()
.withMinTap(-10)
.withMaxTap(10)
.withRangeType(RangeType.RELATIVE_TO_PREVIOUS_INSTANT)
.add()
.add();
PstRangeAction pstRangeAction = crac.getPstRangeAction(RANGE_ACTION_ID);
double initialAlpha = network.getTwoWindingsTransformer(RANGE_ACTION_ELEMENT_ID).getPhaseTapChanger().getCurrentStep().getAlpha();
RangeActionSetpointResult initialRangeActionSetpointResult = new RangeActionSetpointResultImpl(Map.of(pstRangeAction, initialAlpha));
RangeActionSetpointResult initialRangeActionSetpointResult = new RangeActionSetpointResultImpl(Map.of(pstRangeAction, initialAlpha, cra, initialAlpha));
OptimizationPerimeter optimizationPerimeter = Mockito.mock(OptimizationPerimeter.class);

Map<State, Set<RangeAction<?>>> rangeActions = new HashMap<>();
rangeActions.put(state, Set.of(pstRangeAction));
rangeActions.put(curativeState, Set.of(cra));
Mockito.when(optimizationPerimeter.getRangeActionsPerState()).thenReturn(rangeActions);
Mockito.when(optimizationPerimeter.getMainOptimizationState()).thenReturn(state);

RangeActionsOptimizationParameters rangeActionParameters = RangeActionsOptimizationParameters.buildFromRaoParameters(new RaoParameters());

Expand All @@ -65,9 +82,10 @@ void testFillAndUpdateMethods() throws IOException {

Map<State, Set<PstRangeAction>> pstRangeActions = new HashMap<>();
pstRangeActions.put(state, Set.of(pstRangeAction));
pstRangeActions.put(curativeState, Set.of(cra));
DiscretePstTapFiller discretePstTapFiller = new DiscretePstTapFiller(
network,
state,
optimizationPerimeter,
pstRangeActions,
initialRangeActionSetpointResult);

Expand All @@ -84,6 +102,8 @@ void testFillAndUpdateMethods() throws IOException {
OpenRaoMPVariable setpointV = linearProblem.getRangeActionSetpointVariable(pstRangeAction, state);
OpenRaoMPVariable variationUpV = linearProblem.getPstTapVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD);
OpenRaoMPVariable variationDownV = linearProblem.getPstTapVariationVariable(pstRangeAction, state, LinearProblem.VariationDirectionExtension.DOWNWARD);
OpenRaoMPVariable craVariationUpV = linearProblem.getPstTapVariationVariable(cra, curativeState, LinearProblem.VariationDirectionExtension.UPWARD);
OpenRaoMPVariable craVariationDownV = linearProblem.getPstTapVariationVariable(cra, curativeState, LinearProblem.VariationDirectionExtension.DOWNWARD);
OpenRaoMPVariable binaryUpV = linearProblem.getPstTapVariationBinary(pstRangeAction, state, LinearProblem.VariationDirectionExtension.UPWARD);
OpenRaoMPVariable binaryDownV = linearProblem.getPstTapVariationBinary(pstRangeAction, state, LinearProblem.VariationDirectionExtension.DOWNWARD);
OpenRaoMPConstraint tapToAngleConversionC = linearProblem.getTapToAngleConversionConstraint(pstRangeAction, state);
Expand All @@ -103,7 +123,11 @@ void testFillAndUpdateMethods() throws IOException {

// check variable bounds
assertEquals(0, variationUpV.lb(), 1e-6);
assertEquals(30, variationUpV.ub(), 1e-6);
assertEquals(32, craVariationUpV.ub(), 1e-6);
assertEquals(0, variationDownV.lb(), 1e-6);
assertEquals(30, variationDownV.ub(), 1e-6);
assertEquals(32, craVariationDownV.ub(), 1e-6);
assertEquals(0, binaryUpV.lb(), 1e-6);
assertEquals(1, binaryUpV.ub(), 1e-6);
assertEquals(0, binaryDownV.lb(), 1e-6);
Expand Down Expand Up @@ -131,7 +155,7 @@ void testFillAndUpdateMethods() throws IOException {

// update linear problem, with a new PST tap equal to -4
double alphaBeforeUpdate = tapToAngle.get(-4);
RangeActionActivationResult rangeActionActivationResultBeforeUpdate = new RangeActionActivationResultImpl(new RangeActionSetpointResultImpl(Map.of(this.pstRangeAction, alphaBeforeUpdate)));
RangeActionActivationResult rangeActionActivationResultBeforeUpdate = new RangeActionActivationResultImpl(new RangeActionSetpointResultImpl(Map.of(this.pstRangeAction, alphaBeforeUpdate, cra, alphaBeforeUpdate)));
discretePstTapFiller.updateBetweenSensiIteration(linearProblem, flowResult, sensitivityResult, rangeActionActivationResultBeforeUpdate);

// check tap to angle conversion constraints
Expand All @@ -141,6 +165,10 @@ void testFillAndUpdateMethods() throws IOException {
assertEquals(-(tapToAngle.get(-3) - tapToAngle.get(-4)), tapToAngleConversionC.getCoefficient(variationUpV), 1e-6);
assertEquals(-(tapToAngle.get(-5) - tapToAngle.get(-4)), tapToAngleConversionC.getCoefficient(variationDownV), 1e-6);

// checks that variation is only capped by the network limits
assertEquals(32, craVariationUpV.ub(), 1e-6);
assertEquals(32, craVariationDownV.ub(), 1e-6);

// check other constraints
assertEquals(0, upVariationC.ub(), 1e-6);
assertEquals(1, upVariationC.getCoefficient(variationUpV), 1e-6);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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 19.11: Handle maximum number of elementary actions per TSO

@fast @rao @second-preventive
Scenario: US.19.11.9: Reference case with optimal solution (no global optimization).
Given network file is "epic19/small-network-2P.uct"
Given crac file is "epic19/SL_ep19us11case9.json"
Given configuration file is "epic19/RaoParameters_19_11_9.json"
When I launch search_tree_rao
Then 1 remedial actions are used in preventive
And the tap of PstRangeAction "pst_be" should be -10 in preventive
And the tap of PstRangeAction "pst_be" should be -16 after "co1_fr2_fr3_1" at "curative"
And the tap of PstRangeAction "pst_fr" should be 13 after "co1_fr2_fr3_1" at "curative"
And the worst margin is -218.5 A

@fast @rao @second-preventive
Scenario: US.19.11.9.bis: Same case with global optimization: should have the same results
Given network file is "epic19/small-network-2P.uct"
Given crac file is "epic19/SL_ep19us11case9.json"
Given configuration file is "epic19/RaoParameters_19_11_9_bis.json"
When I launch search_tree_rao
Then 1 remedial actions are used in preventive
And the tap of PstRangeAction "pst_be" should be -16 after "co1_fr2_fr3_1" at "curative"
And the tap of PstRangeAction "pst_fr" should be 13 after "co1_fr2_fr3_1" at "curative"
And the worst margin is -218.5 A

Loading

0 comments on commit cc4df17

Please sign in to comment.