diff --git a/docs/flow_decomposition/algorithm-description.md b/docs/flow_decomposition/algorithm-description.md index f1d392db..2f30724c 100644 --- a/docs/flow_decomposition/algorithm-description.md +++ b/docs/flow_decomposition/algorithm-description.md @@ -33,6 +33,15 @@ compensated at both sides proportionally to the resistance of each half line. ## Nodal Injections partitioning +> **_NOTE:_** In PowSyBl terminology, nodal injections are injection connectables that +> - are not paired dangling lines, +> - are connected to the network, +> - are in the main synchronous component (for sensitivity computation reasons), +> - are not a bus bar section (because of a lack of reference injection), +> - are not shunt compensator or static var compensator (for sensitivity computation reasons). + +> **_NOTE:_** In PowSyBl terminology, xnodes are unpaired dangling lines connected to the network and in the main synchronous component. + In order to distinguish internal/loop flows and allocated flows, the nodal injections in each zone must de decomposed in two parts: - Nodal injections for allocated flows - Nodal injections for loop flows and internal flows @@ -63,6 +72,16 @@ where: ## Sensitivity analysis +> **_NOTE:_** In PowSyBl terminology, only two windings transformers are considered. PSTs must: +> - be connected to the network, +> - have a phase tap changer, +> - have a neutral step on the phase tap changer, +> - have a bus at each terminal, +> - be connected to the main synchronous component. +> Therefore, three windings transformers are not supported. + +> **_NOTE:_** Each element of the sensitivity computation must be connected to the main synchronous component (variables (injections) and functions (branches)). + In order to assess the linear impact (implied by the DC approximation) of each nodal injection and phase shift transformer on the network elements' flow, a sensitivity analysis is run. @@ -89,7 +108,7 @@ where: - $\mathrm{F}_\mathrm{PST}$ is the vector of the network element PST (phase shift transformer) flow, - $\mathrm{F}_\mathrm{X}$ is the vector of the network element xnode flow, - $\mathrm{AM}$ is the allocation matrix, which associates each injection to its zone. $\mathrm{AM}_{ij}$ = 1 if node i is in zone j, 0 otherwise, -- $\mathrm{\Delta}_\mathrm{PST}$ is the phase shift transformers angle vector, +- $\mathrm{\Delta}_\mathrm{PST}$ is the phase shift transformers angle vector. The neutral tap position of each PST is used to compute this difference. ## Flow parts rescaling diff --git a/flow-decomposition/pom.xml b/flow-decomposition/pom.xml index c2618d17..16e3d914 100644 --- a/flow-decomposition/pom.xml +++ b/flow-decomposition/pom.xml @@ -14,95 +14,102 @@ Implementation of ACER methodology for flow decomposition + com.powsybl powsybl-commons com.powsybl - powsybl-iidm-api + powsybl-glsk-document-api com.powsybl - powsybl-loadflow-api + powsybl-iidm-api com.powsybl - powsybl-glsk-document-api + powsybl-loadflow-api com.powsybl powsybl-sensitivity-analysis-api + + org.apache.commons + commons-csv + ${commonscsv.version} + org.ejml ejml-all ${ejml.version} + - org.apache.commons - commons-csv - ${commonscsv.version} + com.powsybl + powsybl-iidm-impl + runtime com.powsybl - powsybl-glsk-document-io-api - test + powsybl-iidm-serde + runtime com.powsybl - powsybl-glsk-document-ucte - test + powsybl-open-loadflow + runtime com.powsybl - powsybl-iidm-impl + powsybl-ucte-converter runtime - com.powsybl - powsybl-iidm-serde + ch.qos.logback + logback-classic runtime + - com.powsybl - powsybl-cgmes-conversion + com.google.jimfs + jimfs test com.powsybl - powsybl-cgmes-conformity + powsybl-config-test test com.powsybl - powsybl-triple-store-impl-rdf4j + powsybl-cgmes-conformity test com.powsybl - powsybl-open-loadflow - ${powsyblopenloadflow.version} - runtime + powsybl-cgmes-conversion + test com.powsybl - powsybl-ucte-converter - runtime + powsybl-glsk-document-io-api + test - ch.qos.logback - logback-classic - runtime + com.powsybl + powsybl-glsk-document-ucte + test - com.google.jimfs - jimfs + com.powsybl + powsybl-iidm-test test com.powsybl - powsybl-config-test + powsybl-triple-store-impl-rdf4j test diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlow.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlow.java index edceba81..63b4b55a 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlow.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlow.java @@ -60,7 +60,7 @@ public String getContingencyId() { } public String getId() { - return NetworkUtil.getXnecId(contingencyId, branchId); + return getXnecId(contingencyId, branchId); } public Country getCountry1() { @@ -140,4 +140,8 @@ private TreeMap getAllKeyMap() { localDecomposedFlowMap.putAll(loopFlowsMap); return localDecomposedFlowMap; } + + public static String getXnecId(String contingencyId, String branchId) { + return contingencyId.isEmpty() ? branchId : String.format("%s_%s", branchId, contingencyId); + } } diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionComputer.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionComputer.java index 0dd0c290..51bee3f3 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionComputer.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionComputer.java @@ -141,31 +141,27 @@ private void decomposeFlowForState(Network network, Map netPositions, Map> glsks, LoadFlowRunningService.Result loadFlowServiceAcResult) { - saveAcReferenceFlows(flowDecompositionResultsBuilder, xnecList, loadFlowServiceAcResult); - compensateLosses(network); - observers.computedAcFlows(network, loadFlowServiceAcResult); + // AC load flow + saveAcReferenceFlows(flowDecompositionResultsBuilder, network, xnecList, loadFlowServiceAcResult); - // None - NetworkMatrixIndexes networkMatrixIndexes = new NetworkMatrixIndexes(network, new ArrayList<>(xnecList)); - observers.computedAcNodalInjections(networkMatrixIndexes, loadFlowServiceAcResult.fallbackHasBeenActivated()); + // Losses compensation + compensateLosses(network); + // DC load flow runDcLoadFlow(network); - observers.computedDcFlows(network); - observers.computedDcNodalInjections(networkMatrixIndexes); + saveDcReferenceFlow(flowDecompositionResultsBuilder, network, xnecList); - SparseMatrixWithIndexesTriplet nodalInjectionsMatrix = getNodalInjectionsMatrix(network, - netPositions, networkMatrixIndexes, glsks); - saveDcReferenceFlow(flowDecompositionResultsBuilder, xnecList); - observers.computedNodalInjectionsMatrix(nodalInjectionsMatrix); + // Nodal injections + NetworkMatrixIndexes networkMatrixIndexes = new NetworkMatrixIndexes(network, new ArrayList<>(xnecList)); + SparseMatrixWithIndexesTriplet nodalInjectionsMatrix = getNodalInjectionsMatrix(network, netPositions, + networkMatrixIndexes, glsks); - // DC Sensi + // Sensitivity SensitivityAnalyser sensitivityAnalyser = getSensitivityAnalyser(network, networkMatrixIndexes); SparseMatrixWithIndexesTriplet ptdfMatrix = getPtdfMatrix(networkMatrixIndexes, sensitivityAnalyser); - observers.computedPtdfMatrix(ptdfMatrix); SparseMatrixWithIndexesTriplet psdfMatrix = getPsdfMatrix(networkMatrixIndexes, sensitivityAnalyser); - observers.computedPsdfMatrix(psdfMatrix); - // None + // Flows computeAllocatedAndLoopFlows(flowDecompositionResultsBuilder, nodalInjectionsMatrix, ptdfMatrix); computePstFlows(network, flowDecompositionResultsBuilder, networkMatrixIndexes, psdfMatrix); @@ -184,11 +180,13 @@ private LoadFlowRunningService.Result runAcLoadFlow(Network network) { return loadFlowRunningService.runAcLoadflow(network, loadFlowParameters, parameters.isDcFallbackEnabledAfterAcDivergence()); } - private void saveAcReferenceFlows(FlowDecompositionResults.PerStateBuilder flowDecompositionResultBuilder, Set xnecList, LoadFlowRunningService.Result loadFlowServiceAcResult) { + private void saveAcReferenceFlows(FlowDecompositionResults.PerStateBuilder flowDecompositionResultBuilder, Network network, Set xnecList, LoadFlowRunningService.Result loadFlowServiceAcResult) { Map acTerminal1ReferenceFlows = FlowComputerUtils.calculateAcTerminalReferenceFlows(xnecList, loadFlowServiceAcResult, TwoSides.ONE); Map acTerminal2ReferenceFlows = FlowComputerUtils.calculateAcTerminalReferenceFlows(xnecList, loadFlowServiceAcResult, TwoSides.TWO); flowDecompositionResultBuilder.saveAcTerminal1ReferenceFlow(acTerminal1ReferenceFlows); flowDecompositionResultBuilder.saveAcTerminal2ReferenceFlow(acTerminal2ReferenceFlows); + observers.computedAcFlows(network, loadFlowServiceAcResult); + observers.computedAcNodalInjections(network, loadFlowServiceAcResult.fallbackHasBeenActivated()); } private Map getZonesNetPosition(Network network) { @@ -220,11 +218,15 @@ private SparseMatrixWithIndexesTriplet getNodalInjectionsMatrix(Network network, NetworkMatrixIndexes networkMatrixIndexes, Map> glsks) { NodalInjectionComputer nodalInjectionComputer = new NodalInjectionComputer(networkMatrixIndexes); - return nodalInjectionComputer.run(network, glsks, netPositions); + SparseMatrixWithIndexesTriplet nodalInjectionsMatrix = nodalInjectionComputer.run(network, glsks, netPositions); + observers.computedNodalInjectionsMatrix(nodalInjectionsMatrix); + return nodalInjectionsMatrix; } - private void saveDcReferenceFlow(FlowDecompositionResults.PerStateBuilder flowDecompositionResultBuilder, Set xnecList) { + private void saveDcReferenceFlow(FlowDecompositionResults.PerStateBuilder flowDecompositionResultBuilder, Network network, Set xnecList) { flowDecompositionResultBuilder.saveDcReferenceFlow(FlowComputerUtils.getTerminalReferenceFlow(xnecList, TwoSides.ONE)); + observers.computedDcFlows(network); + observers.computedDcNodalInjections(network); } private SensitivityAnalyser getSensitivityAnalyser(Network network, NetworkMatrixIndexes networkMatrixIndexes) { @@ -233,9 +235,11 @@ private SensitivityAnalyser getSensitivityAnalyser(Network network, NetworkMatri private SparseMatrixWithIndexesTriplet getPtdfMatrix(NetworkMatrixIndexes networkMatrixIndexes, SensitivityAnalyser sensitivityAnalyser) { - return sensitivityAnalyser.run(networkMatrixIndexes.getNodeIdList(), + SparseMatrixWithIndexesTriplet ptdfMatrix = sensitivityAnalyser.run(networkMatrixIndexes.getNodeIdList(), networkMatrixIndexes.getNodeIndex(), SensitivityVariableType.INJECTION_ACTIVE_POWER); + observers.computedPtdfMatrix(ptdfMatrix); + return ptdfMatrix; } private void computeAllocatedAndLoopFlows(FlowDecompositionResults.PerStateBuilder flowDecompositionResultBuilder, @@ -248,8 +252,10 @@ private void computeAllocatedAndLoopFlows(FlowDecompositionResults.PerStateBuild private SparseMatrixWithIndexesTriplet getPsdfMatrix(NetworkMatrixIndexes networkMatrixIndexes, SensitivityAnalyser sensitivityAnalyser) { - return sensitivityAnalyser.run(networkMatrixIndexes.getPstList(), + SparseMatrixWithIndexesTriplet psdfMatrix = sensitivityAnalyser.run(networkMatrixIndexes.getPstList(), networkMatrixIndexes.getPstIndex(), SensitivityVariableType.TRANSFORMER_PHASE); + observers.computedPsdfMatrix(psdfMatrix); + return psdfMatrix; } private void computePstFlows(Network network, diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionObserverList.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionObserverList.java index 4a56c9a3..36ad609c 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionObserverList.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionObserverList.java @@ -107,24 +107,24 @@ public void computedDcFlows(Network network) { } } - public void computedAcNodalInjections(NetworkMatrixIndexes networkMatrixIndexes, boolean fallbackHasBeenActivated) { + public void computedAcNodalInjections(Network network, boolean fallbackHasBeenActivated) { if (observers.isEmpty()) { return; } - Map results = new ReferenceNodalInjectionComputer().run(networkMatrixIndexes.getNodeList()); + Map results = new ReferenceNodalInjectionComputer().run(NetworkUtil.getNodeList(network)); for (FlowDecompositionObserver o : observers) { o.computedAcNodalInjections(results, fallbackHasBeenActivated); } } - public void computedDcNodalInjections(NetworkMatrixIndexes networkMatrixIndexes) { + public void computedDcNodalInjections(Network network) { if (observers.isEmpty()) { return; } - Map results = new ReferenceNodalInjectionComputer().run(networkMatrixIndexes.getNodeList()); + Map results = new ReferenceNodalInjectionComputer().run(NetworkUtil.getNodeList(network)); for (FlowDecompositionObserver o : observers) { o.computedDcNodalInjections(results); diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionResults.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionResults.java index f6329f9b..255f0439 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionResults.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionResults.java @@ -77,7 +77,7 @@ void saveDcReferenceFlow(Map dcReferenceFlow) { void build(DecomposedFlowRescaler decomposedFlowRescaler) { allocatedAndLoopFlowsMatrix.toMap() .forEach((branchId, decomposedFlow) -> { - String xnecId = NetworkUtil.getXnecId(contingencyId, branchId); + String xnecId = DecomposedFlow.getXnecId(contingencyId, branchId); decomposedFlowMap.put(xnecId, createDecomposedFlow(branchId, decomposedFlow, decomposedFlowRescaler)); }); } diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/LossesCompensator.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/LossesCompensator.java index aff9ddea..874541f8 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/LossesCompensator.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/LossesCompensator.java @@ -18,10 +18,12 @@ * * @author Sebastien Murgey {@literal } * @author Hugo Schindler {@literal } + * @author Caio Luke {@literal } */ class LossesCompensator { private final double epsilon; private Integer networkHash; + public static final String LOSSES_ID_PREFIX = "LOSSES "; LossesCompensator(double epsilon) { this.epsilon = epsilon; @@ -33,7 +35,7 @@ class LossesCompensator { } static String getLossesId(String id) { - return String.format("LOSSES %s", id); + return LOSSES_ID_PREFIX + id; } public void run(Network network) { @@ -58,39 +60,17 @@ private void addZeroMWLossesLoadsOnBuses(Network network) { private void compensateLossesOnBranches(Network network) { network.getBranchStream() - .filter(this::hasBuses) - .filter(this::hasP0s) + .filter(branch -> branch.getTerminal1().isConnected() || branch.getTerminal2().isConnected()) .forEach(branch -> compensateLossesOnBranch(network, branch)); } - private boolean hasBus(Terminal terminal) { - return terminal.getBusBreakerView().getBus() != null; - } - - private boolean hasBuses(Branch branch) { - return hasBus(branch.getTerminal1()) && hasBus(branch.getTerminal2()); - } - - private boolean hasP0(Terminal terminal) { - return !Double.isNaN(terminal.getP()); - } - - private boolean hasP0s(Branch branch) { - return hasP0(branch.getTerminal1()) && hasP0(branch.getTerminal2()); - } - private static void addZeroMWLossesLoad(Network network, String busId) { String lossesId = getLossesId(busId); Bus bus = network.getBusBreakerView().getBus(busId); switch (bus.getVoltageLevel().getTopologyKind()) { - case BUS_BREAKER: - addZeroMWLossesLoadForBusBreakerTopology(bus, lossesId); - return; - case NODE_BREAKER: - addZeroMWLossesLoadForNodeTopology(bus, lossesId); - return; - default: - throw new PowsyblException("This topology is not managed by the loss compensation."); + case BUS_BREAKER -> addZeroMWLossesLoadForBusBreakerTopology(bus, lossesId); + case NODE_BREAKER -> addZeroMWLossesLoadForNodeBreakerTopology(bus, lossesId); + default -> throw new PowsyblException("Topology not supported by loss compensation."); } } @@ -100,10 +80,11 @@ private static void addZeroMWLossesLoadForBusBreakerTopology(Bus bus, String los .setBus(bus.getId()) .setP0(0) .setQ0(0) + .setFictitious(true) .add(); } - private static void addZeroMWLossesLoadForNodeTopology(Bus bus, String lossesId) { + private static void addZeroMWLossesLoadForNodeBreakerTopology(Bus bus, String lossesId) { VoltageLevel voltageLevel = bus.getVoltageLevel(); VoltageLevel.NodeBreakerView nodeBreakerView = voltageLevel.getNodeBreakerView(); int nodeNum = nodeBreakerView.getMaximumNodeIndex() + 1; @@ -116,12 +97,13 @@ private static void addZeroMWLossesLoadForNodeTopology(Bus bus, String lossesId) .setNode(nodeNum) .setP0(0) .setQ0(0) + .setFictitious(true) .add(); } private void compensateLossesOnBranch(Network network, Branch branch) { - if (branch instanceof TieLine) { - compensateLossesOnTieLine(network, (TieLine) branch); + if (branch instanceof TieLine tieLine) { + compensateLossesOnTieLine(network, tieLine); } else { Terminal sendingTerminal = getSendingTerminal(branch); double losses = branch.getTerminal1().getP() + branch.getTerminal2().getP(); @@ -144,13 +126,26 @@ private void compensateLossesOnTieLine(Network network, TieLine tieLine) { } private void updateLoadForLossesOnTerminal(Network network, Terminal terminal, double losses) { - if (Math.abs(losses) > epsilon) { + if (losses > epsilon) { Load load = network.getLoad(getLossesId(terminal.getBusBreakerView().getBus().getId())); load.setP0(load.getP0() + losses); } } private Terminal getSendingTerminal(Branch branch) { - return branch.getTerminal1().getP() > 0 ? branch.getTerminal1() : branch.getTerminal2(); + Terminal terminal1 = branch.getTerminal1(); + Terminal terminal2 = branch.getTerminal2(); + + // if only one terminal is connected, that is the sending terminal + if (!(terminal1.isConnected() && terminal2.isConnected())) { + return terminal1.isConnected() ? terminal1 : terminal2; + } + + // terminal with greater P is the sending terminal + if (terminal1.getP() >= terminal2.getP()) { + return terminal1; + } else { + return terminal2; + } } } diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetPositionComputer.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetPositionComputer.java index 3fe22400..44356ac2 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetPositionComputer.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetPositionComputer.java @@ -13,7 +13,7 @@ /** * @author Sebastien Murgey {@literal } - * @author Hugo Schindler{@literal } + * @author Hugo Schindler{@literal } */ class NetPositionComputer { Map run(Network network) { diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetworkMatrixIndexes.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetworkMatrixIndexes.java index dc4245e3..358eac3a 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetworkMatrixIndexes.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetworkMatrixIndexes.java @@ -10,13 +10,9 @@ import java.util.List; import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; /** - * @author Hugo Schindler{@literal } + * @author Hugo Schindler{@literal } * @author Sebastien Murgey{@literal } */ class NetworkMatrixIndexes { @@ -31,13 +27,13 @@ class NetworkMatrixIndexes { NetworkMatrixIndexes(Network network, List xnecList) { this.xnecList = xnecList; - nodeList = getNodeList(network); + nodeList = NetworkUtil.getNodeList(network); nodeIdList = getNodeIdList(nodeList); - pstList = getPstIdList(network); - xnecIndex = getXnecIndex(this.xnecList); + pstList = NetworkUtil.getPstIdList(network); + xnecIndex = NetworkUtil.getIndex(getXnecIdList(this.xnecList)); nodeIndex = NetworkUtil.getIndex(nodeIdList); pstIndex = NetworkUtil.getIndex(pstList); - xnodeList = getXNodeList(network); + xnodeList = NetworkUtil.getXNodeList(network); } List getXnecList() { @@ -76,74 +72,11 @@ public List> getUnmergedXNodeList() { return xnodeList; } - private List> getNodeList(Network network) { - return getAllNetworkInjections(network) - .filter(this::isNotPairedDanglingLine) - .filter(this::isInjectionConnected) - .filter(this::isInjectionInMainSynchronousComponent) - .filter(this::managedInjectionTypes) - .collect(Collectors.toList()); - } - - private boolean managedInjectionTypes(Injection injection) { - return !(injection instanceof BusbarSection || injection instanceof ShuntCompensator || injection instanceof StaticVarCompensator); // TODO Remove this fix once the active power computation after a DC load flow is fixed in OLF - } - - private Stream> getAllNetworkInjections(Network network) { - return network.getConnectableStream() - .filter(Injection.class::isInstance) - .map(connectable -> (Injection) connectable); - } - - private boolean isInjectionConnected(Injection injection) { - return injection.getTerminal().isConnected(); - } - - private boolean isNotPairedDanglingLine(Injection injection) { - return !(injection instanceof DanglingLine && ((DanglingLine) injection).isPaired()); - } - - private boolean isInjectionInMainSynchronousComponent(Injection injection) { - return NetworkUtil.isTerminalInMainSynchronousComponent(injection.getTerminal()); - } - private List getNodeIdList(List> nodeList) { - return nodeList.stream() - .map(Injection::getId) - .collect(Collectors.toList()); - } - - private List getPstIdList(Network network) { - return network.getTwoWindingsTransformerStream() - .filter(this::isPst) - .filter(this::hasNeutralStep) - .map(Identifiable::getId) - .collect(Collectors.toList()); - } - - private boolean isPst(TwoWindingsTransformer twt) { - return twt.getPhaseTapChanger() != null; - } - - private boolean hasNeutralStep(TwoWindingsTransformer pst) { - return pst.getPhaseTapChanger().getNeutralStep().isPresent(); - } - - private Map getXnecIndex(List xnecList) { - return IntStream.range(0, xnecList.size()) - .boxed() - .collect(Collectors.toMap( - i -> xnecList.get(i).getId(), - Function.identity() - )); + return nodeList.stream().map(Injection::getId).toList(); } - private List> getXNodeList(Network network) { - return network.getDanglingLineStream() - .filter(dl -> !dl.isPaired()) - .filter(this::isInjectionConnected) - .filter(this::isInjectionInMainSynchronousComponent) - .map(danglingLine -> (Injection) danglingLine) - .collect(Collectors.toList()); + private List getXnecIdList(List xnecList) { + return xnecList.stream().map(Identifiable::getId).toList(); } } diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetworkUtil.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetworkUtil.java index 2f5a2641..3c0fd036 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetworkUtil.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/NetworkUtil.java @@ -11,26 +11,40 @@ import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; /** - * @author Hugo Schindler{@literal } + * @author Hugo Schindler{@literal } * @author Sebastien Murgey{@literal } */ public final class NetworkUtil { static final String LOOP_FLOWS_COLUMN_PREFIX = "Loop Flow from"; private NetworkUtil() { - throw new AssertionError("Utility class should not be instantiated"); + // Utility class + } + + public static String getLoopFlowIdFromCountry(Network network, String identifiableId) { + Identifiable identifiable = network.getIdentifiable(identifiableId); + if (identifiable instanceof Injection injection) { + return getLoopFlowIdFromCountry(getInjectionCountry(injection)); + } + throw new PowsyblException(String.format("Identifiable %s must be an Injection", identifiableId)); } public static String getLoopFlowIdFromCountry(Country country) { return String.format("%s %s", LOOP_FLOWS_COLUMN_PREFIX, country.toString()); } + public static Country getInjectionCountry(Injection injection) { + return getTerminalCountry(injection.getTerminal()); + } + public static Country getTerminalCountry(Terminal terminal) { Optional optionalSubstation = terminal.getVoltageLevel().getSubstation(); if (optionalSubstation.isEmpty()) { @@ -40,37 +54,23 @@ public static Country getTerminalCountry(Terminal terminal) { Substation substation = optionalSubstation.get(); Optional optionalCountry = substation.getCountry(); if (optionalCountry.isEmpty()) { - throw new PowsyblException(String.format("Substation %s does not have country property" + + throw new PowsyblException(String.format("Substation %s does not have country property " + "needed for the algorithm.", substation.getId())); } return optionalCountry.get(); } - static String getLoopFlowIdFromCountry(Network network, String identifiableId) { - Identifiable identifiable = network.getIdentifiable(identifiableId); - if (identifiable instanceof Injection) { - return getLoopFlowIdFromCountry(getInjectionCountry((Injection) identifiable)); - } - throw new PowsyblException(String.format("Identifiable %s must be an Injection", identifiableId)); - } - - static Map getIndex(List idList) { + public static Map getIndex(List idList) { return IntStream.range(0, idList.size()) .boxed() - .collect(Collectors.toMap( - idList::get, - Function.identity() - )); - } - - public static Country getInjectionCountry(Injection injection) { - return getTerminalCountry(injection.getTerminal()); + .collect(Collectors.toMap(idList::get, Function.identity())); } public static List getAllValidBranches(Network network) { return network.getBranchStream() .filter(NetworkUtil::isConnected) - .filter(NetworkUtil::isInMainSynchronousComponent) // TODO Is connectedCompenent enough ? + .filter(NetworkUtil::hasABusToEachTerminal) + .filter(NetworkUtil::isInMainSynchronousComponent) .collect(Collectors.toList()); } @@ -78,16 +78,81 @@ private static boolean isConnected(Branch branch) { return branch.getTerminal1().isConnected() && branch.getTerminal2().isConnected(); } + private static boolean hasABusToEachTerminal(Branch branch) { + return hasABusInBusBreakerView(branch.getTerminal1()) && hasABusInBusBreakerView(branch.getTerminal2()); + } + + private static boolean hasABusInBusBreakerView(Terminal terminal1) { + return Objects.nonNull(terminal1.getBusBreakerView().getBus()); + } + private static boolean isInMainSynchronousComponent(Branch branch) { return isTerminalInMainSynchronousComponent(branch.getTerminal1()) && isTerminalInMainSynchronousComponent(branch.getTerminal2()); } - static boolean isTerminalInMainSynchronousComponent(Terminal terminal) { + private static boolean isTerminalInMainSynchronousComponent(Terminal terminal) { + // Sensitivity analysis does not work outside synchronous component... return terminal.getBusBreakerView().getBus().isInMainSynchronousComponent(); } - static String getXnecId(String contingencyId, String branchId) { - return contingencyId.isEmpty() ? branchId : String.format("%s_%s", branchId, contingencyId); + public static List> getNodeList(Network network) { + return getAllNetworkInjections(network) + .filter(NetworkUtil::isNotPairedDanglingLine) + .filter(NetworkUtil::isInjectionConnected) + .filter(NetworkUtil::isInjectionInMainSynchronousComponent) + .filter(NetworkUtil::hasReferenceInjections) + .filter(NetworkUtil::isValidInjectionsForSensitivityComputation) + .toList(); + } + + public static List> getXNodeList(Network network) { + return network.getDanglingLineStream() + .filter(NetworkUtil::isNotPairedDanglingLine) + .filter(NetworkUtil::isInjectionConnected) + .filter(NetworkUtil::isInjectionInMainSynchronousComponent) + .map(danglingLine -> (Injection) danglingLine) + .collect(Collectors.toList()); + } + + private static Stream> getAllNetworkInjections(Network network) { + return network.getConnectableStream() + .filter(Injection.class::isInstance) + .map(connectable -> (Injection) connectable); + } + + private static boolean isNotPairedDanglingLine(Injection injection) { + return !(injection instanceof DanglingLine danglingLine && danglingLine.isPaired()); + } + + private static boolean isInjectionConnected(Injection injection) { + return injection.getTerminal().isConnected(); + } + + private static boolean isInjectionInMainSynchronousComponent(Injection injection) { + return isTerminalInMainSynchronousComponent(injection.getTerminal()); + } + + private static boolean hasReferenceInjections(Injection injection) { + return !(injection instanceof BusbarSection); + } + + private static boolean isValidInjectionsForSensitivityComputation(Injection injection) { + return !(injection instanceof ShuntCompensator || injection instanceof StaticVarCompensator); + } + + public static List getPstIdList(Network network) { + return network.getTwoWindingsTransformerStream() + .filter(NetworkUtil::isConnected) + .filter(PhaseTapChangerHolder::hasPhaseTapChanger) + .filter(NetworkUtil::hasNeutralStep) + .filter(NetworkUtil::hasABusToEachTerminal) + .filter(NetworkUtil::isInMainSynchronousComponent) + .map(Identifiable::getId) + .toList(); + } + + private static boolean hasNeutralStep(TwoWindingsTransformer pst) { + return pst.getPhaseTapChanger().getNeutralStep().isPresent(); } } diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionObserverTest.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionObserverTest.java index 327c9660..2239f08e 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionObserverTest.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionObserverTest.java @@ -44,20 +44,20 @@ private enum Event { * ObserverReport gathers all observed events from the flow decomposition. It keeps the events occuring, and the * matrices */ - private final class ObserverReport implements FlowDecompositionObserver { + private static final class ObserverReport implements FlowDecompositionObserver { - private List events = new LinkedList<>(); + private final List events = new LinkedList<>(); private String currentContingency = null; - private ContingencyValue> eventsPerContingency = new ContingencyValue<>(); + private final ContingencyValue> eventsPerContingency = new ContingencyValue<>(); private Map> glsks; private Map netPositions; - private ContingencyValue>> nodalInjections = new ContingencyValue<>(); - private ContingencyValue>> ptdfs = new ContingencyValue<>(); - private ContingencyValue>> psdfs = new ContingencyValue<>(); - private ContingencyValue> acNodalInjections = new ContingencyValue<>(); - private ContingencyValue> dcNodalInjections = new ContingencyValue<>(); - private ContingencyValue> acFlows = new ContingencyValue<>(); - private ContingencyValue> dcFlows = new ContingencyValue<>(); + private final ContingencyValue>> nodalInjections = new ContingencyValue<>(); + private final ContingencyValue>> ptdfs = new ContingencyValue<>(); + private final ContingencyValue>> psdfs = new ContingencyValue<>(); + private final ContingencyValue> acNodalInjections = new ContingencyValue<>(); + private final ContingencyValue> dcNodalInjections = new ContingencyValue<>(); + private final ContingencyValue> acFlows = new ContingencyValue<>(); + private final ContingencyValue> dcFlows = new ContingencyValue<>(); public List allEvents() { return events; @@ -329,15 +329,33 @@ void testRemoveObserver() { assertTrue(reportRemoved.allEvents().isEmpty()); } + @Test + void testObserverWithEnableLossesCompensation() { + String networkFileName = "19700101_0000_FO4_UX1.uct"; + String branchId = "DB000011 DF000011 1"; + Network network = TestUtils.importNetwork(networkFileName); + XnecProvider xnecProvider = XnecProviderByIds.builder() + .addNetworkElementsOnBasecase(Set.of(branchId)) + .build(); + var flowDecompositionParameters = FlowDecompositionParameters.load().setEnableLossesCompensation(true); + FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters); + var report = new ObserverReport(); + flowComputer.addObserver(report); + flowComputer.run(xnecProvider, network); + + // there are no losses in acNodalInjection + report.acNodalInjections.forBaseCase().forEach((inj, p) -> assertFalse(inj.startsWith(LossesCompensator.LOSSES_ID_PREFIX))); + } + private void assertEventsFired(Collection firedEvents, Event... expectedEvents) { var missing = new HashSet(); Collections.addAll(missing, expectedEvents); missing.removeAll(firedEvents); - assertTrue(missing.isEmpty(), () -> "Missing events: " + missing.toString()); + assertTrue(missing.isEmpty(), () -> "Missing events: " + missing); } private static final class ContingencyValue { - private Map values = new HashMap<>(); + private final Map values = new HashMap<>(); public void put(String contingencyId, T value) { values.put(contingencyId, value); diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionTests.java new file mode 100644 index 00000000..73294242 --- /dev/null +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionTests.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2024, 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; + +import com.powsybl.flow_decomposition.xnec_provider.XnecProviderAllBranches; +import com.powsybl.flow_decomposition.xnec_provider.XnecProviderByIds; +import com.powsybl.flow_decomposition.xnec_provider.XnecProviderUnion; +import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.Network; +import com.powsybl.loadflow.LoadFlowParameters; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.powsybl.flow_decomposition.TestUtils.validateFlowDecomposition; +import static java.lang.Double.NaN; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Hugo Schindler {@literal } + */ +public class FlowDecompositionTests { + + public static final double EPSILON = 1e-6; + + @Test + void testFlowDecompositionOnNetworkWithBusBarSectionOnly() { + Network network = TestUtils.getMicroGridNetworkWithBusBarSectionOnly(); + + FlowDecompositionResults flowDecompositionResults = runFlowDecomposition(network, new XnecProviderAllBranches()); + assertEquals(6, flowDecompositionResults.getDecomposedFlowMap().size()); + validateFlowDecomposition(flowDecompositionResults, "a708c3bc-465d-4fe7-b6ef-6fa6408a62b0", "a708c3bc-465d-4fe7-b6ef-6fa6408a62b0", "", Country.BE, Country.BE, 105.367771, 115.127961, -8.071105, 30.705093, 33.029749, 59.464224, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "b58bf21a-096a-4dae-9a01-3f03b60c24c7", "b58bf21a-096a-4dae-9a01-3f03b60c24c7", "", Country.BE, Country.BE, -116.377022, -118.547677, -0.000000, 126.160372, -0.000000, -7.612696, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "b94318f6-6d24-4f56-96b9-df2531ad6543", "b94318f6-6d24-4f56-96b9-df2531ad6543", "", Country.BE, Country.BE, 0.330698, 0.287049, -4.994947, 76.274497, -33.029749, -37.962752, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "df16b3dd-c905-4a6f-84ee-f067be86f5da", "df16b3dd-c905-4a6f-84ee-f067be86f5da", "", Country.BE, Country.BE, -99.872370, -103.741300, 0.000000, 103.741300, -0.000000, -0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "e482b89a-fa84-4ea9-8e70-a83d44790957", "e482b89a-fa84-4ea9-8e70-a83d44790957", "", Country.BE, Country.BE, -94.838378, -84.584990, 13.066052, -106.979590, -0.000000, 178.498528, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "ffbabc27-1ccd-4fdc-b037-e341706c8d29", "ffbabc27-1ccd-4fdc-b037-e341706c8d29", "", Country.BE, Country.BE, -53.579738, -57.103247, -0.000000, 60.770208, -0.000000, -3.666960, 0.000000, 0.000000, 0.000000, 0.000000); + assertEquals(1, flowDecompositionResults.getZoneSet().size()); + assertTrue(flowDecompositionResults.getZoneSet().contains(Country.BE)); + + } + + @Test + void testFlowDecompositionOnNetworkWithShuntCompensatorOnly() { + Network network = TestUtils.getMicroGridNetworkWithShuntCompensatorOnly(); + + FlowDecompositionResults flowDecompositionResults = runFlowDecomposition(network, new XnecProviderAllBranches()); + assertEquals(6, flowDecompositionResults.getDecomposedFlowMap().size()); + validateFlowDecomposition(flowDecompositionResults, "a708c3bc-465d-4fe7-b6ef-6fa6408a62b0", "a708c3bc-465d-4fe7-b6ef-6fa6408a62b0", "", Country.BE, Country.BE, 105.223381, 115.160484, -8.066648, 30.705093, 33.029749, 59.492290, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "b58bf21a-096a-4dae-9a01-3f03b60c24c7", "b58bf21a-096a-4dae-9a01-3f03b60c24c7", "", Country.BE, Country.BE, -116.362525, -118.534989, -0.000000, 126.160372, -0.000000, -7.625383, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "b94318f6-6d24-4f56-96b9-df2531ad6543", "b94318f6-6d24-4f56-96b9-df2531ad6543", "", Country.BE, Country.BE, -0.700299, 0.246463, -4.992189, 76.274497, -33.029749, -38.006096, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "df16b3dd-c905-4a6f-84ee-f067be86f5da", "df16b3dd-c905-4a6f-84ee-f067be86f5da", "", Country.BE, Country.BE, -99.868184, -103.741300, 0.000000, 103.741300, -0.000000, -0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "e482b89a-fa84-4ea9-8e70-a83d44790957", "e482b89a-fa84-4ea9-8e70-a83d44790957", "", Country.BE, Country.BE, -95.848612, -84.605325, 13.058837, -106.979590, -0.000000, 178.526079, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "ffbabc27-1ccd-4fdc-b037-e341706c8d29", "ffbabc27-1ccd-4fdc-b037-e341706c8d29", "", Country.BE, Country.BE, -53.568417, -57.097136, -0.000000, 60.770208, -0.000000, -3.673072, 0.000000, 0.000000, 0.000000, 0.000000); + assertEquals(1, flowDecompositionResults.getZoneSet().size()); + assertTrue(flowDecompositionResults.getZoneSet().contains(Country.BE)); + + } + + @Test + void testFlowDecompositionOnNetworkWithStaticVarCompensatorOnly() { + Network network = TestUtils.getNetworkWithStaticVarCompensatorOnly(); + + FlowDecompositionResults flowDecompositionResults = runFlowDecomposition(network, new XnecProviderAllBranches()); + assertEquals(1, flowDecompositionResults.getDecomposedFlowMap().size()); + validateFlowDecomposition(flowDecompositionResults, "L1", "L1", "", Country.FR, Country.FR, 100.260455, 100.000000, -0.000000, 0.000000, 0.000000, 100.000000, 0.000000, 0.000000, 0.000000, 0.000000); + assertEquals(1, flowDecompositionResults.getZoneSet().size()); + assertTrue(flowDecompositionResults.getZoneSet().contains(Country.FR)); + } + + @Test + void testFlowDecompositionOnHvdcNetwork() { + Network network = TestUtils.importNetwork("TestCase16NodesWithHvdc.xiidm"); + + Set branchIds = network.getBranchStream().map(Identifiable::getId).collect(Collectors.toSet()); + XnecProvider xnecProviderContingency = XnecProviderByIds.builder() + .addContingency("contingency_1", Set.of("DDE2AA11 NNL3AA11 1")) + .addContingency("contingency_desync", Set.of("DDE2AA11 NNL3AA11 1", "FFR3AA11 FFR5AA11 1")) + .addContingency("contingency_split_network", Set.of("DDE2AA11 NNL3AA11 1", "FFR3AA11 FFR5AA11 1", "NNL2AA11 BBE3AA11 1")) + .addNetworkElementsAfterContingencies(branchIds, Set.of("contingency_1", "contingency_desync", "contingency_split_network")) + .build(); + XnecProvider xnecProvider = new XnecProviderUnion(List.of(new XnecProviderAllBranches(), xnecProviderContingency)); + FlowDecompositionResults flowDecompositionResults = runFlowDecomposition(network, xnecProvider); + assertEquals(98, flowDecompositionResults.getDecomposedFlowMap().size()); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE3AA11 1_contingency_split_network", "BBE1AA11 BBE3AA11 1", "contingency_split_network", Country.BE, Country.BE, -571.575251, -571.428571, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR3AA11 2", "FFR2AA11 FFR3AA11 2", "", Country.FR, Country.FR, -1048.852694, -1044.697128, 418.391705, 0.000000, 38.815350, 611.949424, -16.125814, -4.610042, 0.000000, -3.723495); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR3AA11 1", "FFR2AA11 FFR3AA11 1", "", Country.FR, Country.FR, -1048.852694, -1044.697128, 418.391705, 0.000000, 38.815350, 611.949424, -16.125814, -4.610042, 0.000000, -3.723495); + validateFlowDecomposition(flowDecompositionResults, "FFR3AA11 FFR5AA11 1", "FFR3AA11 FFR5AA11 1", "", Country.FR, Country.FR, -1012.008034, -1009.730695, 1246.058874, 0.000000, 18.657301, -134.559417, -46.431224, -63.273741, 0.000000, -10.721099); + validateFlowDecomposition(flowDecompositionResults, "DDE2AA11 DDE3AA11 1_contingency_1", "DDE2AA11 DDE3AA11 1", "contingency_1", Country.DE, Country.DE, -553.509702, -561.186194, 443.113772, 0.000000, -70.550332, 125.748503, 0.000000, 0.000000, 62.874251, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR4AA11 DDE4AA11 1_contingency_1", "FFR4AA11 DDE4AA11 1", "contingency_1", Country.FR, Country.DE, 148.880291, 146.271269, 518.962076, 0.000000, 23.516777, 0.000000, 0.000000, -375.249501, -20.958084, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR4AA11 1_contingency_split_network", "FFR1AA11 FFR4AA11 1", "contingency_split_network", Country.FR, Country.FR, 871.892782, 889.220358, 431.137725, 0.000000, -161.677845, 706.586826, 0.000000, -86.826347, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR3AA11 2_contingency_split_network", "FFR2AA11 FFR3AA11 2", "contingency_split_network", Country.FR, Country.FR, -625.590192, -622.155928, -86.227545, 0.000000, 32.335569, 658.682635, 0.000000, 17.365269, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE1AA11 DDE4AA11 1_contingency_1", "DDE1AA11 DDE4AA11 1", "contingency_1", Country.DE, Country.DE, -648.880291, -646.271269, 118.962076, 0.000000, 23.516777, 524.750499, 0.000000, 0.000000, -20.958084, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL1AA11 NNL2AA11 1_contingency_1", "NNL1AA11 NNL2AA11 1", "contingency_1", Country.NL, Country.NL, 166.672696, 166.666667, -208.333333, 0.000000, -0.000000, 375.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL2AA11 BBE3AA11 1_contingency_1", "NNL2AA11 BBE3AA11 1", "contingency_1", Country.NL, Country.BE, -500.000000, -500.000000, 500.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL1AA11 NNL2AA11 1_contingency_desync", "NNL1AA11 NNL2AA11 1", "contingency_desync", Country.NL, Country.NL, 166.672696, 166.666667, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR4AA11 1", "FFR1AA11 FFR4AA11 1", "", Country.FR, Country.FR, 779.213147, 795.975751, 400.159224, 0.000000, -156.762149, 671.134046, -12.233376, -103.497273, 0.000000, -2.824721); + validateFlowDecomposition(flowDecompositionResults, "FFR3AA11 FFR5AA11 1_contingency_1", "FFR3AA11 FFR5AA11 1", "contingency_1", Country.FR, Country.FR, -2000.000000, -2000.000000, 2000.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL1AA11 NNL2AA11 1", "NNL1AA11 NNL2AA11 1", "", Country.NL, Country.NL, -162.721962, -163.423102, 459.647042, 0.000000, -6.219100, -371.426300, 15.477075, 21.091247, 44.853139, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR5AA11 1_contingency_desync", "FFR1AA11 FFR5AA11 1", "contingency_desync", Country.FR, Country.FR, NaN, NaN, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE1AA11 DDE4AA11 1_contingency_split_network", "DDE1AA11 DDE4AA11 1", "contingency_split_network", Country.DE, Country.DE, -636.877548, -634.295221, 106.986028, 0.000000, 23.516777, 524.750499, 0.000000, 0.000000, -20.958084, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR4AA11 DDE1AA11 1", "FFR4AA11 DDE1AA11 1", "", Country.FR, Country.DE, 478.144120, 472.335696, 394.134806, 0.000000, 53.066455, 0.000000, -15.013689, 129.041225, -85.426398, -3.466703); + validateFlowDecomposition(flowDecompositionResults, "NNL2AA11 BBE3AA11 1", "NNL2AA11 BBE3AA11 1", "", Country.NL, Country.BE, -1487.991966, -1490.269305, 1253.941126, 0.000000, -18.657301, 0.000000, 46.431224, 63.273741, 134.559417, 10.721099); + validateFlowDecomposition(flowDecompositionResults, "BBE2AA11 BBE3AA11 1", "BBE2AA11 BBE3AA11 1", "", Country.BE, Country.BE, 926.977948, 927.181329, 423.495349, 0.000000, -2.665329, 476.557844, 0.000000, 9.039106, 19.222774, 1.531586); + validateFlowDecomposition(flowDecompositionResults, "DDE1AA11 DDE2AA11 1_contingency_desync", "DDE1AA11 DDE2AA11 1", "contingency_desync", Country.DE, Country.DE, 410.522825, 402.885662, 54.291417, 0.000000, 70.550332, 340.918164, 0.000000, 0.000000, -62.874251, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL1AA11 NNL3AA11 1_contingency_1", "NNL1AA11 NNL3AA11 1", "contingency_1", Country.NL, Country.NL, 333.327304, 333.333333, 20.833333, 0.000000, -0.000000, 312.500000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR4AA11 DDE4AA11 1", "FFR4AA11 DDE4AA11 1", "", Country.FR, Country.DE, -10.930437, -13.832152, -397.067403, 0.000000, -26.533227, 0.000000, 7.506845, 385.479387, 42.713199, 1.733351); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR5AA11 1_contingency_split_network", "FFR1AA11 FFR5AA11 1", "contingency_split_network", Country.FR, Country.FR, NaN, NaN, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL2AA11 NNL3AA11 1_contingency_desync", "NNL2AA11 NNL3AA11 1", "contingency_desync", Country.NL, Country.NL, 166.672696, 166.666667, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE2AA11 1", "BBE1AA11 BBE2AA11 1", "", Country.BE, Country.BE, -1073.022052, -1072.818671, 365.978335, 0.000000, 2.665329, 733.968472, 0.000000, -9.039106, -19.222774, -1.531586); + validateFlowDecomposition(flowDecompositionResults, "FFR4AA11 DDE4AA11 1_contingency_split_network", "FFR4AA11 DDE4AA11 1", "contingency_split_network", Country.FR, Country.DE, 136.883741, 134.295221, 506.986028, 0.000000, 23.516777, 0.000000, 0.000000, -375.249501, -20.958084, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL2AA11 BBE3AA11 1_contingency_desync", "NNL2AA11 BBE3AA11 1", "contingency_desync", Country.NL, Country.BE, -500.000000, -500.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 DDE3AA11 1", "FFR2AA11 DDE3AA11 1", "", Country.FR, Country.DE, 544.794351, 551.227151, 454.856666, 0.000000, -60.942381, 0.000000, -23.910690, 193.164421, -6.419820, -5.521045); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE2AA11 1_contingency_split_network", "BBE1AA11 BBE2AA11 1", "contingency_split_network", Country.BE, Country.BE, -1285.274247, -1285.714286, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE1AA11 DDE2AA11 1_contingency_1", "DDE1AA11 DDE2AA11 1", "contingency_1", Country.DE, Country.DE, 446.490298, 438.813806, 90.219561, 0.000000, 70.550332, 340.918164, 0.000000, 0.000000, -62.874251, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR4AA11 1_contingency_split_network", "FFR2AA11 FFR4AA11 1", "contingency_split_network", Country.FR, Country.FR, 1038.609398, 1013.665303, 689.820359, 0.000000, 232.228178, 230.538922, 0.000000, -138.922156, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 DDE3AA11 1_contingency_desync", "FFR2AA11 DDE3AA11 1", "contingency_desync", Country.FR, Country.DE, 1089.444201, 1097.114338, 879.041916, 0.000000, -70.550332, 0.000000, 0.000000, 225.748503, 62.874251, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR3AA11 1", "FFR1AA11 FFR3AA11 1", "", Country.FR, Country.FR, -414.302646, -420.336439, 409.275465, 0.000000, -58.973399, 141.541735, -14.179595, -54.053657, 0.000000, -3.274108); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 DDE3AA11 1_contingency_1", "FFR2AA11 DDE3AA11 1", "contingency_1", Country.FR, Country.DE, 1053.509702, 1061.186194, 843.113772, 0.000000, -70.550332, 0.000000, 0.000000, 225.748503, 62.874251, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE1AA11 DDE4AA11 1", "DDE1AA11 DDE4AA11 1", "", Country.DE, Country.DE, -489.069563, -486.167848, -2.932597, 0.000000, 26.533227, 514.520613, -7.506845, 0.000000, -42.713199, -1.733351); + validateFlowDecomposition(flowDecompositionResults, "NNL1AA11 NNL3AA11 1_contingency_desync", "NNL1AA11 NNL3AA11 1", "contingency_desync", Country.NL, Country.NL, 333.327304, 333.333333, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE2AA11 DDE3AA11 1_contingency_desync", "DDE2AA11 DDE3AA11 1", "contingency_desync", Country.DE, Country.DE, -589.470478, -597.114338, 479.041916, 0.000000, -70.550332, 125.748503, 0.000000, 0.000000, 62.874251, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE2AA11 BBE3AA11 1_contingency_split_network", "BBE2AA11 BBE3AA11 1", "contingency_split_network", Country.BE, Country.BE, 714.725753, 714.285714, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL1AA11 NNL3AA11 1_contingency_split_network", "NNL1AA11 NNL3AA11 1", "contingency_split_network", Country.NL, Country.NL, NaN, NaN, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE3AA11 2_contingency_desync", "BBE1AA11 BBE3AA11 2", "contingency_desync", Country.BE, Country.BE, -428.685692, -428.571429, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE2AA11 1_contingency_desync", "BBE1AA11 BBE2AA11 1", "contingency_desync", Country.BE, Country.BE, -1213.942924, -1214.285714, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR3AA11 2_contingency_desync", "FFR2AA11 FFR3AA11 2", "contingency_desync", Country.FR, Country.FR, -625.590192, -622.155928, -86.227545, 0.000000, 32.335569, 658.682635, 0.000000, 17.365269, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 FFR5AA11 1_contingency_desync", "BBE1AA11 FFR5AA11 1", "contingency_desync", Country.BE, Country.FR, 0.000000, -0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE3AA11 BBE4AA11 1_contingency_1", "BBE3AA11 BBE4AA11 1", "contingency_1", Country.BE, Country.BE, 428.685692, 428.571429, 157.894737, 0.000000, 0.000000, 270.676692, 0.000000, 0.000000, 0.000000, -0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE3AA11 1_contingency_desync", "BBE1AA11 BBE3AA11 1", "contingency_desync", Country.BE, Country.BE, -428.685692, -428.571429, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR4AA11 1_contingency_desync", "FFR2AA11 FFR4AA11 1", "contingency_desync", Country.FR, Country.FR, 1038.609398, 1013.665303, 689.820359, 0.000000, 232.228178, 230.538922, 0.000000, -138.922156, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE1AA11 DDE2AA11 1_contingency_split_network", "DDE1AA11 DDE2AA11 1", "contingency_split_network", Country.DE, Country.DE, 410.522825, 402.885662, 54.291417, 0.000000, 70.550332, 340.918164, 0.000000, 0.000000, -62.874251, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR3AA11 1_contingency_desync", "FFR2AA11 FFR3AA11 1", "contingency_desync", Country.FR, Country.FR, -625.590192, -622.155928, -86.227545, 0.000000, 32.335569, 658.682635, 0.000000, 17.365269, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR3AA11 1_contingency_split_network", "FFR2AA11 FFR3AA11 1", "contingency_split_network", Country.FR, Country.FR, -625.590192, -622.155928, -86.227545, 0.000000, 32.335569, 658.682635, 0.000000, 17.365269, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE3AA11 BBE4AA11 1", "BBE3AA11 BBE4AA11 1", "", Country.BE, Country.BE, 146.328661, 145.637341, -57.517013, 0.000000, 5.330657, 257.410628, 0.000000, -18.078212, -38.445548, -3.063171); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR2AA11 1_contingency_1", "FFR1AA11 FFR2AA11 1", "contingency_1", Country.FR, Country.FR, 676.752713, 665.868983, 40.718563, 0.000000, 97.006707, 476.047904, 0.000000, 52.095808, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE2AA11 BBE3AA11 1_contingency_1", "BBE2AA11 BBE3AA11 1", "contingency_1", Country.BE, Country.BE, 786.057076, 785.714286, 315.789474, 0.000000, 0.000000, 469.924812, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR3AA11 1_contingency_desync", "FFR1AA11 FFR3AA11 1", "contingency_desync", Country.FR, Country.FR, -248.807353, -255.688143, 172.455090, 0.000000, -64.671138, 182.634731, 0.000000, -34.730539, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE3AA11 2_contingency_split_network", "BBE1AA11 BBE3AA11 2", "contingency_split_network", Country.BE, Country.BE, -571.575251, -571.428571, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE2AA11 DDE3AA11 1_contingency_split_network", "DDE2AA11 DDE3AA11 1", "contingency_split_network", Country.DE, Country.DE, -589.470478, -597.114338, 479.041916, 0.000000, -70.550332, 125.748503, 0.000000, 0.000000, 62.874251, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 DDE3AA11 1_contingency_split_network", "FFR2AA11 DDE3AA11 1", "contingency_split_network", Country.FR, Country.DE, 1089.444201, 1097.114338, 879.041916, 0.000000, -70.550332, 0.000000, 0.000000, 225.748503, 62.874251, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE3AA11 2", "BBE1AA11 BBE3AA11 2", "", Country.BE, Country.BE, -146.328661, -145.637341, -57.517013, 0.000000, 5.330657, 257.410628, 0.000000, -18.078212, -38.445548, -3.063171); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 FFR5AA11 1", "BBE1AA11 FFR5AA11 1", "", Country.BE, Country.FR, 506.004017, 504.865347, 623.029437, 0.000000, 9.328651, 0.000000, -23.215612, -31.636871, -67.279708, -5.360549); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE3AA11 1", "BBE1AA11 BBE3AA11 1", "", Country.BE, Country.BE, -146.328661, -145.637341, -57.517013, 0.000000, 5.330657, 257.410628, 0.000000, -18.078212, -38.445548, -3.063171); + validateFlowDecomposition(flowDecompositionResults, "NNL2AA11 NNL3AA11 1_contingency_split_network", "NNL2AA11 NNL3AA11 1", "contingency_split_network", Country.NL, Country.NL, NaN, NaN, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE4AA11 FFR5AA11 1_contingency_split_network", "BBE4AA11 FFR5AA11 1", "contingency_split_network", Country.BE, Country.FR, 0.000000, -0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE2AA11 BBE3AA11 1_contingency_desync", "BBE2AA11 BBE3AA11 1", "contingency_desync", Country.BE, Country.BE, 786.057076, 785.714286, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE3AA11 1_contingency_1", "BBE1AA11 BBE3AA11 1", "contingency_1", Country.BE, Country.BE, -428.685692, -428.571429, 157.894737, 0.000000, 0.000000, 270.676692, 0.000000, 0.000000, 0.000000, -0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 FFR5AA11 1_contingency_split_network", "BBE1AA11 FFR5AA11 1", "contingency_split_network", Country.BE, Country.FR, 0.000000, -0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR4AA11 DDE1AA11 1_contingency_desync", "FFR4AA11 DDE1AA11 1", "contingency_desync", Country.FR, Country.DE, 773.629497, 768.590441, 613.972056, 0.000000, 47.033555, 0.000000, 0.000000, 149.500998, -41.916168, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE2AA11 1_contingency_1", "BBE1AA11 BBE2AA11 1", "contingency_1", Country.BE, Country.BE, -1213.942924, -1214.285714, 473.684211, 0.000000, 0.000000, 740.601504, 0.000000, 0.000000, 0.000000, -0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR3AA11 1_contingency_1", "FFR1AA11 FFR3AA11 1", "contingency_1", Country.FR, Country.FR, -716.378043, -722.754012, 639.520958, 0.000000, -64.671138, 182.634731, 0.000000, -34.730539, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE1AA11 DDE2AA11 1", "DDE1AA11 DDE2AA11 1", "", Country.DE, Country.DE, -32.786317, -41.496456, 275.464458, 0.000000, -79.599682, -310.228505, 22.520534, 0.000000, 128.139597, 5.200054); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR2AA11 1", "FFR1AA11 FFR2AA11 1", "", Country.FR, Country.FR, 635.089500, 624.360688, 9.116240, 0.000000, 97.788750, 470.407689, -1.946219, 49.443616, 0.000000, -0.449387); + validateFlowDecomposition(flowDecompositionResults, "FFR4AA11 DDE1AA11 1_contingency_split_network", "FFR4AA11 DDE1AA11 1", "contingency_split_network", Country.FR, Country.DE, 773.629497, 768.590441, 613.972056, 0.000000, 47.033555, 0.000000, 0.000000, 149.500998, -41.916168, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL2AA11 NNL3AA11 1", "NNL2AA11 NNL3AA11 1", "", Country.NL, Country.NL, 825.270004, 826.846203, 731.794084, 0.000000, -12.438201, -55.352601, 30.954149, 42.182494, 89.706278, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE4AA11 FFR5AA11 1_contingency_desync", "BBE4AA11 FFR5AA11 1", "contingency_desync", Country.BE, Country.FR, 0.000000, -0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE1AA11 DDE4AA11 1_contingency_desync", "DDE1AA11 DDE4AA11 1", "contingency_desync", Country.DE, Country.DE, -636.877548, -634.295221, 106.986028, 0.000000, 23.516777, 524.750499, 0.000000, 0.000000, -20.958084, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL1AA11 NNL2AA11 1_contingency_split_network", "NNL1AA11 NNL2AA11 1", "contingency_split_network", Country.NL, Country.NL, NaN, NaN, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR4AA11 DDE4AA11 1_contingency_desync", "FFR4AA11 DDE4AA11 1", "contingency_desync", Country.FR, Country.DE, 136.883741, 134.295221, 506.986028, 0.000000, 23.516777, 0.000000, 0.000000, -375.249501, -20.958084, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR5AA11 1", "FFR1AA11 FFR5AA11 1", "", Country.FR, Country.FR, NaN, NaN, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE3AA11 BBE4AA11 1_contingency_desync", "BBE3AA11 BBE4AA11 1", "contingency_desync", Country.BE, Country.BE, 428.685692, 428.571429, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 FFR5AA11 1_contingency_1", "BBE1AA11 FFR5AA11 1", "contingency_1", Country.BE, Country.FR, 1000.000000, 1000.000000, 1000.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL1AA11 NNL3AA11 1", "NNL1AA11 NNL3AA11 1", "", Country.NL, Country.NL, 662.721962, 663.423102, 272.147042, 0.000000, -6.219100, 316.073700, 15.477075, 21.091247, 44.853139, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR4AA11 DDE1AA11 1_contingency_1", "FFR4AA11 DDE1AA11 1", "contingency_1", Country.FR, Country.DE, 797.610007, 792.542537, 637.924152, 0.000000, 47.033555, 0.000000, 0.000000, 149.500998, -41.916168, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR2AA11 1_contingency_desync", "FFR1AA11 FFR2AA11 1", "contingency_desync", Country.FR, Country.FR, 376.897452, 366.467785, -258.682635, 0.000000, 97.006707, 476.047904, 0.000000, 52.095808, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR4AA11 1_contingency_desync", "FFR1AA11 FFR4AA11 1", "contingency_desync", Country.FR, Country.FR, 871.892782, 889.220358, 431.137725, 0.000000, -161.677845, 706.586826, 0.000000, -86.826347, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR4AA11 1_contingency_1", "FFR2AA11 FFR4AA11 1", "contingency_1", Country.FR, Country.FR, 906.864968, 881.928776, 558.083832, 0.000000, 232.228178, 230.538922, 0.000000, -138.922156, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR3AA11 2_contingency_1", "FFR2AA11 FFR3AA11 2", "contingency_1", Country.FR, Country.FR, -1391.810978, -1388.622994, 680.239521, 0.000000, 32.335569, 658.682635, 0.000000, 17.365269, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "NNL2AA11 NNL3AA11 1_contingency_1", "NNL2AA11 NNL3AA11 1", "contingency_1", Country.NL, Country.NL, 166.672696, 166.666667, 229.166667, 0.000000, -0.000000, -62.500000, -0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR2AA11 1_contingency_split_network", "FFR1AA11 FFR2AA11 1", "contingency_split_network", Country.FR, Country.FR, 376.897452, 366.467785, -258.682635, 0.000000, 97.006707, 476.047904, 0.000000, 52.095808, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR3AA11 1_contingency_1", "FFR2AA11 FFR3AA11 1", "contingency_1", Country.FR, Country.FR, -1391.810978, -1388.622994, 680.239521, 0.000000, 32.335569, 658.682635, 0.000000, 17.365269, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR4AA11 1_contingency_1", "FFR1AA11 FFR4AA11 1", "contingency_1", Country.FR, Country.FR, 1039.625330, 1056.885029, 598.802395, 0.000000, -161.677845, 706.586826, 0.000000, -86.826347, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE4AA11 FFR5AA11 1_contingency_1", "BBE4AA11 FFR5AA11 1", "contingency_1", Country.BE, Country.FR, 1000.000000, 1000.000000, 1000.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE2AA11 DDE3AA11 1", "DDE2AA11 DDE3AA11 1", "", Country.DE, Country.DE, -44.794351, -51.227151, 54.856666, 0.000000, -60.942381, 93.164421, -23.910690, 0.000000, -6.419820, -5.521045); + validateFlowDecomposition(flowDecompositionResults, "BBE3AA11 BBE4AA11 1_contingency_split_network", "BBE3AA11 BBE4AA11 1", "contingency_split_network", Country.BE, Country.BE, 571.575251, 571.428571, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "DDE2AA11 NNL3AA11 1", "DDE2AA11 NNL3AA11 1", "", Country.DE, Country.NL, -987.991966, -990.269305, 753.941126, 0.000000, -18.657301, 0.000000, 46.431224, 63.273741, 134.559417, 10.721099); + validateFlowDecomposition(flowDecompositionResults, "FFR2AA11 FFR4AA11 1", "FFR2AA11 FFR4AA11 1", "", Country.FR, Country.FR, 688.000536, 662.527793, 391.042984, 0.000000, 236.361831, 200.726357, -10.287157, -152.940889, 0.000000, -2.375333); + validateFlowDecomposition(flowDecompositionResults, "BBE4AA11 FFR5AA11 1", "BBE4AA11 FFR5AA11 1", "", Country.BE, Country.FR, 506.004017, 504.865347, 623.029437, 0.000000, 9.328651, 0.000000, -23.215612, -31.636871, -67.279708, -5.360549); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR3AA11 1_contingency_split_network", "FFR1AA11 FFR3AA11 1", "contingency_split_network", Country.FR, Country.FR, -248.807353, -255.688143, 172.455090, 0.000000, -64.671138, 182.634731, 0.000000, -34.730539, 0.000000, 0.000000); + validateFlowDecomposition(flowDecompositionResults, "BBE1AA11 BBE3AA11 2_contingency_1", "BBE1AA11 BBE3AA11 2", "contingency_1", Country.BE, Country.BE, -428.685692, -428.571429, 157.894737, 0.000000, 0.000000, 270.676692, 0.000000, 0.000000, 0.000000, -0.000000); + validateFlowDecomposition(flowDecompositionResults, "FFR1AA11 FFR5AA11 1_contingency_1", "FFR1AA11 FFR5AA11 1", "contingency_1", Country.FR, Country.FR, NaN, NaN, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000); + assertEquals(4, flowDecompositionResults.getZoneSet().size()); + assertTrue(flowDecompositionResults.getZoneSet().contains(Country.BE)); + assertTrue(flowDecompositionResults.getZoneSet().contains(Country.DE)); + assertTrue(flowDecompositionResults.getZoneSet().contains(Country.FR)); + assertTrue(flowDecompositionResults.getZoneSet().contains(Country.NL)); + } + + private static FlowDecompositionResults runFlowDecomposition(Network network, XnecProvider xnecProvider) { + FlowDecompositionParameters flowDecompositionParameters = new FlowDecompositionParameters() + .setEnableLossesCompensation(FlowDecompositionParameters.ENABLE_LOSSES_COMPENSATION) + .setLossesCompensationEpsilon(FlowDecompositionParameters.DISABLE_LOSSES_COMPENSATION_EPSILON) + .setSensitivityEpsilon(FlowDecompositionParameters.DISABLE_SENSITIVITY_EPSILON) + .setRescaleMode(FlowDecompositionParameters.RescaleMode.NONE); + FlowDecompositionComputer flowDecompositionComputer = new FlowDecompositionComputer(flowDecompositionParameters, new LoadFlowParameters()); + FlowDecompositionResults flowDecompositionResults = flowDecompositionComputer.run(xnecProvider, network); + TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.getRescaleMode(), flowDecompositionResults); + return flowDecompositionResults; + } +} diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/LossesCompensationTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/LossesCompensationTests.java index 30d42800..779e3dc2 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/LossesCompensationTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/LossesCompensationTests.java @@ -22,10 +22,7 @@ import static com.powsybl.flow_decomposition.TestUtils.getLossOnBus; import static com.powsybl.flow_decomposition.TestUtils.importNetwork; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.*; /** * @author Sebastien Murgey {@literal } @@ -278,4 +275,45 @@ void testLossCompensationComputerCanBeAppliedToMultipleNetworks() { assertEquals(losses, load.getP0()); }); } + + /* + Test flow decomposition with losses compensation in a simple network + FR : bus1 + BE : bus2, bus3 + ____ bus3 + | | + l32 d3 (1) + | + bus1 --- l21 --- bus2 + | | + g1 (3) d2 (2) + + Line l32 is open at side 1 (bus 3) + Line characteristics: (r,x,g,b) = (0.01, 0.1, 0.0, 0.5) + */ + @Test + void testLossCompensationWithLineConnectedToOnlyOneSide() { + String networkFileName = "lossesCompensatorLineConnectedToOneSide.xiidm"; + Network network = importNetwork(networkFileName); + assertEquals(2, network.getLoadStream().count()); + LoadFlow.run(network, new LoadFlowParameters().setDc(AC_LOAD_FLOW)); + LossesCompensator lossesCompensator = new LossesCompensator(FlowDecompositionParameters.DEFAULT_LOSSES_COMPENSATION_EPSILON); + lossesCompensator.run(network); + assertEquals(5, network.getLoadStream().count()); + Load lossesb1 = network.getLoad("LOSSES b1"); + assertNotNull(lossesb1); + assertEquals("b1_vl", lossesb1.getTerminal().getVoltageLevel().getId()); + assertEquals(0.061171, lossesb1.getP0(), 1e-6); + assertTrue(lossesb1.isFictitious()); + Load lossesb2 = network.getLoad("LOSSES b2"); + assertNotNull(lossesb2); + assertEquals("b2_vl", lossesb2.getTerminal().getVoltageLevel().getId()); + assertEquals(0.003581, lossesb2.getP0(), 1e-6); + assertTrue(lossesb2.isFictitious()); + Load lossesb3 = network.getLoad("LOSSES b3"); + assertNotNull(lossesb3); + assertEquals("b3_vl", lossesb3.getTerminal().getVoltageLevel().getId()); + assertEquals(0.0, lossesb3.getP0(), 1e-6); + assertTrue(lossesb3.isFictitious()); + } } diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/NetPositionTest.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/NetPositionTest.java index 20b896bd..a3d217b2 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/NetPositionTest.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/NetPositionTest.java @@ -19,7 +19,7 @@ /** * @author Sebastien Murgey {@literal } * @author Peter Mitri {@literal } - * @author Hugo Schindler{@literal } + * @author Hugo Schindler{@literal } */ class NetPositionTest { private static final double DOUBLE_TOLERANCE = 1e-3; diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/NetworkUtilTest.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/NetworkUtilTest.java new file mode 100644 index 00000000..226b4a2a --- /dev/null +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/NetworkUtilTest.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2024, 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; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.test.FourSubstationsNodeBreakerFactory; +import org.junit.jupiter.api.Test; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Hugo Schindler {@literal } + */ +class NetworkUtilTest { + + @Test + void testGetFlowIdFromCountry() { + Network network = TestUtils.importNetwork("19700101_0000_FO4_UX1.uct"); + + assertEquals("Loop Flow from SI", NetworkUtil.getLoopFlowIdFromCountry(Country.SI)); + + assertEquals("Loop Flow from BE", NetworkUtil.getLoopFlowIdFromCountry(network, "BB000021_load")); + PowsyblException exception = assertThrows(PowsyblException.class, () -> NetworkUtil.getLoopFlowIdFromCountry(network, "DUMMY")); + + assertEquals("Identifiable DUMMY must be an Injection", exception.getMessage()); + } + + @Test + void testGetFlowIdFromCountryWithNetworkWithoutCountries() { + Network network = FourSubstationsNodeBreakerFactory.create(); + PowsyblException exception = assertThrows(PowsyblException.class, () -> NetworkUtil.getLoopFlowIdFromCountry(network, "GH1")); + assertEquals("Substation S1 does not have country property needed for the algorithm.", exception.getMessage()); + } + + @Test + void testGetIndex() { + Map result = Map.of("a", 0, "b", 1, "c", 2); + assertEquals(result, NetworkUtil.getIndex(List.of("a", "b", "c"))); + } + + @Test + void testGetAllValidBranches() { + Network network = TestUtils.importNetwork("NETWORK_LOOP_FLOW_WITH_COUNTRIES.uct"); + + List allValidBranches = NetworkUtil.getAllValidBranches(network); + assertEquals(5, allValidBranches.size()); + assertTrue(allValidBranches.contains(network.getBranch("EGEN 11 FGEN 11 1"))); + assertTrue(allValidBranches.contains(network.getBranch("FGEN 11 BGEN 11 1"))); + assertTrue(allValidBranches.contains(network.getBranch("BGEN 11 BLOAD 11 1"))); + assertTrue(allValidBranches.contains(network.getBranch("BLOAD 11 FLOAD 11 1"))); + assertTrue(allValidBranches.contains(network.getBranch("FLOAD 11 ELOAD 11 1"))); + } + + @Test + void testGetAllValidBranchesWithDisconnectedTerminals() { + Network network = TestUtils.importNetwork("NETWORK_LOOP_FLOW_WITH_COUNTRIES.uct"); + network.getBranch("BLOAD 11 FLOAD 11 1").getTerminal2().disconnect(); + network.getBranch("FGEN 11 BGEN 11 1").getTerminal1().disconnect(); + + List allValidBranches = NetworkUtil.getAllValidBranches(network); + assertEquals(1, allValidBranches.size()); + assertTrue(allValidBranches.contains(network.getBranch("EGEN 11 FGEN 11 1"))); + assertFalse(allValidBranches.contains(network.getBranch("FGEN 11 BGEN 11 1"))); + assertFalse(allValidBranches.contains(network.getBranch("BGEN 11 BLOAD 11 1"))); + assertFalse(allValidBranches.contains(network.getBranch("BLOAD 11 FLOAD 11 1"))); + assertFalse(allValidBranches.contains(network.getBranch("FLOAD 11 ELOAD 11 1"))); + } + + @Test + void testGetAllValidBranchesWithLinesOutsideMainConnectedComponent() { + Network network = TestUtils.importNetwork("NETWORK_LOOP_FLOW_WITH_COUNTRIES.uct"); + network.getLine("FGEN 11 BGEN 11 1").disconnect(); + assertFalse(network.getBranch("EGEN 11 FGEN 11 1").getTerminal1().getBusBreakerView().getBus().isInMainConnectedComponent()); + assertFalse(network.getBranch("EGEN 11 FGEN 11 1").getTerminal1().getBusBreakerView().getBus().isInMainSynchronousComponent()); + + List allValidBranches = NetworkUtil.getAllValidBranches(network); + assertEquals(3, allValidBranches.size()); + assertFalse(allValidBranches.contains(network.getBranch("EGEN 11 FGEN 11 1"))); + assertFalse(allValidBranches.contains(network.getBranch("FGEN 11 BGEN 11 1"))); + assertTrue(allValidBranches.contains(network.getBranch("BGEN 11 BLOAD 11 1"))); + assertTrue(allValidBranches.contains(network.getBranch("BLOAD 11 FLOAD 11 1"))); + assertTrue(allValidBranches.contains(network.getBranch("FLOAD 11 ELOAD 11 1"))); + } + + @Test + void testGetAllValidBranchesWithLinesOutsideMainSynchronousComponent() { + Network network = TestUtils.importNetwork("TestCase16NodesWithHvdc.xiidm"); + assertEquals(1, network.getBusView().getConnectedComponents().size()); + assertEquals(1, network.getBusView().getSynchronousComponents().size()); + Line lineOutsideSync = network.getLine("BBE1AA11 FFR5AA11 1"); + assertTrue(lineOutsideSync.getTerminal1().getBusBreakerView().getBus().isInMainConnectedComponent()); + assertTrue(lineOutsideSync.getTerminal1().getBusBreakerView().getBus().isInMainSynchronousComponent()); + + Line lineDisconnected1 = network.getLine("DDE2AA11 NNL3AA11 1"); + Line lineDisconnected2 = network.getLine("FFR3AA11 FFR5AA11 1"); + lineDisconnected1.disconnect(); + lineDisconnected2.disconnect(); + assertEquals(1, network.getBusView().getConnectedComponents().size()); + assertEquals(2, network.getBusView().getSynchronousComponents().size()); + assertTrue(lineOutsideSync.getTerminal1().getBusBreakerView().getBus().isInMainConnectedComponent()); + assertFalse(lineOutsideSync.getTerminal1().getBusBreakerView().getBus().isInMainSynchronousComponent()); + + validateValidBranches(network); + } + + @Test + void testGetAllValidBranchesWithLinesOutsideMainConnectedAndSynchronousComponent() { + Network network = TestUtils.importNetwork("TestCase16NodesWithHvdc.xiidm"); + assertEquals(1, network.getBusView().getConnectedComponents().size()); + assertEquals(1, network.getBusView().getSynchronousComponents().size()); + Line lineOutsideSync = network.getLine("BBE1AA11 FFR5AA11 1"); + assertTrue(lineOutsideSync.getTerminal1().getBusBreakerView().getBus().isInMainConnectedComponent()); + assertTrue(lineOutsideSync.getTerminal1().getBusBreakerView().getBus().isInMainSynchronousComponent()); + Line lineOutsideConnected = network.getLine("NNL1AA11 NNL3AA11 1"); + assertTrue(lineOutsideConnected.getTerminal1().getBusBreakerView().getBus().isInMainConnectedComponent()); + assertTrue(lineOutsideConnected.getTerminal1().getBusBreakerView().getBus().isInMainSynchronousComponent()); + + Line lineDisconnected1 = network.getLine("DDE2AA11 NNL3AA11 1"); + Line lineDisconnected2 = network.getLine("FFR3AA11 FFR5AA11 1"); + Line lineDisconnected3 = network.getLine("NNL2AA11 BBE3AA11 1"); + lineDisconnected1.disconnect(); + lineDisconnected2.disconnect(); + lineDisconnected3.disconnect(); + assertEquals(2, network.getBusView().getConnectedComponents().size()); + assertEquals(3, network.getBusView().getSynchronousComponents().size()); + assertTrue(lineOutsideSync.getTerminal1().getBusBreakerView().getBus().isInMainConnectedComponent()); + assertFalse(lineOutsideSync.getTerminal1().getBusBreakerView().getBus().isInMainSynchronousComponent()); + assertFalse(lineOutsideConnected.getTerminal1().getBusBreakerView().getBus().isInMainConnectedComponent()); + assertFalse(lineOutsideConnected.getTerminal1().getBusBreakerView().getBus().isInMainSynchronousComponent()); + + validateValidBranches(network); + } + + private static void validateValidBranches(Network network) { + List allValidBranches = NetworkUtil.getAllValidBranches(network); + assertEquals(12, allValidBranches.size()); + assertTrue(allValidBranches.contains(network.getLine("DDE1AA11 DDE2AA11 1"))); + assertTrue(allValidBranches.contains(network.getLine("DDE1AA11 DDE4AA11 1"))); + assertTrue(allValidBranches.contains(network.getLine("DDE2AA11 DDE3AA11 1"))); + assertTrue(allValidBranches.contains(network.getLine("FFR1AA11 FFR2AA11 1"))); + assertTrue(allValidBranches.contains(network.getLine("FFR1AA11 FFR3AA11 1"))); + assertTrue(allValidBranches.contains(network.getLine("FFR1AA11 FFR4AA11 1"))); + assertTrue(allValidBranches.contains(network.getLine("FFR2AA11 DDE3AA11 1"))); + assertTrue(allValidBranches.contains(network.getLine("FFR2AA11 FFR3AA11 1"))); + assertTrue(allValidBranches.contains(network.getLine("FFR2AA11 FFR3AA11 2"))); + assertTrue(allValidBranches.contains(network.getLine("FFR4AA11 DDE1AA11 1"))); + assertTrue(allValidBranches.contains(network.getLine("FFR4AA11 DDE4AA11 1"))); + assertTrue(allValidBranches.contains(network.getTwoWindingsTransformer("FFR2AA11 FFR4AA11 1"))); + } + + @Test + void testGetNodeList() { + Network network = TestUtils.importNetwork("NETWORK_LOOP_FLOW_WITH_COUNTRIES.uct"); + + List> nodeList = NetworkUtil.getNodeList(network); + assertEquals(6, nodeList.size()); + assertTrue(nodeList.contains(network.getGenerator("EGEN 11_generator"))); + assertTrue(nodeList.contains(network.getGenerator("FGEN 11_generator"))); + assertTrue(nodeList.contains(network.getGenerator("BGEN 11_generator"))); + assertTrue(nodeList.contains(network.getLoad("BLOAD 11_load"))); + assertTrue(nodeList.contains(network.getLoad("FLOAD 11_load"))); + assertTrue(nodeList.contains(network.getLoad("ELOAD 11_load"))); + } + + @Test + void testGetNodeListWithDisconnectedTerminals() { + Network network = TestUtils.importNetwork("NETWORK_LOOP_FLOW_WITH_COUNTRIES.uct"); + network.getBranch("BLOAD 11 FLOAD 11 1").getTerminal2().disconnect(); + network.getBranch("FGEN 11 BGEN 11 1").getTerminal1().disconnect(); + + List> nodeList = NetworkUtil.getNodeList(network); + assertEquals(2, nodeList.size()); + assertTrue(nodeList.contains(network.getGenerator("EGEN 11_generator"))); + assertTrue(nodeList.contains(network.getGenerator("FGEN 11_generator"))); + } + + @Test + void testGetNodeListWithoutBusBarSection() { + Network network = TestUtils.getMicroGridNetworkWithBusBarSectionOnly(); + + List> nodeList = NetworkUtil.getNodeList(network); + validateNodeListForMicroGridBE3DanglingLinesSameBoundary1Disconnected(nodeList, network); + } + + @Test + void testGetNodeListWithoutShuntCompensator() { + Network network = TestUtils.getMicroGridNetworkWithShuntCompensatorOnly(); + + List> nodeList = NetworkUtil.getNodeList(network); + validateNodeListForMicroGridBE3DanglingLinesSameBoundary1Disconnected(nodeList, network); + } + + private static void validateNodeListForMicroGridBE3DanglingLinesSameBoundary1Disconnected(List> nodeList, Network network) { + assertEquals(9, nodeList.size()); + assertTrue(nodeList.contains(network.getGenerator("3a3b27be-b18b-4385-b557-6735d733baf0"))); + assertTrue(nodeList.contains(network.getGenerator("550ebe0d-f2b2-48c1-991f-cebea43a21aa"))); + assertTrue(nodeList.contains(network.getLoad("b1480a00-b427-4001-a26c-51954d2bb7e9"))); + assertTrue(nodeList.contains(network.getLoad("1c6beed6-1acf-42e7-ba55-0cc9f04bddd8"))); + assertTrue(nodeList.contains(network.getLoad("cb459405-cc14-4215-a45c-416789205904"))); + assertTrue(nodeList.contains(network.getDanglingLine("ed0c5d75-4a54-43c8-b782-b20d7431630b"))); + assertTrue(nodeList.contains(network.getDanglingLine("b18cd1aa-7808-49b9-a7cf-605eaf07b006"))); + assertTrue(nodeList.contains(network.getDanglingLine("a16b4a6c-70b1-4abf-9a9d-bd0fa47f9fe4"))); + assertTrue(nodeList.contains(network.getDanglingLine("17086487-56ba-4979-b8de-064025a6b4da"))); + } + + @Test + void testGetNodeListWithoutStaticVarCompensator() { + Network network = TestUtils.getNetworkWithStaticVarCompensatorOnly(); + + List> nodeList = NetworkUtil.getNodeList(network); + assertEquals(2, nodeList.size()); + assertTrue(nodeList.contains(network.getGenerator("G1"))); + assertTrue(nodeList.contains(network.getLoad("L2"))); + } + + @Test + void testGetXNodeList() { + Network network = TestUtils.importNetwork("NETWORK_SINGLE_LOAD_TWO_GENERATORS_WITH_UNBOUNDED_XNODE.uct"); + + List> nodeList = NetworkUtil.getXNodeList(network); + assertEquals(1, nodeList.size()); + assertTrue(nodeList.contains(network.getDanglingLine("BLOAD 11 X 11 1"))); + } + + @Test + void testGetPstIdList() { + Network network = TestUtils.importNetwork("NETWORK_PST_FLOW_WITH_COUNTRIES.uct"); + List nodeList = NetworkUtil.getPstIdList(network); + assertEquals(1, nodeList.size()); + assertTrue(nodeList.contains("BLOAD 11 BLOAD 12 2")); + } +} diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/TestUtils.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/TestUtils.java index d977eaef..44f715f7 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/TestUtils.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/TestUtils.java @@ -6,15 +6,18 @@ */ package com.powsybl.flow_decomposition; +import com.powsybl.cgmes.conformity.CgmesConformity3ModifiedCatalog; import com.powsybl.glsk.api.GlskDocument; import com.powsybl.glsk.api.io.GlskDocumentImporters; -import com.powsybl.iidm.network.Bus; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.Terminal; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.test.SvcTestCaseFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.file.Paths; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * This class contains helper functions for tests. @@ -22,9 +25,11 @@ * @author Hugo Schindler {@literal } */ public final class TestUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(TestUtils.class); private static final double EPSILON = 1e-6; private TestUtils() { + // Utility class } public static Network importNetwork(String networkResourcePath) { @@ -43,12 +48,22 @@ public static void assertCoherenceTotalFlow(FlowDecompositionParameters.RescaleM switch (rescaleMode) { case ACER_METHODOLOGY -> assertEquals(Math.abs(decomposedFlow.getAcTerminal1ReferenceFlow()), decomposedFlow.getTotalFlow(), EPSILON); case PROPORTIONAL -> assertEquals(decomposedFlow.getMaxAbsAcFlow(), decomposedFlow.getTotalFlow(), EPSILON); - default -> assertEquals(Math.abs(decomposedFlow.getDcReferenceFlow()), decomposedFlow.getTotalFlow(), EPSILON); + default -> assertEqualsWithoutRescaling(xnec, decomposedFlow); } } } - static double getLossOnBus(Network network, Bus bus) { + private static void assertEqualsWithoutRescaling(String xnec, DecomposedFlow decomposedFlow) { + if (Double.isNaN(decomposedFlow.getDcReferenceFlow())) { + LOGGER.error("XNEC \"{}\" is probably not connected", xnec); // TODO: should we decompose such xnecs ? + } else if (decomposedFlow.getTotalFlow() == 0) { + LOGGER.error("XNEC \"{}\" is outside main synchronous component", xnec); // TODO: should we decompose such xnecs ? + } else { + assertEquals(Math.abs(decomposedFlow.getDcReferenceFlow()), decomposedFlow.getTotalFlow(), EPSILON); + } + } + + public static double getLossOnBus(Network network, Bus bus) { return network.getBranchStream() .filter(branch -> terminalIsSendingPowerToBus(branch.getTerminal1(), bus) || terminalIsSendingPowerToBus(branch.getTerminal2(), bus)) .mapToDouble(branch -> branch.getTerminal1().getP() + branch.getTerminal2().getP()) @@ -58,4 +73,70 @@ static double getLossOnBus(Network network, Bus bus) { private static boolean terminalIsSendingPowerToBus(Terminal terminal, Bus bus) { return terminal.getBusBreakerView().getBus() == bus && terminal.getP() > 0; } + + public static Network getMicroGridNetworkWithBusBarSectionOnly() { + Network network = Importers.importData("CGMES", CgmesConformity3ModifiedCatalog.microGridBE3DanglingLinesSameBoundary1Disconnected().dataSource(), null); + network.getShuntCompensator("d771118f-36e9-4115-a128-cc3d9ce3e3da").remove(); + network.getShuntCompensator("002b0a40-3957-46db-b84a-30420083558f").remove(); + assertEquals(5, network.getBusbarSectionCount()); + assertEquals(0, network.getShuntCompensatorCount()); + assertEquals(0, network.getStaticVarCompensatorCount()); + return network; + } + + public static Network getMicroGridNetworkWithShuntCompensatorOnly() { + Network network = Importers.importData("CGMES", CgmesConformity3ModifiedCatalog.microGridBE3DanglingLinesSameBoundary1Disconnected().dataSource(), null); + network.getBusbarSection("5caf27ed-d2f8-458a-834a-6b3193a982e6").remove(); + network.getBusbarSection("64901aec-5a8a-4bcb-8ca7-a3ddbfcd0e6c").remove(); + network.getBusbarSection("364c9ca2-0d1d-4363-8f46-e586f8f66a8c").remove(); + network.getBusbarSection("ef45b632-3028-4afe-bc4c-a4fa323d83fe").remove(); + network.getBusbarSection("fd649fe1-bdf5-4062-98ea-bbb66f50402d").remove(); + assertEquals(0, network.getBusbarSectionCount()); + assertEquals(2, network.getShuntCompensatorCount()); + assertEquals(0, network.getStaticVarCompensatorCount()); + return network; + } + + public static Network getNetworkWithStaticVarCompensatorOnly() { + Network network = SvcTestCaseFactory.create(); + assertEquals(0, network.getBusbarSectionCount()); + assertEquals(0, network.getShuntCompensatorCount()); + assertEquals(1, network.getStaticVarCompensatorCount()); + return network; + } + + public static void validateFlowDecomposition(FlowDecompositionResults flowDecompositionResults, + String id, + String branchId, + String contingencyId, + Country country1, + Country country2, + double acReferenceFlow, + double dcReferenceFlow, + double allocatedFlow, + double xNodeFlow, + double pstFlow, + double internalFlow, + double loopFlowBe, + double loopFlowDe, + double loopFLowFr, + double loopFlowNl) { + DecomposedFlow l1 = flowDecompositionResults.getDecomposedFlowMap().get(id); + assertNotNull(l1); + assertEquals(id, l1.getId()); + assertEquals(branchId, l1.getBranchId()); + assertEquals(contingencyId, l1.getContingencyId()); + assertEquals(country1, l1.getCountry1()); + assertEquals(country2, l1.getCountry2()); + assertEquals(acReferenceFlow, l1.getAcTerminal1ReferenceFlow(), EPSILON); + assertEquals(dcReferenceFlow, l1.getDcReferenceFlow(), EPSILON); + assertEquals(allocatedFlow, l1.getAllocatedFlow(), EPSILON); + assertEquals(xNodeFlow, l1.getXNodeFlow(), EPSILON); + assertEquals(pstFlow, l1.getPstFlow(), EPSILON); + assertEquals(internalFlow, l1.getInternalFlow(), EPSILON); + assertEquals(loopFlowBe, l1.getLoopFlow(Country.BE), EPSILON); + assertEquals(loopFlowDe, l1.getLoopFlow(Country.DE), EPSILON); + assertEquals(loopFLowFr, l1.getLoopFlow(Country.FR), EPSILON); + assertEquals(loopFlowNl, l1.getLoopFlow(Country.NL), EPSILON); + } } diff --git a/flow-decomposition/src/test/resources/com/powsybl/flow_decomposition/TestCase16NodesWithHvdc.xiidm b/flow-decomposition/src/test/resources/com/powsybl/flow_decomposition/TestCase16NodesWithHvdc.xiidm new file mode 100644 index 00000000..d103164d --- /dev/null +++ b/flow-decomposition/src/test/resources/com/powsybl/flow_decomposition/TestCase16NodesWithHvdc.xiidm @@ -0,0 +1,378 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flow-decomposition/src/test/resources/com/powsybl/flow_decomposition/lossesCompensatorLineConnectedToOneSide.xiidm b/flow-decomposition/src/test/resources/com/powsybl/flow_decomposition/lossesCompensatorLineConnectedToOneSide.xiidm new file mode 100644 index 00000000..3ec65e7e --- /dev/null +++ b/flow-decomposition/src/test/resources/com/powsybl/flow_decomposition/lossesCompensatorLineConnectedToOneSide.xiidm @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +