AbstractGeneratorTest.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.iidm.network.tck;

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.test.FictitiousSwitchFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.Arrays;
import java.util.List;

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

public abstract class AbstractGeneratorTest {

    private static final String GEN_ID = "gen_id";

    private static final String INVALID = "invalid";

    private static final String TO_REMOVE = "toRemove";

    private Network network;
    private VoltageLevel voltageLevel;

    @BeforeEach
    public void initNetwork() {
        network = FictitiousSwitchFactory.create();
        voltageLevel = network.getVoltageLevel("C");
    }

    @Test
    public void testSetterGetter() {
        Generator generator = network.getGenerator("CB");
        assertNotNull(generator);
        assertEquals(EnergySource.HYDRO, generator.getEnergySource());
        generator.setEnergySource(EnergySource.NUCLEAR);
        assertEquals(EnergySource.NUCLEAR, generator.getEnergySource());
        double targetP = 15.0;
        generator.setTargetP(targetP);
        assertEquals(targetP, generator.getTargetP(), 0.0);
        double minP = 10.0;
        generator.setMinP(minP);
        assertEquals(minP, generator.getMinP(), 0.0);
        double maxP = 20.0;
        generator.setMaxP(maxP);
        assertEquals(maxP, generator.getMaxP(), 0.0);

        double activePowerSetpoint = 11.0;
        double reactivePowerSetpoint = 21.0;
        double voltageSetpoint = 31.0;
        double ratedS = 41.0;
        generator.setTargetP(activePowerSetpoint);
        generator.setTargetQ(reactivePowerSetpoint);
        generator.setTargetV(voltageSetpoint);
        generator.setRatedS(ratedS);
        assertEquals(activePowerSetpoint, generator.getTargetP(), 0.0);
        assertEquals(reactivePowerSetpoint, generator.getTargetQ(), 0.0);
        assertEquals(voltageSetpoint, generator.getTargetV(), 0.0);
        assertEquals(ratedS, generator.getRatedS(), 0.0);

        generator.setVoltageRegulatorOn(false);
        assertFalse(generator.isVoltageRegulatorOn());
        generator.setVoltageRegulatorOn(true);
        assertTrue(generator.isVoltageRegulatorOn());
        assertFalse(generator.isCondenser());

        assertEquals(12, generator.getTerminal().getNodeBreakerView().getNode());
    }

    @Test
    public void undefinedVoltageRegulatorOn() {
        GeneratorAdder generatorAdder = voltageLevel.newGenerator()
                .setId("GEN")
                .setMaxP(Double.MAX_VALUE)
                .setMinP(-Double.MAX_VALUE)
                .setTargetP(30.0)
                .setNode(1);
        ValidationException e = assertThrows(ValidationException.class, generatorAdder::add);
        assertEquals("Generator 'GEN': voltage regulator status is not set", e.getMessage());
    }

    @Test
    public void invalidMaxP() {
        ValidationException e = assertThrows(ValidationException.class, () -> createGenerator(INVALID, EnergySource.HYDRO, Double.NaN, 10.0, 20.0,
                30.0, 40.0, false, 20.0));
        assertTrue(e.getMessage().contains("for maximum P"));
    }

    @Test
    public void invalidMinP() {
        ValidationException e = assertThrows(ValidationException.class, () -> createGenerator(INVALID, EnergySource.HYDRO, 20.0, Double.NaN, 20.0,
                30.0, 40.0, false, 20.0));
        assertTrue(e.getMessage().contains("for minimum P"));
    }

    @Test
    public void invalidLimitsP() {
        ValidationException e = assertThrows(ValidationException.class, () -> createGenerator(INVALID, EnergySource.HYDRO, 20.0, 21., 20.0,
                30.0, 40.0, false, 20.0));
        assertTrue(e.getMessage().contains("invalid active limits"));
    }

    /**
     * This test goal is to check if targetP is allowed to be freely set outside the bounds defined by minP and maxP.
     * <p>
     * For a Battery it is expected that the current power is between these bounds, but it is not mandatory for a Generator</p>
     */
    @Test
    public void invalidPowerBounds() {
        // targetP < minP
        Generator invalidMinGenerator = createGenerator("invalid_min", EnergySource.HYDRO, 20.0, 10.0, 20.0,
                0.0, 40.0, false, 20.0);
        assertEquals(0.0, invalidMinGenerator.getTargetP(), 0.0);
        invalidMinGenerator.remove();

        // targetP > maxP
        Generator invalidMaxGenerator = createGenerator("invalid_max", EnergySource.HYDRO, 20.0, 10.0, 20.0,
                30.0, 40.0, false, 20.0);
        assertEquals(30.0, invalidMaxGenerator.getTargetP(), 0.0);

    }

    @Test
    public void invalidRatedS() {
        ValidationException e = assertThrows(ValidationException.class, () -> createGenerator(INVALID, EnergySource.HYDRO, 20.0, 11., 0.0,
                15.0, 40.0, false, 20.0));
        assertTrue(e.getMessage().contains("Invalid value of rated S"));
    }

