diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractComplexIdentifiableSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractComplexIdentifiableSerDe.java index e723ae538b5..eb4991c2337 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractComplexIdentifiableSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractComplexIdentifiableSerDe.java @@ -45,6 +45,27 @@ public final void read(P parent, NetworkDeserializerContext context) { String id = readIdentifierAttributes(adder, context); readRootElementAttributes(adder, toApply, context); readSubElements(id, adder, toApply, context); + if (postponeElementCreation()) { + context.getEndTasks().add(() -> createElement(adder, toApply)); + } else { + createElement(adder, toApply); + } + } + + /** + *

Should the current element's creation be postponed?

+ *

In some specific cases, the element could not be created right after it is read, typically if it references + * other network elements which may have not been yet created. (It is better to create the element without the said + * references and to fill them in later, but it is not always possible, or at high cost.) + * If this method returns true, the element's creation will be defined as an "end task" and + * will be performed after the whole network is read.

+ * @return true if the creation should be postponed, false otherwise. + */ + protected boolean postponeElementCreation() { + return false; + } + + private static , A extends IdentifiableAdder> void createElement(A adder, List> toApply) { T identifiable = adder.add(); toApply.forEach(consumer -> consumer.accept(identifiable)); } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractIdentifiableSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractIdentifiableSerDe.java index 40fe44da585..85043270f0b 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractIdentifiableSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractIdentifiableSerDe.java @@ -56,11 +56,15 @@ public final void write(T identifiable, P parent, NetworkSerializerContext conte protected String readIdentifierAttributes(A adder, NetworkDeserializerContext context) { String id = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("id")); String name = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("name")); - adder.setId(id) - .setName(name); + if (adder != null) { + adder.setId(id) + .setName(name); + } IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_2, context, () -> { boolean fictitious = context.getReader().readBooleanAttribute("fictitious", false); - adder.setFictitious(fictitious); + if (adder != null) { + adder.setFictitious(fictitious); + } }); return id; } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataExporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataExporter.java index 2f548775be4..5ea9d3fbfa8 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataExporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataExporter.java @@ -89,6 +89,11 @@ * version in which files will be generated * 1.5 or 1.4 etc * + * + * iidm.export.xml.with-automation-systems + * if true automation systems are exported + * true or false + * * * * @author Geoffroy Jamgotchian {@literal } @@ -109,6 +114,7 @@ public abstract class AbstractTreeDataExporter implements Exporter { public static final String EXTENSIONS_LIST = "iidm.export.xml.extensions"; public static final String SORTED = "iidm.export.xml.sorted"; public static final String VERSION = "iidm.export.xml.version"; + public static final String WITH_AUTOMATION_SYSTEMS = "iidm.export.xml.with-automation-systems"; private static final Parameter INDENT_PARAMETER = new Parameter(INDENT, ParameterType.BOOLEAN, "Indent export output file", Boolean.TRUE); private static final Parameter WITH_BRANCH_STATE_VARIABLES_PARAMETER = new Parameter(WITH_BRANCH_STATE_VARIABLES, ParameterType.BOOLEAN, "Export network with branch state variables", Boolean.TRUE); @@ -126,11 +132,12 @@ public abstract class AbstractTreeDataExporter implements Exporter { private static final Parameter SORTED_PARAMETER = new Parameter(SORTED, ParameterType.BOOLEAN, "Sort export output file", Boolean.FALSE); private static final Parameter VERSION_PARAMETER = new Parameter(VERSION, ParameterType.STRING, "IIDM version in which files will be generated", IidmSerDeConstants.CURRENT_IIDM_VERSION.toString("."), Arrays.stream(IidmVersion.values()).map(v -> v.toString(".")).collect(Collectors.toList())); - + private static final Parameter WITH_AUTOMATION_SYSTEMS_PARAMETER = new Parameter(WITH_AUTOMATION_SYSTEMS, ParameterType.BOOLEAN, + "Export network with automation systems", Boolean.TRUE); private static final List STATIC_PARAMETERS = List.of(INDENT_PARAMETER, WITH_BRANCH_STATE_VARIABLES_PARAMETER, ONLY_MAIN_CC_PARAMETER, ANONYMISED_PARAMETER, IIDM_VERSION_INCOMPATIBILITY_BEHAVIOR_PARAMETER, TOPOLOGY_LEVEL_PARAMETER, THROW_EXCEPTION_IF_EXTENSION_NOT_FOUND_PARAMETER, EXTENSIONS_LIST_PARAMETER, - SORTED_PARAMETER, VERSION_PARAMETER); + SORTED_PARAMETER, VERSION_PARAMETER, WITH_AUTOMATION_SYSTEMS_PARAMETER); private final ParameterDefaultValueConfig defaultValueConfig; @@ -191,7 +198,8 @@ private ExportOptions createExportOptions(Properties parameters) { .setExtensions(Parameter.readStringList(getFormat(), parameters, EXTENSIONS_LIST_PARAMETER, defaultValueConfig) != null ? new HashSet<>(Parameter.readStringList(getFormat(), parameters, EXTENSIONS_LIST_PARAMETER, defaultValueConfig)) : null) .setSorted(Parameter.readBoolean(getFormat(), parameters, SORTED_PARAMETER, defaultValueConfig)) .setVersion(Parameter.readString(getFormat(), parameters, VERSION_PARAMETER, defaultValueConfig)) - .setFormat(getTreeDataFormat()); + .setFormat(getTreeDataFormat()) + .setWithAutomationSystems(Parameter.readBoolean(getFormat(), parameters, WITH_AUTOMATION_SYSTEMS_PARAMETER, defaultValueConfig)); addExtensionsVersions(parameters, options); return options; } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java index 2c850181ec6..caddb83a507 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java @@ -50,6 +50,8 @@ public abstract class AbstractTreeDataImporter implements Importer { public static final String EXTENSIONS_LIST = "iidm.import.xml.extensions"; + public static final String WITH_AUTOMATION_SYSTEMS = "iidm.import.xml.with-automation-systems"; + private static final Parameter THROW_EXCEPTION_IF_EXTENSION_NOT_FOUND_PARAMETER = new Parameter(THROW_EXCEPTION_IF_EXTENSION_NOT_FOUND, ParameterType.BOOLEAN, "Throw exception if extension not found", Boolean.FALSE) .addAdditionalNames("throwExceptionIfExtensionNotFound"); @@ -58,6 +60,9 @@ public abstract class AbstractTreeDataImporter implements Importer { = new Parameter(EXTENSIONS_LIST, ParameterType.STRING_LIST, "The list of extension files ", null, EXTENSIONS_SUPPLIER.get().getProviders().stream().map(ExtensionProvider::getExtensionName).collect(Collectors.toList())); + private static final Parameter WITH_AUTOMATION_SYSTEMS_PARAMETER = new Parameter(WITH_AUTOMATION_SYSTEMS, ParameterType.BOOLEAN, + "Import network with automation systems", Boolean.TRUE); + private final ParameterDefaultValueConfig defaultValueConfig; static final String SUFFIX_MAPPING = "_mapping"; @@ -72,7 +77,7 @@ protected AbstractTreeDataImporter(PlatformConfig platformConfig) { @Override public List getParameters() { - return List.of(THROW_EXCEPTION_IF_EXTENSION_NOT_FOUND_PARAMETER, EXTENSIONS_LIST_PARAMETER); + return List.of(THROW_EXCEPTION_IF_EXTENSION_NOT_FOUND_PARAMETER, EXTENSIONS_LIST_PARAMETER, WITH_AUTOMATION_SYSTEMS_PARAMETER); } private String findExtension(ReadOnlyDataSource dataSource) throws IOException { @@ -150,7 +155,8 @@ public Network importData(ReadOnlyDataSource dataSource, NetworkFactory networkF protected ImportOptions createImportOptions(Properties parameters) { return new ImportOptions() .setThrowExceptionIfExtensionNotFound(Parameter.readBoolean(getFormat(), parameters, THROW_EXCEPTION_IF_EXTENSION_NOT_FOUND_PARAMETER, defaultValueConfig)) - .setExtensions(Parameter.readStringList(getFormat(), parameters, EXTENSIONS_LIST_PARAMETER, defaultValueConfig) != null ? new HashSet<>(Parameter.readStringList(getFormat(), parameters, EXTENSIONS_LIST_PARAMETER, defaultValueConfig)) : null); + .setExtensions(Parameter.readStringList(getFormat(), parameters, EXTENSIONS_LIST_PARAMETER, defaultValueConfig) != null ? new HashSet<>(Parameter.readStringList(getFormat(), parameters, EXTENSIONS_LIST_PARAMETER, defaultValueConfig)) : null) + .setWithAutomationSystems(Parameter.readBoolean(getFormat(), parameters, WITH_AUTOMATION_SYSTEMS_PARAMETER, defaultValueConfig)); } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java index e2bdc644516..fb1e8eb27c6 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ExportOptions.java @@ -62,6 +62,8 @@ public enum IidmVersionIncompatibilityBehavior { */ private boolean sorted = false; + private boolean withAutomationSystems = true; + public ExportOptions() { } @@ -233,4 +235,13 @@ public ExportOptions setSorted(boolean sorted) { this.sorted = sorted; return this; } + + public boolean isWithAutomationSystems() { + return withAutomationSystems; + } + + public ExportOptions setWithAutomationSystems(boolean withAutomationSystems) { + this.withAutomationSystems = withAutomationSystems; + return this; + } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ImportOptions.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ImportOptions.java index 0f9901dffc6..a701c8451ee 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ImportOptions.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ImportOptions.java @@ -17,6 +17,7 @@ public class ImportOptions extends AbstractOptions { private boolean throwExceptionIfExtensionNotFound = false; + private boolean withAutomationSystems = true; public ImportOptions() { } @@ -50,4 +51,13 @@ public ImportOptions addExtension(String extension) { public boolean isThrowExceptionIfExtensionNotFound() { return throwExceptionIfExtensionNotFound; } + + public boolean isWithAutomationSystems() { + return withAutomationSystems; + } + + public ImportOptions setWithAutomationSystems(boolean withAutomationSystems) { + this.withAutomationSystems = withAutomationSystems; + return this; + } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java index 5debecd41b4..1db679a68c7 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java @@ -553,6 +553,7 @@ private static BiMap createArrayNameSingleNameBiMap(boolean with Map.entry(LineSerDe.ARRAY_ELEMENT_NAME, LineSerDe.ROOT_ELEMENT_NAME), Map.entry(LoadSerDe.ARRAY_ELEMENT_NAME, LoadSerDe.ROOT_ELEMENT_NAME), Map.entry(NodeBreakerViewInternalConnectionSerDe.ARRAY_ELEMENT_NAME, NodeBreakerViewInternalConnectionSerDe.ROOT_ELEMENT_NAME), + Map.entry(OverloadManagementSystemSerDe.ARRAY_ELEMENT_NAME, OverloadManagementSystemSerDe.ROOT_ELEMENT_NAME), Map.entry(PropertiesSerDe.ARRAY_ELEMENT_NAME, PropertiesSerDe.ROOT_ELEMENT_NAME), Map.entry(ReactiveLimitsSerDe.POINT_ARRAY_ELEMENT_NAME, ReactiveLimitsSerDe.POINT_ROOT_ELEMENT_NAME), Map.entry(ShuntSerDe.ARRAY_ELEMENT_NAME, ShuntSerDe.ROOT_ELEMENT_NAME), diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDe.java new file mode 100644 index 00000000000..d5824bf83a9 --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDe.java @@ -0,0 +1,197 @@ +/** + * 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.PowsyblException; +import com.powsybl.iidm.network.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * @author Olivier Perrin {@literal } + */ +class OverloadManagementSystemSerDe extends AbstractComplexIdentifiableSerDe { + + static final OverloadManagementSystemSerDe INSTANCE = new OverloadManagementSystemSerDe(); + + static final String ROOT_ELEMENT_NAME = "overloadManagementSystem"; + static final String ARRAY_ELEMENT_NAME = "overloadManagementSystems"; + + private static final String BRANCH_TRIPPING_TAG = "branchTripping"; + private static final String SWITCH_TRIPPING_TAG = "switchTripping"; + private static final String THREE_WINDINGS_TRANSFORMER_TRIPPING_TAG = "threeWindingsTransformerTripping"; + + @Override + protected String getRootElementName() { + return ROOT_ELEMENT_NAME; + } + + @Override + protected void writeRootElementAttributes(OverloadManagementSystem oms, Substation substation, NetworkSerializerContext context) { + context.getWriter().writeBooleanAttribute("enabled", oms.isEnabled()); + context.getWriter().writeStringAttribute("monitoredElementId", context.getAnonymizer().anonymizeString(oms.getMonitoredElementId())); + context.getWriter().writeStringAttribute("side", oms.getMonitoredSide().name()); + } + + @Override + protected void writeSubElements(OverloadManagementSystem oms, Substation substation, NetworkSerializerContext context) { + oms.getTrippings().forEach(t -> writeTripping(t, context)); + } + + private void writeTripping(OverloadManagementSystem.Tripping tripping, NetworkSerializerContext context) { + switch (tripping.getType()) { + case BRANCH_TRIPPING -> { + OverloadManagementSystem.BranchTripping branchTripping = (OverloadManagementSystem.BranchTripping) tripping; + context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), BRANCH_TRIPPING_TAG); + writeTrippingCommonAttributes(tripping, context); + context.getWriter().writeStringAttribute("branchId", + context.getAnonymizer().anonymizeString(branchTripping.getBranchToOperateId())); + context.getWriter().writeStringAttribute("side", branchTripping.getSideToOperate().name()); + context.getWriter().writeEndNode(); + } + case SWITCH_TRIPPING -> { + OverloadManagementSystem.SwitchTripping switchTripping = (OverloadManagementSystem.SwitchTripping) tripping; + context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), SWITCH_TRIPPING_TAG); + writeTrippingCommonAttributes(tripping, context); + context.getWriter().writeStringAttribute("switchId", + context.getAnonymizer().anonymizeString(switchTripping.getSwitchToOperateId())); + context.getWriter().writeEndNode(); + } + case THREE_WINDINGS_TRANSFORMER_TRIPPING -> { + OverloadManagementSystem.ThreeWindingsTransformerTripping twtTripping = + (OverloadManagementSystem.ThreeWindingsTransformerTripping) tripping; + context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), THREE_WINDINGS_TRANSFORMER_TRIPPING_TAG); + writeTrippingCommonAttributes(tripping, context); + context.getWriter().writeStringAttribute("threeWindingsTransformerId", + context.getAnonymizer().anonymizeString(twtTripping.getThreeWindingsTransformerToOperateId())); + context.getWriter().writeStringAttribute("side", twtTripping.getSideToOperate().name()); + context.getWriter().writeEndNode(); + } + default -> throw new PowsyblException("Unexpected tripping type: " + tripping.getType()); + } + } + + private void writeTrippingCommonAttributes(OverloadManagementSystem.Tripping tripping, NetworkSerializerContext context) { + context.getWriter().writeStringAttribute("key", tripping.getKey()); + String nameOrKey = tripping.getNameOrKey(); + if (nameOrKey != null && !nameOrKey.equals(tripping.getKey())) { + context.getWriter().writeStringAttribute("name", nameOrKey); + } + context.getWriter().writeDoubleAttribute("currentLimit", tripping.getCurrentLimit()); + context.getWriter().writeBooleanAttribute("openAction", tripping.isOpenAction()); + } + + @Override + protected OverloadManagementSystemAdder createAdder(Substation s) { + return s.newOverloadManagementSystem(); + } + + @Override + protected void readRootElementAttributes(OverloadManagementSystemAdder adder, + List> toApply, + NetworkDeserializerContext context) { + boolean enabled = context.getReader().readBooleanAttribute("enabled", true); + String monitoredElementId = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("monitoredElementId")); + String side = context.getReader().readStringAttribute("side"); + ThreeSides monitoredSide = side == null ? ThreeSides.ONE : ThreeSides.valueOf(side); + if (adder != null) { + adder.setEnabled(enabled) + .setMonitoredElementId(monitoredElementId) + .setMonitoredElementSide(monitoredSide); + } + } + + @Override + protected void readSubElements(String id, OverloadManagementSystemAdder adder, + List> toApply, + NetworkDeserializerContext context) { + context.getReader().readChildNodes(elementName -> { + String key = context.getReader().readStringAttribute("key"); + String name = context.getReader().readStringAttribute("name"); + double currentLimit = context.getReader().readDoubleAttribute("currentLimit"); + boolean openAction = context.getReader().readBooleanAttribute("openAction"); + + switch (elementName) { + case BRANCH_TRIPPING_TAG -> readBranchTripping(adder, context, key, name, currentLimit, openAction); + case SWITCH_TRIPPING_TAG -> readSwitchTripping(adder, context, key, name, currentLimit, openAction); + case THREE_WINDINGS_TRANSFORMER_TRIPPING_TAG -> readThreeWindingsTransformerTripping(adder, context, key, name, currentLimit, openAction); + default -> readSubElement(elementName, id, toApply, context); + } + }); + } + + private static void readBranchTripping(OverloadManagementSystemAdder adder, NetworkDeserializerContext context, + String key, String name, double currentLimit, boolean openAction) { + String branchId = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("branchId")); + String side = context.getReader().readStringAttribute("side"); + TwoSides sideToOperate = side == null ? TwoSides.ONE : TwoSides.valueOf(side); + context.getReader().readEndNode(); + if (adder != null) { + adder.newBranchTripping() + .setKey(key) + .setName(name) + .setCurrentLimit(currentLimit) + .setOpenAction(openAction) + .setBranchToOperateId(branchId) + .setSideToOperate(sideToOperate) + .add(); + } + } + + private static void readSwitchTripping(OverloadManagementSystemAdder adder, NetworkDeserializerContext context, + String key, String name, double currentLimit, boolean openAction) { + String switchId = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("switchId")); + context.getReader().readEndNode(); + if (adder != null) { + adder.newSwitchTripping() + .setKey(key) + .setName(name) + .setCurrentLimit(currentLimit) + .setOpenAction(openAction) + .setSwitchToOperateId(switchId) + .add(); + } + } + + private static void readThreeWindingsTransformerTripping(OverloadManagementSystemAdder adder, NetworkDeserializerContext context, + String key, String name, double currentLimit, boolean openAction) { + String twtId = context.getAnonymizer().deanonymizeString( + context.getReader().readStringAttribute("threeWindingsTransformerId")); + String side = context.getReader().readStringAttribute("side"); + ThreeSides sideToOperate = side == null ? ThreeSides.ONE : ThreeSides.valueOf(side); + context.getReader().readEndNode(); + if (adder != null) { + adder.newThreeWindingsTransformerTripping() + .setKey(key) + .setName(name) + .setCurrentLimit(currentLimit) + .setOpenAction(openAction) + .setThreeWindingsTransformerToOperateId(twtId) + .setSideToOperate(sideToOperate) + .add(); + } + } + + @Override + protected boolean postponeElementCreation() { + // OverloadManagementSystems may reference other elements which are not in the same substation (for instance lines). + // In that case, there's no guarantee that the other elements were previously read when deserializing the network. + // This could lead to errors at the OverloadManagementSystem's creation. + // To avoid this, this latter is postponed. + return true; + } + + public final void skip(NetworkDeserializerContext context) { + List> toApply = new ArrayList<>(); + String id = readIdentifierAttributes(null, context); + readRootElementAttributes(null, toApply, context); + readSubElements(id, null, toApply, context); + } +} diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/SubstationSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/SubstationSerDe.java index c23a9565b0c..e70034b8999 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/SubstationSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/SubstationSerDe.java @@ -8,7 +8,10 @@ import com.powsybl.iidm.network.*; import com.powsybl.iidm.serde.util.IidmSerDeUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Collection; import java.util.Optional; /** @@ -17,6 +20,8 @@ */ class SubstationSerDe extends AbstractSimpleIdentifiableSerDe { + private static final Logger LOGGER = LoggerFactory.getLogger(SubstationSerDe.class); + static final SubstationSerDe INSTANCE = new SubstationSerDe(); static final String ROOT_ELEMENT_NAME = "substation"; @@ -43,12 +48,21 @@ protected void writeRootElementAttributes(Substation s, Network n, NetworkSerial @Override protected void writeSubElements(Substation s, Network n, NetworkSerializerContext context) { + writeVoltageLevels(s, context); + writeTwoWindingsTransformers(s, context); + writeThreeWindingsTransformers(s, context); + writeOverloadManagementSystems(s, context); + } + + private static void writeVoltageLevels(Substation s, NetworkSerializerContext context) { context.getWriter().writeStartNodes(); for (VoltageLevel vl : IidmSerDeUtil.sorted(s.getVoltageLevels(), context.getOptions())) { VoltageLevelSerDe.INSTANCE.write(vl, null, context); } context.getWriter().writeEndNodes(); + } + private static void writeTwoWindingsTransformers(Substation s, NetworkSerializerContext context) { context.getWriter().writeStartNodes(); Iterable twts = IidmSerDeUtil.sorted(s.getTwoWindingsTransformers(), context.getOptions()); for (TwoWindingsTransformer twt : twts) { @@ -58,10 +72,12 @@ protected void writeSubElements(Substation s, Network n, NetworkSerializerContex TwoWindingsTransformerSerDe.INSTANCE.write(twt, null, context); } context.getWriter().writeEndNodes(); + } + private static void writeThreeWindingsTransformers(Substation s, NetworkSerializerContext context) { context.getWriter().writeStartNodes(); - Iterable twts2 = IidmSerDeUtil.sorted(s.getThreeWindingsTransformers(), context.getOptions()); - for (ThreeWindingsTransformer twt : twts2) { + Iterable twts = IidmSerDeUtil.sorted(s.getThreeWindingsTransformers(), context.getOptions()); + for (ThreeWindingsTransformer twt : twts) { if (!context.getFilter().test(twt)) { continue; } @@ -70,6 +86,60 @@ protected void writeSubElements(Substation s, Network n, NetworkSerializerContex context.getWriter().writeEndNodes(); } + private static void writeOverloadManagementSystems(Substation s, NetworkSerializerContext context) { + if (!context.getOptions().isWithAutomationSystems()) { + return; + } + IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_12, context, () -> { + Collection validOverloadManagementSystems = filterValidOverloadManagementSystems(s); + if (!validOverloadManagementSystems.isEmpty()) { + context.getWriter().writeStartNodes(); + IidmSerDeUtil.sorted(validOverloadManagementSystems, context.getOptions()) + .forEach(oms -> OverloadManagementSystemSerDe.INSTANCE.write(oms, null, context)); + context.getWriter().writeEndNodes(); + } + }); + } + + private static Collection filterValidOverloadManagementSystems(Substation s) { + Network n = s.getNetwork(); + return s.getOverloadManagementSystemStream().filter(o -> { + if (n.getIdentifiable(o.getMonitoredElementId()) == null) { + LOGGER.warn(String.format("Discard overload management system '%s': monitored element '%s' is unknown.", + o.getNameOrId(), o.getMonitoredElementId())); + return false; + } + for (OverloadManagementSystem.Tripping tripping : o.getTrippings()) { + Identifiable element = null; + String id = ""; + String type = ""; + switch (tripping.getType()) { + case BRANCH_TRIPPING -> { + type = "branch"; + id = ((OverloadManagementSystem.BranchTripping) tripping).getBranchToOperateId(); + element = n.getBranch(id); + } + case SWITCH_TRIPPING -> { + type = "switch"; + id = ((OverloadManagementSystem.SwitchTripping) tripping).getSwitchToOperateId(); + element = n.getSwitch(id); + } + case THREE_WINDINGS_TRANSFORMER_TRIPPING -> { + type = "three windings transformer"; + id = ((OverloadManagementSystem.ThreeWindingsTransformerTripping) tripping).getThreeWindingsTransformerToOperateId(); + element = n.getThreeWindingsTransformer(id); + } + } + if (element == null) { + LOGGER.warn(String.format("Discard overload management system '%s': invalid %s tripping. '%s' is unknown.", + o.getNameOrId(), type, id)); + return false; + } + } + return true; + }).toList(); + } + @Override protected SubstationAdder createAdder(Network network) { return network.newSubstation(); @@ -97,8 +167,19 @@ protected void readSubElements(Substation s, NetworkDeserializerContext context) case VoltageLevelSerDe.ROOT_ELEMENT_NAME -> VoltageLevelSerDe.INSTANCE.read(s, context); case TwoWindingsTransformerSerDe.ROOT_ELEMENT_NAME -> TwoWindingsTransformerSerDe.INSTANCE.read(s, context); case ThreeWindingsTransformerSerDe.ROOT_ELEMENT_NAME -> ThreeWindingsTransformerSerDe.INSTANCE.read(s, context); + case OverloadManagementSystemSerDe.ROOT_ELEMENT_NAME -> checkSupportedAndReadOverloadManagementSystems(s, context); default -> readSubElement(elementName, s, context); } }); } + + private static void checkSupportedAndReadOverloadManagementSystems(Substation s, NetworkDeserializerContext context) { + IidmSerDeUtil.assertMinimumVersion(ROOT_ELEMENT_NAME, OverloadManagementSystemSerDe.ROOT_ELEMENT_NAME, + IidmSerDeUtil.ErrorMessage.NOT_SUPPORTED, IidmVersion.V_1_12, context); + if (context.getOptions().isWithAutomationSystems()) { + OverloadManagementSystemSerDe.INSTANCE.read(s, context); + } else { + OverloadManagementSystemSerDe.INSTANCE.skip(context); + } + } } diff --git a/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_12.xsd b/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_12.xsd index 91a8ad187f1..c11d28a6b77 100644 --- a/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_12.xsd +++ b/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_12.xsd @@ -78,6 +78,7 @@ + @@ -499,6 +500,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_12.xsd b/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_12.xsd index e3ae492b20f..c3541f1971f 100644 --- a/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_12.xsd +++ b/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_12.xsd @@ -77,6 +77,7 @@ + @@ -495,6 +496,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java index eb2bbe45099..169a85e4316 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java @@ -128,6 +128,15 @@ protected void testForAllPreviousVersions(IidmVersion maxVersion, Consumer test) { + Stream.of(IidmVersion.values()) + .filter(v -> v.compareTo(minVersion) >= 0) + .forEach(test); + } + /** * Execute a write test for the given network, for all IIDM versions strictly older than a given maximum IIDM * version, and compare to the given versioned xml reference test resource. @@ -190,6 +199,7 @@ public Network allFormatsRoundTripTest(Network network, String refXmlFile) throw * * @param network the network to start with * @param filename the filename of the reference versioned file resource + * @param version the version to use for exporting and for the versioned filename * @return the Network read just before the end of the round trip */ public Network allFormatsRoundTripTest(Network network, String filename, IidmVersion version) throws IOException { diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ExportOptionsTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ExportOptionsTest.java index 23e85658b1c..f9c0a7df577 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ExportOptionsTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ExportOptionsTest.java @@ -46,8 +46,10 @@ void exportOptionsTest2() { ExportOptions options = new ExportOptions(); options.setCharset(StandardCharsets.ISO_8859_1); options.setExtensions(extensionsList); + options.setWithAutomationSystems(false); assertEquals(0, (int) options.getExtensions().map(Set::size).orElse(-1)); assertEquals(StandardCharsets.ISO_8859_1, options.getCharset()); + assertFalse(options.isWithAutomationSystems()); } @Test @@ -85,5 +87,6 @@ private void testDefaultExportOptions(ExportOptions options) { assertEquals(IidmSerDeConstants.CURRENT_IIDM_VERSION, options.getVersion()); assertEquals(THROW_EXCEPTION, options.getIidmVersionIncompatibilityBehavior()); assertEquals(StandardCharsets.UTF_8, options.getCharset()); + assertEquals(Boolean.TRUE, options.isWithAutomationSystems()); } } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ImportOptionsTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ImportOptionsTest.java index f39ca4e68f4..0a84c6e2943 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ImportOptionsTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ImportOptionsTest.java @@ -23,7 +23,9 @@ void importOptionsTest() { ImportOptions options = new ImportOptions(); Set extensionsList = Sets.newHashSet("loadFoo", "loadBar"); options.setExtensions(extensionsList); + options.setWithAutomationSystems(false); assertEquals(Boolean.FALSE, options.withNoExtension()); + assertEquals(Boolean.FALSE, options.isWithAutomationSystems()); options.addExtension("loadBar"); assertEquals(2, (int) options.getExtensions().map(Set::size).orElse(-1)); @@ -48,5 +50,6 @@ void importOptionsDefaultValues() { assertEquals(Boolean.FALSE, options.withNoExtension()); assertEquals(-1, (int) options.getExtensions().map(Set::size).orElse(-1)); assertEquals(Boolean.TRUE, options.withAllExtensions()); + assertEquals(Boolean.TRUE, options.isWithAutomationSystems()); } } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDeTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDeTest.java new file mode 100644 index 00000000000..325cfff4321 --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDeTest.java @@ -0,0 +1,338 @@ +/** + * 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.io.TreeDataFormat; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.serde.anonymizer.Anonymizer; +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; +import java.util.stream.Stream; + +import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; + +/** + * @author Olivier Perrin {@literal } + */ +class OverloadManagementSystemSerDeTest extends AbstractIidmSerDeTest { + + private static Network network; + + @BeforeAll + public static void setup() { + network = createNetwork(); + } + + @Test + void roundTripTest() throws IOException { + // backward compatibility + allFormatsRoundTripAllPreviousVersionedXmlTest("overloadManagementSystemRoundTripRef.xml"); + allFormatsRoundTripTest(network, "overloadManagementSystemRoundTripRef.xml", CURRENT_IIDM_VERSION); + } + + private record ExportResult(Anonymizer anonymizer, String content) { + } + + static Stream provideFormats() { + return Stream.of( + Arguments.of(TreeDataFormat.JSON), + Arguments.of(TreeDataFormat.XML) + ); + } + + @ParameterizedTest + @MethodSource("provideFormats") + void exportDisabledTest(TreeDataFormat format) { + testForAllVersionsSince(IidmVersion.V_1_12, v -> exportDisabledTest(format, v)); + } + + private void exportDisabledTest(TreeDataFormat format, IidmVersion version) { + // Export the network without the automation systems + ExportResult exportResult = writeNetwork(network, format, version, false); + // Check that the exported String does NOT contain OMS tags + Assertions.assertFalse(exportResult.content().contains(OverloadManagementSystemSerDe.ROOT_ELEMENT_NAME)); + // Load the exported String to check if it is really valid + Network networkOutput = readNetwork(format, exportResult, true); + // Check that the read network has substations, lines, ... but no OMS (none were exported) + checkNetworkAgainstRef(networkOutput, false); + } + + @ParameterizedTest + @MethodSource("provideFormats") + void importDisabledTest(TreeDataFormat format) { + testForAllVersionsSince(IidmVersion.V_1_12, v -> importDisabledTest(format, v)); + } + + private void importDisabledTest(TreeDataFormat format, IidmVersion version) { + // Export the network (with the automation systems) + ExportResult exportResult = writeNetwork(network, format, version, true); + // Check that the exported String DOES contain OMS tags + Assertions.assertTrue(exportResult.content().contains(OverloadManagementSystemSerDe.ROOT_ELEMENT_NAME)); + // Load the exported String without the automation systems + Network networkOutput = readNetwork(format, exportResult, false); + // Check that the read network has substations, lines, ... but no OMS (none were imported) + checkNetworkAgainstRef(networkOutput, false); + + // Final check: import the same network, but this time with the automation systems + // They should now be present + networkOutput = readNetwork(format, exportResult, true); + checkNetworkAgainstRef(networkOutput, true); + } + + @ParameterizedTest + @MethodSource("provideFormats") + void roundTripWithInvalidOverloadManagementSystemsTest(TreeDataFormat format) { + Network n = createNetwork(); + // Remove the monitoredElement of OMS2 + n.getLine("LINE_1").remove(); + ExportResult exportResult = writeNetwork(n, format, CURRENT_IIDM_VERSION, true); + Network networkOutput = readNetwork(format, exportResult, true); + Assertions.assertNotNull(networkOutput.getOverloadManagementSystem("OMS1")); + Assertions.assertNull(networkOutput.getOverloadManagementSystem("OMS2")); + + // Remove the 3 windings transformer (3WT) of the OMS1's 3WT tripping + n.getThreeWindingsTransformer("3WT").remove(); + exportResult = writeNetwork(n, format, CURRENT_IIDM_VERSION, true); + networkOutput = readNetwork(format, exportResult, true); + Assertions.assertNull(networkOutput.getOverloadManagementSystem("OMS1")); + + // Recreate the network + n = createNetwork(); + + // Remove the branch of the OMS2's tripping + n.getLine("LINE_2").remove(); + exportResult = writeNetwork(n, format, CURRENT_IIDM_VERSION, true); + networkOutput = readNetwork(format, exportResult, true); + Assertions.assertNotNull(networkOutput.getOverloadManagementSystem("OMS1")); + Assertions.assertNull(networkOutput.getOverloadManagementSystem("OMS2")); + + // No test on switch tripping because the switch cannot be removed with the API. + } + + private static ExportResult writeNetwork(Network n, TreeDataFormat format, IidmVersion version, boolean withAutomationSystems) { + ExportOptions options = new ExportOptions() + .setFormat(format) + .setWithAutomationSystems(withAutomationSystems) + .setVersion(version.toString(".")); + ExportResult exportResult; + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + Anonymizer anonymizer = NetworkSerDe.write(n, options, os); + String exportedContent = os.toString(StandardCharsets.UTF_8); + exportResult = new ExportResult(anonymizer, exportedContent); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + return exportResult; + } + + private static Network readNetwork(TreeDataFormat format, ExportResult exportResult, boolean withAutomationSystems) { + ImportOptions options = new ImportOptions() + .setFormat(format) + .setWithAutomationSystems(withAutomationSystems); + Network networkOutput; + try (InputStream is = IOUtils.toInputStream(exportResult.content(), "UTF-8")) { + networkOutput = NetworkSerDe.read(is, options, exportResult.anonymizer()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + return networkOutput; + } + + private static void checkNetworkAgainstRef(Network networkOutput, boolean shouldHaveAutomationSystems) { + Assertions.assertEquals(network.getSubstationCount(), networkOutput.getSubstationCount()); + Assertions.assertEquals(network.getLineCount(), networkOutput.getLineCount()); + Assertions.assertEquals(network.getTwoWindingsTransformerCount(), networkOutput.getTwoWindingsTransformerCount()); + Assertions.assertEquals(network.getThreeWindingsTransformerCount(), networkOutput.getThreeWindingsTransformerCount()); + int expectedNbAutomationSystem = shouldHaveAutomationSystems ? network.getOverloadManagementSystemCount() : 0; + Assertions.assertEquals(expectedNbAutomationSystem, networkOutput.getOverloadManagementSystemCount()); + } + + private static Network createNetwork() { + Network network = NetworkFactory.findDefault().createNetwork("fictitious", "test"); + network.setCaseDate(ZonedDateTime.parse("2024-01-02T15:00:00.000+01:00")); + network.setForecastDistance(0); + + // Create a substation "S1", with 3 voltage levels "S1_400", "S1_220" and "S1_90" + Substation s1 = network.newSubstation().setId("S1").add(); + VoltageLevel s1v400 = createVoltageLevel(s1, 400); + VoltageLevel s1v225 = createVoltageLevel(s1, 225); + VoltageLevel s1v90 = createVoltageLevel(s1, 90); + + // Create a substation "S2", with 1 voltage level "S2_400" + Substation s2 = network.newSubstation().setId("S2").add(); + VoltageLevel s2v400 = createVoltageLevel(s2, 400); + + // Create 2 lines between "S1_400" and "S2_400" ("LINE_1" and "LINE_2") + createLine(network, s1v400, s2v400, 1); + createLine(network, s1v400, s2v400, 2); + + // Create a 2-windings transformer between "S1_400" and "S1_225" ("2WT") + createTwoWindingsTransformer(s1, s1v400, s1v225); + + // Create a 3-windings transformer between "S1_400", "S1_225" and "S1_90" ("3WT") + createThreeWindingsTransformer(s1, s1v400, s1v225, s1v90); + + // Create an overload management system with trippings on "2WT", "3WT" and "S1_400_LINE_2_BREAKER" + s1.newOverloadManagementSystem() + .setId("OMS1") + .setName("1st OMS") + .setEnabled(true) + .setMonitoredElementId("2WT") + .setMonitoredElementSide(ThreeSides.TWO) + .newBranchTripping() + .setKey("tripping1") + .setName("1st tripping name") + .setCurrentLimit(1200) + .setOpenAction(true) + .setBranchToOperateId("2WT") + .setSideToOperate(TwoSides.ONE) + .add() + .newThreeWindingsTransformerTripping() + .setKey("tripping2") + .setName("2nd tripping name") + .setCurrentLimit(1000) + .setOpenAction(false) + .setThreeWindingsTransformerToOperateId("3WT") + .setSideToOperate(ThreeSides.ONE) + .add() + .newSwitchTripping() + .setKey("tripping3") + .setName("3rd tripping name") + .setCurrentLimit(1000) + .setOpenAction(true) + .setSwitchToOperateId("S1_400_LINE_2_BREAKER") + .add() + .add(); + + // 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 + // after the substations). This means that the referenced elements won't be already in the network in creation + // when the OMS will be read. + s1.newOverloadManagementSystem() + .setId("OMS2") + .setName("2nd OMS") + .setEnabled(true) + .setMonitoredElementId("LINE_1") + .setMonitoredElementSide(ThreeSides.ONE) + .newBranchTripping() + .setKey("tripping") + .setName("tripping name") + .setCurrentLimit(1300) + .setOpenAction(true) + .setBranchToOperateId("LINE_2") + .setSideToOperate(TwoSides.ONE) + .add() + .add(); + return network; + } + + private static VoltageLevel createVoltageLevel(Substation substation, int nominalV) { + String vlId = String.format("%s_%d", substation.getId(), nominalV); + VoltageLevel vl = substation.newVoltageLevel() + .setId(vlId) + .setNominalV(nominalV) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + vl.getNodeBreakerView().newBusbarSection() + .setId(vlId + "_BBS") + .setNode(0) + .add(); + return vl; + } + + private static void createLine(Network network, VoltageLevel s1v400, VoltageLevel s2v400, int nb) { + createSwitch(s1v400, "S1_400_LINE_" + nb + "_DISCONNECTOR", SwitchKind.DISCONNECTOR, 0, nb); + createSwitch(s1v400, "S1_400_LINE_" + nb + "_BREAKER", SwitchKind.BREAKER, nb, 10 + nb); + createSwitch(s2v400, "S2_400_LINE_" + nb + "_DISCONNECTOR", SwitchKind.DISCONNECTOR, 0, nb); + createSwitch(s2v400, "S2_400_LINE_" + nb + "_BREAKER", SwitchKind.BREAKER, nb, 10 + nb); + network.newLine() + .setId("LINE_" + nb) + .setR(0.01) + .setX(50) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .setNode1(10 + nb) + .setVoltageLevel1("S1_400") + .setNode2(10 + nb) + .setVoltageLevel2("S2_400") + .add(); + } + + private static void createTwoWindingsTransformer(Substation s1, VoltageLevel s1v400, VoltageLevel s1v225) { + createSwitch(s1v400, "S1_400_BBS_2WT_DISCONNECTOR", SwitchKind.DISCONNECTOR, 0, 13); + createSwitch(s1v400, "S1_400_2WT_BREAKER", SwitchKind.BREAKER, 13, 23); + createSwitch(s1v225, "S1_225_BBS_2WT_DISCONNECTOR", SwitchKind.DISCONNECTOR, 0, 13); + createSwitch(s1v225, "S1_225_2WT_BREAKER", SwitchKind.BREAKER, 13, 23); + s1.newTwoWindingsTransformer() + .setId("2WT") + .setR(2.0) + .setX(25) + .setG(0.0) + .setB(3.2E-5) + .setRatedU1(400.0) + .setRatedU2(225.0) + .setNode1(23) + .setVoltageLevel1("S1_400") + .setNode2(23) + .setVoltageLevel2("S1_225") + .add(); + } + + private static void createThreeWindingsTransformer(Substation s1, VoltageLevel s1v400, VoltageLevel s1v225, VoltageLevel s1v90) { + createSwitch(s1v400, "S1_400_BBS_3WT_DISCONNECTOR", SwitchKind.DISCONNECTOR, 0, 14); + createSwitch(s1v400, "S1_400_3WT_BREAKER", SwitchKind.BREAKER, 14, 24); + createSwitch(s1v225, "S1_225_BBS_3WT_DISCONNECTOR", SwitchKind.DISCONNECTOR, 0, 14); + createSwitch(s1v225, "S1_225_3WT_BREAKER", SwitchKind.BREAKER, 14, 24); + createSwitch(s1v90, "S1_90_BBS_3WT_DISCONNECTOR", SwitchKind.DISCONNECTOR, 0, 14); + createSwitch(s1v90, "S1_90_3WT_BREAKER", SwitchKind.BREAKER, 14, 24); + s1.newThreeWindingsTransformer() + .setId("3WT") + .setRatedU0(400) + .newLeg1() + .setR(0.001).setX(0.000001).setB(0).setG(0) + .setNode(24) + .setRatedU(400) + .setVoltageLevel("S1_400") + .add() + .newLeg2() + .setR(0.1).setX(0.00001).setB(0).setG(0) + .setNode(24) + .setRatedU(225) + .setVoltageLevel("S1_225") + .add() + .newLeg3() + .setR(0.01).setX(0.0001).setB(0).setG(0) + .setNode(24) + .setRatedU(90) + .setVoltageLevel("S1_90") + .add() + .add(); + } + + private static void createSwitch(VoltageLevel vl, String id, SwitchKind kind, int node1, int node2) { + vl.getNodeBreakerView().newSwitch() + .setId(id) + .setKind(kind) + .setOpen(true) + .setNode1(node1) + .setNode2(node2) + .add(); + } +} diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLExporterTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLExporterTest.java index b38b91254a2..d18d0e8dda7 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLExporterTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLExporterTest.java @@ -56,7 +56,7 @@ void exportTest() throws IOException { @Test void paramsTest() { var xmlExporter = new XMLExporter(); - assertEquals(10, xmlExporter.getParameters().size()); + assertEquals(11, xmlExporter.getParameters().size()); assertEquals("IIDM XML v" + CURRENT_IIDM_VERSION.toString(".") + " exporter", xmlExporter.getComment()); } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java index 01e194663fb..90294d2fc8b 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java @@ -137,7 +137,7 @@ void getFormat() { @Test void getParameters() { - assertEquals(2, importer.getParameters().size()); + assertEquals(3, importer.getParameters().size()); assertEquals("iidm.import.xml.throw-exception-if-extension-not-found", importer.getParameters().get(0).getName()); assertEquals(Arrays.asList("iidm.import.xml.throw-exception-if-extension-not-found", "throwExceptionIfExtensionNotFound"), importer.getParameters().get(0).getNames()); } diff --git a/iidm/iidm-serde/src/test/resources/V1_0/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_0/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..57741decbc2 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_0/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_1/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_1/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..24815f6c2e8 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_1/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_10/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_10/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..b7d479a61e8 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_10/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_11/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_11/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..2ac9f768f2a --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_11/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_12/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_12/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..027422ab9d7 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_12/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_2/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_2/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..18aadca2b1f --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_2/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_3/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_3/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..5ce6c74523e --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_3/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_4/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_4/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..5cccf75ba2b --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_4/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_5/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_5/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..e1a3fa3787f --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_5/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_6/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_6/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..4c9c25e0450 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_6/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_7/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_7/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..c6866f9cbec --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_7/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_8/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_8/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..3a06ecbcfa5 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_8/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_9/overloadManagementSystemRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_9/overloadManagementSystemRoundTripRef.xml new file mode 100644 index 00000000000..ac324374aa4 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_9/overloadManagementSystemRoundTripRef.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +