FlowCnecAdderImplTest.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.data.crac.impl;
import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.contingency.Contingency;
import com.powsybl.openrao.data.crac.api.Instant;
import com.powsybl.openrao.data.crac.api.InstantKind;
import com.powsybl.openrao.data.crac.api.cnec.FlowCnec;
import com.powsybl.openrao.data.crac.api.cnec.FlowCnecAdder;
import com.powsybl.openrao.data.crac.api.threshold.BranchThresholdAdder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Optional;
import static com.powsybl.iidm.network.TwoSides.ONE;
import static com.powsybl.iidm.network.TwoSides.TWO;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author Peter Mitri {@literal <peter.mitri at rte-france.com>}
*/
class FlowCnecAdderImplTest {
private static final String PREVENTIVE_INSTANT_ID = "preventive";
private static final String OUTAGE_INSTANT_ID = "outage";
private static final String AUTO_INSTANT_ID = "auto";
private static final String CURATIVE_INSTANT_ID = "curative";
private static final double DOUBLE_TOLERANCE = 1e-6;
private CracImpl crac;
private final String contingency1Id = "condId1";
private Contingency contingency1;
private Instant preventiveInstant;
private Instant outageInstant;
@BeforeEach
public void setUp() {
crac = new CracImpl("test-crac")
.newInstant(PREVENTIVE_INSTANT_ID, InstantKind.PREVENTIVE)
.newInstant(OUTAGE_INSTANT_ID, InstantKind.OUTAGE)
.newInstant(AUTO_INSTANT_ID, InstantKind.AUTO)
.newInstant(CURATIVE_INSTANT_ID, InstantKind.CURATIVE);
preventiveInstant = crac.getInstant(PREVENTIVE_INSTANT_ID);
outageInstant = crac.getInstant(OUTAGE_INSTANT_ID);
contingency1 = crac.newContingency().withId(contingency1Id).add();
}
@Test
void testAdd() {
FlowCnec cnec1 = crac.newFlowCnec()
.withId("cnecId1")
.withName("cnecName1")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withOperator("cnec1Operator")
.withNetworkElement("neId1", "neName1")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(1000.0).withMin(-1000.0).add()
.add();
FlowCnec cnec2 = crac.newFlowCnec()
.withId("cnecId2")
.withInstant(PREVENTIVE_INSTANT_ID)
.withOperator("cnec2Operator")
.withNetworkElement("neId2")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(500.0).add()
.add();
assertEquals(2, crac.getFlowCnecs().size());
// Verify 1st cnec content
assertEquals(cnec1, crac.getFlowCnec("cnecId1"));
assertEquals("cnecName1", cnec1.getName());
assertEquals(contingency1, cnec1.getState().getContingency().orElseThrow());
assertEquals(outageInstant, cnec1.getState().getInstant());
assertEquals("cnec1Operator", cnec1.getOperator());
assertEquals("neName1", cnec1.getNetworkElement().getName());
assertEquals(1000.0, cnec1.getUpperBound(ONE, Unit.MEGAWATT).orElseThrow(), DOUBLE_TOLERANCE);
assertEquals(-1000.0, cnec1.getLowerBound(ONE, Unit.MEGAWATT).orElseThrow(), DOUBLE_TOLERANCE);
// Verify 2nd cnec content
assertEquals(cnec2, crac.getFlowCnec("cnecId2"));
assertEquals("cnecId2", cnec2.getName());
assertEquals(preventiveInstant, cnec2.getState().getInstant());
assertEquals("cnec2Operator", cnec2.getOperator());
assertEquals(Optional.empty(), cnec2.getState().getContingency());
assertEquals("neId2", cnec2.getNetworkElement().getName());
assertEquals(500.0, cnec2.getUpperBound(ONE, Unit.MEGAWATT).orElseThrow(), DOUBLE_TOLERANCE);
assertFalse(cnec2.getLowerBound(ONE, Unit.MEGAWATT).isPresent());
// Verify that network elements were created
crac.newFlowCnec()
.withId("cnecId3")
.withInstant(PREVENTIVE_INSTANT_ID)
.withOperator("cnec2Operator")
.withNetworkElement("neId2") // same as cnec2
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(500.0).add()
.add();
assertEquals(2, crac.getNetworkElements().size());
assertNotNull(crac.getNetworkElement("neId1"));
assertNotNull(crac.getNetworkElement("neId2"));
// Verify states were created
assertEquals(2, crac.getStates().size());
assertNotNull(crac.getPreventiveState());
assertNotNull(crac.getState(contingency1Id, outageInstant));
}
@Test
void testFrmHandling() {
double maxValueInMw = 100.0;
double frmInMw = 5.0;
FlowCnec cnec = crac.newFlowCnec().withId("Cnec ID")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(maxValueInMw).withMin(-maxValueInMw).add()
.withReliabilityMargin(frmInMw)
.add();
assertEquals(maxValueInMw - frmInMw, cnec.getUpperBound(ONE, Unit.MEGAWATT).orElseThrow(OpenRaoException::new), 0.0);
assertEquals(frmInMw - maxValueInMw, cnec.getLowerBound(ONE, Unit.MEGAWATT).orElseThrow(OpenRaoException::new), 0.0);
}
@Test
void testNotOptimizedMonitored() {
FlowCnec cnec = crac.newFlowCnec().withId("Cnec ID")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withMonitored()
.add();
assertFalse(cnec.isOptimized());
assertTrue(cnec.isMonitored());
}
@Test
void testOptimizedNotMonitored() {
FlowCnec cnec = crac.newFlowCnec().withId("Cnec ID")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withOptimized()
.add();
assertTrue(cnec.isOptimized());
assertFalse(cnec.isMonitored());
}
@Test
void testNotOptimizedNotMonitored() {
FlowCnec cnec = crac.newFlowCnec().withId("Cnec ID")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.PERCENT_IMAX).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withNominalVoltage(380.)
.withIMax(1200., ONE)
.add();
assertFalse(cnec.isOptimized());
assertFalse(cnec.isMonitored());
}
@Test
void testNotOptimizedNotMonitored2() {
FlowCnec cnec = crac.newFlowCnec().withId("Cnec ID")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.AMPERE).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withOptimized(false)
.withMonitored(false)
.withNominalVoltage(380.)
.add();
assertFalse(cnec.isOptimized());
assertFalse(cnec.isMonitored());
}
@Test
void checkThresholdSideInitialisation1() {
FlowCnec cnec = crac.newFlowCnec().withId("Cnec ID")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.PERCENT_IMAX).withSide(ONE).withMax(1.).withMin(-100.0).add()
.newThreshold().withUnit(Unit.AMPERE).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.newThreshold().withUnit(Unit.AMPERE).withSide(ONE).withMax(200.0).withMin(-100.0).add()
.withOptimized(false)
.withMonitored(false)
.withNominalVoltage(380., ONE)
.withNominalVoltage(220., TWO)
.withIMax(2000.)
.add();
assertEquals(3, cnec.getThresholds().size());
assertTrue(cnec.getThresholds().stream().allMatch(th -> th.getSide().equals(ONE)));
}
@Test
void checkThresholdSideInitialisation2() {
FlowCnec cnec = crac.newFlowCnec().withId("Cnec ID")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(TWO).withMax(100.0).withMin(-200.0).add()
.newThreshold().withUnit(Unit.MEGAWATT).withSide(TWO).withMax(100.0).withMin(-100.0).add()
.newThreshold().withUnit(Unit.PERCENT_IMAX).withSide(TWO).withMax(1.).withMin(-100.0).add()
.withOptimized(false)
.withMonitored(false)
.withNominalVoltage(380., ONE)
.withNominalVoltage(220., TWO)
.withIMax(2000.)
.add();
assertEquals(3, cnec.getThresholds().size());
assertTrue(cnec.getThresholds().stream().allMatch(th -> th.getSide().equals(TWO)));
}
@Test
void testNullParentFail() {
assertThrows(NullPointerException.class, () -> new FlowCnecAdderImpl(null));
}
@Test
void testUniqueNetworkElement() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec()
.withNetworkElement("neId1", "neName1");
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> flowCnecAdder.withNetworkElement("neId2", "neName2"));
assertEquals("Cannot add multiple network elements for a flow cnec.", exception.getMessage());
}
@Test
void testNoIdFail() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec()
.withName("cnecName1")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("neId1", "neName1")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(1000.0).add()
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("Cannot add a FlowCnec object with no specified id. Please use withId()", exception.getMessage());
}
@Test
void testNoStateInstantFail() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec()
.withId("cnecId1")
.withName("cnecName1")
.withContingency(contingency1Id)
.newThreshold().withUnit(Unit.MEGAWATT).withMax(1000.0).withSide(ONE).add()
.withNetworkElement("neId1", "neName1")
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("Cannot add Cnec without a instant. Please use withInstant() with a non null value", exception.getMessage());
}
@Test
void testNoNetworkElementFail() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec()
.withId("cnecId1")
.withName("cnecName1")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.newThreshold().withSide(ONE).withUnit(Unit.MEGAWATT).withMax(1000.0).add()
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("Cannot add Cnec without a network element. Please use withNetworkElement()", exception.getMessage());
}
@Test
void testNoThresholdFail() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec()
.withId("cnecId1")
.withName("cnecName1")
.withInstant(OUTAGE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("neId1", "neName1")
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("Cannot add a cnec without a threshold. Please use newThreshold", exception.getMessage());
}
@Test
void testAddTwiceError() {
crac.newFlowCnec().withId("Cnec ID")
.withInstant(PREVENTIVE_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withNominalVoltage(220)
.withIMax(2000.)
.add();
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(PREVENTIVE_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("Cannot add a cnec with an already existing ID - Cnec ID.", exception.getMessage());
}
@Test
void testAddPreventiveCnecWithContingencyError() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(PREVENTIVE_INSTANT_ID)
.withContingency(contingency1Id)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("You cannot define a contingency for a preventive cnec.", exception.getMessage());
}
@Test
void testAddOutageCnecWithNoContingencyError() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(OUTAGE_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("You must define a contingency for a non-preventive cnec.", exception.getMessage());
}
@Test
void testAddAutoCnecWithNoContingencyError() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(AUTO_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("You must define a contingency for a non-preventive cnec.", exception.getMessage());
}
@Test
void testAddCurativeCnecWithNoContingencyError() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(CURATIVE_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("You must define a contingency for a non-preventive cnec.", exception.getMessage());
}
@Test
void testAddCurativeCnecWithAbsentContingencyError() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(CURATIVE_INSTANT_ID)
.withContingency("absent-from-crac-contingency")
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.MEGAWATT).withSide(ONE).withMax(100.0).withMin(-100.0).add()
.withNominalVoltage(220)
.withIMax(2000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("Contingency absent-from-crac-contingency of Cnec Cnec ID does not exist in the crac. Use crac.newContingency() first.", exception.getMessage());
}
@Test
void testThresholdInPercentImaxButNoIMax1() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(PREVENTIVE_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.PERCENT_IMAX).withSide(ONE).withMax(1.).withMin(-1.).add()
.withNominalVoltage(220.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("iMax on left side of FlowCnec Cnec ID must be defined, as one of its threshold is on PERCENT_IMAX on the left side. Please use withIMax()", exception.getMessage());
}
@Test
void testThresholdInPercentImaxButNoIMax2() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(PREVENTIVE_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.PERCENT_IMAX).withSide(ONE).withMax(1.).withMin(-1.).add()
.withNominalVoltage(220.)
.withIMax(1000., TWO); // threshold on left side cannot be interpreted
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("iMax on left side of FlowCnec Cnec ID must be defined, as one of its threshold is on PERCENT_IMAX on the left side. Please use withIMax()", exception.getMessage());
}
@Test
void testThresholdInAmpereButNoNominalVoltage() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(PREVENTIVE_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.AMPERE).withSide(ONE).withMax(1000.).add()
.withIMax(1000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("nominal voltages on both side of FlowCnec Cnec ID must be defined, as one of its threshold is on PERCENT_IMAX or AMPERE. Please use withNominalVoltage()", exception.getMessage());
}
@Test
void testThresholdInPercentImaxButNoNominalVoltage() {
FlowCnecAdder flowCnecAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(PREVENTIVE_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold().withUnit(Unit.PERCENT_IMAX).withSide(ONE).withMax(1000.).add()
.withNominalVoltage(220., ONE) // should be defined on both side
.withIMax(1000.);
OpenRaoException exception = assertThrows(OpenRaoException.class, flowCnecAdder::add);
assertEquals("nominal voltages on both side of FlowCnec Cnec ID must be defined, as one of its threshold is on PERCENT_IMAX or AMPERE. Please use withNominalVoltage()", exception.getMessage());
}
@Test
void testThresholdWithUnitKiloVolt() {
BranchThresholdAdder branchThresholdAdder = crac.newFlowCnec().withId("Cnec ID")
.withInstant(PREVENTIVE_INSTANT_ID)
.withNetworkElement("Network Element ID")
.newThreshold();
OpenRaoException exception = assertThrows(OpenRaoException.class, () -> branchThresholdAdder.withUnit(Unit.KILOVOLT));
assertEquals("kV Unit is not suited to measure a FLOW value.", exception.getMessage());
}
}