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 18 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,232 @@
package com.powsybl.ucte.converter;
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

import com.google.auto.service.AutoService;
import com.powsybl.commons.PowsyblException;
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.*;

@AutoService(NamingStrategy.class)
public class CounterNamingStrategy implements NamingStrategy {
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
private static final List<Character> ORDER_CODES = List.of('1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '-', '.', ' ');
private static final String NO_COUNTRY_ERROR = "No country for this substation";
private static final String NO_UCTE_CODE_ERROR = "No UCTE code found for id: %s";
private static final String NO_UCTE_COUNTRY_ERROR = "No UCTE country code for %s";
private static final String INVALID_NODE_CODE_ERROR = "Invalid ucte node code: %s";
private static final char DEFAULT_VOLTAGE_CODE = '7';
private static final int MAX_COUNTER = 99999;
private static final int BASE_LETTER_OFFSET = 100000;
private static final int MODULO_VALUE = 10000;

private final Map<String, UcteNodeCode> ucteNodeIds = new HashMap<>();
private final Map<String, UcteElementId> ucteElementIds = new HashMap<>();
private int namingCounter;

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

@Override
public void initialiseNetwork(Network network) {
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
namingCounter = 0;
network.getSubstationStream()
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
.flatMap(s -> s.getVoltageLevelStream())
.forEach(this::processVoltageLevel);

network.getBranchStream().forEach(this::generateUcteElementId);
network.getDanglingLineStream().forEach(this::generateUcteElementId);
}

private void processVoltageLevel(VoltageLevel voltageLevel) {
voltageLevel.getBusBreakerView().getBuses()
.forEach(bus -> generateUcteNodeId(bus.getId(), voltageLevel));
voltageLevel.getBusBreakerView().getSwitches()
.forEach(this::generateUcteElementId);
}

private UcteNodeCode generateUcteNodeId(String nodeId, VoltageLevel voltageLevel) {
if (UcteNodeCode.isUcteNodeId(nodeId)) {
return processExistingUcteNodeId(nodeId);
}
if (ucteNodeIds.containsKey(nodeId)) {
return ucteNodeIds.get(nodeId);
}
return createNewUcteNodeId(nodeId, voltageLevel);
}

private UcteNodeCode processExistingUcteNodeId(String nodeId) {
return UcteNodeCode.parseUcteNodeCode(nodeId)
.map(code -> {
ucteNodeIds.put(nodeId, code);
return code;
})
.orElseThrow(() -> new UcteException(String.format(INVALID_NODE_CODE_ERROR, nodeId)));
}

private UcteNodeCode createNewUcteNodeId(String nodeId, VoltageLevel voltageLevel) {
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved
StringBuilder newNodeCode = new StringBuilder(8);
String newNodeId = generateIDFromCounter();
char countryCode = getCountryCode(voltageLevel).getUcteCode();
char voltageLevelCode = getUcteVoltageLevelCode(voltageLevel.getNominalV());

return generateUniqueNodeCode(nodeId, newNodeCode, newNodeId, countryCode, voltageLevelCode);
}

private UcteNodeCode generateUniqueNodeCode(String nodeId, StringBuilder newNodeCode, String newNodeId,
char countryCode, char voltageLevelCode) {
for (Character orderCode : ORDER_CODES) {
newNodeCode.setLength(0);
newNodeCode.append(countryCode)
.append(newNodeId)
.append(voltageLevelCode)
.append(orderCode);

Optional<UcteNodeCode> nodeCode = UcteNodeCode.parseUcteNodeCode(newNodeCode.toString());
if (nodeCode.isPresent() && !ucteNodeIds.containsValue(nodeCode.get())) {
ucteNodeIds.put(nodeId, nodeCode.get());
return nodeCode.get();
}
}
throw new UcteException("Unable to generate unique node code");
}

private String generateIDFromCounter() {
namingCounter++;
if (namingCounter > MAX_COUNTER) {
Copy link
Member

Choose a reason for hiding this comment

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

For a first version we can do simpler and use only digits and throw an exception if > 99999

int baseNumber = (namingCounter - BASE_LETTER_OFFSET) % MODULO_VALUE;
char letter = (char) ('A' + ((namingCounter - BASE_LETTER_OFFSET) / MODULO_VALUE));
return String.format("%04d%c", baseNumber, letter);
}
return String.format("%05d", namingCounter);
}

private static char getUcteVoltageLevelCode(double voltage) {
Copy link
Member

Choose a reason for hiding this comment

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

To move as a utility class in UcteVoltageLevelCode

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

return Arrays.stream(UcteVoltageLevelCode.values())
.min(Comparator.comparingDouble(code ->
Math.abs(voltage - code.getVoltageLevel())))
.map(code -> (char) ('0' + code.ordinal()))
.orElse(DEFAULT_VOLTAGE_CODE);
}

private UcteCountryCode getCountryCode(VoltageLevel voltageLevel) {
Country country = voltageLevel.getSubstation()
.flatMap(Substation::getCountry)
.orElseThrow(() -> new UcteException(NO_COUNTRY_ERROR));

try {
return UcteCountryCode.valueOf(country.name());
} catch (IllegalArgumentException e) {
throw new UcteException(String.format(NO_UCTE_COUNTRY_ERROR, country.getName()));
}
}

private UcteElementId generateUcteElementId(String id, UcteNodeCode node1, UcteNodeCode node2) {
if (ucteElementIds.containsKey(id)) {
return ucteElementIds.get(id);
}

return ORDER_CODES.stream()
.map(orderCode -> new UcteElementId(node1, node2, orderCode))
.filter(elementId -> !ucteElementIds.containsValue(elementId))
.findFirst()
.map(elementId -> {
ucteElementIds.put(id, elementId);
return elementId;
})
.orElseThrow(() -> new UcteException("Unable to generate unique element ID"));
}

private UcteElementId generateUcteElementId(Branch<?> branch) {
if (ucteElementIds.containsKey(branch.getId())) {
return ucteElementIds.get(branch.getId());
}
UcteNodeCode node1 = ucteNodeIds.get(branch.getTerminal1().getBusBreakerView().getBus().getId());
UcteNodeCode node2 = 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());
}

Bus bus = danglingLine.getTerminal().getBusBreakerView().getBus();
VoltageLevel voltageLevel = danglingLine.getTerminal().getVoltageLevel();

generateUcteNodeId(danglingLine.getPairingKey(), voltageLevel);
olperr1 marked this conversation as resolved.
Show resolved Hide resolved
generateUcteNodeId(bus.getId(), voltageLevel);

UcteNodeCode node1 = ucteNodeIds.get(danglingLine.getPairingKey());
UcteNodeCode node2 = ucteNodeIds.get(bus.getId());

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

private UcteElementId generateUcteElementId(Switch sw) {
if (ucteElementIds.containsKey(sw.getId())) {
return ucteElementIds.get(sw.getId());
}

VoltageLevel.BusBreakerView view = sw.getVoltageLevel().getBusBreakerView();
Bus bus1 = view.getBus1(sw.getId());
Bus bus2 = view.getBus2(sw.getId());

UcteNodeCode u1 = generateUcteNodeId(bus1.getId(), bus1.getVoltageLevel());
UcteNodeCode u2 = generateUcteNodeId(bus2.getId(), bus2.getVoltageLevel());

return generateUcteElementId(sw.getId(), u1, u2);
}

@Override
public UcteNodeCode getUcteNodeCode(String id) {
if (id == null) {
throw new PowsyblException("ID is null");
}
return Optional.ofNullable(ucteNodeIds.get(id))
.orElseThrow(() -> new UcteException(String.format(NO_UCTE_CODE_ERROR, id)));
}

@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) {
return Optional.ofNullable(ucteElementIds.get(id))
.orElseThrow(() -> new UcteException(String.format("No UCTE element id found for: %s", id)));
}

