NodalInjectionComputer.java

/*
 * 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/.
 */
package com.powsybl.flow_decomposition.partitioners;

import com.powsybl.flow_decomposition.NetworkUtil;
import com.powsybl.iidm.network.Country;
import com.powsybl.iidm.network.Injection;
import com.powsybl.iidm.network.Network;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.powsybl.flow_decomposition.DecomposedFlow.ALLOCATED_COLUMN_NAME;
import static com.powsybl.flow_decomposition.DecomposedFlow.NO_FLOW;
import static com.powsybl.flow_decomposition.DecomposedFlow.XNODE_COLUMN_NAME;

/**
 * @author Hugo Schindler {@literal <hugo.schindler at rte-france.com>}
 * @author Sebastien Murgey {@literal <sebastien.murgey at rte-france.com>}
 */
class NodalInjectionComputer {
    private static final double DEFAULT_GLSK_FACTOR = 0.0;
    public static final double DEFAULT_NET_POSITION = 0.0;
    private final NetworkMatrixIndexes networkMatrixIndexes;

    NodalInjectionComputer(NetworkMatrixIndexes networkMatrixIndexes) {
        this.networkMatrixIndexes = networkMatrixIndexes;
    }

    SparseMatrixWithIndexesTriplet run(Network network,
                                       Map<Country, Map<String, Double>> glsks,
                                       Map<Country, Double> netPositions) {
        ReferenceNodalInjectionComputer referenceNodalInjectionComputer = new ReferenceNodalInjectionComputer();
        Map<String, Double> nodalInjectionDcReference = referenceNodalInjectionComputer.run(networkMatrixIndexes.getNodeList());
        Map<String, Double> nodalInjectionForXNodeFlow = referenceNodalInjectionComputer.run(networkMatrixIndexes.getUnmergedXNodeList());
        Map<String, Double> nodalInjectionsForAllocatedFlow = getNodalInjectionsForAllocatedFlows(glsks, netPositions);

        SparseMatrixWithIndexesTriplet nodalInjectionMatrix = getEmptyNodalInjectionMatrix(glsks,
            nodalInjectionsForAllocatedFlow.size() + nodalInjectionDcReference.size() + nodalInjectionForXNodeFlow.size());
        fillNodalInjectionsWithAllocatedFlow(nodalInjectionsForAllocatedFlow, nodalInjectionMatrix);
        fillNodalInjectionsWithXNodeFlow(nodalInjectionForXNodeFlow, nodalInjectionMatrix);
        fillNodalInjectionsWithLoopFlow(network, nodalInjectionsForAllocatedFlow, nodalInjectionForXNodeFlow, nodalInjectionDcReference, nodalInjectionMatrix);
        return nodalInjectionMatrix;
    }

    private Map<String, Double> getNodalInjectionsForAllocatedFlows(Map<Country, Map<String, Double>> glsks,
                                                                    Map<Country, Double> netPositions) {
        return networkMatrixIndexes.getNodeList().stream()
            .collect(Collectors.toMap(
                    Injection::getId,
                    injection -> getIndividualNodalInjectionForAllocatedFlows(injection, glsks, netPositions)
                )
            );
    }

    private double getIndividualNodalInjectionForAllocatedFlows(Injection<?> injection,
                                                                Map<Country, Map<String, Double>> glsks,
                                                                Map<Country, Double> netPositions) {
        Country injectionCountry = NetworkUtil.getInjectionCountry(injection);
        return glsks.get(injectionCountry).getOrDefault(injection.getId(), DEFAULT_GLSK_FACTOR)
            * netPositions.getOrDefault(injectionCountry, DEFAULT_NET_POSITION);
    }

    private SparseMatrixWithIndexesTriplet getEmptyNodalInjectionMatrix(Map<Country, Map<String, Double>> glsks, Integer size) {
        List<String> columns = glsks.keySet().stream()
            .map(NetworkUtil::getLoopFlowIdFromCountry)
            .collect(Collectors.toList());
        columns.add(ALLOCATED_COLUMN_NAME);
        columns.add(XNODE_COLUMN_NAME);
        return new SparseMatrixWithIndexesTriplet(
            networkMatrixIndexes.getNodeIndex(), NetworkUtil.getIndex(columns), size);
    }

    private void fillNodalInjectionsWithAllocatedFlow(Map<String, Double> nodalInjectionsForAllocatedFlow,
                                                      SparseMatrixWithIndexesTriplet nodalInjectionMatrix) {
        nodalInjectionsForAllocatedFlow.forEach(
            (injectionId, injectionValue) -> nodalInjectionMatrix.addItem(injectionId,
                ALLOCATED_COLUMN_NAME, injectionValue)
        );
    }

    private void fillNodalInjectionsWithXNodeFlow(Map<String, Double> xNodeInjection,
                                                  SparseMatrixWithIndexesTriplet nodalInjectionMatrix) {
        xNodeInjection.forEach(
            (injectionId, injectionValue) -> nodalInjectionMatrix.addItem(injectionId,
                XNODE_COLUMN_NAME, injectionValue)
        );
    }

    private void fillNodalInjectionsWithLoopFlow(Network network,
                                                 Map<String, Double> nodalInjectionsForAllocatedFlow,
                                                 Map<String, Double> nodalInjectionsForXNodeFlow,
                                                 Map<String, Double> nodalInjectionDcReference,
                                                 SparseMatrixWithIndexesTriplet nodalInjectionMatrix) {
        networkMatrixIndexes.getNodeList().forEach(
            node -> {
                String nodeId = node.getId();
                nodalInjectionMatrix.addItem(
                    nodeId,
                    NetworkUtil.getLoopFlowIdFromCountry(network, nodeId),
                    computeNodalInjectionForLoopFLow(
                        nodalInjectionDcReference.get(nodeId),
                        nodalInjectionsForAllocatedFlow.get(nodeId),
                        nodalInjectionsForXNodeFlow.getOrDefault(nodeId, NO_FLOW)
                    )
                );
            });
    }

    private double computeNodalInjectionForLoopFLow(double referenceDcNodalInjection,
                                                    double nodalInjectionForAllocatedFlow,
                                                    double nodalInjectionForXNodeFlow) {
        return referenceDcNodalInjection - nodalInjectionForAllocatedFlow - nodalInjectionForXNodeFlow;
    }
}