diff --git a/docs/flow_decomposition/algorithm-description.md b/docs/flow_decomposition/algorithm-description.md index cd827706..f1d392db 100644 --- a/docs/flow_decomposition/algorithm-description.md +++ b/docs/flow_decomposition/algorithm-description.md @@ -99,5 +99,15 @@ calculated by the DC power flow. However, the flow reference is the one calculated using AC power flow which is different. The final step of the algorithm is though to rescale the different flow parts in order to ensure that the sum of the parts is equal to the initially calculated AC flow. +By default, no rescaling to the AC flow is done on the flow decomposition results. + +Available rescaling modes are defined here below. + +#### ACER methodology-based rescaling The difference between reference AC flow and the sum of the parts of the decomposition is redispatched on the different -parts proportionally to their rectified linear unit ($\mathrm{ReLU}(x) = \mathrm{max}(x, 0)$). \ No newline at end of file +parts proportionally to their rectified linear unit ($\mathrm{ReLU}(x) = \mathrm{max}(x, 0)$). + +#### Proportional rescaling +Each flow is rescaled with a proportional coefficient. The coefficient is defined by $$\alpha_{\text{rescale}} = \frac{max(|AC p1|, |AC p2|)}{|DC p1|}$$. +In this way, the DC flow will have the same magnitude as the AC flow. +Since we divide by the DC flow to calculate the coefficient, lines with a too small DC flow are not rescaled. \ No newline at end of file diff --git a/docs/flow_decomposition/configuration.md b/docs/flow_decomposition/configuration.md index c8332ba9..9682852a 100644 --- a/docs/flow_decomposition/configuration.md +++ b/docs/flow_decomposition/configuration.md @@ -2,14 +2,15 @@ ## Dedicated parameters -| Name | Type | Default value | Description | -|-----------------------------------------|---------|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| enable-losses-compensation | boolean | false | When set to true, adds losses compensation step of the algorithm. Otherwise, all losses will be compensated using chosen power flow compensation strategy. | -| losses-compensation-epsilon | double | 1e-5 | Threshold used in losses compensation step of the algorihm. If actual losses are below the given threshold on a branch, no injection is created in the network to compensate these losses. Used to avoid creating too many injections in the network. May have an impact in overall algorithm performance and memory usage. | -| sensitivity-epsilon | double | 1e-5 | Threshold used when filling PTDF and PSDF matrices. If a sensitivity is below the given threshold, it is set to zero. Used to keep sparse matrices in the algorithm. May have an impact in overall algorithm performance and memory usage. | -| rescale-enabled | boolean | false | When set to true, rescaling step is done to ensure that the sum of all flow parts is equal to the AC reference flow. | -| dc-fallback-enabled-after-ac-divergence | boolean | true | Defines the fallback behavior after an AC divergence Use True to run DC loadflow if an AC loadflow diverges (default). Use False to throw an exception if an AC loadflow diverges. | -| sensitivity-variable-batch-size | int | 15000 | When set to a lower value, this parameter will reduce memory usage, but it might increase computation time | +| Name | Type | Default value | Description | +|-------------------------------------------|---------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| enable-losses-compensation | boolean | false | When set to true, adds losses compensation step of the algorithm. Otherwise, all losses will be compensated using chosen power flow compensation strategy. | +| losses-compensation-epsilon | double | 1e-5 | Threshold used in losses compensation step of the algorihm. If actual losses are below the given threshold on a branch, no injection is created in the network to compensate these losses. Used to avoid creating too many injections in the network. May have an impact in overall algorithm performance and memory usage. | +| sensitivity-epsilon | double | 1e-5 | Threshold used when filling PTDF and PSDF matrices. If a sensitivity is below the given threshold, it is set to zero. Used to keep sparse matrices in the algorithm. May have an impact in overall algorithm performance and memory usage. | +| rescale-mode | enum | NONE | Use NONE if you don't want to rescale flow decomposition results. Use ACER_METHODOLOGY for the ACER methodology rescaling strategy. Use PROPORTIONAL for a proportional rescaling. See [Flow parts rescaling](../flow_decomposition/algorithm-description.md#flow-parts-rescaling) for more details. | +| proportional-rescaler-min-flow-tolerance | double | 1e-6 | Option only used if rescale-mode is PROPORTIONAL. Defines the minimum DC flow required in MW for the rescaling to happen. | +| dc-fallback-enabled-after-ac-divergence | boolean | true | Defines the fallback behavior after an AC divergence Use True to run DC loadflow if an AC loadflow diverges (default). Use False to throw an exception if an AC loadflow diverges. | +| sensitivity-variable-batch-size | int | 15000 | When set to a lower value, this parameter will reduce memory usage, but it might increase computation time | ## Impact of existing parameters diff --git a/docs/flow_decomposition/flow-decomposition-outputs.md b/docs/flow_decomposition/flow-decomposition-outputs.md index b2de9ae4..a61c222a 100644 --- a/docs/flow_decomposition/flow-decomposition-outputs.md +++ b/docs/flow_decomposition/flow-decomposition-outputs.md @@ -4,7 +4,8 @@ For each network element of interest, flow decomposition outputs contain the following elements: - Reference flow : active power flow that is considered as the reference for the decomposition. It is actually equal - to the sum of all the flow parts calculated by the algorithm. + to the sum of all the flow parts calculated by the algorithm. to the sum of all the flow parts calculated by the algorithm. + Reference AC flows are available on terminal 1 and 2. - Allocated flow : allocated flow part of the network element's flow. - Internal flow : internal flow part of the network element's flow. It is calculated as the loop flow from the country which network element is part of (interconnections are considered as part of no specific country, so will always have an internal flow to 0). @@ -16,6 +17,7 @@ For each network element of interest, flow decomposition outputs contain the fol On one hand, the reference flows are oriented from side 1 to side 2 of the associated IIDM branch. A positive reference flow implies a flow from side 1 to side 2, while a negative one means a flow from side 2 to side 1. +For coherence and simplicity purposes, the entire algorithm (except [proportional rescaling](../flow_decomposition/algorithm-description.md#proportional-rescaling)) is based on the side 1. On the other hand, all flow parts (allocated flow, internal flow, loop flows and PST flow) are oriented in the branch flow convention. A positive flow part tends to increase the absolute flow on the branch (i.e. a burdening flow), while a diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/AcReferenceFlowComputer.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/AcReferenceFlowComputer.java deleted file mode 100644 index cb342b82..00000000 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/AcReferenceFlowComputer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) - * 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.iidm.network.Branch; -import com.powsybl.iidm.network.Identifiable; - -import java.util.Collection; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * @author Guillaume Verger {@literal } - */ -class AcReferenceFlowComputer { - private static ReferenceFlowComputer flowComputer = new ReferenceFlowComputer(); - - Map run(Collection xnecList, LoadFlowRunningService.Result loadFlowServiceAcResult) { - if (loadFlowServiceAcResult.fallbackHasBeenActivated()) { - return xnecList.stream().collect(Collectors.toMap(Identifiable::getId, branch -> Double.NaN)); - } - - return flowComputer.run(xnecList); - } -} diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/CsvExporter.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/CsvExporter.java index e5065529..04008736 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/CsvExporter.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/CsvExporter.java @@ -85,7 +85,8 @@ private void printHeaderRow(Set loopFlowKeys, CSVPrinter printer) { failSilentlyPrint(printer, DecomposedFlow.INTERNAL_COLUMN_NAME); failSilentlyPrint(printer, DecomposedFlow.PST_COLUMN_NAME); loopFlowKeys.stream().sorted().forEach(loopFlowKey -> failSilentlyPrint(printer, loopFlowKey)); - failSilentlyPrint(printer, DecomposedFlow.AC_REFERENCE_FLOW_COLUMN_NAME); + failSilentlyPrint(printer, DecomposedFlow.AC_REFERENCE_FLOW_1_COLUMN_NAME); + failSilentlyPrint(printer, DecomposedFlow.AC_REFERENCE_FLOW_2_COLUMN_NAME); failSilentlyPrint(printer, DecomposedFlow.DC_REFERENCE_FLOW_COLUMN_NAME); failSilentlyPrintLn(printer); } @@ -100,7 +101,8 @@ private void printContentRow(String xnecId, DecomposedFlow decomposedFlow, Set failSilentlyPrint(printer, decomposedFlow.getLoopFlows().getOrDefault(loopFlowKey, NO_FLOW))); - failSilentlyPrint(printer, decomposedFlow.getAcReferenceFlow()); + failSilentlyPrint(printer, decomposedFlow.getAcTerminal1ReferenceFlow()); + failSilentlyPrint(printer, decomposedFlow.getAcTerminal2ReferenceFlow()); failSilentlyPrint(printer, decomposedFlow.getDcReferenceFlow()); failSilentlyPrintLn(printer); } 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 6fbe1aec..edceba81 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 @@ -19,7 +19,8 @@ public class DecomposedFlow { private final String contingencyId; private final Country country1; private final Country country2; - private final double acReferenceFlow; + private final double acTerminal1ReferenceFlow; + private final double acTerminal2ReferenceFlow; private final double dcReferenceFlow; private final double allocatedFlow; private final double xNodeFlow; @@ -27,25 +28,27 @@ public class DecomposedFlow { private final double internalFlow; private final Map loopFlowsMap = new TreeMap<>(); static final double NO_FLOW = 0.; - static final String AC_REFERENCE_FLOW_COLUMN_NAME = "Reference AC Flow"; + static final String AC_REFERENCE_FLOW_1_COLUMN_NAME = "Reference AC Flow 1"; + static final String AC_REFERENCE_FLOW_2_COLUMN_NAME = "Reference AC Flow 2"; static final String DC_REFERENCE_FLOW_COLUMN_NAME = "Reference DC Flow"; static final String ALLOCATED_COLUMN_NAME = "Allocated Flow"; static final String XNODE_COLUMN_NAME = "Xnode Flow"; static final String PST_COLUMN_NAME = "PST Flow"; static final String INTERNAL_COLUMN_NAME = "Internal Flow"; - protected DecomposedFlow(String branchId, String contingencyId, Country country1, Country country2, double acReferenceFlow, double dcReferenceFlow, double allocatedFlow, double xNodeFlow, double pstFlow, double internalFlow, Map loopFlowsMap) { - this.branchId = branchId; - this.contingencyId = contingencyId; - this.country1 = country1; - this.country2 = country2; - this.acReferenceFlow = acReferenceFlow; - this.dcReferenceFlow = dcReferenceFlow; - this.allocatedFlow = allocatedFlow; - this.xNodeFlow = xNodeFlow; - this.pstFlow = pstFlow; - this.internalFlow = internalFlow; - this.loopFlowsMap.putAll(loopFlowsMap); + protected DecomposedFlow(DecomposedFlowBuilder builder) { + this.branchId = Objects.requireNonNull(builder.branchId); + this.contingencyId = Objects.requireNonNull(builder.contingencyId); + this.country1 = Objects.requireNonNull(builder.country1); + this.country2 = Objects.requireNonNull(builder.country2); + this.acTerminal1ReferenceFlow = builder.acTerminal1ReferenceFlow; + this.acTerminal2ReferenceFlow = builder.acTerminal2ReferenceFlow; + this.dcReferenceFlow = builder.dcReferenceFlow; + this.allocatedFlow = builder.allocatedFlow; + this.xNodeFlow = builder.xNodeFlow; + this.pstFlow = builder.pstFlow; + this.internalFlow = builder.internalFlow; + this.loopFlowsMap.putAll(Objects.requireNonNull(builder.loopFlowsMap)); } public String getBranchId() { @@ -68,8 +71,12 @@ public Country getCountry2() { return country2; } - public double getAcReferenceFlow() { - return acReferenceFlow; + public double getAcTerminal1ReferenceFlow() { + return acTerminal1ReferenceFlow; + } + + public double getAcTerminal2ReferenceFlow() { + return acTerminal2ReferenceFlow; } public double getDcReferenceFlow() { @@ -112,6 +119,10 @@ public double getTotalFlow() { return getAllocatedFlow() + getXNodeFlow() + getPstFlow() + getInternalFlow() + getTotalLoopFlow(); } + public double getMaxAbsAcFlow() { + return Math.max(Math.abs(acTerminal1ReferenceFlow), Math.abs(acTerminal2ReferenceFlow)); + } + @Override public String toString() { return String.format("branchId: %s, contingencyId: %s, decomposition: %s", branchId, contingencyId, getAllKeyMap()); @@ -119,7 +130,8 @@ public String toString() { private TreeMap getAllKeyMap() { TreeMap localDecomposedFlowMap = new TreeMap<>(); - localDecomposedFlowMap.put(AC_REFERENCE_FLOW_COLUMN_NAME, getAcReferenceFlow()); + localDecomposedFlowMap.put(AC_REFERENCE_FLOW_1_COLUMN_NAME, getAcTerminal1ReferenceFlow()); + localDecomposedFlowMap.put(AC_REFERENCE_FLOW_2_COLUMN_NAME, getAcTerminal2ReferenceFlow()); localDecomposedFlowMap.put(DC_REFERENCE_FLOW_COLUMN_NAME, getDcReferenceFlow()); localDecomposedFlowMap.put(ALLOCATED_COLUMN_NAME, getAllocatedFlow()); localDecomposedFlowMap.put(XNODE_COLUMN_NAME, getXNodeFlow()); diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlowBuilder.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlowBuilder.java new file mode 100644 index 00000000..409ba388 --- /dev/null +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlowBuilder.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.flow_decomposition; + +import com.powsybl.iidm.network.Country; + +import java.util.Map; + +/** + * @author Caio Luke {@literal } + * @author Sebastien Murgey {@literal } + * @author Hugo Schindler {@literal } + */ +public class DecomposedFlowBuilder { + protected String branchId; + protected String contingencyId; + protected Country country1; + protected Country country2; + protected double acTerminal1ReferenceFlow; + protected double acTerminal2ReferenceFlow; + protected double dcReferenceFlow; + protected double allocatedFlow; + protected double xNodeFlow; + protected double pstFlow; + protected double internalFlow; + protected Map loopFlowsMap; + + public DecomposedFlowBuilder() { + // empty constructor + } + + public DecomposedFlowBuilder withBranchId(String branchId) { + this.branchId = branchId; + return this; + } + + public DecomposedFlowBuilder withContingencyId(String contingencyId) { + this.contingencyId = contingencyId; + return this; + } + + public DecomposedFlowBuilder withCountry1(Country country1) { + this.country1 = country1; + return this; + } + + public DecomposedFlowBuilder withCountry2(Country country2) { + this.country2 = country2; + return this; + } + + public DecomposedFlowBuilder withAcTerminal1ReferenceFlow(double acTerminal1ReferenceFlow) { + this.acTerminal1ReferenceFlow = acTerminal1ReferenceFlow; + return this; + } + + public DecomposedFlowBuilder withAcTerminal2ReferenceFlow(double acTerminal2ReferenceFlow) { + this.acTerminal2ReferenceFlow = acTerminal2ReferenceFlow; + return this; + } + + public DecomposedFlowBuilder withDcReferenceFlow(double dcReferenceFlow) { + this.dcReferenceFlow = dcReferenceFlow; + return this; + } + + public DecomposedFlowBuilder withAllocatedFlow(double allocatedFlow) { + this.allocatedFlow = allocatedFlow; + return this; + } + + public DecomposedFlowBuilder withXNodeFlow(double xNodeFlow) { + this.xNodeFlow = xNodeFlow; + return this; + } + + public DecomposedFlowBuilder withPstFlow(double pstFlow) { + this.pstFlow = pstFlow; + return this; + } + + public DecomposedFlowBuilder withInternalFlow(double internalFlow) { + this.internalFlow = internalFlow; + return this; + } + + public DecomposedFlowBuilder withLoopFlowsMap(Map loopFlowsMap) { + this.loopFlowsMap = loopFlowsMap; + return this; + } + + public DecomposedFlow build() { + return new DecomposedFlow(this); + } +} diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowComputerUtils.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowComputerUtils.java new file mode 100644 index 00000000..206e5d75 --- /dev/null +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowComputerUtils.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * 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.iidm.network.Branch; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.TwoSides; + +import java.util.Collection; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Sebastien Murgey {@literal } + * @author Hugo Schindler {@literal } + * @author Guillaume Verger {@literal } + * @author Caio Luke {@literal } + */ +public final class FlowComputerUtils { + + private FlowComputerUtils() { + // empty constructor + } + + public static Map calculateAcTerminalReferenceFlows(Collection xnecList, LoadFlowRunningService.Result loadFlowServiceAcResult, TwoSides side) { + if (loadFlowServiceAcResult.fallbackHasBeenActivated()) { + return xnecList.stream().collect(Collectors.toMap(Identifiable::getId, branch -> Double.NaN)); + } + return getTerminalReferenceFlow(xnecList, side); + } + + public static Map getTerminalReferenceFlow(Collection xnecList, TwoSides side) { + return xnecList.stream() + .collect(Collectors.toMap( + Identifiable::getId, + branch -> branch.getTerminal(side).getP() + )); + } +} 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 99d35078..0dd0c290 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 @@ -6,10 +6,16 @@ */ package com.powsybl.flow_decomposition; +import com.powsybl.commons.PowsyblException; import com.powsybl.flow_decomposition.glsk_provider.AutoGlskProvider; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescaler; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerAcerMethodology; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerNoOp; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerProportional; import com.powsybl.iidm.network.Branch; import com.powsybl.iidm.network.Country; import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; import com.powsybl.loadflow.LoadFlow; import com.powsybl.loadflow.LoadFlowParameters; import com.powsybl.sensitivity.SensitivityAnalysis; @@ -32,7 +38,7 @@ public class FlowDecompositionComputer { private final LoadFlowRunningService loadFlowRunningService; private final SensitivityAnalysis.Runner sensitivityAnalysisRunner; private final LossesCompensator lossesCompensator; - + private final DecomposedFlowRescaler decomposedFlowRescaler; private final FlowDecompositionObserverList observers; public FlowDecompositionComputer() { @@ -47,6 +53,7 @@ public FlowDecompositionComputer(FlowDecompositionParameters flowDecompositionPa this.loadFlowRunningService = new LoadFlowRunningService(LoadFlow.find(loadFlowProvider)); this.sensitivityAnalysisRunner = SensitivityAnalysis.find(sensitivityAnalysisProvider); this.lossesCompensator = parameters.isLossesCompensationEnabled() ? new LossesCompensator(parameters) : null; + this.decomposedFlowRescaler = getDecomposedFlowRescaler(); this.observers = new FlowDecompositionObserverList(); } @@ -134,7 +141,7 @@ private void decomposeFlowForState(Network network, Map netPositions, Map> glsks, LoadFlowRunningService.Result loadFlowServiceAcResult) { - saveAcReferenceFlow(flowDecompositionResultsBuilder, xnecList, loadFlowServiceAcResult); + saveAcReferenceFlows(flowDecompositionResultsBuilder, xnecList, loadFlowServiceAcResult); compensateLosses(network); observers.computedAcFlows(network, loadFlowServiceAcResult); @@ -162,7 +169,7 @@ private void decomposeFlowForState(Network network, computeAllocatedAndLoopFlows(flowDecompositionResultsBuilder, nodalInjectionsMatrix, ptdfMatrix); computePstFlows(network, flowDecompositionResultsBuilder, networkMatrixIndexes, psdfMatrix); - flowDecompositionResultsBuilder.build(parameters.isRescaleEnabled()); + flowDecompositionResultsBuilder.build(decomposedFlowRescaler); } public void addObserver(FlowDecompositionObserver observer) { @@ -177,9 +184,11 @@ private LoadFlowRunningService.Result runAcLoadFlow(Network network) { return loadFlowRunningService.runAcLoadflow(network, loadFlowParameters, parameters.isDcFallbackEnabledAfterAcDivergence()); } - private void saveAcReferenceFlow(FlowDecompositionResults.PerStateBuilder flowDecompositionResultBuilder, Set xnecList, LoadFlowRunningService.Result loadFlowServiceAcResult) { - Map acReferenceFlows = new AcReferenceFlowComputer().run(xnecList, loadFlowServiceAcResult); - flowDecompositionResultBuilder.saveAcReferenceFlow(acReferenceFlows); + private void saveAcReferenceFlows(FlowDecompositionResults.PerStateBuilder flowDecompositionResultBuilder, 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); } private Map getZonesNetPosition(Network network) { @@ -187,16 +196,21 @@ private Map getZonesNetPosition(Network network) { return netPositionComputer.run(network); } - private Map getBranchReferenceFlows(Set branches) { - return new ReferenceFlowComputer().run(branches); - } - private void compensateLosses(Network network) { if (parameters.isLossesCompensationEnabled()) { lossesCompensator.run(network); } } + private DecomposedFlowRescaler getDecomposedFlowRescaler() { + return switch (parameters.getRescaleMode()) { + case NONE -> new DecomposedFlowRescalerNoOp(); + case ACER_METHODOLOGY -> new DecomposedFlowRescalerAcerMethodology(); + case PROPORTIONAL -> new DecomposedFlowRescalerProportional(parameters.getProportionalRescalerMinFlowTolerance()); + default -> throw new PowsyblException("DecomposedFlowRescaler not defined for mode: " + parameters.getRescaleMode()); + }; + } + private LoadFlowRunningService.Result runDcLoadFlow(Network network) { return loadFlowRunningService.runDcLoadflow(network, loadFlowParameters); } @@ -210,7 +224,7 @@ private SparseMatrixWithIndexesTriplet getNodalInjectionsMatrix(Network network, } private void saveDcReferenceFlow(FlowDecompositionResults.PerStateBuilder flowDecompositionResultBuilder, Set xnecList) { - flowDecompositionResultBuilder.saveDcReferenceFlow(getBranchReferenceFlows(xnecList)); + flowDecompositionResultBuilder.saveDcReferenceFlow(FlowComputerUtils.getTerminalReferenceFlow(xnecList, TwoSides.ONE)); } private SensitivityAnalyser getSensitivityAnalyser(Network network, NetworkMatrixIndexes networkMatrixIndexes) { 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 4f55bd14..4a56c9a3 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 @@ -10,6 +10,7 @@ import com.powsybl.flow_decomposition.LoadFlowRunningService.Result; import com.powsybl.iidm.network.Country; import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoSides; import java.util.ArrayList; import java.util.List; @@ -19,6 +20,7 @@ * @author Guillaume Verger {@literal } */ public class FlowDecompositionObserverList { + private final List observers; public FlowDecompositionObserverList() { @@ -86,8 +88,7 @@ public void computedAcFlows(Network network, Result loadFlowServiceAcResult) { return; } - Map acFlows = - new AcReferenceFlowComputer().run(network.getBranchStream().toList(), loadFlowServiceAcResult); + Map acFlows = FlowComputerUtils.calculateAcTerminalReferenceFlows(network.getBranchStream().toList(), loadFlowServiceAcResult, TwoSides.ONE); for (FlowDecompositionObserver o : observers) { o.computedAcFlows(acFlows); @@ -99,7 +100,7 @@ public void computedDcFlows(Network network) { return; } - Map dcFlows = new ReferenceFlowComputer().run(network.getBranchStream().toList()); + Map dcFlows = FlowComputerUtils.getTerminalReferenceFlow(network.getBranchStream().toList(), TwoSides.ONE); for (FlowDecompositionObserver o : observers) { o.computedDcFlows(dcFlows); @@ -143,6 +144,6 @@ private void sendMatrix(MatrixNotification notification, SparseMatrixWithIndexes @FunctionalInterface private interface MatrixNotification { - public void sendMatrix(FlowDecompositionObserver o, Map> matrix); + void sendMatrix(FlowDecompositionObserver o, Map> matrix); } } diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionParameters.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionParameters.java index edd5f18b..8386ec57 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionParameters.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/FlowDecompositionParameters.java @@ -7,6 +7,7 @@ package com.powsybl.flow_decomposition; import com.powsybl.commons.config.PlatformConfig; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerProportional; import java.util.Objects; @@ -15,8 +16,6 @@ * @author Hugo Schindler {@literal } */ public class FlowDecompositionParameters { - public static final boolean ENABLE_RESCALED_RESULTS = true; - public static final boolean DISABLE_RESCALED_RESULTS = false; public static final double DISABLE_SENSITIVITY_EPSILON = -1; public static final boolean DISABLE_LOSSES_COMPENSATION = false; public static final boolean ENABLE_LOSSES_COMPENSATION = true; @@ -24,15 +23,23 @@ public class FlowDecompositionParameters { public static final boolean DEFAULT_ENABLE_LOSSES_COMPENSATION = DISABLE_LOSSES_COMPENSATION; public static final double DEFAULT_LOSSES_COMPENSATION_EPSILON = 1e-5; public static final double DEFAULT_SENSITIVITY_EPSILON = 1e-5; - public static final boolean DEFAULT_RESCALE_ENABLED = DISABLE_RESCALED_RESULTS; public static final boolean DISABLE_DC_FALLBACK_AFTER_AC_DIVERGENCE = false; public static final boolean ENABLE_DC_FALLBACK_AFTER_AC_DIVERGENCE = true; public static final boolean DEFAULT_DC_FALLBACK_ENABLED_AFTER_AC_DIVERGENCE = ENABLE_DC_FALLBACK_AFTER_AC_DIVERGENCE; private static final int DEFAULT_SENSITIVITY_VARIABLE_BATCH_SIZE = 15000; + + public enum RescaleMode { + NONE, + ACER_METHODOLOGY, + PROPORTIONAL + } + + public static final RescaleMode DEFAULT_RESCALE_MODE = RescaleMode.NONE; private boolean enableLossesCompensation; private double lossesCompensationEpsilon; private double sensitivityEpsilon; - private boolean rescaleEnabled; + private RescaleMode rescaleMode; + private double proportionalRescalerMinFlowTolerance; private boolean dcFallbackEnabledAfterAcDivergence; private int sensitivityVariableBatchSize; @@ -53,7 +60,8 @@ private static void load(FlowDecompositionParameters parameters, PlatformConfig parameters.setEnableLossesCompensation(moduleConfig.getBooleanProperty("enable-losses-compensation", DEFAULT_ENABLE_LOSSES_COMPENSATION)); parameters.setLossesCompensationEpsilon(moduleConfig.getDoubleProperty("losses-compensation-epsilon", DEFAULT_LOSSES_COMPENSATION_EPSILON)); parameters.setSensitivityEpsilon(moduleConfig.getDoubleProperty("sensitivity-epsilon", DEFAULT_SENSITIVITY_EPSILON)); - parameters.setRescaleEnabled(moduleConfig.getBooleanProperty("rescale-enabled", DEFAULT_RESCALE_ENABLED)); + parameters.setRescaleMode(moduleConfig.getEnumProperty("rescale-mode", RescaleMode.class, DEFAULT_RESCALE_MODE)); + parameters.setProportionalRescalerMinFlowTolerance(moduleConfig.getDoubleProperty("proportional-rescaler-min-flow-tolerance", DecomposedFlowRescalerProportional.DEFAULT_MIN_FLOW_TOLERANCE)); parameters.setDcFallbackEnabledAfterAcDivergence(moduleConfig.getBooleanProperty("dc-fallback-enabled-after-ac-divergence", DEFAULT_DC_FALLBACK_ENABLED_AFTER_AC_DIVERGENCE)); parameters.setSensitivityVariableBatchSize(moduleConfig.getIntProperty("sensitivity-variable-batch-size", DEFAULT_SENSITIVITY_VARIABLE_BATCH_SIZE)); }); @@ -63,7 +71,8 @@ public FlowDecompositionParameters() { this.enableLossesCompensation = DEFAULT_ENABLE_LOSSES_COMPENSATION; this.lossesCompensationEpsilon = DEFAULT_LOSSES_COMPENSATION_EPSILON; this.sensitivityEpsilon = DEFAULT_SENSITIVITY_EPSILON; - this.rescaleEnabled = DEFAULT_RESCALE_ENABLED; + this.rescaleMode = DEFAULT_RESCALE_MODE; + this.proportionalRescalerMinFlowTolerance = DecomposedFlowRescalerProportional.DEFAULT_MIN_FLOW_TOLERANCE; this.dcFallbackEnabledAfterAcDivergence = DEFAULT_DC_FALLBACK_ENABLED_AFTER_AC_DIVERGENCE; this.sensitivityVariableBatchSize = DEFAULT_SENSITIVITY_VARIABLE_BATCH_SIZE; } @@ -95,15 +104,23 @@ public FlowDecompositionParameters setSensitivityEpsilon(double sensitivityEpsil return this; } - public boolean isRescaleEnabled() { - return rescaleEnabled; + public RescaleMode getRescaleMode() { + return rescaleMode; } - public FlowDecompositionParameters setRescaleEnabled(boolean rescaleEnabled) { - this.rescaleEnabled = rescaleEnabled; + public FlowDecompositionParameters setRescaleMode(RescaleMode rescaleMode) { + this.rescaleMode = rescaleMode; return this; } + public void setProportionalRescalerMinFlowTolerance(double proportionalRescalerMinFlowTolerance) { + this.proportionalRescalerMinFlowTolerance = proportionalRescalerMinFlowTolerance; + } + + public double getProportionalRescalerMinFlowTolerance() { + return proportionalRescalerMinFlowTolerance; + } + public boolean isDcFallbackEnabledAfterAcDivergence() { return this.dcFallbackEnabledAfterAcDivergence; } 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 10b1a42e..f6329f9b 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 @@ -6,6 +6,7 @@ */ package com.powsybl.flow_decomposition; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescaler; import com.powsybl.iidm.network.Branch; import com.powsybl.iidm.network.Country; import com.powsybl.iidm.network.Identifiable; @@ -44,7 +45,8 @@ class PerStateBuilder { private final String contingencyId; private SparseMatrixWithIndexesCSC allocatedAndLoopFlowsMatrix; private Map> pstFlowMap; - private Map acReferenceFlow; + private Map acTerminal1ReferenceFlow; + private Map acTerminal2ReferenceFlow; private Map dcReferenceFlow; PerStateBuilder(String contingencyId, Set xnecList) { @@ -60,23 +62,27 @@ void savePstFlowMatrix(SparseMatrixWithIndexesCSC pstFlowMatrix) { this.pstFlowMap = pstFlowMatrix.toMap(); } - void saveAcReferenceFlow(Map acReferenceFlow) { - this.acReferenceFlow = acReferenceFlow; + void saveAcTerminal1ReferenceFlow(Map acTerminal1ReferenceFlow) { + this.acTerminal1ReferenceFlow = acTerminal1ReferenceFlow; + } + + void saveAcTerminal2ReferenceFlow(Map acTerminal2ReferenceFlow) { + this.acTerminal2ReferenceFlow = acTerminal2ReferenceFlow; } void saveDcReferenceFlow(Map dcReferenceFlow) { this.dcReferenceFlow = dcReferenceFlow; } - void build(boolean isRescaleEnable) { + void build(DecomposedFlowRescaler decomposedFlowRescaler) { allocatedAndLoopFlowsMatrix.toMap() .forEach((branchId, decomposedFlow) -> { String xnecId = NetworkUtil.getXnecId(contingencyId, branchId); - decomposedFlowMap.put(xnecId, createDecomposedFlow(branchId, decomposedFlow, isRescaleEnable)); + decomposedFlowMap.put(xnecId, createDecomposedFlow(branchId, decomposedFlow, decomposedFlowRescaler)); }); } - private DecomposedFlow createDecomposedFlow(String branchId, Map allocatedAndLoopFlowMap, boolean isRescaleEnable) { + private DecomposedFlow createDecomposedFlow(String branchId, Map allocatedAndLoopFlowMap, DecomposedFlowRescaler decomposedFlowRescaler) { Map loopFlowsMap = allocatedAndLoopFlowMap.entrySet().stream() .filter(entry -> entry.getKey().startsWith(LOOP_FLOWS_COLUMN_PREFIX)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); @@ -86,11 +92,21 @@ private DecomposedFlow createDecomposedFlow(String branchId, Map Country country1 = NetworkUtil.getTerminalCountry(xnecMap.get(branchId).getTerminal1()); Country country2 = NetworkUtil.getTerminalCountry(xnecMap.get(branchId).getTerminal2()); double internalFlow = extractInternalFlow(loopFlowsMap, country1, country2); - DecomposedFlow decomposedFlow = new DecomposedFlow(branchId, contingencyId, country1, country2, acReferenceFlow.get(branchId), dcReferenceFlow.get(branchId), allocatedFlow, xNodeFlow, pstFlow, internalFlow, loopFlowsMap); - if (isRescaleEnable) { - return DecomposedFlowsRescaler.rescale(decomposedFlow); - } - return decomposedFlow; + DecomposedFlow decomposedFlow = new DecomposedFlowBuilder() + .withBranchId(branchId) + .withContingencyId(contingencyId) + .withCountry1(country1) + .withCountry2(country2) + .withAcTerminal1ReferenceFlow(acTerminal1ReferenceFlow.get(branchId)) + .withAcTerminal2ReferenceFlow(acTerminal2ReferenceFlow.get(branchId)) + .withDcReferenceFlow(dcReferenceFlow.get(branchId)) + .withAllocatedFlow(allocatedFlow) + .withXNodeFlow(xNodeFlow) + .withPstFlow(pstFlow) + .withInternalFlow(internalFlow) + .withLoopFlowsMap(loopFlowsMap) + .build(); + return decomposedFlowRescaler.rescale(decomposedFlow); } private double extractInternalFlow(Map loopFlowsMap, Country country1, Country country2) { diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/ReferenceFlowComputer.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/ReferenceFlowComputer.java deleted file mode 100644 index 68fb4061..00000000 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/ReferenceFlowComputer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2022, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package com.powsybl.flow_decomposition; - -import com.powsybl.iidm.network.Branch; -import com.powsybl.iidm.network.Identifiable; - -import java.util.Collection; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * @author Sebastien Murgey {@literal } - * @author Hugo Schindler {@literal } - */ -class ReferenceFlowComputer { - Map run(Collection xnecList) { - return xnecList.stream() - .collect(Collectors.toMap( - Identifiable::getId, - this::getP - )); - } - - private double getP(Branch branch) { - return branch.getTerminal1().getP(); - } -} diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescaler.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescaler.java new file mode 100644 index 00000000..0051eeca --- /dev/null +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescaler.java @@ -0,0 +1,19 @@ +/** + * 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.rescaler; + +import com.powsybl.flow_decomposition.DecomposedFlow; + +/** + * @author Sebastien Murgey {@literal } + * @author Hugo Schindler {@literal } + * @author Caio Luke {@literal } + */ +public interface DecomposedFlowRescaler { + DecomposedFlow rescale(DecomposedFlow decomposedFlow); +} diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlowsRescaler.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescalerAcerMethodology.java similarity index 51% rename from flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlowsRescaler.java rename to flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescalerAcerMethodology.java index 3e2f1af4..fc72f893 100644 --- a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/DecomposedFlowsRescaler.java +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescalerAcerMethodology.java @@ -1,11 +1,14 @@ -/* - * Copyright (c) 2022, RTE (http://www.rte-france.com) +/** + * 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; +package com.powsybl.flow_decomposition.rescaler; +import com.powsybl.flow_decomposition.DecomposedFlow; +import com.powsybl.flow_decomposition.DecomposedFlowBuilder; import com.powsybl.iidm.network.Country; import java.util.Map; @@ -14,9 +17,12 @@ /** * @author Sebastien Murgey {@literal } * @author Hugo Schindler {@literal } + * @author Caio Luke {@literal } */ -final class DecomposedFlowsRescaler { - private DecomposedFlowsRescaler() { +public class DecomposedFlowRescalerAcerMethodology implements DecomposedFlowRescaler { + + public DecomposedFlowRescalerAcerMethodology() { + // empty constructor } private static double reLU(double value) { @@ -27,13 +33,18 @@ private static double rescaleValue(double initialValue, double delta, double sum return initialValue + delta * reLU(initialValue) / sumOfReLUFlows; } - static DecomposedFlow rescale(DecomposedFlow decomposedFlow) { + @Override + public DecomposedFlow rescale(DecomposedFlow decomposedFlow) { + double acTerminal1ReferenceFlow = decomposedFlow.getAcTerminal1ReferenceFlow(); + double acTerminal2ReferenceFlow = decomposedFlow.getAcTerminal2ReferenceFlow(); + if (Double.isNaN(acTerminal1ReferenceFlow) || Double.isNaN(acTerminal2ReferenceFlow)) { + return decomposedFlow; + } + String branchId = decomposedFlow.getBranchId(); String contingencyId = decomposedFlow.getContingencyId(); Country country1 = decomposedFlow.getCountry1(); Country country2 = decomposedFlow.getCountry2(); - - double acReferenceFlow = decomposedFlow.getAcReferenceFlow(); double dcReferenceFlow = decomposedFlow.getDcReferenceFlow(); double allocatedFlow = decomposedFlow.getAllocatedFlow(); double xNodeFlow = decomposedFlow.getXNodeFlow(); @@ -41,20 +52,29 @@ static DecomposedFlow rescale(DecomposedFlow decomposedFlow) { double internalFlow = decomposedFlow.getInternalFlow(); Map loopFlows = decomposedFlow.getLoopFlows(); - if (Double.isNaN(acReferenceFlow)) { - return decomposedFlow; - } - - double deltaToRescale = acReferenceFlow * Math.signum(acReferenceFlow) - decomposedFlow.getTotalFlow(); - double sumOfReLUFlows = reLU(allocatedFlow) + reLU(pstFlow) + reLU(xNodeFlow) + loopFlows.values().stream().mapToDouble(DecomposedFlowsRescaler::reLU).sum() + reLU(internalFlow); + double deltaToRescale = acTerminal1ReferenceFlow * Math.signum(acTerminal1ReferenceFlow) - decomposedFlow.getTotalFlow(); + double sumOfReLUFlows = reLU(allocatedFlow) + reLU(pstFlow) + reLU(xNodeFlow) + loopFlows.values().stream().mapToDouble(DecomposedFlowRescalerAcerMethodology::reLU).sum() + reLU(internalFlow); double rescaledAllocatedFlow = rescaleValue(allocatedFlow, deltaToRescale, sumOfReLUFlows); double rescaledXNodeFlow = rescaleValue(xNodeFlow, deltaToRescale, sumOfReLUFlows); double rescaledPstFlow = rescaleValue(pstFlow, deltaToRescale, sumOfReLUFlows); double rescaleInternalFlow = rescaleValue(internalFlow, deltaToRescale, sumOfReLUFlows); Map rescaledLoopFlows = loopFlows.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> rescaleValue(entry.getValue(), deltaToRescale, sumOfReLUFlows))); + .collect(Collectors.toMap(Map.Entry::getKey, entry -> rescaleValue(entry.getValue(), deltaToRescale, sumOfReLUFlows))); - return new DecomposedFlow(branchId, contingencyId, country1, country2, acReferenceFlow, dcReferenceFlow, rescaledAllocatedFlow, rescaledXNodeFlow, rescaledPstFlow, rescaleInternalFlow, rescaledLoopFlows); + return new DecomposedFlowBuilder() + .withBranchId(branchId) + .withContingencyId(contingencyId) + .withCountry1(country1) + .withCountry2(country2) + .withAcTerminal1ReferenceFlow(acTerminal1ReferenceFlow) + .withAcTerminal2ReferenceFlow(acTerminal2ReferenceFlow) + .withDcReferenceFlow(dcReferenceFlow) + .withAllocatedFlow(rescaledAllocatedFlow) + .withXNodeFlow(rescaledXNodeFlow) + .withPstFlow(rescaledPstFlow) + .withInternalFlow(rescaleInternalFlow) + .withLoopFlowsMap(rescaledLoopFlows) + .build(); } } diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescalerNoOp.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescalerNoOp.java new file mode 100644 index 00000000..64e1bdca --- /dev/null +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescalerNoOp.java @@ -0,0 +1,27 @@ +/** + * 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.rescaler; + +import com.powsybl.flow_decomposition.DecomposedFlow; + +/** + * @author Sebastien Murgey {@literal } + * @author Hugo Schindler {@literal } + * @author Caio Luke {@literal } + */ +public class DecomposedFlowRescalerNoOp implements DecomposedFlowRescaler { + + public DecomposedFlowRescalerNoOp() { + // empty constructor + } + + @Override + public DecomposedFlow rescale(DecomposedFlow decomposedFlow) { + return decomposedFlow; + } +} diff --git a/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescalerProportional.java b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescalerProportional.java new file mode 100644 index 00000000..5aaac833 --- /dev/null +++ b/flow-decomposition/src/main/java/com/powsybl/flow_decomposition/rescaler/DecomposedFlowRescalerProportional.java @@ -0,0 +1,82 @@ +/** + * 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.rescaler; + +import com.powsybl.flow_decomposition.DecomposedFlow; +import com.powsybl.flow_decomposition.DecomposedFlowBuilder; +import com.powsybl.iidm.network.Country; + +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Caio Luke {@literal } + */ +public class DecomposedFlowRescalerProportional implements DecomposedFlowRescaler { + private final double minFlowTolerance; // min flow in MW to rescale + public static final double DEFAULT_MIN_FLOW_TOLERANCE = 1E-6; // default min tolerance is 1 W = 1E-6 MW + + public DecomposedFlowRescalerProportional(double minFlowTolerance) { + this.minFlowTolerance = minFlowTolerance; + } + + public DecomposedFlowRescalerProportional() { + this(DEFAULT_MIN_FLOW_TOLERANCE); + } + + @Override + public DecomposedFlow rescale(DecomposedFlow decomposedFlow) { + double acTerminal1ReferenceFlow = decomposedFlow.getAcTerminal1ReferenceFlow(); + double acTerminal2ReferenceFlow = decomposedFlow.getAcTerminal2ReferenceFlow(); + if (Double.isNaN(acTerminal1ReferenceFlow) || Double.isNaN(acTerminal2ReferenceFlow)) { + return decomposedFlow; + } + + // if dcReferenceFlow is too small, do not rescale + double dcReferenceFlow = decomposedFlow.getDcReferenceFlow(); + if (Math.abs(dcReferenceFlow) < minFlowTolerance) { + return decomposedFlow; + } + + String branchId = decomposedFlow.getBranchId(); + String contingencyId = decomposedFlow.getContingencyId(); + Country country1 = decomposedFlow.getCountry1(); + Country country2 = decomposedFlow.getCountry2(); + double allocatedFlow = decomposedFlow.getAllocatedFlow(); + double xNodeFlow = decomposedFlow.getXNodeFlow(); + double pstFlow = decomposedFlow.getPstFlow(); + double internalFlow = decomposedFlow.getInternalFlow(); + Map loopFlows = decomposedFlow.getLoopFlows(); + + // rescale proportionally to max (abs) ac flow + double acMaxAbsFlow = decomposedFlow.getMaxAbsAcFlow(); + double rescaleFactor = Math.abs(acMaxAbsFlow / dcReferenceFlow); + + double rescaledAllocatedFlow = rescaleFactor * allocatedFlow; + double rescaledXNodeFlow = rescaleFactor * xNodeFlow; + double rescaledPstFlow = rescaleFactor * pstFlow; + double rescaleInternalFlow = rescaleFactor * internalFlow; + Map rescaledLoopFlows = loopFlows.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> rescaleFactor * entry.getValue())); + + return new DecomposedFlowBuilder() + .withBranchId(branchId) + .withContingencyId(contingencyId) + .withCountry1(country1) + .withCountry2(country2) + .withAcTerminal1ReferenceFlow(acTerminal1ReferenceFlow) + .withAcTerminal2ReferenceFlow(acTerminal2ReferenceFlow) + .withDcReferenceFlow(dcReferenceFlow) + .withAllocatedFlow(rescaledAllocatedFlow) + .withXNodeFlow(rescaledXNodeFlow) + .withPstFlow(rescaledPstFlow) + .withInternalFlow(rescaleInternalFlow) + .withLoopFlowsMap(rescaledLoopFlows) + .build(); + } +} diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/AllocatedFlowTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/AllocatedFlowTests.java index 9dcb0b22..69b7634b 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/AllocatedFlowTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/AllocatedFlowTests.java @@ -45,7 +45,7 @@ void checkThatAllocatedFlowAreExtractedForEachXnecGivenABasicNetwork() { Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); assertEquals(100.0935, decomposedFlowMap.get(xnecFrBee).getAllocatedFlow(), EPSILON); - TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.DEFAULT_RESCALE_ENABLED, flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.DEFAULT_RESCALE_MODE, flowDecompositionResults); } @Test @@ -60,6 +60,6 @@ void checkThatAllocatedFlowAreExtractedForEachXnecGivenABasicNetworkWithInverted Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); assertEquals(100.0935, decomposedFlowMap.get(xnecFrBee).getAllocatedFlow(), EPSILON); - TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.DEFAULT_RESCALE_ENABLED, flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.DEFAULT_RESCALE_MODE, flowDecompositionResults); } } diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/CgmesIntegrationTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/CgmesIntegrationTests.java index dcb00100..016b96c9 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/CgmesIntegrationTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/CgmesIntegrationTests.java @@ -59,13 +59,13 @@ void checkFlowDecompositionWorksOnCgmesFile() { .setEnableLossesCompensation(FlowDecompositionParameters.ENABLE_LOSSES_COMPENSATION) .setLossesCompensationEpsilon(FlowDecompositionParameters.DISABLE_LOSSES_COMPENSATION_EPSILON) .setSensitivityEpsilon(FlowDecompositionParameters.DISABLE_SENSITIVITY_EPSILON) - .setRescaleEnabled(FlowDecompositionParameters.ENABLE_RESCALED_RESULTS); + .setRescaleMode(FlowDecompositionParameters.RescaleMode.ACER_METHODOLOGY); String xnecId = "044cd006-c766-11e1-8775-005056c00008"; XnecProvider xnecProvider = XnecProviderByIds.builder().addNetworkElementsOnBasecase(Set.of(xnecId)).build(); FlowDecompositionComputer flowDecompositionComputer = new FlowDecompositionComputer(flowDecompositionParameters); FlowDecompositionResults flowDecompositionResults = flowDecompositionComputer.run(xnecProvider, network); assertNotNull(flowDecompositionResults.getDecomposedFlowMap().get(xnecId)); assertEquals(1, flowDecompositionResults.getDecomposedFlowMap().size()); - TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.isRescaleEnabled(), flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.getRescaleMode(), flowDecompositionResults); } } diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionParametersTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionParametersTests.java index 9d176bd4..c166d390 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionParametersTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionParametersTests.java @@ -10,6 +10,7 @@ import com.google.common.jimfs.Jimfs; import com.powsybl.commons.config.InMemoryPlatformConfig; import com.powsybl.commons.config.MapModuleConfig; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerProportional; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -50,7 +51,8 @@ void checkDefaultParameters() { assertFalse(parameters.isLossesCompensationEnabled()); assertEquals(1e-5, parameters.getLossesCompensationEpsilon(), EPSILON); assertEquals(1e-5, parameters.getSensitivityEpsilon(), EPSILON); - assertFalse(parameters.isRescaleEnabled()); + assertEquals(FlowDecompositionParameters.RescaleMode.NONE, parameters.getRescaleMode()); + assertEquals(DecomposedFlowRescalerProportional.DEFAULT_MIN_FLOW_TOLERANCE, parameters.getProportionalRescalerMinFlowTolerance(), EPSILON / 100); assertTrue(parameters.isDcFallbackEnabledAfterAcDivergence()); assertEquals(15000, parameters.getSensitivityVariableBatchSize()); } @@ -61,7 +63,8 @@ void checkCompleteConfigurationOfParameters() { mapModuleConfig.setStringProperty("enable-losses-compensation", Boolean.toString(true)); mapModuleConfig.setStringProperty("losses-compensation-epsilon", Double.toString(2e-5)); mapModuleConfig.setStringProperty("sensitivity-epsilon", Double.toString(3e-3)); - mapModuleConfig.setStringProperty("rescale-enabled", Boolean.toString(true)); + mapModuleConfig.setStringProperty("rescale-mode", FlowDecompositionParameters.RescaleMode.ACER_METHODOLOGY.name()); + mapModuleConfig.setStringProperty("proportional-rescaler-min-flow-tolerance", Double.toString(1e-2)); mapModuleConfig.setStringProperty("dc-fallback-enabled-after-ac-divergence", Boolean.toString(false)); mapModuleConfig.setStringProperty("sensitivity-variable-batch-size", Integer.toString(1234)); @@ -69,7 +72,8 @@ void checkCompleteConfigurationOfParameters() { assertTrue(parameters.isLossesCompensationEnabled()); assertEquals(2e-5, parameters.getLossesCompensationEpsilon(), EPSILON); assertEquals(3e-3, parameters.getSensitivityEpsilon(), EPSILON); - assertTrue(parameters.isRescaleEnabled()); + assertEquals(FlowDecompositionParameters.RescaleMode.ACER_METHODOLOGY, parameters.getRescaleMode()); + assertEquals(1e-2, parameters.getProportionalRescalerMinFlowTolerance(), EPSILON); assertFalse(parameters.isDcFallbackEnabledAfterAcDivergence()); assertEquals(1234, parameters.getSensitivityVariableBatchSize()); } @@ -83,7 +87,8 @@ void checkIncompleteConfigurationOfParameters() { assertFalse(parameters.isLossesCompensationEnabled()); assertEquals(2e-5, parameters.getLossesCompensationEpsilon(), EPSILON); assertEquals(1e-5, parameters.getSensitivityEpsilon(), EPSILON); - assertFalse(parameters.isRescaleEnabled()); + assertEquals(FlowDecompositionParameters.RescaleMode.NONE, parameters.getRescaleMode()); + assertEquals(DecomposedFlowRescalerProportional.DEFAULT_MIN_FLOW_TOLERANCE, parameters.getProportionalRescalerMinFlowTolerance(), EPSILON / 100); assertTrue(parameters.isDcFallbackEnabledAfterAcDivergence()); } } diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionResultsTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionResultsTests.java index a46e136c..3a4226e3 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionResultsTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionResultsTests.java @@ -7,6 +7,8 @@ */ package com.powsybl.flow_decomposition; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescaler; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerNoOp; import com.powsybl.flow_decomposition.xnec_provider.XnecProviderByIds; import com.powsybl.iidm.network.Branch; import com.powsybl.iidm.network.Country; @@ -81,12 +83,14 @@ void testResultMetadata() { void testBuilderNState() { Set nStateXnecList = xnecProvider.getNetworkElements(network); FlowDecompositionResults.PerStateBuilder nStateBuilder = flowDecompositionResults.getBuilder(nStateXnecList); + DecomposedFlowRescaler decomposedFlowRescaler = new DecomposedFlowRescalerNoOp(); - nStateBuilder.saveAcReferenceFlow(Map.of(branchId, 10.0)); + nStateBuilder.saveAcTerminal1ReferenceFlow(Map.of(branchId, 10.0)); + nStateBuilder.saveAcTerminal2ReferenceFlow(Map.of(branchId, 10.0)); nStateBuilder.saveDcReferenceFlow(Map.of(branchId, 11.0)); nStateBuilder.saveAllocatedAndLoopFlowsMatrix(new SparseMatrixWithIndexesCSC(xnecMap, Map.of(ALLOCATED_COLUMN_NAME, 0, NetworkUtil.getLoopFlowIdFromCountry(FR), 1), alloMatrix)); nStateBuilder.savePstFlowMatrix(new SparseMatrixWithIndexesCSC(xnecMap, Map.of(PST_COLUMN_NAME, 0), pstMatrix)); - nStateBuilder.build(FlowDecompositionParameters.DISABLE_RESCALED_RESULTS); + nStateBuilder.build(decomposedFlowRescaler); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); assertEquals(1, decomposedFlowMap.size()); @@ -95,7 +99,8 @@ void testBuilderNState() { assertEquals(branchId, decomposedFlow.getBranchId()); assertEquals("", decomposedFlow.getContingencyId()); assertEquals(branchId, decomposedFlow.getId()); - assertEquals(10.0, decomposedFlow.getAcReferenceFlow()); + assertEquals(10.0, decomposedFlow.getAcTerminal1ReferenceFlow()); + assertEquals(10.0, decomposedFlow.getAcTerminal2ReferenceFlow()); assertEquals(11.0, decomposedFlow.getDcReferenceFlow()); assertEquals(20.0, decomposedFlow.getAllocatedFlow()); assertEquals(12.0, decomposedFlow.getLoopFlow(FR)); @@ -107,12 +112,14 @@ void testBuilderN1State() { Set n1StateContingency2XnecList = xnecProvider.getNetworkElements(contingencyId2, network); FlowDecompositionResults.PerStateBuilder n1StateBuilder = flowDecompositionResults.getBuilder(contingencyId2, n1StateContingency2XnecList); String xnecId = "DB000011 DF000011 1_DD000011 DF000011 1"; + DecomposedFlowRescaler decomposedFlowRescaler = new DecomposedFlowRescalerNoOp(); - n1StateBuilder.saveAcReferenceFlow(Map.of(branchId, 10.0)); + n1StateBuilder.saveAcTerminal1ReferenceFlow(Map.of(branchId, 10.0)); + n1StateBuilder.saveAcTerminal2ReferenceFlow(Map.of(branchId, 10.0)); n1StateBuilder.saveDcReferenceFlow(Map.of(branchId, 11.0)); n1StateBuilder.saveAllocatedAndLoopFlowsMatrix(new SparseMatrixWithIndexesCSC(xnecMap, Map.of(ALLOCATED_COLUMN_NAME, 0, NetworkUtil.getLoopFlowIdFromCountry(FR), 1), alloMatrix)); n1StateBuilder.savePstFlowMatrix(new SparseMatrixWithIndexesCSC(xnecMap, Map.of(PST_COLUMN_NAME, 0), pstMatrix)); - n1StateBuilder.build(FlowDecompositionParameters.DISABLE_RESCALED_RESULTS); + n1StateBuilder.build(decomposedFlowRescaler); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); assertEquals(1, decomposedFlowMap.size()); @@ -121,7 +128,8 @@ void testBuilderN1State() { assertEquals(branchId, decomposedFlow.getBranchId()); assertEquals(contingencyId2, decomposedFlow.getContingencyId()); assertEquals(xnecId, decomposedFlow.getId()); - assertEquals(10.0, decomposedFlow.getAcReferenceFlow()); + assertEquals(10.0, decomposedFlow.getAcTerminal1ReferenceFlow()); + assertEquals(10.0, decomposedFlow.getAcTerminal2ReferenceFlow()); assertEquals(11.0, decomposedFlow.getDcReferenceFlow()); assertEquals(20.0, decomposedFlow.getAllocatedFlow()); assertEquals(12.0, decomposedFlow.getLoopFlow(FR)); @@ -133,12 +141,14 @@ void testBuilderN2State() { Set n1StateContingency3XnecList = xnecProvider.getNetworkElements(contingencyId3, network); FlowDecompositionResults.PerStateBuilder n2StateBuilder = flowDecompositionResults.getBuilder(contingencyId3, n1StateContingency3XnecList); String xnecId = "DB000011 DF000011 1_FB000011 FD000011 1_FB000021 FD000021 1"; + DecomposedFlowRescaler decomposedFlowRescaler = new DecomposedFlowRescalerNoOp(); - n2StateBuilder.saveAcReferenceFlow(Map.of(branchId, 10.0)); + n2StateBuilder.saveAcTerminal1ReferenceFlow(Map.of(branchId, 10.0)); + n2StateBuilder.saveAcTerminal2ReferenceFlow(Map.of(branchId, 10.0)); n2StateBuilder.saveDcReferenceFlow(Map.of(branchId, 11.0)); n2StateBuilder.saveAllocatedAndLoopFlowsMatrix(new SparseMatrixWithIndexesCSC(xnecMap, Map.of(ALLOCATED_COLUMN_NAME, 0, NetworkUtil.getLoopFlowIdFromCountry(FR), 1), alloMatrix)); n2StateBuilder.savePstFlowMatrix(new SparseMatrixWithIndexesCSC(xnecMap, Map.of(PST_COLUMN_NAME, 0), pstMatrix)); - n2StateBuilder.build(FlowDecompositionParameters.DISABLE_RESCALED_RESULTS); + n2StateBuilder.build(decomposedFlowRescaler); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); assertEquals(1, decomposedFlowMap.size()); @@ -147,7 +157,8 @@ void testBuilderN2State() { assertEquals(branchId, decomposedFlow.getBranchId()); assertEquals(contingencyId3, decomposedFlow.getContingencyId()); assertEquals(xnecId, decomposedFlow.getId()); - assertEquals(10.0, decomposedFlow.getAcReferenceFlow()); + assertEquals(10.0, decomposedFlow.getAcTerminal1ReferenceFlow()); + assertEquals(10.0, decomposedFlow.getAcTerminal2ReferenceFlow()); assertEquals(11.0, decomposedFlow.getDcReferenceFlow()); assertEquals(20.0, decomposedFlow.getAllocatedFlow()); assertEquals(12.0, decomposedFlow.getLoopFlow(FR)); diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionWithContingencyTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionWithContingencyTests.java index 7716cbae..95c4d3af 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionWithContingencyTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/FlowDecompositionWithContingencyTests.java @@ -37,10 +37,10 @@ void testSingleN1PostContingencyState() { .build(); FlowDecompositionParameters flowDecompositionParameters = FlowDecompositionParameters.load() .setEnableLossesCompensation(FlowDecompositionParameters.ENABLE_LOSSES_COMPENSATION) - .setRescaleEnabled(FlowDecompositionParameters.DISABLE_RESCALED_RESULTS); + .setRescaleMode(FlowDecompositionParameters.RescaleMode.NONE); FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters); FlowDecompositionResults flowDecompositionResults = flowComputer.run(xnecProvider, network); - TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.isRescaleEnabled(), flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.getRescaleMode(), flowDecompositionResults); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); validateFlowDecompositionOnXnec(xnecId, branchId, contingencyId, decomposedFlowMap.get(xnecId), -1269.932, 31.943); @@ -63,10 +63,10 @@ void testNStateAndN1PostContingencyState() { .build(); FlowDecompositionParameters flowDecompositionParameters = FlowDecompositionParameters.load() .setEnableLossesCompensation(FlowDecompositionParameters.ENABLE_LOSSES_COMPENSATION) - .setRescaleEnabled(FlowDecompositionParameters.DISABLE_RESCALED_RESULTS); + .setRescaleMode(FlowDecompositionParameters.RescaleMode.NONE); FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters); FlowDecompositionResults flowDecompositionResults = flowComputer.run(xnecProvider, network); - TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.isRescaleEnabled(), flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.getRescaleMode(), flowDecompositionResults); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); validateFlowDecompositionOnXnec(xnecId1, branchId, contingencyId1, decomposedFlowMap.get(xnecId1), -300.420, 22.472); @@ -89,10 +89,10 @@ void testSingleN2PostContingencyState() { .build(); FlowDecompositionParameters flowDecompositionParameters = FlowDecompositionParameters.load() .setEnableLossesCompensation(FlowDecompositionParameters.ENABLE_LOSSES_COMPENSATION) - .setRescaleEnabled(FlowDecompositionParameters.DISABLE_RESCALED_RESULTS); + .setRescaleMode(FlowDecompositionParameters.RescaleMode.NONE); FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters); FlowDecompositionResults flowDecompositionResults = flowComputer.run(xnecProvider, network); - TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.isRescaleEnabled(), flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.getRescaleMode(), flowDecompositionResults); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); validateFlowDecompositionOnXnec(xnecId, branchId, contingencyId, decomposedFlowMap.get(xnecId), -406.204, 48.362); @@ -119,10 +119,10 @@ void testNStateN1AndN2PostContingencyState() { .build(); FlowDecompositionParameters flowDecompositionParameters = FlowDecompositionParameters.load() .setEnableLossesCompensation(FlowDecompositionParameters.ENABLE_LOSSES_COMPENSATION) - .setRescaleEnabled(FlowDecompositionParameters.DISABLE_RESCALED_RESULTS); + .setRescaleMode(FlowDecompositionParameters.RescaleMode.NONE); FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters); FlowDecompositionResults flowDecompositionResults = flowComputer.run(xnecProvider, network); - TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.isRescaleEnabled(), flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.getRescaleMode(), flowDecompositionResults); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); validateFlowDecompositionOnXnec(xnecId1, branchId, contingencyId1, decomposedFlowMap.get(xnecId1), -300.420, 22.472); diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/InternalFlowTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/InternalFlowTests.java index ea109903..0ecf0ceb 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/InternalFlowTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/InternalFlowTests.java @@ -6,6 +6,7 @@ */ package com.powsybl.flow_decomposition; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerAcerMethodology; import com.powsybl.flow_decomposition.xnec_provider.XnecProviderAllBranches; import com.powsybl.iidm.network.Country; import com.powsybl.iidm.network.Network; @@ -90,14 +91,27 @@ private DecomposedFlow getDecomposedFlow(double internalFlow, double acReference loopFlows.put(NetworkUtil.getLoopFlowIdFromCountry(Country.ES), 700.); Country country1 = Country.FR; Country country2 = Country.FR; - return new DecomposedFlow("", "", country1, country2, acReferenceFlow, dcReferenceFlow, allocatedFlow, 0, pstFlow, internalFlow, loopFlows); + return new DecomposedFlowBuilder() + .withBranchId("") + .withContingencyId("") + .withCountry1(country1) + .withCountry2(country2) + .withAcTerminal1ReferenceFlow(acReferenceFlow) + .withAcTerminal2ReferenceFlow(acReferenceFlow) + .withDcReferenceFlow(dcReferenceFlow) + .withAllocatedFlow(allocatedFlow) + .withXNodeFlow(0) + .withPstFlow(pstFlow) + .withInternalFlow(internalFlow) + .withLoopFlowsMap(loopFlows) + .build(); } private DecomposedFlow getRescaledFlow(double internalFlow, double acReferenceFlow, double dcReferenceFlow) { DecomposedFlow decomposedFlow = getDecomposedFlow(internalFlow, acReferenceFlow, dcReferenceFlow); assertEquals(Math.abs(dcReferenceFlow), decomposedFlow.getTotalFlow(), EPSILON); - return DecomposedFlowsRescaler.rescale(decomposedFlow); + return new DecomposedFlowRescalerAcerMethodology().rescale(decomposedFlow); } private void checkRescaleAcReference(double acReferenceFlow, double dcReferenceFlow, DecomposedFlow rescaledFlow, double expectedAllocatedFlow, double expectedInternalFlow, double expectedPstFlow, double expectedLoopFlowBE, double expectedLoopFlowES) { @@ -109,7 +123,7 @@ private void checkRescaleAcReference(double acReferenceFlow, double dcReferenceF assertEquals(expectedLoopFlowGE, rescaledFlow.getLoopFlow(Country.GE), EPSILON); assertEquals(expectedLoopFlowES, rescaledFlow.getLoopFlow(Country.ES), EPSILON); assertEquals(expectedInternalFlow, rescaledFlow.getInternalFlow(), EPSILON); - assertEquals(acReferenceFlow, rescaledFlow.getAcReferenceFlow(), EPSILON); + assertEquals(acReferenceFlow, rescaledFlow.getAcTerminal1ReferenceFlow(), EPSILON); assertEquals(dcReferenceFlow, rescaledFlow.getDcReferenceFlow(), EPSILON); } } diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/LoopFlowTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/LoopFlowTests.java index cde60489..5e3ee61d 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/LoopFlowTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/LoopFlowTests.java @@ -35,7 +35,7 @@ void checkThatLoopFlowsAreExtractedForEachXnecAndForEachCountryGivenABasicNetwor XnecProvider xnecProvider = XnecProviderByIds.builder().addNetworkElementsOnBasecase(Set.of(x1, x2, x4, x5)).build(); FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(); FlowDecompositionResults flowDecompositionResults = flowComputer.run(xnecProvider, network); - TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.DEFAULT_RESCALE_ENABLED, flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.RescaleMode.NONE, flowDecompositionResults); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); assertEquals(0, decomposedFlowMap.get(x1).getAllocatedFlow(), EPSILON); @@ -55,10 +55,10 @@ void checkThatLoopFlowsAreExtractedForEachXnecAndForEachCountryGivenABasicNetwor assertEquals(100, decomposedFlowMap.get(x5).getLoopFlow(Country.ES), EPSILON); assertEquals(0, decomposedFlowMap.get(x5).getLoopFlow(Country.FR), EPSILON); - assertTrue(Double.isNaN(decomposedFlowMap.get(x1).getAcReferenceFlow())); - assertTrue(Double.isNaN(decomposedFlowMap.get(x2).getAcReferenceFlow())); - assertTrue(Double.isNaN(decomposedFlowMap.get(x4).getAcReferenceFlow())); - assertTrue(Double.isNaN(decomposedFlowMap.get(x5).getAcReferenceFlow())); + assertTrue(Double.isNaN(decomposedFlowMap.get(x1).getAcTerminal1ReferenceFlow())); + assertTrue(Double.isNaN(decomposedFlowMap.get(x2).getAcTerminal1ReferenceFlow())); + assertTrue(Double.isNaN(decomposedFlowMap.get(x4).getAcTerminal1ReferenceFlow())); + assertTrue(Double.isNaN(decomposedFlowMap.get(x5).getAcTerminal1ReferenceFlow())); assertEquals(100, decomposedFlowMap.get(x1).getDcReferenceFlow(), EPSILON); assertEquals(200, decomposedFlowMap.get(x2).getDcReferenceFlow(), EPSILON); diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/PstFlowTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/PstFlowTests.java index 5fce73d5..25bc5eb4 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/PstFlowTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/PstFlowTests.java @@ -32,7 +32,7 @@ void checkThatPSTFlowsAreExtractedForEachXnecAndForEachPSTGivenABasicNetworkWith FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(); XnecProvider xnecProvider = XnecProviderByIds.builder().addNetworkElementsOnBasecase(Set.of(x1, x2)).build(); FlowDecompositionResults flowDecompositionResults = flowComputer.run(xnecProvider, network); - TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.DEFAULT_RESCALE_ENABLED, flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.RescaleMode.NONE, flowDecompositionResults); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); assertEquals(0, decomposedFlowMap.get(x1).getPstFlow(), EPSILON); @@ -49,7 +49,7 @@ void checkThatPSTFlowsAreExtractedForEachXnecAndForEachPSTGivenABasicNetworkWith FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(); XnecProvider xnecProvider = XnecProviderByIds.builder().addNetworkElementsOnBasecase(Set.of(x1, x2)).build(); FlowDecompositionResults flowDecompositionResults = flowComputer.run(xnecProvider, network); - TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.DEFAULT_RESCALE_ENABLED, flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(FlowDecompositionParameters.RescaleMode.NONE, flowDecompositionResults); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); assertEquals(163.652702605, decomposedFlowMap.get(x1).getPstFlow(), EPSILON); diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/RescalingTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/RescalingTests.java index 92bf9714..f70899d6 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/RescalingTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/RescalingTests.java @@ -6,6 +6,9 @@ */ package com.powsybl.flow_decomposition; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerAcerMethodology; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerNoOp; +import com.powsybl.flow_decomposition.rescaler.DecomposedFlowRescalerProportional; import com.powsybl.flow_decomposition.xnec_provider.XnecProviderAllBranches; import com.powsybl.flow_decomposition.xnec_provider.XnecProviderByIds; import com.powsybl.iidm.network.Country; @@ -16,13 +19,12 @@ import java.util.Set; import java.util.TreeMap; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; /** * @author Sebastien Murgey {@literal } * @author Hugo Schindler {@literal } + * @author Caio Luke {@literal } */ class RescalingTests { private static final double EPSILON = 1e-5; @@ -37,7 +39,7 @@ private void checkRescaleAcReference(double acReferenceFlow, double dcReferenceF assertEquals(expectedLoopFlowGE, rescaledFlow.getLoopFlow(Country.GE), EPSILON); assertEquals(expectedLoopFlowES, rescaledFlow.getLoopFlow(Country.ES), EPSILON); assertEquals(expectedInternalFlow, rescaledFlow.getInternalFlow(), EPSILON); - assertEquals(acReferenceFlow, rescaledFlow.getAcReferenceFlow(), EPSILON); + assertEquals(acReferenceFlow, rescaledFlow.getAcTerminal1ReferenceFlow(), EPSILON); assertEquals(dcReferenceFlow, rescaledFlow.getDcReferenceFlow(), EPSILON); } @@ -59,14 +61,27 @@ private DecomposedFlow getDecomposedFlow(double acReferenceFlow, double dcRefere loopFlows.put(NetworkUtil.getLoopFlowIdFromCountry(Country.ES), 700.); Country country1 = Country.FR; Country country2 = Country.FR; - return new DecomposedFlow("", "", country1, country2, acReferenceFlow, dcReferenceFlow, allocatedFlow, 0, pstFlow, internalFlow, loopFlows); + return new DecomposedFlowBuilder() + .withBranchId("") + .withContingencyId("") + .withCountry1(country1) + .withCountry2(country2) + .withAcTerminal1ReferenceFlow(acReferenceFlow) + .withAcTerminal2ReferenceFlow(acReferenceFlow) + .withDcReferenceFlow(dcReferenceFlow) + .withAllocatedFlow(allocatedFlow) + .withXNodeFlow(0) + .withPstFlow(pstFlow) + .withInternalFlow(internalFlow) + .withLoopFlowsMap(loopFlows) + .build(); } private DecomposedFlow getRescaledFlow(double acReferenceFlow, double dcReferenceFlow) { DecomposedFlow decomposedFlow = getDecomposedFlow(acReferenceFlow, dcReferenceFlow); assertEquals(Math.abs(dcReferenceFlow), decomposedFlow.getTotalFlow(), EPSILON); - return DecomposedFlowsRescaler.rescale(decomposedFlow); + return new DecomposedFlowRescalerAcerMethodology().rescale(decomposedFlow); } @Test @@ -134,31 +149,37 @@ void testAcerNormalizationWithPositiveAbsoluteSmallerReferenceFlows() { } @Test - void testNormalizationWithFlowDecompositionResultsWithPstNetwork() { + void testReLUNormalizationWithFlowDecompositionResultsWithPstNetwork() { String networkFileName = "NETWORK_PST_FLOW_WITH_COUNTRIES.uct"; - testNormalizationWithFlowDecompositionResults(networkFileName, FlowDecompositionParameters.ENABLE_RESCALED_RESULTS); + testNormalizationWithFlowDecompositionResults(networkFileName, FlowDecompositionParameters.RescaleMode.ACER_METHODOLOGY); } @Test void testNoNormalizationWithFlowDecompositionResultsWithPstNetwork() { String networkFileName = "NETWORK_PST_FLOW_WITH_COUNTRIES.uct"; - testNormalizationWithFlowDecompositionResults(networkFileName, FlowDecompositionParameters.DISABLE_RESCALED_RESULTS); + testNormalizationWithFlowDecompositionResults(networkFileName, FlowDecompositionParameters.RescaleMode.NONE); } - static void testNormalizationWithFlowDecompositionResults(String networkFileName, boolean enableRescaledResults) { + @Test + void testProportionalNormalizationWithFlowDecompositionResultsWithPstNetwork() { + String networkFileName = "NETWORK_PST_FLOW_WITH_COUNTRIES.uct"; + testNormalizationWithFlowDecompositionResults(networkFileName, FlowDecompositionParameters.RescaleMode.PROPORTIONAL); + } + + static void testNormalizationWithFlowDecompositionResults(String networkFileName, FlowDecompositionParameters.RescaleMode rescaleMode) { Network network = TestUtils.importNetwork(networkFileName); FlowDecompositionParameters flowDecompositionParameters = new FlowDecompositionParameters() .setEnableLossesCompensation(FlowDecompositionParameters.ENABLE_LOSSES_COMPENSATION) .setLossesCompensationEpsilon(FlowDecompositionParameters.DISABLE_LOSSES_COMPENSATION_EPSILON) .setSensitivityEpsilon(FlowDecompositionParameters.DISABLE_SENSITIVITY_EPSILON) - .setRescaleEnabled(enableRescaledResults); + .setRescaleMode(rescaleMode); FlowDecompositionComputer flowDecompositionComputer = new FlowDecompositionComputer(flowDecompositionParameters); XnecProvider xnecProvider = new XnecProviderAllBranches(); FlowDecompositionResults flowDecompositionResults = flowDecompositionComputer.run(xnecProvider, network); - TestUtils.assertCoherenceTotalFlow(enableRescaledResults, flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(rescaleMode, flowDecompositionResults); } @Test @@ -171,14 +192,54 @@ void testRescalingDoesNotOccurWhenAcDiverge() { .setEnableLossesCompensation(FlowDecompositionParameters.ENABLE_LOSSES_COMPENSATION) .setLossesCompensationEpsilon(FlowDecompositionParameters.DISABLE_LOSSES_COMPENSATION_EPSILON) .setSensitivityEpsilon(FlowDecompositionParameters.DISABLE_SENSITIVITY_EPSILON) - .setRescaleEnabled(FlowDecompositionParameters.ENABLE_RESCALED_RESULTS); + .setRescaleMode(FlowDecompositionParameters.RescaleMode.ACER_METHODOLOGY); FlowDecompositionComputer flowDecompositionComputer = new FlowDecompositionComputer(flowDecompositionParameters); XnecProvider xnecProvider = XnecProviderByIds.builder().addNetworkElementsOnBasecase(Set.of(xnecId)).build(); FlowDecompositionResults flowDecompositionResults = flowDecompositionComputer.run(xnecProvider, network); - assertTrue(Double.isNaN(flowDecompositionResults.getDecomposedFlowMap().get(xnecId).getAcReferenceFlow())); + assertTrue(Double.isNaN(flowDecompositionResults.getDecomposedFlowMap().get(xnecId).getAcTerminal1ReferenceFlow())); assertFalse(Double.isNaN(flowDecompositionResults.getDecomposedFlowMap().get(xnecId).getAllocatedFlow())); } + + @Test + void testRescalingNoOpDoesNotRescale() { + double acReferenceFlow = 1.0; + double dcReferenceFlow = 0.9; + DecomposedFlow decomposedFlow = getDecomposedFlow(acReferenceFlow, dcReferenceFlow); + DecomposedFlow decomposedFlowRescaled = new DecomposedFlowRescalerNoOp().rescale(decomposedFlow); + // check that same object is returned by rescaler + assertSame(decomposedFlow, decomposedFlowRescaled); + } + + @Test + void testRescalingAcerMethodologyDoesNotRescaleNaN() { + double acReferenceFlow = Double.NaN; + double dcReferenceFlow = 0.9; + DecomposedFlow decomposedFlow = getDecomposedFlow(acReferenceFlow, dcReferenceFlow); + DecomposedFlow decomposedFlowRescaled = new DecomposedFlowRescalerAcerMethodology().rescale(decomposedFlow); + // check that same object is returned by rescaler + assertSame(decomposedFlow, decomposedFlowRescaled); + } + + @Test + void testRescalingProportionalDoesNotRescaleNaN() { + double acReferenceFlow = Double.NaN; + double dcReferenceFlow = 0.9; + DecomposedFlow decomposedFlow = getDecomposedFlow(acReferenceFlow, dcReferenceFlow); + DecomposedFlow decomposedFlowRescaled = new DecomposedFlowRescalerProportional().rescale(decomposedFlow); + // check that same object is returned by rescaler + assertSame(decomposedFlow, decomposedFlowRescaled); + } + + @Test + void testRescalingProportionalDoesNotRescaleWithSmallFlow() { + double acReferenceFlow = 1.0; + double dcReferenceFlow = 0.001; + DecomposedFlow decomposedFlow = getDecomposedFlow(acReferenceFlow, dcReferenceFlow); + DecomposedFlow decomposedFlowRescaled = new DecomposedFlowRescalerProportional(0.5).rescale(decomposedFlow); + // check that same object is returned by rescaler + assertSame(decomposedFlow, decomposedFlowRescaled); + } } 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 acb00c3f..d977eaef 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 @@ -37,13 +37,13 @@ public static GlskDocument importGlskDocument(String glskFileName) { return GlskDocumentImporters.importGlsk(TestUtils.class.getResourceAsStream(glskName)); } - public static void assertCoherenceTotalFlow(boolean enableRescaledResults, FlowDecompositionResults flowDecompositionResults) { + public static void assertCoherenceTotalFlow(FlowDecompositionParameters.RescaleMode rescaleMode, FlowDecompositionResults flowDecompositionResults) { for (String xnec : flowDecompositionResults.getDecomposedFlowMap().keySet()) { DecomposedFlow decomposedFlow = flowDecompositionResults.getDecomposedFlowMap().get(xnec); - if (enableRescaledResults) { - assertEquals(Math.abs(decomposedFlow.getAcReferenceFlow()), Math.abs(decomposedFlow.getTotalFlow()), EPSILON); - } else { - assertEquals(Math.abs(decomposedFlow.getDcReferenceFlow()), Math.abs(decomposedFlow.getTotalFlow()), EPSILON); + 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); } } } diff --git a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/XnodeFlowTests.java b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/XnodeFlowTests.java index b91d4161..8293a0d8 100644 --- a/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/XnodeFlowTests.java +++ b/flow-decomposition/src/test/java/com/powsybl/flow_decomposition/XnodeFlowTests.java @@ -38,10 +38,10 @@ void testXnodeFlowDecomposition() { .build(); FlowDecompositionParameters flowDecompositionParameters = FlowDecompositionParameters.load() .setEnableLossesCompensation(FlowDecompositionParameters.DISABLE_LOSSES_COMPENSATION) - .setRescaleEnabled(FlowDecompositionParameters.DISABLE_RESCALED_RESULTS); + .setRescaleMode(FlowDecompositionParameters.RescaleMode.NONE); FlowDecompositionComputer flowComputer = new FlowDecompositionComputer(flowDecompositionParameters); FlowDecompositionResults flowDecompositionResults = flowComputer.run(xnecProvider, network); - TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.isRescaleEnabled(), flowDecompositionResults); + TestUtils.assertCoherenceTotalFlow(flowDecompositionParameters.getRescaleMode(), flowDecompositionResults); Map decomposedFlowMap = flowDecompositionResults.getDecomposedFlowMap(); assertEquals(44.109, decomposedFlowMap.get(branchId1).getXNodeFlow(), EPSILON);