PowerFactoryImporterTest.java

/**
 * Copyright (c) 2021, 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.powerfactory.converter;

import com.powsybl.commons.datasource.DirectoryDataSource;
import com.powsybl.commons.datasource.ResourceDataSource;
import com.powsybl.commons.datasource.ResourceSet;
import com.powsybl.commons.test.AbstractSerDeTest;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.util.BranchData;
import com.powsybl.iidm.network.util.TwtData;
import com.powsybl.iidm.serde.NetworkSerDe;
import com.powsybl.powerfactory.model.PowerFactoryDataLoader;
import com.powsybl.powerfactory.model.StudyCase;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static com.powsybl.commons.test.ComparisonUtils.assertXmlEquals;
import static org.junit.jupiter.api.Assertions.*;

/**
 * @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
 * @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
 * @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
 */
class PowerFactoryImporterTest extends AbstractSerDeTest {

    @Test
    void testBase() {
        PowerFactoryImporter importer = new PowerFactoryImporter();
        assertEquals("POWER-FACTORY", importer.getFormat());
        assertTrue(importer.getParameters().isEmpty());
        assertEquals("PowerFactory to IIDM converter", importer.getComment());
        assertEquals(List.of("json", "dgs", "properties"), importer.getSupportedExtensions());
    }

    @Test
    void testExistsAndCopy() throws IOException {
        InputStream is = Objects.requireNonNull(getClass().getResourceAsStream("/ieee14.dgs"));
        Path dgsFile = fileSystem.getPath("/work/ieee14.dgs");
        Files.copy(is, dgsFile);

        Optional<StudyCase> studyCase = PowerFactoryDataLoader.load(dgsFile, StudyCase.class);
        assertTrue(studyCase.isPresent());

        PowerFactoryImporter importer = new PowerFactoryImporter();
        assertTrue(importer.exists(new DirectoryDataSource(fileSystem.getPath("/work"), "ieee14")));
        assertFalse(importer.exists(new DirectoryDataSource(fileSystem.getPath("/work"), "error")));

        importer.copy(new DirectoryDataSource(fileSystem.getPath("/work"), "ieee14"),
                new DirectoryDataSource(fileSystem.getPath("/work"), "ieee14-copy"));
        assertTrue(Files.exists(fileSystem.getPath("/work/ieee14-copy.dgs")));
    }

    private Network importAndCompareXml(String id) {
        return importAndCompareXml(id, ".dgs");
    }

    private Network importJsonAndCompareXml(String id) {
        return importAndCompareXml(id, ".json");
    }

    private Network importAndCompareXml(String id, String fileExtension) {
        Network network = new PowerFactoryImporter()
                .importData(new ResourceDataSource(id, new ResourceSet("/", id + fileExtension)),
                        NetworkFactory.findDefault(),
                        null);

        Path file = fileSystem.getPath("/work/" + id + ".xiidm");
        network.setCaseDate(ZonedDateTime.parse("2021-01-01T10:00:00.000+02:00"));
        NetworkSerDe.write(network, file);
        try (InputStream is = Files.newInputStream(file)) {
            assertXmlEquals(getClass().getResourceAsStream("/" + id + ".xiidm"), is);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }

        return network;
    }

    @Test
    void ieee14Test() {
        assertTrue(importAndCompareXiidm("ieee14"));
    }

    @Test
    void twoBusesLineWithBTest() {
        assertTrue(importAndCompareXiidm("TwoBusesLineWithB"));
    }

    @Test
    void twoBusesLineWithGandBTest() {
        assertTrue(importAndCompareXiidm("TwoBusesLineWithGandB"));
    }

    @Test
    void twoBusesLineWithTandBTest() {
        assertTrue(importAndCompareXiidm("TwoBusesLineWithTandB"));
    }

    @Test
    void twoBusesLineWithCTest() {
        assertTrue(importAndCompareXiidm("TwoBusesLineWithC"));
    }

    @Test
    void twoBusesLineWithNumberOfParallelLines() {
        assertTrue(importAndCompareXiidm("TwoBusesLineWithNumberOfParallelLines"));
    }

    @Test
    void twoBusesGeneratorTest() {
        assertTrue(importAndCompareXiidm("TwoBusesGenerator"));
    }