@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 @@ -145,6 +145,8 @@ private static UcteNetwork createUcteNetwork(Network network, NamingStrategy nam

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
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.powsybl.ucte.converter;
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.*;
import com.powsybl.commons.datasource.ResourceDataSource;
import com.powsybl.commons.datasource.ResourceSet;
import com.powsybl.ucte.network.UcteNodeCode;
import com.powsybl.ucte.network.UcteElementId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class CounterNamingStrategyTest {
clementleclercRTE marked this conversation as resolved.
Show resolved Hide resolved

private Network network;
private CounterNamingStrategy strategy;

@BeforeEach
void setUp() {
ResourceDataSource dataSource = new ResourceDataSource("network", new ResourceSet("/", "network.xiidm"));
network = Network.read(dataSource);
strategy = new CounterNamingStrategy();
strategy.initialiseNetwork(network);
}

@Test
void testName() {
assertEquals("Counter", strategy.getName());
}

@Test
void testBasicNodeCodeGeneration() {
Bus genBus = network.getBusBreakerView().getBus("NGEN");
Bus hv1Bus = network.getBusBreakerView().getBus("NHV1");
Bus hv2Bus = network.getBusBreakerView().getBus("NHV2");
Bus loadBus = network.getBusBreakerView().getBus("NLOAD");

UcteNodeCode genCode = strategy.getUcteNodeCode(genBus);
UcteNodeCode hv1Code = strategy.getUcteNodeCode(hv1Bus);
UcteNodeCode hv2Code = strategy.getUcteNodeCode(hv2Bus);
UcteNodeCode loadCode = strategy.getUcteNodeCode(loadBus);

assertAll(
() -> assertEquals(8, genCode.toString().length()),
() -> assertEquals(8, hv1Code.toString().length()),
() -> assertEquals(8, hv2Code.toString().length()),
() -> assertEquals(8, loadCode.toString().length())
);

assertAll(
() -> assertNotEquals(genCode, hv1Code),
() -> assertNotEquals(genCode, hv2Code),
() -> assertNotEquals(genCode, loadCode),
() -> assertNotEquals(hv1Code, hv2Code),
() -> assertNotEquals(hv1Code, loadCode),
() -> assertNotEquals(hv2Code, loadCode)
);
}

@Test
void testTransformerElementIds() {
Branch<?> transformer1 = network.getBranch("NGEN_NHV1");
Branch<?> transformer2 = network.getBranch("NHV2_NLOAD");

UcteElementId id1 = strategy.getUcteElementId(transformer1);
UcteElementId id2 = strategy.getUcteElementId(transformer2);

assertAll(
() -> assertNotNull(id1),
() -> assertNotNull(id2),
() -> assertEquals(19, id1.toString().length()),
() -> assertEquals(19, id2.toString().length()),
() -> assertNotEquals(id1, id2)
);
}

@Test
void testParallelLines() {
Branch<?> line1 = network.getBranch("NHV1_NHV2_1");
Branch<?> line2 = network.getBranch("NHV1_NHV2_2");

UcteElementId id1 = strategy.getUcteElementId(line1);
UcteElementId id2 = strategy.getUcteElementId(line2);

assertAll(
() -> assertNotNull(id1),
() -> assertNotNull(id2),
() -> assertEquals(19, id1.toString().length()),
() -> assertEquals(19, id2.toString().length()),
() -> assertNotEquals(id1, id2)
);
}

@Test
void testVoltageLevelCodes() {
Bus genBus = network.getBusBreakerView().getBus("NGEN"); // 27.0 kV
Bus hv1Bus = network.getBusBreakerView().getBus("NHV1"); // 380.0 kV
Bus loadBus = network.getBusBreakerView().getBus("NLOAD"); // 150.0 kV

UcteNodeCode genCode = strategy.getUcteNodeCode(genBus);
UcteNodeCode hv1Code = strategy.getUcteNodeCode(hv1Bus);
UcteNodeCode loadCode = strategy.getUcteNodeCode(loadBus);

assertAll(
() -> assertEquals('7', genCode.toString().charAt(6)),
() -> assertEquals('1', hv1Code.toString().charAt(6)),
() -> assertEquals('3', loadCode.toString().charAt(6))
);
}

@Test
void testNullAndInvalidIds() {
assertAll(
() -> assertThrows(PowsyblException.class, () -> strategy.getUcteNodeCode((String) null)),
() -> assertThrows(PowsyblException.class, () -> strategy.getUcteElementId((String) null)),
() -> assertThrows(UcteException.class, () -> strategy.getUcteNodeCode("INVALID_ID")),
() -> assertThrows(UcteException.class, () -> strategy.getUcteElementId("INVALID_ID"))
);
}

@Test
void testCountryCode() {
Bus genBus = network.getBusBreakerView().getBus("NGEN");
UcteNodeCode code = strategy.getUcteNodeCode(genBus);
assertEquals('F', code.toString().charAt(0)); // France
}

}
Loading
Loading