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

More robust UCTE naming strategy #3206

Merged
merged 41 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d9b20e7
add xiidm to ucte element's id conversion
clementleclercRTE Oct 23, 2024
4d9e59d
add unique NodeId
clementleclercRTE Oct 23, 2024
7a1e787
add unique NodeId
clementleclercRTE Oct 23, 2024
0d55a0b
add unique NodeId
clementleclercRTE Oct 23, 2024
d5d9613
change getVoltageLevelCode method
clementleclercRTE Oct 28, 2024
97b572a
optimisation of checking elements
clementleclercRTE Oct 29, 2024
6a15014
create CounterNamingStrategy
clementleclercRTE Nov 12, 2024
1ce1b17
create CounterNamingStrategy
clementleclercRTE Nov 12, 2024
dadc852
change nodeCounter incrementation loop
clementleclercRTE Nov 12, 2024
f0bf333
change ElementId orderCode incrementation
clementleclercRTE Nov 12, 2024
74ad500
change UtceExporter exception
clementleclercRTE Nov 13, 2024
92ab8f9
change
clementleclercRTE Nov 13, 2024
b8f8027
add LOGGER and update with change request
clementleclercRTE Nov 18, 2024
1dec470
change dangling lines code generation
clementleclercRTE Nov 18, 2024
cd42dcd
change nodeID generation
clementleclercRTE Nov 18, 2024
609bece
delete LOGGER
clementleclercRTE Nov 19, 2024
8f159d3
code optimisation
leclerc92 Nov 21, 2024
d7f076e
add CounterNamingStrategyTest
leclerc92 Nov 21, 2024
82783ef
code review
clementleclercRTE Nov 25, 2024
4c9e246
changer danglingline getBusBreakerView()
clementleclercRTE Nov 25, 2024
bbfbee3
inverse node order for Transformaters
clementleclercRTE Nov 25, 2024
61995d8
change incrementation of namingcounter
clementleclercRTE Nov 26, 2024
45ed401
retry change incrementation of namingcounter
clementleclercRTE Nov 26, 2024
84cf3c4
change implementation
clementleclercRTE Nov 26, 2024
e618277
change OrderCode list
clementleclercRTE Dec 2, 2024
20e844c
change OrderCode list
clementleclercRTE Dec 2, 2024
9762869
change DanlingLine conversion
clementleclercRTE Dec 2, 2024
e2d11b7
add orderCode getter
clementleclercRTE Dec 2, 2024
5c20757
delete system.out
clementleclercRTE Dec 3, 2024
696ca51
delete system.out
clementleclercRTE Dec 3, 2024
f2ea4e3
Merge branch 'main' into 3191-conversion-to-ucte-format
geofjamg Dec 3, 2024
b29e7b1
update CounterNamingStategyTest
clementleclercRTE Dec 3, 2024
bfe1509
Merge remote-tracking branch 'origin/3191-conversion-to-ucte-format' …
clementleclercRTE Dec 3, 2024
5b63d1e
update danglingline xnodeCode generation
clementleclercRTE Dec 3, 2024
7c480e7
update test
clementleclercRTE Dec 3, 2024
ed79865
create fromVoltageLevel in UcteCountryCode
clementleclercRTE Dec 3, 2024
71046bb
update test
clementleclercRTE Dec 9, 2024
444b327
add Exception for null equipement
clementleclercRTE Dec 9, 2024
1b686ae
add Exception for null equipment
clementleclercRTE Dec 9, 2024
930add0
fix checkstyle error
clementleclercRTE Dec 9, 2024
7e109f4
Merge branch 'main' into 3191-conversion-to-ucte-format
olperr1 Dec 16, 2024
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
@@ -0,0 +1,358 @@
/**
* Copyright (c) 2019, RTE (http://www.rte-france.com)
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
* 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.ucte.converter;
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

import com.google.auto.service.AutoService;
import com.powsybl.iidm.network.*;
import com.powsybl.ucte.network.UcteCountryCode;
import com.powsybl.ucte.network.UcteElementId;
import com.powsybl.ucte.network.UcteNodeCode;
import com.powsybl.ucte.network.UcteVoltageLevelCode;

import java.util.*;


/**
* A {@link NamingStrategy} implementation that ensures the conformity of IDs with the UCTE-DEF format
*
* @author Clément LECLERC {@literal <[email protected]>}
*/
@AutoService(NamingStrategy.class)
public class CounterNamingStrategy implements NamingStrategy {
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

private final Map<String, UcteNodeCode> ucteNodeIds = new HashMap<>();
private final Map<String, UcteElementId> ucteElementIds = new HashMap<>();
private int namingCounter;
private final List<Character> oderCode = Arrays.asList('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '-', '.', ' ');
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

@Override
public String getName() {
return "Counter";
}

/**
* Converts all network elements to UCTE format using a sequential naming scheme.
* The conversion process follows these steps:
* <ol>
* <li>Reset the global naming counter to ensure unique identifiers</li>
* <li>Convert all elements within each voltage level
* <li>Convert network-level elements
* </ol>
*
* Each element receives a unique sequential identifier during the conversion process.
*
* @param network the network to convert
*/
@Override
public void initialiseNetwork(Network network) {
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
namingCounter = 0;
network.getSubstations().forEach(substation -> substation.getVoltageLevels().forEach(voltageLevel -> {

System.out.println(voltageLevel.getId());
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
voltageLevel.getBusBreakerView().getBuses().forEach(bus -> {
generateUcteNodeId(bus.getId(), voltageLevel);
logConversion("BUS", bus);
});
voltageLevel.getBusBreakerView().getSwitches().forEach(sw -> {
if (sw.getKind().equals(SwitchKind.BREAKER)) {
generateUcteElementId(sw);
} else {
generateUcteNodeId(sw.getId(), voltageLevel);
}
logConversion("SW", sw);
});
}));

network.getBranchStream().forEach(branch -> {
generateUcteElementId(branch);
logConversion(branch.getType().name(), branch);
});

network.getDanglingLineStream().forEach(d -> {
generateUcteNodeId(d);
generateUcteElementId(d);
logConversion(d.getType().name(), d);
});

}

private void logConversion(String elementType, Identifiable element) {
System.out.println(String.format("%s : %s--> %s %s",
elementType,
element.getId(),
ucteNodeIds.get(element.getId()),
ucteElementIds.get(element.getId())
));
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

}

/**
* Generates a UCTE node code for a network element.
* The generation process involves:
* <ol>
* <li>Checking for existing node ID to avoid duplicates</li>
* <li>Extracting country code from the voltage level</li>
* <li>Determining voltage level code based on nominal voltage</li>
* <li>Creating a new node ID using namingCounter</li>
* </ol>
*
* The generated UCTE node code follows the format: [CountryCode][NodeId][VoltageLevelCode]
*
* @param nodeId original identifier of the network element
* @param voltageLevel voltage level containing the element
* @return generated UcteNodeCode, or null if nodeId already exists in ucteNodeIds
*/
private UcteNodeCode generateUcteNodeId(String nodeId, VoltageLevel voltageLevel) {

if (ucteNodeIds.containsKey(nodeId)) {
return ucteNodeIds.get(nodeId);
}
if (namingCounter > 9999) {
namingCounter = 0;
}

StringBuilder newNodeCode = new StringBuilder(8);
String newNodeId = String.format("%05d", ++namingCounter);
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
char countryCode = getCountryCode(voltageLevel);
char voltageLevelCode = getUcteVoltageLevelCode(voltageLevel.getNominalV());
char orderCode = oderCode.get(0);
int i = 0;

newNodeCode
.append(countryCode)
.append(newNodeId)
.append(voltageLevelCode)
.append(orderCode);
while (ucteNodeIds.containsKey(newNodeId)) {
i++;
orderCode = oderCode.get(i);
newNodeCode.replace(7, 7, String.valueOf(orderCode));
}
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

Optional<UcteNodeCode> nodeCode = UcteNodeCode.parseUcteNodeCode(newNodeCode.toString());
if (!nodeCode.isPresent()) {
System.out.println("le code en erreur est : " + newNodeCode);
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
throw new IllegalArgumentException("Invalid node code: " + newNodeCode);
}
ucteNodeIds.put(nodeId, nodeCode.get());
return nodeCode.get();
}

private UcteNodeCode generateUcteNodeId(DanglingLine danglingLine) {

String nodeId = danglingLine.getPairingKey();

if (ucteNodeIds.containsKey(nodeId)) {
return ucteNodeIds.get(nodeId);
}

if (namingCounter > 9999) {
namingCounter = 0;
}

StringBuilder newNodeCode = new StringBuilder(8);
String newNodeId = String.format("%05d", ++namingCounter);
char voltageLevelCode = getUcteVoltageLevelCode(danglingLine.getTerminal().getVoltageLevel().getNominalV());

newNodeCode
.append('X')
.append(newNodeId)
.append(voltageLevelCode)
.append('0');

int i = 0;
while (ucteNodeIds.containsKey(newNodeId)) {
i++;
if (i >= oderCode.size()) {
throw new UcteException("Too many nodes with same prefix");
}
newNodeCode.setCharAt(7, oderCode.get(i));
}

Optional<UcteNodeCode> nodeCode = UcteNodeCode.parseUcteNodeCode(newNodeCode.toString());
if (!nodeCode.isPresent()) {
throw new IllegalArgumentException("Invalid node code: " + newNodeCode);
}

ucteNodeIds.put(nodeId, nodeCode.get());
return nodeCode.get();
}

private UcteElementId generateUcteElementId(String id, UcteNodeCode node1, UcteNodeCode node2) {

if (ucteElementIds.containsKey(id)) {
return ucteElementIds.get(id);
}
int i = 0;

char orderCode = oderCode.get(0);
UcteElementId ucteElementId = new UcteElementId(node1, node2, orderCode);

while (ucteElementIds.containsValue(ucteElementId)) {
i++;
if (i < oderCode.size()) {
orderCode = oderCode.get(i);
}
ucteElementId = new UcteElementId(node1, node2, orderCode);
}

ucteElementIds.put(id, ucteElementId);
return ucteElementId;
}

private UcteElementId generateUcteElementId(Branch<?> branch) {
if (ucteElementIds.containsKey(branch.getId())) {
return ucteElementIds.get(branch.getId());
}
UcteNodeCode node1 = this.ucteNodeIds.get(branch.getTerminal1().getBusBreakerView().getBus().getId());
UcteNodeCode node2 = this.ucteNodeIds.get(branch.getTerminal2().getBusBreakerView().getBus().getId());

return generateUcteElementId(branch.getId(), node1, node2);
}

private UcteElementId generateUcteElementId(DanglingLine danglingLine) {
if (ucteElementIds.containsKey(danglingLine.getId())) {
return ucteElementIds.get(danglingLine.getId());
}

for (Terminal t : danglingLine.getTerminals()) {
System.out.println(t.getBusBreakerView().getBus().getId());
System.out.println(danglingLine.getPairingKey());
}
UcteNodeCode node1 = this.ucteNodeIds.get(danglingLine.getTerminal().getBusBreakerView().getBus().getId());
UcteNodeCode node2 = this.ucteNodeIds.get(danglingLine.getPairingKey());

return generateUcteElementId(danglingLine.getId(), node1, node2);
}

private UcteElementId generateUcteElementId(Switch sw) {

if (ucteElementIds.containsKey(sw.getId())) {
return ucteElementIds.get(sw.getId());
}

UcteNodeCode node1 = generateUcteNodeId(sw.getId(), sw.getVoltageLevel());
UcteNodeCode node2 = generateUcteNodeId(sw.getId(), sw.getVoltageLevel());
return generateUcteElementId(sw.getId(), node1, node2);
}

/**
* Extracts and converts a country code to UCTE format for a given voltage level.
*
* <p>The country code is determined through the following process:
* <ol>
* <li>Attempts to get the country from the voltage level's substation</li>
* <li>Maps the country name to its corresponding UCTE code</li>
* <li>Returns 'X' as fallback if no valid mapping is found</li>
* </ol>
*
* @param voltageLevel the voltage level from which to extract the country code
* @return the UCTE country code character, or 'X' if no valid country is found)
*/
private static char getCountryCode(VoltageLevel voltageLevel) {
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

String country = voltageLevel.getSubstation()
.map(s -> s.getCountry()
.map(Country::getName)
.orElse("XX"))
.orElse("XX");

for (UcteCountryCode countryCode : UcteCountryCode.values()) {
if (country.equalsIgnoreCase(countryCode.getPrettyName())) {
return countryCode.getUcteCode();
}
}
return 'X';
}

/**
* Determines the UCTE voltage level code for a given voltage value.
* The code is determined by finding the closest standard UCTE voltage level.
*
* <p>The method follows these rules:
* <ul>
* <li>For voltage < 27kV: returns '7'</li>
* <li>For voltage > 750kV: returns '0'</li>
* <li>For other voltages: returns code of the closest standard UCTE level</li>
* </ul>
*
* @param voltage the nominal voltage value in kV
* @return a character representing the UCTE voltage level code
*/
private static char getUcteVoltageLevelCode(double voltage) {

if (voltage < UcteVoltageLevelCode.VL_27.getVoltageLevel()) {
return '7';
}
if (voltage > UcteVoltageLevelCode.VL_750.getVoltageLevel()) {
return '0';
}

UcteVoltageLevelCode closest = null;
double minDiff = Double.MAX_VALUE;

for (UcteVoltageLevelCode code : UcteVoltageLevelCode.values()) {
double diff = Math.abs(voltage - code.getVoltageLevel());
if (diff < minDiff) {
minDiff = diff;
closest = code;
}
}
return closest != null ? (char) ('0' + closest.ordinal()) : '7';
}

@Override
public UcteNodeCode getUcteNodeCode(String id) {
if (id == null) {
return null;
}

UcteNodeCode code = ucteNodeIds.get(id);
if (code == null) {
throw new UcteException("No UCTE code found for id: " + id);
}
return code;
}

@Override
public UcteNodeCode getUcteNodeCode(Bus bus) {
return getUcteNodeCode(bus.getId());
}

@Override
public UcteNodeCode getUcteNodeCode(DanglingLine danglingLine) {
return getUcteNodeCode(danglingLine.getPairingKey());
}

@Override
public UcteElementId getUcteElementId(String id) {
UcteElementId elementId = ucteElementIds.get(id);
if (elementId == null) {
throw new UcteException("No UCTE element id found for: " + id);
}
return elementId;
}

@Override
public UcteElementId getUcteElementId(Switch sw) {
return getUcteElementId(sw.getId());
}

@Override
public UcteElementId getUcteElementId(Branch branch) {
return getUcteElementId(branch.getId());
}

@Override
public UcteElementId getUcteElementId(DanglingLine danglingLine) {
return getUcteElementId(danglingLine.getId());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public String getName() {
return "Default";
}

@Override
public void initialiseNetwork(Network network) {
olperr1 marked this conversation as resolved.
Show resolved Hide resolved

}

@Override
public UcteNodeCode getUcteNodeCode(String id) {
return ucteNodeIds.computeIfAbsent(id, k -> UcteNodeCode.parseUcteNodeCode(k).orElseThrow(() -> new UcteException("Invalid UCTE node identifier: " + k)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public interface NamingStrategy {

String getName();

void initialiseNetwork(Network network);

UcteNodeCode getUcteNodeCode(String id);

UcteNodeCode getUcteNodeCode(Bus bus);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,13 @@ private static UcteNetwork createUcteNetwork(Network network, NamingStrategy nam
network.getHvdcLineCount() > 0 ||
network.getThreeWindingsTransformerCount() > 0) {

throw new UcteException("This network contains unsupported equipments");
//throw new UcteException("This network contains unsupported equipments");
olperr1 marked this conversation as resolved.
Show resolved Hide resolved
}

UcteExporterContext context = new UcteExporterContext(namingStrategy, combinePhaseAngleRegulation);

namingStrategy.initialiseNetwork(network);
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

UcteNetwork ucteNetwork = new UcteNetworkImpl();
ucteNetwork.setVersion(UcteFormatVersion.SECOND);

Expand Down
Loading