MnecFillerTest.java
/*
* Copyright (c) 2020, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.fillers;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.data.crac.api.InstantKind;
import com.powsybl.openrao.data.crac.api.State;
import com.powsybl.openrao.data.crac.api.cnec.Cnec;
import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction;
import com.powsybl.openrao.data.raoresult.api.ComputationStatus;
import com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoMnecParameters;
import com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoRangeActionsOptimizationParameters.PstModel;
import com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoRangeActionsOptimizationParameters.Solver;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;
import com.powsybl.openrao.raoapi.parameters.MnecParameters;
import com.powsybl.openrao.searchtreerao.commons.optimizationperimeters.OptimizationPerimeter;
import com.powsybl.openrao.searchtreerao.linearoptimisation.algorithms.linearproblem.*;
import com.powsybl.openrao.searchtreerao.result.api.FlowResult;
import com.powsybl.openrao.searchtreerao.result.api.RangeActionSetpointResult;
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.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
/**
* @author Peter Mitri {@literal <peter.mitri at rte-france.com>}
*/
class MnecFillerTest extends AbstractFillerTest {
private LinearProblem linearProblem;
private MarginCoreProblemFiller coreProblemFiller;
private FlowCnec mnec1;
private FlowCnec mnec2;
private FlowCnec mnec3;
@BeforeEach
public void setUp() throws IOException {
init();
mnec1 = crac.newFlowCnec()
.withId("MNEC1 - N - preventive")
.withNetworkElement("DDE2AA1 NNL3AA1 1")
.newThreshold()
.withMin(-1000.)
.withSide(TwoSides.TWO)
.withMax(1000.0)
.withUnit(Unit.MEGAWATT)
.add()
.withNominalVoltage(380.)
.withOptimized(true)
.withMonitored(true)
.withInstant(PREVENTIVE_INSTANT_ID)
.add();
mnec2 = crac.newFlowCnec()
.withId("MNEC2 - N - preventive")
.withNetworkElement("NNL2AA1 BBE3AA1 1")
.newThreshold()
.withMin(-100.)
.withSide(TwoSides.ONE)
.withMax(100.0)
.withUnit(Unit.MEGAWATT)
.add()
.withNominalVoltage(380.)
.withOptimized(true)
.withMonitored(true)
.withInstant(PREVENTIVE_INSTANT_ID)
.add();
mnec3 = crac.newFlowCnec()
.withId("MNEC3 - curative")
.withNetworkElement("NNL2AA1 BBE3AA1 1")
.newThreshold().withMin(-100.).withSide(TwoSides.ONE).withMax(100.0).withUnit(Unit.MEGAWATT).add()
.newThreshold().withMin(-100.).withSide(TwoSides.TWO).withMax(100.0).withUnit(Unit.MEGAWATT).add()
.withNominalVoltage(380.)
.withOptimized(true)
.withMonitored(true)
.withContingency("N-1 NL1-NL3")
.withInstant(crac.getInstant(InstantKind.CURATIVE).getId())
.add();
RangeActionSetpointResult initialRangeActionSetpointResult = new RangeActionSetpointResultImpl(Collections.emptyMap());
OptimizationPerimeter optimizationPerimeter = Mockito.mock(OptimizationPerimeter.class);
when(optimizationPerimeter.getFlowCnecs()).thenReturn(Set.of(mnec1, mnec2, mnec3));
Map<State, Set<RangeAction<?>>> rangeActions = new HashMap<>();
rangeActions.put(cnec1.getState(), Collections.emptySet());
when(optimizationPerimeter.getRangeActionsPerState()).thenReturn(rangeActions);
RaoParameters raoParameters = new RaoParameters();
raoParameters.getRangeActionsOptimizationParameters().setPstRAMinImpactThreshold(0.01);
raoParameters.getRangeActionsOptimizationParameters().setHvdcRAMinImpactThreshold(0.01);
raoParameters.getRangeActionsOptimizationParameters().setInjectionRAMinImpactThreshold(0.01);
coreProblemFiller = new MarginCoreProblemFiller(
optimizationPerimeter,
initialRangeActionSetpointResult,
raoParameters.getRangeActionsOptimizationParameters(),
null,
Unit.MEGAWATT,
false,
PstModel.CONTINUOUS,
null);
}
private void fillProblemWithFiller(Unit unit) {
MnecParameters parameters = new MnecParameters();
SearchTreeRaoMnecParameters parametersExtension = new SearchTreeRaoMnecParameters();
parameters.setAcceptableMarginDecrease(50);
parametersExtension.setViolationCost(10);
parametersExtension.setConstraintAdjustmentCoefficient(3.5);
FlowResult flowResult = Mockito.mock(FlowResult.class);
when(flowResult.getFlow(mnec1, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(900.);
when(flowResult.getFlow(mnec2, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(-200.);
when(flowResult.getFlow(mnec3, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(-200.);
when(flowResult.getFlow(mnec3, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(-200.);
MnecFiller mnecFiller = new MnecFiller(
flowResult,
Set.of(mnec1, mnec2, mnec3),
unit,
parametersExtension.getViolationCost(),
parameters.getAcceptableMarginDecrease(),
parametersExtension.getConstraintAdjustmentCoefficient(),
null);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(mnecFiller)
.withSolver(Solver.SCIP)
.build();
linearProblem.fill(flowResult, sensitivityResult);
}
@Test
void testAddMnecViolationVariables() {
fillProblemWithFiller(Unit.MEGAWATT);
crac.getFlowCnecs().forEach(cnec -> cnec.getMonitoredSides().forEach(side -> {
if (cnec.isMonitored()) {
OpenRaoMPVariable variable = linearProblem.getMnecViolationVariable(cnec, side, Optional.empty());
assertNotNull(variable);
assertEquals(0, variable.lb(), DOUBLE_TOLERANCE);
assertEquals(linearProblem.infinity(), variable.ub(), linearProblem.infinity() * 1e-3);
} else {
Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getMnecViolationVariable(cnec, side, Optional.empty()));
assertEquals(String.format("Variable %s has not been created yet", LinearProblemIdGenerator.mnecViolationVariableId(cnec, side, Optional.empty())), e.getMessage());
}
}));
}
@Test
void testAddMnecMinFlowConstraints() {
fillProblemWithFiller(Unit.MEGAWATT);
crac.getFlowCnecs().stream().filter(cnec -> !cnec.isMonitored()).forEach(cnec -> cnec.getMonitoredSides().forEach(side -> {
Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getMnecFlowConstraint(cnec, side, LinearProblem.MarginExtension.BELOW_THRESHOLD, Optional.empty()));
assertEquals(String.format("Constraint %s has not been created yet", LinearProblemIdGenerator.mnecFlowConstraintId(cnec, side, LinearProblem.MarginExtension.BELOW_THRESHOLD, Optional.empty())), e.getMessage());
}));
OpenRaoMPConstraint ct1Max = linearProblem.getMnecFlowConstraint(mnec1, TwoSides.TWO, LinearProblem.MarginExtension.BELOW_THRESHOLD, Optional.empty());
assertNotNull(ct1Max);
assertEquals(-linearProblem.infinity(), ct1Max.lb(), linearProblem.infinity() * 1e-3);
double mnec1MaxFlow = 1000 - 3.5;
assertEquals(mnec1MaxFlow, ct1Max.ub(), DOUBLE_TOLERANCE);
assertEquals(1, ct1Max.getCoefficient(linearProblem.getFlowVariable(mnec1, TwoSides.TWO, Optional.empty())), DOUBLE_TOLERANCE);
assertEquals(-1, ct1Max.getCoefficient(linearProblem.getMnecViolationVariable(mnec1, TwoSides.TWO, Optional.empty())), DOUBLE_TOLERANCE);
OpenRaoMPConstraint ct1Min = linearProblem.getMnecFlowConstraint(mnec1, TwoSides.TWO, LinearProblem.MarginExtension.ABOVE_THRESHOLD, Optional.empty());
assertNotNull(ct1Min);
double mnec1MinFlow = -1000 + 3.5;
assertEquals(mnec1MinFlow, ct1Min.lb(), DOUBLE_TOLERANCE);
assertEquals(linearProblem.infinity(), ct1Min.ub(), linearProblem.infinity() * 1e-3);
assertEquals(1, ct1Min.getCoefficient(linearProblem.getFlowVariable(mnec1, TwoSides.TWO, Optional.empty())), DOUBLE_TOLERANCE);
assertEquals(1, ct1Min.getCoefficient(linearProblem.getMnecViolationVariable(mnec1, TwoSides.TWO, Optional.empty())), DOUBLE_TOLERANCE);
OpenRaoMPConstraint ct2Max = linearProblem.getMnecFlowConstraint(mnec2, TwoSides.ONE, LinearProblem.MarginExtension.BELOW_THRESHOLD, Optional.empty());
assertNotNull(ct2Max);
assertEquals(-linearProblem.infinity(), ct2Max.lb(), linearProblem.infinity() * 1e-3);
double mnec2MaxFlow = 100 - 3.5;
assertEquals(mnec2MaxFlow, ct2Max.ub(), DOUBLE_TOLERANCE);
assertEquals(1, ct2Max.getCoefficient(linearProblem.getFlowVariable(mnec2, TwoSides.ONE, Optional.empty())), DOUBLE_TOLERANCE);
assertEquals(-1, ct2Max.getCoefficient(linearProblem.getMnecViolationVariable(mnec2, TwoSides.ONE, Optional.empty())), DOUBLE_TOLERANCE);
OpenRaoMPConstraint ct2Min = linearProblem.getMnecFlowConstraint(mnec2, TwoSides.ONE, LinearProblem.MarginExtension.ABOVE_THRESHOLD, Optional.empty());
assertNotNull(ct2Min);
double mnec2MinFlow = -250 + 3.5;
assertEquals(mnec2MinFlow, ct2Min.lb(), DOUBLE_TOLERANCE);
assertEquals(linearProblem.infinity(), ct2Min.ub(), linearProblem.infinity() * 1e-3);
assertEquals(1, ct2Min.getCoefficient(linearProblem.getFlowVariable(mnec2, TwoSides.ONE, Optional.empty())), DOUBLE_TOLERANCE);
assertEquals(1, ct2Min.getCoefficient(linearProblem.getMnecViolationVariable(mnec2, TwoSides.ONE, Optional.empty())), DOUBLE_TOLERANCE);
}
@Test
void testAddMnecPenaltyCostMW() {
fillProblemWithFiller(Unit.MEGAWATT);
crac.getFlowCnecs().stream().filter(Cnec::isMonitored).forEach(cnec -> cnec.getMonitoredSides().forEach(side -> {
OpenRaoMPVariable mnecViolationVariable = linearProblem.getMnecViolationVariable(cnec, side, Optional.empty());
assertEquals(10.0 / cnec.getMonitoredSides().size(), linearProblem.getObjective().getCoefficient(mnecViolationVariable), DOUBLE_TOLERANCE);
}));
}
@Test
void testAddMnecPenaltyCostA() {
fillProblemWithFiller(Unit.AMPERE);
crac.getFlowCnecs().stream().filter(Cnec::isMonitored).forEach(cnec -> cnec.getMonitoredSides().forEach(side -> {
OpenRaoMPVariable mnecViolationVariable = linearProblem.getMnecViolationVariable(cnec, side, Optional.empty());
assertEquals(10.0 / 0.658179 / cnec.getMonitoredSides().size(), linearProblem.getObjective().getCoefficient(mnecViolationVariable), DOUBLE_TOLERANCE);
}));
}
@Test
void testFilterCnecWithNoInitialFlow() {
MnecParameters parameters = new MnecParameters();
SearchTreeRaoMnecParameters parametersExtension = new SearchTreeRaoMnecParameters();
parameters.setAcceptableMarginDecrease(50);
parametersExtension.setViolationCost(10);
parametersExtension.setConstraintAdjustmentCoefficient(3.5);
FlowResult flowResult = Mockito.mock(FlowResult.class);
when(flowResult.getFlow(mnec1, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(900.);
when(flowResult.getFlow(mnec2, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(-200.);
when(flowResult.getFlow(mnec3, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(-200.);
when(flowResult.getFlow(mnec3, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(Double.NaN);
when(sensitivityResult.getSensitivityStatus(crac.getState("N-1 NL1-NL3", crac.getInstant(InstantKind.CURATIVE)))).thenReturn(ComputationStatus.FAILURE);
MnecFiller mnecFiller = new MnecFiller(
flowResult,
Set.of(mnec1, mnec2, mnec3),
Unit.MEGAWATT,
parametersExtension.getViolationCost(),
parameters.getAcceptableMarginDecrease(),
parametersExtension.getConstraintAdjustmentCoefficient(),
null);
linearProblem = new LinearProblemBuilder()
.withProblemFiller(coreProblemFiller)
.withProblemFiller(mnecFiller)
.withSolver(Solver.SCIP)
.build();
linearProblem.fill(flowResult, sensitivityResult);
Exception e = assertThrows(OpenRaoException.class, () -> linearProblem.getMnecFlowConstraint(mnec3, TwoSides.ONE, LinearProblem.MarginExtension.ABOVE_THRESHOLD, Optional.empty()));
assertEquals("Constraint MNEC3 - curative_one_mnecflow_above_threshold_constraint has not been created yet", e.getMessage());
e = assertThrows(OpenRaoException.class, () -> linearProblem.getMnecFlowConstraint(mnec3, TwoSides.ONE, LinearProblem.MarginExtension.BELOW_THRESHOLD, Optional.empty()));
assertEquals("Constraint MNEC3 - curative_one_mnecflow_below_threshold_constraint has not been created yet", e.getMessage());
e = assertThrows(OpenRaoException.class, () -> linearProblem.getMnecFlowConstraint(mnec3, TwoSides.TWO, LinearProblem.MarginExtension.ABOVE_THRESHOLD, Optional.empty()));
assertEquals("Constraint MNEC3 - curative_two_mnecflow_above_threshold_constraint has not been created yet", e.getMessage());
e = assertThrows(OpenRaoException.class, () -> linearProblem.getMnecFlowConstraint(mnec3, TwoSides.TWO, LinearProblem.MarginExtension.BELOW_THRESHOLD, Optional.empty()));
assertEquals("Constraint MNEC3 - curative_two_mnecflow_below_threshold_constraint has not been created yet", e.getMessage());
e = assertThrows(OpenRaoException.class, () -> linearProblem.getMnecViolationVariable(mnec3, TwoSides.ONE, Optional.empty()));
assertEquals("Variable MNEC3 - curative_one_mnecviolation_variable has not been created yet", e.getMessage());
e = assertThrows(OpenRaoException.class, () -> linearProblem.getMnecViolationVariable(mnec3, TwoSides.TWO, Optional.empty()));
assertEquals("Variable MNEC3 - curative_two_mnecviolation_variable has not been created yet", e.getMessage());
}
}