VoltageCnecImplTest.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.data.crac.impl;

import com.powsybl.iidm.network.*;
import com.powsybl.openrao.commons.Unit;
import com.powsybl.openrao.data.crac.impl.utils.NetworkImportsUtil;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.crac.api.InstantKind;
import com.powsybl.openrao.data.crac.api.cnec.Cnec;
import com.powsybl.openrao.data.crac.api.cnec.VoltageCnec;
import com.powsybl.openrao.data.crac.api.cnec.VoltageCnecAdder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.Optional;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author Philippe Edwards {@literal <philippe.edwards at rte-france.com>}
 */
class VoltageCnecImplTest {
    private static final String PREVENTIVE_INSTANT_ID = "preventive";
    private static final double DOUBLE_TOLERANCE = 1e-3;

    private Crac crac;

    @BeforeEach
    public void setUp() {
        crac = new CracImplFactory().create("cracId")
            .newInstant(PREVENTIVE_INSTANT_ID, InstantKind.PREVENTIVE);
    }

    private VoltageCnecAdder initPreventiveCnecAdder() {
        return crac.newVoltageCnec()
            .withId("voltage-cnec")
            .withName("voltage-cnec-name")
            .withNetworkElement("networkElement")
            .withOperator("FR")
            .withInstant(PREVENTIVE_INSTANT_ID)
            .withOptimized(false);
    }

    @Test
    void testGetLocation1() {

        Network network = NetworkImportsUtil.import12NodesNetwork();

        VoltageCnec cnec1 = crac.newVoltageCnec()
            .withId("cnec-1-id")
            .withNetworkElement("BBE1AA1")
            .withInstant(PREVENTIVE_INSTANT_ID)
            .newThreshold().withUnit(Unit.KILOVOLT).withMax(1000.).add()
            .add();

        VoltageCnec cnec2 = crac.newVoltageCnec()
            .withId("cnec-2-id")
            .withNetworkElement("DDE2AA1")
            .withInstant(PREVENTIVE_INSTANT_ID)
            .newThreshold().withUnit(Unit.KILOVOLT).withMax(1000.).add()
            .add();

        Set<Optional<Country>> countries = cnec1.getLocation(network);
        assertEquals(1, countries.size());
        assertTrue(countries.contains(Optional.of(Country.BE)));

        countries = cnec2.getLocation(network);
        assertEquals(1, countries.size());
        assertTrue(countries.contains(Optional.of(Country.DE)));
    }

    @Test
    void testComputeValue() {
        VoltageCnec cnec = initPreventiveCnecAdder()
            .newThreshold().withUnit(Unit.KILOVOLT).withMin(200.).withMax(500.).add()
            .add();
        Network networkMock1 = mockBusVoltagesInNetwork("networkElement", 400.);
        assertEquals(400., ((VoltageCnecValue) cnec.computeValue(networkMock1, Unit.KILOVOLT)).minValue(), DOUBLE_TOLERANCE);
        assertEquals(400., ((VoltageCnecValue) cnec.computeValue(networkMock1, Unit.KILOVOLT)).maxValue(), DOUBLE_TOLERANCE);
    }

    @Test
    void testComputeSecurityStatus() {
        VoltageCnec cnec = initPreventiveCnecAdder()
            .newThreshold().withUnit(Unit.KILOVOLT).withMin(200.).withMax(500.).add()
            .add();
        Network networkMock1 = mockBusVoltagesInNetwork("networkElement", 400.);
        Network networkMock2 = mockBusVoltagesInNetwork("networkElement", 700.);
        Network networkMock3 = mockBusVoltagesInNetwork("networkElement", 100.);

        assertEquals(Cnec.SecurityStatus.SECURE, cnec.computeSecurityStatus(networkMock1, Unit.KILOVOLT));
        assertEquals(Cnec.SecurityStatus.HIGH_CONSTRAINT, cnec.computeSecurityStatus(networkMock2, Unit.KILOVOLT));
        assertEquals(Cnec.SecurityStatus.LOW_CONSTRAINT, cnec.computeSecurityStatus(networkMock3, Unit.KILOVOLT));
    }

