AutomatonSimulatorTest.java
/*
* Copyright (c) 2022, 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.castor.algorithm;
import com.powsybl.contingency.Contingency;
import com.powsybl.contingency.ContingencyElementType;
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.CracFactory;
import com.powsybl.openrao.data.crac.api.Identifiable;
import com.powsybl.openrao.data.crac.api.Instant;
import com.powsybl.openrao.data.crac.api.InstantKind;
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.HvdcRangeAction;
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.usagerule.UsageMethod;
import com.powsybl.openrao.data.raoresult.api.ComputationStatus;
import com.powsybl.openrao.raoapi.parameters.ObjectiveFunctionParameters;
import com.powsybl.openrao.raoapi.parameters.RaoParameters;
import com.powsybl.openrao.raoapi.parameters.extensions.OpenRaoSearchTreeParameters;
import com.powsybl.openrao.searchtreerao.commons.ToolProvider;
import com.powsybl.openrao.searchtreerao.result.api.FlowResult;
import com.powsybl.openrao.searchtreerao.result.api.PrePerimeterResult;
import com.powsybl.openrao.searchtreerao.result.api.RangeActionSetpointResult;
import com.powsybl.openrao.searchtreerao.result.api.SensitivityResult;
import com.powsybl.openrao.searchtreerao.result.impl.AutomatonPerimeterResultImpl;
import com.powsybl.iidm.network.HvdcLine;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControl;
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 java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* @author Peter Mitri {@literal <peter.mitri at rte-france.com>}
*/
class AutomatonSimulatorTest {
private AutomatonSimulator automatonSimulator;
private Crac crac;
private Network network;
private State autoState;
private RangeAction<?> ra2;
private RangeAction<?> ra3;
private RangeAction<?> ra4;
private PstRangeAction ara1;
private RangeAction<?> ara2;
private RangeAction<?> ara3;
private RangeAction<?> ara4;
private RangeAction<?> ara5;
private RangeAction<?> ara6;
private NetworkAction na;
private HvdcRangeAction hvdcRa1;
private HvdcRangeAction hvdcRa2;
private FlowCnec cnec1;
private FlowCnec cnec2;
private PrePerimeterSensitivityAnalysis mockedPreAutoPerimeterSensitivityAnalysis;
private PrePerimeterResult mockedPrePerimeterResult;
private static final double DOUBLE_TOLERANCE = 0.01;
private static final String PREVENTIVE_INSTANT_ID = "preventive";
private static final String OUTAGE_INSTANT_ID = "outage";
private static final String AUTO_INSTANT_ID = "auto";
@BeforeEach
public void setup() {
network = Network.read("TestCase16NodesWith2Hvdc.xiidm", getClass().getResourceAsStream("/network/TestCase16NodesWith2Hvdc.xiidm"));
// Add some lines otherwise HVDC2 is connected to nothing and load-flow produces NaN angles
network.newLine()
.setId("newline1")
.setR(0.01).setX(0.01)
.setBus1("BBE2AA12").setVoltageLevel1("BBE2AA1").setG1(0.01).setB1(0.01)
.setBus2("DDE3AA11").setVoltageLevel2("DDE3AA1").setG2(0.01).setB2(0.01)
.add();
network.newLine()
.setId("newline2")
.setR(0.01).setX(0.01)
.setBus1("FFR3AA12").setVoltageLevel1("FFR3AA1").setG1(0.01).setB1(0.01)
.setBus2("DDE2AA11").setVoltageLevel2("DDE2AA1").setG2(0.01).setB2(0.01)
.add();
crac = CracFactory.findDefault().create("test-crac")
.newInstant(PREVENTIVE_INSTANT_ID, InstantKind.PREVENTIVE)
.newInstant(OUTAGE_INSTANT_ID, InstantKind.OUTAGE)
.newInstant(AUTO_INSTANT_ID, InstantKind.AUTO);
Contingency contingency1 = crac.newContingency()
.withId("contingency1")
.withContingencyElement("NNL1AA11 NNL2AA11 1", ContingencyElementType.LINE)
.add();
crac.newFlowCnec()
.withId("cnec-prev")
.withNetworkElement("cnec-ne")
.withInstant(PREVENTIVE_INSTANT_ID)
.withNominalVoltage(220.)
.newThreshold().withSide(TwoSides.TWO).withMax(1000.).withUnit(Unit.AMPERE).add()
.add();
cnec1 = crac.newFlowCnec()
.withId("cnec1")
.withNetworkElement("cnec-ne")
.withContingency("contingency1")
.withInstant(AUTO_INSTANT_ID)
.withNominalVoltage(220.)
.newThreshold().withSide(TwoSides.TWO).withMax(1000.).withUnit(Unit.AMPERE).add()
.add();
cnec2 = crac.newFlowCnec()
.withId("cnec2")
.withNetworkElement("cnec-ne")
.withContingency("contingency1")
.withInstant(AUTO_INSTANT_ID)
.withNominalVoltage(220.)
.newThreshold().withSide(TwoSides.TWO).withMax(1000.).withUnit(Unit.AMPERE).add()
.add();
Instant autoInstant = crac.getInstant(AUTO_INSTANT_ID);
autoState = crac.getState(contingency1, autoInstant);
ra2 = crac.newPstRangeAction()
.withId("ra2")
.withNetworkElement("ra2-ne")
.withSpeed(2)
.newOnInstantUsageRule().withInstant(AUTO_INSTANT_ID).withUsageMethod(UsageMethod.FORCED).add()
.withInitialTap(0).withTapToAngleConversionMap(Map.of(0, -100., 1, 100.))
.add();
ra3 = crac.newPstRangeAction()
.withId("ra3")
.withNetworkElement("ra3-ne")
.withSpeed(4)
.newOnConstraintUsageRule().withInstant(AUTO_INSTANT_ID).withCnec("cnec1").withUsageMethod(UsageMethod.FORCED).add()
.withInitialTap(0).withTapToAngleConversionMap(Map.of(0, -100., 1, 100.))
.add();
ra4 = crac.newPstRangeAction()
.withId("ra4")
.withNetworkElement("ra4-ne")
.withSpeed(4)
.newOnConstraintUsageRule().withInstant(PREVENTIVE_INSTANT_ID).withCnec("cnec-prev").withUsageMethod(UsageMethod.AVAILABLE).add()
.withInitialTap(0).withTapToAngleConversionMap(Map.of(0, -100., 1, 100.))
.add();
// Add 2 aligned range actions
ara1 = crac.newPstRangeAction()
.withId("ara1")
.withGroupId("group1")
.withNetworkElement("BBE2AA11 BBE3AA11 1")
.withSpeed(3)
.newOnConstraintUsageRule().withInstant(AUTO_INSTANT_ID).withCnec("cnec1").withUsageMethod(UsageMethod.FORCED).add()
.withInitialTap(0).withTapToAngleConversionMap(Map.of(0, 0.1, 1, 1.1, 2, 2.1, 3, 3.1, -1, -1.1, -2, -2.1, -3, -3.1))
.add();
ara2 = crac.newPstRangeAction()
.withId("ara2")
.withGroupId("group1")
.withNetworkElement("FFR2AA11 FFR4AA11 1")
.withSpeed(3)
.newOnConstraintUsageRule().withInstant(AUTO_INSTANT_ID).withCnec("cnec1").withUsageMethod(UsageMethod.FORCED).add()
.withInitialTap(0).withTapToAngleConversionMap(Map.of(0, 0.1, 1, 1.1, 2, 2.1, 3, 3.1, -1, -1.1, -2, -2.1, -3, -3.1))
.add();
// Add 2 aligned range actions of different types
ara3 = crac.newPstRangeAction()
.withId("ara3")
.withGroupId("group2")
.withNetworkElement("ra2-ne")
.withSpeed(5)
.newOnInstantUsageRule().withInstant(AUTO_INSTANT_ID).withUsageMethod(UsageMethod.FORCED).add()
.withInitialTap(0).withTapToAngleConversionMap(Map.of(0, -100., 1, 100.))
.add();
ara4 = crac.newHvdcRangeAction()
.withId("ara4")
.withNetworkElement("ra1-ne")
.withGroupId("group2")
.withSpeed(5)
.newRange().withMax(1).withMin(-1).add()
.add();
// Add 2 aligned range actions with different usage methods
ara5 = crac.newPstRangeAction()
.withId("ara5")
.withGroupId("group3")
.withNetworkElement("ra2-ne")
.withSpeed(6)
.newOnInstantUsageRule().withInstant(AUTO_INSTANT_ID).withUsageMethod(UsageMethod.FORCED).add()
.withInitialTap(0).withTapToAngleConversionMap(Map.of(0, -100., 1, 100.))
.add();
ara6 = crac.newPstRangeAction()
.withId("ara6")
.withGroupId("group3")
.withNetworkElement("ra3-ne")
.withSpeed(6)
.newOnConstraintUsageRule().withInstant(AUTO_INSTANT_ID).withCnec("cnec1").withUsageMethod(UsageMethod.FORCED).add()
.withInitialTap(0).withTapToAngleConversionMap(Map.of(0, -100., 1, 100.))
.add();
// Add a network action
na = crac.newNetworkAction()
.withId("na")
.newSwitchAction().withActionType(ActionType.CLOSE).withNetworkElement("DDE3AA11 DDE4AA11 1").add()
.newOnConstraintUsageRule().withInstant(AUTO_INSTANT_ID).withCnec("cnec2").withUsageMethod(UsageMethod.FORCED).add()
.add();
// Add HVDC range actions
hvdcRa1 = crac.newHvdcRangeAction()
.withId("hvdc-ra1")
.withGroupId("hvdcGroup")
.withNetworkElement("BBE2AA11 FFR3AA11 1")
.withSpeed(1)
.newRange().withMax(3000).withMin(-3000).add()
.newOnInstantUsageRule().withInstant(AUTO_INSTANT_ID).withUsageMethod(UsageMethod.FORCED).add()
.add();
hvdcRa2 = crac.newHvdcRangeAction()
.withId("hvdc-ra2")
.withGroupId("hvdcGroup")
.withNetworkElement("BBE2AA12 FFR3AA12 1")
.withSpeed(1)
.newRange().withMax(3000).withMin(-3000).add()
.newOnInstantUsageRule().withInstant(AUTO_INSTANT_ID).withUsageMethod(UsageMethod.FORCED).add()
.add();
autoState = crac.getState(contingency1, autoInstant);
RaoParameters raoParameters = new RaoParameters();
raoParameters.addExtension(OpenRaoSearchTreeParameters.class, new OpenRaoSearchTreeParameters());
OpenRaoSearchTreeParameters searchTreeParameters = raoParameters.getExtension(OpenRaoSearchTreeParameters.class);
raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN);
raoParameters.getObjectiveFunctionParameters().setUnit(Unit.MEGAWATT);
searchTreeParameters.getLoadFlowAndSensitivityParameters().setSensitivityProvider("OpenLoadFlow");
mockedPreAutoPerimeterSensitivityAnalysis = mock(PrePerimeterSensitivityAnalysis.class);
mockedPrePerimeterResult = mock(PrePerimeterResult.class);
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
FlowResult mockedFlowResult = mock(FlowResult.class);
when(mockedPrePerimeterResult.getFlowResult()).thenReturn(mockedFlowResult);
ToolProvider toolProvider = Mockito.mock(ToolProvider.class);
when(toolProvider.getLoopFlowCnecs(any())).thenReturn(Collections.emptySet());
automatonSimulator = new AutomatonSimulator(crac, raoParameters, toolProvider, null, mockedPrePerimeterResult, null, 0);
}
@Test
void testGatherCnecs() {
assertEquals(2, automatonSimulator.gatherFlowCnecsForAutoRangeAction(ra2, autoState, network).size());
assertEquals(1, automatonSimulator.gatherFlowCnecsForAutoRangeAction(ra3, autoState, network).size());
}
@Test
void testGatherCnecsError() {
RangeAction<?> ra = Mockito.mock(RangeAction.class);
Mockito.when(ra.toString()).thenReturn("RangeAction");
Mockito.when(ra.getUsageMethod(autoState)).thenReturn(UsageMethod.AVAILABLE);
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> automatonSimulator.gatherFlowCnecsForAutoRangeAction(ra, autoState, network));
assertEquals("Range action RangeAction has usage method AVAILABLE although FORCED was expected.", exception.getMessage());
}
@Test
void testCheckAlignedRangeActions1() {
// OK
assertTrue(AutomatonSimulator.checkAlignedRangeActions(List.of(ara1, ara2), List.of(ara1, ara2)));
assertTrue(AutomatonSimulator.checkAlignedRangeActions(List.of(ara2, ara1), List.of(ara1, ara2)));
assertTrue(AutomatonSimulator.checkAlignedRangeActions(List.of(ara5, ra4), List.of(ara5, ra3, ra4)));
// different types
assertFalse(AutomatonSimulator.checkAlignedRangeActions(List.of(ara3, ara4), List.of(ara3, ara4)));
assertFalse(AutomatonSimulator.checkAlignedRangeActions(List.of(ara4, ara3), List.of(ara3, ara4)));
// one unavailable RA
assertFalse(AutomatonSimulator.checkAlignedRangeActions(List.of(ara1, ara2), List.of(ara1)));
}
@Test
void testBuildRangeActionsGroupsOrderedBySpeed() {
PrePerimeterResult rangeActionSensitivity = Mockito.mock(PrePerimeterResult.class);
assertEquals(List.of(List.of(hvdcRa1, hvdcRa2)), automatonSimulator.buildRangeActionsGroupsForSpeed(rangeActionSensitivity, autoState, network, 1));
assertEquals(List.of(List.of(ra2)), automatonSimulator.buildRangeActionsGroupsForSpeed(rangeActionSensitivity, autoState, network, 2));
assertEquals(List.of(List.of(ara1, ara2)), automatonSimulator.buildRangeActionsGroupsForSpeed(rangeActionSensitivity, autoState, network, 3));
assertEquals(List.of(List.of(ra3)), automatonSimulator.buildRangeActionsGroupsForSpeed(rangeActionSensitivity, autoState, network, 4));
assertTrue(automatonSimulator.buildRangeActionsGroupsForSpeed(rangeActionSensitivity, autoState, network, 5).isEmpty());
assertEquals(List.of(List.of(ara5, ara6)), automatonSimulator.buildRangeActionsGroupsForSpeed(rangeActionSensitivity, autoState, network, 6));
}
@Test
void testDisableHvdcAngleDroopControl1() {
PrePerimeterResult prePerimeterResult = mock(PrePerimeterResult.class);
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
Pair<PrePerimeterResult, Map<HvdcRangeAction, Double>> result = automatonSimulator.disableHvdcAngleDroopActivePowerControl(List.of(hvdcRa1), network, mockedPreAutoPerimeterSensitivityAnalysis, prePerimeterResult, autoState);
// check that angle-droop control was disabled on HVDC
assertFalse(network.getHvdcLine("BBE2AA11 FFR3AA11 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
// check that other HVDC was not touched
assertTrue(network.getHvdcLine("BBE2AA12 FFR3AA12 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
// check that sensitivity computation has been run
assertEquals(mockedPrePerimeterResult, result.getLeft());
assertEquals(1, result.getRight().size());
assertEquals(2451.3764524964786, result.getRight().get(hvdcRa1), DOUBLE_TOLERANCE);
assertEquals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER, network.getHvdcLine(hvdcRa1.getNetworkElement().getId()).getConvertersMode());
assertEquals(2451.3764524964786, network.getHvdcLine(hvdcRa1.getNetworkElement().getId()).getActivePowerSetpoint(), DOUBLE_TOLERANCE);
assertEquals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER, network.getHvdcLine(hvdcRa2.getNetworkElement().getId()).getConvertersMode());
assertEquals(0, network.getHvdcLine(hvdcRa2.getNetworkElement().getId()).getActivePowerSetpoint(), DOUBLE_TOLERANCE);
// run a second time => no influence + sensitivity not run
result = automatonSimulator.disableHvdcAngleDroopActivePowerControl(List.of(hvdcRa1), network, mockedPreAutoPerimeterSensitivityAnalysis, prePerimeterResult, autoState);
assertFalse(network.getHvdcLine("BBE2AA11 FFR3AA11 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
assertTrue(network.getHvdcLine("BBE2AA12 FFR3AA12 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
assertEquals(prePerimeterResult, result.getLeft());
assertEquals(Map.of(), result.getRight());
assertEquals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER, network.getHvdcLine(hvdcRa1.getNetworkElement().getId()).getConvertersMode());
assertEquals(2451.3764524964786, network.getHvdcLine(hvdcRa1.getNetworkElement().getId()).getActivePowerSetpoint(), DOUBLE_TOLERANCE);
assertEquals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER, network.getHvdcLine(hvdcRa2.getNetworkElement().getId()).getConvertersMode());
assertEquals(0, network.getHvdcLine(hvdcRa2.getNetworkElement().getId()).getActivePowerSetpoint(), DOUBLE_TOLERANCE);
}
@Test
void testDisableHvdcAngleDroopControl2() {
PrePerimeterResult prePerimeterResult = mock(PrePerimeterResult.class);
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// Test on 2 aligned HVDC RAs
Pair<PrePerimeterResult, Map<HvdcRangeAction, Double>> result = automatonSimulator.disableHvdcAngleDroopActivePowerControl(List.of(hvdcRa1, hvdcRa2), network, mockedPreAutoPerimeterSensitivityAnalysis, prePerimeterResult, autoState);
assertFalse(network.getHvdcLine("BBE2AA11 FFR3AA11 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
assertFalse(network.getHvdcLine("BBE2AA12 FFR3AA12 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
assertEquals(mockedPrePerimeterResult, result.getLeft());
assertEquals(2, result.getRight().size());
assertEquals(2451.3764524964786, result.getRight().get(hvdcRa1), DOUBLE_TOLERANCE);
assertEquals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER, network.getHvdcLine(hvdcRa1.getNetworkElement().getId()).getConvertersMode());
assertEquals(2451.3764524964786, network.getHvdcLine(hvdcRa1.getNetworkElement().getId()).getActivePowerSetpoint(), DOUBLE_TOLERANCE);
assertEquals(-45.72718047413281, result.getRight().get(hvdcRa2), DOUBLE_TOLERANCE);
assertEquals(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER, network.getHvdcLine(hvdcRa2.getNetworkElement().getId()).getConvertersMode());
assertEquals(45.72718047413281, network.getHvdcLine(hvdcRa2.getNetworkElement().getId()).getActivePowerSetpoint(), DOUBLE_TOLERANCE);
}
@Test
void testDisableHvdcAngleDroopControl3() {
PrePerimeterResult prePerimeterResult = mock(PrePerimeterResult.class);
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// Test on an HVDC with no HvdcAngleDroopActivePowerControl
network.getHvdcLine("BBE2AA11 FFR3AA11 1").removeExtension(HvdcAngleDroopActivePowerControl.class);
Pair<PrePerimeterResult, Map<HvdcRangeAction, Double>> result = automatonSimulator.disableHvdcAngleDroopActivePowerControl(List.of(hvdcRa1), network, mockedPreAutoPerimeterSensitivityAnalysis, prePerimeterResult, autoState);
assertEquals(prePerimeterResult, result.getLeft());
assertEquals(Map.of(), result.getRight());
}
@Test
void testDisableHvdcAngleDroopControl4() {
PrePerimeterResult prePerimeterResult = mock(PrePerimeterResult.class);
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// Test on non-HVDC : nothing should happen
Pair<PrePerimeterResult, Map<HvdcRangeAction, Double>> result = automatonSimulator.disableHvdcAngleDroopActivePowerControl(List.of(ra2), network, mockedPreAutoPerimeterSensitivityAnalysis, prePerimeterResult, autoState);
assertEquals(prePerimeterResult, result.getLeft());
assertEquals(Map.of(), result.getRight());
}
@Test
void testDisableHvdcAngleDroopControl6() {
// Initial setpoint is out of RA's allowed range. Do not disable HvdcAngleDroopControl
hvdcRa1 = crac.newHvdcRangeAction()
.withId("hvdc-ra3")
.withGroupId("hvdcGroup")
.withNetworkElement("BBE2AA11 FFR3AA11 1")
.withSpeed(1)
.newRange().withMax(1000).withMin(-1000).add()
.newOnInstantUsageRule().withInstant(AUTO_INSTANT_ID).withUsageMethod(UsageMethod.FORCED).add()
.add();
PrePerimeterResult prePerimeterResult = mock(PrePerimeterResult.class);
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
Pair<PrePerimeterResult, Map<HvdcRangeAction, Double>> result = automatonSimulator.disableHvdcAngleDroopActivePowerControl(List.of(hvdcRa1), network, mockedPreAutoPerimeterSensitivityAnalysis, prePerimeterResult, autoState);
assertTrue(network.getHvdcLine("BBE2AA11 FFR3AA11 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
assertTrue(result.getRight().isEmpty());
}
@Test
void testRoundUpAngleToTapWrtInitialSetpoint() {
assertEquals(2.1, AutomatonSimulator.roundUpAngleToTapWrtInitialSetpoint(ara1, 1.2, 0.1), DOUBLE_TOLERANCE);
assertEquals(1.1, AutomatonSimulator.roundUpAngleToTapWrtInitialSetpoint(ara1, 1.1, 0.1), DOUBLE_TOLERANCE);
assertEquals(1.1, AutomatonSimulator.roundUpAngleToTapWrtInitialSetpoint(ara1, 0.2, 0.1), DOUBLE_TOLERANCE);
assertEquals(0.1, AutomatonSimulator.roundUpAngleToTapWrtInitialSetpoint(ara1, 0.2, 1.1), DOUBLE_TOLERANCE);
assertEquals(2.1, AutomatonSimulator.roundUpAngleToTapWrtInitialSetpoint(ara1, 1.2, 1.1), DOUBLE_TOLERANCE);
assertEquals(2.1, AutomatonSimulator.roundUpAngleToTapWrtInitialSetpoint(ara1, 2.1, 2.1), DOUBLE_TOLERANCE);
assertEquals(3.1, AutomatonSimulator.roundUpAngleToTapWrtInitialSetpoint(ara1, 3.1, 2.1), DOUBLE_TOLERANCE);
assertEquals(-3.1, AutomatonSimulator.roundUpAngleToTapWrtInitialSetpoint(ara1, -3.1, 2.1), DOUBLE_TOLERANCE);
}
@Test
void testComputeOptimalSetpoint() {
double optimalSetpoint;
// limit by min
optimalSetpoint = automatonSimulator.computeOptimalSetpoint(0.1, 1100., -100., 1, ara1, -3.1, 3.1);
assertEquals(-3.1, optimalSetpoint, DOUBLE_TOLERANCE);
// limit by max
optimalSetpoint = automatonSimulator.computeOptimalSetpoint(0.1, 1100., -100., -1, ara1, -3.1, 3.1);
assertEquals(3.1, optimalSetpoint, DOUBLE_TOLERANCE);
// Reduce flow from 1100 to 1000 with one setpoint change, sensi > 0
optimalSetpoint = automatonSimulator.computeOptimalSetpoint(0.1, 1100., -100., 100, ara1, -3.1, 3.1);
assertEquals(-1.1, optimalSetpoint, DOUBLE_TOLERANCE);
// Reduce flow from 1100 to 1000 with two setpoint changes, sensi > 0
optimalSetpoint = automatonSimulator.computeOptimalSetpoint(0.1, 1100., -100., 50, ara1, -3.1, 3.1);
assertEquals(-2.1, optimalSetpoint, DOUBLE_TOLERANCE);
// Increase flow from -1100 to -1000 with two setpoint changes, sensi > 0
optimalSetpoint = automatonSimulator.computeOptimalSetpoint(0.1, -1100., -100., 50, ara1, -3.1, 3.1);
assertEquals(2.1, optimalSetpoint, DOUBLE_TOLERANCE);
// Reduce flow from 1100 to 1000 with two setpoint changes, sensi < 0
optimalSetpoint = automatonSimulator.computeOptimalSetpoint(0.1, 1100., -100., -50, ara1, -3.1, 3.1);
assertEquals(2.1, optimalSetpoint, DOUBLE_TOLERANCE);
// Increase flow from -1100 to -1000 with two setpoint changes, sensi < 0
optimalSetpoint = automatonSimulator.computeOptimalSetpoint(0.1, -1100., -100., -50, ara1, -3.1, 3.1);
assertEquals(-2.1, optimalSetpoint, DOUBLE_TOLERANCE);
}
@Test
void testShiftRangeActionsUntilFlowCnecsSecureCase1() {
FlowCnec cnec = mock(FlowCnec.class);
when(cnec.getMonitoredSides()).thenReturn(Set.of(TwoSides.TWO));
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// suppose threshold is -1000, flow is -1100 then -1010 then -1000
// getFlow is called once in every iteration
when(mockedPrePerimeterResult.getFlow(cnec, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(-1100., -1010., -1000.);
// getMargin is called once before loop, once in 1st iteration, once in second iteration
when(mockedPrePerimeterResult.getMargin(cnec, Unit.MEGAWATT)).thenReturn(-100., -10., 0.);
// getMargin with side is called once before loop, once in 1st iteration, once in second iteration
when(mockedPrePerimeterResult.getMargin(cnec, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(-100., -100., -10., -10., 0.);
// suppose approx sensi is +50 on both RAs first, then +5 (so +100 then +10 total)
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.TWO, ara1, Unit.MEGAWATT)).thenReturn(50., 5.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.TWO, ara2, Unit.MEGAWATT)).thenReturn(50., 5.);
// so PSTs should be shifted to setpoint +1.1 on first iteration, then +3.1 on second because of under-estimator
AutomatonSimulator.RangeAutomatonSimulationResult shiftResult =
automatonSimulator.shiftRangeActionsUntilFlowCnecsSecure(List.of(ara1, ara2), Set.of(cnec), network, mockedPreAutoPerimeterSensitivityAnalysis, mockedPrePerimeterResult, autoState);
assertEquals(3.1, shiftResult.rangeActionsWithSetpoint().get(ara1), DOUBLE_TOLERANCE);
assertEquals(3.1, shiftResult.rangeActionsWithSetpoint().get(ara2), DOUBLE_TOLERANCE);
}
@Test
void testShiftRangeActionsUntilFlowCnecsSecureCase2() {
FlowCnec cnec = mock(FlowCnec.class);
when(cnec.getMonitoredSides()).thenReturn(Set.of(TwoSides.ONE, TwoSides.TWO));
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// same as case 1 but flows & sensi are inverted -> setpoints should be the same
// getFlow is called once in every iteration
when(mockedPrePerimeterResult.getFlow(cnec, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(1100., 1010., 1000.);
when(mockedPrePerimeterResult.getFlow(cnec, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(0., 0., 0.);
// getMargin is called once before loop, once in 1st iteration, once in second iteration
when(mockedPrePerimeterResult.getMargin(cnec, Unit.MEGAWATT)).thenReturn(-100., -10., 0.);
// getMargin with side is called once before loop, once in 1st iteration, once in second iteration
when(mockedPrePerimeterResult.getMargin(cnec, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(-100., -100., -10., -10., 0.);
when(mockedPrePerimeterResult.getMargin(cnec, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(100.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.ONE, ara1, Unit.MEGAWATT)).thenReturn(-50., -5.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.ONE, ara2, Unit.MEGAWATT)).thenReturn(-50., -5.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.TWO, ara1, Unit.MEGAWATT)).thenReturn(-50., -5.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.TWO, ara2, Unit.MEGAWATT)).thenReturn(-50., -5.);
AutomatonSimulator.RangeAutomatonSimulationResult shiftResult =
automatonSimulator.shiftRangeActionsUntilFlowCnecsSecure(List.of(ara1, ara2), Set.of(cnec), network, mockedPreAutoPerimeterSensitivityAnalysis, mockedPrePerimeterResult, autoState);
assertEquals(3.1, shiftResult.rangeActionsWithSetpoint().get(ara1), DOUBLE_TOLERANCE);
assertEquals(3.1, shiftResult.rangeActionsWithSetpoint().get(ara2), DOUBLE_TOLERANCE);
}
@Test
void testShiftRangeActionsUntilFlowCnecsSecureCase3() {
FlowCnec cnec = mock(FlowCnec.class);
when(cnec.getMonitoredSides()).thenReturn(Set.of(TwoSides.ONE));
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// same as case 1 but flows are inverted -> setpoints should be inverted
// getFlow is called once in every iteration
when(mockedPrePerimeterResult.getFlow(cnec, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(1100., 1010., 1000.);
// getMargin is called once before loop, once in second iteration
when(mockedPrePerimeterResult.getMargin(cnec, Unit.MEGAWATT)).thenReturn(-100., -10., 0.);
// getMargin with side is called once before loop, once in 1st iteration, once in second iteration
when(mockedPrePerimeterResult.getMargin(cnec, TwoSides.ONE, Unit.MEGAWATT)).thenReturn(-100., -100., -10., -10., 0.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.ONE, ara1, Unit.MEGAWATT)).thenReturn(50., 5.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.ONE, ara2, Unit.MEGAWATT)).thenReturn(50., 5.);
AutomatonSimulator.RangeAutomatonSimulationResult shiftResult =
automatonSimulator.shiftRangeActionsUntilFlowCnecsSecure(List.of(ara1, ara2), Set.of(cnec), network, mockedPreAutoPerimeterSensitivityAnalysis, mockedPrePerimeterResult, autoState);
assertEquals(-3.1, shiftResult.rangeActionsWithSetpoint().get(ara1), DOUBLE_TOLERANCE);
assertEquals(-3.1, shiftResult.rangeActionsWithSetpoint().get(ara2), DOUBLE_TOLERANCE);
}
@Test
void testShiftRangeActionsUntilFlowCnecsSecureCase4() {
FlowCnec cnec = mock(FlowCnec.class);
when(cnec.getMonitoredSides()).thenReturn(Set.of(TwoSides.TWO));
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// same as case 1 but sensi are inverted -> setpoints should be inverted
// + added a cnec with sensi = 0
// getFlow is called once in every iteration
when(mockedPrePerimeterResult.getFlow(cnec, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(-1100., -1010., -1000.);
// getMargin is called once before loop, once in 1st iteration when most limiting cnec is different, once in second iteration, once in third iteration
when(mockedPrePerimeterResult.getMargin(cnec, Unit.MEGAWATT)).thenReturn(-100., -100., -100., -10., 0.);
// getMargin with side is called once before loop, once in 1st iteration when most limiting cnec is different, once in second iteration, once in third iteration
when(mockedPrePerimeterResult.getMargin(cnec, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(-100., -100., -100., -100., -10., -10., 0.);
// computeMargin is called once in 1st iteration, once in second iteration, once in third iteration
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.TWO, ara1, Unit.MEGAWATT)).thenReturn(-50., -5.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec, TwoSides.TWO, ara2, Unit.MEGAWATT)).thenReturn(-50., -5.);
FlowCnec cnecNullSensi = mock(FlowCnec.class);
when(cnecNullSensi.getMonitoredSides()).thenReturn(Set.of(TwoSides.TWO));
when(mockedPrePerimeterResult.getFlow(cnecNullSensi, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(2200.);
when(mockedPrePerimeterResult.getMargin(cnecNullSensi, Unit.MEGAWATT)).thenReturn(-200.);
when(mockedPrePerimeterResult.getMargin(cnecNullSensi, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(-200.);
when(mockedPrePerimeterResult.getSensitivityValue(cnecNullSensi, TwoSides.TWO, ara1, Unit.MEGAWATT)).thenReturn(0.);
when(mockedPrePerimeterResult.getSensitivityValue(cnecNullSensi, TwoSides.TWO, ara2, Unit.MEGAWATT)).thenReturn(0.);
AutomatonSimulator.RangeAutomatonSimulationResult shiftResult =
automatonSimulator.shiftRangeActionsUntilFlowCnecsSecure(List.of(ara1, ara2), Set.of(cnec, cnecNullSensi), network, mockedPreAutoPerimeterSensitivityAnalysis, mockedPrePerimeterResult, autoState);
assertEquals(-3.1, shiftResult.rangeActionsWithSetpoint().get(ara1), DOUBLE_TOLERANCE);
assertEquals(-3.1, shiftResult.rangeActionsWithSetpoint().get(ara2), DOUBLE_TOLERANCE);
}
@Test
void testSimulateRangeAutomatons() {
State curativeState = mock(State.class);
when(curativeState.getContingency()).thenReturn(Optional.of(crac.getContingency("contingency1")));
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// only keep ara1 & ara2
Set<String> toRemove = crac.getRangeActions().stream().map(Identifiable::getId).collect(Collectors.toSet());
toRemove.remove("ara1");
toRemove.remove("ara2");
toRemove.forEach(ra -> crac.removeRemedialAction(ra));
when(mockedPrePerimeterResult.getFlow(cnec1, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(1100.);
when(mockedPrePerimeterResult.getMargin(cnec1, Unit.MEGAWATT)).thenReturn(-100.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec1, TwoSides.TWO, ara1, Unit.MEGAWATT)).thenReturn(0.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec1, TwoSides.TWO, ara2, Unit.MEGAWATT)).thenReturn(0.);
AutomatonSimulator.RangeAutomatonSimulationResult result = automatonSimulator.simulateRangeAutomatons(autoState, Set.of(curativeState), network, mockedPreAutoPerimeterSensitivityAnalysis, mockedPrePerimeterResult, 3, Set.of(), Map.of(ara1, 0.1, ara2, 0.1), Map.of(ara1, 0.1, ara2, 0.1));
assertNotNull(result);
assertNotNull(result.perimeterResult());
assertNotNull(result.activatedRangeActions());
assertTrue(result.activatedRangeActions().isEmpty());
assertEquals(Map.of(ara1, 0.1, ara2, 0.1), result.rangeActionsWithSetpoint());
}
@Test
void testSimulateTopologicalAutomatons() {
String initialVariantId = network.getVariantManager().getWorkingVariantId();
String workingVariantId = "workingVariant";
// margin < 0 => activate NA
when(mockedPrePerimeterResult.getMargin(cnec2, Unit.MEGAWATT)).thenReturn(-100.);
network.getVariantManager().cloneVariant(initialVariantId, workingVariantId);
network.getVariantManager().setWorkingVariant(workingVariantId);
AutomatonSimulator.TopoAutomatonSimulationResult result = automatonSimulator.simulateTopologicalAutomatons(autoState, network, mockedPreAutoPerimeterSensitivityAnalysis, 0, Set.of(), mockedPrePerimeterResult);
assertNotNull(result);
assertNotNull(result.perimeterResult());
assertEquals(Set.of(na), result.activatedNetworkActions());
// NA already activated (stay on same variant), do not activate NA
when(mockedPrePerimeterResult.getMargin(cnec2, Unit.MEGAWATT)).thenReturn(-100.);
result = automatonSimulator.simulateTopologicalAutomatons(autoState, network, mockedPreAutoPerimeterSensitivityAnalysis, 0, Set.of(), mockedPrePerimeterResult);
assertNotNull(result);
assertNotNull(result.perimeterResult());
assertEquals(Set.of(), result.activatedNetworkActions());
// margin = 0 => activate NA
when(mockedPrePerimeterResult.getMargin(cnec2, Unit.MEGAWATT)).thenReturn(0.);
network.getVariantManager().cloneVariant(initialVariantId, workingVariantId, true);
network.getVariantManager().setWorkingVariant(workingVariantId);
result = automatonSimulator.simulateTopologicalAutomatons(autoState, network, mockedPreAutoPerimeterSensitivityAnalysis, 0, Set.of(), mockedPrePerimeterResult);
assertNotNull(result);
assertNotNull(result.perimeterResult());
assertEquals(Set.of(na), result.activatedNetworkActions());
// margin > 0 => do not activate NA
when(mockedPrePerimeterResult.getMargin(cnec2, Unit.MEGAWATT)).thenReturn(1.);
network.getVariantManager().cloneVariant(initialVariantId, workingVariantId, true);
network.getVariantManager().setWorkingVariant(workingVariantId);
result = automatonSimulator.simulateTopologicalAutomatons(autoState, network, mockedPreAutoPerimeterSensitivityAnalysis, 0, Set.of(), mockedPrePerimeterResult);
assertNotNull(result);
assertNotNull(result.perimeterResult());
assertEquals(Set.of(), result.activatedNetworkActions());
}
@Test
void testSimulateTopologicalAutomatonsFailure() {
when(mockedPrePerimeterResult.getSensitivityStatus()).thenReturn(ComputationStatus.FAILURE);
AutomatonSimulator.TopoAutomatonSimulationResult result = automatonSimulator.simulateTopologicalAutomatons(autoState, network, mockedPreAutoPerimeterSensitivityAnalysis, 0, Set.of(), mockedPrePerimeterResult);
assertNotNull(result);
assertEquals(ComputationStatus.FAILURE, result.perimeterResult().getSensitivityStatus());
}
@Test
void testSimulateRangeAutomatonsFailure() {
when(mockedPrePerimeterResult.getSensitivityStatus()).thenReturn(ComputationStatus.FAILURE);
AutomatonSimulator.RangeAutomatonSimulationResult result = automatonSimulator.simulateRangeAutomatons(autoState, Set.of(), network, mockedPreAutoPerimeterSensitivityAnalysis, mockedPrePerimeterResult, 3, Set.of(), Map.of(), Map.of());
assertNotNull(result);
assertEquals(ComputationStatus.FAILURE, result.perimeterResult().getSensitivityStatus());
}
@Test
void testSimulateAutomatonState() {
State curativeState = mock(State.class);
Instant curativeInstant = Mockito.mock(Instant.class);
when(curativeState.getInstant()).thenReturn(curativeInstant);
when(curativeState.getContingency()).thenReturn(Optional.of(crac.getContingency("contingency1")));
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// only keep ara1, ara2 & na
Set<String> toRemove = crac.getRemedialActions().stream().map(Identifiable::getId).collect(Collectors.toSet());
toRemove.remove("ara1");
toRemove.remove("ara2");
toRemove.remove("na");
toRemove.forEach(ra -> crac.removeRemedialAction(ra));
when(mockedPrePerimeterResult.getMargin(cnec2, Unit.MEGAWATT)).thenReturn(100.);
when(mockedPrePerimeterResult.getFlow(cnec1, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(1100.);
when(mockedPrePerimeterResult.getMargin(cnec1, Unit.MEGAWATT)).thenReturn(-100.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec1, TwoSides.TWO, ara1, Unit.MEGAWATT)).thenReturn(0.);
when(mockedPrePerimeterResult.getSensitivityValue(cnec1, TwoSides.TWO, ara2, Unit.MEGAWATT)).thenReturn(0.);
RangeActionSetpointResult rangeActionSetpointResult = Mockito.mock(RangeActionSetpointResult.class);
when(mockedPrePerimeterResult.getRangeActionSetpointResult()).thenReturn(rangeActionSetpointResult);
when(rangeActionSetpointResult.getRangeActions()).thenReturn(Set.of(ara1, ara2));
when(rangeActionSetpointResult.getSetpoint(ara1)).thenReturn(0.1);
when(rangeActionSetpointResult.getSetpoint(ara2)).thenReturn(0.1);
when(mockedPrePerimeterResult.getSensitivityStatus(autoState)).thenReturn(ComputationStatus.DEFAULT);
SensitivityResult mockedSensitivityResult = mock(SensitivityResult.class);
when(mockedPrePerimeterResult.getSensitivityResult()).thenReturn(mockedSensitivityResult);
AutomatonPerimeterResultImpl result = automatonSimulator.simulateAutomatonState(autoState, Set.of(curativeState), network);
assertNotNull(result);
assertEquals(Set.of(), result.getActivatedNetworkActions());
assertEquals(Set.of(), result.getActivatedRangeActions(autoState));
assertEquals(Map.of(ara1, 0.1, ara2, 0.1), result.getOptimizedSetpointsOnState(autoState));
}
@Test
void testSimulateAutomatonStateFailure() {
when(mockedPrePerimeterResult.getSensitivityStatus(autoState)).thenReturn(ComputationStatus.FAILURE);
when(mockedPrePerimeterResult.getSensitivityStatus()).thenReturn(ComputationStatus.FAILURE);
State curativeState = mock(State.class);
Instant curativeInstant = Mockito.mock(Instant.class);
when(curativeState.getInstant()).thenReturn(curativeInstant);
when(curativeState.getContingency()).thenReturn(Optional.of(crac.getContingency("contingency1")));
AutomatonPerimeterResultImpl result = automatonSimulator.simulateAutomatonState(autoState, Set.of(curativeState), network);
assertNotNull(result);
assertEquals(ComputationStatus.FAILURE, result.getComputationStatus());
assertEquals(Set.of(), result.getActivatedRangeActions(autoState));
assertEquals(Set.of(), result.getActivatedNetworkActions());
}
@Test
void testDisableHvdcAngleDroopControlBeforeShifting() {
PrePerimeterResult prePerimeterResult = mock(PrePerimeterResult.class);
when(mockedPreAutoPerimeterSensitivityAnalysis.runBasedOnInitialResults(any(), any(), any(), any(), any())).thenReturn(mockedPrePerimeterResult);
// check that angle-droop control was not disabled when margins are positive
when(prePerimeterResult.getMargin(cnec1, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(0.);
when(prePerimeterResult.getMargin(cnec2, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(100.);
automatonSimulator.shiftRangeActionsUntilFlowCnecsSecure(List.of(hvdcRa1, hvdcRa2), Set.of(cnec1, cnec2), network, mockedPreAutoPerimeterSensitivityAnalysis, prePerimeterResult, autoState);
assertTrue(network.getHvdcLine("BBE2AA11 FFR3AA11 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
assertTrue(network.getHvdcLine("BBE2AA12 FFR3AA12 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
// check that angle-droop control is disabled when one margin is negative
when(prePerimeterResult.getMargin(cnec1, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(-1.);
when(prePerimeterResult.getMargin(cnec2, TwoSides.TWO, Unit.MEGAWATT)).thenReturn(100.);
automatonSimulator.shiftRangeActionsUntilFlowCnecsSecure(List.of(hvdcRa1, hvdcRa2), Set.of(cnec1, cnec2), network, mockedPreAutoPerimeterSensitivityAnalysis, prePerimeterResult, autoState);
assertFalse(network.getHvdcLine("BBE2AA11 FFR3AA11 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
assertFalse(network.getHvdcLine("BBE2AA12 FFR3AA12 1").getExtension(HvdcAngleDroopActivePowerControl.class).isEnabled());
assertEquals(2451.3764524964786, network.getHvdcLine("BBE2AA11 FFR3AA11 1").getActivePowerSetpoint(), DOUBLE_TOLERANCE);
assertEquals(HvdcLine.ConvertersMode.SIDE_1_RECTIFIER_SIDE_2_INVERTER, network.getHvdcLine("BBE2AA11 FFR3AA11 1").getConvertersMode());
assertEquals(45.72718047413281, network.getHvdcLine("BBE2AA12 FFR3AA12 1").getActivePowerSetpoint(), DOUBLE_TOLERANCE);
assertEquals(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER, network.getHvdcLine("BBE2AA12 FFR3AA12 1").getConvertersMode());
}
}