TransformersTestUtils.java

/**
 * Copyright (c) 2024, 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.modification;

import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.*;
import com.powsybl.iidm.network.util.TwtData;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static com.powsybl.iidm.modification.util.TransformerUtils.copyAndAddPhaseTapChanger;
import static com.powsybl.iidm.modification.util.TransformerUtils.copyAndAddRatioTapChanger;

/**
 * @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
 * @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
 */

final class TransformersTestUtils {

    private TransformersTestUtils() {
    }

    static void addPhaseTapChanger(TwoWindingsTransformer t2w) {
        PhaseTapChangerAdder ptcAdder = t2w.newPhaseTapChanger();
        fillTapChangerAdder(ptcAdder, t2w.getTerminal1());
        ptcAdder.add();
    }

    static void addPhaseTapChanger(ThreeWindingsTransformer.Leg leg) {
        PhaseTapChangerAdder ptcAdder = leg.newPhaseTapChanger();
        fillTapChangerAdder(ptcAdder, leg.getTerminal());
        ptcAdder.add();
    }

    private static void fillTapChangerAdder(PhaseTapChangerAdder ptcAdder, Terminal terminal) {
        ptcAdder.setLowTapPosition(0)
                .setTapPosition(2)
                .setRegulationTerminal(terminal)
                .setRegulationValue(10.0)
                .setRegulationMode(PhaseTapChanger.RegulationMode.FIXED_TAP)
                .setTargetDeadband(0.5)
                .setRegulating(false)
                .beginStep()
                .setRho(0.99)
                .setAlpha(-2.0)
                .setR(1.01)
                .setX(1.02)
                .setG(1.03)
                .setB(1.04)
                .endStep()
                .beginStep()
                .setRho(1.00)
                .setAlpha(0.0)
                .setR(0.0)
                .setX(0.0)
                .setG(0.0)
                .setB(0.0)
                .endStep()
                .beginStep()
                .setRho(1.01)
                .setAlpha(2.0)
                .setR(0.99)
                .setX(0.98)
                .setG(0.97)
                .setB(0.96)
                .endStep()
                .add();
    }

    static void addLoadingLimitsEnd1(TwoWindingsTransformer t2w) {
        OperationalLimitsGroup summer = t2w.newOperationalLimitsGroup1("OperationalLimitsGroup-summer");
        OperationalLimitsGroup winter = t2w.newOperationalLimitsGroup1("OperationalLimitsGroup-winter");
        addSummerLoadingLimits(summer);
        addWinterLoadingLimits(winter);
    }

    static void addLoadingLimitsEnd2(TwoWindingsTransformer t2w) {
        OperationalLimitsGroup summer = t2w.newOperationalLimitsGroup2("OperationalLimitsGroup-summer-end2");
        OperationalLimitsGroup winter = t2w.newOperationalLimitsGroup2("OperationalLimitsGroup-winter-end2");
        addSummerLoadingLimits(summer);
        addWinterLoadingLimits(winter);
    }

    static void addLoadingLimits(ThreeWindingsTransformer.Leg leg) {
        OperationalLimitsGroup summer = leg.newOperationalLimitsGroup("OperationalLimitsGroup-summer");
        OperationalLimitsGroup winter = leg.newOperationalLimitsGroup("OperationalLimitsGroup-winter");
        addSummerLoadingLimits(summer);
        addWinterLoadingLimits(winter);
    }

    private static void addSummerLoadingLimits(OperationalLimitsGroup summer) {
        summer.newActivePowerLimits()
                .setPermanentLimit(100.0)
                .beginTemporaryLimit()
                .setName("TemporaryActivePowerLimit-1-summer")
                .setAcceptableDuration(2)
                .setValue(110.0)
                .endTemporaryLimit()
                .beginTemporaryLimit()
                .setName("TemporaryActivePowerLimit-2-summer")
                .setAcceptableDuration(1)
                .setValue(120.0)
                .endTemporaryLimit().add();
        summer.newApparentPowerLimits()
                .setPermanentLimit(105.0)
                .beginTemporaryLimit()
                .setName("TemporaryApparentPowerLimit-1")
                .setAcceptableDuration(2)
                .setValue(115.0)
                .endTemporaryLimit()
                .beginTemporaryLimit()
                .setName("TemporaryApparentPowerLimit-2-summer")
                .setAcceptableDuration(1)
                .setValue(125.0)
                .endTemporaryLimit().add();
        summer.newCurrentLimits()
                .setPermanentLimit(1050.0)
                .beginTemporaryLimit()
                .setName("TemporaryCurrentLimit-1-summer")
                .setAcceptableDuration(2)
                .setValue(1150.0)
                .endTemporaryLimit()
                .beginTemporaryLimit()
                .setName("TemporaryCurrentLimit-2-summer")
                .setAcceptableDuration(1)
                .setValue(1250.0)
                .endTemporaryLimit().add();
    }