    @Test
    void twoBusesGeneratorWithoutIvmodeTest() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorWithoutIvmode"));
    }

    @Test
    void twoBusesGeneratorAvmodeTest() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorAvmode"));
    }

    @Test
    void twoBusesGeneratorWithoutActiveLimitsTest() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorWithoutActiveLimits"));
    }

    @Test
    void twoBusesGeneratorIqtypeTest() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorIqtype"));
    }

    @Test
    void twoBusesGeneratorWithoutIqtypeTest() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorWithoutIqtype"));
    }

    @Test
    void twoBusesGeneratorElmReactiveLimits() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorElmReactiveLimits"));
    }

    @Test
    void twoBusesGeneratorTypReactiveLimits() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorTypReactiveLimits"));
    }

    @Test
    void twoBusesGeneratorTypMvarReactiveLimits() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorTypMvarReactiveLimits"));
    }

    @Test
    void switches() {
        assertTrue(importAndCompareXiidm("Switches"));
    }

    @Test
    void switchesNegativeVoltage() {
        assertTrue(importAndCompareXiidm("Switches-negative-voltage"));
    }

    @Test
    void switchesMissingVoltage() {
        assertTrue(importAndCompareXiidm("Switches-missing-voltage"));
    }

    @Test
    void switchesMissingAngle() {
        assertTrue(importAndCompareXiidm("Switches-missing-angle"));
    }

    @Test
    void switchesWithoutBus() {
        assertTrue(importAndCompareXiidm("Switches-without-bus"));
    }

    @Test
    void transformerPhaseGBComplete() {
        assertTrue(importJsonAndCompareXiidm("Transformer-Phase-GB-complete"));
    }

    @Test
    void threeMibPhaseWinding1Complete() {
        assertTrue(importJsonAndCompareXiidm("ThreeMIB_T3W_phase_winding1_complete"));
    }

    @Test
    void commonImpedance() {
        assertTrue(importAndCompareXiidm("CommonImpedance"));
    }

    @Test
    void commonImpedanceOnlyImpedance12() {
        assertTrue(importAndCompareXiidm("CommonImpedanceOnlyImpedance12"));
    }

    @Test
    void commonImpedanceWithDifferentNominal() {
        assertTrue(importAndCompareXiidm("CommonImpedanceWithDifferentNominal"));
    }

    @Test
    void twoBusesGeneratorAndShuntRL() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorAndShuntRL"));
    }

    @Test
    void twoBusesGeneratorAndShuntRLrxrea() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorAndShuntRLrxrea"));
    }

    @Test
    void twoBusesGeneratorAndShuntC() {
        assertTrue(importAndCompareXiidm("TwoBusesGeneratorAndShuntC"));
    }

    @Test
    void tower() {
        assertTrue(importAndCompareXiidm("Tower"));
    }

    @Test
    void voltageLevelsAndSubstations() {
        assertTrue(importAndCompareXiidm("VoltageLevelsAndSubstations"));
    }

    @Test
    void hvdc() {
        assertTrue(importAndCompareXiidm("Hvdc"));
    }

    @Test
    void capabilityCurve() {
        assertTrue(importAndCompareXiidm("CapabilityCurve"));
    }

    @Test
    void transformersWithPhaseAngleClock() {
        assertTrue(importAndCompareXiidm("TransformersWithPhaseAngleClock"));
    }

    @Test
    void threeWindingsTransformerWinding1Ratio() {
        assertTrue(importAndCompareXiidm("ThreeWindingsTransformerWinding1Ratio"));
    }

    @Test
    void slackBustp() {
        assertTrue(importAndCompareXiidm("Slack_bustp"));
    }

    @Test
    void slackIpctrl() {
        assertTrue(importAndCompareXiidm("Slack_ip_ctrl"));
    }

    private boolean importAndCompareXiidm(String powerfactoryCase) {
        importAndCompareXml(powerfactoryCase);
        return true;
    }

    private boolean importJsonAndCompareXiidm(String powerfactoryCase) {
        importJsonAndCompareXml(powerfactoryCase);
        return true;
    }

    @Test
    void transformerVhVl() {
        assertTrue(transformerBalance("Transformer-VhVl", 0.00009));
    }

    @Test
    void transformerVhVlNonNeutral() {
        assertTrue(transformerBalance("Transformer-VhVl-Non-Neutral", 0.0001));
    }

    @Test
    void transformerVhVlGB() {
        assertTrue(transformerBalance("Transformer-VhVl-GB", 0.09));
    }

    @Test
    void transformerVhVlGBNonNeutral() {
        assertTrue(transformerBalance("Transformer-VhVl-GB-Non-Neutral", 0.09));
    }

    @Test
    void transformerVhVlGBNonNeutralProportion() {
        assertTrue(transformerBalance("Transformer-VhVl-GB-Non-Neutral-proportion", 0.000025));
    }

    @Test
    void transformerVlVh() {
        assertTrue(transformerBalance("Transformer-VlVh", 0.0009));
    }

    @Test
    void transformerVlVhNonNeutral() {
        assertTrue(transformerBalance("Transformer-VlVh-Non-Neutral", 0.0003));
    }

    @Test
    void transformerVlVhGB() {
        assertTrue(transformerBalance("Transformer-VlVh-GB", 0.09));
    }

    @Test
    void transformerVlVhGBNonNeutral() {
        assertTrue(transformerBalance("Transformer-VlVh-GB-Non-Neutral", 0.09));
    }

    @Test
    void transformerVlVhGBNonNeutralProportion() {
        assertTrue(transformerBalance("Transformer-VlVh-GB-Non-Neutral-proportion", 0.0003));
    }

    private boolean transformerBalance(String powerfactoryCase, double tol) {
        Network network = importAndCompareXml(powerfactoryCase);
        transformerNetworkBalance(network, tol);
        return true;
    }

    /**
     * Three buses solved case:
     * Bus 1: SlackBus where a generator and the line "lne_1_2_1" are connected
     * Bus 2: TransportBus where the line "lne_1_2_1" and the transformer "trf_2_3_1" are connected
     * Bus 3: LoadBus where the transformer "trf_2_3_1" and the load "lod_3_1" are connected
     */
    private static void transformerNetworkBalance(Network network, double tol) {

        Load load = network.getLoad("lod_3_1");
        assertNotNull(load);
        Line line = network.getLine("lne_1_2_1");
        assertNotNull(line);
        TwoWindingsTransformer t2wt = network.getTwoWindingsTransformer("trf_2_3_1");
        assertNotNull(t2wt);

        BranchData lineData = new BranchData(line, 0.0, false);
        BranchData t2wtData = new BranchData(t2wt, 0.0, false, true);
        assertEquals(0.0, lineData.getComputedP2() + t2wtData.getComputedP1(), tol);
        assertEquals(0.0, lineData.getComputedQ2() + t2wtData.getComputedQ1(), tol);
        assertEquals(0.0, t2wtData.getComputedP2() + load.getP0(), tol);
        assertEquals(0.0, t2wtData.getComputedQ2() + load.getQ0(), tol);
    }

    @Test
    void transformerPhase() {
        assertTrue(phaseShifterBalance("Transformer-Phase", 0.001));
    }

    @Test
    void transformerPhaseWithmTaps() {
        assertTrue(phaseShifterBalance("Transformer-Phase-with-mTaps", 0.001));
    }

    @Test
    void transformerPhaseNeutral() {
        assertTrue(phaseShifterBalance("Transformer-Phase-Neutral", 0.0009));
    }

    @Test
    void transformerPhaseNeutralWithmTaps() {
        assertTrue(phaseShifterBalance("Transformer-Phase-Neutral-with-mTaps", 0.0009));
    }

    @Test
    void transformerPhaseGB() {
        assertTrue(phaseShifterBalance("Transformer-Phase-GB", 0.9));
    }

    @Test
    void transformerPhaseGBWithmTaps() {
        assertTrue(phaseShifterBalance("Transformer-Phase-GB-with-mTaps", 0.9));
    }

    @Test
    void transformerPhaseGBNeutral() {
        assertTrue(phaseShifterBalance("Transformer-Phase-GB-Neutral", 0.9));
    }

    @Test
    void transformerPhaseGBNeutralWithmTaps() {
        assertTrue(phaseShifterBalance("Transformer-Phase-GB-Neutral-with-mTaps", 0.9));
    }

    private boolean phaseShifterBalance(String powerfactoryCase, double tol) {
        Network network = importAndCompareXml(powerfactoryCase);
        phaseNetworkBalance(network, tol);
        return true;
    }

    /**
     * Three buses solved case:
     * Bus 1: SlackBus where a generator, the line "lne_1_2_1" and the line "lne_1_3_1" are connected
     * Bus 2: TransportBus where the line "lne_1_2_1" and the transformer "trf_2_3_1" are connected
     * Bus 3: LoadBus where the transformer "trf_2_3_1", the line "lne_1_3_1" and the load "lod_3_1" are connected
     */
    private static void phaseNetworkBalance(Network network, double tol) {

        Load load = network.getLoad("lod_3_1");
        assertNotNull(load);
        Line line12 = network.getLine("lne_1_2_1");
        assertNotNull(line12);
        Line line13 = network.getLine("lne_1_3_1");
        assertNotNull(line13);
        TwoWindingsTransformer t2wt = network.getTwoWindingsTransformer("trf_2_3_1");
        assertNotNull(t2wt);

        BranchData line12Data = new BranchData(line12, 0.0, false);
        BranchData line13Data = new BranchData(line13, 0.0, false);
        BranchData t2wtData = new BranchData(t2wt, 0.0, false, true);
        assertEquals(0.0, line12Data.getComputedP2() + t2wtData.getComputedP1(), tol);
        assertEquals(0.0, line12Data.getComputedQ2() + t2wtData.getComputedQ1(), tol);
        assertEquals(0.0, line13Data.getComputedP2() + t2wtData.getComputedP2() + load.getP0(), tol);
        assertEquals(0.0, line13Data.getComputedQ2() + t2wtData.getComputedQ2() + load.getQ0(), tol);
    }

    @Test
    void threeMibT3wPhaseTest() {
        Network network = importAndCompareXml("ThreeMIB_T3W_phase_solved");
        threeMibT3wPhaseTestNetworkBalance(network, 435.876560, 0.09);
        assertTrue(true);
    }

    /**
     * Only the balance at the three buses of the three windings transformer is done:
     * Bus 4 (500 kV): Load "lod_4_1", line "lne_4_1_1", twoWindingsTransformer "trf_4_1_1" and threeWindingsTransformer "trf_4_2_7_1" are connected
     * Bus 2 (18 kV) : Generator "sym_1_2_1" and  threeWindingsTransformer "trf_4_2_7_1" are connected
     * Bus 7 (16 kV) : Load "lod_7_1" and threeWindingsTransformer "trf_4_2_7_1" are connected
     */
    private static void threeMibT3wPhaseTestNetworkBalance(Network network, double targetQ, double tol) {

        Load load4 = network.getLoad("lod_4_1");
        assertNotNull(load4);
        Load load7 = network.getLoad("lod_7_1");
        assertNotNull(load7);
        Generator generator2 = network.getGenerator("sym_2_1");
        assertNotNull(generator2);

        Line line45 = network.getLine("lne_4_5_1");
        assertNotNull(line45);
        TwoWindingsTransformer t2wt41 = network.getTwoWindingsTransformer("trf_4_1_1");
        assertNotNull(t2wt41);
        ThreeWindingsTransformer t3wt427 = network.getThreeWindingsTransformer("tr3_4_2_7_1");
        assertNotNull(t2wt41);

        BranchData line45Data = new BranchData(line45, 0.0, false);
        BranchData t2wtData41 = new BranchData(t2wt41, 0.0, false, true);
        TwtData t3wtData427 = new TwtData(t3wt427, 0.0, false, true);

        // The case does not have the reactive of the generator. We set it manually
        generator2.setTargetQ(targetQ);

        assertEquals(0.0, t3wtData427.getComputedP(ThreeSides.ONE) + line45Data.getComputedP1() + t2wtData41.getComputedP1() + load4.getP0(), tol);
        assertEquals(0.0, t3wtData427.getComputedQ(ThreeSides.ONE) + line45Data.getComputedQ1() + t2wtData41.getComputedQ1() + load4.getQ0(), tol);
        assertEquals(0.0, t3wtData427.getComputedP(ThreeSides.TWO) - generator2.getTargetP(), tol);
        assertEquals(0.0, t3wtData427.getComputedQ(ThreeSides.TWO) - generator2.getTargetQ(), tol);
        assertEquals(0.0, t3wtData427.getComputedP(ThreeSides.THREE) + load7.getP0(), tol);
        assertEquals(0.0, t3wtData427.getComputedQ(ThreeSides.THREE) + load7.getQ0(), tol);
    }

    @Test
    void threeMibPhaseWinding1() {
        assertTrue(threeWindingPhaseImportCompareXmlAndNetworkBalance("ThreeMIB_T3W_phase_winding1", 514.75293551, 0.09));
    }

    @Test
    void threeMibPhaseWinding1Ratio() {
        assertTrue(threeWindingPhaseImportCompareXmlAndNetworkBalance("ThreeMIB_T3W_phase_winding1_ratio", 591.898015, 0.09));
    }

    @Test
    void threeMibPhaseWinding2() {
        assertTrue(threeWindingPhaseImportCompareXmlAndNetworkBalance("ThreeMIB_T3W_phase_winding2", 658.367984, 0.09));
    }

    @Test
    void threeMibPhaseWinding3() {
        assertTrue(threeWindingPhaseImportCompareXmlAndNetworkBalance("ThreeMIB_T3W_phase_winding3", 596.52371, 0.09));
    }

    @Test
    void threeMibPhaseWinding12() {
        assertTrue(threeWindingPhaseImportCompareXmlAndNetworkBalance("ThreeMIB_T3W_phase_winding12", 575.835158, 0.09));
    }

    private boolean threeWindingPhaseImportCompareXmlAndNetworkBalance(String caseFile, double targetQ, double tol) {
        Network network = importAndCompareXml(caseFile);
        threeMibPhaseWindingTestNetworkBalance(network, targetQ, tol);
        return true;
    }

    /**
     * Only the balance at the three buses of the three windings transformer is done:
     * Bus 4 (500 kV): Load "lod_4_1", line "lne_4_1_1", twoWindingsTransformer "trf_4_1_1" and threeWindingsTransformer "trf_4_2_7_1" are connected
     * Bus 2 (18 kV) : Generator "sym_1_2_1", twoWindingsTransformer "trf_6_2_1" and  threeWindingsTransformer "trf_4_2_7_1" are connected
     * Bus 7 (16 kV) : Load "lod_7_1", twoWindingsTransformer "trf_5_7_1" and threeWindingsTransformer "trf_4_2_7_1" are connected
     */
    private static void threeMibPhaseWindingTestNetworkBalance(Network network, double targetQ, double tol) {

        Load load4 = network.getLoad("lod_4_1");
        assertNotNull(load4);
        Load load7 = network.getLoad("lod_7_1");
        assertNotNull(load7);
        Generator generator2 = network.getGenerator("sym_2_1");
        assertNotNull(generator2);

        Line line45 = network.getLine("lne_4_5_1");
        assertNotNull(line45);
        TwoWindingsTransformer t2wt41 = network.getTwoWindingsTransformer("trf_4_1_1");
        assertNotNull(t2wt41);
        TwoWindingsTransformer t2wt62 = network.getTwoWindingsTransformer("trf_6_2_1");
        assertNotNull(t2wt62);
        TwoWindingsTransformer t2wt57 = network.getTwoWindingsTransformer("trf_5_7_1");
        assertNotNull(t2wt57);
        ThreeWindingsTransformer t3wt427 = network.getThreeWindingsTransformer("tr3_4_2_7_1");
        assertNotNull(t2wt41);

        BranchData line45Data = new BranchData(line45, 0.0, false);
        BranchData t2wtData41 = new BranchData(t2wt41, 0.0, false, true);
        BranchData t2wtData62 = new BranchData(t2wt62, 0.0, false, true);
        BranchData t2wtData57 = new BranchData(t2wt57, 0.0, false, true);
        TwtData t3wtData427 = new TwtData(t3wt427, 0.0, false, true);

        // The case does not have the reactive of the generator. We set it manually
        generator2.setTargetQ(targetQ);

        assertEquals(0.0, t3wtData427.getComputedP(ThreeSides.ONE) + line45Data.getComputedP1() + t2wtData41.getComputedP1() + load4.getP0(), tol);
        assertEquals(0.0, t3wtData427.getComputedQ(ThreeSides.ONE) + line45Data.getComputedQ1() + t2wtData41.getComputedQ1() + load4.getQ0(), tol);
        assertEquals(0.0, t3wtData427.getComputedP(ThreeSides.TWO) + t2wtData62.getComputedP2() - generator2.getTargetP(), tol);
        assertEquals(0.0, t3wtData427.getComputedQ(ThreeSides.TWO) + t2wtData62.getComputedQ2() - generator2.getTargetQ(), tol);
        assertEquals(0.0, t3wtData427.getComputedP(ThreeSides.THREE) + t2wtData57.getComputedP2() + load7.getP0(), tol);
        assertEquals(0.0, t3wtData427.getComputedQ(ThreeSides.THREE) + t2wtData57.getComputedQ2() + load7.getQ0(), tol);
    }

}