DynamicModelsSupplierTest.java

/**
 * Copyright (c) 2023, 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.dynawo.dsl;

import com.powsybl.dynamicsimulation.DynamicModel;
import com.powsybl.dynamicsimulation.DynamicModelsSupplier;
import com.powsybl.dynamicsimulation.groovy.DynamicModelGroovyExtension;
import com.powsybl.dynamicsimulation.groovy.GroovyDynamicModelsSupplier;
import com.powsybl.dynamicsimulation.groovy.GroovyExtension;
import com.powsybl.dynawo.DynawoSimulationProvider;
import com.powsybl.dynawo.models.BlackBoxModel;
import com.powsybl.dynawo.models.EquipmentBlackBoxModel;
import com.powsybl.dynawo.models.automationsystems.TapChangerAutomationSystem;
import com.powsybl.dynawo.models.automationsystems.TapChangerBlockingAutomationSystem;
import com.powsybl.dynawo.models.automationsystems.UnderVoltageAutomationSystem;
import com.powsybl.dynawo.models.automationsystems.overloadmanagments.DynamicOverloadManagementSystem;
import com.powsybl.dynawo.models.automationsystems.overloadmanagments.DynamicTwoLevelOverloadManagementSystem;
import com.powsybl.dynawo.models.automationsystems.phaseshifters.PhaseShifterIAutomationSystem;
import com.powsybl.dynawo.models.automationsystems.phaseshifters.PhaseShifterPAutomationSystem;
import com.powsybl.dynawo.models.buses.InfiniteBus;
import com.powsybl.dynawo.models.buses.StandardBus;
import com.powsybl.dynawo.models.generators.*;
import com.powsybl.dynawo.models.loads.*;
import com.powsybl.dynawo.models.hvdc.BaseHvdc;
import com.powsybl.dynawo.models.hvdc.HvdcDangling;
import com.powsybl.dynawo.models.lines.StandardLine;
import com.powsybl.dynawo.models.svarcs.BaseStaticVarCompensator;
import com.powsybl.dynawo.models.transformers.TransformerFixedRatio;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory;
import com.powsybl.iidm.network.test.FourSubstationsNodeBreakerFactory;
import com.powsybl.iidm.network.test.HvdcTestNetwork;
import com.powsybl.iidm.network.test.SvcTestCaseFactory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.IOException;
import java.util.List;
import java.util.stream.Stream;

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

/**
 * @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
 */
class DynamicModelsSupplierTest extends AbstractModelSupplierTest {

    private static final List<DynamicModelGroovyExtension> EXTENSIONS = GroovyExtension.find(DynamicModelGroovyExtension.class, DynawoSimulationProvider.NAME);

    @Test
    void testLibsInfo() {
        for (DynamicModelGroovyExtension extension : EXTENSIONS) {
            assertNotNull(extension.getModelNames());
        }
    }

    @ParameterizedTest(name = "{0}")
    @MethodSource("provideEquipmentModelData")
    void testEquipmentDynamicModels(String groovyScriptName, Class<? extends EquipmentBlackBoxModel> modelClass, Network network, String staticId, String parameterId, String lib) {
        DynamicModelsSupplier supplier = new GroovyDynamicModelsSupplier(getResourceAsStream(groovyScriptName), EXTENSIONS);
        List<DynamicModel> dynamicModels = supplier.get(network);
        assertEquals(1, dynamicModels.size());
        assertTrue(modelClass.isInstance(dynamicModels.get(0)));
        assertEquipmentBlackBoxModel(modelClass.cast(dynamicModels.get(0)), staticId, parameterId, lib);
    }

    @ParameterizedTest(name = "{0}")
    @MethodSource("provideAutomationSystemModelData")
    void testAutomationSystemDynamicModels(String groovyScriptName, Class<? extends BlackBoxModel> modelClass, Network network, String dynamicId, String parameterId, String lib) {
        DynamicModelsSupplier supplier = new GroovyDynamicModelsSupplier(getResourceAsStream(groovyScriptName), EXTENSIONS);
        List<DynamicModel> dynamicModels = supplier.get(network);
        assertEquals(1, dynamicModels.size());
        assertTrue(modelClass.isInstance(dynamicModels.get(0)));
        assertPureDynamicBlackBoxModel(modelClass.cast(dynamicModels.get(0)), dynamicId, parameterId, lib);
    }

