UcteImporterTest.java

/**
 * Copyright (c) 2017, 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/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.ucte.converter;

import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.config.InMemoryPlatformConfig;
import com.powsybl.commons.datasource.ReadOnlyDataSource;
import com.powsybl.commons.datasource.ResourceDataSource;
import com.powsybl.commons.datasource.ResourceSet;
import com.powsybl.entsoe.util.EntsoeArea;
import com.powsybl.entsoe.util.EntsoeGeographicalCode;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.impl.NetworkFactoryImpl;
import com.powsybl.ucte.converter.util.UcteConverterConstants;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

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

/**
 * @author Sebastien Murgey {@literal <sebastien.murgey at rte-france.com>}
 */
class UcteImporterTest {

    @Test
    void trimIssueTest() {
        // Import network that could fail because of id conflicts due to trim mechanism
        ReadOnlyDataSource dataSource = new ResourceDataSource("importIssue", new ResourceSet("/", "importIssue.uct"));

        assertDoesNotThrow(() -> new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null));
    }

    @Test
    void countryAssociationIssueTest() {
        ReadOnlyDataSource dataSource = new ResourceDataSource("countryIssue", new ResourceSet("/", "countryIssue.uct"));

        Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);

        assertEquals(Country.ES, network.getSubstation("EHORTA").getCountry().orElse(null));
        assertEquals(1, network.getSubstation("EHORTA").getVoltageLevelStream().count());
        assertEquals(Country.BE, network.getSubstation("BHORTA").getCountry().orElse(null));
        assertEquals(1, network.getSubstation("BHORTA").getVoltageLevelStream().count());

    }

    @Test
    void germanTsosImport() {
        ReadOnlyDataSource dataSource = new ResourceDataSource("germanTsos", new ResourceSet("/", "germanTsos.uct"));

        Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);

        //Check D4 is correctly parsed
        EntsoeArea ext = network.getSubstation("D4NEUR").getExtension(EntsoeArea.class);
        assertNotNull(ext);
        assertEquals(EntsoeGeographicalCode.D4, ext.getCode());

        //Check that for other countries, no extension is added
        ext = network.getSubstation("BAVLGM").getExtension(EntsoeArea.class);
        assertNull(ext);

        //Check that for "D-nodes", no extension is added
        ext = network.getSubstation("DJA_KA").getExtension(EntsoeArea.class);
        assertNull(ext);

        //Check that for a "D-node" starting with "DE", no extension is added
        ext = network.getSubstation("DEA_KA").getExtension(EntsoeArea.class);
        assertNull(ext);

    }

    @Test
    void elementNameTest() {
        ReadOnlyDataSource dataSource = new ResourceDataSource("elementName", new ResourceSet("/", "elementName.uct"));

        Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);
        // Test Element name Line
        assertEquals("Test Line", network.getLine("F_SU1_12 F_SU2_11 1").getProperty("elementName"));
        // Test Dangling Line element name
        assertEquals("Test DL", network.getDanglingLine("XG__F_21 F_SU1_21 1").getProperty("elementName"));
        // Test Switch element name
        assertEquals("Test Coupler", network.getSwitch("F_SU1_12 F_SU1_11 1").getProperty("elementName"));
        // Test 2WT element name
        assertEquals("Test 2WT 1", network.getBranch("F_SU1_11 F_SU1_21 1").getProperty("elementName"));
        assertEquals("Test 2WT 2", network.getBranch("B_SU1_11 B_SU1_21 1").getProperty("elementName"));
        // Test tie line
        // cannot refer to side of tieline directly cause order of half lines may change
        // at import : due to HashSet iterator on dangling lines ?
        TieLine tieLine1 = network.getTieLineStream()
                .filter(line -> line.getDanglingLine1().getId().contains("XB__F_11 B_SU1_11 1") || line.getDanglingLine2().getId().contains("XB__F_11 B_SU1_11 1")).findAny().orElseThrow();
        String expectedElementName1 = tieLine1.getDanglingLine1().getId().contains("XB__F_11 B_SU1_11 1") ? "Test TL 1/2" : "Test TL 1/1";
        String expectedElementName2 = tieLine1.getDanglingLine2().getId().contains("XB__F_11 B_SU1_11 1") ? "Test TL 1/2" : "Test TL 1/1";
        assertEquals(expectedElementName1, tieLine1.getProperty("elementName_1"));
        assertEquals(expectedElementName2, tieLine1.getProperty("elementName_2"));

        TieLine tieLine2 = network.getTieLineStream()
                .filter(line -> line.getDanglingLine1().getId().contains("XB__F_21 B_SU1_21 1") || line.getDanglingLine2().getId().contains("XB__F_21 B_SU1_21 1")).findAny().orElseThrow();
        expectedElementName1 = tieLine2.getDanglingLine1().getId().contains("XB__F_21 B_SU1_21 1") ? "Test TL 2/2" : "Test TL 2/1";
        expectedElementName2 = tieLine2.getDanglingLine2().getId().contains("XB__F_21 B_SU1_21 1") ? "Test TL 2/2" : "Test TL 2/1";
        assertEquals(expectedElementName1, tieLine2.getProperty("elementName_1"));
        assertEquals(expectedElementName2, tieLine2.getProperty("elementName_2"));
    }

    @Test
    void xnodeMergingIssueTest() {
        ReadOnlyDataSource dataSource = new ResourceDataSource("mergedXnodeIssue", new ResourceSet("/", "mergedXnodeIssue.uct"));

        Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);

        assertEquals(2, network.getVoltageLevelCount());
        assertEquals(1, network.getDanglingLineStream(DanglingLineFilter.UNPAIRED).count());
        assertEquals(1, network.getTieLineCount());
        TieLine l = network.getTieLineStream().findFirst().orElseThrow(IllegalStateException::new);
        assertEquals("ESNODE11 XXNODE11 1 + FRNODE11 XXNODE11 1", l.getId());
        assertTrue(l.getDanglingLine1().getCurrentLimits().isPresent());
        assertTrue(l.getDanglingLine2().getCurrentLimits().isPresent());
    }

    @Test
    void lineAndTransformerSameId() {
        ReadOnlyDataSource dataSource = new ResourceDataSource("sameId", new ResourceSet("/", "sameId.uct"));

        Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);

        assertEquals(0, network.getLineCount());
        assertEquals(1, network.getTwoWindingsTransformerCount());
        assertEquals(1, network.getSwitchStream().count());
    }

    @Test
    void testCouplerToXnodeImport() {
        ReadOnlyDataSource dataSource = new ResourceDataSource("couplerToXnodeExample", new ResourceSet("/", "couplerToXnodeExample.uct"));
        Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);
        assertEquals(1, network.getBusBreakerView().getBusStream().count());
    }

    @Test
    void testEmptyLastCharacterOfLineImport() {
        ResourceDataSource dataSource = new ResourceDataSource("lastCharacterIssue", new ResourceSet("/", "lastCharacterIssue.uct"));
        Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);
        assertEquals(2, network.getBusBreakerView().getBusStream().count());
    }

    @Test
    void testImportLinesDifferentNominalvoltage() {
        ResourceDataSource dataSource = new ResourceDataSource("differentLinesVoltage", new ResourceSet("/", "differentLinesVoltage.uct"));

        NetworkFactory networkFactory = NetworkFactory.findDefault();
        Importer ucteImporter = new UcteImporter();
        IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> ucteImporter.importData(dataSource, networkFactory, null));
        assertTrue(e.getMessage().contains("with two different nominal voltages"));
    }

    @Test
    void testVoltageRegulatingXnode() {
        ResourceDataSource dataSource = new ResourceDataSource("frVoltageRegulatingXnode", new ResourceSet("/", "frVoltageRegulatingXnode.uct"));
        Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);
        DanglingLine dl = network.getDanglingLine("FFFFFF13 XXXXXX14 1");
        assertTrue(dl.getGeneration().isVoltageRegulationOn());
        assertEquals(409.08, dl.getGeneration().getTargetV(), 0.01);
        assertEquals(1.0, dl.getGeneration().getTargetP(), 0.01);
        assertEquals(2.0, dl.getGeneration().getMaxP(), 0.01);
        assertEquals(-2.0, dl.getGeneration().getMinP(), 0.01);
        assertEquals(1.0, dl.getGeneration().getReactiveLimits().getMaxQ(dl.getGeneration().getTargetP()), 0.01);
        assertEquals(-1.0, dl.getGeneration().getReactiveLimits().getMinQ(dl.getGeneration().getTargetP()), 0.01);
    }

    @Test
    void testXnodeTransformer() {
        ResourceDataSource dataSource = new ResourceDataSource("xNodeTransformer", new ResourceSet("/", "xNodeTransformer.uct"));
        Network network = new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null);
        assertEquals(2, network.getBusBreakerView().getBusStream().count());
    }

    @Test
    void substationNameInvariance() {
        ResourceDataSource dataSource = new ResourceDataSource("substationName", new ResourceSet("/", "substationName.uct"));

        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), null);
        assertEquals(2, network.getSubstationCount());
        assertNotNull(network.getSubstation("F1TEST"));
        assertNotNull(network.getSubstation("FTEST1"));
        assertNull(network.getSubstation("FTESTA"));
        assertNull(network.getSubstation("FTESTB"));
    }

    @Test
    void testInvalidRegulation() {
        ResourceDataSource dataSource = new ResourceDataSource("invalidRegulationNetwork", new ResourceSet("/", "invalidRegulationNetwork.uct"));
        assertDoesNotThrow(() -> new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null));
    }

    @Test
    void testInvalidVoltageReference() {
        ResourceDataSource dataSource = new ResourceDataSource("invalidVoltageReference", new ResourceSet("/", "invalidVoltageReference.uct"));
        assertDoesNotThrow(() -> new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null));
    }

    @Test
    void checkTapPositionsRangeIsExtended() {
        ResourceDataSource dataSource = new ResourceDataSource("tapPositionsRange", new ResourceSet("/", "tapPositionsRange.uct"));
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), null);
        // Ratio tap with negative tap position higher than initial tap's number
        assertEquals(-7, network.getTwoWindingsTransformer("0BBBBB5  0AAAAA2  1").getRatioTapChanger().getLowTapPosition());
        assertEquals(7, network.getTwoWindingsTransformer("0BBBBB5  0AAAAA2  1").getRatioTapChanger().getHighTapPosition());
        // Phase tap with positive tap position higher than initial tap's number
        assertEquals(-9, network.getTwoWindingsTransformer("HDDDDD2  HCCCCC1  1").getPhaseTapChanger().getLowTapPosition());
        assertEquals(9, network.getTwoWindingsTransformer("HDDDDD2  HCCCCC1  1").getPhaseTapChanger().getHighTapPosition());
        // Phase tap with negative tap position equal to initial tap's number
        assertEquals(-8, network.getTwoWindingsTransformer("ZABCD221 ZEFGH221 1").getPhaseTapChanger().getLowTapPosition());
        assertEquals(8, network.getTwoWindingsTransformer("ZABCD221 ZEFGH221 1").getPhaseTapChanger().getHighTapPosition());
    }

    @Test
    void importOfNetworkWithXnodesConnectedToOneClosedLineMustSucceed() {
        ResourceDataSource dataSource = new ResourceDataSource("xnodeOneClosedLine", new ResourceSet("/", "xnodeOneClosedLine.uct"));
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), null);
        assertNotNull(network.getDanglingLine("FFFFFF12 XXXXXX11 1"));
    }

    @Test
    void importOfNetworkWithXnodesConnectedToTwoClosedLineMustSucceed() {
        ResourceDataSource dataSource = new ResourceDataSource("xnodeTwoClosedLine", new ResourceSet("/", "xnodeTwoClosedLine.uct"));
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), null);
        assertNotNull(network.getTieLine("BEBBBB11 XXXXXX11 1 + FFFFFF12 XXXXXX11 1"));
        assertNotNull(network.getDanglingLine("FFFFFF11 XXXXXX11 1"));
        assertNotNull(network.getDanglingLine("BEBBBB12 XXXXXX11 1"));
    }

    @Test
    void importOfNetworkWithXnodesConnectedToMoreThanTwoClosedLineMustFail() {
        ResourceDataSource dataSource = new ResourceDataSource("xnodeThreeClosedLine", new ResourceSet("/", "xnodeTwoClosedLine.uct"));
        NetworkFactory networkFactory = new NetworkFactoryImpl();
        Importer ucteImporter = new UcteImporter();
        assertThrows(UcteException.class, () -> ucteImporter.importData(dataSource, networkFactory, null));
    }

    @Test
    void checkPhaseShifterRegulationMode() {
        ResourceDataSource dataSource = new ResourceDataSource("phaseShifterActivePowerOn", new ResourceSet("/", "phaseShifterActivePowerOn.uct"));
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), null);
        assertSame(PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL, network.getTwoWindingsTransformer("HDDDDD2  HCCCCC1  1").getPhaseTapChanger().getRegulationMode());
        assertSame(PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL, network.getTwoWindingsTransformer("ZABCD221 ZEFGH221 1").getPhaseTapChanger().getRegulationMode());
        assertEquals(0.0, network.getTwoWindingsTransformer("ZABCD221 ZEFGH221 1").getPhaseTapChanger().getTargetDeadband(), 1e-3);
        assertEquals(network.getTwoWindingsTransformer("ZABCD221 ZEFGH221 1").getTerminal1(), network.getTwoWindingsTransformer("ZABCD221 ZEFGH221 1").getPhaseTapChanger().getRegulationTerminal());
        assertEquals(-5.0, network.getTwoWindingsTransformer("ZABCD221 ZEFGH221 1").getPhaseTapChanger().getRegulationValue(), 1e-3);
        assertFalse(network.getTwoWindingsTransformer("ZABCD221 ZEFGH221 1").getPhaseTapChanger().isRegulating());
    }

    @Test
    void ignoreCoupler() {
        ResourceDataSource dataSource = new ResourceDataSource("ignoreCoupler", new ResourceSet("/", "ignoreCoupler.uct"));
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), null);
        assertNull(network.getSwitch("BBBBBB11 BBBBBB11 1"));
    }

    @Test
    void emptyElementName() {
        ResourceDataSource dataSource = new ResourceDataSource("emptyElementName", new ResourceSet("/", "emptyElementName.uct"));
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), null);
        Line l = network.getLine("F_SU1_12 F_SU1_11 1");
        assertNotNull(l);
        assertFalse(l.hasProperty(UcteConverterConstants.ELEMENT_NAME_PROPERTY_KEY));
    }

    @Test
    void combineRtcAndPtc() {
        ResourceDataSource dataSource = new ResourceDataSource("combineRtcAndPtc", new ResourceSet("/", "combineRtcAndPtc.uct"));
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), null);
        assertEquals(1.948, network.getTwoWindingsTransformer("BBE2AA1  BBE3AA1  1").getPhaseTapChanger().getCurrentStep().getAlpha(), 0.001);
        assertEquals(1.0, network.getTwoWindingsTransformer("BBE2AA1  BBE3AA1  1").getPhaseTapChanger().getCurrentStep().getRho(), 0.0000001);

        Properties parameters = new Properties();
        parameters.put("ucte.import.combine-phase-angle-regulation", "true");
        Network network2 = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), parameters);
        assertEquals(1.92419, network2.getTwoWindingsTransformer("BBE2AA1  BBE3AA1  1").getPhaseTapChanger().getCurrentStep().getAlpha(), 0.001);
        assertEquals(1.00000694, network2.getTwoWindingsTransformer("BBE2AA1  BBE3AA1  1").getPhaseTapChanger().getCurrentStep().getRho(), 0.0000001); // FIXME, symmetrical no impact
    }

    @Test
    void lineBetweenTwoXnodesTest() {
        ReadOnlyDataSource dataSource = new ResourceDataSource("lineBetweenTwoXnodes", new ResourceSet("/", "lineBetweenTwoXnodes.uct"));
        NetworkFactory networkFactory = NetworkFactory.findDefault();
        UcteImporter importer = new UcteImporter();
        PowsyblException e = assertThrows(PowsyblException.class, () -> importer.importData(dataSource, networkFactory, null));
        assertEquals("Line between 2 X-nodes: 'XXNODE11' and 'XXNODE12'", e.getMessage());
    }

    @Test
    void testCreateAreas() {
        ResourceDataSource dataSource = new ResourceDataSource("uxTestGridForMerging", new ResourceSet("/", "uxTestGridForMerging.uct"));
        Properties parameters = new Properties();
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), parameters);
        assertEquals(1, network.getAreaTypeCount());
        assertEquals(List.of("ControlArea"), network.getAreaTypeStream().toList());
        assertEquals(2, network.getAreaCount());
        var frArea = network.getArea("FR");
        var beArea = network.getArea("BE");
        assertNotNull(frArea);
        assertNotNull(beArea);
        assertEquals(2, frArea.getAreaBoundaryStream().count());
        assertNotNull(frArea.getAreaBoundary(network.getDanglingLine("FFFFFF11 XXXXXX11 1").getBoundary()));
        assertNotNull(frArea.getAreaBoundary(network.getDanglingLine("FFFFFF11 XXXXXX12 1").getBoundary()));
        assertEquals(2, beArea.getAreaBoundaryStream().count());
        assertNotNull(beArea.getAreaBoundary(network.getDanglingLine("BBBBBB11 XXXXXX11 1").getBoundary()));
        assertNotNull(beArea.getAreaBoundary(network.getDanglingLine("BBBBBB11 XXXXXX12 1").getBoundary()));
        frArea.getAreaBoundaries().forEach(ab -> assertTrue(ab.isAc()));
        beArea.getAreaBoundaries().forEach(ab -> assertTrue(ab.isAc()));

        assertEquals(Set.of("FFFFFF1"), frArea.getVoltageLevelStream().map(Identifiable::getId).collect(Collectors.toUnmodifiableSet()));
        assertEquals(Set.of("BBBBBB1"), beArea.getVoltageLevelStream().map(Identifiable::getId).collect(Collectors.toUnmodifiableSet()));
    }

    @Test
    void testCreateAreasDcXnode() {
        ResourceDataSource dataSource = new ResourceDataSource("uxTestGridForMerging", new ResourceSet("/", "uxTestGridForMerging.uct"));
        Properties parameters = new Properties();
        parameters.put("ucte.import.areas-dc-xnodes", "XXXXXX11");
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), parameters);
        var frArea = network.getArea("FR");
        var beArea = network.getArea("BE");
        var frDanglingLine1 = network.getDanglingLine("FFFFFF11 XXXXXX11 1");
        var frDanglingLine2 = network.getDanglingLine("FFFFFF11 XXXXXX12 1");
        var beDanglingLine1 = network.getDanglingLine("BBBBBB11 XXXXXX11 1");
        var beDanglingLine2 = network.getDanglingLine("BBBBBB11 XXXXXX12 1");
        assertFalse(frArea.getAreaBoundary(frDanglingLine1.getBoundary()).isAc());
        assertTrue(frArea.getAreaBoundary(frDanglingLine2.getBoundary()).isAc());
        assertFalse(beArea.getAreaBoundary(beDanglingLine1.getBoundary()).isAc());
        assertTrue(beArea.getAreaBoundary(beDanglingLine2.getBoundary()).isAc());
    }

    @Test
    void testDontCreateAreas() {
        ResourceDataSource dataSource = new ResourceDataSource("uxTestGridForMerging", new ResourceSet("/", "uxTestGridForMerging.uct"));
        Properties parameters = new Properties();
        parameters.put("ucte.import.create-areas", "false");
        Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), parameters);
        assertEquals(0, network.getAreaTypeCount());
        assertEquals(0, network.getAreaCount());
    }

    @Test
    void testMetaInfos() throws IOException {
        try (var fs = Jimfs.newFileSystem(Configuration.unix())) {
            var importer = new UcteImporter(new InMemoryPlatformConfig(fs));
            assertEquals("UCTE", importer.getFormat());
            assertEquals("UCTE-DEF", importer.getComment());
            assertEquals(List.of("uct", "UCT"), importer.getSupportedExtensions());
        }
    }
}