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

Overload management system extension fix #3211

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
@@ -0,0 +1,26 @@
/**
* 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.commons.extensions;

import com.powsybl.commons.io.DeserializerContext;

import java.util.function.Function;

/**
* @author Olivier Perrin {@literal <olivier.perrin at rte-france.com>}
*/
public interface PostponableCreationExtensionSerDe<T extends Extendable<T>, E extends Extension<T>>
extends ExtensionSerDe<T, E> {

@Override
default E read(T extendable, DeserializerContext context) {
return extensionCreator(context).apply(extendable);
}

Function<T, E> extensionCreator(DeserializerContext context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public class NetworkDeserializerContext extends AbstractNetworkSerDeContext<Impo

private ValidationLevel networkValidationLevel;

private Set<String> ignoredEquipments = new HashSet<>();

public NetworkDeserializerContext(Anonymizer anonymizer, TreeDataReader reader) {
this(anonymizer, reader, new ImportOptions(), CURRENT_IIDM_VERSION, Collections.emptyMap());
}
Expand Down Expand Up @@ -80,4 +82,12 @@ public NetworkDeserializerContext setNetworkValidationLevel(ValidationLevel vali
public ValidationLevel getNetworkValidationLevel() {
return this.networkValidationLevel;
}

public void addIgnoredEquipment(String equipment) {
ignoredEquipments.add(equipment);
}

public boolean isIgnoredEquipment(String equipment) {
return ignoredEquipments.contains(equipment);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
import com.powsybl.commons.datasource.ReadOnlyDataSource;
import com.powsybl.commons.exceptions.UncheckedSaxException;
import com.powsybl.commons.exceptions.UncheckedXmlStreamException;
import com.powsybl.commons.extensions.Extension;
import com.powsybl.commons.extensions.ExtensionProviders;
import com.powsybl.commons.extensions.ExtensionSerDe;
import com.powsybl.commons.extensions.*;
import com.powsybl.commons.io.TreeDataFormat;
import com.powsybl.commons.io.TreeDataHeader;
import com.powsybl.commons.io.TreeDataReader;
Expand Down Expand Up @@ -54,6 +52,7 @@
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.zip.GZIPInputStream;
Expand Down Expand Up @@ -199,7 +198,7 @@ private static void writeVoltageAngleLimits(Network n, NetworkSerializerContext
private static void writeExtensions(Network n, NetworkSerializerContext context) {
context.getWriter().writeStartNodes();
for (Identifiable<?> identifiable : IidmSerDeUtil.sorted(n.getIdentifiables(), context.getOptions())) {
if (!context.isExportedEquipment(identifiable) || !isElementWrittenInsideNetwork(identifiable, n, context)) {
if (ignoreEquipmentAtExport(identifiable, context) || !isElementWrittenInsideNetwork(identifiable, n, context)) {
continue;
}
Collection<? extends Extension<? extends Identifiable<?>>> extensions = identifiable.getExtensions().stream()
Expand All @@ -218,6 +217,11 @@ private static void writeExtensions(Network n, NetworkSerializerContext context)
context.getWriter().writeEndNodes();
}

private static boolean ignoreEquipmentAtExport(Identifiable<?> identifiable, NetworkSerializerContext context) {
return !context.isExportedEquipment(identifiable)
|| identifiable instanceof OverloadManagementSystem && !context.getOptions().isWithAutomationSystems();
}

private static boolean canTheExtensionBeWritten(ExtensionSerDe extensionSerDe, IidmVersion version, ExportOptions options) {
if (extensionSerDe == null) {
return false;
Expand Down Expand Up @@ -640,7 +644,7 @@ private static void readNetworkElement(String elementName, Deque<Network> networ
case TieLineSerDe.ROOT_ELEMENT_NAME -> TieLineSerDe.INSTANCE.read(networks.peek(), context);
case HvdcLineSerDe.ROOT_ELEMENT_NAME -> HvdcLineSerDe.INSTANCE.read(networks.peek(), context);
case VoltageAngleLimitSerDe.ROOT_ELEMENT_NAME -> VoltageAngleLimitSerDe.read(networks.peek(), context);
case EXTENSION_ROOT_ELEMENT_NAME -> findExtendableAndReadExtension(networks.getFirst(), context, extensionNamesImported, extensionNamesNotFound);
case EXTENSION_ROOT_ELEMENT_NAME -> readExtensionTag(networks.getFirst(), context, extensionNamesImported, extensionNamesNotFound);
default -> throw new PowsyblException("Unknown element name '" + elementName + "' in 'network'");
}
}
Expand Down Expand Up @@ -679,13 +683,9 @@ private static void checkSupportedAndReadVoltageLevel(NetworkDeserializerContext
VoltageLevelSerDe.INSTANCE.read(networks.peek(), context);
}

private static void findExtendableAndReadExtension(Network network, NetworkDeserializerContext context, Set<String> extensionNamesImported, Set<String> extensionNamesNotFound) {
String id2 = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("id"));
Identifiable identifiable = network.getIdentifiable(id2);
if (identifiable == null) {
throw new PowsyblException("Identifiable " + id2 + " not found");
}
readExtensions(identifiable, context, extensionNamesImported, extensionNamesNotFound);
private static void readExtensionTag(Network network, NetworkDeserializerContext context, Set<String> extensionNamesImported, Set<String> extensionNamesNotFound) {
String id = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("id"));
readExtensions(network, id, context, extensionNamesImported, extensionNamesNotFound);
}

private static Network initNetwork(NetworkFactory networkFactory, NetworkDeserializerContext context, TreeDataReader reader, Network rootNetwork) {
Expand Down Expand Up @@ -811,19 +811,22 @@ public static Network validateAndRead(Path xmlFile) {
return validateAndRead(xmlFile, new ImportOptions());
}

private static void readExtensions(Identifiable identifiable, NetworkDeserializerContext context,
private static void readExtensions(Network network, String id, NetworkDeserializerContext context,
Set<String> extensionNamesImported, Set<String> extensionNamesNotFound) {

context.getReader().readChildNodes(extensionName -> {
// extensions root elements are nested directly in 'extension' element, so there is no need
// to check for an extension to exist if depth is greater than zero. Furthermore, in case of
// missing extension serializer, we must not check for an extension in sub elements.
if (context.getOptions().withExtension(extensionName)) {
ExtensionSerDe extensionXmlSerializer = EXTENSIONS_SUPPLIER.get().findProvider(extensionName);
if (context.getOptions().withExtension(extensionName) && !context.isIgnoredEquipment(id)) {
ExtensionSerDe<?, ?> extensionXmlSerializer = EXTENSIONS_SUPPLIER.get().findProvider(extensionName);
if (extensionXmlSerializer != null) {
Extension<? extends Identifiable<?>> extension = extensionXmlSerializer.read(identifiable, context);
identifiable.addExtension(extensionXmlSerializer.getExtensionClass(), extension);
extensionNamesImported.add(extensionName);
if (extensionXmlSerializer instanceof PostponableCreationExtensionSerDe<? extends Extendable<?>, ? extends Extension <?>> postponableCreationExtensionSerDe) {
Function<Extendable<?>, ?> extensionCreator = (Function<Extendable<?>, ?>) postponableCreationExtensionSerDe.extensionCreator(context);
context.getEndTasks().add(() -> createExtension(network, id, extensionCreator, extensionNamesImported, extensionName));
} else {
Identifiable<?> identifiable = getIdentifiable(network, id);
createExtension(identifiable, context, extensionNamesImported, extensionName, extensionXmlSerializer);
}
} else {
extensionNamesNotFound.add(extensionName);
context.getReader().skipChildNodes();
Expand All @@ -834,6 +837,30 @@ private static void readExtensions(Identifiable identifiable, NetworkDeserialize
});
}

private static Identifiable<?> getIdentifiable(Network network, String id) {
Identifiable<?> identifiable = network.getIdentifiable(id);
if (identifiable == null) {
throw new PowsyblException("Identifiable " + id + " not found");
}
return identifiable;
}

private static void createExtension(Network network, String id,
Function<Extendable<?>, ?> extensionCreator,
Set<String> extensionNamesImported, String extensionName) {
Identifiable<?> identifiable = getIdentifiable(network, id);
extensionCreator.apply(identifiable);
extensionNamesImported.add(extensionName);
}

private static void createExtension(Identifiable identifiable, NetworkDeserializerContext context,
Set<String> extensionNamesImported, String extensionName,
ExtensionSerDe extensionXmlSerializer) {
Extension<? extends Identifiable<?>> extension = extensionXmlSerializer.read(identifiable, context);
identifiable.addExtension(extensionXmlSerializer.getExtensionClass(), extension);
extensionNamesImported.add(extensionName);
}

public static byte[] gzip(Network network) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (GZIPOutputStream gzos = new GZIPOutputStream(bos)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,5 +193,6 @@ public final void skip(Substation s, NetworkDeserializerContext context) {
String id = readIdentifierAttributes(null, context);
readRootElementAttributes(null, s, toApply, context);
readSubElements(id, null, toApply, context);
context.addIgnoredEquipment(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* 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.serde;

import com.powsybl.commons.extensions.AbstractExtension;
import com.powsybl.iidm.network.OverloadManagementSystem;

/**
* @author Olivier Perrin {@literal <olivier.perrin at rte-france.com>}
*/
public class OverloadManagementSystemMockExt extends AbstractExtension<OverloadManagementSystem> {
private final String foo;

public OverloadManagementSystemMockExt(OverloadManagementSystem overloadManagementSystem, String foo) {
super(overloadManagementSystem);
this.foo = foo;
}

public String getFoo() {
return foo;
}

@Override
public String getName() {
return "omsMock";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* 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.serde;

import com.google.auto.service.AutoService;
import com.powsybl.commons.extensions.AbstractExtensionSerDe;
import com.powsybl.commons.extensions.ExtensionSerDe;
import com.powsybl.commons.extensions.PostponableCreationExtensionSerDe;
import com.powsybl.commons.io.DeserializerContext;
import com.powsybl.commons.io.SerializerContext;
import com.powsybl.iidm.network.OverloadManagementSystem;

import java.util.function.Function;

/**
* @author Olivier Perrin {@literal <olivier.perrin at rte-france.com>}
*/
@AutoService(ExtensionSerDe.class)
public class OverloadManagementSystemMockSerDe extends AbstractExtensionSerDe<OverloadManagementSystem, OverloadManagementSystemMockExt>
implements PostponableCreationExtensionSerDe<OverloadManagementSystem, OverloadManagementSystemMockExt> {

public OverloadManagementSystemMockSerDe() {
super("omsMock", "network", OverloadManagementSystemMockExt.class,
"overloadManagementSystemMock.xsd",
"http://www.powsybl.org/schema/iidm/ext/overloadmanagementsystem_mock/1_0", "omsmock");
}

@Override
public void write(OverloadManagementSystemMockExt omsFoo, SerializerContext context) {
context.getWriter().writeStringAttribute("foo", omsFoo.getFoo());
}

@Override
public Function<OverloadManagementSystem, OverloadManagementSystemMockExt> extensionCreator(DeserializerContext context) {
// Read the elements
String foo = context.getReader().readStringAttribute("foo");
context.getReader().readEndNode();

// Return the function creating the adder
return oms -> {
OverloadManagementSystemMockExt extension = new OverloadManagementSystemMockExt(oms, foo);
oms.addExtension(getExtensionClass(), extension);
return extension;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ private static Network createNetwork() {
createThreeWindingsTransformer(s1, s1v400, s1v225, s1v90);

// Create an overload management system with trippings on "2WT", "3WT" and "S1_400_LINE_2_BREAKER"
s1.newOverloadManagementSystem()
OverloadManagementSystem oms1 = s1.newOverloadManagementSystem()
.setId("OMS1")
.setName("1st OMS")
.setEnabled(true)
Expand Down Expand Up @@ -224,6 +224,8 @@ private static Network createNetwork() {
.add()
.add();

oms1.addExtension(OverloadManagementSystemMockExt.class, new OverloadManagementSystemMockExt(oms1, "bar"));

// Create an overload management system monitoring "LINE_1" with a tripping on "LINE_2".
// Note that this test is very important since the OMS uses identifiers of elements which are not
// defined in the same substation, and furthermore which will be serialized AFTER the OMS (lines are serialized
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_13" id="fictitious" caseDate="2024-01-02T15:00:00.000+01:00" forecastDistance="0" sourceFormat="test" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_13" xmlns:omsmock="http://www.powsybl.org/schema/iidm/ext/overloadmanagementsystem_mock/1_0" id="fictitious" caseDate="2024-01-02T15:00:00.000+01:00" forecastDistance="0" sourceFormat="test" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:substation id="S1">
<iidm:voltageLevel id="S1_400" nominalV="400.0" topologyKind="NODE_BREAKER">
<iidm:nodeBreakerTopology>
Expand Down Expand Up @@ -55,4 +55,7 @@
</iidm:substation>
<iidm:line id="LINE_1" r="0.01" x="50.0" g1="0.0" b1="0.0" g2="0.0" b2="0.0" voltageLevelId1="S1_400" node1="11" voltageLevelId2="S2_400" node2="11"/>
<iidm:line id="LINE_2" r="0.01" x="50.0" g1="0.0" b1="0.0" g2="0.0" b2="0.0" voltageLevelId1="S1_400" node1="12" voltageLevelId2="S2_400" node2="12"/>
<iidm:extension id="OMS1">
<omsmock:omsMock foo="bar"/>
</iidm:extension>
</iidm:network>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

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

-->
<xs:schema version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.powsybl.org/schema/iidm/ext/overloadmanagementsystem_mock/1_0"
elementFormDefault="qualified">
<xs:element name="omsMock">
<xs:complexType>
<xs:attribute name="foo" use="required" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:schema>
Loading