RaoUtilTest.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.commons;
import com.powsybl.contingency.Contingency;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.Instant;
import com.powsybl.openrao.data.crac.api.InstantKind;
import com.powsybl.openrao.data.crac.api.NetworkElement;
import com.powsybl.openrao.data.crac.api.RemedialAction;
import com.powsybl.openrao.data.crac.api.State;
import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
import com.powsybl.iidm.network.TwoSides;
import com.powsybl.openrao.data.crac.api.networkaction.ActionType;
import com.powsybl.openrao.data.crac.api.networkaction.NetworkAction;
import com.powsybl.openrao.data.crac.api.rangeaction.RangeAction;
import com.powsybl.openrao.data.crac.api.usagerule.OnConstraint;
import com.powsybl.openrao.data.crac.api.usagerule.OnInstant;
import com.powsybl.openrao.data.crac.api.usagerule.UsageMethod;
import com.powsybl.openrao.data.crac.impl.utils.CommonCracCreation;
import com.powsybl.openrao.data.crac.impl.utils.NetworkImportsUtil;
import com.powsybl.openrao.raoapi.RaoInput;
import com.powsybl.openrao.raoapi.parameters.ObjectiveFunctionParameters;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;
import com.powsybl.openrao.raoapi.parameters.RelativeMarginsParameters;
import com.powsybl.openrao.raoapi.parameters.extensions.OpenRaoSearchTreeParameters;
import com.powsybl.openrao.raoapi.parameters.extensions.SearchTreeRaoRangeActionsOptimizationParameters;
import com.powsybl.openrao.searchtreerao.commons.optimizationperimeters.OptimizationPerimeter;
import com.powsybl.openrao.searchtreerao.result.api.FlowResult;
import com.powsybl.openrao.searchtreerao.result.api.PrePerimeterResult;
import com.powsybl.glsk.commons.ZonalData;
import com.powsybl.glsk.ucte.UcteGlskDocument;
import com.powsybl.iidm.network.Country;
import com.powsybl.iidm.network.Network;
import com.powsybl.sensitivity.SensitivityVariableSet;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Joris Mancini {@literal <joris.mancini at rte-france.com>}
*/
class RaoUtilTest {
private static final double DOUBLE_TOLERANCE = 0.1;
private static final String PREVENTIVE_INSTANT_ID = "preventive";
private static final String CURATIVE_INSTANT_ID = "curative";
private static final String AUTO_INSTANT_ID = "auto";
private RaoParameters raoParameters;
private RaoInput raoInput;
private Network network;
private Crac crac;
private String variantId;
@BeforeEach
public void setUp() {
network = NetworkImportsUtil.import12NodesNetwork();
crac = CommonCracCreation.createWithPreventivePstRange();
variantId = network.getVariantManager().getWorkingVariantId();
raoInput = RaoInput.buildWithPreventiveState(network, crac)
.withNetworkVariantId(variantId)
.build();
raoParameters = new RaoParameters();
}
private void addGlskProvider() {
ZonalData<SensitivityVariableSet> glskProvider = UcteGlskDocument.importGlsk(getClass().getResourceAsStream("/glsk/GlskCountry.xml"))
.getZonalGlsks(network);
raoInput = RaoInput.buildWithPreventiveState(network, crac)
.withNetworkVariantId(variantId)
.withGlskProvider(glskProvider)
.build();
}
@Test
void testExceptionForGlskOnRelativeMargin() {
RelativeMarginsParameters relativeMarginsParameters = new RelativeMarginsParameters();
raoParameters.setRelativeMarginsParameters(relativeMarginsParameters);
relativeMarginsParameters.setPtdfBoundariesFromString(new ArrayList<>(Arrays.asList("{FR}-{ES}", "{ES}-{PT}")));
raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN);
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> RaoUtil.checkParameters(raoParameters, raoInput));
assertEquals("Objective function MAX_MIN_RELATIVE_MARGIN requires glsks", exception.getMessage());
}
@Test
void testExceptionForNoRelativeMarginParametersOnRelativeMargin() {
addGlskProvider();
raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN);
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> RaoUtil.checkParameters(raoParameters, raoInput));
assertEquals("Objective function MAX_MIN_RELATIVE_MARGIN requires a config with a non empty boundary set", exception.getMessage());
}
@Test
void testExceptionForNullBoundariesOnRelativeMargin() {
addGlskProvider();
raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN);
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> RaoUtil.checkParameters(raoParameters, raoInput));
assertEquals("Objective function MAX_MIN_RELATIVE_MARGIN requires a config with a non empty boundary set", exception.getMessage());
}
@Test
void testExceptionForEmptyBoundariesOnRelativeMargin() {
addGlskProvider();
RelativeMarginsParameters relativeMarginsParameters = new RelativeMarginsParameters();
raoParameters.setRelativeMarginsParameters(relativeMarginsParameters);
relativeMarginsParameters.setPtdfBoundariesFromString(new ArrayList<>());
raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN);
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> RaoUtil.checkParameters(raoParameters, raoInput));
assertEquals("Objective function MAX_MIN_RELATIVE_MARGIN requires a config with a non empty boundary set", exception.getMessage());
}
@Test
void testAmpereWithDc() {
raoParameters.addExtension(OpenRaoSearchTreeParameters.class, new OpenRaoSearchTreeParameters());
OpenRaoSearchTreeParameters searchTreeParameters = raoParameters.getExtension(OpenRaoSearchTreeParameters.class);
searchTreeParameters.getLoadFlowAndSensitivityParameters().getSensitivityWithLoadFlowParameters().getLoadFlowParameters().setDc(true);
raoParameters.getObjectiveFunctionParameters().setUnit(Unit.AMPERE);
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> RaoUtil.checkParameters(raoParameters, raoInput));
assertEquals("Objective function unit A cannot be calculated with a DC default sensitivity engine", exception.getMessage());
}
@Test
void testGetBranchFlowUnitMultiplier() {
FlowCnec cnec = Mockito.mock(FlowCnec.class);
Mockito.when(cnec.getNominalVoltage(TwoSides.ONE)).thenReturn(400.);
Mockito.when(cnec.getNominalVoltage(TwoSides.TWO)).thenReturn(200.);
assertEquals(1., RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.ONE, Unit.MEGAWATT, Unit.MEGAWATT), DOUBLE_TOLERANCE);
assertEquals(1., RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.TWO, Unit.MEGAWATT, Unit.MEGAWATT), DOUBLE_TOLERANCE);
assertEquals(1., RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.ONE, Unit.AMPERE, Unit.AMPERE), DOUBLE_TOLERANCE);
assertEquals(1., RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.TWO, Unit.AMPERE, Unit.AMPERE), DOUBLE_TOLERANCE);
assertEquals(1000 / 400. / Math.sqrt(3), RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.ONE, Unit.MEGAWATT, Unit.AMPERE), DOUBLE_TOLERANCE);
assertEquals(400 * Math.sqrt(3) / 1000., RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.ONE, Unit.AMPERE, Unit.MEGAWATT), DOUBLE_TOLERANCE);
assertEquals(1000 / 200. / Math.sqrt(3), RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.TWO, Unit.MEGAWATT, Unit.AMPERE), DOUBLE_TOLERANCE);
assertEquals(200 * Math.sqrt(3) / 1000., RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.TWO, Unit.AMPERE, Unit.MEGAWATT), DOUBLE_TOLERANCE);
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.ONE, Unit.MEGAWATT, Unit.PERCENT_IMAX));
assertEquals("Only conversions between MW and A are supported.", exception.getMessage());
exception = assertThrows(OpenRaoException.class, () -> RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.ONE, Unit.KILOVOLT, Unit.MEGAWATT));
assertEquals("Only conversions between MW and A are supported.", exception.getMessage());
exception = assertThrows(OpenRaoException.class, () -> RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.TWO, Unit.AMPERE, Unit.TAP));
assertEquals("Only conversions between MW and A are supported.", exception.getMessage());
exception = assertThrows(OpenRaoException.class, () -> RaoUtil.getFlowUnitMultiplier(cnec, TwoSides.TWO, Unit.DEGREE, Unit.AMPERE));
assertEquals("Only conversions between MW and A are supported.", exception.getMessage());
}
@Test
void testGetLargestCnecThreshold() {
FlowCnec cnecA = Mockito.mock(FlowCnec.class);
FlowCnec cnecB = Mockito.mock(FlowCnec.class);
FlowCnec cnecC = Mockito.mock(FlowCnec.class);
FlowCnec cnecD = Mockito.mock(FlowCnec.class);
Mockito.when(cnecA.isOptimized()).thenReturn(true);
Mockito.when(cnecB.isOptimized()).thenReturn(true);
Mockito.when(cnecC.isOptimized()).thenReturn(true);
Mockito.when(cnecD.isOptimized()).thenReturn(false);
Mockito.when(cnecA.getUpperBound(TwoSides.ONE, Unit.MEGAWATT)).thenReturn(Optional.of(1000.));
Mockito.when(cnecA.getLowerBound(TwoSides.ONE, Unit.MEGAWATT)).thenReturn(Optional.empty());
Mockito.when(cnecB.getUpperBound(TwoSides.ONE, Unit.MEGAWATT)).thenReturn(Optional.empty());
Mockito.when(cnecB.getLowerBound(TwoSides.ONE, Unit.MEGAWATT)).thenReturn(Optional.of(-1500.));
Mockito.when(cnecC.getUpperBound(TwoSides.ONE, Unit.MEGAWATT)).thenReturn(Optional.empty());
Mockito.when(cnecC.getLowerBound(TwoSides.ONE, Unit.MEGAWATT)).thenReturn(Optional.empty());
Mockito.when(cnecD.getUpperBound(TwoSides.ONE, Unit.MEGAWATT)).thenReturn(Optional.of(-16000.));
Mockito.when(cnecD.getLowerBound(TwoSides.ONE, Unit.MEGAWATT)).thenReturn(Optional.of(-16000.));
Set.of(cnecA, cnecB, cnecC, cnecD).forEach(cnec -> when(cnec.getMonitoredSides()).thenReturn(Set.of(TwoSides.ONE)));
assertEquals(1000., RaoUtil.getLargestCnecThreshold(Set.of(cnecA), Unit.MEGAWATT), DOUBLE_TOLERANCE);
assertEquals(1500., RaoUtil.getLargestCnecThreshold(Set.of(cnecB), Unit.MEGAWATT), DOUBLE_TOLERANCE);
assertEquals(1500., RaoUtil.getLargestCnecThreshold(Set.of(cnecA, cnecB), Unit.MEGAWATT), DOUBLE_TOLERANCE);
assertEquals(1500., RaoUtil.getLargestCnecThreshold(Set.of(cnecA, cnecB, cnecC), Unit.MEGAWATT), DOUBLE_TOLERANCE);
assertEquals(1000., RaoUtil.getLargestCnecThreshold(Set.of(cnecA, cnecC), Unit.MEGAWATT), DOUBLE_TOLERANCE);
assertEquals(1500., RaoUtil.getLargestCnecThreshold(Set.of(cnecA, cnecB, cnecD), Unit.MEGAWATT), DOUBLE_TOLERANCE);
}
@Test
void testIsOnFlowConstraintAvailable() {
Instant curativeInstant = crac.getInstant(CURATIVE_INSTANT_ID);
State optimizedState = crac.getState("Contingency FR1 FR3", curativeInstant);
FlowCnec flowCnec = crac.getFlowCnec("cnec1stateCurativeContingency1");
FlowResult flowResult = mock(FlowResult.class);
PrePerimeterResult prePerimeterResult = mock(PrePerimeterResult.class);
RemedialAction<?> na1 = crac.newNetworkAction().withId("na1")
.newSwitchAction().withNetworkElement("ne1").withActionType(ActionType.OPEN).add()
.newOnInstantUsageRule().withInstant(CURATIVE_INSTANT_ID).withUsageMethod(UsageMethod.AVAILABLE).add()
.add();
// Asserts that the method returns True when given an empty set
assertTrue(RaoUtil.isRemedialActionAvailable(na1, optimizedState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
RemedialAction<?> na2 = crac.newNetworkAction().withId("na2")
.newSwitchAction().withNetworkElement("ne2").withActionType(ActionType.OPEN).add()
.newOnConstraintUsageRule().withInstant(CURATIVE_INSTANT_ID).withCnec(flowCnec.getId()).withUsageMethod(UsageMethod.AVAILABLE).add()
.add();
when(flowResult.getMargin(eq(flowCnec), any())).thenReturn(10.);
when(prePerimeterResult.getMargin(eq(flowCnec), any())).thenReturn(10.);
assertFalse(RaoUtil.isRemedialActionAvailable(na2, optimizedState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
when(flowResult.getMargin(eq(flowCnec), any())).thenReturn(-10.);
when(prePerimeterResult.getMargin(eq(flowCnec), any())).thenReturn(-10.);
assertTrue(RaoUtil.isRemedialActionAvailable(na2, optimizedState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
when(flowResult.getMargin(eq(flowCnec), any())).thenReturn(0.);
when(prePerimeterResult.getMargin(eq(flowCnec), any())).thenReturn(0.);
assertTrue(RaoUtil.isRemedialActionAvailable(na2, optimizedState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
optimizedState = crac.getPreventiveState();
assertFalse(RaoUtil.isRemedialActionAvailable(na1, optimizedState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
assertFalse(RaoUtil.isRemedialActionAvailable(na2, optimizedState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
// asserts that a preventive remedial action with forced usage rule cannot be available
RemedialAction<?> na3 = crac.newNetworkAction().withId("na3")
.newTerminalsConnectionAction().withNetworkElement("ne2").withActionType(ActionType.CLOSE).add()
.newOnInstantUsageRule().withInstant(PREVENTIVE_INSTANT_ID).withUsageMethod(UsageMethod.FORCED).add()
.add();
assertFalse(RaoUtil.isRemedialActionAvailable(na3, optimizedState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
// asserts that a remedial action with no usage rule cannot be available
NetworkAction networkActionWhithoutUsageRule = Mockito.mock(NetworkAction.class);
when(networkActionWhithoutUsageRule.getName()).thenReturn("ra without usage rule");
when(networkActionWhithoutUsageRule.getUsageRules()).thenReturn(Set.of());
assertFalse(RaoUtil.isRemedialActionAvailable(networkActionWhithoutUsageRule, optimizedState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
// mock AUTO state for the next assertions
NetworkAction automatonRa = Mockito.mock(NetworkAction.class);
when(automatonRa.getName()).thenReturn("fake automaton");
OnInstant onInstant = Mockito.mock(OnInstant.class);
OnConstraint<FlowCnec> onFlowConstraint = Mockito.mock(OnConstraint.class);
State automatonState = Mockito.mock(State.class);
when(automatonState.getInstant()).thenReturn(crac.getInstant(AUTO_INSTANT_ID));
when(automatonState.getId()).thenReturn("fake automaton state");
// remedial action with OnInstant Usage Rule
when(automatonRa.getUsageRules()).thenReturn(Set.of(onInstant));
when(onInstant.getUsageMethod(automatonState)).thenReturn(UsageMethod.FORCED);
assertTrue(RaoUtil.isRemedialActionForced(automatonRa, automatonState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
when(onInstant.getUsageMethod(automatonState)).thenReturn(UsageMethod.AVAILABLE);
assertTrue(RaoUtil.isRemedialActionAvailable(automatonRa, automatonState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
// remedial action with OnFlowConstraint Usage Rule
when(automatonRa.getUsageRules()).thenReturn(Set.of(onFlowConstraint));
when(onFlowConstraint.getUsageMethod(automatonState)).thenReturn(UsageMethod.AVAILABLE);
assertFalse(RaoUtil.isRemedialActionAvailable(automatonRa, automatonState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
when(onFlowConstraint.getUsageMethod(automatonState)).thenReturn(UsageMethod.FORCED);
assertFalse(RaoUtil.isRemedialActionAvailable(automatonRa, automatonState, prePerimeterResult, crac.getFlowCnecs(), network, raoParameters));
}
@Test
void testIsOnFlowConstraintInCountryAvailable() {
Instant preventiveInstant = crac.getInstant(PREVENTIVE_INSTANT_ID);
Instant curativeInstant = crac.getInstant(CURATIVE_INSTANT_ID);
State optimizedState = Mockito.mock(State.class);
when(optimizedState.getInstant()).thenReturn(curativeInstant);
FlowCnec cnecFrBe = crac.getFlowCnec("cnec1stateCurativeContingency1");
FlowCnec cnecFrDe = crac.getFlowCnec("cnec2stateCurativeContingency2");
PrePerimeterResult flowResult = mock(PrePerimeterResult.class);
RemedialAction<?> na1 = crac.newNetworkAction().withId("na1")
.newTerminalsConnectionAction().withNetworkElement("ne1").withActionType(ActionType.OPEN).add()
.newOnFlowConstraintInCountryUsageRule().withInstant(CURATIVE_INSTANT_ID).withCountry(Country.FR).withUsageMethod(UsageMethod.AVAILABLE).add()
.add();
RemedialAction<?> na2 = crac.newNetworkAction().withId("na2")
.newSwitchAction().withNetworkElement("ne2").withActionType(ActionType.OPEN).add()
.newOnFlowConstraintInCountryUsageRule().withInstant(CURATIVE_INSTANT_ID).withCountry(Country.BE).withUsageMethod(UsageMethod.AVAILABLE).add()
.add();
RemedialAction<?> na3 = crac.newNetworkAction().withId("na3")
.newSwitchAction().withNetworkElement("ne3").withActionType(ActionType.OPEN).add()
.newOnFlowConstraintInCountryUsageRule().withInstant(CURATIVE_INSTANT_ID).withCountry(Country.DE).withUsageMethod(UsageMethod.AVAILABLE).add()
.add();
when(flowResult.getMargin(any(), any())).thenReturn(100.);
when(flowResult.getMargin(eq(cnecFrBe), any())).thenReturn(10.);
assertIsOnFlowInCountryAvailable(na1, optimizedState, flowResult, false);
assertIsOnFlowInCountryAvailable(na2, optimizedState, flowResult, false);
assertIsOnFlowInCountryAvailable(na3, optimizedState, flowResult, false);
when(flowResult.getMargin(eq(cnecFrBe), any())).thenReturn(-10.);
assertIsOnFlowInCountryAvailable(na1, optimizedState, flowResult, true);
assertIsOnFlowInCountryAvailable(na2, optimizedState, flowResult, true);
assertIsOnFlowInCountryAvailable(na3, optimizedState, flowResult, false);
when(flowResult.getMargin(eq(cnecFrBe), any())).thenReturn(0.);
assertIsOnFlowInCountryAvailable(na1, optimizedState, flowResult, true);
assertIsOnFlowInCountryAvailable(na2, optimizedState, flowResult, true);
assertIsOnFlowInCountryAvailable(na3, optimizedState, flowResult, false);
when(flowResult.getMargin(eq(cnecFrBe), any())).thenReturn(150.);
when(flowResult.getMargin(eq(cnecFrDe), any())).thenReturn(0.);
assertIsOnFlowInCountryAvailable(na1, optimizedState, flowResult, true);
assertIsOnFlowInCountryAvailable(na2, optimizedState, flowResult, false);
assertIsOnFlowInCountryAvailable(na3, optimizedState, flowResult, true);
when(flowResult.getMargin(eq(cnecFrBe), any())).thenReturn(-150.);
when(optimizedState.getInstant()).thenReturn(preventiveInstant);
assertIsOnFlowInCountryAvailable(na1, optimizedState, flowResult, false);
assertIsOnFlowInCountryAvailable(na2, optimizedState, flowResult, false);
assertIsOnFlowInCountryAvailable(na3, optimizedState, flowResult, false);
}
@Test
void testIsOnFlowConstraintInCountryAvailableWithContingency() {
Instant curativeInstant = crac.getInstant(CURATIVE_INSTANT_ID);
State optimizedState = Mockito.mock(State.class);
when(optimizedState.getInstant()).thenReturn(curativeInstant);
when(optimizedState.getContingency()).thenReturn(Optional.of(crac.getContingency("Contingency FR1 FR3")));
FlowCnec cnecCont1 = crac.getFlowCnec("cnec1stateCurativeContingency1");
FlowCnec cnecCont2 = crac.getFlowCnec("cnec2stateCurativeContingency2");
PrePerimeterResult flowResult = mock(PrePerimeterResult.class);
RemedialAction<?> na = crac.newNetworkAction().withId("na1")
.newSwitchAction().withNetworkElement("ne1").withActionType(ActionType.OPEN).add()
.newOnFlowConstraintInCountryUsageRule().withInstant(CURATIVE_INSTANT_ID).withContingency("Contingency FR1 FR3").withCountry(Country.FR).withUsageMethod(UsageMethod.AVAILABLE).add()
.add();
// cnecCont1 is after same contingency as usage rule, not cnecCont2
// So the RA should only be available when cnecCont1 has a negative margin
when(flowResult.getMargin(any(), any())).thenReturn(100.);
when(flowResult.getMargin(eq(cnecCont1), any())).thenReturn(-10.);
when(flowResult.getMargin(eq(cnecCont2), any())).thenReturn(10.);
assertIsOnFlowInCountryAvailable(na, optimizedState, flowResult, true);
when(flowResult.getMargin(eq(cnecCont1), any())).thenReturn(10.);
when(flowResult.getMargin(eq(cnecCont2), any())).thenReturn(-10.);
assertIsOnFlowInCountryAvailable(na, optimizedState, flowResult, false);
}
private void assertIsOnFlowInCountryAvailable(RemedialAction<?> ra, State optimizedState, FlowResult flowResult, boolean available) {
assertEquals(available, RaoUtil.isRemedialActionAvailable(ra, optimizedState, flowResult, ra.getFlowCnecsConstrainingUsageRules(crac.getFlowCnecs(), network, optimizedState), network, raoParameters));
}
@Test
void testElementaryActionsLimitWithNonDiscretePsts() {
raoParameters.addExtension(OpenRaoSearchTreeParameters.class, new OpenRaoSearchTreeParameters());
raoParameters.getExtension(OpenRaoSearchTreeParameters.class).getRangeActionsOptimizationParameters().setPstModel(SearchTreeRaoRangeActionsOptimizationParameters.PstModel.CONTINUOUS);
raoInput.getCrac().newRaUsageLimits(PREVENTIVE_INSTANT_ID).withMaxElementaryActionPerTso(Map.of("TSO", 2)).add();
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> RaoUtil.checkParameters(raoParameters, raoInput));
assertEquals("The PSTs must be approximated as integers to use the limitations of elementary actions as a constraint in the RAO.", exception.getMessage());
}
@Test
void testGetLastAvailableRangeActionOnSameNetworkElementWithOutageState() {
OptimizationPerimeter optimizationContext = Mockito.mock(OptimizationPerimeter.class);
Mockito.when(optimizationContext.getMainOptimizationState()).thenReturn(crac.getPreventiveState());
State outageState = Mockito.mock(State.class);
Mockito.when(outageState.getContingency()).thenReturn(Optional.of(crac.getContingency("Contingency FR1 FR3")));
Mockito.when(outageState.getInstant()).thenReturn(crac.getInstant(InstantKind.OUTAGE));
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> RaoUtil.getLastAvailableRangeActionOnSameNetworkElement(optimizationContext, crac.getRangeActions().iterator().next(), outageState));
assertEquals("Linear optimization does not handle range actions which are neither PREVENTIVE nor CURATIVE.", exception.getMessage());
}
@Test
void testGetLastAvailableRangeActionOnSameNetworkElementMultiCurative() {
Contingency contingency = crac.getContingency("Contingency FR1 FR3");
Instant curative1Instant = Mockito.mock(Instant.class);
Mockito.when(curative1Instant.getKind()).thenReturn(InstantKind.CURATIVE);
State curativeState1 = Mockito.mock(State.class);
Mockito.when(curativeState1.getInstant()).thenReturn(curative1Instant);
Mockito.when(curativeState1.getContingency()).thenReturn(Optional.of(contingency));
Instant curative3Instant = Mockito.mock(Instant.class);
Mockito.when(curative3Instant.getKind()).thenReturn(InstantKind.CURATIVE);
Mockito.when(curative3Instant.isCurative()).thenReturn(true);
State curativeState3 = Mockito.mock(State.class);
Mockito.when(curativeState3.getInstant()).thenReturn(curative3Instant);
Mockito.when(curativeState3.getContingency()).thenReturn(Optional.of(contingency));
Mockito.when(curative1Instant.comesBefore(curative3Instant)).thenReturn(true);
NetworkElement pst = Mockito.mock(NetworkElement.class);
Mockito.when(pst.getId()).thenReturn("pst");
RangeAction<?> rangeAction1 = Mockito.mock(RangeAction.class);
Mockito.when(rangeAction1.getNetworkElements()).thenReturn(Set.of(pst));
Mockito.when(rangeAction1.getId()).thenReturn("range-action-1");
RangeAction<?> rangeAction2 = Mockito.mock(RangeAction.class);
Mockito.when(rangeAction2.getNetworkElements()).thenReturn(Set.of(pst));
Mockito.when(rangeAction2.getId()).thenReturn("range-action-2");
OptimizationPerimeter optimizationContext = Mockito.mock(OptimizationPerimeter.class);
Mockito.when(optimizationContext.getMainOptimizationState()).thenReturn(curativeState1);
Mockito.when(optimizationContext.getRangeActionsPerState()).thenReturn(Map.of(curativeState1, Set.of(rangeAction1)));
assertEquals(Pair.of(rangeAction1, curativeState1), RaoUtil.getLastAvailableRangeActionOnSameNetworkElement(optimizationContext, rangeAction2, curativeState3));
}
}