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 ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import com.powsybl.contingency.Contingency;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.commons.logs.RaoBusinessWarns;
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.CracImplFactory;
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 org.slf4j.LoggerFactory;
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));
}
@Test
void checkWarningThresholdInMwWithAc() {
ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(RaoBusinessWarns.class);
ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
listAppender.start();
logger.addAppender(listAppender);
List<ILoggingEvent> logsList = listAppender.list;
raoParameters.addExtension(OpenRaoSearchTreeParameters.class, new OpenRaoSearchTreeParameters());
OpenRaoSearchTreeParameters searchTreeParameters = raoParameters.getExtension(OpenRaoSearchTreeParameters.class);
searchTreeParameters.getLoadFlowAndSensitivityParameters().getSensitivityWithLoadFlowParameters().getLoadFlowParameters().setDc(false);
Crac crac = new CracImplFactory().create("SimpleCracId", "SimpleCracName")
.newInstant("prev", InstantKind.PREVENTIVE);
crac.newFlowCnec()
.withId("cnecOneMwThresholdOneAmpThreshold")
.withNetworkElement("anyNetworkElement")
.withInstant("prev")
.withNominalVoltage(200.)
.newThreshold().withUnit(Unit.MEGAWATT).withMax(1000.).withSide(TwoSides.ONE).add()
.newThreshold().withUnit(Unit.AMPERE).withMax(1000.).withSide(TwoSides.ONE).add()
.add();
crac.newFlowCnec()
.withId("cnecOneAmpThreshold")
.withNetworkElement("anyNetworkElement")
.withInstant("prev")
.withNominalVoltage(200.)
.newThreshold().withUnit(Unit.AMPERE).withMax(1000.).withSide(TwoSides.ONE).add()
.add();
crac.newFlowCnec()
.withId("cnecOneMwThreshold")
.withNetworkElement("anyNetworkElement")
.withInstant("prev")
.withNominalVoltage(200.)
.newThreshold().withUnit(Unit.MEGAWATT).withMax(1000.).withSide(TwoSides.ONE).add()
.add();
RaoInput raoInput = Mockito.mock(RaoInput.class);
when(raoInput.getCrac()).thenReturn(crac);
RaoUtil.checkCnecsThresholdsUnit(raoParameters, raoInput);
String expectedMsg1 = "A threshold for the flowCnec cnecOneMwThresholdOneAmpThreshold is defined in MW but the loadflow computation is in AC. It will be imprecisely converted by the RAO which could create uncoherent results due to side effects";
String expectedMsg2 = "A threshold for the flowCnec cnecOneMwThreshold is defined in MW but the loadflow computation is in AC. It will be imprecisely converted by the RAO which could create uncoherent results due to side effects";
String notExpectedMsg = "A threshold for the flowCnec cnecOneAmpThreshold is defined in MW but the loadflow computation is in AC. It will be imprecisely converted by the RAO which could create uncoherent results due to side effects";
assertEquals(2, logsList.size());
assertEquals(expectedMsg1, logsList.get(0).getMessage());
assertEquals(expectedMsg2, logsList.get(1).getMessage());
assertFalse(logsList.stream().anyMatch(e -> e.getMessage().contains(notExpectedMsg)));
}
}