diff --git a/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowEngine.java b/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowEngine.java index 1247f27ec9..240c2893b2 100644 --- a/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowEngine.java +++ b/src/main/java/com/powsybl/openloadflow/dc/DcLoadFlowEngine.java @@ -10,10 +10,7 @@ import com.powsybl.math.matrix.MatrixFactory; import com.powsybl.openloadflow.dc.equations.DcEquationSystem; import com.powsybl.openloadflow.dc.equations.DcEquationSystemCreationParameters; -import com.powsybl.openloadflow.equations.EquationSystem; -import com.powsybl.openloadflow.equations.JacobianMatrix; -import com.powsybl.openloadflow.equations.UniformValueVoltageInitializer; -import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.equations.*; import com.powsybl.openloadflow.network.FirstSlackBusSelector; import com.powsybl.openloadflow.network.LfBus; import com.powsybl.openloadflow.network.LfNetwork; @@ -22,10 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; +import java.util.*; /** * @author Geoffroy Jamgotchian @@ -38,6 +32,8 @@ public class DcLoadFlowEngine { private final DcLoadFlowParameters parameters; + private double[] targetVector; + public DcLoadFlowEngine(LfNetwork network, MatrixFactory matrixFactory) { this.networks = Collections.singletonList(network); parameters = new DcLoadFlowParameters(new FirstSlackBusSelector(), matrixFactory); @@ -53,52 +49,88 @@ public DcLoadFlowEngine(List networks, DcLoadFlowParameters parameter this.parameters = Objects.requireNonNull(parameters); } - private void distributeSlack(LfNetwork network) { - double mismatch = network.getActivePowerMismatch(); + private void distributeSlack(Collection buses) { + double mismatch = getActivePowerMismatch(buses); ActivePowerDistribution activePowerDistribution = ActivePowerDistribution.create(parameters.getBalanceType(), false); - activePowerDistribution.run(network, mismatch); + activePowerDistribution.run(buses, mismatch); + } + + private static double getActivePowerMismatch(Collection buses) { + double mismatch = 0; + for (LfBus b : buses) { + mismatch += b.getGenerationTargetP() - b.getLoadTargetP(); + } + return -mismatch; } public DcLoadFlowResult run() { // only process main (largest) connected component LfNetwork network = networks.get(0); - if (parameters.isDistributedSlack()) { - distributeSlack(network); - } - DcEquationSystemCreationParameters creationParameters = new DcEquationSystemCreationParameters(parameters.isUpdateFlows(), false, parameters.isForcePhaseControlOffAndAddAngle1Var(), parameters.isUseTransformerRatio()); EquationSystem equationSystem = DcEquationSystem.create(network, new VariableSet(), creationParameters); + LoadFlowResult.ComponentResult.Status status = LoadFlowResult.ComponentResult.Status.FAILED; + try (JacobianMatrix j = new JacobianMatrix(equationSystem, parameters.getMatrixFactory())) { + + status = run(equationSystem, j, Collections.emptyList()); + } catch (Exception e) { + LOGGER.error("Failed to solve linear system for DC load flow", e); + } + + return new DcLoadFlowResult(network, getActivePowerMismatch(network.getBuses()), status); + } + + public LoadFlowResult.ComponentResult.Status run(EquationSystem equationSystem, JacobianMatrix j, Collection disabledBuses) { + double[] x = equationSystem.createStateVector(new UniformValueVoltageInitializer()); - equationSystem.updateEquations(x); + // only process main (largest) connected component + LfNetwork network = networks.get(0); - double[] targets = equationSystem.createTargetVector(); + Collection remainingBuses = new HashSet<>(network.getBuses()); + remainingBuses.removeAll(disabledBuses); - try (JacobianMatrix j = new JacobianMatrix(equationSystem, parameters.getMatrixFactory())) { - double[] dx = Arrays.copyOf(targets, targets.length); + if (parameters.isDistributedSlack()) { + distributeSlack(remainingBuses); + } - LoadFlowResult.ComponentResult.Status status; - try { - j.solveTransposed(dx); - status = LoadFlowResult.ComponentResult.Status.CONVERGED; - } catch (Exception e) { - status = LoadFlowResult.ComponentResult.Status.FAILED; - LOGGER.error("Failed to solve linear system for DC load flow", e); - } + equationSystem.updateEquations(x); - equationSystem.updateEquations(dx); - equationSystem.updateNetwork(dx); + this.targetVector = equationSystem.createTargetVector(); - // set all calculated voltages to NaN - for (LfBus bus : network.getBuses()) { - bus.setV(Double.NaN); - } + if (!disabledBuses.isEmpty()) { + // set buses injections and transformers to 0 + disabledBuses.stream() + .map(lfBus -> equationSystem.getEquation(lfBus.getNum(), EquationType.BUS_P)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(Equation::getColumn) + .forEach(column -> targetVector[column] = 0); + } + + LoadFlowResult.ComponentResult.Status status; + try { + j.solveTransposed(targetVector); + status = LoadFlowResult.ComponentResult.Status.CONVERGED; + } catch (Exception e) { + status = LoadFlowResult.ComponentResult.Status.FAILED; + LOGGER.error("Failed to solve linear system for DC load flow", e); + } - LOGGER.info("Dc loadflow complete (status={})", status); + equationSystem.updateEquations(targetVector); + equationSystem.updateNetwork(targetVector); - return new DcLoadFlowResult(network, network.getActivePowerMismatch(), status); + // set all calculated voltages to NaN + for (LfBus bus : network.getBuses()) { + bus.setV(Double.NaN); } + + LOGGER.info("Dc loadflow complete (status={})", status); + return status; + } + + public double[] getTargetVector() { + return targetVector; } } diff --git a/src/main/java/com/powsybl/openloadflow/graph/EvenShiloachGraphDecrementalConnectivity.java b/src/main/java/com/powsybl/openloadflow/graph/EvenShiloachGraphDecrementalConnectivity.java index d94a037c30..3b2ed1abd7 100644 --- a/src/main/java/com/powsybl/openloadflow/graph/EvenShiloachGraphDecrementalConnectivity.java +++ b/src/main/java/com/powsybl/openloadflow/graph/EvenShiloachGraphDecrementalConnectivity.java @@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory; import java.util.*; +import java.util.stream.Collectors; /** * Implementing the Even-Shiloach algorithm (see https://dl.acm.org/doi/10.1145/322234.322235) @@ -131,6 +132,7 @@ public void reset() { @Override public int getComponentNumber(V vertex) { + checkVertex(vertex); lazyComputeConnectivity(); updateVertexMapCache(); return vertexToConnectedComponent.get(vertex); @@ -142,6 +144,31 @@ public List> getSmallComponents() { return newConnectedComponents; } + @Override + public Set getConnectedComponent(V vertex) { + checkVertex(vertex); + lazyComputeConnectivity(); + updateVertexMapCache(); + int cn = vertexToConnectedComponent.get(vertex); + return cn == 0 ? getMainConnectedComponent() : newConnectedComponents.get(cn - 1); + } + + private Set getMainConnectedComponent() { + return vertices.stream().filter(v -> newConnectedComponents.stream().noneMatch(cc -> cc.contains(v))).collect(Collectors.toSet()); + } + + @Override + public Set getNonConnectedVertices(V vertex) { + checkVertex(vertex); + lazyComputeConnectivity(); + List> nonConnectedComponents = new ArrayList<>(newConnectedComponents); + newConnectedComponents.stream().filter(c -> c.contains(vertex)).findFirst().ifPresent(c -> { + nonConnectedComponents.remove(c); + nonConnectedComponents.add(getMainConnectedComponent()); + }); + return nonConnectedComponents.stream().flatMap(Collection::stream).collect(Collectors.toSet()); + } + private void lazyComputeConnectivity() { if (init && unprocessedCutEdges.isEmpty()) { return; @@ -218,6 +245,12 @@ private void updateVertexMapCache() { } } + private void checkVertex(V vertex) { + if (!graph.containsVertex(vertex)) { + throw new AssertionError("given vertex " + vertex + " is not in the graph"); + } + } + private interface GraphProcess { void next(); diff --git a/src/main/java/com/powsybl/openloadflow/graph/GraphDecrementalConnectivity.java b/src/main/java/com/powsybl/openloadflow/graph/GraphDecrementalConnectivity.java index 071004e9dc..632390b4a2 100644 --- a/src/main/java/com/powsybl/openloadflow/graph/GraphDecrementalConnectivity.java +++ b/src/main/java/com/powsybl/openloadflow/graph/GraphDecrementalConnectivity.java @@ -44,4 +44,8 @@ public interface GraphDecrementalConnectivity { * @return the collection of small connected components */ Collection> getSmallComponents(); + + Set getConnectedComponent(V vertex); + + Set getNonConnectedVertices(V vertex); } diff --git a/src/main/java/com/powsybl/openloadflow/graph/MinimumSpanningTreeGraphDecrementalConnectivity.java b/src/main/java/com/powsybl/openloadflow/graph/MinimumSpanningTreeGraphDecrementalConnectivity.java index b34f191ce9..229bdd68f2 100644 --- a/src/main/java/com/powsybl/openloadflow/graph/MinimumSpanningTreeGraphDecrementalConnectivity.java +++ b/src/main/java/com/powsybl/openloadflow/graph/MinimumSpanningTreeGraphDecrementalConnectivity.java @@ -91,18 +91,43 @@ private void invalidateMst() { @Override public int getComponentNumber(V vertex) { - if (this.mst == null) { - this.mst = new KruskalMinimumSpanningTrees().getSpanningTree(); - } - if (this.parentMap == null) { - this.parentMap = mst.forest.getParentMap(); - this.sortedRoots = mst.forest.getSortedRoots(); - } + checkVertex(vertex); + lazyCompute(); return sortedRoots.indexOf(parentMap.get(vertex)); } @Override public List> getSmallComponents() { + List> components = getConnectedComponents(); + return components.subList(1, components.size()); + } + + private List> getConnectedComponents() { + lazyCompute(); + List> components = new ArrayList<>(); + for (V root : sortedRoots) { + Set set = parentMap.entrySet().stream() + .filter(e -> e.getValue() == root) + .map(Map.Entry::getKey).collect(Collectors.toSet()); + components.add(set); + } + return components; + } + + @Override + public Set getConnectedComponent(V vertex) { + checkVertex(vertex); + return getConnectedComponents().get(getComponentNumber(vertex)); + } + + @Override + public Set getNonConnectedVertices(V vertex) { + checkVertex(vertex); + return getConnectedComponents().stream().filter(component -> !component.contains(vertex)) + .flatMap(Collection::stream).collect(Collectors.toSet()); + } + + private void lazyCompute() { if (this.mst == null) { this.mst = new KruskalMinimumSpanningTrees().getSpanningTree(); } @@ -110,14 +135,12 @@ public List> getSmallComponents() { this.parentMap = mst.forest.getParentMap(); this.sortedRoots = mst.forest.getSortedRoots(); } - List> components = new ArrayList<>(); - for (V root : sortedRoots) { - Set set = parentMap.entrySet().stream() - .filter(e -> e.getValue() == root) - .map(Map.Entry::getKey).collect(Collectors.toSet()); - components.add(set); + } + + private void checkVertex(V vertex) { + if (!graph.containsVertex(vertex)) { + throw new AssertionError("given vertex " + vertex + " is not in the graph"); } - return components.subList(1, components.size()); } class KruskalMinimumSpanningTrees implements SpanningTreeAlgorithm { diff --git a/src/main/java/com/powsybl/openloadflow/graph/NaiveGraphDecrementalConnectivity.java b/src/main/java/com/powsybl/openloadflow/graph/NaiveGraphDecrementalConnectivity.java index 6fe86f2f8c..964daedf58 100644 --- a/src/main/java/com/powsybl/openloadflow/graph/NaiveGraphDecrementalConnectivity.java +++ b/src/main/java/com/powsybl/openloadflow/graph/NaiveGraphDecrementalConnectivity.java @@ -92,6 +92,7 @@ public void reset() { @Override public int getComponentNumber(V vertex) { + checkVertex(vertex); updateComponents(); return components[numGetter.applyAsInt(vertex)]; } @@ -101,4 +102,25 @@ public Collection> getSmallComponents() { updateComponents(); return componentSets.subList(1, componentSets.size()); } + + @Override + public Set getConnectedComponent(V vertex) { + checkVertex(vertex); + updateComponents(); + return componentSets.get(components[numGetter.applyAsInt(vertex)]); + } + + @Override + public Set getNonConnectedVertices(V vertex) { + checkVertex(vertex); + updateComponents(); + return componentSets.stream().filter(component -> !component.contains(vertex)) + .flatMap(Collection::stream).collect(Collectors.toSet()); + } + + private void checkVertex(V vertex) { + if (!graph.containsVertex(vertex)) { + throw new AssertionError("given vertex " + vertex + " is not in the graph"); + } + } } diff --git a/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java b/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java index 836cacdb86..f8eae5d609 100644 --- a/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java +++ b/src/main/java/com/powsybl/openloadflow/network/LfNetwork.java @@ -369,25 +369,6 @@ public void logBalance() { num, activeGeneration, activeLoad, reactiveGeneration, reactiveLoad); } - public double getActivePowerMismatch() { - double mismatch = 0; - for (LfBus b : busesById.values()) { - mismatch += b.getGenerationTargetP() - b.getLoadTargetP(); - } - return -mismatch; - } - - public double getActivePowerMismatchInMainComponent(GraphDecrementalConnectivity connectivity) { - double mismatch = 0; - int mainComponent = connectivity.getComponentNumber(getSlackBus()); - for (LfBus b : busesById.values()) { - if (connectivity.getComponentNumber(b) == mainComponent) { - mismatch += b.getGenerationTargetP() - b.getLoadTargetP(); - } - } - return -mismatch; - } - private static void fix(LfNetwork network, boolean minImpedance) { if (minImpedance) { for (LfBranch branch : network.getBranches()) { diff --git a/src/main/java/com/powsybl/openloadflow/network/util/ActivePowerDistribution.java b/src/main/java/com/powsybl/openloadflow/network/util/ActivePowerDistribution.java index bdd4ebaf5e..90ea85e3e3 100644 --- a/src/main/java/com/powsybl/openloadflow/network/util/ActivePowerDistribution.java +++ b/src/main/java/com/powsybl/openloadflow/network/util/ActivePowerDistribution.java @@ -7,8 +7,10 @@ package com.powsybl.openloadflow.network.util; import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.openloadflow.network.LfBus; import com.powsybl.openloadflow.network.LfNetwork; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -26,7 +28,7 @@ public interface Step { String getElementType(); - List getParticipatingElements(LfNetwork network); + List getParticipatingElements(Collection buses); double run(List participatingElements, int iteration, double remainingMismatch); } @@ -62,7 +64,11 @@ public String getElementType() { } public Result run(LfNetwork network, double activePowerMismatch) { - List participatingElements = step.getParticipatingElements(network); + return run(network.getBuses(), activePowerMismatch); + } + + public Result run(Collection buses, double activePowerMismatch) { + List participatingElements = step.getParticipatingElements(buses); int iteration = 0; double remainingMismatch = activePowerMismatch; diff --git a/src/main/java/com/powsybl/openloadflow/network/util/GenerationActionPowerDistributionStep.java b/src/main/java/com/powsybl/openloadflow/network/util/GenerationActionPowerDistributionStep.java index 4b18121a5a..b828554ad9 100644 --- a/src/main/java/com/powsybl/openloadflow/network/util/GenerationActionPowerDistributionStep.java +++ b/src/main/java/com/powsybl/openloadflow/network/util/GenerationActionPowerDistributionStep.java @@ -6,12 +6,13 @@ */ package com.powsybl.openloadflow.network.util; +import com.powsybl.openloadflow.network.LfBus; import com.powsybl.openloadflow.network.LfGenerator; -import com.powsybl.openloadflow.network.LfNetwork; import com.powsybl.openloadflow.network.PerUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; @@ -29,9 +30,8 @@ public String getElementType() { } @Override - public List getParticipatingElements(LfNetwork network) { - return network.getBuses() - .stream() + public List getParticipatingElements(Collection buses) { + return buses.stream() .filter(bus -> !(bus.isDisabled() || bus.isFictitious())) .flatMap(bus -> bus.getGenerators().stream()) .filter(generator -> generator.isParticipating() && generator.getParticipationFactor() != 0) diff --git a/src/main/java/com/powsybl/openloadflow/network/util/LoadActivePowerDistributionStep.java b/src/main/java/com/powsybl/openloadflow/network/util/LoadActivePowerDistributionStep.java index 78a96a2d14..7765561c95 100644 --- a/src/main/java/com/powsybl/openloadflow/network/util/LoadActivePowerDistributionStep.java +++ b/src/main/java/com/powsybl/openloadflow/network/util/LoadActivePowerDistributionStep.java @@ -7,11 +7,11 @@ package com.powsybl.openloadflow.network.util; import com.powsybl.openloadflow.network.LfBus; -import com.powsybl.openloadflow.network.LfNetwork; import com.powsybl.openloadflow.network.PerUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; @@ -38,9 +38,8 @@ public String getElementType() { } @Override - public List getParticipatingElements(LfNetwork network) { - return network.getBuses() - .stream() + public List getParticipatingElements(Collection buses) { + return buses.stream() .filter(bus -> bus.getPositiveLoadCount() > 0 && getVariableLoadTargetP(bus) > 0 && !(bus.isFictitious() || bus.isDisabled())) .map(bus -> new ParticipatingElement(bus, getVariableLoadTargetP(bus))) .collect(Collectors.toList()); diff --git a/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java index 7bf7ad8684..ed40ec26de 100644 --- a/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sensi/AbstractSensitivityAnalysis.java @@ -40,7 +40,6 @@ import org.slf4j.LoggerFactory; import java.util.*; -import java.util.function.Function; import java.util.stream.Collectors; /** @@ -212,7 +211,7 @@ public boolean areVariableAndFunctionDisconnected(GraphDecrementalConnectivity connectivity) { + public boolean isConnectedToComponent(Set connectedComponent) { throw new NotImplementedException("isConnectedToComponent should have an override"); } } @@ -236,8 +235,8 @@ public boolean areVariableAndFunctionDisconnected(final GraphDecrementalConnecti } @Override - public boolean isConnectedToComponent(Integer componentNumber, GraphDecrementalConnectivity connectivity) { - return connectivity.getComponentNumber(injectionLfBus) == componentNumber; + public boolean isConnectedToComponent(Set connectedComponent) { + return connectedComponent.contains(injectionLfBus); } public LfBus getInjectionLfBus() { @@ -264,8 +263,8 @@ public boolean areVariableAndFunctionDisconnected(final GraphDecrementalConnecti } @Override - public boolean isConnectedToComponent(Integer componentNumber, GraphDecrementalConnectivity connectivity) { - return componentNumber == connectivity.getComponentNumber(phaseTapChangerLfBranch.getBus1()); + public boolean isConnectedToComponent(Set connectedComponent) { + return connectedComponent.contains(phaseTapChangerLfBranch.getBus1()); } } @@ -307,13 +306,13 @@ public boolean areVariableAndFunctionDisconnected(final GraphDecrementalConnecti } @Override - public boolean isConnectedToComponent(Integer componentNumber, GraphDecrementalConnectivity connectivity) { - if (connectivity.getComponentNumber(getFunctionLfBranch().getBus1()) != componentNumber - || connectivity.getComponentNumber(getFunctionLfBranch().getBus2()) != componentNumber) { + public boolean isConnectedToComponent(Set connectedComponent) { + if (!connectedComponent.contains(getFunctionLfBranch().getBus1()) + || !connectedComponent.contains(getFunctionLfBranch().getBus2())) { return false; } for (LfBus lfBus : injectionBuses.keySet()) { - if (connectivity.getComponentNumber(lfBus) == componentNumber) { + if (connectedComponent.contains(lfBus)) { return true; } } @@ -477,17 +476,13 @@ protected List createFactorGrou return new ArrayList<>(groupIndexedById.values()); } - protected List getParticipatingElements(LfNetwork lfNetwork, LoadFlowParameters loadFlowParameters, OpenLoadFlowParameters openLoadFlowParameters, Function filter) { + protected List getParticipatingElements(Collection buses, LoadFlowParameters loadFlowParameters, OpenLoadFlowParameters openLoadFlowParameters) { ActivePowerDistribution.Step step = ActivePowerDistribution.getStep(loadFlowParameters.getBalanceType(), openLoadFlowParameters.isLoadPowerFactorConstant()); - List participatingElements = step.getParticipatingElements(lfNetwork).stream().filter(filter::apply).collect(Collectors.toList()); + List participatingElements = step.getParticipatingElements(buses); ParticipatingElement.normalizeParticipationFactors(participatingElements, "bus"); return participatingElements; } - protected List getParticipatingElements(LfNetwork lfNetwork, LoadFlowParameters loadFlowParameters, OpenLoadFlowParameters openLoadFlowParameters) { - return getParticipatingElements(lfNetwork, loadFlowParameters, openLoadFlowParameters, element -> true); - } - protected void computeInjectionFactors(Map participationFactorByBus, List factorGroups) { // compute the corresponding injection (including participation) for each factor for (SensitivityFactorGroup factorGroup : factorGroups) { @@ -516,12 +511,13 @@ public void cutConnectivity(LfNetwork lfNetwork, GraphDecrementalConnectivity connectivity.cut(lfBranch.getBus1(), lfBranch.getBus2())); } - protected void setPredefinedResults(Collection> lfFactors, GraphDecrementalConnectivity connectivity, int mainComponent) { + protected void setPredefinedResults(Collection> lfFactors, Set connectedComponent, + GraphDecrementalConnectivity connectivity) { for (LfSensitivityFactor factor : lfFactors) { // check if the factor function and variable are in different connected components if (factor.areVariableAndFunctionDisconnected(connectivity)) { factor.setPredefinedResult(0d); - } else if (!factor.isConnectedToComponent(mainComponent, connectivity)) { + } else if (!factor.isConnectedToComponent(connectedComponent)) { factor.setPredefinedResult(Double.NaN); // works for sensitivity and function reference } } @@ -531,7 +527,7 @@ protected static SensitivityValue createZeroValue(LfSensitivityFactor lfFactor) return new SensitivityValue(lfFactor.getFactor(), 0, Double.NaN, Double.NaN); } - protected void rescaleGlsk(List factorGroups, GraphDecrementalConnectivity connectivity, Integer mainComponentNumber) { + protected void rescaleGlsk(List factorGroups, Set nonConnectedBuses) { // compute the corresponding injection (with participation) for each factor for (SensitivityFactorGroup factorGroup : factorGroups) { if (!(factorGroup instanceof LinearGlskGroup)) { @@ -539,7 +535,7 @@ protected void rescaleGlsk(List factorGroups, GraphDecre } LinearGlskGroup glskGroup = (LinearGlskGroup) factorGroup; Map remainingGlskInjections = glskGroup.getGlskMap().entrySet().stream() - .filter(entry -> connectivity.getComponentNumber(entry.getKey()) == mainComponentNumber) + .filter(entry -> !nonConnectedBuses.contains(entry.getKey())) .collect(Collectors.toMap(entry -> entry.getKey().getId(), Map.Entry::getValue)); glskGroup.setGlskMapInMainComponent(remainingGlskInjections); } diff --git a/src/main/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysis.java index 6e01f6333c..d7678e8e2e 100644 --- a/src/main/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sensi/AcSensitivityAnalysis.java @@ -159,7 +159,7 @@ public Pair, Map>> analyse Map slackParticipationByBus; if (lfParameters.isDistributedSlack()) { - List participatingElements = getParticipatingElements(lfNetwork, lfParameters, lfParametersExt); + List participatingElements = getParticipatingElements(lfNetwork.getBuses(), lfParameters, lfParametersExt); slackParticipationByBus = participatingElements.stream().collect(Collectors.toMap( element -> element.getLfBus().getId(), element -> -element.getFactor(), @@ -210,18 +210,21 @@ public Pair, Map>> analyse for (LfContingency lfContingency : lfContingencies.stream().filter(lfContingency -> !lfContingency.getBuses().isEmpty()).collect(Collectors.toSet())) { lfFactors.forEach(lfFactor -> lfFactor.setPredefinedResult(null)); - int mainComponent = connectivity.getComponentNumber(lfNetwork.getSlackBus()); cutConnectivity(lfNetwork, connectivity, propagatedContingencyMap.get(lfContingency.getContingency())); - setPredefinedResults(lfFactors, connectivity, mainComponent); // check if factors are still in the main component + Set nonConnectedBuses = connectivity.getNonConnectedVertices(lfNetwork.getSlackBus()); + Set slackConnectedComponent = new HashSet<>(lfNetwork.getBuses()); + slackConnectedComponent.removeAll(nonConnectedBuses); + setPredefinedResults(lfFactors, slackConnectedComponent, connectivity); // check if factors are still in the main component - rescaleGlsk(factorGroups, connectivity, mainComponent); + rescaleGlsk(factorGroups, nonConnectedBuses); // compute the participation for each injection factor (+1 on the injection and then -participation factor on all // buses that contain elements participating to slack distribution Map slackParticipationByBusForThisConnectivity; if (lfParameters.isDistributedSlack()) { - List participatingElementsForThisConnectivity = getParticipatingElements(lfNetwork, lfParameters, lfParametersExt, element -> connectivity.getComponentNumber(element.getLfBus()) == mainComponent); // will also be used to recompute the loadflow + List participatingElementsForThisConnectivity = getParticipatingElements( + slackConnectedComponent, lfParameters, lfParametersExt); // will also be used to recompute the loadflow slackParticipationByBusForThisConnectivity = participatingElementsForThisConnectivity.stream().collect(Collectors.toMap( element -> element.getLfBus().getId(), element -> -element.getFactor(), diff --git a/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java b/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java index 29df226520..6e2b86ef2c 100644 --- a/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java +++ b/src/main/java/com/powsybl/openloadflow/sensi/DcSensitivityAnalysis.java @@ -15,13 +15,14 @@ import com.powsybl.math.matrix.Matrix; import com.powsybl.math.matrix.MatrixFactory; import com.powsybl.openloadflow.OpenLoadFlowParameters; +import com.powsybl.openloadflow.dc.DcLoadFlowEngine; +import com.powsybl.openloadflow.dc.DcLoadFlowParameters; import com.powsybl.openloadflow.dc.equations.ClosedBranchSide1DcFlowEquationTerm; import com.powsybl.openloadflow.dc.equations.DcEquationSystem; import com.powsybl.openloadflow.dc.equations.DcEquationSystemCreationParameters; import com.powsybl.openloadflow.equations.*; import com.powsybl.openloadflow.graph.GraphDecrementalConnectivity; import com.powsybl.openloadflow.network.*; -import com.powsybl.openloadflow.network.util.ActivePowerDistribution; import com.powsybl.openloadflow.network.util.ParticipatingElement; import com.powsybl.openloadflow.util.BusState; import com.powsybl.openloadflow.util.PropagatedContingency; @@ -121,67 +122,28 @@ public DcSensitivityAnalysis(MatrixFactory matrixFactory) { super(matrixFactory); } - protected DenseMatrix setReferenceActivePowerFlows(LfNetwork network, EquationSystem equationSystem, JacobianMatrix j, List> factors, - LoadFlowParameters lfParameters, List participatingElements, GraphDecrementalConnectivity connectivity) { + protected DenseMatrix setReferenceActivePowerFlows(DcLoadFlowEngine dcLoadFlowEngine, EquationSystem equationSystem, JacobianMatrix j, + List> factors, LoadFlowParameters lfParameters, + List participatingElements, Collection disabledBuses) { - double[] x = equationSystem.createStateVector(new UniformValueVoltageInitializer()); Map busStates = new HashMap<>(); if (lfParameters.isDistributedSlack()) { - double mismatch; - if (connectivity != null) { - mismatch = network.getActivePowerMismatchInMainComponent(connectivity); - } else { - mismatch = network.getActivePowerMismatch(); - } busStates = BusState.createBusStates(participatingElements.stream() - .map(ParticipatingElement::getLfBus) - .collect(Collectors.toSet())); - int iteration = 0; - ActivePowerDistribution.Step step = ActivePowerDistribution.getStep(lfParameters.getBalanceType(), false); - while (!participatingElements.isEmpty() - && Math.abs(mismatch) > ActivePowerDistribution.P_RESIDUE_EPS) { - mismatch -= step.run(participatingElements, iteration, mismatch); - - iteration++; - } - } - - equationSystem.updateEquations(x); - - double[] dx = equationSystem.createTargetVector(); - - if (connectivity != null) { - // set buses injections and transformers to 0 outside the main connected component - int mainComponentNumber = connectivity.getComponentNumber(network.getSlackBus()); - Set columnsToSetToZero = network.getBuses().stream() - .filter(lfBus -> connectivity.getComponentNumber(lfBus) != mainComponentNumber) - .map(lfBus -> equationSystem.getEquation(lfBus.getNum(), EquationType.BUS_P)) - .filter(Optional::isPresent) - .map(Optional::get) - .map(Equation::getColumn) - .collect(Collectors.toSet()); - for (Integer column : columnsToSetToZero) { - dx[column] = 0; - } + .map(ParticipatingElement::getLfBus) + .collect(Collectors.toSet())); } - j.solveTransposed(dx); - - equationSystem.updateEquations(dx); - equationSystem.updateNetwork(dx); + dcLoadFlowEngine.run(equationSystem, j, disabledBuses); - // set all calculated voltages to NaN - for (LfBus bus : network.getBuses()) { - bus.setV(Double.NaN); - } for (LfSensitivityFactor factor : factors) { factor.setFunctionReference(factor.getFunctionLfBranch().getP1()); } if (lfParameters.isDistributedSlack()) { - BusState.restoreDcBusStates(busStates); + BusState.restoreBusActiveStates(busStates); } + double[] dx = dcLoadFlowEngine.getTargetVector(); return new DenseMatrix(dx.length, 1, dx); } @@ -392,9 +354,16 @@ public Pair, Map>> analyse checkSensitivities(network, factors); checkLoadFlowParameters(lfParameters); + // create dc load flow engine for setting reference + DcLoadFlowParameters dcLoadFlowParameters = new DcLoadFlowParameters(lfParametersExt.getSlackBusSelector(), matrixFactory, + true, lfParametersExt.isDcUseTransformerRatio(), lfParameters.isDistributedSlack(), lfParameters.getBalanceType(), true, + lfParametersExt.getPlausibleActivePowerLimit(), lfParametersExt.isAddRatioToLinesWithDifferentNominalVoltageAtBothEnds()); + DcLoadFlowEngine dcLoadFlowEngine = new DcLoadFlowEngine(lfNetworks, dcLoadFlowParameters); + // create DC equation system for sensitivity analysis - EquationSystem equationSystem = DcEquationSystem.create(lfNetwork, new VariableSet(), - new DcEquationSystemCreationParameters(true, true, true, lfParametersExt.isDcUseTransformerRatio())); + DcEquationSystemCreationParameters dcEquationSystemCreationParameters = new DcEquationSystemCreationParameters(dcLoadFlowParameters.isUpdateFlows(), true, + dcLoadFlowParameters.isForcePhaseControlOffAndAddAngle1Var(), lfParametersExt.isDcUseTransformerRatio()); + EquationSystem equationSystem = DcEquationSystem.create(lfNetwork, new VariableSet(), dcEquationSystemCreationParameters); // we wrap the factor into a class that allows us to have access to their branch and EquationTerm instantly List> lfFactors = factors.stream().map(factor -> LfSensitivityFactor.create(factor, network, lfNetwork, equationSystem, ClosedBranchSide1DcFlowEquationTerm.class)).collect(Collectors.toList()); @@ -413,7 +382,7 @@ public Pair, Map>> analyse List participatingElements = null; Map slackParticipationByBus; if (lfParameters.isDistributedSlack()) { - participatingElements = getParticipatingElements(lfNetwork, lfParameters, lfParametersExt); + participatingElements = getParticipatingElements(lfNetwork.getBuses(), lfParameters, lfParametersExt); slackParticipationByBus = participatingElements.stream().collect(Collectors.toMap( element -> element.getLfBus().getId(), element -> -element.getFactor(), @@ -443,7 +412,7 @@ public Pair, Map>> analyse try (JacobianMatrix j = createJacobianMatrix(equationSystem, voltageInitializer)) { // run DC load on pre-contingency network - DenseMatrix flowStates = setReferenceActivePowerFlows(lfNetwork, equationSystem, j, lfFactors, lfParameters, participatingElements, null); + DenseMatrix flowStates = setReferenceActivePowerFlows(dcLoadFlowEngine, equationSystem, j, lfFactors, lfParameters, participatingElements, Collections.emptyList()); // compute the pre-contingency sensitivity values + the states with +1 -1 to model the contingencies DenseMatrix factorsStates = initFactorsRhs(lfNetwork, equationSystem, factorGroups); // this is the rhs for the moment @@ -489,11 +458,14 @@ public Pair, Map>> analyse List contingencyList = entry.getValue(); lfFactors.forEach(factor -> factor.setPredefinedResult(null)); cutConnectivity(lfNetwork, connectivity, breakingConnectivityCandidates.stream().map(ComputedContingencyElement::getElement).map(ContingencyElement::getId).collect(Collectors.toSet())); - int mainComponent = connectivity.getComponentNumber(lfNetwork.getSlackBus()); - setPredefinedResults(lfFactors, connectivity, mainComponent); // check if factors are still in the main component + + Set disabledBuses = connectivity.getNonConnectedVertices(lfNetwork.getSlackBus()); + Set slackConnectedComponent = new HashSet<>(lfNetwork.getBuses()); + slackConnectedComponent.removeAll(disabledBuses); + setPredefinedResults(lfFactors, slackConnectedComponent, connectivity); // check if factors are still in the main component // some elements of the GLSK may not be in the connected component anymore, we recompute the injections - rescaleGlsk(factorGroups, connectivity, mainComponent); + rescaleGlsk(factorGroups, disabledBuses); // null and unused if slack is not distributed List participatingElementsForThisConnectivity = participatingElements; @@ -503,7 +475,7 @@ public Pair, Map>> analyse Map slackParticipationByBusForThisConnectivity; if (lfParameters.isDistributedSlack()) { - participatingElementsForThisConnectivity = getParticipatingElements(lfNetwork, lfParameters, lfParametersExt, element -> connectivity.getComponentNumber(element.getLfBus()) == mainComponent); // will also be used to recompute the loadflow + participatingElementsForThisConnectivity = getParticipatingElements(slackConnectedComponent, lfParameters, lfParametersExt); // will also be used to recompute the loadflow slackParticipationByBusForThisConnectivity = participatingElementsForThisConnectivity.stream().collect(Collectors.toMap( element -> element.getLfBus().getId(), element -> -element.getFactor(), @@ -520,7 +492,8 @@ public Pair, Map>> analyse setBaseCaseSensitivityValues(factorGroups, factorsStates); // use this state to compute the base sensitivity (without +1-1) } - flowStates = setReferenceActivePowerFlows(lfNetwork, equationSystem, j, lfFactors, lfParameters, participatingElementsForThisConnectivity, connectivity); + flowStates = setReferenceActivePowerFlows(dcLoadFlowEngine, equationSystem, j, lfFactors, lfParameters, + participatingElementsForThisConnectivity, disabledBuses); Set elementsToReconnect = getElementsToReconnect(connectivity, breakingConnectivityCandidates); diff --git a/src/main/java/com/powsybl/openloadflow/util/BusState.java b/src/main/java/com/powsybl/openloadflow/util/BusState.java index edf3305c6d..59fff3ec54 100644 --- a/src/main/java/com/powsybl/openloadflow/util/BusState.java +++ b/src/main/java/com/powsybl/openloadflow/util/BusState.java @@ -39,21 +39,21 @@ public BusState(LfBus b) { } public void restoreBusState(LfBus bus) { - restoreDcBusState(bus); + restoreBusActiveState(bus); bus.setV(v); bus.setLoadTargetQ(loadTargetQ); bus.setGenerationTargetQ(generationTargetQ); + bus.setDisabled(disabled); bus.setVoltageControllerEnabled(isVoltageControllerEnabled); bus.setVoltageControlSwitchOffCount(0); } - public void restoreDcBusState(LfBus bus) { + public void restoreBusActiveState(LfBus bus) { bus.setAngle(angle); bus.setLoadTargetP(loadTargetP); bus.getGenerators().forEach(g -> { g.setTargetP(generatorsTargetP.get(g.getId())); }); - bus.setDisabled(disabled); } /** @@ -77,8 +77,8 @@ public static void restoreBusStates(Map busStates) { * Set the bus states based on the given map of states * @param busStates the map containing the bus states, indexed by buses */ - public static void restoreDcBusStates(Map busStates) { - busStates.forEach((b, state) -> state.restoreDcBusState(b)); + public static void restoreBusActiveStates(Map busStates) { + busStates.forEach((b, state) -> state.restoreBusActiveState(b)); } } diff --git a/src/test/java/com/powsybl/openloadflow/graph/ConnectivityTest.java b/src/test/java/com/powsybl/openloadflow/graph/ConnectivityTest.java index 3d548023d0..a6f4a62bcf 100644 --- a/src/test/java/com/powsybl/openloadflow/graph/ConnectivityTest.java +++ b/src/test/java/com/powsybl/openloadflow/graph/ConnectivityTest.java @@ -7,7 +7,6 @@ package com.powsybl.openloadflow.graph; import com.powsybl.commons.PowsyblException; -import com.powsybl.iidm.network.Bus; import com.powsybl.iidm.network.Network; import com.powsybl.openloadflow.network.*; import org.junit.jupiter.api.BeforeEach; @@ -15,6 +14,8 @@ import java.util.Arrays; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -29,7 +30,7 @@ class ConnectivityTest { @BeforeEach void setup() { - Network network = new ConnectedFactory().createThreeCcLinkedByASingleBus(); + Network network = ConnectedComponentNetworkFactory.createThreeCcLinkedByASingleBus(); List lfNetworks = LfNetwork.load(network, new FirstSlackBusSelector()); lfNetwork = lfNetworks.get(0); } @@ -71,6 +72,20 @@ void testNonConnected() { assertEquals("Algorithm not implemented for a network with several connected components at start", e.getMessage()); } + @Test + void testNonConnectedComponents() { + testNonConnectedComponents(new NaiveGraphDecrementalConnectivity<>(LfBus::getNum)); + testNonConnectedComponents(new EvenShiloachGraphDecrementalConnectivity<>()); + testNonConnectedComponents(new MinimumSpanningTreeGraphDecrementalConnectivity<>()); + } + + @Test + void testConnectedComponents() { + testConnectedComponents(new NaiveGraphDecrementalConnectivity<>(LfBus::getNum)); + testConnectedComponents(new EvenShiloachGraphDecrementalConnectivity<>()); + testConnectedComponents(new MinimumSpanningTreeGraphDecrementalConnectivity<>()); + } + private void testConnectivity(GraphDecrementalConnectivity connectivity) { updateConnectivity(connectivity); cutBranches(connectivity, "l34", "l48"); @@ -79,6 +94,9 @@ private void testConnectivity(GraphDecrementalConnectivity connectivity) assertEquals(0, connectivity.getComponentNumber(lfNetwork.getBusById("b4_vl_0"))); assertEquals(2, connectivity.getComponentNumber(lfNetwork.getBusById("b8_vl_0"))); assertEquals(2, connectivity.getSmallComponents().size()); + + AssertionError e = assertThrows(AssertionError.class, () -> connectivity.getComponentNumber(null)); + assertEquals("given vertex null is not in the graph", e.getMessage()); } private void testReducedMainComponent(GraphDecrementalConnectivity connectivity) { @@ -113,6 +131,36 @@ private void testReaddEdge(GraphDecrementalConnectivity connectivity) { assertEquals(1, connectivity.getComponentNumber(lfNetwork.getBusById("b8_vl_0"))); } + private void testNonConnectedComponents(GraphDecrementalConnectivity connectivity) { + updateConnectivity(connectivity); + cutBranches(connectivity, "l34", "l48"); + + assertEquals(createVerticesSet("b4_vl_0", "b5_vl_0", "b6_vl_0", "b7_vl_0", "b8_vl_0", "b9_vl_0", "b10_vl_0"), + connectivity.getNonConnectedVertices(lfNetwork.getBusById("b3_vl_0"))); + assertEquals(createVerticesSet("b1_vl_0", "b2_vl_0", "b3_vl_0", "b8_vl_0", "b9_vl_0", "b10_vl_0"), + connectivity.getNonConnectedVertices(lfNetwork.getBusById("b6_vl_0"))); + assertEquals(createVerticesSet("b4_vl_0", "b5_vl_0", "b6_vl_0", "b7_vl_0", "b1_vl_0", "b2_vl_0", "b3_vl_0"), + connectivity.getNonConnectedVertices(lfNetwork.getBusById("b10_vl_0"))); + + AssertionError e = assertThrows(AssertionError.class, () -> connectivity.getNonConnectedVertices(null)); + assertEquals("given vertex null is not in the graph", e.getMessage()); + } + + private void testConnectedComponents(GraphDecrementalConnectivity connectivity) { + updateConnectivity(connectivity); + cutBranches(connectivity, "l34", "l56", "l57"); + + assertEquals(createVerticesSet("b1_vl_0", "b2_vl_0", "b3_vl_0"), + connectivity.getConnectedComponent(lfNetwork.getBusById("b3_vl_0"))); + assertEquals(createVerticesSet("b6_vl_0", "b7_vl_0"), + connectivity.getConnectedComponent(lfNetwork.getBusById("b6_vl_0"))); + assertEquals(createVerticesSet("b4_vl_0", "b5_vl_0", "b8_vl_0", "b9_vl_0", "b10_vl_0"), + connectivity.getConnectedComponent(lfNetwork.getBusById("b10_vl_0"))); + + AssertionError e = assertThrows(AssertionError.class, () -> connectivity.getConnectedComponent(null)); + assertEquals("given vertex null is not in the graph", e.getMessage()); + } + private void cutBranches(GraphDecrementalConnectivity connectivity, String... branches) { Arrays.stream(branches).map(lfNetwork::getBranchById).forEach(lfBranch -> connectivity.cut(lfBranch.getBus2(), lfBranch.getBus1())); } @@ -126,44 +174,7 @@ private void updateConnectivity(GraphDecrementalConnectivity connectivity } } - public static class ConnectedFactory extends AbstractLoadFlowNetworkFactory { - public Network createThreeCcLinkedByASingleBus() { - Network network = Network.create("test", "code"); - Bus b1 = createBus(network, "b1"); - Bus b2 = createBus(network, "b2"); - Bus b3 = createBus(network, "b3"); - Bus b4 = createBus(network, "b4"); - Bus b5 = createBus(network, "b5"); - Bus b6 = createBus(network, "b6"); - Bus b7 = createBus(network, "b7"); - Bus b8 = createBus(network, "b8"); - Bus b9 = createBus(network, "b9"); - Bus b10 = createBus(network, "b10"); - createLine(network, b1, b2, "l12", 0.1f); - createLine(network, b1, b3, "l13", 0.1f); - createLine(network, b2, b3, "l23", 0.1f); - createLine(network, b3, b4, "l34", 0.1f); - createLine(network, b4, b5, "l45", 0.1f); - createLine(network, b5, b6, "l56", 0.1f); - createLine(network, b5, b7, "l57", 0.1f); - createLine(network, b6, b7, "l67", 0.1f); - createLine(network, b4, b8, "l48", 0.1f); - createLine(network, b8, b9, "l89", 0.1f); - createLine(network, b8, b10, "l810", 0.1f); - createLine(network, b9, b10, "l910", 0.1f); - - createGenerator(b2, "g2", 3); - createGenerator(b6, "g6", 2); - createGenerator(b10, "g10", 4); - createLoad(b1, "d1", 1); - createLoad(b3, "d3", 1); - createLoad(b4, "d4", 1); - createLoad(b5, "d5", 2); - createLoad(b7, "d7", 2); - createLoad(b8, "d8", 1); - createLoad(b9, "d9", 1); - - return network; - } + private Set createVerticesSet(String... busIds) { + return Arrays.stream(busIds).map(lfNetwork::getBusById).collect(Collectors.toSet()); } }