    private static void addWinterLoadingLimits(OperationalLimitsGroup winter) {
        winter.newActivePowerLimits()
                .setPermanentLimit(125.0)
                .beginTemporaryLimit()
                .setName("TemporaryActivePowerLimit-1-winter")
                .setAcceptableDuration(3)
                .setValue(135.0)
                .endTemporaryLimit()
                .beginTemporaryLimit()
                .setName("TemporaryActivePowerLimit-2-winter")
                .setAcceptableDuration(2)
                .setValue(145.0)
                .endTemporaryLimit().add();
        winter.newApparentPowerLimits()
                .setPermanentLimit(130.0)
                .beginTemporaryLimit()
                .setName("TemporaryApparentPowerLimit-1-winter")
                .setAcceptableDuration(3)
                .setValue(140.0)
                .endTemporaryLimit()
                .beginTemporaryLimit()
                .setName("TemporaryApparentPowerLimit-2-winter")
                .setAcceptableDuration(2)
                .setValue(150.0)
                .endTemporaryLimit().add();
        winter.newCurrentLimits()
                .setPermanentLimit(130.0)
                .beginTemporaryLimit()
                .setName("TemporaryCurrentLimit-1-winter")
                .setAcceptableDuration(3)
                .setValue(140.0)
                .endTemporaryLimit()
                .beginTemporaryLimit()
                .setName("TemporaryCurrentLimit-2-winter")
                .setAcceptableDuration(2)
                .setValue(150.0)
                .endTemporaryLimit().add();
    }

    static boolean compareRatioTapChanger(RatioTapChanger expectedRtc, RatioTapChanger rtc) {
        String expected = ratioTapChangerToString(expectedRtc);
        String actual = ratioTapChangerToString(rtc);
        return expected.equals(actual);
    }

    private static String ratioTapChangerToString(RatioTapChanger rtc) {
        List<String> strings = new ArrayList<>();
        strings.add(String.valueOf(rtc.getLowTapPosition()));
        strings.add(String.valueOf(rtc.getTapPosition()));
        strings.add(rtc.getRegulationTerminal().getBusView().getBus().getId());
        strings.add(String.valueOf(rtc.getTargetV()));
        strings.add(String.valueOf(rtc.getRegulationValue()));
        strings.add(String.valueOf(rtc.getRegulationMode()));
        strings.add(String.valueOf(rtc.getTargetDeadband()));
        strings.add(String.valueOf(rtc.isRegulating()));
        strings.add(String.valueOf(rtc.getStepCount()));
        rtc.getAllSteps().forEach((step, rtcStep) -> {
            strings.add(String.valueOf(step));
            strings.add(String.valueOf(rtcStep.getRho()));
            strings.add(String.valueOf(rtcStep.getR()));
            strings.add(String.valueOf(rtcStep.getX()));
            strings.add(String.valueOf(rtcStep.getG()));
            strings.add(String.valueOf(rtcStep.getB()));
        });

        return String.join(",", strings);
    }

    static boolean comparePhaseTapChanger(PhaseTapChanger expectedPtc, PhaseTapChanger ptc) {
        String expected = phaseTapChangerToString(expectedPtc);
        String actual = phaseTapChangerToString(ptc);
        return expected.equals(actual);
    }

    private static String phaseTapChangerToString(PhaseTapChanger ptc) {
        List<String> strings = new ArrayList<>();
        strings.add(String.valueOf(ptc.getLowTapPosition()));
        strings.add(String.valueOf(ptc.getTapPosition()));
        strings.add(ptc.getRegulationTerminal().getBusView().getBus().getId());
        strings.add(String.valueOf(ptc.getRegulationValue()));
        strings.add(String.valueOf(ptc.getRegulationMode()));
        strings.add(String.valueOf(ptc.getTargetDeadband()));
        strings.add(String.valueOf(ptc.isRegulating()));
        strings.add(String.valueOf(ptc.getStepCount()));
        ptc.getAllSteps().forEach((step, rtcStep) -> {
            strings.add(String.valueOf(step));
            strings.add(String.valueOf(rtcStep.getRho()));
            strings.add(String.valueOf(rtcStep.getAlpha()));
            strings.add(String.valueOf(rtcStep.getR()));
            strings.add(String.valueOf(rtcStep.getX()));
            strings.add(String.valueOf(rtcStep.getG()));
            strings.add(String.valueOf(rtcStep.getB()));
        });

        return String.join(",", strings);
    }