    @Test
    public void invalidRatedS2() {
        ValidationException e = assertThrows(ValidationException.class, () -> createGenerator(INVALID, EnergySource.HYDRO, 20.0, 11., -1.0,
                15.0, 40.0, false, 20.0));
        assertTrue(e.getMessage().contains("Invalid value of rated S"));
    }

    @Test
    public void invalidActiveP() {
        ValidationException e = assertThrows(ValidationException.class, () -> createGenerator(INVALID, EnergySource.HYDRO, 20.0, 11., 2.0,
                Double.NaN, 10.0, false, 10.0));
        assertTrue(e.getMessage().contains("for active power setpoint"));
    }

    @Test
    public void invalidReactiveQ() {
        ValidationException e = assertThrows(ValidationException.class, () -> createGenerator(INVALID, EnergySource.HYDRO, 20.0, 11., 2.0,
                30.0, Double.NaN, false, 10.0));
        assertTrue(e.getMessage().contains("for reactive power setpoint"));
    }

    @Test
    public void invalidVoltageSetpoint() {
        ValidationException e = assertThrows(ValidationException.class, () -> createGenerator(INVALID, EnergySource.HYDRO, 20.0, 11., 2.0,
                30.0, 40.0, true, 0.0));
        assertTrue(e.getMessage().contains("for voltage setpoint"));
    }

    @Test
    public void duplicateEquipment() {
        createGenerator("duplicate", EnergySource.HYDRO, 20.0, 11., 2.0,
                15.0, 40.0, true, 2.0);
        PowsyblException e = assertThrows(PowsyblException.class, () -> createGenerator("duplicate", EnergySource.HYDRO, 20.0, 11., 2.0,
                15.0, 40.0, true, 2.0));
        assertTrue(e.getMessage().contains("contains an object 'GeneratorImpl' with the id 'duplicate'"));
    }

    @Test
    public void duplicateId() {
        PowsyblException e = assertThrows(PowsyblException.class, () -> createGenerator("A", EnergySource.HYDRO, 20.0, 11., 2.0,
                30.0, 40.0, true, 2.0));
        assertTrue(e.getMessage().contains("with the id 'A'"));
    }

    @Test
    public void testAdder() {
        voltageLevel.newGenerator()
                .setId(GEN_ID)
                .setVoltageRegulatorOn(true)
                .setEnergySource(EnergySource.NUCLEAR)
                .setMaxP(100.0)
                .setMinP(10.0)
                .setRatedS(2.0)
                .setTargetP(30.0)
                .setTargetQ(20.0)
                .setNode(1)
                .setTargetV(31.0)
                .setCondenser(true)
                .add();
        Generator generator = network.getGenerator(GEN_ID);
        assertNotNull(generator);
        assertEquals(GEN_ID, generator.getId());
        assertTrue(generator.isVoltageRegulatorOn());
        assertEquals(EnergySource.NUCLEAR, generator.getEnergySource());
        assertEquals(100.0, generator.getMaxP(), 0.0);
        assertEquals(10.0, generator.getMinP(), 0.0);
        assertEquals(2.0, generator.getRatedS(), 0.0);
        assertEquals(30.0, generator.getTargetP(), 0.0);
        assertEquals(20.0, generator.getTargetQ(), 0.0);
        assertEquals(31.0, generator.getTargetV(), 0.0);
        assertTrue(generator.isCondenser());
    }

