com.powsybl.cgmes.conversion.elements.transformers.TapChangerConversion Maven / Gradle / Ivy
Show all versions of powsybl-cgmes-conversion Show documentation
/**
* Copyright (c) 2020, 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/.
*/
package com.powsybl.cgmes.conversion.elements.transformers;
import com.powsybl.cgmes.conversion.Context;
import com.powsybl.cgmes.conversion.elements.transformers.TapChanger.Step;
import com.powsybl.cgmes.model.CgmesModelException;
import org.apache.commons.math3.complex.Complex;
import java.util.Objects;
/**
* TapChangerTypes
*
* TapChangers are classified into four categories: NULL, FIXED, NON_REGULATING and REGULATING
* NULL: TapChanger does not exist
* FIXED: TapChanger has only one step
* NON_REGULATING: TapChanger is not regulating. It has several steps
* REGULATING: TapChanger is regulating and has several steps
*
* These categories are used to establish a priority ranking when two tapChangers are combined to only one.
* The tapChanger with lower priority is fixed at the current tap position and the tapChanger with higher priority
* is preserved. NULL is the lowest priority and REGULATING the highest one.
* If both tapChangers are regulating tapChanger at end2 is fixed.
*
* The combineTapChanger method does a Cartesian product of two tapChangers getting as result a new tapChanger with
* steps1 x steps2 steps. The combined tapChanger will not be useful for network analysis and it is not possible to
* map back to the original one.
* To avoid this, one of the tapChangers must be fixed. To do that the tapChangerFixPosition method is used.
*
* A warning message is logged when a tapChanger is fixed.
*
* When structural ratio is moved from one side to the other a correction is applied to transmission impedance (r, x) and shunt admittance (g and b).
* When tapChanger is moved the transmission impedance and shunt admittance corrections are managed as step correction
* expressed as percentage deviation of nominal value.
*
* @author Luma Zamarreño
* @author José Antonio Marqués
*/
public class TapChangerConversion {
protected final Context context;
TapChangerConversion(Context context) {
Objects.requireNonNull(context);
this.context = context;
}
private enum TapChangerType {
NULL, FIXED, NON_REGULATING, REGULATING
}
protected TapChanger combineTapChangers(TapChanger tc1, TapChanger tc2) {
switch (tapChangerType(tc1)) {
case NULL:
return combineTapChangerNull(tc2);
case FIXED:
return combineTapChangerFixed(tc1, tc2);
case NON_REGULATING:
return combineTapChangerNonRegulating(tc1, tc2);
case REGULATING:
return combineTapChangerRegulating(tc1, tc2);
}
return null;
}
private static TapChanger combineTapChangerNull(TapChanger tc) {
switch (tapChangerType(tc)) {
case NULL:
return null;
case FIXED:
case NON_REGULATING:
case REGULATING:
return tc;
}
return null;
}
private static TapChanger combineTapChangerFixed(TapChanger fixTc, TapChanger tc2) {
switch (tapChangerType(tc2)) {
case NULL:
return fixTc;
case FIXED:
case NON_REGULATING:
case REGULATING:
return combineTapChanger(tc2, fixTc);
}
return null;
}
private TapChanger combineTapChangerNonRegulating(TapChanger tcNonRegulating, TapChanger tc2) {
switch (tapChangerType(tc2)) {
case NULL:
return tcNonRegulating;
case FIXED:
return combineTapChanger(tcNonRegulating, tc2);
case NON_REGULATING:
TapChanger ntc2 = tapChangerFixPosition(tc2);
return combineTapChanger(tcNonRegulating, ntc2);
case REGULATING:
TapChanger ntcNonRegulating = tapChangerFixPosition(tcNonRegulating);
return combineTapChanger(tc2, ntcNonRegulating);
}
return null;
}
private TapChanger combineTapChangerRegulating(TapChanger tcRegulating, TapChanger tc2) {
switch (tapChangerType(tc2)) {
case NULL:
return tcRegulating;
case FIXED:
return combineTapChanger(tcRegulating, tc2);
case NON_REGULATING:
case REGULATING:
TapChanger ntc2 = tapChangerFixPosition(tc2);
return combineTapChanger(tcRegulating, ntc2);
}
return null;
}
private static TapChanger combineTapChanger(TapChanger tc1, TapChanger tc2) {
TapChanger tapChanger = baseCloneTapChanger(tc1);
combineTapChangerSteps(tapChanger, tc1, tc2);
tapChanger.setHiddenCombinedTapChanger(tc2);
return tapChanger;
}
/**
* A new tapChanger is created with only one step, the current tap position.
*/
private TapChanger tapChangerFixPosition(TapChanger tc) {
if (tc.getLowTapPosition() != tc.getHighTapPosition()) {
context.fixed("TapChanger", () -> String.format("%s fixed tap at position %d ", tc.getId(), tc.getTapPosition()));
}
TapChanger tapChanger = baseCloneTapChanger(tc);
tapChanger.setLowTapPosition(tapChanger.getTapPosition());
Step step = getTapChangerFixedStep(tc);
double ratio = step.getRatio();
double angle = step.getAngle();
double r = step.getR();
double x = step.getX();
double g1 = step.getG1();
double b1 = step.getB1();
double g2 = step.getG2();
double b2 = step.getB2();
tapChanger.beginStep()
.setRatio(ratio)
.setAngle(angle)
.setR(r)
.setX(x)
.setG1(g1)
.setB1(b1)
.setG2(g2)
.setB2(b2)
.endStep();
return tapChanger;
}
/**
* A new tapChanger is created as Cartesian product of tc1 and tc2
* One of the tapChangers must be fixed (only one step)
*/
private static void combineTapChangerSteps(TapChanger tapChanger, TapChanger tc1, TapChanger tc2) {
TapChanger fixTc;
TapChanger tc;
if (tc1 != null && tc2 != null && tc1.getSteps().size() == 1
&& tc1.getLowTapPosition() == tc1.getHighTapPosition()) {
fixTc = tc1;
tc = tc2;
} else if (tc1 != null && tc2 != null && tc2.getSteps().size() == 1
&& tc2.getLowTapPosition() == tc2.getHighTapPosition()) {
fixTc = tc2;
tc = tc1;
} else if (tc1 != null && tc2 != null) {
throw new CgmesModelException(
"Unexpected number of steps in tapChangers: " + tc1.getId() + ", " + tc2.getId());
} else {
throw new CgmesModelException("Unexpected null tapChanger");
}
Step stepFixed = getTapChangerFixedStep(fixTc);
double ratioFixed = stepFixed.getRatio();
double angleFixed = stepFixed.getAngle();
double rFixed = stepFixed.getR();
double xFixed = stepFixed.getX();
double g1Fixed = stepFixed.getG1();
double b1Fixed = stepFixed.getB1();
double g2Fixed = stepFixed.getG2();
double b2Fixed = stepFixed.getB2();
Complex aFixed = new Complex(ratioFixed * Math.cos(Math.toRadians(angleFixed)),
ratioFixed * Math.sin(Math.toRadians(angleFixed)));
tc.getSteps().forEach(step -> {
double ratio = step.getRatio();
double angle = step.getAngle();
double r = step.getR();
double x = step.getX();
double g1 = step.getG1();
double b1 = step.getB1();
double g2 = step.getG2();
double b2 = step.getB2();
Complex a = new Complex(ratio * Math.cos(Math.toRadians(angle)),
ratio * Math.sin(Math.toRadians(angle)));
Complex na = a.multiply(aFixed);
tapChanger.beginStep()
.setRatio(na.abs())
.setAngle(Math.toDegrees(na.getArgument()))
.setR(combineTapChangerCorrection(rFixed, r))
.setX(combineTapChangerCorrection(xFixed, x))
.setG1(combineTapChangerCorrection(g1Fixed, g1))
.setB1(combineTapChangerCorrection(b1Fixed, b1))
.setG2(combineTapChangerCorrection(g2Fixed, g2))
.setB2(combineTapChangerCorrection(b2Fixed, b2))
.endStep();
});
tapChanger.setLowTapPosition(tc.getLowTapPosition());
tapChanger.setTapPosition(tc.getTapPosition());
}
/**
* To combine the percentage deviations express them as multiplying factors, then combine and
* express them again as percentage deviations
*/
private static double combineTapChangerCorrection(double correction1, double correction2) {
if (correction1 != 0.0 && correction2 != 0.0) {
return 100 * ((1 + correction1 / 100) * (1 + correction2 / 100) - 1);
} else if (correction1 != 0.0) {
return correction1;
} else if (correction2 != 0.0) {
return correction2;
} else {
return 0.0;
}
}
// not used at the moment
protected static TapChanger moveTapChangerFrom1To2(TapChanger tc) {
return moveTapChangerFromOneEndToTheOther(tc);
}
protected static TapChanger moveTapChangerFrom2To1(TapChanger tc) {
return moveTapChangerFromOneEndToTheOther(tc);
}
private static TapChanger moveTapChangerFromOneEndToTheOther(TapChanger tc) {
switch (tapChangerType(tc)) {
case NULL:
return null;
case FIXED:
case NON_REGULATING:
case REGULATING:
return moveTapChanger(tc);
}
return null;
}
/**
* A new equivalent tapChanger located in the other side is created.
* When given in RatioTapChangerTablePoint
* r, x, g, b of the step are already percentage deviations of nominal values
* R = R * (1 + r / 100)
* X = X * (1 + x / 100)
* G = G * (1 + g / 100)
* B = B * (1 + b / 100)
*
* New tapChanger
* Will have the same base attributes, ratio will be the reciprocal and
* an impedance/admittance deviations are required by step
*/
private static TapChanger moveTapChanger(TapChanger tc) {
TapChanger tapChanger = baseCloneTapChanger(tc);
moveTapChangerSteps(tapChanger, tc);
return tapChanger;
}
private static void moveTapChangerSteps(TapChanger tapChanger, TapChanger tc) {
tc.getSteps().forEach(step -> {
double ratio = step.getRatio();
double angle = step.getAngle();
double r = step.getR();
double x = step.getX();
double g1 = step.getG1();
double b1 = step.getB1();
double g2 = step.getG2();
double b2 = step.getB2();
TapChangerStepConversion convertedStep = calculateConversionStep(ratio, angle, r, x, g1, b1, g2, b2);
tapChanger.beginStep()
.setRatio(convertedStep.ratio)
.setAngle(convertedStep.angle)
.setR(convertedStep.r)
.setX(convertedStep.x)
.setG1(convertedStep.g1)
.setB1(convertedStep.b1)
.setG2(convertedStep.g2)
.setB2(convertedStep.b2)
.endStep();
});
}
private static TapChangerStepConversion calculateConversionStep(double a, double angle, double r,
double x, double g1, double b1, double g2, double b2) {
Complex ratio = new Complex(a * Math.cos(Math.toRadians(angle)),
a * Math.sin(Math.toRadians(angle)));
return calculateConversionStep(ratio, r, x, g1, b1, g2, b2);
}
/**
* To calculate the step correction express it as multiplying factor, then apply the correction
* and express again as percentage deviation
*/
private static TapChangerStepConversion calculateConversionStep(Complex a, double r,
double x, double g1, double b1, double g2, double b2) {
TapChangerStepConversion step = new TapChangerStepConversion();
Complex na = a.reciprocal();
step.ratio = na.abs();
step.angle = Math.toDegrees(na.getArgument());
step.r = 100 * (impedanceConversion(1 + r / 100, a) - 1);
step.x = 100 * (impedanceConversion(1 + x / 100, a) - 1);
step.g1 = 100 * (admittanceConversion(1 + g1 / 100, a) - 1);
step.b1 = 100 * (admittanceConversion(1 + b1 / 100, a) - 1);
step.g2 = 100 * (admittanceConversion(1 + g2 / 100, a) - 1);
step.b2 = 100 * (admittanceConversion(1 + b2 / 100, a) - 1);
return step;
}
protected static RatioConversion identityRatioConversion(double r, double x, double g1,
double b1, double g2, double b2) {
RatioConversion ratio = new RatioConversion();
ratio.r = r;
ratio.x = x;
ratio.g1 = g1;
ratio.b1 = b1;
ratio.g2 = g2;
ratio.b2 = b2;
return ratio;
}
protected static RatioConversion moveRatioFrom2To1(double a, double angle, double r, double x, double g1,
double b1, double g2, double b2) {
return moveRatio(a, angle, r, x, g1, b1, g2, b2);
}
protected static RatioConversion moveRatioFrom1To2(double a, double angle, double r, double x, double g1,
double b1, double g2, double b2) {
return moveRatio(a, angle, r, x, g1, b1, g2, b2);
}
/**
* Equivalent impedance / admittance after moving a complex ratio from one end to the other
*/
private static RatioConversion moveRatio(double a, double angle, double r, double x, double g1,
double b1, double g2, double b2) {
Complex ratio = new Complex(a * Math.cos(Math.toRadians(angle)),
a * Math.sin(Math.toRadians(angle)));
return moveRatio(ratio, r, x, g1, b1, g2, b2);
}
private static RatioConversion moveRatio(Complex a, double r, double x, double g1,
double b1, double g2, double b2) {
RatioConversion ratio = new RatioConversion();
ratio.r = impedanceConversion(r, a);
ratio.x = impedanceConversion(x, a);
ratio.g1 = admittanceConversion(g1, a);
ratio.b1 = admittanceConversion(b1, a);
ratio.g2 = admittanceConversion(g2, a);
ratio.b2 = admittanceConversion(b2, a);
return ratio;
}
private static double admittanceConversion(double correction, Complex a) {
double a2 = a.abs() * a.abs();
return correction / a2;
}
private static double impedanceConversion(double correction, Complex a) {
double a2 = a.abs() * a.abs();
return correction * a2;
}
/**
* A new tapChanger is created with the same base attributes. Steps are not cloned.
*/
private static TapChanger baseCloneTapChanger(TapChanger rtc) {
TapChanger tapChanger = new TapChanger();
String id = rtc.getId();
boolean isLtcFlag = rtc.isLtcFlag();
boolean isRegulating = rtc.isRegulating();
String regulatingControlId = rtc.getRegulatingControlId();
String tculControlMode = rtc.getTculControlMode();
boolean isTapChangerControlEnabled = rtc.isTapChangerControlEnabled();
int lowStep = rtc.getLowTapPosition();
int position = rtc.getTapPosition();
String type = rtc.getType();
TapChanger hiddenCombinedTapChanger = rtc.getHiddenCombinedTapChanger();
tapChanger.setLowTapPosition(lowStep)
.setTapPosition(position)
.setLtcFlag(isLtcFlag)
.setId(id)
.setRegulating(isRegulating)
.setRegulatingControlId(regulatingControlId)
.setTculControlMode(tculControlMode)
.setTapChangerControlEnabled(isTapChangerControlEnabled)
.setType(type)
.setHiddenCombinedTapChanger(hiddenCombinedTapChanger);
return tapChanger;
}
private static Step getTapChangerFixedStep(TapChanger tc) {
if (isTapChangerFixed(tc)) {
return tc.getSteps().get(0);
}
int position = tc.getTapPosition();
int lowPosition = tc.getLowTapPosition();
return tc.getSteps().get(position - lowPosition);
}
private static TapChangerType tapChangerType(TapChanger tc) {
if (isTapChangerNull(tc)) {
return TapChangerType.NULL;
}
if (isTapChangerFixed(tc)) {
return TapChangerType.FIXED;
}
if (!isTapChangerRegulating(tc)) {
return TapChangerType.NON_REGULATING;
} else {
return TapChangerType.REGULATING;
}
}
private static boolean isTapChangerNull(TapChanger tc) {
return tc == null;
}
private static boolean isTapChangerFixed(TapChanger tc) {
return tc.getSteps().size() == 1;
}
private static boolean isTapChangerRegulating(TapChanger tc) {
return tc.isRegulating();
}
static class InterpretedEnd {
final double g;
final double b;
final TapChanger ratioTapChanger;
final TapChanger phaseTapChanger;
final double ratedU;
final String terminal;
InterpretedEnd(double g, double b, TapChanger ratioTapChanger, TapChanger phaseTapChanger, double ratedU, String terminal) {
this.g = g;
this.b = b;
this.ratioTapChanger = ratioTapChanger;
this.phaseTapChanger = phaseTapChanger;
this.ratedU = ratedU;
this.terminal = terminal;
}
}
static class ConvertedEnd1 {
final double g;
final double b;
final TapChanger ratioTapChanger;
final TapChanger phaseTapChanger;
final double ratedU;
final String terminal;
ConvertedEnd1(double g, double b, TapChanger ratioTapChanger, TapChanger phaseTapChanger, double ratedU, String terminal) {
this.g = g;
this.b = b;
this.ratioTapChanger = ratioTapChanger;
this.phaseTapChanger = phaseTapChanger;
this.ratedU = ratedU;
this.terminal = terminal;
}
}
static class TapChangerStepConversion {
double ratio;
double angle;
double r;
double x;
double g1;
double b1;
double g2;
double b2;
}
static class RatioConversion {
double r;
double x;
double g1;
double b1;
double g2;
double b2;
}
static class AllTapChanger {
TapChanger ratioTapChanger1;
TapChanger phaseTapChanger1;
TapChanger ratioTapChanger2;
TapChanger phaseTapChanger2;
}
static class AllShunt {
double g1;
double b1;
double g2;
double b2;
}
}