    static boolean compareOperationalLimitsGroups(Collection<OperationalLimitsGroup> expected, Collection<OperationalLimitsGroup> actual) {
        String expectedString = operationalLimitsToString(expected);
        String actualString = operationalLimitsToString(actual);
        return expectedString.equals(actualString);
    }

    private static String operationalLimitsToString(Collection<OperationalLimitsGroup> operationalLimitsGroups) {
        List<String> strings = new ArrayList<>();
        operationalLimitsGroups.forEach(operationalLimitGroup -> {
            strings.add(operationalLimitGroup.getId());
            operationalLimitGroup.getActivePowerLimits().ifPresent(activePowerLimits -> {
                strings.add(activePowerLimits.getLimitType().name());
                addLegLimits(activePowerLimits, strings);
            });
            operationalLimitGroup.getApparentPowerLimits().ifPresent(apparentPowerLimits -> {
                strings.add(apparentPowerLimits.getLimitType().name());
                addLegLimits(apparentPowerLimits, strings);
            });
            operationalLimitGroup.getCurrentLimits().ifPresent(currentLimits -> {
                strings.add(currentLimits.getLimitType().name());
                addLegLimits(currentLimits, strings);
            });
        });
        return String.join(",", strings);
    }

    private static void addLegLimits(LoadingLimits loadingLimits, List<String> strings) {
        strings.add(String.valueOf(loadingLimits.getPermanentLimit()));
        loadingLimits.getTemporaryLimits().forEach(temporaryLimit -> {
            strings.add(temporaryLimit.getName());
            strings.add(String.valueOf(temporaryLimit.getAcceptableDuration()));
            strings.add(String.valueOf(temporaryLimit.getValue()));
            strings.add(String.valueOf(temporaryLimit.isFictitious()));
        });
    }

    static String createThreeWindingsTransformerFortescueToString(ThreeWindingsTransformer t3w) {
        List<String> strings = new ArrayList<>();
        ThreeWindingsTransformerFortescue extension = t3w.getExtension(ThreeWindingsTransformerFortescue.class);
        if (extension != null) {
            addLegFortescue(extension.getLeg1(), strings);
            addLegFortescue(extension.getLeg2(), strings);
            addLegFortescue(extension.getLeg3(), strings);
        }
        return String.join(",", strings);
    }

    private static void addLegFortescue(ThreeWindingsTransformerFortescue.LegFortescue legFortescue, List<String> strings) {
        strings.add(legFortescue.getConnectionType().name());
        strings.add(String.valueOf(legFortescue.isFreeFluxes()));
        strings.add(String.valueOf(legFortescue.getGroundingR()));
        strings.add(String.valueOf(legFortescue.getGroundingX()));
        strings.add(String.valueOf(legFortescue.getRz()));
        strings.add(String.valueOf(legFortescue.getXz()));
    }

    static String createTwoWindingsTransformerFortescueToString(TwoWindingsTransformer t2w1, TwoWindingsTransformer t2w2, TwoWindingsTransformer t2w3) {
        List<String> strings = new ArrayList<>();
        addT2wFortescue(t2w1.getExtension(TwoWindingsTransformerFortescue.class), strings);
        addT2wFortescue(t2w2.getExtension(TwoWindingsTransformerFortescue.class), strings);
        addT2wFortescue(t2w3.getExtension(TwoWindingsTransformerFortescue.class), strings);
        return String.join(",", strings);
    }

    private static void addT2wFortescue(TwoWindingsTransformerFortescue t2wFortescue, List<String> strings) {
        if (t2wFortescue != null) {
            strings.add(t2wFortescue.getConnectionType1().name());
            strings.add(String.valueOf(t2wFortescue.isFreeFluxes()));
            strings.add(String.valueOf(t2wFortescue.getGroundingR1()));
            strings.add(String.valueOf(t2wFortescue.getGroundingX1()));
            strings.add(String.valueOf(t2wFortescue.getRz()));
            strings.add(String.valueOf(t2wFortescue.getXz()));
        }
    }

    static String createThreeWindingsTransformerPhaseAngleClockToString(ThreeWindingsTransformer t3w) {
        List<String> strings = new ArrayList<>();
        ThreeWindingsTransformerPhaseAngleClock extension = t3w.getExtension(ThreeWindingsTransformerPhaseAngleClock.class);
        if (extension != null) {
            strings.add(String.valueOf(extension.getPhaseAngleClockLeg2()));
            strings.add(String.valueOf(extension.getPhaseAngleClockLeg3()));
        }
        return String.join(",", strings);
    }