    @Test
    public void testRemove() {
        String unmodifiableRemovedEqMessage = "Cannot modify removed equipment " + TO_REMOVE;
        createGenerator(TO_REMOVE, EnergySource.HYDRO, 20.0, 11., 2.0,
                15.0, 40.0, true, 2.0);
        int count = network.getGeneratorCount();
        Generator generator = network.getGenerator(TO_REMOVE);
        assertNotNull(generator);
        generator.remove();
        assertNotNull(generator);
        Terminal terminal = generator.getTerminal();
        assertNotNull(terminal);
        try {
            terminal.isConnected();
            fail();
        } catch (PowsyblException e) {
            assertEquals("Cannot access connectivity status of removed equipment " + TO_REMOVE, e.getMessage());
        }
        try {
            terminal.getNodeBreakerView().getNode();
            fail();
        } catch (PowsyblException e) {
            assertEquals("Cannot access node of removed equipment " + TO_REMOVE, e.getMessage());
        }
        try {
            terminal.getBusBreakerView().getBus();
            fail();
        } catch (PowsyblException e) {
            assertEquals("Cannot access bus of removed equipment " + TO_REMOVE, e.getMessage());
        }
        try {
            terminal.getBusBreakerView().getConnectableBus();
            fail();
        } catch (PowsyblException e) {
            assertEquals("Cannot access bus of removed equipment " + TO_REMOVE, e.getMessage());
        }
        try {
            terminal.getBusView().getBus();
            fail();
        } catch (PowsyblException e) {
            assertEquals("Cannot access bus of removed equipment " + TO_REMOVE, e.getMessage());
        }
        try {
            terminal.getBusView().getConnectableBus();
            fail();
        } catch (PowsyblException e) {
            assertEquals("Cannot access bus of removed equipment " + TO_REMOVE, e.getMessage());
        }
        try {
            terminal.getVoltageLevel();
            fail();
        } catch (PowsyblException e) {
            assertEquals("Cannot access voltage level of removed equipment " + TO_REMOVE, e.getMessage());
        }
        try {
            terminal.traverse(Mockito.mock(Terminal.TopologyTraverser.class));
            fail();
        } catch (PowsyblException e) {
            assertEquals("Associated equipment toRemove is removed", e.getMessage());
        }
        Terminal.BusBreakerView bbView = terminal.getBusBreakerView();
        assertNotNull(bbView);
        try {
            bbView.moveConnectable("BUS", true);
            fail();
        } catch (PowsyblException e) {
            assertEquals(unmodifiableRemovedEqMessage, e.getMessage());
        }
        Terminal.NodeBreakerView nbView = terminal.getNodeBreakerView();
        try {
            nbView.moveConnectable(0, "VL");
            fail();
        } catch (PowsyblException e) {
            assertEquals(unmodifiableRemovedEqMessage, e.getMessage());
        }
        try {
            terminal.setP(1.0);
            fail();
        } catch (PowsyblException e) {
            assertEquals(unmodifiableRemovedEqMessage, e.getMessage());
        }
        try {
            terminal.setQ(1.0);
            fail();
        } catch (PowsyblException e) {
            assertEquals(unmodifiableRemovedEqMessage, e.getMessage());
        }
        try {
            generator.getNetwork();
            fail();
        } catch (PowsyblException e) {
            assertEquals("Cannot access network of removed equipment " + TO_REMOVE, e.getMessage());
        }
        assertEquals(count - 1L, network.getGeneratorCount());
        assertNull(network.getGenerator(TO_REMOVE));
    }

    @Test
    public void testSetterGetterInMultiVariants() {
        VariantManager variantManager = network.getVariantManager();
        createGenerator("testMultiVariant", EnergySource.HYDRO, 20.0, 11., 2.0,
                15.0, 40.0, true, 2.0);
        Generator generator = network.getGenerator("testMultiVariant");
        List<String> variantsToAdd = Arrays.asList("s1", "s2", "s3", "s4");
        variantManager.cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, variantsToAdd);

        variantManager.setWorkingVariant("s4");
        // check values cloned by extend
        assertTrue(generator.isVoltageRegulatorOn());
        assertEquals(15.0, generator.getTargetP(), 0.0);
        assertEquals(40.0, generator.getTargetQ(), 0.0);
        assertEquals(2.0, generator.getTargetV(), 0.0);
        // change values in s4
        generator.setVoltageRegulatorOn(false);
        generator.setTargetP(12.1);
        generator.setTargetQ(9.2);
        generator.setTargetV(9.3);

        // remove s2
        variantManager.removeVariant("s2");

        variantManager.cloneVariant("s4", "s2b");
        variantManager.setWorkingVariant("s2b");
        // check values cloned by allocate
        assertFalse(generator.isVoltageRegulatorOn());
        assertEquals(12.1, generator.getTargetP(), 0.0);
        assertEquals(9.2, generator.getTargetQ(), 0.0);
        assertEquals(9.3, generator.getTargetV(), 0.0);

        // recheck initial variant value
        variantManager.setWorkingVariant(VariantManagerConstants.INITIAL_VARIANT_ID);
        assertTrue(generator.isVoltageRegulatorOn());
        assertEquals(15.0, generator.getTargetP(), 0.0);
        assertEquals(40.0, generator.getTargetQ(), 0.0);
        assertEquals(2.0, generator.getTargetV(), 0.0);

        // remove working variant s4
        variantManager.setWorkingVariant("s4");
        variantManager.removeVariant("s4");
        try {
            generator.getTargetP();
            fail();
        } catch (Exception ignored) {
            // ignore
        }
    }

    private Generator createGenerator(String id, EnergySource source, double maxP, double minP, double ratedS,
                                 double activePowerSetpoint, double reactivePowerSetpoint, boolean regulatorOn, double voltageSetpoint) {
        return voltageLevel.newGenerator()
                .setId(id)
                .setVoltageRegulatorOn(regulatorOn)
                .setEnergySource(source)
                .setMaxP(maxP)
                .setMinP(minP)
                .setRatedS(ratedS)
                .setTargetP(activePowerSetpoint)
                .setTargetQ(reactivePowerSetpoint)
                .setNode(1)
                .setTargetV(voltageSetpoint)
                .add();
    }
}