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

com.powsybl.openloadflow.network.util.ZeroImpedanceFlows Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2022, 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.openloadflow.network.util;

import com.powsybl.openloadflow.network.*;
import org.jgrapht.Graph;
import org.jgrapht.alg.interfaces.SpanningTreeAlgorithm;

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

/**
 * @author Luma Zamarreño {@literal }
 * @author José Antonio Marqués {@literal }
 */
public class ZeroImpedanceFlows {

    private final Graph graph;
    private final SpanningTreeAlgorithm.SpanningTree tree;
    private final LoadFlowModel loadFlowModel;

    public ZeroImpedanceFlows(Graph zeroImpedanceSubGraph, SpanningTreeAlgorithm.SpanningTree spanningTree,
                              LoadFlowModel loadFlowModel) {
        this.graph = zeroImpedanceSubGraph;
        this.tree = spanningTree;
        this.loadFlowModel = loadFlowModel;
    }

    public void compute() {
        Set processed = new HashSet<>();

        graph.vertexSet().stream().sorted(Comparator.comparingInt(LfElement::getNum)).forEach(lfBus -> {
            if (processed.contains(lfBus)) {
                return;
            }
            TreeByLevels treeByLevels = new TreeByLevels(graph, tree, lfBus);
            treeByLevels.updateFlows(loadFlowModel);
            processed.addAll(treeByLevels.getProcessedLfBuses());
        });

        // zero flows for all zero impedance branches outside the tree
        graph.edgeSet().stream().filter(branch -> !tree.getEdges().contains(branch))
                .forEach(branch -> branch.updateFlows(0.0, 0.0, 0.0, 0.0));
    }

    private static final class TreeByLevels {

        private final LfBus lfBus;
        private final List> levels;
        private final Map parent;
        private final List processedLfBuses;

        private TreeByLevels(Graph graph, SpanningTreeAlgorithm.SpanningTree tree, LfBus lfBus) {
            this.lfBus = lfBus;
            levels = new ArrayList<>();
            parent = new HashMap<>();
            processedLfBuses = new ArrayList<>();
            createTreeByLevels(graph, tree);
        }

        private List getProcessedLfBuses() {
            return processedLfBuses;
        }

        private void createTreeByLevels(Graph graph, SpanningTreeAlgorithm.SpanningTree tree) {
            // set the root
            levels.add(new ArrayList<>(Collections.singleton(lfBus)));
            processedLfBuses.add(lfBus);

            int level = 0;
            while (level < levels.size()) {
                List nextLevel = new ArrayList<>();
                for (LfBus bus : levels.get(level)) {
                    graph.edgesOf(bus).stream()
                            .filter(branch -> tree.getEdges().contains(branch) && !isParentBranch(parent, bus, branch))
                            .forEach(childrenBranch -> {
                                LfBus otherBus = getOtherSideBus(childrenBranch, bus);
                                nextLevel.add(otherBus);
                                parent.put(otherBus, childrenBranch);
                            });
                }
                if (!nextLevel.isEmpty()) {
                    levels.add(nextLevel);
                    processedLfBuses.addAll(nextLevel);
                }
                level++;
            }
        }

        private static boolean isParentBranch(Map parent, LfBus bus, LfBranch branch) {
            return parent.containsKey(bus) && parent.get(bus).equals(branch);
        }

        private static LfBus getOtherSideBus(LfBranch branch, LfBus bus) {
            return branch.getBus1().equals(bus) ? branch.getBus2() : branch.getBus1();
        }

        private void updateFlows(LoadFlowModel loadFlowModel) {
            Map descendantZeroImpedanceFlow = new HashMap<>();

            // traverse the tree from leaves to root
            // (The root itself does not need to be processed)
            int level = levels.size() - 1;
            while (level >= 1) {
                levels.get(level).forEach(bus -> {
                    PQ balance = balanceWithImpedance(bus, loadFlowModel);
                    PQ z0flow = getDescendantZeroImpedanceFlow(descendantZeroImpedanceFlow, bus);
                    PQ branchFlow = balance.add(z0flow);

                    LfBranch branch = parent.get(bus);
                    updateBranchFlows(branch, bus, branchFlow.negate(), branchFlow);
                    descendantZeroImpedanceFlow.merge(getOtherSideBus(branch, bus), branchFlow, PQ::add);
                });
                level--;
            }
        }

        private PQ balanceWithImpedance(LfBus bus, LoadFlowModel loadFlowModel) {
            // balance considering injections and flow from lines with impedance

            double pShunt = getPShunt(bus.getShunt()) + getPShunt(bus.getControllerShunt()) + getPShunt(bus.getSvcShunt());
            double qShunt = getQShunt(bus.getShunt()) + getQShunt(bus.getControllerShunt()) + getQShunt(bus.getSvcShunt());

            // take care of the sign
            PQ balancePQ = new PQ(-bus.getP().eval() + pShunt, -bus.getQ().eval() + qShunt);

            // only lines with impedance
            List adjacentBranchesWithImpedance = bus.getBranches().stream()
                .filter(branch -> !branch.isZeroImpedance(loadFlowModel)).collect(Collectors.toList());

            adjacentBranchesWithImpedance.forEach(branch -> {
                PQ branchFlow = getBranchFlow(branch, bus);
                balancePQ.p += branchFlow.p;
                balancePQ.q += branchFlow.q;
            });

            return balancePQ;
        }

        private static double getPShunt(Optional optionalLfShunt) {
            return optionalLfShunt.map(shunt -> shunt.getP().eval()).filter(val -> !Double.isNaN(val)).orElse(0.0);
        }

        private static double getQShunt(Optional optionalLfShunt) {
            return optionalLfShunt.map(shunt -> shunt.getQ().eval()).filter(val -> !Double.isNaN(val)).orElse(0.0);
        }

        private PQ getDescendantZeroImpedanceFlow(Map descendantZeroImpedanceFlow, LfBus bus) {
            return descendantZeroImpedanceFlow.containsKey(bus) ? descendantZeroImpedanceFlow.get(bus) : new PQ(0.0, 0.0);
        }

        private void updateBranchFlows(LfBranch branch, LfBus bus, PQ pqBus, PQ pqOtherBus) {
            if (branch.getBus1() != null && branch.getBus1().equals(bus)) {
                branch.updateFlows(pqBus.p, pqBus.q, pqOtherBus.p, pqOtherBus.q);
            } else {
                branch.updateFlows(pqOtherBus.p, pqOtherBus.q, pqBus.p, pqBus.q);
            }
        }

        private PQ getBranchFlow(LfBranch branch, LfBus bus) {
            if (branch.getBus1() != null && branch.getBus1().equals(bus)) {
                return new PQ(branch.getP1().eval(), branch.getQ1().eval());
            } else {
                return new PQ(branch.getP2().eval(), branch.getQ2().eval());
            }
        }
    }

    private static final class PQ {
        private double p;
        private double q;

        private PQ(double p, double q) {
            this.p = p;
            this.q = q;
        }

        private PQ add(PQ pq) {
            return new PQ(this.p + pq.p, this.q + pq.q);
        }

        private PQ negate() {
            return new PQ(-this.p, -this.q);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy