Skip to content
Open
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 @@ -62,7 +62,8 @@ public enum ModificationType {
CREATE_COUPLING_DEVICE(PreloadingStrategy.NONE),
CREATE_VOLTAGE_LEVEL_TOPOLOGY(PreloadingStrategy.NONE),
LIMIT_SETS_TABULAR_MODIFICATION(PreloadingStrategy.COLLECTION),
CREATE_VOLTAGE_LEVEL_SECTION(PreloadingStrategy.NONE);
CREATE_VOLTAGE_LEVEL_SECTION(PreloadingStrategy.NONE),
MOVE_VOLTAGE_LEVEL_FEEDER_BAYS(PreloadingStrategy.NONE);

private final PreloadingStrategy strategy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ public enum Type {
MODIFY_VOLTAGE_LEVEL_TOPOLOGY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),
CREATE_COUPLING_DEVICE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),
CREATE_VOLTAGE_LEVEL_TOPOLOGY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),
CREATE_VOLTAGE_LEVEL_SECTION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR);
CREATE_VOLTAGE_LEVEL_SECTION_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),
MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),;

public final HttpStatus status;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2025, 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 org.gridsuite.modification.dto;

import com.powsybl.iidm.network.extensions.ConnectablePosition;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.SuperBuilder;

/**
* @author Etienne Lesot <etienne.lesot at rte-france.com>
*/
@SuperBuilder
@NoArgsConstructor
@Getter
@Setter
@ToString(callSuper = true)
@Schema(description = "Connectable position modification")
public class ConnectablePositionModificationInfos {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public class ConnectablePositionModificationInfos {
public class MoveFeederBayInfos {

@Schema(description = "Connectable id")
private String equipmentId;

@Schema(description = "busbar section id")
private String busbarSectionId;

@Schema(description = "connection side")
private String connectionSide;

@Schema(description = "connection position")
private Integer connectionPosition;

@Schema(description = "connection name")
private String connectionName;

@Schema(description = "connection direction")
private ConnectablePosition.Direction connectionDirection;
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
@JsonSubTypes.Type(value = CreateVoltageLevelTopologyInfos.class),
@JsonSubTypes.Type(value = LimitSetsTabularModificationInfos.class),
@JsonSubTypes.Type(value = CreateVoltageLevelSectionInfos.class),
@JsonSubTypes.Type(value = MoveVoltageLevelFeederBaysInfos.class),
})
@SuperBuilder
@NoArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Copyright (c) 2025, 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 org.gridsuite.modification.dto;

import com.fasterxml.jackson.annotation.JsonTypeName;
import com.powsybl.commons.report.ReportNode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import org.gridsuite.modification.dto.annotation.ModificationErrorTypeName;
import org.gridsuite.modification.modifications.AbstractModification;
import org.gridsuite.modification.modifications.MoveVoltageLevelFeederBays;

import java.util.List;
import java.util.Map;

/**
* @author Etienne Lesot <etienne.lesot at rte-france.com>
*/
@SuperBuilder
@NoArgsConstructor
@Getter
@Setter
@Schema(description = "Move voltage level feeder bays")
@JsonTypeName("MOVE_VOLTAGE_LEVEL_FEEDER_BAYS")
@ModificationErrorTypeName("MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR")
public class MoveVoltageLevelFeederBaysInfos extends ModificationInfos {

@Schema(description = "Voltage level id")
private String voltageLevelId;

@Schema(description = "Feeder bays list")
private List<ConnectablePositionModificationInfos> feederBaysAttributeList;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private List<ConnectablePositionModificationInfos> feederBaysAttributeList;
private List<ConnectablePositionModificationInfos> feederBays;


@Override
public AbstractModification toModification() {
return new MoveVoltageLevelFeederBays(this);
}

@Override
public ReportNode createSubReportNode(ReportNode reportNode) {
return reportNode.newReportNode()
.withMessageTemplate("network.modification.MOVE_VOLTAGE_LEVEL_FEEDER_BAYS")
.withUntypedValue("voltageLevelId", getVoltageLevelId())
.add();
}

@Override
public Map<String, String> getMapMessageValues() {
return Map.of("voltageLevelId", getVoltageLevelId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,83 @@

import com.powsybl.iidm.network.IdentifiableType;
import com.powsybl.iidm.network.Switch;
import com.powsybl.iidm.network.SwitchKind;
import com.powsybl.iidm.network.Terminal;
import com.powsybl.math.graph.TraverseResult;
import lombok.Getter;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* @author Slimane Amar <slimane.amar at rte-france.com>
*/
// FIXME : to remove when this class is available in network-store
public class BusbarSectionFinderTraverser implements Terminal.TopologyTraverser {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move those changes in another PR. So the review can be done with gridsuite/network-map-server#288. And the changes in the tests will be reviewed with it


private final boolean onlyConnectedBbs;

private String firstTraversedBbsId;

public BusbarSectionFinderTraverser(boolean onlyConnectedBbs) {
this.onlyConnectedBbs = onlyConnectedBbs;
}
private final List<BusbarCandidate> busbarCandidates = new ArrayList<>();
private final Set<String> visitedTerminals = new HashSet<>();
private static final int MAX_VISITED = 50;

@Override
public TraverseResult traverse(Terminal terminal, boolean connected) {
if (terminal.getConnectable().getType() == IdentifiableType.BUSBAR_SECTION) {
firstTraversedBbsId = terminal.getConnectable().getId();
String terminalId = terminal.getConnectable().getId();
if (visitedTerminals.contains(terminalId)) {
return TraverseResult.TERMINATE_PATH;
}
visitedTerminals.add(terminalId);
if (visitedTerminals.size() > MAX_VISITED) {
return TraverseResult.TERMINATE_TRAVERSER;
}

// If a busbar section is found, add it as a candidate
if (terminal.getConnectable().getType() == IdentifiableType.BUSBAR_SECTION) {
busbarCandidates.add(new BusbarCandidate(terminalId, connected));
// CONTINUE to explore other paths to other busbars
return TraverseResult.CONTINUE;
}
return TraverseResult.CONTINUE;
}

@Override
public TraverseResult traverse(Switch aSwitch) {
if (onlyConnectedBbs && aSwitch.isOpen()) {
return TraverseResult.TERMINATE_PATH;
if (visitedTerminals.size() > MAX_VISITED) {
return TraverseResult.TERMINATE_TRAVERSER;
}

// KEY: Open disconnectors end this path but not the overall traversal
// They block access to this busbar but not to the others
if (aSwitch.isOpen() && aSwitch.getKind() == SwitchKind.DISCONNECTOR) {
return TraverseResult.TERMINATE_PATH; // Ends this path, not the whole traversal
}
return TraverseResult.CONTINUE;
}

public String getFirstTraversedBbsId() {
return firstTraversedBbsId;
public String getBusbarWithClosedDisconnector() {
// Search for a connected busbar (disconnector closed)
for (BusbarCandidate candidate : busbarCandidates) {
if (candidate.isConnected()) {
return candidate.getId();
}
}

// If none is connected, return the first one found (fallback)
if (!busbarCandidates.isEmpty()) {
return busbarCandidates.getFirst().getId();
}
return null;
}
}

@Getter
private static class BusbarCandidate {
private final String id;
private final boolean connected;

public BusbarCandidate(String id, boolean connected) {
this.id = id;
this.connected = connected;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/**
* Copyright (c) 2025, 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 org.gridsuite.modification.modifications;

import com.powsybl.commons.report.ReportNode;
import com.powsybl.iidm.network.*;
import com.powsybl.iidm.network.extensions.ConnectablePosition;
import com.powsybl.iidm.network.extensions.ConnectablePositionAdder;
import org.gridsuite.modification.ModificationType;
import org.gridsuite.modification.NetworkModificationException;
import org.gridsuite.modification.dto.*;
import org.gridsuite.modification.utils.ModificationUtils;

import static org.gridsuite.modification.NetworkModificationException.Type.MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR;

/**
* @author Etienne Lesot <etienne.lesot at rte-france.com>
*/
public class MoveVoltageLevelFeederBays extends AbstractModification {
private static final String VOLTAGE_LEVEL_NOT_FOUND = "Voltage level %s is not found";
private static final String CONNECTABLE_NOT_FOUND = "Connectable %s not found";
private static final String BUSBAR_NOT_FOUND = "Bus or busbar section %s where connectable %s is supposed to be is not found in voltage level %s";
private static final String UNSUPPORTED_CONNECTABLE = "ConnectablePositionModification is not implemented for %s";
private static final String INVALID_CONNECTION_SIDE = "Invalid connection side: %s for branch %s";

private final MoveVoltageLevelFeederBaysInfos modificationInfos;

public MoveVoltageLevelFeederBays(MoveVoltageLevelFeederBaysInfos modificationInfos) {
this.modificationInfos = modificationInfos;
}

@Override
public void check(Network network) throws NetworkModificationException {
VoltageLevel voltageLevel = checkVoltageLevelOrThrow(network, modificationInfos.getVoltageLevelId());
for (ConnectablePositionModificationInfos info : modificationInfos.getFeederBaysAttributeList()) {
checkBusOrBusbarSection(voltageLevel, info);
checkConnectable(network, info);
}
}

private VoltageLevel checkVoltageLevelOrThrow(Network network, String voltageLevelId) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private VoltageLevel checkVoltageLevelOrThrow(Network network, String voltageLevelId) {
private VoltageLevel getVoltageLevelOrThrow(Network network, String voltageLevelId) {

VoltageLevel voltageLevel = network.getVoltageLevel(voltageLevelId);
if (voltageLevel == null) {
throw new NetworkModificationException(MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR, String.format(VOLTAGE_LEVEL_NOT_FOUND, voltageLevelId));
}
return voltageLevel;
}

private void checkBusOrBusbarSection(VoltageLevel voltageLevel, ConnectablePositionModificationInfos info) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private void checkBusOrBusbarSection(VoltageLevel voltageLevel, ConnectablePositionModificationInfos info) {
private void checkBusOrBusbarSectionExist(VoltageLevel voltageLevel, ConnectablePositionModificationInfos info) {

boolean busOrBusbarSectionExists = voltageLevel.getTopologyKind().equals(TopologyKind.NODE_BREAKER)
? voltageLevel.getNodeBreakerView().getBusbarSection(info.getBusbarSectionId()) != null
: voltageLevel.getBusBreakerView().getBus(info.getBusbarSectionId()) != null;
if (!busOrBusbarSectionExists) {
throw new NetworkModificationException(MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR, String.format(BUSBAR_NOT_FOUND,
info.getBusbarSectionId(), info.getEquipmentId(), modificationInfos.getVoltageLevelId()));
}
}

private void checkConnectable(Network network, ConnectablePositionModificationInfos info) {
Connectable<?> connectable = network.getConnectable(info.getEquipmentId());
if (connectable == null) {
throw new NetworkModificationException(MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR, String.format(CONNECTABLE_NOT_FOUND, info.getEquipmentId()));
}
if (!(connectable instanceof Injection<?>) && !(connectable instanceof Branch<?>)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it excudes 3WT

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should rather exclude busbarSection as it is done in MoveFeederBay. Something like :
(connectable instanceof BusbarSection) throw ...

throw new NetworkModificationException(MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR, String.format(UNSUPPORTED_CONNECTABLE, connectable.getClass()));
}
}

@Override
public void apply(Network network, ReportNode subReportNode) {
for (ConnectablePositionModificationInfos info : modificationInfos.getFeederBaysAttributeList()) {
Connectable<?> connectable = network.getConnectable(info.getEquipmentId());
switch (connectable) {
case Injection<?> injection -> modifyInjectionConnectablePosition(network, injection, info, subReportNode);
case Branch<?> branch -> modifyBranchConnectablePosition(network, branch, info, subReportNode);
default -> throw new NetworkModificationException(MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR, String.format(UNSUPPORTED_CONNECTABLE, connectable.getClass()));
}
}
}

@Override
public String getName() {
return ModificationType.MOVE_VOLTAGE_LEVEL_FEEDER_BAYS.name();
}

private void modifyInjectionConnectablePosition(Network network, Injection<?> injection, ConnectablePositionModificationInfos info, ReportNode subReportNode) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modifyInjectionConnectablePosition and modifyBranchConnectablePosition should be one method modifyConnectablePosition().
With a if else for modifyInjectionConnectivityAttributes() and modifyBranchConnectivityAttributes()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private void modifyInjectionConnectablePosition(Network network, Injection<?> injection, ConnectablePositionModificationInfos info, ReportNode subReportNode) {
private void modifyInjectionConnectablePosition(Network network, Injection<?> injection, ConnectablePositionModificationInfos newConnectablePositionInfos, ReportNode subReportNode) {

ConnectablePosition connectablePosition = injection.getExtension(ConnectablePosition.class);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ConnectablePosition connectablePosition = injection.getExtension(ConnectablePosition.class);
ConnectablePosition oldConnectablePosition = injection.getExtension(ConnectablePosition.class);

ConnectablePositionAdder connectablePositionAdder = injection.newExtension(ConnectablePositionAdder.class);
InjectionModificationInfos injectionModificationInfos = buildInjectionModificationInfos(info);
ModificationUtils.getInstance().modifyInjectionConnectivityAttributes(connectablePosition, connectablePositionAdder, injection, injectionModificationInfos, subReportNode);
moveVoltageLevelBusOrBusbarSection(network, injection, info, subReportNode);
}

private void modifyBranchConnectablePosition(Network network, Branch<?> branch, ConnectablePositionModificationInfos info, ReportNode subReportNode) {
ConnectablePosition connectablePosition = branch.getExtension(ConnectablePosition.class);
ConnectablePositionAdder connectablePositionAdder = branch.newExtension(ConnectablePositionAdder.class);
BranchModificationInfos branchModificationInfos = buildBranchModificationInfos(info);
ModificationUtils.getInstance().modifyBranchConnectivityAttributes(connectablePosition, connectablePositionAdder, branch, branchModificationInfos, subReportNode);
moveVoltageLevelBusOrBusbarSection(network, (Connectable<?>) branch, info, subReportNode);
}

private InjectionModificationInfos buildInjectionModificationInfos(ConnectablePositionModificationInfos info) {
InjectionModificationInfos injectionInfos = new InjectionModificationInfos();
injectionInfos.setEquipmentId(info.getEquipmentId());
injectionInfos.setConnectionPosition(new AttributeModification<>(info.getConnectionPosition(), OperationType.SET));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those 3 rows can be a method. And the method used here and in buildBranchModificationInfos()

injectionInfos.setConnectionName(new AttributeModification<>(info.getConnectionName(), OperationType.SET));
injectionInfos.setConnectionDirection(new AttributeModification<>(info.getConnectionDirection(), OperationType.SET));
return injectionInfos;
}

private BranchModificationInfos buildBranchModificationInfos(ConnectablePositionModificationInfos info) {
BranchModificationInfos branchInfos = new BranchModificationInfos();
branchInfos.setEquipmentId(info.getEquipmentId());

ThreeSides connectionSide = ThreeSides.valueOf(info.getConnectionSide());
switch (connectionSide) {
case ONE -> {
branchInfos.setConnectionPosition1(new AttributeModification<>(info.getConnectionPosition(), OperationType.SET));
branchInfos.setConnectionName1(new AttributeModification<>(info.getConnectionName(), OperationType.SET));
branchInfos.setConnectionDirection1(new AttributeModification<>(info.getConnectionDirection(), OperationType.SET));
}
case TWO -> {
branchInfos.setConnectionPosition2(new AttributeModification<>(info.getConnectionPosition(), OperationType.SET));
branchInfos.setConnectionName2(new AttributeModification<>(info.getConnectionName(), OperationType.SET));
branchInfos.setConnectionDirection2(new AttributeModification<>(info.getConnectionDirection(), OperationType.SET));
}
default -> throw new NetworkModificationException(MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR, String.format(INVALID_CONNECTION_SIDE, info.getConnectionSide(), branchInfos.getEquipmentId()));
}
return branchInfos;
}

private void moveVoltageLevelBusOrBusbarSection(Network network, Connectable<?> connectable, ConnectablePositionModificationInfos info, ReportNode subReportNode) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should be named moveFeederBay(). And also modifyVoltageLevelBusOrBusBarSectionAttributes() should be named moveFeederBay(). As the only thing they do is apply moveFeederBay() modfication from powsybl-core.

Terminal terminal = getTerminal(network, info);
String currentBusbarId = ModificationUtils.getInstance().getBusOrBusbarSection(terminal);
String targetBusbarId = info.getBusbarSectionId();
if (!currentBusbarId.equals(targetBusbarId)) {
ModificationUtils.getInstance().modifyVoltageLevelBusOrBusBarSectionAttributes(
connectable, terminal,
new AttributeModification<>(modificationInfos.getVoltageLevelId(), OperationType.SET),
new AttributeModification<>(targetBusbarId, OperationType.SET),
subReportNode);
}
}

public Terminal getTerminal(Network network, ConnectablePositionModificationInfos info) {
Connectable<?> connectable = network.getConnectable(info.getEquipmentId());
return switch (connectable) {
case Injection<?> injection -> injection.getTerminal();
case Branch<?> branch -> getTerminalFromBranch(branch, info);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
case Branch<?> branch -> getTerminalFromBranch(branch, info);
case Branch<?> branch -> branch.getTerminal(info.getConnectionSide());

default -> throw new NetworkModificationException(MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR, String.format(UNSUPPORTED_CONNECTABLE, connectable.getClass()));
};
}

private Terminal getTerminalFromBranch(Branch<?> branch, ConnectablePositionModificationInfos info) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove method

return switch (ThreeSides.valueOf(info.getConnectionSide())) {
case ONE -> branch.getTerminal1();
case TWO -> branch.getTerminal2();
default -> throw new NetworkModificationException(MOVE_VOLTAGE_LEVEL_FEEDER_BAYS_ERROR, String.format(INVALID_CONNECTION_SIDE, info.getConnectionSide(), branch.getId()));
};
}
}
Loading