    @ParameterizedTest(name = "{0}")
    @MethodSource("provideWarningsModel")
    void testDslWarnings(String groovyScriptName, Network network, String report) throws IOException {
        DynamicModelsSupplier supplier = new GroovyDynamicModelsSupplier(getResourceAsStream(groovyScriptName), EXTENSIONS);
        assertTrue(supplier.get(network, reportNode).isEmpty());
        checkReportNode(report);
    }

    @ParameterizedTest(name = "{0}")
    @MethodSource("provideGenerator")
    void testGeneratorPrefixes(String groovyScriptName, Class<? extends GeneratorModel> modelClass, Network network, String terminalVarName, String report) throws IOException {
        DynamicModelsSupplier supplier = new GroovyDynamicModelsSupplier(getResourceAsStream(groovyScriptName), EXTENSIONS);
        List<DynamicModel> dynamicModels = supplier.get(network, reportNode);
        assertEquals(1, dynamicModels.size());
        assertTrue(modelClass.isInstance(dynamicModels.get(0)));
        assertEquals(terminalVarName, modelClass.cast(dynamicModels.get(0)).getTerminalVarName());
        checkReportNode(report);
    }

    void assertEquipmentBlackBoxModel(EquipmentBlackBoxModel bbm, String id, String parameterId, String lib) {
        assertEquals(id, bbm.getDynamicModelId());
        assertEquals(parameterId, bbm.getParameterSetId());
        assertEquals(lib, bbm.getLib());
    }

    void assertPureDynamicBlackBoxModel(BlackBoxModel bbm, String dynamicId, String parameterId, String lib) {
        assertEquals(dynamicId, bbm.getDynamicModelId());
        assertEquals(parameterId, bbm.getParameterSetId());
        assertEquals(lib, bbm.getLib());
    }

    private static Stream<Arguments> provideEquipmentModelData() {
        return Stream.of(
                Arguments.of("/dynamicModels/bus.groovy", StandardBus.class, EurostagTutorialExample1Factory.create(), "NGEN", "SB", "Bus"),
                Arguments.of("/dynamicModels/hvdcP.groovy", BaseHvdc.class, HvdcTestNetwork.createVsc(), "L", "HVDC", "HvdcPV"),
                Arguments.of("/dynamicModels/hvdcVsc.groovy", BaseHvdc.class, HvdcTestNetwork.createVsc(), "L", "HVDC", "HvdcVsc"),
                Arguments.of("/dynamicModels/hvdcPDangling.groovy", HvdcDangling.class, HvdcTestNetwork.createVsc(), "L", "HVDC", "HvdcPVDanglingDiagramPQ"),
                Arguments.of("/dynamicModels/hvdcVscDangling.groovy", HvdcDangling.class, HvdcTestNetwork.createVsc(), "L", "HVDC", "HvdcVscDanglingUdc"),
                Arguments.of("/dynamicModels/loadAB.groovy", BaseLoad.class, EurostagTutorialExample1Factory.create(), "LOAD", "LAB", "LoadAlphaBetaRestorative"),
                Arguments.of("/dynamicModels/loadABControllable.groovy", BaseLoadControllable.class, EurostagTutorialExample1Factory.create(), "LOAD", "LAB", "LoadAlphaBeta"),
                Arguments.of("/dynamicModels/loadTransformer.groovy", LoadOneTransformer.class, EurostagTutorialExample1Factory.create(), "LOAD", "LOT", "LoadOneTransformer"),
                Arguments.of("/dynamicModels/loadTransformerTapChanger.groovy", LoadOneTransformerTapChanger.class, EurostagTutorialExample1Factory.create(), "LOAD", "LOT", "LoadOneTransformerTapChanger"),
                Arguments.of("/dynamicModels/loadTwoTransformers.groovy", LoadTwoTransformers.class, EurostagTutorialExample1Factory.create(), "LOAD", "LTT", "LoadTwoTransformers"),
                Arguments.of("/dynamicModels/loadTwoTransformersTapChangers.groovy", LoadTwoTransformersTapChangers.class, EurostagTutorialExample1Factory.create(), "LOAD", "LTT", "LoadTwoTransformersTapChangers"),
                Arguments.of("/dynamicModels/infiniteBus.groovy", InfiniteBus.class, HvdcTestNetwork.createVsc(), "B1", "b", "InfiniteBusWithVariations"),
                Arguments.of("/dynamicModels/line.groovy", StandardLine.class, EurostagTutorialExample1Factory.create(), "NHV1_NHV2_1", "LINE", "Line"),
                Arguments.of("/dynamicModels/genFictitious.groovy", BaseGenerator.class, EurostagTutorialExample1Factory.create(), "GEN", "GF", "GeneratorFictitious"),
                Arguments.of("/dynamicModels/gen.groovy", SynchronousGenerator.class, EurostagTutorialExample1Factory.create(), "GEN", "GSFWPR", "GeneratorSynchronousThreeWindings"),
                Arguments.of("/dynamicModels/genControllable.groovy", SynchronousGeneratorControllable.class, EurostagTutorialExample1Factory.create(), "GEN", "GSFWPR", "GeneratorSynchronousFourWindingsProportionalRegulations"),
                Arguments.of("/dynamicModels/omegaGen.groovy", SynchronizedGenerator.class, EurostagTutorialExample1Factory.create(), "GEN", "GPQ", "GeneratorPQ"),
                Arguments.of("/dynamicModels/omegaGenControllable.groovy", SynchronizedGeneratorControllable.class, EurostagTutorialExample1Factory.create(), "GEN", "GPQ", "GeneratorPV"),
                Arguments.of("/dynamicModels/transformer.groovy", TransformerFixedRatio.class, EurostagTutorialExample1Factory.create(), "NGEN_NHV1", "TFR", "TransformerFixedRatio"),
                Arguments.of("/dynamicModels/svarc.groovy", BaseStaticVarCompensator.class, SvcTestCaseFactory.create(), "SVC2", "svarc", "StaticVarCompensatorPV"),
                Arguments.of("/dynamicModels/wecc.groovy", WeccGen.class, EurostagTutorialExample1Factory.create(), "GEN", "Wind", "WT4BWeccCurrentSource"),
                Arguments.of("/dynamicModels/weccSynchro.groovy", SynchronizedWeccGen.class, EurostagTutorialExample1Factory.create(), "GEN", "Wind", "WTG4AWeccCurrentSource"),
                Arguments.of("/dynamicModels/gridFormingConverter.groovy", GridFormingConverter.class, EurostagTutorialExample1Factory.create(), "GEN", "GF", "GridFormingConverterMatchingControl")
        );
    }

