All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.powsybl.iidm.modification.ReplaceThreeWindingsTransformersBy3TwoWindingsTransformers Maven / Gradle / Ivy

/**
 * 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.commons.extensions.Extension;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.computation.ComputationManager;
import com.powsybl.iidm.modification.topology.NamingStrategy;
import com.powsybl.iidm.modification.util.RegulatedTerminalControllers;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

import static com.powsybl.iidm.modification.util.ModificationReports.*;
import static com.powsybl.iidm.modification.util.TransformerUtils.*;
import static com.powsybl.iidm.modification.util.TransformerUtils.copyAndAddPhaseAngleClock;

/**
 * 

This network modification is used to replace threeWindingsTransformers by 3 twoWindingsTransformers.

*

For each threeWindingsTransformer to be replaced:

*
    *
  • A new voltage level is created for the star node with nominal voltage of ratedU0.
  • *
  • Three new TwoWindingsTransformers are created, one for each leg of the removed ThreeWindingsTransformer.
  • *
  • The following attributes are copied from each leg to the new associated twoWindingsTransformer: *
      *
    • Electrical characteristics, ratioTapChangers, and phaseTapChangers. No adjustments are required.
    • *
    • Operational Loading Limits are copied to the non-star end of the twoWindingsTransformers.
    • *
    • Active and reactive power at the terminal are copied to the non-star terminal of the twoWindingsTransformer.
    • *
    *
  • *
  • Aliases: *
      *
    • Aliases for known CGMES identifiers (terminal, transformer end, ratio, and phase tap changer) are copied to the right twoWindingsTransformer after adjusting the aliasType.
    • *
    • Aliases that are not mapped are recorded in the functional log.
    • *
    *
  • *
  • Properties: *
      *
    • Star bus voltage and angle are set to the bus created for the star node.
    • *
    • The names of the operationalLimitsSet are copied to the right twoWindingsTransformer.
    • *
    • The rest of the properties of the threeWindingsTransformer are transferred to all 3 twoWindingsTransformers.
    • *
    *
  • *
  • Extensions: *
      *
    • Only IIDM extensions are copied: TransformerFortescueData, PhaseAngleClock, and TransformerToBeEstimated.
    • *
    • CGMES extensions can not be copied, as they cause circular dependencies.
    • *
    • Extensions that are not copied are recorded in the functional log.
    • *
    *
  • *
  • All the controllers using any of the threeWindingsTransformer terminals as regulated terminal are updated.
  • *
  • New and removed equipment is also recorded in the functional log.
  • *
  • Internal connections are created to manage the replacement.
  • *
* * @author Luma Zamarreño {@literal } * @author José Antonio Marqués {@literal } */ public class ReplaceThreeWindingsTransformersBy3TwoWindingsTransformers extends AbstractNetworkModification { private static final Logger LOG = LoggerFactory.getLogger(ReplaceThreeWindingsTransformersBy3TwoWindingsTransformers.class); private static final String THREE_WINDINGS_TRANSFORMER = "ThreeWindingsTransformer"; private static final String CGMES_OPERATIONAL_LIMIT_SET = "CGMES.OperationalLimitSet_"; private final List transformersToBeReplaced; /** *

Used to replace all threeWindingsTransformers by 3 twoWindingsTransformers.

*/ public ReplaceThreeWindingsTransformersBy3TwoWindingsTransformers() { this.transformersToBeReplaced = null; } /** *

Used to replace the threeWindingsTransformers included in the list by 3 twoWindingsTransformers.

*/ public ReplaceThreeWindingsTransformersBy3TwoWindingsTransformers(List transformersToBeReplaced) { this.transformersToBeReplaced = Objects.requireNonNull(transformersToBeReplaced); } @Override public String getName() { return "ReplaceThreeWindingsTransformersBy3TwoWindingsTransformers"; } @Override public void apply(Network network, NamingStrategy namingStrategy, boolean throwException, ComputationManager computationManager, ReportNode reportNode) { RegulatedTerminalControllers regulatedTerminalControllers = new RegulatedTerminalControllers(network); network.getThreeWindingsTransformerStream().filter(t3w -> isGoingToBeReplaced(transformersToBeReplaced, t3w.getId())).toList() // toList is required to create a temporary list since the threeWindingsTransformer is removed during the replacement .forEach(t3w -> replaceThreeWindingsTransformerBy3TwoWindingsTransformer(t3w, regulatedTerminalControllers, throwException, reportNode)); } private static boolean isGoingToBeReplaced(List transformersToBeReplaced, String t3wId) { return transformersToBeReplaced == null || transformersToBeReplaced.contains(t3wId); } private void replaceThreeWindingsTransformerBy3TwoWindingsTransformer(ThreeWindingsTransformer t3w, RegulatedTerminalControllers regulatedTerminalControllers, boolean throwException, ReportNode reportNode) { VoltageLevel starVoltageLevel = createStarVoltageLevel(t3w, throwException); if (starVoltageLevel == null) { return; } createTopologyInsideStarVoltageLevel(t3w, starVoltageLevel); TwoWindingsTransformer t2wLeg1 = createTwoWindingsTransformer(t3w, t3w.getLeg1(), starVoltageLevel); TwoWindingsTransformer t2wLeg2 = createTwoWindingsTransformer(t3w, t3w.getLeg2(), starVoltageLevel); TwoWindingsTransformer t2wLeg3 = createTwoWindingsTransformer(t3w, t3w.getLeg3(), starVoltageLevel); ThreeT2wsR threeT2ws = new ThreeT2wsR(t2wLeg1, t2wLeg2, t2wLeg3); regulatedTerminalControllers.replaceRegulatedTerminal(t3w.getLeg1().getTerminal(), threeT2ws.t2wOne.getTerminal1()); regulatedTerminalControllers.replaceRegulatedTerminal(t3w.getLeg2().getTerminal(), threeT2ws.t2wTwo.getTerminal1()); regulatedTerminalControllers.replaceRegulatedTerminal(t3w.getLeg3().getTerminal(), threeT2ws.t2wThree.getTerminal1()); // t2wLeg1, t2wLeg, and t2wLeg3 are not considered in regulatedTerminalControllers (created later in the model) replaceRegulatedTerminal(threeT2ws.t2wOne, t3w, threeT2ws); replaceRegulatedTerminal(threeT2ws.t2wTwo, t3w, threeT2ws); replaceRegulatedTerminal(threeT2ws.t2wThree, t3w, threeT2ws); copyTerminalActiveAndReactivePower(threeT2ws.t2wOne.getTerminal1(), t3w.getLeg1().getTerminal()); copyTerminalActiveAndReactivePower(threeT2ws.t2wTwo.getTerminal1(), t3w.getLeg2().getTerminal()); copyTerminalActiveAndReactivePower(threeT2ws.t2wThree.getTerminal1(), t3w.getLeg3().getTerminal()); List lostProperties = copyProperties(t3w, threeT2ws, starVoltageLevel); List lostExtensions = copyExtensions(t3w, threeT2ws); // copy necessary data before removing the transformer String t3wId = t3w.getId(); List t3wAliases = getAliases(t3w); t3w.remove(); // after removing the threeWindingsTransformer List lostAliases = copyAliases(t3wAliases, threeT2ws); // warnings if (!lostProperties.isEmpty()) { lostProperties.forEach(propertyName -> LOG.warn("Property '{}' of threeWindingsTransformer '{}' was not transferred", propertyName, t3wId)); } if (!lostExtensions.isEmpty()) { lostExtensions.forEach(extensionName -> LOG.warn("Extension '{}' of threeWindingsTransformer '{}' was not transferred", extensionName, t3wId)); } if (!lostAliases.isEmpty()) { lostAliases.forEach(aliasR -> LOG.warn("Alias '{}' '{}' of threeWindingsTransformer '{}' was not transferred", aliasR.alias, aliasR.aliasType, t3wId)); } // report createReportNode(reportNode, t3wId, lostProperties, lostExtensions, lostAliases, starVoltageLevel.getId(), threeT2ws); } // It is a fictitious bus, then we do not set voltage limits private VoltageLevel createStarVoltageLevel(ThreeWindingsTransformer t3w, boolean throwException) { Optional substation = t3w.getSubstation(); if (substation.isEmpty()) { logOrThrow(throwException, THREE_WINDINGS_TRANSFORMER + "'" + t3w.getId() + "' without substation"); return null; } TopologyKind topologykind = t3w.getLeg1().getTerminal().getVoltageLevel().getTopologyKind() == TopologyKind.BUS_BREAKER && t3w.getLeg2().getTerminal().getVoltageLevel().getTopologyKind() == TopologyKind.BUS_BREAKER && t3w.getLeg3().getTerminal().getVoltageLevel().getTopologyKind() == TopologyKind.BUS_BREAKER ? TopologyKind.BUS_BREAKER : TopologyKind.NODE_BREAKER; return substation.get().newVoltageLevel() .setId(t3w.getId() + "-Star-VL") .setName(t3w.getNameOrId() + "-Star-VL") .setNominalV(t3w.getRatedU0()) .setTopologyKind(topologykind) .add(); } private static void createTopologyInsideStarVoltageLevel(ThreeWindingsTransformer t3w, VoltageLevel starVoltageLevel) { if (starVoltageLevel.getTopologyKind() == TopologyKind.BUS_BREAKER) { starVoltageLevel.getBusBreakerView().newBus() .setId(t3w.getId() + "-Star-Bus") .setName(t3w.getNameOrId() + "-Star-Bus") .add(); } else { starVoltageLevel.getNodeBreakerView().newInternalConnection().setNode1(1).setNode2(0).add(); starVoltageLevel.getNodeBreakerView().newInternalConnection().setNode1(2).setNode2(0).add(); starVoltageLevel.getNodeBreakerView().newInternalConnection().setNode1(3).setNode2(0).add(); } } private static TwoWindingsTransformer createTwoWindingsTransformer(ThreeWindingsTransformer t3w, ThreeWindingsTransformer.Leg leg, VoltageLevel starVoltageLevel) { TwoWindingsTransformerAdder t2wAdder = starVoltageLevel.getSubstation().orElseThrow() .newTwoWindingsTransformer() .setEnsureIdUnicity(true) .setId(t3w.getId() + "-Leg" + leg.getSide().getNum()) .setName(t3w.getNameOrId() + "-Leg" + leg.getSide().getNum()) .setRatedU1(leg.getRatedU()) .setRatedU2(starVoltageLevel.getNominalV()) .setR(leg.getR()) .setX(leg.getX()) .setG(leg.getG()) .setB(leg.getB()) .setRatedS(leg.getRatedS()) .setVoltageLevel1(leg.getTerminal().getVoltageLevel().getId()) .setVoltageLevel2(starVoltageLevel.getId()); connect(t2wAdder, getConnectivityLegAfterCreatingInternalConnection(leg), getConnectivityStar(leg.getSide().getNum(), starVoltageLevel)); TwoWindingsTransformer t2w = t2wAdder.add(); leg.getOptionalRatioTapChanger().ifPresent(rtc -> copyAndAddRatioTapChanger(t2w.newRatioTapChanger(), rtc)); leg.getOptionalPhaseTapChanger().ifPresent(rtc -> copyAndAddPhaseTapChanger(t2w.newPhaseTapChanger(), rtc)); leg.getOperationalLimitsGroups().forEach(operationalLimitGroup -> copyOperationalLimitsGroup(t2w.newOperationalLimitsGroup1(operationalLimitGroup.getId()), operationalLimitGroup)); return t2w; } private static void connect(TwoWindingsTransformerAdder t2wAdder, ConnectivityR connectivityEnd1, ConnectivityR connectivityEnd2) { if (connectivityEnd1.node != null) { t2wAdder.setNode1(connectivityEnd1.node); } else { t2wAdder.setConnectableBus1(connectivityEnd1.connectableBus.getId()); if (connectivityEnd1.bus != null) { t2wAdder.setBus1(connectivityEnd1.bus.getId()); } } if (connectivityEnd2.node != null) { t2wAdder.setNode2(connectivityEnd2.node); } else { t2wAdder.setConnectableBus2(connectivityEnd2.connectableBus.getId()); if (connectivityEnd2.bus != null) { t2wAdder.setBus2(connectivityEnd2.bus.getId()); } } } private static ConnectivityR getConnectivityLegAfterCreatingInternalConnection(ThreeWindingsTransformer.Leg leg) { if (leg.getTerminal().getVoltageLevel().getTopologyKind() == TopologyKind.NODE_BREAKER) { int newNode = leg.getTerminal().getVoltageLevel().getNodeBreakerView().getMaximumNodeIndex() + 1; leg.getTerminal().getVoltageLevel().getNodeBreakerView() .newInternalConnection() .setNode1(leg.getTerminal().getNodeBreakerView().getNode()) .setNode2(newNode).add(); return new ConnectivityR(newNode, null, null); } else { return new ConnectivityR(null, leg.getTerminal().getBusBreakerView().getBus(), leg.getTerminal().getBusBreakerView().getConnectableBus()); } } private static ConnectivityR getConnectivityStar(int node, VoltageLevel startVoltageLevel) { if (startVoltageLevel.getTopologyKind() == TopologyKind.NODE_BREAKER) { return new ConnectivityR(node, null, null); } else { Bus bus = startVoltageLevel.getBusBreakerView().getBuses().iterator().next(); return new ConnectivityR(null, bus, bus); } } private record ConnectivityR(Integer node, Bus bus, Bus connectableBus) { } private static void replaceRegulatedTerminal(TwoWindingsTransformer t2w, ThreeWindingsTransformer t3w, ThreeT2wsR threeT2ws) { t2w.getOptionalRatioTapChanger().ifPresent(rtc -> findNewRegulatedTerminal(rtc.getRegulationTerminal(), t3w, threeT2ws).ifPresent(rtc::setRegulationTerminal)); t2w.getOptionalPhaseTapChanger().ifPresent(ptc -> findNewRegulatedTerminal(ptc.getRegulationTerminal(), t3w, threeT2ws).ifPresent(ptc::setRegulationTerminal)); } private static Optional findNewRegulatedTerminal(Terminal regulatedTerminal, ThreeWindingsTransformer t3w, ThreeT2wsR threeT2ws) { if (regulatedTerminal != null && regulatedTerminal.getConnectable().getId().equals(t3w.getId())) { return switch (regulatedTerminal.getSide()) { case ONE -> Optional.of(threeT2ws.t2wOne.getTerminal1()); case TWO -> Optional.of(threeT2ws.t2wTwo.getTerminal1()); case THREE -> Optional.of(threeT2ws.t2wThree.getTerminal1()); }; } else { return Optional.empty(); } } private static List copyProperties(ThreeWindingsTransformer t3w, ThreeT2wsR threeT2ws, VoltageLevel starVoltageLevel) { List lostProperties = new ArrayList<>(); t3w.getPropertyNames().forEach(propertyName -> { boolean copied = copyProperty(propertyName, t3w.getProperty(propertyName), threeT2ws, starVoltageLevel); if (!copied) { lostProperties.add(propertyName); } }); return lostProperties; } private static boolean copyProperty(String propertyName, String property, ThreeT2wsR threeT2ws, VoltageLevel starVoltageLevel) { boolean copied = true; if ("v".equals(propertyName)) { starVoltageLevel.getBusView().getBuses().iterator().next().setV(Double.parseDouble(property)); } else if ("angle".equals(propertyName)) { starVoltageLevel.getBusView().getBuses().iterator().next().setAngle(Double.parseDouble(property)); } else if (propertyName.startsWith(CGMES_OPERATIONAL_LIMIT_SET)) { if (threeT2ws.t2wOne.getOperationalLimitsGroups1().stream().anyMatch(operationalLimitsGroup -> propertyName.equals(CGMES_OPERATIONAL_LIMIT_SET + operationalLimitsGroup.getId()))) { threeT2ws.t2wOne.setProperty(propertyName, property); } else if (threeT2ws.t2wTwo.getOperationalLimitsGroups1().stream().anyMatch(operationalLimitsGroup -> propertyName.equals(CGMES_OPERATIONAL_LIMIT_SET + operationalLimitsGroup.getId()))) { threeT2ws.t2wTwo.setProperty(propertyName, property); } else if (threeT2ws.t2wThree.getOperationalLimitsGroups1().stream().anyMatch(operationalLimitsGroup -> propertyName.equals(CGMES_OPERATIONAL_LIMIT_SET + operationalLimitsGroup.getId()))) { threeT2ws.t2wThree.setProperty(propertyName, property); } else { copied = false; } } else { // we copy all other properties on the 3 2wt threeT2ws.t2wOne.setProperty(propertyName, property); threeT2ws.t2wTwo.setProperty(propertyName, property); threeT2ws.t2wThree.setProperty(propertyName, property); } return copied; } // TODO For now, only a few extensions are supported. But a wider mechanism should be developed to support custom extensions. private static List copyExtensions(ThreeWindingsTransformer t3w, ThreeT2wsR threeT2w) { List lostExtensions = new ArrayList<>(); t3w.getExtensions().stream().map(Extension::getName).forEach(extensionName -> { boolean copied = copyExtension(extensionName, t3w, threeT2w); if (!copied) { lostExtensions.add(extensionName); } }); return lostExtensions; } private static boolean copyExtension(String extensionName, ThreeWindingsTransformer t3w, ThreeT2wsR threeT2ws) { boolean copied = true; switch (extensionName) { case "threeWindingsTransformerFortescue" -> { ThreeWindingsTransformerFortescue extension = t3w.getExtension(ThreeWindingsTransformerFortescue.class); copyAndAddFortescue(threeT2ws.t2wOne.newExtension(TwoWindingsTransformerFortescueAdder.class), extension.getLeg1()); copyAndAddFortescue(threeT2ws.t2wTwo.newExtension(TwoWindingsTransformerFortescueAdder.class), extension.getLeg2()); copyAndAddFortescue(threeT2ws.t2wThree.newExtension(TwoWindingsTransformerFortescueAdder.class), extension.getLeg3()); } case "threeWindingsTransformerPhaseAngleClock" -> { ThreeWindingsTransformerPhaseAngleClock extension = t3w.getExtension(ThreeWindingsTransformerPhaseAngleClock.class); copyAndAddPhaseAngleClock(threeT2ws.t2wTwo.newExtension(TwoWindingsTransformerPhaseAngleClockAdder.class), extension.getPhaseAngleClockLeg2()); copyAndAddPhaseAngleClock(threeT2ws.t2wThree.newExtension(TwoWindingsTransformerPhaseAngleClockAdder.class), extension.getPhaseAngleClockLeg3()); } case "threeWindingsTransformerToBeEstimated" -> { ThreeWindingsTransformerToBeEstimated extension = t3w.getExtension(ThreeWindingsTransformerToBeEstimated.class); copyAndAddToBeEstimated(threeT2ws.t2wOne.newExtension(TwoWindingsTransformerToBeEstimatedAdder.class), extension.shouldEstimateRatioTapChanger1(), extension.shouldEstimatePhaseTapChanger1()); copyAndAddToBeEstimated(threeT2ws.t2wTwo.newExtension(TwoWindingsTransformerToBeEstimatedAdder.class), extension.shouldEstimateRatioTapChanger2(), extension.shouldEstimatePhaseTapChanger2()); copyAndAddToBeEstimated(threeT2ws.t2wThree.newExtension(TwoWindingsTransformerToBeEstimatedAdder.class), extension.shouldEstimateRatioTapChanger3(), extension.shouldEstimatePhaseTapChanger3()); } default -> copied = false; } return copied; } private static List getAliases(ThreeWindingsTransformer t3w) { return t3w.getAliases().stream().map(alias -> new AliasR(alias, t3w.getAliasType(alias).orElse(""))).toList(); } private static List copyAliases(List t3wAliases, ThreeT2wsR threeT2w) { List lostAliases = new ArrayList<>(); t3wAliases.forEach(aliasR -> { boolean copied = copyAlias(aliasR.alias, aliasR.aliasType, threeT2w); if (!copied) { lostAliases.add(aliasR); } }); return lostAliases; } private static boolean copyAlias(String alias, String aliasType, ThreeT2wsR threeT2ws) { return copyLegAlias(alias, aliasType, "1", threeT2ws.t2wOne) || copyLegAlias(alias, aliasType, "2", threeT2ws.t2wTwo) || copyLegAlias(alias, aliasType, "3", threeT2ws.t2wThree); } private static boolean copyLegAlias(String alias, String aliasType, String legEnd, TwoWindingsTransformer t2wLeg) { boolean copied = true; if (aliasType.equals("CGMES.TransformerEnd" + legEnd)) { t2wLeg.addAlias(alias, "CGMES.TransformerEnd1", true); } else if (aliasType.equals("CGMES.Terminal" + legEnd)) { t2wLeg.addAlias(alias, "CGMES.Terminal1", true); } else if (aliasType.equals("CGMES.RatioTapChanger" + legEnd)) { t2wLeg.addAlias(alias, "CGMES.RatioTapChanger1", true); } else if (aliasType.equals("CGMES.PhaseTapChanger" + legEnd)) { t2wLeg.addAlias(alias, "CGMES.PhaseTapChanger1", true); } else { copied = false; } return copied; } private record AliasR(String alias, String aliasType) { } private static void createReportNode(ReportNode reportNode, String t3wId, List lostProperties, List lostExtensions, List lostAliases, String starVoltageLevelId, ThreeT2wsR threeT2ws) { ReportNode reportNodeReplacement = replaceThreeWindingsTransformersBy3TwoWindingsTransformersReport(reportNode); removedThreeWindingsTransformerReport(reportNodeReplacement, t3wId); if (!lostProperties.isEmpty()) { String properties = String.join(",", lostProperties); lostThreeWindingsTransformerProperties(reportNodeReplacement, properties, t3wId); } if (!lostExtensions.isEmpty()) { String extensions = String.join(",", lostExtensions); lostThreeWindingsTransformerExtensions(reportNodeReplacement, extensions, t3wId); } if (!lostAliases.isEmpty()) { String aliases = lostAliases.stream().map(AliasR::alias).collect(Collectors.joining(",")); lostThreeWindingsTransformerAliases(reportNodeReplacement, aliases, t3wId); } createdVoltageLevelReport(reportNodeReplacement, starVoltageLevelId); createdTwoWindingsTransformerReport(reportNodeReplacement, threeT2ws.t2wOne.getId()); createdTwoWindingsTransformerReport(reportNodeReplacement, threeT2ws.t2wTwo.getId()); createdTwoWindingsTransformerReport(reportNodeReplacement, threeT2ws.t2wThree.getId()); } private record ThreeT2wsR(TwoWindingsTransformer t2wOne, TwoWindingsTransformer t2wTwo, TwoWindingsTransformer t2wThree) { } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy