TransformerConverter.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.psse.converter;
import java.util.*;
import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import org.apache.commons.math3.complex.Complex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.powsybl.iidm.network.ThreeWindingsTransformer.Leg;
import com.powsybl.iidm.network.RatioTapChanger;
import com.powsybl.iidm.network.util.ContainersMapping;
import com.powsybl.psse.converter.PsseImporter.PerUnitContext;
import com.powsybl.psse.model.PsseException;
import com.powsybl.psse.model.PsseVersion;
import com.powsybl.psse.model.pf.PsseBus;
import com.powsybl.psse.model.pf.PssePowerFlowModel;
import com.powsybl.psse.model.pf.PsseTransformer;
import com.powsybl.psse.model.pf.PsseTransformerWinding;
import static com.powsybl.psse.converter.AbstractConverter.PsseEquipmentType.*;
import static com.powsybl.psse.model.PsseVersion.Major.V35;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
* @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
*/
class TransformerConverter extends AbstractConverter {
private static final double TOLERANCE = 0.00001;
TransformerConverter(PsseTransformer psseTransformer, ContainersMapping containersMapping,
PerUnitContext perUnitContext, Network network, Map<Integer, PsseBus> busNumToPsseBus, double sbase,
PsseVersion version, NodeBreakerImport nodeBreakerImport) {
super(containersMapping, network);
this.psseTransformer = Objects.requireNonNull(psseTransformer);
this.busNumToPsseBus = Objects.requireNonNull(busNumToPsseBus);
this.sbase = sbase;
this.perUnitContext = Objects.requireNonNull(perUnitContext);
this.version = Objects.requireNonNull(version);
this.nodeBreakerImport = Objects.requireNonNull(nodeBreakerImport);
}
void create() {
if (psseTransformer.getK() == 0) {
createTwoWindingsTransformer();
} else {
createThreeWindingsTransformer();
}
}
private void createTwoWindingsTransformer() {
if (!getContainersMapping().isBusDefined(psseTransformer.getI()) || !getContainersMapping().isBusDefined(psseTransformer.getJ())) {
return;
}
String id = getTransformerId(psseTransformer.getI(), psseTransformer.getJ(), psseTransformer.getCkt());
String voltageLevel1Id = getContainersMapping().getVoltageLevelId(psseTransformer.getI());
VoltageLevel voltageLevel1 = getNetwork().getVoltageLevel(voltageLevel1Id);
double baskv1 = busNumToPsseBus.get(psseTransformer.getI()).getBaskv();
String voltageLevel2Id = getContainersMapping().getVoltageLevelId(psseTransformer.getJ());
VoltageLevel voltageLevel2 = getNetwork().getVoltageLevel(voltageLevel2Id);
double baskv2 = busNumToPsseBus.get(psseTransformer.getJ()).getBaskv();
double sbase12 = psseTransformer.getSbase12();
double nomV1 = getNomV(psseTransformer.getWinding1(), voltageLevel1);
double nomV2 = getNomV(psseTransformer.getWinding2(), voltageLevel2);
Complex z = defineImpedanceBetweenWindings(psseTransformer.getR12(), psseTransformer.getX12(), sbase, sbase12, psseTransformer.getCz());
// Handling terminal ratios
ComplexRatio w1 = defineComplexRatio(psseTransformer.getWinding1().getWindv(), psseTransformer.getWinding1().getAng(), baskv1, nomV1, psseTransformer.getCw());
double w2 = defineRatio(psseTransformer.getWinding2().getWindv(), baskv2, nomV2, psseTransformer.getCw());
TapChanger tapChanger = defineTapChanger(w1, psseTransformer.getWinding1(), baskv1, nomV1, psseTransformer.getCw());
// Handling magnetizing admittance Gm and Bm
Complex ysh = defineShuntAdmittance(id, psseTransformer.getMag1(), psseTransformer.getMag2(), sbase, sbase12, baskv1, nomV1, psseTransformer.getCm());
// To engineering units
z = impedanceToEngineeringUnits(z, voltageLevel2.getNominalV(), perUnitContext.sb());
ysh = admittanceToEngineeringUnits(ysh, voltageLevel2.getNominalV(), perUnitContext.sb());
// move w2 to side 1
z = impedanceAdjustmentAfterMovingRatio(z, w2);
TapChanger tapChangerAdjustedRatio = tapChangerAdjustmentAfterMovingRatio(tapChanger, w2);
// move ysh between w1 and z
// Ysh_eu = Ysh_pu * sbase / (vn1 *vn1) Convert Ysh per unit to engineering units
// Ysh_eu_moved = Ysh_eu * (ratedU1 / ratedU2) * (ratedU1 / ratedU2) Apply the structural ratio when moving
// Ysh_eu_moved = Ysh_eu * sbase / (vn2 * vn2) as ratedU1 = vn1 and ratedU2 = vn2
// As vn2 is used to convert to eu, only the ratio remains to be applied
TapChanger tapChangerAdjustedYsh = tapChangerAdjustmentAfterMovingShuntAdmittanceBetweenRatioAndTransmissionImpedance(tapChangerAdjustedRatio);
TwoWindingsTransformerAdder adder = voltageLevel2.getSubstation()
.orElseThrow(() -> new PowsyblException("Substation null! Transformer must be within a substation"))
.newTwoWindingsTransformer()
.setId(id)
.setEnsureIdUnicity(true)
.setVoltageLevel1(voltageLevel1Id)
.setVoltageLevel2(voltageLevel2Id)
.setRatedU1(voltageLevel1.getNominalV())
.setRatedU2(voltageLevel2.getNominalV())
.setR(z.getReal())
.setX(z.getImaginary())
.setG(ysh.getReal())
.setB(ysh.getImaginary());
String equipmentId = getNodeBreakerEquipmentId(PSSE_TWO_WINDING, psseTransformer.getI(), psseTransformer.getJ(), psseTransformer.getCkt());
OptionalInt node1 = nodeBreakerImport.getNode(getNodeBreakerEquipmentIdBus(equipmentId, psseTransformer.getI()));
if (node1.isPresent()) {
adder.setNode1(node1.getAsInt());
} else {
String bus1Id = getBusId(psseTransformer.getI());
adder.setConnectableBus1(bus1Id);
adder.setBus1(psseTransformer.getStat() == 1 ? bus1Id : null);
}
OptionalInt node2 = nodeBreakerImport.getNode(getNodeBreakerEquipmentIdBus(equipmentId, psseTransformer.getJ()));
if (node2.isPresent()) {
adder.setNode2(node2.getAsInt());
} else {
String bus2Id = getBusId(psseTransformer.getJ());
adder.setConnectableBus2(bus2Id);
adder.setBus2(psseTransformer.getStat() == 1 ? bus2Id : null);
}
TwoWindingsTransformer twt = adder.add();
tapChangerToIidm(tapChangerAdjustedYsh, twt);
defineOperationalLimits(twt, voltageLevel1.getNominalV(), voltageLevel2.getNominalV());
}
private void createThreeWindingsTransformer() {
if (!getContainersMapping().isBusDefined(psseTransformer.getI()) || !getContainersMapping().isBusDefined(psseTransformer.getJ()) || !getContainersMapping().isBusDefined(psseTransformer.getK())) {
return;
}
String id = getTransformerId(psseTransformer.getI(), psseTransformer.getJ(), psseTransformer.getK(), psseTransformer.getCkt());
String bus1Id = getBusId(psseTransformer.getI());
String voltageLevel1Id = getContainersMapping().getVoltageLevelId(psseTransformer.getI());
VoltageLevel voltageLevel1 = getNetwork().getVoltageLevel(voltageLevel1Id);
double baskv1 = busNumToPsseBus.get(psseTransformer.getI()).getBaskv();
String bus2Id = getBusId(psseTransformer.getJ());
String voltageLevel2Id = getContainersMapping().getVoltageLevelId(psseTransformer.getJ());
VoltageLevel voltageLevel2 = getNetwork().getVoltageLevel(voltageLevel2Id);
double baskv2 = busNumToPsseBus.get(psseTransformer.getJ()).getBaskv();
String bus3Id = getBusId(psseTransformer.getK());
String voltageLevel3Id = getContainersMapping().getVoltageLevelId(psseTransformer.getK());
VoltageLevel voltageLevel3 = getNetwork().getVoltageLevel(voltageLevel3Id);
double baskv3 = busNumToPsseBus.get(psseTransformer.getK()).getBaskv();
double sbase12 = psseTransformer.getSbase12();
double sbase23 = psseTransformer.getSbase23();
double sbase31 = psseTransformer.getSbase31();
double nomV1 = getNomV(psseTransformer.getWinding1(), voltageLevel1);
double nomV2 = getNomV(psseTransformer.getWinding2(), voltageLevel2);
double nomV3 = getNomV(psseTransformer.getWinding3(), voltageLevel3);
Complex z12 = defineImpedanceBetweenWindings(psseTransformer.getR12(), psseTransformer.getX12(), sbase, sbase12, psseTransformer.getCz());
Complex z23 = defineImpedanceBetweenWindings(psseTransformer.getR23(), psseTransformer.getX23(), sbase, sbase23, psseTransformer.getCz());
Complex z31 = defineImpedanceBetweenWindings(psseTransformer.getR31(), psseTransformer.getX31(), sbase, sbase31, psseTransformer.getCz());
// Transform impedances between windings to star
Complex z1 = z12.add(z31).subtract(z23).multiply(0.5);
Complex z2 = z12.add(z23).subtract(z31).multiply(0.5);
Complex z3 = z23.add(z31).subtract(z12).multiply(0.5);
// Handling terminal ratios
ComplexRatio w1 = defineComplexRatio(psseTransformer.getWinding1().getWindv(), psseTransformer.getWinding1().getAng(), baskv1, nomV1, psseTransformer.getCw());
ComplexRatio w2 = defineComplexRatio(psseTransformer.getWinding2().getWindv(), psseTransformer.getWinding2().getAng(), baskv2, nomV2, psseTransformer.getCw());
ComplexRatio w3 = defineComplexRatio(psseTransformer.getWinding3().getWindv(), psseTransformer.getWinding3().getAng(), baskv3, nomV3, psseTransformer.getCw());
TapChanger tapChanger1 = defineTapChanger(w1, psseTransformer.getWinding1(), baskv1, nomV1, psseTransformer.getCw());
TapChanger tapChanger2 = defineTapChanger(w2, psseTransformer.getWinding2(), baskv2, nomV2, psseTransformer.getCw());
TapChanger tapChanger3 = defineTapChanger(w3, psseTransformer.getWinding3(), baskv3, nomV3, psseTransformer.getCw());
// Handling magnetizing admittance Gm and Bm
Complex ysh = defineShuntAdmittance(id, psseTransformer.getMag1(), psseTransformer.getMag2(), sbase, sbase12, baskv1, nomV1, psseTransformer.getCm());
// set a voltage base at star node with the associated Zbase
double v0 = 1.0;
// To engineering units
z1 = impedanceToEngineeringUnits(z1, v0, perUnitContext.sb());
z2 = impedanceToEngineeringUnits(z2, v0, perUnitContext.sb());
z3 = impedanceToEngineeringUnits(z3, v0, perUnitContext.sb());
ysh = admittanceToEngineeringUnits(ysh, v0, perUnitContext.sb());
// move ysh between w1 and z
TapChanger tapChanger1AdjustedYsh = tapChangerAdjustmentAfterMovingShuntAdmittanceBetweenRatioAndTransmissionImpedance(tapChanger1);
ThreeWindingsTransformerAdder adder = voltageLevel1.getSubstation()
.orElseThrow(() -> new PowsyblException("Substation null! Transformer must be within a substation"))
.newThreeWindingsTransformer()
.setRatedU0(v0)
.setEnsureIdUnicity(true)
.setId(id);
ThreeWindingsTransformerAdder.LegAdder legAdder1 = adder
.newLeg1()
.setR(z1.getReal())
.setX(z1.getImaginary())
.setG(ysh.getReal())
.setB(ysh.getImaginary())
.setRatedU(voltageLevel1.getNominalV())
.setVoltageLevel(voltageLevel1Id);
ThreeWindingsTransformerAdder.LegAdder legAdder2 = adder
.newLeg2()
.setR(z2.getReal())
.setX(z2.getImaginary())
.setG(0)
.setB(0)
.setRatedU(voltageLevel2.getNominalV())
.setVoltageLevel(voltageLevel2Id);
ThreeWindingsTransformerAdder.LegAdder legAdder3 = adder
.newLeg3()
.setR(z3.getReal())
.setX(z3.getImaginary())
.setG(0)
.setB(0)
.setRatedU(voltageLevel3.getNominalV())
.setVoltageLevel(voltageLevel3Id);
String equipmentId = getNodeBreakerEquipmentId(PSSE_THREE_WINDING, psseTransformer.getI(), psseTransformer.getJ(), psseTransformer.getK(), psseTransformer.getCkt());
legConnectivity(legAdder1, equipmentId, psseTransformer.getI(), bus1Id, leg1IsConnected());
legAdder1.add();
legConnectivity(legAdder2, equipmentId, psseTransformer.getJ(), bus2Id, leg2IsConnected());
legAdder2.add();
legConnectivity(legAdder3, equipmentId, psseTransformer.getK(), bus3Id, leg3IsConnected());
legAdder3.add();
ThreeWindingsTransformer twt = adder.add();
twt.setProperty("v", Double.toString(psseTransformer.getVmstar() * v0));
twt.setProperty("angle", Double.toString(psseTransformer.getAnstar()));
tapChangersToIidm(tapChanger1AdjustedYsh, tapChanger2, tapChanger3, twt);
defineOperationalLimits(twt, voltageLevel1.getNominalV(), voltageLevel2.getNominalV(), voltageLevel3.getNominalV());
}
private void legConnectivity(ThreeWindingsTransformerAdder.LegAdder legAdder, String equipmentId, int bus, String busId, boolean isLegConnected) {
OptionalInt node1 = nodeBreakerImport.getNode(getNodeBreakerEquipmentIdBus(equipmentId, bus));
if (node1.isPresent()) {
legAdder.setNode(node1.getAsInt());
} else {
legAdder.setConnectableBus(busId);
legAdder.setBus(isLegConnected ? busId : null);
}
}
private static double getNomV(PsseTransformerWinding winding, VoltageLevel voltageLevel) {
double nomV = winding.getNomv();
if (nomV == 0.0) {
nomV = voltageLevel.getNominalV();
}
return nomV;
}
private boolean leg1IsConnected() {
return psseTransformer.getStat() == 1 || psseTransformer.getStat() == 2 || psseTransformer.getStat() == 3;
}
private boolean leg2IsConnected() {
return psseTransformer.getStat() == 1 || psseTransformer.getStat() == 3 || psseTransformer.getStat() == 4;
}
private boolean leg3IsConnected() {
return psseTransformer.getStat() == 1 || psseTransformer.getStat() == 2 || psseTransformer.getStat() == 4;
}
private static Complex defineImpedanceBetweenWindings(double r, double x, double sbase, double windingSbase, int cz) {
double rw;
double xw;
switch (cz) {
case 1 -> {
rw = r;
xw = x;
}
case 2 -> {
// change to right Sbase pu
rw = r * sbase / windingSbase;
xw = x * sbase / windingSbase;
}
case 3 -> {
// convert load loss power and current into pu impedances
rw = r / windingSbase / 1000000;
xw = Math.sqrt(x * x - rw * rw);
rw = rw * sbase / windingSbase;
xw = xw * sbase / windingSbase;
}
default -> throw new PsseException("Unexpected CZ = " + cz);
}
return new Complex(rw, xw);
}
private static Complex defineShuntAdmittance(String id, double magG, double magB, double sbase, double windingSbase,
double baskv, double nomV, int cm) {
double g;
double b;
switch (cm) {
case 1 -> {
// g and b represent the values of the magnetizing admittance at the i end in pu at 1/Zb1 base where Zb1 = Vb1*Vb1/Sb1
// Vb1 is the bus i voltage base (BASKV) and Sb1 is the system MVA base which is SBASE
g = magG;
b = magB;
}
case 2 -> {
g = magG / (1000000 * sbase) * (baskv / nomV) * (baskv / nomV);
double y = magB * (windingSbase / sbase) * (baskv / nomV) * (baskv / nomV);
double b2 = y * y - g * g;
if (b2 >= 0) {
b = -Math.sqrt(b2);
} else {
b = 0.0;
LOGGER.warn("Magnetizing susceptance of Transformer ({}) set to 0 because admittance module is ({}) and conductance is ({}) ", id, y, g);
}
}
default -> throw new PsseException("Unexpected CM = " + cm);
}
return new Complex(g, b);
}
private static ComplexRatio defineComplexRatio(double windV, double ang, double baskv, double nomV, int cw) {
return new ComplexRatio(defineRatio(windV, baskv, nomV, cw), ang);
}
private static double defineRatio(double windV, double baskv, double nomV, int cw) {
return switch (cw) {
case 1 -> windV;
case 2 -> windV / baskv;
case 3 -> windV * nomV / baskv;
default -> throw new PsseException("Unexpected CW = " + cw);
};
}
private static double defineWindV(double ratio, double baskv, double nomV, int cw) {
return switch (cw) {
case 1 -> ratio;
case 2 -> ratio * baskv;
case 3 -> ratio * baskv / nomV;
default -> throw new PsseException("Unexpected CW = " + cw);
};
}
private static TapChanger defineTapChanger(ComplexRatio complexRatio, PsseTransformerWinding winding, double baskv,
double nomv, int cw) {
TapChanger tapChanger = defineRawTapChanger(complexRatio, winding.getRma(), winding.getRmi(),
winding.getNtp(), baskv, nomv, cw, winding.getCod());
tapChanger.setTapPosition(defineTapPositionAndAdjustTapChangerToCurrentRatio(complexRatio, tapChanger));
return tapChanger;
}
private static TapChanger defineRawTapChanger(ComplexRatio complexRatio, double rma, double rmi,
int ntp, double baskv, double nomv, int cw, int cod) {
TapChanger tapChanger = new TapChanger();
if (ntp <= 1) {
tapChanger.getSteps().add(new TapChangerStep(complexRatio.getRatio(), complexRatio.getAngle()));
return tapChanger;
}
// RatioTapChanger
if (complexRatio.getAngle() == 0.0 && (cod == 1 || cod == 2)) {
double stepRatioIncrement = (rma - rmi) / (ntp - 1);
for (int i = 0; i < ntp; i++) {
double ratio = defineRatio(rmi + stepRatioIncrement * i, baskv, nomv, cw);
tapChanger.getSteps().add(new TapChangerStep(ratio, complexRatio.getAngle()));
}
return tapChanger;
} else if (cod == 3) { // PhaseTapChanger
double stepAngleIncrement = (rma - rmi) / (ntp - 1);
for (int i = 0; i < ntp; i++) {
double angle = rmi + stepAngleIncrement * i;
tapChanger.getSteps().add(new TapChangerStep(complexRatio.getRatio(), angle));
}
return tapChanger;
} else {
tapChanger.getSteps().add(new TapChangerStep(complexRatio.getRatio(), complexRatio.getAngle()));
return tapChanger;
}
}
private static int defineTapPositionAndAdjustTapChangerToCurrentRatio(ComplexRatio complexRatio, TapChanger tapChanger) {
List<TapChangerStep> steps = tapChanger.getSteps();
for (int i = 0; i < steps.size(); i++) {
TapChangerStep step = steps.get(i);
double distanceRatio = distance(step.getRatio(), complexRatio.getRatio());
double distanceAngle = distance(step.getAngle(), complexRatio.getAngle());
if (distanceRatio == 0.0 && distanceAngle == 0.0) {
return i;
}
if (distanceAngle > 0.0
|| distanceAngle == 0.0 && distanceRatio > 0.0) {
tapChanger.getSteps().add(i, new TapChangerStep(complexRatio.getRatio(), complexRatio.getAngle()));
return i;
}
}
tapChanger.getSteps().add(new TapChangerStep(complexRatio.getRatio(), complexRatio.getAngle()));
return tapChanger.getSteps().size() - 1;
}
private static double distance(double stepValue, double currentValue) {
double distance = stepValue - currentValue;
if (Math.abs(distance) <= TOLERANCE) {
return 0.0;
}
return distance;
}
private static void tapChangerToIidm(TapChanger tapChanger, TwoWindingsTransformer twt) {
if (isPhaseTapChanger(tapChanger)) {
PhaseTapChangerAdder ptc = twt.newPhaseTapChanger();
tapChangerToPhaseTapChanger(tapChanger, ptc);
} else if (isRatioTapChanger(tapChanger)) {
RatioTapChangerAdder rtc = twt.newRatioTapChanger();
tapChangerToRatioTapChanger(tapChanger, rtc);
}
}
private static void tapChangersToIidm(TapChanger tapChanger1, TapChanger tapChanger2, TapChanger tapChanger3, ThreeWindingsTransformer twt) {
tapChangerToIidmLeg(tapChanger1, twt.getLeg1());
tapChangerToIidmLeg(tapChanger2, twt.getLeg2());
tapChangerToIidmLeg(tapChanger3, twt.getLeg3());
}
private static void tapChangerToIidmLeg(TapChanger tapChanger, Leg leg) {
if (isPhaseTapChanger(tapChanger)) {
PhaseTapChangerAdder ptc = leg.newPhaseTapChanger();
tapChangerToPhaseTapChanger(tapChanger, ptc);
} else if (isRatioTapChanger(tapChanger)) {
RatioTapChangerAdder rtc = leg.newRatioTapChanger();
tapChangerToRatioTapChanger(tapChanger, rtc);
}
}
private static boolean isPhaseTapChanger(TapChanger tapChanger) {
return tapChanger.getSteps().stream().anyMatch(step -> step.getAngle() != 0.0);
}
private static boolean isRatioTapChanger(TapChanger tapChanger) {
return tapChanger.getSteps().stream().anyMatch(step -> step.getRatio() != 1.0);
}
private static void tapChangerToRatioTapChanger(TapChanger tapChanger, RatioTapChangerAdder rtc) {
rtc.setLoadTapChangingCapabilities(false)
.setLowTapPosition(0)
.setTapPosition(tapChanger.getTapPosition());
tapChanger.getSteps().forEach(step ->
rtc.beginStep()
.setRho(1 / step.getRatio())
.setR(step.getR())
.setX(step.getX())
.setB(step.getB1())
.setG(step.getG1())
.endStep());
rtc.add();
}
private static void tapChangerToPhaseTapChanger(TapChanger tapChanger, PhaseTapChangerAdder ptc) {
ptc.setLowTapPosition(0)
.setTapPosition(tapChanger.getTapPosition());
tapChanger.getSteps().forEach(step ->
ptc.beginStep()
.setRho(1 / step.getRatio())
.setAlpha(-step.getAngle())
.setR(step.getR())
.setX(step.getX())
.setB(step.getB1())
.setG(step.getG1())
.endStep());
ptc.setRegulating(false).setRegulationMode(PhaseTapChanger.RegulationMode.FIXED_TAP).add();
}
private static Complex impedanceAdjustmentAfterMovingRatio(Complex impedance, double a) {
return impedance.multiply(a * a);
}
private static double admittanceAdjustmentAfterMovingItBetweenRatioAndTransmissionImpedance(double admittance, Complex a) {
return admittance * a.abs() * a.abs();
}
private TapChanger tapChangerAdjustmentAfterMovingRatio(TapChanger tapChanger, double a) {
tapChanger.getSteps().forEach(step -> step.setRatio(step.getRatio() / a));
return tapChanger;
}
private TapChanger tapChangerAdjustmentAfterMovingShuntAdmittanceBetweenRatioAndTransmissionImpedance(TapChanger tapChanger) {
tapChanger.getSteps().forEach(step -> {
Complex a = new Complex(step.getRatio() * Math.cos(Math.toRadians(step.getAngle())), step.getRatio() * Math.sin(Math.toRadians(step.getAngle())));
step.setG1(100 * (admittanceAdjustmentAfterMovingItBetweenRatioAndTransmissionImpedance(1 + step.getG1() / 100, a) - 1));
step.setB1(100 * (admittanceAdjustmentAfterMovingItBetweenRatioAndTransmissionImpedance(1 + step.getB1() / 100, a) - 1));
});
return tapChanger;
}
private void defineOperationalLimits(TwoWindingsTransformer twt, double vnom1, double vnom2) {
double rateMva = getRateWinding1();
double currentLimit1 = rateMva / (Math.sqrt(3.0) * vnom1);
double currentLimit2 = rateMva / (Math.sqrt(3.0) * vnom2);
// CurrentPermanentLimit in A
if (currentLimit1 > 0) {
CurrentLimitsAdder currentLimitFrom = twt.newCurrentLimits1();
currentLimitFrom.setPermanentLimit(currentLimit1 * 1000);
currentLimitFrom.add();
}
if (currentLimit2 > 0) {
CurrentLimitsAdder currentLimitTo = twt.newCurrentLimits2();
currentLimitTo.setPermanentLimit(currentLimit2 * 1000);
currentLimitTo.add();
}
}
private void defineOperationalLimits(ThreeWindingsTransformer twt, double vnom1, double vnom2, double vnom3) {
double rateMva1 = getRateWinding1();
double rateMva2 = getRateWinding2();
double rateMva3 = getRateWinding3();
double currentLimit1 = rateMva1 / (Math.sqrt(3.0) * vnom1);
double currentLimit2 = rateMva2 / (Math.sqrt(3.0) * vnom2);
double currentLimit3 = rateMva3 / (Math.sqrt(3.0) * vnom3);
// CurrentPermanentLimit in A
if (currentLimit1 > 0) {
CurrentLimitsAdder currentLimitFrom = twt.getLeg1().newCurrentLimits();
currentLimitFrom.setPermanentLimit(currentLimit1 * 1000);
currentLimitFrom.add();
}
if (currentLimit2 > 0) {
CurrentLimitsAdder currentLimitFrom = twt.getLeg2().newCurrentLimits();
currentLimitFrom.setPermanentLimit(currentLimit2 * 1000);
currentLimitFrom.add();
}
if (currentLimit3 > 0) {
CurrentLimitsAdder currentLimitFrom = twt.getLeg3().newCurrentLimits();
currentLimitFrom.setPermanentLimit(currentLimit3 * 1000);
currentLimitFrom.add();
}
}
private double getRateWinding1() {
double rateMva;
if (version.major() == V35) {
rateMva = psseTransformer.getWinding1Rates().getRate1();
} else {
rateMva = psseTransformer.getWinding1Rates().getRatea();
}
return rateMva;
}
private double getRateWinding2() {
double rateMva;
if (version.major() == V35) {
rateMva = psseTransformer.getWinding2Rates().getRate1();
} else {
rateMva = psseTransformer.getWinding2Rates().getRatea();
}
return rateMva;
}
private double getRateWinding3() {
double rateMva;
if (version.major() == V35) {
rateMva = psseTransformer.getWinding3Rates().getRate1();
} else {
rateMva = psseTransformer.getWinding3Rates().getRatea();
}
return rateMva;
}
static class TapChanger {
int tapPosition;
List<TapChangerStep> steps;
TapChanger() {
steps = new ArrayList<>();
}
void setTapPosition(int tapPosition) {
this.tapPosition = tapPosition;
}
int getTapPosition() {
return tapPosition;
}
List<TapChangerStep> getSteps() {
return steps;
}
}
// angle in degrees
static class TapChangerStep {
double ratio;
double angle;
double r;
double x;
double g1;
double b1;
TapChangerStep(double ratio, double angle) {
this.ratio = ratio;
this.angle = angle;
this.r = 0.0;
this.x = 0.0;
this.g1 = 0.0;
this.b1 = 0.0;
}
TapChangerStep(double ratio, double angle, double r, double x, double g1, double b1) {
this.ratio = ratio;
this.angle = angle;
this.r = r;
this.x = x;
this.g1 = g1;
this.b1 = b1;
}
void setRatio(double ratio) {
this.ratio = ratio;
}
double getRatio() {
return ratio;
}
void setAngle(double angle) {
this.angle = angle;
}
double getAngle() {
return angle;
}
double getR() {
return r;
}
double getX() {
return x;
}
void setG1(double g1) {
this.g1 = g1;
}
double getG1() {
return g1;
}
void setB1(double b1) {
this.b1 = b1;
}
double getB1() {
return b1;
}
}
// angle in degrees
static class ComplexRatio {
double ratio;
double angle;
ComplexRatio(double ratio, double angle) {
this.ratio = ratio;
this.angle = angle;
}
double getRatio() {
return ratio;
}
double getAngle() {
return angle;
}
}
public void addControl() {
if (isTwoWindingsTransformer(psseTransformer)) {
addControlTwoWindingsTransformer();
} else {
addControlThreeWindingsTransformer();
}
}
private static boolean isTwoWindingsTransformer(PsseTransformer psseTransformer) {
return psseTransformer.getK() == 0;
}
private void addControlTwoWindingsTransformer() {
String id = getTransformerId(psseTransformer.getI(), psseTransformer.getJ(), psseTransformer.getCkt());
TwoWindingsTransformer twt = getNetwork().getTwoWindingsTransformer(id);
if (twt == null) {
return;
}
boolean regulatingForcedToOff = false;
if (twt.hasRatioTapChanger()) {
boolean regulating = defineVoltageControl(getNetwork(), twt.getId(), psseTransformer.getWinding1(), twt.getRatioTapChanger(), regulatingForcedToOff, nodeBreakerImport);
regulatingForcedToOff = forceRegulatingToOff(regulatingForcedToOff, regulating);
}
if (twt.hasPhaseTapChanger()) {
defineActivePowerControl(getNetwork(), twt.getId(), psseTransformer.getWinding1(), twt.getPhaseTapChanger(), regulatingForcedToOff, nodeBreakerImport);
}
}
private void addControlThreeWindingsTransformer() {
String id = getTransformerId(psseTransformer.getI(), psseTransformer.getJ(), psseTransformer.getK(), psseTransformer.getCkt());
ThreeWindingsTransformer twt = getNetwork().getThreeWindingsTransformer(id);
if (twt == null) {
return;
}
boolean regulatingForcedToOff = false;
regulatingForcedToOff = addControlThreeWindingsTransformerLeg(getNetwork(), twt.getId(), twt.getLeg1(), psseTransformer.getWinding1(), regulatingForcedToOff, nodeBreakerImport);
regulatingForcedToOff = addControlThreeWindingsTransformerLeg(getNetwork(), twt.getId(), twt.getLeg2(), psseTransformer.getWinding2(), regulatingForcedToOff, nodeBreakerImport);
addControlThreeWindingsTransformerLeg(getNetwork(), twt.getId(), twt.getLeg3(), psseTransformer.getWinding3(), regulatingForcedToOff, nodeBreakerImport);
}
private static boolean addControlThreeWindingsTransformerLeg(Network network, String id, Leg leg,
PsseTransformerWinding winding, boolean regulatingForcedToOffInput, NodeBreakerImport nodeBreakerImport) {
boolean regulatingForcedToOff = regulatingForcedToOffInput;
if (leg.hasRatioTapChanger()) {
boolean regulating = defineVoltageControl(network, id, winding, leg.getRatioTapChanger(), regulatingForcedToOff, nodeBreakerImport);
regulatingForcedToOff = forceRegulatingToOff(regulatingForcedToOff, regulating);
}
if (leg.hasPhaseTapChanger()) {
boolean regulating = defineActivePowerControl(network, id, winding, leg.getPhaseTapChanger(), regulatingForcedToOff, nodeBreakerImport);
regulatingForcedToOff = forceRegulatingToOff(regulatingForcedToOff, regulating);
}
return regulatingForcedToOff;
}
private static boolean defineVoltageControl(Network network, String id, PsseTransformerWinding winding, RatioTapChanger rtc,
boolean regulatingForcedToOff, NodeBreakerImport nodeBreakerImport) {
if (Math.abs(winding.getCod()) == 2) {
LOGGER.warn("Transformer {}. Reactive power control not supported", id);
return false;
}
if (Math.abs(winding.getCod()) != 1) {
return false;
}
Terminal regulatingTerminal = defineRegulatingTerminal(network, id, winding, nodeBreakerImport);
// Discard control if the transformer is controlling an isolated bus
if (regulatingTerminal == null) {
return false;
}
double vnom = regulatingTerminal.getVoltageLevel().getNominalV();
double vmin = winding.getVmi() * vnom;
double vmax = winding.getVma() * vnom;
double targetV = (vmin + vmax) * 0.5;
double targetDeadBand = vmax - vmin;
boolean regulating = targetV > 0.0 && targetDeadBand >= 0.0;
if (regulating && regulatingForcedToOff) {
LOGGER.warn("Transformer {}. Regulating control forced to off. Only one control is supported", id);
regulating = false;
}
rtc.setTargetV(targetV)
.setTargetDeadband(targetDeadBand)
.setRegulationTerminal(regulatingTerminal)
.setRegulating(regulating);
return regulating;
}
private static boolean defineActivePowerControl(Network network, String id, PsseTransformerWinding winding, PhaseTapChanger ptc, boolean regulatingForcedToOff, NodeBreakerImport nodeBreakerImport) {
if (Math.abs(winding.getCod()) != 3) {
return false;
}
Terminal regulatingTerminal = defineRegulatingTerminal(network, id, winding, nodeBreakerImport);
// Discard control if the transformer is controlling an isolated bus
if (regulatingTerminal == null) {
return false;
}
double activePowerMin = winding.getVmi();
double activePowerMax = winding.getVma();
double targetValue = 0.5 * (activePowerMin + activePowerMax);
double targetDeadBand = activePowerMax - activePowerMin;
boolean regulating = targetDeadBand >= 0.0;
if (regulating && regulatingForcedToOff) {
LOGGER.warn("Transformer {}. Regulating control forced to off. Only one control is supported", id);
regulating = false;
}
ptc.setRegulationValue(targetValue)
.setTargetDeadband(targetDeadBand)
.setRegulationTerminal(regulatingTerminal)
.setRegulationMode(PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL)
.setRegulating(regulating);
return regulating;
}
private static boolean forceRegulatingToOff(boolean regulatingForcedToOff, boolean regulating) {
return regulatingForcedToOff || regulating;
}
private static Terminal defineRegulatingTerminal(Network network, String id, PsseTransformerWinding winding, NodeBreakerImport nodeBreakerImport) {
Terminal regulatingTerminal = null;
int busI = Math.abs(winding.getCont());
Optional<NodeBreakerImport.ControlR> control = nodeBreakerImport.getControl(busI);
if (control.isPresent()) {
int controlledNode = winding.getNode() != 0 ? winding.getNode() : control.get().node();
regulatingTerminal = findTerminalNode(network, control.get().voltageLevelId(), controlledNode);
} else {
String regulatingBusId = getBusId(busI);
Bus bus = network.getBusBreakerView().getBus(regulatingBusId);
if (bus != null) {
regulatingTerminal = bus.getConnectedTerminalStream().findFirst().orElse(null);
}
}
if (regulatingTerminal == null) {
LOGGER.warn("Transformer {}. Regulating terminal is not assigned", id);
}
return regulatingTerminal;
}
// At the moment we do not consider new transformers and antenna twoWindingsTransformers are exported as open
static void update(Network network, PssePowerFlowModel psseModel) {
psseModel.getTransformers().forEach(psseTransformer -> {
if (isTwoWindingsTransformer(psseTransformer)) {
updateTwoWindingsTransformer(network, psseTransformer);
} else {
updateThreeWindingsTransformer(network, psseTransformer);
}
});
}
private static void updateTwoWindingsTransformer(Network network, PsseTransformer psseTransformer) {
String transformerId = getTransformerId(psseTransformer.getI(), psseTransformer.getJ(), psseTransformer.getCkt());
TwoWindingsTransformer t2w = network.getTwoWindingsTransformer(transformerId);
if (t2w == null) {
psseTransformer.setStat(0);
} else {
double baskv1 = t2w.getTerminal1().getVoltageLevel().getNominalV();
double nomV1 = getNomV(psseTransformer.getWinding1(), t2w.getTerminal1().getVoltageLevel());
psseTransformer.getWinding1().setWindv(defineWindV(getRatio(t2w.getRatioTapChanger(), t2w.getPhaseTapChanger()), baskv1, nomV1, psseTransformer.getCw()));
psseTransformer.getWinding1().setAng(getAngle(t2w.getPhaseTapChanger()));
psseTransformer.setStat(getStatus(t2w));
}
}
private static int getStatus(TwoWindingsTransformer t2w) {
return t2w.getTerminal1().isConnected() && t2w.getTerminal1().getBusBreakerView().getBus() != null
&& t2w.getTerminal2().isConnected() && t2w.getTerminal2().getBusBreakerView().getBus() != null ? 1 : 0;
}
private static void updateThreeWindingsTransformer(Network network, PsseTransformer psseTransformer) {
String transformerId = getTransformerId(psseTransformer.getI(), psseTransformer.getJ(), psseTransformer.getK(), psseTransformer.getCkt());
ThreeWindingsTransformer t3w = network.getThreeWindingsTransformer(transformerId);
if (t3w == null) {
psseTransformer.setStat(0);
} else {
double baskv1 = t3w.getLeg1().getTerminal().getVoltageLevel().getNominalV();
double nomV1 = getNomV(psseTransformer.getWinding1(), t3w.getLeg1().getTerminal().getVoltageLevel());
psseTransformer.getWinding1().setWindv(defineWindV(getRatio(t3w.getLeg1().getRatioTapChanger(), t3w.getLeg1().getPhaseTapChanger()), baskv1, nomV1, psseTransformer.getCw()));
psseTransformer.getWinding1().setAng(getAngle(t3w.getLeg1().getPhaseTapChanger()));
double baskv2 = t3w.getLeg2().getTerminal().getVoltageLevel().getNominalV();
double nomV2 = getNomV(psseTransformer.getWinding2(), t3w.getLeg2().getTerminal().getVoltageLevel());
psseTransformer.getWinding2().setWindv(defineWindV(getRatio(t3w.getLeg2().getRatioTapChanger(), t3w.getLeg2().getPhaseTapChanger()), baskv2, nomV2, psseTransformer.getCw()));
psseTransformer.getWinding2().setAng(getAngle(t3w.getLeg2().getPhaseTapChanger()));
double baskv3 = t3w.getLeg3().getTerminal().getVoltageLevel().getNominalV();
double nomV3 = getNomV(psseTransformer.getWinding3(), t3w.getLeg3().getTerminal().getVoltageLevel());
psseTransformer.getWinding3().setWindv(defineWindV(getRatio(t3w.getLeg3().getRatioTapChanger(), t3w.getLeg3().getPhaseTapChanger()), baskv3, nomV3, psseTransformer.getCw()));
psseTransformer.getWinding3().setAng(getAngle(t3w.getLeg3().getPhaseTapChanger()));
psseTransformer.setStat(getStatus(t3w));
}
}
private static int getStatus(ThreeWindingsTransformer t3w) {
if (t3w.getLeg1().getTerminal().isConnected() && t3w.getLeg1().getTerminal().getBusBreakerView().getBus() != null
&& t3w.getLeg2().getTerminal().isConnected() && t3w.getLeg2().getTerminal().getBusBreakerView().getBus() != null
&& t3w.getLeg3().getTerminal().isConnected() && t3w.getLeg3().getTerminal().getBusBreakerView().getBus() != null) {
return 1;
} else if (t3w.getLeg1().getTerminal().isConnected() && t3w.getLeg1().getTerminal().getBusBreakerView().getBus() != null
&& t3w.getLeg2().getTerminal().isConnected() && t3w.getLeg2().getTerminal().getBusBreakerView().getBus() != null) {
return 3;
} else if (t3w.getLeg1().getTerminal().isConnected() && t3w.getLeg1().getTerminal().getBusBreakerView().getBus() != null
&& t3w.getLeg3().getTerminal().isConnected() && t3w.getLeg3().getTerminal().getBusBreakerView().getBus() != null) {
return 2;
} else if (t3w.getLeg2().getTerminal().isConnected() && t3w.getLeg2().getTerminal().getBusBreakerView().getBus() != null
&& t3w.getLeg3().getTerminal().isConnected() && t3w.getLeg3().getTerminal().getBusBreakerView().getBus() != null) {
return 4;
} else {
return 0;
}
}
private static double getRatio(RatioTapChanger rtc, PhaseTapChanger ptc) {
if (rtc != null && ptc != null) {
return getRatio(rtc) * getRatio(ptc);
} else if (rtc != null) {
return getRatio(rtc);
} else if (ptc != null) {
return getRatio(ptc);
} else {
return 1.0;
}
}
private static double getRatio(RatioTapChanger rtc) {
return rtc != null ? 1.0 / rtc.getCurrentStep().getRho() : 1.0;
}
private static double getRatio(PhaseTapChanger ptc) {
return ptc != null ? 1.0 / ptc.getCurrentStep().getRho() : 1.0;
}
private static double getAngle(PhaseTapChanger ptc) {
return ptc != null ? convertToAngle(ptc.getCurrentStep().getAlpha()) : 0.0;
}
private static double convertToAngle(double alpha) {
return alpha != 0.0 ? -alpha : alpha; // To avoid - 0.0
}
private final PsseTransformer psseTransformer;
private final Map<Integer, PsseBus> busNumToPsseBus;
private final double sbase;
private final PerUnitContext perUnitContext;
private final PsseVersion version;
private final NodeBreakerImport nodeBreakerImport;
private static final Logger LOGGER = LoggerFactory.getLogger(TransformerConverter.class);
}