    private static Stream<Arguments> provideAutomationSystemModelData() {
        return Stream.of(
                Arguments.of("/dynamicModels/overloadManagement.groovy", DynamicOverloadManagementSystem.class, EurostagTutorialExample1Factory.create(), "AM_NHV1_NHV2_1", "CLA", "CurrentLimitAutomaton"),
                Arguments.of("/dynamicModels/overloadManagementTwoLevel.groovy", DynamicTwoLevelOverloadManagementSystem.class, EurostagTutorialExample1Factory.create(), "AM_NHV1_NHV2_1", "CLA", "CurrentLimitAutomatonTwoLevels"),
                Arguments.of("/dynamicModels/tapChanger.groovy", TapChangerAutomationSystem.class, EurostagTutorialExample1Factory.create(), "TC", "tc", "TapChangerAutomaton"),
                Arguments.of("/dynamicModels/tapChangerBlockingBusBar.groovy", TapChangerBlockingAutomationSystem.class, FourSubstationsNodeBreakerFactory.create(), "ZAB", "ZAB", "TapChangerBlockingAutomaton2"),
                Arguments.of("/dynamicModels/tapChangerBlocking.groovy", TapChangerBlockingAutomationSystem.class, EurostagTutorialExample1Factory.createWithLFResults(), "ZAB", "ZAB", "TapChangerBlockingAutomaton3"),
                Arguments.of("/dynamicModels/phaseShifterI.groovy", PhaseShifterIAutomationSystem.class, EurostagTutorialExample1Factory.create(), "PS_NGEN_NHV1", "ps", "PhaseShifterI"),
                Arguments.of("/dynamicModels/phaseShifterP.groovy", PhaseShifterPAutomationSystem.class, EurostagTutorialExample1Factory.create(), "PS_NGEN_NHV1", "ps", "PhaseShifterP"),
                Arguments.of("/dynamicModels/underVoltage.groovy", UnderVoltageAutomationSystem.class, EurostagTutorialExample1Factory.create(), "UV_GEN", "uv", "UnderVoltageAutomaton")
        );
    }