    static String createTwoWindingsTransformerPhaseAngleClockToString(TwoWindingsTransformer t2w2, TwoWindingsTransformer t2w3) {
        List<String> strings = new ArrayList<>();
        TwoWindingsTransformerPhaseAngleClock t2w2Extension = t2w2.getExtension(TwoWindingsTransformerPhaseAngleClock.class);
        if (t2w2Extension != null) {
            strings.add(String.valueOf(t2w2Extension.getPhaseAngleClock()));
        }
        TwoWindingsTransformerPhaseAngleClock t2w3Extension = t2w3.getExtension(TwoWindingsTransformerPhaseAngleClock.class);
        if (t2w3Extension != null) {
            strings.add(String.valueOf(t2w3Extension.getPhaseAngleClock()));
        }
        return String.join(",", strings);
    }

    static String createThreeWindingsTransformerToBeEstimatedToString(ThreeWindingsTransformer t3w) {
        List<String> strings = new ArrayList<>();
        ThreeWindingsTransformerToBeEstimated extension = t3w.getExtension(ThreeWindingsTransformerToBeEstimated.class);
        if (extension != null) {
            strings.add(String.valueOf(extension.shouldEstimateRatioTapChanger1()));
            strings.add(String.valueOf(extension.shouldEstimatePhaseTapChanger1()));
            strings.add(String.valueOf(extension.shouldEstimateRatioTapChanger2()));
            strings.add(String.valueOf(extension.shouldEstimatePhaseTapChanger2()));
            strings.add(String.valueOf(extension.shouldEstimateRatioTapChanger3()));
            strings.add(String.valueOf(extension.shouldEstimatePhaseTapChanger3()));
        }
        return String.join(",", strings);
    }

    static String createTwoWindingsTransformerToBeEstimatedToString(TwoWindingsTransformer t2w1, TwoWindingsTransformer t2w2, TwoWindingsTransformer t2w3) {
        List<String> strings = new ArrayList<>();
        addToBeEstimated(t2w1.getExtension(TwoWindingsTransformerToBeEstimated.class), strings);
        addToBeEstimated(t2w2.getExtension(TwoWindingsTransformerToBeEstimated.class), strings);
        addToBeEstimated(t2w3.getExtension(TwoWindingsTransformerToBeEstimated.class), strings);
        return String.join(",", strings);
    }

    private static void addToBeEstimated(TwoWindingsTransformerToBeEstimated extension, List<String> strings) {
        if (extension != null) {
            strings.add(String.valueOf(extension.shouldEstimateRatioTapChanger()));
            strings.add(String.valueOf(extension.shouldEstimatePhaseTapChanger()));
        }
    }

    static void addVoltages(Bus bus1, Bus bus2, Bus bus3) {
        bus1.setV(bus1.getVoltageLevel().getNominalV() * 1.01);
        bus1.setAngle(2.0);

        bus2.setV(bus2.getVoltageLevel().getNominalV() * 0.99);
        bus2.setAngle(4.0);

        bus3.setV(bus3.getVoltageLevel().getNominalV() * 0.98);
        bus3.setAngle(3.0);
    }

    static void setStarBusVoltage(TwtData twtData, Bus starBus) {
        starBus.setV(twtData.getStarU());
        starBus.setAngle(Math.toDegrees(twtData.getStarTheta()));
    }

    static void reOrientedTwoWindingsTransformer(TwoWindingsTransformer t2w) {
        TwoWindingsTransformer t2wNotWellOriented = t2w.getTerminal1().getVoltageLevel().getSubstation().orElseThrow().newTwoWindingsTransformer()
                .setId(t2w.getId() + "-" + "notWellOriented")
                .setName(t2w.getNameOrId() + "-" + "notWellOriented")
                .setRatedU1(t2w.getRatedU2())
                .setRatedU2(t2w.getRatedU1())
                .setR(t2w.getR())
                .setX(t2w.getX())
                .setG(t2w.getG())
                .setB(t2w.getB())
                .setRatedS(t2w.getRatedS())
                .setVoltageLevel1(t2w.getTerminal2().getVoltageLevel().getId())
                .setConnectableBus1(t2w.getTerminal2().getBusBreakerView().getBus().getId())
                .setBus1(t2w.getTerminal2().getBusBreakerView().getBus().getId())
                .setVoltageLevel2(t2w.getTerminal1().getVoltageLevel().getId())
                .setConnectableBus2(t2w.getTerminal1().getBusBreakerView().getBus().getId())
                .setBus2(t2w.getTerminal1().getBusBreakerView().getBus().getId())
                .add();

        copyAndAddRatioTapChanger(t2wNotWellOriented.newRatioTapChanger(), t2w.getRatioTapChanger());
        copyAndAddPhaseTapChanger(t2wNotWellOriented.newPhaseTapChanger(), t2w.getPhaseTapChanger());

        t2w.remove();
    }

