DanglingLineData.java

/**
 * 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/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.iidm.network.util;

import org.apache.commons.math3.complex.Complex;
import org.apache.commons.math3.complex.ComplexUtils;

import com.powsybl.iidm.network.DanglingLine;

import java.util.Objects;

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

    private final DanglingLine danglingLine;

    private final double boundaryBusU;
    private final double boundaryBusTheta;

    public DanglingLineData(DanglingLine danglingLine) {
        this.danglingLine = Objects.requireNonNull(danglingLine);

        double u1 = getV(danglingLine);
        double theta1 = getTheta(danglingLine);

        if (!valid(u1, theta1)) {
            boundaryBusU = Double.NaN;
            boundaryBusTheta = Double.NaN;
            return;
        }

        if (zeroImpedance(danglingLine)) {
            boundaryBusU = u1;
            boundaryBusTheta = theta1;
            return;
        }

        Complex v1 = ComplexUtils.polar2Complex(u1, theta1);

        // DanglingLine model has shunt admittance on network side only, so it is not split between both sides.
        Complex vBoundaryBus = new Complex(Double.NaN, Double.NaN);
        if (danglingLine.getP0() == 0.0 && danglingLine.getQ0() == 0.0) {
            LinkData.BranchAdmittanceMatrix adm = LinkData.calculateBranchAdmittance(
                    danglingLine.getR(), danglingLine.getX(), 1.0, 0.0, 1.0, 0.0, new Complex(danglingLine.getG(), danglingLine.getB()), new Complex(0, 0));
            vBoundaryBus = adm.y21().multiply(v1).negate().divide(adm.y22());
        } else {

            // Two buses Loadflow
            Complex sBoundary = new Complex(-danglingLine.getP0(), -danglingLine.getQ0());
            Complex zt = new Complex(danglingLine.getR(), danglingLine.getX());
            double v12 = v1.abs() * v1.abs();

            Complex sigma = zt.multiply(sBoundary.conjugate()).multiply(1.0 / v12);
            double d = 0.25 + sigma.getReal() - sigma.getImaginary() * sigma.getImaginary();
            // d < 0 Collapsed network
            if (d >= 0) {
                vBoundaryBus = new Complex(0.5 + Math.sqrt(d), sigma.getImaginary()).multiply(v1);
            }
        }

        boundaryBusU = vBoundaryBus.abs();
        boundaryBusTheta = vBoundaryBus.getArgument();
    }

    static double getV(DanglingLine danglingLine) {
        return danglingLine.getTerminal().isConnected() ? danglingLine.getTerminal().getBusView().getBus().getV()
            : Double.NaN;
    }

    static double getTheta(DanglingLine danglingLine) {
        return danglingLine.getTerminal().isConnected()
            ? Math.toRadians(danglingLine.getTerminal().getBusView().getBus().getAngle())
            : Double.NaN;
    }

    private static boolean valid(double v, double theta) {
        if (Double.isNaN(v) || v <= 0.0) {
            return false;
        }
        return !Double.isNaN(theta);
    }

    public String getId() {
        return danglingLine.getId();
    }

    public double getBoundaryBusU() {
        return boundaryBusU;
    }

    public double getBoundaryBusTheta() {
        return boundaryBusTheta;
    }

    public static boolean zeroImpedance(DanglingLine parent) {
        // Simple way to deal with zero impedance dangling line.
        return parent.getR() == 0.0 && parent.getX() == 0.0;
    }
}