    private static Stream<Arguments> provideWarningsModel() {
        return Stream.of(
                Arguments.of("/warnings/missingStaticId.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model LoadAlphaBeta null instantiation KO
                                 'staticId' field is not set
                        """),
                Arguments.of("/warnings/missingParameterId.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model LoadAlphaBeta LOAD instantiation KO
                                 'parameterSetId' field is not set
                        """),
                Arguments.of("/warnings/missingEquipment.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model LoadAlphaBeta GEN instantiation KO
                                 'staticId' field value 'GEN' not found for equipment type(s) LOAD
                        """),
                Arguments.of("/warnings/missingDanglingProperty.groovy", HvdcTestNetwork.createVsc(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model HvdcPV L instantiation KO
                                 'dangling' field is set but HvdcPV does not possess this option
                        """),
                Arguments.of("/warnings/underVoltageMissingGenerator.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model UnderVoltage UV_GEN instantiation KO
                                 'generator' field value 'NGEN' not found for equipment type(s) GENERATOR
                        """),
                Arguments.of("/warnings/phaseShifterMissingTransformer.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model PhaseShifterI PS_NGEN_NHV1 instantiation KO
                                 'transformer' field value 'NGEN' not found for equipment type(s) TWO_WINDINGS_TRANSFORMER
                        """),
                Arguments.of("/warnings/claMissingMeasurement.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model OverloadManagementSystem CLA_NGEN instantiation KO
                                 'iMeasurement' field value 'NGEN' not found for equipment type(s) BRANCH
                        """),
                Arguments.of("/warnings/claMissingMeasurementSide.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model OverloadManagementSystem CLA_NGEN instantiation KO
                                 'iMeasurementSide' field is not set
                        """),
                Arguments.of("/warnings/claMissingControlled.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model OverloadManagementSystem CLA_NGEN instantiation KO
                                 'controlledBranch' field value 'GEN' not found for equipment type(s) BRANCH
                        """),
                Arguments.of("/warnings/cla2MissingMeasurement2.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model TwoLevelOverloadManagementSystem CLA_NGEN instantiation KO
                                 'iMeasurement2' field value 'NGEN' not found for equipment type(s) BRANCH
                        """),
                Arguments.of("/warnings/cla2MissingMeasurementSide2.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model TwoLevelOverloadManagementSystem CLA_NGEN instantiation KO
                                 'iMeasurement2Side' field is not set
                        """),
                Arguments.of("/warnings/tapChangerMissingBus.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model TapChangerBlockingAutomaton ZAB instantiation KO
                                 'uMeasurements' field value 'LOAD' not found for equipment type(s) BUS/BUSBAR_SECTION
                                 'uMeasurements' field value 'Wrong_ID' not found for equipment type(s) BUS/BUSBAR_SECTION
                                 'uMeasurements' list is empty
                        """),
                Arguments.of("/warnings/tapChangerMissingBusList.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model TapChangerBlockingAutomaton ZAB instantiation KO
                                 'uMeasurements' field value '[LOAD, Wrong_ID]' not found for equipment type(s) BUS/BUSBAR_SECTION
                                 'uMeasurements' field value '[NGEN_NHV1]' not found for equipment type(s) BUS/BUSBAR_SECTION
                                 'uMeasurements' list is empty
                        """),
                Arguments.of("/warnings/tapChangerCompatible.groovy", EurostagTutorialExample1Factory.create(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model TapChangerBlockingAutomaton ZAB instantiation KO
                                 'transformers' field value 'GEN' not found for equipment type(s) TWO_WINDINGS_TRANSFORMER/LOAD, id will be used as pure dynamic model id
                                 'uMeasurements' field value 'GEN' not found for equipment type(s) BUS/BUSBAR_SECTION
                                 'uMeasurements' list is empty
                        """),
                Arguments.of("/warnings/hvdcVscWrongStaticType.groovy", HvdcTestNetwork.createLcc(),
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              + Model HvdcVsc L instantiation KO
                                 'staticId' field value 'L' not found for equipment type(s) VSC HVDC_LINE
                        """)
                );
    }

    private static Stream<Arguments> provideGenerator() {
        return Stream.of(
                Arguments.of("/dynamicModels/gen.groovy", SynchronousGenerator.class, EurostagTutorialExample1Factory.create(), "generator_terminal",
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              Model GeneratorSynchronousThreeWindings GEN instantiation OK
                        """),
                Arguments.of("/dynamicModels/genTfo.groovy", SynchronousGenerator.class, EurostagTutorialExample1Factory.create(), "transformer_terminal1",
                        """
                        + DSL tests
                           + Groovy Dynamic Models Supplier
                              Model GeneratorSynchronousThreeWindingsPmConstVRNordicTfo GEN instantiation OK
                        """)
        );
    }
}