    static Network createThreeWindingsTransformerNodeBreakerNetwork() {
        Network network = NetworkFactory.findDefault().createNetwork("three-windings-transformer-nodeBreaker", "test");
        network.setCaseDate(ZonedDateTime.parse("2018-03-05T13:30:30.486+01:00"));
        Substation substation = network.newSubstation()
                .setId("SUBSTATION")
                .setCountry(Country.FR)
                .add();
        VoltageLevel vl1 = substation.newVoltageLevel()
                .setId("VL_132")
                .setNominalV(132.0)
                .setTopologyKind(TopologyKind.NODE_BREAKER)
                .add();
        VoltageLevel vl2 = substation.newVoltageLevel()
                .setId("VL_33")
                .setNominalV(33.0)
                .setTopologyKind(TopologyKind.NODE_BREAKER)
                .add();
        VoltageLevel vl3 = substation.newVoltageLevel()
                .setId("VL_11")
                .setNominalV(11.0)
                .setTopologyKind(TopologyKind.NODE_BREAKER)
                .add();

        substation.newThreeWindingsTransformer()
                .setId("3WT")
                .setRatedU0(132.0)
                .newLeg1()
                .setR(17.424)
                .setX(1.7424)
                .setG(0.00573921028466483)
                .setB(0.000573921028466483)
                .setRatedU(132.0)
                .setVoltageLevel(vl1.getId())
                .setNode(1)
                .add()
                .newLeg2()
                .setR(1.089)
                .setX(0.1089)
                .setG(0.0)
                .setB(0.0)
                .setRatedU(33.0)
                .setVoltageLevel(vl2.getId())
                .setNode(1)
                .add()
                .newLeg3()
                .setR(0.121)
                .setX(0.0121)
                .setG(0.0)
                .setB(0.0)
                .setRatedU(11.0)
                .setVoltageLevel(vl3.getId())
                .setNode(1)
                .add()
                .add();
        return network;
    }

    static void addExtensions(ThreeWindingsTransformer t3w) {
        t3w.newExtension(ThreeWindingsTransformerFortescueAdder.class).leg1()
                .withConnectionType(WindingConnectionType.Y)
                .withFreeFluxes(false)
                .withGroundingR(0.1)
                .withGroundingX(0.11)
                .withRz(0.12)
                .withXz(0.121)
                .leg2().withConnectionType(WindingConnectionType.Y)
                .withFreeFluxes(false)
                .withGroundingR(0.2)
                .withGroundingX(0.21)
                .withRz(0.22)
                .withXz(0.221)
                .leg3()
                .withConnectionType(WindingConnectionType.DELTA)
                .withFreeFluxes(true)
                .withGroundingR(0.3)
                .withGroundingX(0.31)
                .withRz(0.32)
                .withXz(0.321)
                .add();
        t3w.newExtension(ThreeWindingsTransformerPhaseAngleClockAdder.class)
                .withPhaseAngleClockLeg2(2)
                .withPhaseAngleClockLeg3(6)
                .add();
        t3w.newExtension(ThreeWindingsTransformerToBeEstimatedAdder.class)
                .withRatioTapChanger1Status(true)
                .withPhaseTapChanger1Status(true)
                .withRatioTapChanger2Status(true)
                .withPhaseTapChanger2Status(true)
                .withRatioTapChanger3Status(true)
                .withPhaseTapChanger3Status(true)
                .add();
    }

    static void addExtensions(TwoWindingsTransformer t2w, int diferenceFactor) {
        t2w.newExtension(TwoWindingsTransformerFortescueAdder.class)
                .withConnectionType1(WindingConnectionType.Y)
                .withFreeFluxes(false)
                .withGroundingR1(0.1 * diferenceFactor)
                .withGroundingX1(0.11 * diferenceFactor)
                .withRz(0.12 * diferenceFactor)
                .withXz(0.121 * diferenceFactor)
                .withConnectionType2(WindingConnectionType.Y)
                .withGroundingR1(0.0)
                .withGroundingX1(0.0)
                .add();
        t2w.newExtension(TwoWindingsTransformerPhaseAngleClockAdder.class)
                .withPhaseAngleClock(2 * diferenceFactor)
                .add();
        t2w.newExtension(TwoWindingsTransformerToBeEstimatedAdder.class)
                .withRatioTapChangerStatus(true)
                .withPhaseTapChangerStatus(true)
                .add();
    }
}