Skip to content

Commit

Permalink
Add a new identifier with wildcards support (#2965)
Browse files Browse the repository at this point in the history
* add new identifier for unknown character

Signed-off-by: Etienne LESOT <etienne.lesot@rte-france.com>
  • Loading branch information
EtienneLt authored May 14, 2024
1 parent 65a56be commit 7b6df4e
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,17 @@ public List<NetworkElementIdentifier> getIdentifiants() {
@Override
public List<Contingency> getContingencies(Network network) {
return networkElementIdentifiers.stream()
.filter(identifier -> !identifier.filterIdentifiable(network).isEmpty())
.map(identifier -> {
List<ContingencyElement> contingencyElements = identifier.filterIdentifiable(network)
.stream()
.map(ContingencyElement::of)
.collect(Collectors.toList());
String contingencyId = identifier.getContingencyId().orElse(getGeneratedContingencyId(contingencyElements));
return new Contingency(contingencyId, contingencyElements);
})
.filter(contingency -> contingency.isValid(network))
.collect(Collectors.toList());
.filter(identifier -> !identifier.filterIdentifiable(network).isEmpty())
.map(identifier -> {
List<ContingencyElement> contingencyElements = identifier.filterIdentifiable(network)
.stream()
.map(ContingencyElement::of)
.toList();
String contingencyId = identifier.getContingencyId().orElse(getGeneratedContingencyId(contingencyElements));
return new Contingency(contingencyId, contingencyElements);
})
.filter(contingency -> contingency.isValid(network))
.collect(Collectors.toList());
}

public Map<String, Set<String>> getNotFoundElements(Network network) {
Expand All @@ -71,8 +71,8 @@ public Map<String, Set<String>> getNotFoundElements(Network network) {
Set<String> notFoundElements = identifier.getNotFoundElements(network);
if (!notFoundElements.isEmpty()) {
String contingencyId = identifier.getContingencyId().orElse(getGeneratedContingencyId(identifier.filterIdentifiable(network)
.stream()
.map(ContingencyElement::of).toList()));
.stream()
.map(ContingencyElement::of).toList()));
notFoundElementsMap.put(contingencyId, notFoundElements);
}

Expand All @@ -82,8 +82,8 @@ public Map<String, Set<String>> getNotFoundElements(Network network) {

private String getGeneratedContingencyId(List<ContingencyElement> contingencyElements) {
return "Contingency : " + contingencyElements
.stream()
.map(ContingencyElement::getId)
.collect(Collectors.joining(" + "));
.stream()
.map(ContingencyElement::getId)
.collect(Collectors.joining(" + "));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
*/
package com.powsybl.contingency;

import com.powsybl.commons.PowsyblException;
import com.powsybl.contingency.contingency.list.IdentifierContingencyList;
import com.powsybl.iidm.network.Identifiable;
import com.powsybl.iidm.network.identifiers.*;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory;
import com.powsybl.iidm.network.test.FourSubstationsNodeBreakerFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.*;

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

/**
* @author Etienne Lesot {@literal <etienne.lesot@rte-france.com>}
Expand Down Expand Up @@ -183,4 +186,40 @@ void testIdentifierListOfDefaultIds() {
Map<String, Set<String>> notFoundElements = contingencyList.getNotFoundElements(network);
assertEquals(0, notFoundElements.size());
}

@Test
void testUnknownCharacterIdentifierWithList() {
Network network = EurostagTutorialExample1Factory.create();
List<NetworkElementIdentifier> networkElementIdentifierListElements = new ArrayList<>();
NetworkElementIdentifier elementIdentifier = new IdWithWildcardsNetworkElementIdentifier("NHV1_NHV2_?");
networkElementIdentifierListElements.add(elementIdentifier);
IdentifierContingencyList contingencyList = new IdentifierContingencyList("list", networkElementIdentifierListElements);
List<Contingency> contingencies = contingencyList.getContingencies(network);
assertEquals(1, contingencies.size());
List<String> elementIds = contingencies.get(0).getElements().stream().map(ContingencyElement::getId).toList();
assertTrue(elementIds.containsAll(Arrays.asList("NHV1_NHV2_1", "NHV1_NHV2_2")));
}

@Test
void testUnknownCharacterIdentifier() {
String message = Assertions.assertThrows(PowsyblException.class, () -> new IdWithWildcardsNetworkElementIdentifier("NHV1_NHV2_?_?_?_?_?_?_?")).getMessage();
Assertions.assertEquals("There can be a maximum of 5 wildcards ('?')", message);
String message2 = Assertions.assertThrows(PowsyblException.class, () -> new IdWithWildcardsNetworkElementIdentifier("NHV1_NHV2_ç")).getMessage();
Assertions.assertEquals("Only characters allowed for this identifier are letters, numbers, '_', '-', '.' and the wildcard character '?'", message2);
NetworkElementIdentifier elementIdentifier = new IdWithWildcardsNetworkElementIdentifier("NHV1_NHV?_?");
Network network = EurostagTutorialExample1Factory.create();
List<String> identifiables = elementIdentifier.filterIdentifiable(network).stream().map(Identifiable::getId).toList();
Assertions.assertEquals(2, identifiables.size());
assertTrue(identifiables.containsAll(Arrays.asList("NHV1_NHV2_1", "NHV1_NHV2_2")));

network.newSubstation().setId("NHV1.NHV2-1").add();
network.newSubstation().setId("NHV10NHV2-1").add();
elementIdentifier = new IdWithWildcardsNetworkElementIdentifier("NHV1.NHV?-?");
identifiables = elementIdentifier.filterIdentifiable(network).stream().map(Identifiable::getId).toList();
Assertions.assertEquals(1, identifiables.size());
assertTrue(identifiables.contains("NHV1.NHV2-1"));

String message3 = assertThrows(PowsyblException.class, () -> new IdWithWildcardsNetworkElementIdentifier("TEST_WITH_NO_WILDCARDS")).getMessage();
assertEquals("There is no wildcard in your identifier, please use IdBasedNetworkElementIdentifier instead", message3);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@
import com.powsybl.commons.test.ComparisonUtils;
import com.powsybl.contingency.contingency.list.ContingencyList;
import com.powsybl.contingency.contingency.list.IdentifierContingencyList;
import com.powsybl.iidm.network.identifiers.NetworkElementIdentifier;
import com.powsybl.iidm.network.identifiers.NetworkElementIdentifierContingencyList;
import com.powsybl.iidm.network.identifiers.IdBasedNetworkElementIdentifier;
import com.powsybl.iidm.network.identifiers.VoltageLevelAndOrderNetworkElementIdentifier;
import com.powsybl.iidm.network.identifiers.*;
import org.junit.jupiter.api.Test;

import java.io.*;
Expand All @@ -44,6 +41,7 @@ private static IdentifierContingencyList create() {
"vl2", '1', "contingencyId2"));
networkElementIdentifiers.add(new NetworkElementIdentifierContingencyList(Collections.singletonList(new
IdBasedNetworkElementIdentifier("identifier")), "contingencyId3"));
networkElementIdentifiers.add(new IdWithWildcardsNetworkElementIdentifier("identifier?", "contingencyId4"));
return new IdentifierContingencyList("list1", networkElementIdentifiers);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,9 @@
"type" : "ID_BASED",
"identifier" : "identifier"
} ]
}, {
"type" : "ID_WITH_WILDCARDS",
"contingencyId" : "contingencyId4",
"identifier" : "identifier?"
} ]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* 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.network.identifiers;

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Identifiable;
import com.powsybl.iidm.network.Network;
import org.apache.commons.lang3.StringUtils;

import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static com.powsybl.iidm.network.identifiers.NetworkElementIdentifier.IdentifierType.ID_WITH_WILDCARDS;

/**
*
* <p>Identifier that finds a network element that have some unknown characters.</p>
* <p>The unknown characters should be replaced in the identifier by the wildcard character '?'.</p>
* <p>There can be a maximum of 5 wildcards in the identifier and the only special characters allowed for the identifier are '_', '-' and '.'.</p>
* <p>For example, the identifier "GEN_?" allows to find "GEN_1".</p>
*
* @author Etienne Lesot {@literal <etienne.lesot at rte-france.com>}
*
*/
public class IdWithWildcardsNetworkElementIdentifier implements NetworkElementIdentifier {
private String identifier;
public static final char WILDCARD = '?';
public static final int ALLOWED_WILDCARDS_NUMBER = 5;
private final String contingencyId;

public IdWithWildcardsNetworkElementIdentifier(String identifier) {
this(identifier, null);
}

public IdWithWildcardsNetworkElementIdentifier(String identifier, String contingencyId) {
this.identifier = Objects.requireNonNull(identifier);
this.contingencyId = contingencyId;
initialize();
}

private void initialize() {
String allowedCharactersRegex = "^[A-Za-z0-9_?.-]*$";

if (!identifier.matches(allowedCharactersRegex)) {
throw new PowsyblException("Only characters allowed for this identifier are letters, numbers, '_', '-', '.' and the wildcard character '?'");
}
int separatorNumber = StringUtils.countMatches(identifier, WILDCARD);
if (separatorNumber > ALLOWED_WILDCARDS_NUMBER) {
throw new PowsyblException("There can be a maximum of " + ALLOWED_WILDCARDS_NUMBER + " wildcards ('?')");
}
if (separatorNumber == 0) {
throw new PowsyblException("There is no wildcard in your identifier, please use IdBasedNetworkElementIdentifier instead");
}
identifier = identifier.replace(".", "\\.").replace(WILDCARD, '.');
}

@Override
public Set<Identifiable> filterIdentifiable(Network network) {
return network.getIdentifiables()
.stream()
.filter(identifiable -> identifiable.getId().matches(identifier))
.collect(Collectors.toUnmodifiableSet());
}

@Override
public Set<String> getNotFoundElements(Network network) {
Identifiable<?> identifiable = network.getIdentifiable(identifier);
return identifiable == null ? Collections.singleton(identifier) : Collections.emptySet();
}

@Override
public IdentifierType getType() {
return ID_WITH_WILDCARDS;
}

@Override
public Optional<String> getContingencyId() {
return Optional.ofNullable(contingencyId);
}

public String getIdentifier() {
return identifier;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public interface NetworkElementIdentifier {
enum IdentifierType {
ID_BASED,
VOLTAGE_LEVELS_AND_ORDER,
LIST
LIST,
ID_WITH_WILDCARDS
}

IdentifierType getType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.powsybl.commons.json.JsonUtil;
import com.powsybl.iidm.network.identifiers.NetworkElementIdentifierContingencyList;
import com.powsybl.iidm.network.identifiers.IdBasedNetworkElementIdentifier;
import com.powsybl.iidm.network.identifiers.NetworkElementIdentifier;
import com.powsybl.iidm.network.identifiers.VoltageLevelAndOrderNetworkElementIdentifier;
import com.powsybl.iidm.network.identifiers.*;

import java.io.IOException;
import java.util.Collections;
Expand Down Expand Up @@ -54,8 +51,7 @@ public NetworkElementIdentifier deserialize(JsonParser parser, DeserializationCo
}
case "identifierList" -> {
parser.nextToken();
networkElementIdentifierList = JsonUtil.readList(deserializationContext,
parser, NetworkElementIdentifier.class);
networkElementIdentifierList = JsonUtil.readList(deserializationContext, parser, NetworkElementIdentifier.class);
}
case "voltageLevelId1" -> voltageLevelId1 = parser.nextTextValue();
case "voltageLevelId2" -> voltageLevelId2 = parser.nextTextValue();
Expand All @@ -76,7 +72,8 @@ public NetworkElementIdentifier deserialize(JsonParser parser, DeserializationCo
case ID_BASED -> new IdBasedNetworkElementIdentifier(identifier, contingencyId);
case LIST -> new NetworkElementIdentifierContingencyList(networkElementIdentifierList, contingencyId);
case VOLTAGE_LEVELS_AND_ORDER ->
new VoltageLevelAndOrderNetworkElementIdentifier(voltageLevelId1, voltageLevelId2, order, contingencyId);
new VoltageLevelAndOrderNetworkElementIdentifier(voltageLevelId1, voltageLevelId2, order, contingencyId);
case ID_WITH_WILDCARDS -> new IdWithWildcardsNetworkElementIdentifier(identifier, contingencyId);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.powsybl.iidm.network.identifiers.NetworkElementIdentifierContingencyList;
import com.powsybl.iidm.network.identifiers.IdBasedNetworkElementIdentifier;
import com.powsybl.iidm.network.identifiers.NetworkElementIdentifier;
import com.powsybl.iidm.network.identifiers.VoltageLevelAndOrderNetworkElementIdentifier;
import com.powsybl.iidm.network.identifiers.*;

import java.io.IOException;
import java.util.Optional;
Expand Down Expand Up @@ -41,15 +38,18 @@ public void serialize(NetworkElementIdentifier networkElementIdentifier, JsonGen
break;
case LIST:
serializerProvider.defaultSerializeField("identifierList",
((NetworkElementIdentifierContingencyList) networkElementIdentifier).getNetworkElementIdentifiers(),
jsonGenerator);
((NetworkElementIdentifierContingencyList) networkElementIdentifier).getNetworkElementIdentifiers(),
jsonGenerator);
break;
case VOLTAGE_LEVELS_AND_ORDER:
VoltageLevelAndOrderNetworkElementIdentifier ucteIdentifier = (VoltageLevelAndOrderNetworkElementIdentifier) networkElementIdentifier;
jsonGenerator.writeStringField("voltageLevelId1", ucteIdentifier.getVoltageLevelId1());
jsonGenerator.writeStringField("voltageLevelId2", ucteIdentifier.getVoltageLevelId2());
jsonGenerator.writeStringField("order", Character.toString(ucteIdentifier.getOrder()));
break;
case ID_WITH_WILDCARDS:
jsonGenerator.writeStringField("identifier", ((IdWithWildcardsNetworkElementIdentifier) networkElementIdentifier)
.getIdentifier().replace('.', IdWithWildcardsNetworkElementIdentifier.WILDCARD));
}
jsonGenerator.writeEndObject();
}
Expand Down

0 comments on commit 7b6df4e

Please sign in to comment.