AbstractComponentsManager.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.components;

import com.powsybl.iidm.network.*;
import com.powsybl.math.graph.GraphUtil;
import com.powsybl.math.graph.GraphUtil.ConnectedComponentsComputationResult;
import gnu.trove.list.array.TIntArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @author Mathieu Bague {@literal <mathieu.bague@rte-france.com>}
 */
abstract class AbstractComponentsManager<C extends Component> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractComponentsManager.class);

    private final String label;

    private List<C> components;

    protected AbstractComponentsManager(String label) {
        this.label = Objects.requireNonNull(label);
    }

    public void invalidate() {
        components = null;
    }

    public void update() {
        if (components != null) {
            return;
        }

        long startTime = System.currentTimeMillis();

        // reset
        for (Bus bus : getNetwork().getBusBreakerView().getBuses()) {
            setComponentNumber(bus, -1);
        }

        int num = 0;
        Map<String, Integer> id2num = new HashMap<>();
        List<Bus> num2bus = new ArrayList<>();
        for (Bus bus : getNetwork().getBusView().getBuses()) {
            num2bus.add(bus);
            id2num.put(bus.getId(), num);
            num++;
        }
        TIntArrayList[] adjacencyList = new TIntArrayList[num];
        for (int i = 0; i < adjacencyList.length; i++) {
            adjacencyList[i] = new TIntArrayList(3);
        }
        fillAdjacencyList(id2num, adjacencyList);

        ConnectedComponentsComputationResult result = GraphUtil.computeConnectedComponents(adjacencyList);

        components = new ArrayList<>(result.getComponentSize().length);
        for (int i = 0; i < result.getComponentSize().length; i++) {
            components.add(createComponent(i, result.getComponentSize()[i]));
        }

        for (int i = 0; i < result.getComponentNumber().length; i++) {
            Bus bus = num2bus.get(i);
            setComponentNumber(bus, result.getComponentNumber()[i]);
        }

        LOGGER.debug("{} components computed in {} ms", getComponentLabel(), System.currentTimeMillis() - startTime);
    }

    public List<C> getConnectedComponents() {
        update();
        return components;
    }

    public C getComponent(int num) {
        // update() must not be put here, but explicitly called each time before because update may
        // trigger a new component computation and so on a change in the value of the num component already passed
        // (and outdated consequently) in parameter of this method
        return num != -1 ? components.get(num) : null;
    }

    void addToAdjacencyList(Bus bus1, Bus bus2, Map<String, Integer> id2num, TIntArrayList[] adjacencyList) {
        if (bus1 != null && bus2 != null) {
            int busNum1 = id2num.get(bus1.getId());
            int busNum2 = id2num.get(bus2.getId());
            adjacencyList[busNum1].add(busNum2);
            adjacencyList[busNum2].add(busNum1);
        }
    }

    protected void fillAdjacencyList(Map<String, Integer> id2num, TIntArrayList[] adjacencyList) {
        for (Line line : getNetwork().getLines()) {
            Bus bus1 = line.getTerminal1().getBusView().getBus();
            Bus bus2 = line.getTerminal2().getBusView().getBus();
            addToAdjacencyList(bus1, bus2, id2num, adjacencyList);
        }
        for (TieLine tl : getNetwork().getTieLines()) {
            Bus bus1 = tl.getDanglingLine1().getTerminal().getBusView().getBus();
            Bus bus2 = tl.getDanglingLine2().getTerminal().getBusView().getBus();
            addToAdjacencyList(bus1, bus2, id2num, adjacencyList);
        }
        for (TwoWindingsTransformer transfo : getNetwork().getTwoWindingsTransformers()) {
            Bus bus1 = transfo.getTerminal1().getBusView().getBus();
            Bus bus2 = transfo.getTerminal2().getBusView().getBus();
            addToAdjacencyList(bus1, bus2, id2num, adjacencyList);
        }
        for (ThreeWindingsTransformer transfo : getNetwork().getThreeWindingsTransformers()) {
            Bus bus1 = transfo.getLeg1().getTerminal().getBusView().getBus();
            Bus bus2 = transfo.getLeg2().getTerminal().getBusView().getBus();
            Bus bus3 = transfo.getLeg3().getTerminal().getBusView().getBus();
            addToAdjacencyList(bus1, bus2, id2num, adjacencyList);
            addToAdjacencyList(bus1, bus3, id2num, adjacencyList);
            addToAdjacencyList(bus2, bus3, id2num, adjacencyList);
        }
    }

    private String getComponentLabel() {
        return label;
    }

    protected abstract Network getNetwork();

    protected abstract C createComponent(int num, int size);

    protected abstract void setComponentNumber(Bus bus, int num);

}