Skip to content
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

Dry run for network modifications #3002

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ public void apply(Network network, NamingStrategy namingStrategy, boolean throwE
ComputationManager computationManager, ReportNode reportNode) {
script.call(network, computationManager);
}

@Override
protected boolean applyDryRun(Network network, NamingStrategy namingStrategy, ComputationManager computationManager, ReportNode reportNode) {
// TODO: what do we do here?
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ public interface Connectable<I extends Connectable<I>> extends Identifiable<I> {

boolean connect(Predicate<Switch> isTypeSwitchToOperate, ThreeSides side);

boolean connect(Predicate<Switch> isTypeSwitchToOperate, ThreeSides side, boolean dryRun);

boolean disconnect();

boolean disconnect(Predicate<Switch> isSwitchOpenable);

boolean disconnect(Predicate<Switch> isSwitchOpenable, ThreeSides side);

boolean disconnect(Predicate<Switch> isSwitchOpenable, ThreeSides side, boolean dryRun);
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,16 @@ public boolean connect(Predicate<Switch> isTypeSwitchToOperate) {

@Override
public boolean connect(Predicate<Switch> isTypeSwitchToOperate, ThreeSides side) {
return connect(isTypeSwitchToOperate, side, false);
}

@Override
public boolean connect(Predicate<Switch> isTypeSwitchToOperate, ThreeSides side, boolean dryRun) {
return ConnectDisconnectUtil.connectAllTerminals(
this,
getTerminals(side),
isTypeSwitchToOperate,
dryRun,
getNetwork().getReportNodeContext().getReportNode());
}

Expand All @@ -225,10 +230,16 @@ public boolean disconnect(Predicate<Switch> isSwitchOpenable) {

@Override
public boolean disconnect(Predicate<Switch> isSwitchOpenable, ThreeSides side) {
return disconnect(isSwitchOpenable, side, false);
}

@Override
public boolean disconnect(Predicate<Switch> isSwitchOpenable, ThreeSides side, boolean dryRun) {
return ConnectDisconnectUtil.disconnectAllTerminals(
this,
getTerminals(side),
isSwitchOpenable,
dryRun,
getNetwork().getReportNodeContext().getReportNode());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,26 @@ private ConnectDisconnectUtil() {
* @param reportNode report node
* @return {@code true} if all the specified terminals have been connected, else {@code false}.
*/
static boolean connectAllTerminals(Identifiable<?> identifiable, List<? extends Terminal> terminals, Predicate<Switch> isTypeSwitchToOperate, ReportNode reportNode) {
static boolean connectAllTerminals(Identifiable<?> identifiable, List<? extends Terminal> terminals,
Predicate<Switch> isTypeSwitchToOperate, ReportNode reportNode) {
return connectAllTerminals(identifiable, terminals, isTypeSwitchToOperate, false, reportNode);
}

/**
* Connect the specified terminals. It will connect all the specified terminals or none if at least one cannot be
* connected.
* @param identifiable network element to connect. It can be a connectable, a tie line or an HVDC line
* @param terminals list of the terminals that should be connected. For a connectable, it should be its own
* terminals, while for a tie line (respectively an HVDC line) it should be the terminals of the
* underlying dangling lines (respectively converter stations)
* @param isTypeSwitchToOperate type of switches that can be operated
* @param dryRun is it a dry run? If {@code true}, no terminal will be effectively connected.
* @param reportNode report node
* @return {@code true} if all the specified terminals have been connected (or can be connected, when
* {@code dryRun} is {@code true}), else {@code false}.
*/
static boolean connectAllTerminals(Identifiable<?> identifiable, List<? extends Terminal> terminals,
Predicate<Switch> isTypeSwitchToOperate, boolean dryRun, ReportNode reportNode) {
// Booleans
boolean isAlreadyConnected = true;
boolean isNowConnected = true;
Expand Down Expand Up @@ -79,6 +97,11 @@ static boolean connectAllTerminals(Identifiable<?> identifiable, List<? extends
return false;
}

// Exit if it's a dry run
if (dryRun) {
return true;
}

// Connect all bus-breaker terminals
for (Terminal terminal : terminals) {
if (!terminal.isConnected()
Expand All @@ -104,7 +127,26 @@ static boolean connectAllTerminals(Identifiable<?> identifiable, List<? extends
* @param reportNode report node
* @return {@code true} if all the specified terminals have been disconnected, else {@code false}.
*/
static boolean disconnectAllTerminals(Identifiable<?> identifiable, List<? extends Terminal> terminals, Predicate<Switch> isSwitchOpenable, ReportNode reportNode) {
static boolean disconnectAllTerminals(Identifiable<?> identifiable, List<? extends Terminal> terminals,
Predicate<Switch> isSwitchOpenable, ReportNode reportNode) {
return disconnectAllTerminals(identifiable, terminals, isSwitchOpenable, false, reportNode);
}

/**
* Disconnect the specified terminals. It will disconnect all the specified terminals or none if at least one cannot
* be disconnected.
* @param identifiable network element to disconnect. It can be a connectable, a tie line or an HVDC line
* @param terminals list of the terminals that should be connected. For a connectable, it should be its own
* terminals, while for a tie line (respectively an HVDC line) it should be the terminals of the
* underlying dangling lines (respectively converter stations)
* @param isSwitchOpenable type of switches that can be operated
* @param dryRun is it a dry run? If {@code true}, no terminal will be effectively disconnected.
* @param reportNode report node
* @return {@code true} if all the specified terminals have been disconnected (or can be disconnected, when
* {@code dryRun} is {@code true}), else {@code false}.
*/
static boolean disconnectAllTerminals(Identifiable<?> identifiable, List<? extends Terminal> terminals,
Predicate<Switch> isSwitchOpenable, boolean dryRun, ReportNode reportNode) {
// Booleans
boolean isAlreadyDisconnected = true;
boolean isNowDisconnected = true;
Expand Down Expand Up @@ -140,6 +182,11 @@ static boolean disconnectAllTerminals(Identifiable<?> identifiable, List<? exten
return false;
}

// Exit if it's a dry run
if (dryRun) {
return true;
}

// Disconnect all bus-breaker terminals
for (Terminal terminal : terminals) {
if (terminal.isConnected()
Expand Down
9 changes: 4 additions & 5 deletions iidm/iidm-modification/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-loadflow-api</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-iidm-serde</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
Expand Down Expand Up @@ -95,11 +99,6 @@
<artifactId>powsybl-iidm-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>powsybl-iidm-serde</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
package com.powsybl.iidm.modification;

import com.powsybl.commons.report.ReportNode;
import com.powsybl.computation.ComputationManager;
import com.powsybl.iidm.modification.topology.NamingStrategy;
import com.powsybl.iidm.network.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -42,6 +44,35 @@ public abstract class AbstractDisconnection extends AbstractNetworkModification
this.side = side;
}

@Override
protected boolean applyDryRun(Network network, NamingStrategy namingStrategy, ComputationManager computationManager, ReportNode reportNode) {
Connectable<?> connectable = network.getConnectable(identifiableId);
if (connectable == null) {
dryRunConclusive = false;
reportOnInconclusiveDryRun(reportNode,
"AbstractDisconnection",
"Identifiable '" + identifiableId + "' not found");
} else if (!connectable.disconnect(openableSwitches, side, true)) {
// TODO : differenciate the cases where the identifiable does not exist, where it is already disconnected, where it cannot be disconnected, etc.
dryRunConclusive = false;
reportOnInconclusiveDryRun(reportNode,
"AbstractDisconnection",
"Disconnection failed");
}
return dryRunConclusive;
}

@Override
public boolean hasImpactOnNetwork() {
return false;
}

@Override
public boolean isLocalDryRunPossible() {
// TODO: see TODO in applyDryRun
return true;
}

public void applyModification(Network network, boolean isPlanned, boolean throwException, ReportNode reportNode) {
// Add the reportNode to the network reportNode context
network.getReportNodeContext().pushReportNode(reportNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@

import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.commons.report.TypedValue;
import com.powsybl.computation.ComputationManager;
import com.powsybl.computation.local.LocalComputationManager;
import com.powsybl.iidm.modification.topology.DefaultNamingStrategy;
import com.powsybl.iidm.modification.topology.NamingStrategy;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -23,6 +24,7 @@
public abstract class AbstractNetworkModification implements NetworkModification {

private static final Logger LOGGER = LoggerFactory.getLogger(AbstractNetworkModification.class);
protected boolean dryRunConclusive = true;

@Override
public void apply(Network network) {
Expand Down Expand Up @@ -79,6 +81,83 @@ public void apply(Network network, NamingStrategy namingStrategy, boolean throwE
apply(network, namingStrategy, throwException, LocalComputationManager.getDefault(), reportNode);
}

@Override
public boolean dryRun(Network network) {
return dryRun(network, new DefaultNamingStrategy(), LocalComputationManager.getDefault(), ReportNode.NO_OP);
}

@Override
public boolean dryRun(Network network, ReportNode reportNode) {
return dryRun(network, new DefaultNamingStrategy(), LocalComputationManager.getDefault(), reportNode);
}

@Override
public boolean dryRun(Network network, NamingStrategy namingStrategy, ComputationManager computationManager, ReportNode reportNode) {
String templateKey = "networkModificationDryRun";
String messageTemplate = "Dry-run: Checking if a network modification can be applied on network '${networkNameOrId}'";
ReportNode dryRunReportNode = reportNode.newReportNode()
.withMessageTemplate(templateKey, messageTemplate)
.withUntypedValue("networkNameOrId", network.getNameOrId())
.withSeverity(TypedValue.INFO_SEVERITY)
.add();
return applyDryRun(network, namingStrategy, computationManager, dryRunReportNode);
}

protected abstract boolean applyDryRun(Network network, NamingStrategy namingStrategy, ComputationManager computationManager, ReportNode reportNode);

@Override
public boolean hasImpactOnNetwork() {
return true;
}

@Override
public boolean isLocalDryRunPossible() {
return false;
}

protected static void reportOnInconclusiveDryRun(ReportNode reportNode, String networkModification, String cause) {
reportNode.newReportNode()
.withMessageTemplate("networkModificationsDryRun-failure",
"Dry-run failed for ${networkModification}. The issue is: ${dryRunError}")
.withUntypedValue("dryRunError", cause)
.withUntypedValue("networkModification", networkModification)
.add();
}

protected static boolean checkVoltageLevel(Identifiable<?> identifiable, ReportNode reportNode, String networkModification, boolean dryRunConclusive) {
boolean localDryRunConclusive = dryRunConclusive;
VoltageLevel vl = null;
if (identifiable instanceof Bus bus) {
vl = bus.getVoltageLevel();
} else if (identifiable instanceof BusbarSection bbs) {
vl = bbs.getTerminal().getVoltageLevel();
} else {
localDryRunConclusive = false;
reportOnInconclusiveDryRun(reportNode,
networkModification,
"Unexpected type of identifiable " + identifiable.getId() + ": " + identifiable.getType());
}
if (vl == null) {
localDryRunConclusive = false;
reportOnInconclusiveDryRun(reportNode,
networkModification,
"Voltage level is null");
}
return localDryRunConclusive;
}

protected static boolean checkLine(Network network, String lineId, ReportNode reportNode, String networkModification, boolean dryRunConclusive) {
boolean localDryRunConclusive = dryRunConclusive;
Line line = network.getLine(lineId);
if (line == null) {
localDryRunConclusive = false;
reportOnInconclusiveDryRun(reportNode,
networkModification,
String.format("Line %s is not found", lineId));
}
return localDryRunConclusive;
}

/**
* Utility during apply functions, logs or throw the message.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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.iidm.modification;

import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.computation.ComputationManager;
import com.powsybl.iidm.modification.topology.NamingStrategy;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.PhaseTapChanger;
import com.powsybl.iidm.network.TwoWindingsTransformer;

import java.util.Objects;

/**
* @author Nicolas Rol {@literal <nicolas.rol at rte-france.com>}
*/
public abstract class AbstractPhaseShifterModification extends AbstractNetworkModification {

protected final String phaseShifterId;
private static final String TRANSFORMER_NOT_FOUND = "Transformer '%s' not found";
private static final String NOT_A_PHASE_SHIFTER = "Transformer '%s' is not a phase shifter";

protected AbstractPhaseShifterModification(String phaseShifterId) {
this.phaseShifterId = Objects.requireNonNull(phaseShifterId);
}

@Override
protected boolean applyDryRun(Network network, NamingStrategy namingStrategy, ComputationManager computationManager, ReportNode reportNode) {
TwoWindingsTransformer phaseShifter = network.getTwoWindingsTransformer(phaseShifterId);
if (phaseShifter == null) {
dryRunConclusive = false;
reportOnInconclusiveDryRun(reportNode,
"AbstractPhaseShifterModification",
String.format(TRANSFORMER_NOT_FOUND, phaseShifterId));
} else if (!phaseShifter.hasPhaseTapChanger()) {
dryRunConclusive = false;
reportOnInconclusiveDryRun(reportNode,
"AbstractPhaseShifterModification",
String.format(NOT_A_PHASE_SHIFTER, phaseShifterId));
}
return dryRunConclusive;
}

protected PhaseTapChanger getPhaseTapChanger(Network network) {
Objects.requireNonNull(network);
TwoWindingsTransformer phaseShifter = network.getTwoWindingsTransformer(phaseShifterId);
if (phaseShifter == null) {
throw new PowsyblException(String.format(TRANSFORMER_NOT_FOUND, phaseShifterId));
}
PhaseTapChanger phaseTapChanger = phaseShifter.getPhaseTapChanger();
if (phaseTapChanger == null) {
throw new PowsyblException(String.format(NOT_A_PHASE_SHIFTER, phaseShifterId));
}
return phaseTapChanger;
}
}
Loading