    @Test
    void testVoltageCnecWithOneMaxThreshold() {

        VoltageCnec cnec = initPreventiveCnecAdder()
            .newThreshold().withUnit(Unit.KILOVOLT).withMax(500.).add()
            .add();

        // bounds
        assertEquals(500., cnec.getUpperBound(Unit.KILOVOLT).orElseThrow(), DOUBLE_TOLERANCE);
        assertFalse(cnec.getLowerBound(Unit.KILOVOLT).isPresent());

        // margin
        Network networkMock1 = mockBusVoltagesInNetwork("networkElement", 400.);
        assertEquals(100., cnec.computeMargin(networkMock1, Unit.KILOVOLT), DOUBLE_TOLERANCE); // bound: 500 MW

        Network networkMock2 = mockBusVoltagesInNetwork("networkElement", -300.);
        assertEquals(800., cnec.computeMargin(networkMock2, Unit.KILOVOLT), DOUBLE_TOLERANCE); // bound: 760 A
    }

    @Test
    void testVoltageCnecWithSeveralThresholds() {
        VoltageCnec cnec = initPreventiveCnecAdder()
            .newThreshold().withUnit(Unit.KILOVOLT).withMax(100.).add()
            .newThreshold().withUnit(Unit.KILOVOLT).withMin(-200.).add()
            .newThreshold().withUnit(Unit.KILOVOLT).withMax(500.).add()
            .newThreshold().withUnit(Unit.KILOVOLT).withMin(-300.).add()
            .newThreshold().withUnit(Unit.KILOVOLT).withMin(-50.).withMax(150.).add()
            .add();

        assertEquals(100., cnec.getUpperBound(Unit.KILOVOLT).orElseThrow(), DOUBLE_TOLERANCE);
        assertEquals(-50., cnec.getLowerBound(Unit.KILOVOLT).orElseThrow(), DOUBLE_TOLERANCE);

        Network networkMock1 = mockBusVoltagesInNetwork("networkElement", 300.);
        assertEquals(-200., cnec.computeMargin(networkMock1, Unit.KILOVOLT), DOUBLE_TOLERANCE);

        Network networkMock2 = mockBusVoltagesInNetwork("networkElement", -200.);
        assertEquals(-150., cnec.computeMargin(networkMock2, Unit.KILOVOLT), DOUBLE_TOLERANCE);
    }

    @Test
    void marginsWithNegativeAndPositiveLimits() {

        VoltageCnec cnec = initPreventiveCnecAdder()
            .newThreshold().withUnit(Unit.KILOVOLT).withMin(-200.).withMax(500.).add()
            .add();

        Network networkMock1 = mockBusVoltagesInNetwork("networkElement", -300.);
        assertEquals(-100, cnec.computeMargin(networkMock1, Unit.KILOVOLT), DOUBLE_TOLERANCE);

        Network networkMock2 = mockBusVoltagesInNetwork("networkElement", 0.);
        assertEquals(200, cnec.computeMargin(networkMock2, Unit.KILOVOLT), DOUBLE_TOLERANCE);

        Network networkMock3 = mockBusVoltagesInNetwork("networkElement", 400.);
        assertEquals(100, cnec.computeMargin(networkMock3, Unit.KILOVOLT), DOUBLE_TOLERANCE);

        Network networkMock4 = mockBusVoltagesInNetwork("networkElement", 800.);
        assertEquals(-300, cnec.computeMargin(networkMock4, Unit.KILOVOLT), DOUBLE_TOLERANCE);
    }

    // other

    @Test
    void testEqualsAndHashCode() {
        VoltageCnec cnec1 = initPreventiveCnecAdder().newThreshold().withUnit(Unit.KILOVOLT).withMax(1000.).add().add();
        VoltageCnec cnec2 = initPreventiveCnecAdder().withId("anotherId").newThreshold().withUnit(Unit.KILOVOLT).withMin(-1000.).add().add();

        assertEquals(cnec1, cnec1);
        assertNotEquals(cnec1, cnec2);
        assertNotNull(cnec1);
        assertNotEquals(1, cnec1);

        assertEquals(cnec1.hashCode(), cnec1.hashCode());
        assertNotEquals(cnec1.hashCode(), cnec2.hashCode());
    }

    private static Network mockBusVoltagesInNetwork(String elementId, double voltage) {
        Network network = Mockito.mock(Network.class);
        VoltageLevel voltageLevel = Mockito.mock(VoltageLevel.class);
        Mockito.when(network.getVoltageLevel(elementId)).thenReturn(voltageLevel);
        BusbarSection busbarSection = Mockito.mock(BusbarSection.class);
        Mockito.when(network.getBusbarSection(elementId)).thenReturn(busbarSection);
        Mockito.when(busbarSection.getV()).thenReturn(voltage);
        return network;
    }
}