SV.java
/**
* Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.iidm.network.util;
import com.powsybl.iidm.network.*;
import org.apache.commons.math3.complex.Complex;
import org.apache.commons.math3.complex.ComplexUtils;
/**
* Utility class to compute the state variables on one side of a branch, knowing
* the state variables on the other side.
*
* @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
* @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
*/
public class SV {
/**
* In this class, lines, two windings transformers and dangling lines can be considered as equivalent branches.
* <p><div>
* <object data="doc-files/SV.svg" type="image/svg+xml">
* </object> </div>
* For dangling lines, side ONE is always on network's side and side TWO is always on boundary's side. <br>
* @param p active power flow on the side of the branch we consider.
* @param q reactive power flow on the side of the branch we consider
* @param u voltage on the side of the branch we consider.
* @param a phase on the side of the branch we consider.
* @param side the side of the branch we consider.
*/
public SV(double p, double q, double u, double a, TwoSides side) {
this.p = p;
this.q = q;
this.u = u;
this.a = a;
this.side = side;
}
private final double p;
private final double q;
private final double u;
private final double a;
private final TwoSides side;
public double getP() {
return p;
}
public double getQ() {
return q;
}
public double getU() {
return u;
}
public double getA() {
return a;
}
public double getI() {
return Math.hypot(p, q) / (Math.sqrt(3.) * u / 1000);
}
public TwoSides getSide() {
return side;
}
public SV otherSide(double r, double x, double g1, double b1, double g2, double b2, double rho, double alpha) {
return otherSide(r, x, g1, b1, g2, b2, rho, alpha, Double.NaN);
}
public SV otherSide(Line l) {
double zb = l.getTerminal1().getVoltageLevel().getNominalV() * l.getTerminal2().getVoltageLevel().getNominalV();
return otherSide(l.getR(), l.getX(), l.getG1(), l.getB1(), l.getG2(), l.getB2(), 1.0, 0.0, zb);
}
public SV otherSide(TieLine l) {
double zb = l.getDanglingLine1().getTerminal().getVoltageLevel().getNominalV() * l.getDanglingLine2().getTerminal().getVoltageLevel().getNominalV();
return otherSide(l.getR(), l.getX(), l.getG1(), l.getB1(), l.getG2(), l.getB2(), 1.0, 0.0, zb);
}
public SV otherSide(TwoWindingsTransformer twt) {
double zbase = twt.getTerminal2().getVoltageLevel().getNominalV() * twt.getTerminal2().getVoltageLevel().getNominalV();
return otherSide(getR(twt), getX(twt), getG(twt), getB(twt), 0.0, 0.0, getRho(twt), getAlpha(twt), zbase);
}
public SV otherSide(TwoWindingsTransformer twt, boolean splitShuntAdmittance) {
if (splitShuntAdmittance) {
double zb = twt.getTerminal2().getVoltageLevel().getNominalV() * twt.getTerminal2().getVoltageLevel().getNominalV();
return otherSide(getR(twt), getX(twt), getG(twt) * 0.5, getB(twt) * 0.5, getG(twt) * 0.5, getB(twt) * 0.5, getRho(twt), getAlpha(twt), zb);
} else {
return otherSide(twt);
}
}
public SV otherSide(DanglingLine dl) {
double zb = dl.getTerminal().getVoltageLevel().getNominalV() * dl.getTerminal().getVoltageLevel().getNominalV();
return otherSide(dl.getR(), dl.getX(), dl.getG(), dl.getB(), 0.0, 0.0, 1.0, 0.0, zb);
}
public SV otherSide(DanglingLine dl, boolean splitShuntAdmittance) {
if (splitShuntAdmittance) {
double zb = dl.getTerminal().getVoltageLevel().getNominalV() * dl.getTerminal().getVoltageLevel().getNominalV();
return otherSide(dl.getR(), dl.getX(), dl.getG() * 0.5, dl.getB() * 0.5, dl.getG() * 0.5, dl.getB() * 0.5, 1.0, 0.0, zb);
} else {
return otherSide(dl);
}
}
public double otherSideP(double r, double x, double g1, double b1, double g2, double b2, double rho, double alpha) {
return otherSide(r, x, g1, b1, g2, b2, rho, alpha, Double.NaN).getP();
}
public double otherSideP(DanglingLine dl) {
return otherSide(dl).getP();
}
public double otherSideP(DanglingLine dl, boolean splitShuntAdmittance) {
return otherSide(dl, splitShuntAdmittance).getP();
}
public double otherSideQ(double r, double x, double g1, double b1, double g2, double b2, double rho, double alpha) {
return otherSide(r, x, g1, b1, g2, b2, rho, alpha, Double.NaN).getQ();
}
public double otherSideQ(DanglingLine dl) {
return otherSide(dl).getQ();
}
public double otherSideQ(DanglingLine dl, boolean splitShuntAdmittance) {
return otherSide(dl, splitShuntAdmittance).getQ();
}
public double otherSideU(double r, double x, double g1, double b1, double g2, double b2, double rho, double alpha) {
return otherSide(r, x, g1, b1, g2, b2, rho, alpha, Double.NaN).getU();
}
public double otherSideU(DanglingLine dl) {
return otherSide(dl).getU();
}
public double otherSideU(DanglingLine dl, boolean splitShuntAdmittance) {
return otherSide(dl, splitShuntAdmittance).getU();
}
public double otherSideA(double r, double x, double g1, double b1, double g2, double b2, double rho, double alpha) {
return otherSide(r, x, g1, b1, g2, b2, rho, alpha, Double.NaN).getA();
}
public double otherSideA(DanglingLine dl) {
return otherSide(dl).getA();
}
public double otherSideA(DanglingLine dl, boolean splitShuntAdmittance) {
return otherSide(dl, splitShuntAdmittance).getA();
}
public double otherSideI(DanglingLine dl) {
return otherSide(dl).getI();
}
public double otherSideI(DanglingLine dl, boolean splitShuntAdmittance) {
return otherSide(dl, splitShuntAdmittance).getI();
}
private static double getRho(TwoWindingsTransformer twt) {
double rho = twt.getRatedU2() / twt.getRatedU1();
if (twt.getRatioTapChanger() != null) {
rho = rho * twt.getRatioTapChanger().getCurrentStep().getRho();
}
if (twt.getPhaseTapChanger() != null) {
rho = rho * twt.getPhaseTapChanger().getCurrentStep().getRho();
}
return rho;
}
private static double getAlpha(TwoWindingsTransformer twt) {
double alpha = 0.0;
if (twt.getPhaseTapChanger() != null) {
alpha = twt.getPhaseTapChanger().getCurrentStep().getAlpha();
}
return Math.toRadians(alpha);
}
private static double getR(TwoWindingsTransformer twt) {
double r = twt.getR();
if (twt.getRatioTapChanger() != null) {
r = r * (1 + twt.getRatioTapChanger().getCurrentStep().getR() / 100);
}
if (twt.getPhaseTapChanger() != null) {
r = r * (1 + twt.getPhaseTapChanger().getCurrentStep().getR() / 100);
}
return r;
}
private static double getX(TwoWindingsTransformer twt) {
double x = twt.getX();
if (twt.getRatioTapChanger() != null) {
x = x * (1 + twt.getRatioTapChanger().getCurrentStep().getX() / 100);
}
if (twt.getPhaseTapChanger() != null) {
x = x * (1 + twt.getPhaseTapChanger().getCurrentStep().getX() / 100);
}
return x;
}
private static double getG(TwoWindingsTransformer twt) {
double g = twt.getG();
if (twt.getRatioTapChanger() != null) {
g = g * (1 + twt.getRatioTapChanger().getCurrentStep().getG() / 100);
}
if (twt.getPhaseTapChanger() != null) {
g = g * (1 + twt.getPhaseTapChanger().getCurrentStep().getG() / 100);
}
return g;
}
private static double getB(TwoWindingsTransformer twt) {
double b = twt.getG();
if (twt.getRatioTapChanger() != null) {
b = b * (1 + twt.getRatioTapChanger().getCurrentStep().getB() / 100);
}
if (twt.getPhaseTapChanger() != null) {
b = b * (1 + twt.getPhaseTapChanger().getCurrentStep().getB() / 100);
}
return b;
}
@Override
public String toString() {
return "p=" + p + ", q=" + q + ", u=" + u + ", a=" + a + ", end=" + side;
}
private boolean isAllDataForCalculatingOtherSide() {
return !(Double.isNaN(p) || Double.isNaN(q) || Double.isNaN(u) || Double.isNaN(a));
}
private boolean isAllDataForCalculatingOtherSideDcApproximation(double zbase) {
return !(Double.isNaN(p) || Double.isNaN(a) || Double.isNaN(zbase));
}
private SV otherSide(double r, double x, double g1, double b1, double g2, double b2, double rho, double alpha, double zb) {
if (isAllDataForCalculatingOtherSide()) {
LinkData.BranchAdmittanceMatrix adm = LinkData.calculateBranchAdmittance(r, x, 1 / rho, -alpha, 1.0, 0.0,
new Complex(g1, b1), new Complex(g2, b2));
return otherSide(adm);
} else if (isAllDataForCalculatingOtherSideDcApproximation(zb)) {
return otherSideDcApproximation(x, 1 / rho, -alpha, zb, true); // we always consider useRatio true
} else {
TwoSides otherSide = (side == TwoSides.ONE) ? TwoSides.TWO : TwoSides.ONE;
return new SV(Double.NaN, Double.NaN, Double.NaN, Double.NaN, otherSide);
}
}
private SV otherSide(LinkData.BranchAdmittanceMatrix adm) {
Complex v;
Complex s;
TwoSides otherSide;
if (side == TwoSides.ONE) {
Complex v1 = ComplexUtils.polar2Complex(u, Math.toRadians(a));
Complex s1 = new Complex(p, q);
v = voltageAtEnd2(adm, v1, s1);
s = flowAtEnd2(adm, v1, v);
otherSide = TwoSides.TWO;
} else {
Complex v2 = ComplexUtils.polar2Complex(u, Math.toRadians(a));
Complex s2 = new Complex(p, q);
v = voltageAtEnd1(adm, v2, s2);
s = flowAtEnd1(adm, v, v2);
otherSide = TwoSides.ONE;
}
return new SV(s.getReal(), s.getImaginary(), v.abs(), Math.toDegrees(v.getArgument()), otherSide);
}
private SV otherSideDcApproximation(double x, double ratio, double angle, double zb, boolean useRatio) {
double pOtherSide = -p;
double xpu = x / zb;
double b = useRatio ? 1 / (xpu * ratio) : 1 / xpu;
double aOtherSide;
TwoSides otherSide;
if (side == TwoSides.ONE) {
aOtherSide = Math.toDegrees(Math.toRadians(a) - angle - p / b);
otherSide = TwoSides.TWO;
} else {
aOtherSide = Math.toDegrees(Math.toRadians(a) + angle - p / b);
otherSide = TwoSides.ONE;
}
return new SV(pOtherSide, Double.NaN, Double.NaN, aOtherSide, otherSide);
}
// Get V2 from Y11.V1 + Y12.V2 = S1* / V1*
private static Complex voltageAtEnd2(LinkData.BranchAdmittanceMatrix adm, Complex vEnd1, Complex sEnd1) {
return sEnd1.conjugate().divide(vEnd1.conjugate()).subtract(adm.y11().multiply(vEnd1)).divide(adm.y12());
}
// Get V1 from Y21.V1 + Y22.V2 = S2* / V2*
private static Complex voltageAtEnd1(LinkData.BranchAdmittanceMatrix adm, Complex vEnd2, Complex sEnd2) {
return sEnd2.conjugate().divide(vEnd2.conjugate()).subtract(adm.y22().multiply(vEnd2)).divide(adm.y21());
}
// Get S1 from Y11.V1 + Y12.V2 = S1* / V1*
private static Complex flowAtEnd1(LinkData.BranchAdmittanceMatrix adm, Complex vEnd1, Complex vEnd2) {
return adm.y11().multiply(vEnd1).add(adm.y12().multiply(vEnd2)).multiply(vEnd1.conjugate()).conjugate();
}
// Get S2 from Y21.V1 + Y22.V2 = S2* / V2*
private static Complex flowAtEnd2(LinkData.BranchAdmittanceMatrix adm, Complex vEnd1, Complex vEnd2) {
return adm.y21().multiply(vEnd1).add(adm.y22().multiply(vEnd2)).multiply(vEnd2.conjugate()).conjugate();
}
}