MatrixBasedPartitioner.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/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.flow_decomposition.partitioners;
import com.powsybl.flow_decomposition.*;
import com.powsybl.iidm.network.Branch;
import com.powsybl.iidm.network.Country;
import com.powsybl.iidm.network.Identifiable;
import com.powsybl.iidm.network.Network;
import com.powsybl.loadflow.LoadFlowParameters;
import com.powsybl.sensitivity.SensitivityAnalysis;
import com.powsybl.sensitivity.SensitivityVariableType;
import java.util.*;
import java.util.stream.Collectors;
import static com.powsybl.flow_decomposition.DecomposedFlow.*;
import static com.powsybl.flow_decomposition.NetworkUtil.LOOP_FLOWS_COLUMN_PREFIX;
/**
* @author Sebastien Murgey {@literal <sebastien.murgey at rte-france.com>}
*/
public class MatrixBasedPartitioner implements FlowPartitioner {
private final LoadFlowParameters loadFlowParameters;
private final FlowDecompositionParameters parameters;
private final SensitivityAnalysis.Runner sensitivityAnalysisRunner;
private final FlowDecompositionObserverList observers;
public MatrixBasedPartitioner(LoadFlowParameters loadFlowParameters, FlowDecompositionParameters parameters, SensitivityAnalysis.Runner sensitivityAnalysisRunner, FlowDecompositionObserverList observers) {
this.loadFlowParameters = loadFlowParameters;
this.parameters = parameters;
this.sensitivityAnalysisRunner = sensitivityAnalysisRunner;
this.observers = observers;
}
@Override
public Map<String, FlowPartition> computeFlowPartitions(Network network, Set<Branch> xnecs, Map<Country, Double> netPositions, Map<Country, Map<String, Double>> glsks) {
NetworkMatrixIndexes networkMatrixIndexes = new NetworkMatrixIndexes(network, new ArrayList<>(xnecs));
SparseMatrixWithIndexesTriplet nodalInjectionsMatrix = getNodalInjectionsMatrix(network, netPositions,
networkMatrixIndexes, glsks);
SensitivityAnalyser sensitivityAnalyser = getSensitivityAnalyser(network, networkMatrixIndexes);
SparseMatrixWithIndexesTriplet ptdfMatrix = getPtdfMatrix(networkMatrixIndexes, sensitivityAnalyser);
SparseMatrixWithIndexesTriplet psdfMatrix = getPsdfMatrix(networkMatrixIndexes, sensitivityAnalyser);
// Flows
SparseMatrixWithIndexesCSC allocatedLoopFlowsMatrix =
SparseMatrixWithIndexesCSC.mult(ptdfMatrix.toCSCMatrix(), nodalInjectionsMatrix.toCSCMatrix());
PstFlowComputer pstFlowComputer = new PstFlowComputer();
SparseMatrixWithIndexesCSC pstFlowMatrix = pstFlowComputer.run(network, networkMatrixIndexes, psdfMatrix);
return xnecs.stream().collect(Collectors.toMap(
Identifiable::getId,
xnec -> flowPartitionForXnec(xnec, allocatedLoopFlowsMatrix.toMap().getOrDefault(xnec.getId(), Collections.emptyMap()), pstFlowMatrix.toMap().getOrDefault(xnec.getId(), Collections.emptyMap()).getOrDefault(PST_COLUMN_NAME, NO_FLOW))
));
}
private SparseMatrixWithIndexesTriplet getNodalInjectionsMatrix(Network network,
Map<Country, Double> netPositions,
NetworkMatrixIndexes networkMatrixIndexes,
Map<Country, Map<String, Double>> glsks) {
NodalInjectionComputer nodalInjectionComputer = new NodalInjectionComputer(networkMatrixIndexes);
SparseMatrixWithIndexesTriplet nodalInjectionsMatrix = nodalInjectionComputer.run(network, glsks, netPositions);
observers.computedNodalInjectionsMatrix(nodalInjectionsMatrix.toMap());
return nodalInjectionsMatrix;
}
private SensitivityAnalyser getSensitivityAnalyser(Network network, NetworkMatrixIndexes networkMatrixIndexes) {
return new SensitivityAnalyser(loadFlowParameters, parameters, sensitivityAnalysisRunner, network, networkMatrixIndexes);
}
private SparseMatrixWithIndexesTriplet getPtdfMatrix(NetworkMatrixIndexes networkMatrixIndexes,
SensitivityAnalyser sensitivityAnalyser) {
SparseMatrixWithIndexesTriplet ptdfMatrix = sensitivityAnalyser.run(networkMatrixIndexes.getNodeIdList(),
networkMatrixIndexes.getNodeIndex(),
SensitivityVariableType.INJECTION_ACTIVE_POWER);
observers.computedPtdfMatrix(ptdfMatrix.toMap());
return ptdfMatrix;
}
private SparseMatrixWithIndexesTriplet getPsdfMatrix(NetworkMatrixIndexes networkMatrixIndexes,
SensitivityAnalyser sensitivityAnalyser) {
SparseMatrixWithIndexesTriplet psdfMatrix = sensitivityAnalyser.run(networkMatrixIndexes.getPstList(),
networkMatrixIndexes.getPstIndex(), SensitivityVariableType.TRANSFORMER_PHASE);
observers.computedPsdfMatrix(psdfMatrix.toMap());
return psdfMatrix;
}
private FlowPartition flowPartitionForXnec(Branch xnec, Map<String, Double> allocatedLoopFlowsMap, double pstFlow) {
double allocatedFlow = allocatedLoopFlowsMap.getOrDefault(ALLOCATED_COLUMN_NAME, NO_FLOW);
double xnodeFlow = allocatedLoopFlowsMap.getOrDefault(XNODE_COLUMN_NAME, NO_FLOW);
Country country1 = NetworkUtil.getTerminalCountry(xnec.getTerminal1());
Country country2 = NetworkUtil.getTerminalCountry(xnec.getTerminal2());
double internalFlow = extractInternalFlow(allocatedLoopFlowsMap, country1, country2);
Map<Country, Double> loopFlow = allocatedLoopFlowsMap.entrySet().stream()
.filter(entry -> entry.getKey().startsWith(LOOP_FLOWS_COLUMN_PREFIX))
.collect(Collectors.toMap(
entry -> Country.valueOf(entry.getKey().substring((LOOP_FLOWS_COLUMN_PREFIX + " ").length())),
Map.Entry::getValue
));
return new FlowPartition(internalFlow, allocatedFlow, loopFlow, pstFlow, xnodeFlow);
}
private double extractInternalFlow(Map<String, Double> loopFlowsMap, Country country1, Country country2) {
if (Objects.equals(country1, country2)) {
return Optional.ofNullable(loopFlowsMap.remove(NetworkUtil.getLoopFlowIdFromCountry(country1)))
.orElse(NO_FLOW);
}
return NO_FLOW;
}
}