-
Notifications
You must be signed in to change notification settings - Fork 16
Support disconnection of DC elements in the load flow #1471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 7 commits
6845ec3
a595b5b
487ed75
9b6f8cd
97c772e
89aacde
d34f682
4e73ea9
ce3844a
c5651ac
4a5f41d
b817df2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| /** | ||
| * Copyright (c) 2026, SuperGrid Institute (http://www.supergrid-institute.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.openloadflow.ac.equations.dcnetwork; | ||
|
|
||
| import com.powsybl.openloadflow.ac.equations.AcEquationType; | ||
| import com.powsybl.openloadflow.ac.equations.AcVariableType; | ||
| import com.powsybl.openloadflow.equations.AbstractElementEquationTerm; | ||
| import com.powsybl.openloadflow.equations.Variable; | ||
| import com.powsybl.openloadflow.network.LfDcLine; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Objects; | ||
|
|
||
| /** | ||
| * Equation term returning 0 for the current in an open DC line. | ||
| * | ||
| * @author Baptiste Perreyon {@literal <baptiste.perreyon at supergrid-institute.com>} | ||
| */ | ||
| public class OpenDcLineEquationTerm extends AbstractElementEquationTerm<LfDcLine, AcVariableType, AcEquationType> { | ||
|
|
||
| public OpenDcLineEquationTerm(LfDcLine dcLine) { | ||
| super(dcLine); | ||
| } | ||
|
|
||
| @Override | ||
| public double eval() { | ||
| return 0.0; | ||
| } | ||
|
|
||
| @Override | ||
| public double der(Variable<AcVariableType> variable) { | ||
| Objects.requireNonNull(variable); | ||
| throw new IllegalStateException("Unknown variable: " + variable); | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return "dc_open"; | ||
|
bperr marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| @Override | ||
| public List<Variable<AcVariableType>> getVariables() { | ||
| return List.of(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| /** | ||
| * Copyright (c) 2026, SuperGrid Institute (http://www.supergrid-institute.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.openloadflow.network.impl; | ||
|
|
||
| import com.powsybl.commons.PowsyblException; | ||
| import com.powsybl.iidm.network.*; | ||
|
|
||
| import java.util.*; | ||
| import java.util.function.Predicate; | ||
|
|
||
| /** | ||
| * Helper methods to validate a DcComponent configuration | ||
| * | ||
| * @author Baptiste Perreyon {@literal <baptiste.perreyon at supergrid-institute.com>} | ||
| */ | ||
| final class DcNetworkValidationHelpers { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call to factor out these static functions. I am wondering if it is possible to simplify this code a bit. Maybe extract the topology visitor, and replace the predicate on converters by a boolean stating whether the test is needed or not? |
||
|
|
||
| private DcNetworkValidationHelpers() { | ||
| } | ||
|
|
||
| /** | ||
| * Check that at least one of the DC terminal of each AC-DC converter is indirectly connected (i.e. through DC | ||
| * lines) to a DC ground. | ||
|
bperr marked this conversation as resolved.
Outdated
|
||
| * | ||
| * @param acDcConverters A list of AC-DC converters to check. | ||
| * @throws PowsyblException If at least one AC-DC converter is not indirectly connected to a DC ground | ||
| */ | ||
| public static void checkAllConvertersAreIndirectlyConnectedToADcGround(List<AcDcConverter<?>> acDcConverters) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to leave it package-private? I think these static methods are only used in |
||
| for (AcDcConverter<?> converter : acDcConverters) { | ||
| boolean isTerminal1IndirectlyConnectedToGround = isConnectedToGround(converter.getDcTerminal1()); | ||
| boolean isTerminal2IndirectlyConnectedToGround = isConnectedToGround(converter.getDcTerminal2()); | ||
| if (!isTerminal1IndirectlyConnectedToGround && !isTerminal2IndirectlyConnectedToGround) { | ||
| throw new PowsyblException(String.format("Converter %s is not indirectly connected to a DC ground", converter.getId())); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Check that at least one AC-DC converter controls the DC voltage. | ||
| * | ||
| * @param acDcConverters A list of AC-DC converters to check. | ||
| * @throws PowsyblException If no AC-DC converter control the DC voltage. | ||
| */ | ||
| public static void checkAtLeastOneConverterControlsVdc(List<AcDcConverter<?>> acDcConverters, int numDcc) { | ||
| // Ensure at least one converter controls the DC voltage. V_DC controls it directly; P_PCC_DROOP also counts | ||
| // as DC-voltage control since the droop law settles the DC voltage. | ||
| boolean isVdcControlled = false; | ||
| for (AcDcConverter<?> converter : acDcConverters) { | ||
| if (converter.getControlMode() == AcDcConverter.ControlMode.V_DC || converter.getControlMode() == AcDcConverter.ControlMode.P_PCC_DROOP) { | ||
| isVdcControlled = true; | ||
| break; | ||
| } | ||
| } | ||
| if (!isVdcControlled) { | ||
| throw new PowsyblException("At least one AC/DC converter control mode must be V_DC or P_PCC_DROOP in each DC component, but DC component " + numDcc + " does not have any"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Check that both DC terminals of each AC-DC converter in P_PCC mode are indirectly connected (i.e. through DC | ||
| * lines) to an element imposing voltage (a non P_PCC AC-DC converter or a DC ground). | ||
| * | ||
| * @param acDcConverters A list of AC-DC converters to check. | ||
| * @throws PowsyblException If at least one terminal of one AC-DC converter is not indirectly connected to an | ||
| * element imposing voltage | ||
| */ | ||
| public static void checkPccConverterAreIndirectlyConnectedToElementImposingVoltage(List<AcDcConverter<?>> acDcConverters) { | ||
| for (AcDcConverter<?> converter : acDcConverters) { | ||
| if (converter.getControlMode() == AcDcConverter.ControlMode.P_PCC) { | ||
| boolean isConverterTerminal1Fine = isConnectedToNonPccConverterOrGround(converter.getDcTerminal1()); | ||
| if (!isConverterTerminal1Fine) { | ||
| throw new PowsyblException(String.format("Converter %s is in P_PCC control mode but its first DC bus is not connected to an element imposing voltage", converter.getId())); | ||
| } | ||
|
|
||
| boolean isConverterTerminal2Fine = isConnectedToNonPccConverterOrGround(converter.getDcTerminal2()); | ||
| if (!isConverterTerminal2Fine) { | ||
| throw new PowsyblException(String.format("Converter %s is in P_PCC control mode but its second DC bus is not connected to an element imposing voltage", converter.getId())); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Perform topological search to find out if the terminal of a AC-DC converter in P_PCC mode is indirectly connected | ||
| * (i.e. through DC lines) to an element imposing voltage (a DC ground or a AC-DC converter not in P_PCC mode) | ||
| * | ||
| * @param startTerminal The terminal of the AC-DC converter used as a starting point. | ||
| * @return true if the terminal is indirectly connected to an element imposing voltage, false otherwise. | ||
| */ | ||
| private static boolean isConnectedToNonPccConverterOrGround(DcTerminal startTerminal) { | ||
| return isIndirectlyConnectedToVoltageImposingElement(startTerminal, converter -> | ||
| converter.getControlMode() != AcDcConverter.ControlMode.P_PCC | ||
| && converter.getDcTerminal1().isConnected() && converter.getDcTerminal2().isConnected() | ||
| && converter.getTerminal1().isConnected()); | ||
| } | ||
|
|
||
| /** | ||
| * Perform topological search to find out if the terminal of a AC-DC converter is indirectly connected (i.e. | ||
| * through DC lines) to a DC ground. | ||
| * | ||
| * @param startTerminal The terminal of the AC-DC converter used as a starting point. | ||
| * @return true if the terminal is indirectly connected to a DC ground, false otherwise. | ||
| */ | ||
| private static boolean isConnectedToGround(DcTerminal startTerminal) { | ||
| return isIndirectlyConnectedToVoltageImposingElement(startTerminal, converter -> false); | ||
| } | ||
|
|
||
| /** | ||
| * Perform a breadth-first topological search, following only DC lines, to find out if the terminal of a AC-DC | ||
| * converter is indirectly connected to an element imposing voltage: a connected DC ground, or an AC-DC converter | ||
| * satisfying {@code isVoltageImposingConverter}. | ||
| * | ||
| * @param startTerminal The terminal of the AC-DC converter used as a starting point. | ||
| * @param isVoltageImposingConverter Tells whether an encountered AC-DC converter should, on its own, be | ||
| * considered as imposing voltage. Pass a predicate always returning false to | ||
| * only look for a DC ground. | ||
| * @return true if the terminal is indirectly connected to an element imposing voltage, false otherwise. | ||
| */ | ||
| private static boolean isIndirectlyConnectedToVoltageImposingElement(DcTerminal startTerminal, | ||
|
Check failure on line 124 in src/main/java/com/powsybl/openloadflow/network/impl/DcNetworkValidationHelpers.java
|
||
| Predicate<AcDcConverter<?>> isVoltageImposingConverter) { | ||
| DcBus startBus = startTerminal.getDcBus(); | ||
| if (startBus == null) { | ||
| return false; // startTerminal not connected | ||
| } | ||
|
|
||
| Set<String> visitedBusIds = new HashSet<>(); | ||
| Deque<DcBus> queue = new ArrayDeque<>(); | ||
| visitedBusIds.add(startBus.getId()); | ||
| queue.add(startBus); | ||
|
|
||
| while (!queue.isEmpty()) { | ||
| DcBus currentBus = queue.poll(); | ||
| boolean[] found = {false}; | ||
|
|
||
| currentBus.visitConnectedEquipments(new DcTopologyVisitor() { | ||
| @Override | ||
| public void visitAcDcConverter(AcDcConverter<?> converter, TerminalNumber terminalNumber) { | ||
| if (isVoltageImposingConverter.test(converter)) { | ||
| found[0] = true; | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void visitDcLine(DcLine dcLine, TwoSides side) { | ||
| // side is connected to currentBus; check the other side | ||
| TwoSides otherSide = (side == TwoSides.ONE) ? TwoSides.TWO : TwoSides.ONE; | ||
| DcTerminal otherTerminal = dcLine.getDcTerminal(otherSide); | ||
| if (otherTerminal.isConnected()) { | ||
| DcBus otherBus = otherTerminal.getDcBus(); | ||
| if (visitedBusIds.add(otherBus.getId())) { | ||
| queue.add(otherBus); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void visitDcGround(DcGround dcGround) { | ||
| if (dcGround.getDcTerminal().isConnected()) { | ||
| found[0] = true; | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| if (found[0]) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will probably require a bit more details, e.g. whether an equation imposing I=0 is added, or what happens with losses and on the AC side.