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 09e8a58c927..f8708271bd0 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 @@ -24,6 +24,9 @@ protected boolean isValid(T identifiable, P parent) { protected abstract void writeRootElementAttributes(T identifiable, P parent, NetworkSerializerContext context); + protected void addExtinctExtensions(T identifiable, NetworkSerializerContext context) { + } + protected void writeSubElements(T identifiable, P parent, NetworkSerializerContext context) { } @@ -38,6 +41,7 @@ public final void write(T identifiable, P parent, NetworkSerializerContext conte IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_2, context, () -> context.getWriter().writeBooleanAttribute("fictitious", identifiable.isFictitious(), false)); writeRootElementAttributes(identifiable, parent, context); + addExtinctExtensions(identifiable, context); IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_3, context, () -> AliasesSerDe.write(identifiable, getRootElementName(), context)); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BatterySerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BatterySerDe.java index e6c6adfbbfe..fa83e4494b1 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BatterySerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BatterySerDe.java @@ -10,6 +10,8 @@ import com.powsybl.iidm.network.Battery; import com.powsybl.iidm.network.BatteryAdder; import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.iidm.network.extensions.removed.VoltageRegulationExtension; +import com.powsybl.iidm.network.regulation.RegulationMode; import com.powsybl.iidm.serde.util.IidmSerDeUtil; import static com.powsybl.iidm.serde.ConnectableSerDeUtil.*; @@ -43,6 +45,17 @@ protected void writeRootElementAttributes(Battery b, VoltageLevel vl, NetworkSer writePQ(null, b.getTerminal(), context.getWriter()); } + @Override + protected void addExtinctExtensions(Battery b, NetworkSerializerContext context) { + if (com.powsybl.iidm.serde.extensions.VoltageRegulationSerDe.isExtensionNeededAndExportable(b, context)) { + VoltageRegulationExtension extension = new VoltageRegulationExtension(b, + b.getVoltageRegulation().getTerminal(), + b.getVoltageRegulation().getMode() == RegulationMode.VOLTAGE, + b.getVoltageRegulation().getTargetValue()); + context.addExtinctExtensionsToSerialize(b.getId(), extension); + } + } + @Override protected void writeSubElements(Battery b, VoltageLevel vl, NetworkSerializerContext context) { ReactiveLimitsSerDe.INSTANCE.write(b, context); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java index 84f6d955795..5952b7aa16c 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java @@ -8,7 +8,9 @@ package com.powsybl.iidm.serde; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.removed.RemoteReactivePowerControl; import com.powsybl.iidm.network.regulation.RegulationMode; +import com.powsybl.iidm.serde.extensions.RemoteReactivePowerControlSerDe; import com.powsybl.iidm.serde.util.IidmSerDeUtil; import java.util.concurrent.atomic.AtomicReference; @@ -90,6 +92,17 @@ private static void writeTargetQByVersion(Generator g, NetworkSerializerContext context.getWriter().writeDoubleAttribute("targetQ", g.getTargetQ())); } + @Override + protected void addExtinctExtensions(Generator g, NetworkSerializerContext context) { + if (RemoteReactivePowerControlSerDe.isExtensionNeededAndExportable(g, context)) { + RemoteReactivePowerControl extension = new RemoteReactivePowerControl(g, + g.getVoltageRegulation().getTargetValue(), + g.getVoltageRegulation().getTerminal(), + g.getVoltageRegulation().isRegulating()); + context.addExtinctExtensionsToSerialize(g.getId(), extension); + } + } + @Override protected void writeSubElements(Generator g, VoltageLevel vl, NetworkSerializerContext context) { IidmSerDeUtil.runUntilMaximumVersion(IidmVersion.V_1_15, context, () -> { 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 9679009c769..2386b6e149d 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 @@ -29,14 +29,9 @@ import com.powsybl.commons.xml.XmlReader; import com.powsybl.commons.xml.XmlWriter; import com.powsybl.iidm.network.*; -import com.powsybl.iidm.network.extensions.removed.RemoteReactivePowerControl; -import com.powsybl.iidm.network.extensions.removed.VoltageRegulationExtension; -import com.powsybl.iidm.network.regulation.RegulationMode; import com.powsybl.iidm.serde.anonymizer.Anonymizer; import com.powsybl.iidm.serde.anonymizer.SimpleAnonymizer; -import com.powsybl.iidm.serde.extensions.AbstractVersionableNetworkExtensionSerDe; -import com.powsybl.iidm.serde.extensions.RemoteReactivePowerControlSerDe; -import com.powsybl.iidm.serde.extensions.VoltageRegulationSerDe; +import com.powsybl.iidm.serde.extensions.*; import com.powsybl.iidm.serde.extensions.util.DefaultExtensionsSupplier; import com.powsybl.iidm.serde.extensions.util.ExtensionsSupplier; import com.powsybl.iidm.serde.util.IidmSerDeUtil; @@ -81,6 +76,55 @@ public final class NetworkSerDe { static final String NETWORK_ARRAY_ELEMENT_NAME = "networks"; private static final String EXTENSION_ROOT_ELEMENT_NAME = "extension"; private static final String EXTENSION_ARRAY_ELEMENT_NAME = "extensions"; + + private static final Map BASIC_MAP = Map.ofEntries( + Map.entry(NETWORK_ARRAY_ELEMENT_NAME, NETWORK_ROOT_ELEMENT_NAME), + Map.entry(EXTENSION_ARRAY_ELEMENT_NAME, EXTENSION_ROOT_ELEMENT_NAME), + Map.entry(AbstractSwitchSerDe.ARRAY_ELEMENT_NAME, AbstractSwitchSerDe.ROOT_ELEMENT_NAME), + Map.entry(AbstractTransformerSerDe.STEP_ARRAY_ELEMENT_NAME, AbstractTransformerSerDe.STEP_ROOT_ELEMENT_NAME), + Map.entry(AliasesSerDe.ARRAY_ELEMENT_NAME, AliasesSerDe.ROOT_ELEMENT_NAME), + Map.entry(AreaSerDe.ARRAY_ELEMENT_NAME, AreaSerDe.ROOT_ELEMENT_NAME), + Map.entry(BatterySerDe.ARRAY_ELEMENT_NAME, BatterySerDe.ROOT_ELEMENT_NAME), + Map.entry(AreaBoundarySerDe.ARRAY_ELEMENT_NAME, AreaBoundarySerDe.ROOT_ELEMENT_NAME), + Map.entry(BusSerDe.ARRAY_ELEMENT_NAME, BusSerDe.ROOT_ELEMENT_NAME), + Map.entry(BusbarSectionSerDe.ARRAY_ELEMENT_NAME, BusbarSectionSerDe.ROOT_ELEMENT_NAME), + Map.entry(ConnectableSerDeUtil.TEMPORARY_LIMITS_ARRAY_ELEMENT_NAME, ConnectableSerDeUtil.TEMPORARY_LIMITS_ROOT_ELEMENT_NAME), + Map.entry(DanglingLineSerDe.ARRAY_ELEMENT_NAME, DanglingLineSerDe.ROOT_ELEMENT_NAME), + Map.entry(DcNodeSerDe.ARRAY_ELEMENT_NAME, DcNodeSerDe.ROOT_ELEMENT_NAME), + Map.entry(DcGroundSerDe.ARRAY_ELEMENT_NAME, DcGroundSerDe.ROOT_ELEMENT_NAME), + Map.entry(DcLineSerDe.ARRAY_ELEMENT_NAME, DcLineSerDe.ROOT_ELEMENT_NAME), + Map.entry(DcSwitchSerDe.ARRAY_ELEMENT_NAME, DcSwitchSerDe.ROOT_ELEMENT_NAME), + Map.entry(DroopCurveSerDe.ARRAY_ELEMENT_NAME, DroopCurveSerDe.ROOT_ELEMENT_NAME), + Map.entry(GeneratorSerDe.ARRAY_ELEMENT_NAME, GeneratorSerDe.ROOT_ELEMENT_NAME), + Map.entry(HvdcLineSerDe.ARRAY_ELEMENT_NAME, HvdcLineSerDe.ROOT_ELEMENT_NAME), + Map.entry(LccConverterStationSerDe.ARRAY_ELEMENT_NAME, LccConverterStationSerDe.ROOT_ELEMENT_NAME), + Map.entry(LineSerDe.ARRAY_ELEMENT_NAME, LineSerDe.ROOT_ELEMENT_NAME), + Map.entry(LineCommutatedConverterSerDe.ARRAY_ELEMENT_NAME, LineCommutatedConverterSerDe.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), //For backward compatibility with IIDM versions < 1.16 + Map.entry(AbstractShuntCompensatorSerDe.SECTION_ARRAY_ELEMENT_NAME, AbstractShuntCompensatorSerDe.SECTION_ROOT_ELEMENT_NAME), + Map.entry(ShuntCompensatorSerDe.ARRAY_ELEMENT_NAME, ShuntCompensatorSerDe.ROOT_ELEMENT_NAME), + Map.entry(StaticVarCompensatorSerDe.ARRAY_ELEMENT_NAME, StaticVarCompensatorSerDe.ROOT_ELEMENT_NAME), + Map.entry(SubstationSerDe.ARRAY_ELEMENT_NAME, SubstationSerDe.ROOT_ELEMENT_NAME), + Map.entry(ThreeWindingsTransformerSerDe.ARRAY_ELEMENT_NAME, ThreeWindingsTransformerSerDe.ROOT_ELEMENT_NAME), + Map.entry(TieLineSerDe.ARRAY_ELEMENT_NAME, TieLineSerDe.ROOT_ELEMENT_NAME), + Map.entry(TwoWindingsTransformerSerDe.ARRAY_ELEMENT_NAME, TwoWindingsTransformerSerDe.ROOT_ELEMENT_NAME), + Map.entry(VoltageAngleLimitSerDe.ARRAY_ELEMENT_NAME, VoltageAngleLimitSerDe.ROOT_ELEMENT_NAME), + Map.entry(VoltageLevelSerDe.ARRAY_ELEMENT_NAME, VoltageLevelSerDe.ROOT_ELEMENT_NAME), + Map.entry(VoltageLevelSerDe.INJ_ARRAY_ELEMENT_NAME, VoltageLevelSerDe.INJ_ROOT_ELEMENT_NAME), + Map.entry(VoltageSourceConverterSerDe.ARRAY_ELEMENT_NAME, VoltageSourceConverterSerDe.ROOT_ELEMENT_NAME), + Map.entry(VscConverterStationSerDe.ARRAY_ELEMENT_NAME, VscConverterStationSerDe.ROOT_ELEMENT_NAME), + Map.entry(GroundSerDe.ARRAY_ELEMENT_NAME, GroundSerDe.ROOT_ELEMENT_NAME), + Map.entry(ConnectableSerDeUtil.LIMITS_GROUPS, ConnectableSerDeUtil.LIMITS_GROUP), + Map.entry(ConnectableSerDeUtil.LIMITS_GROUPS_1, ConnectableSerDeUtil.LIMITS_GROUP_1), + Map.entry(ConnectableSerDeUtil.LIMITS_GROUPS_2, ConnectableSerDeUtil.LIMITS_GROUP_2), + Map.entry(ConnectableSerDeUtil.LIMITS_GROUPS_3, ConnectableSerDeUtil.LIMITS_GROUP_3) + ); + private static final String CASE_DATE = "caseDate"; private static final String FORECAST_DISTANCE = "forecastDistance"; private static final String SOURCE_FORMAT = "sourceFormat"; @@ -213,17 +257,17 @@ private static void writeVoltageAngleLimits(Network n, NetworkSerializerContext private static void writeExtensions(Network n, NetworkSerializerContext context, ExtensionsSupplier extensionsSupplier) { context.getWriter().writeStartNodes(); - Map>>> removedExtensions = getRemovedExtension(n, context.getOptions(), extensionsSupplier); for (Identifiable identifiable : IidmSerDeUtil.sorted(n.getIdentifiables(), context.getOptions())) { if (ignoreEquipmentAtExport(identifiable, context) || !isElementWrittenInsideNetwork(identifiable, n, context)) { continue; } - Collection>> identifiableExtensions = concatExtensionsAndRemovedExtensions(identifiable, removedExtensions); - Collection>> extensions = identifiableExtensions.stream() - .filter(e -> - isExtensionIncluded(getExtensionSerializer(context.getOptions(), e, extensionsSupplier), context.getOptions()) && - canTheExtensionBeWritten(getExtensionSerializer(context.getOptions(), e, extensionsSupplier), context.getVersion(), context.getOptions())) - .toList(); + Collection>> extensions = + Stream.concat(identifiable.getExtensions().stream(), context.getExtinctExtensionsToSerialize(identifiable.getId())) + .filter(e -> { + ExtensionSerDe extensionSerializer = getExtensionSerializer(context.getOptions(), e, extensionsSupplier); + return isExtensionIncluded(extensionSerializer, context.getOptions()) && + canTheExtensionBeWritten(extensionSerializer, context.getVersion(), context.getOptions()); + }).toList(); if (!extensions.isEmpty()) { context.getWriter().writeStartNode(context.getNamespaceURI(), EXTENSION_ROOT_ELEMENT_NAME); @@ -272,16 +316,15 @@ private static void writeMainAttributes(Network n, NetworkSerializerContext cont context.getWriter().writeStringAttribute(SOURCE_FORMAT, n.getSourceFormat()); } - private static XmlWriter createXmlWriter(Network n, OutputStream os, ExportOptions options, ExtensionsSupplier extensionsSupplier) { + private static XmlWriter createXmlWriter(Network n, OutputStream os, ExportOptions options, Set> usedExtensionSerDes) { try { String iidmNamespace = options.getVersion().getNamespaceURI(n.getValidationLevel() == ValidationLevel.STEADY_STATE_HYPOTHESIS); String indent = options.isIndent() ? INDENT : null; XmlWriter xmlWriter = new XmlWriter(os, indent, options.getCharset(), iidmNamespace, IIDM_PREFIX); - Set> serializers = getExtensionSerializers(n, options, extensionsSupplier); Set extensionUris = new HashSet<>(); Set extensionPrefixes = new HashSet<>(); - for (ExtensionSerDe extensionSerDe : serializers) { + for (ExtensionSerDe extensionSerDe : usedExtensionSerDes) { String extensionVersion = getExtensionVersion(extensionSerDe, options); String namespaceUri = extensionSerDe.getNamespaceUri(extensionVersion); String realNamespacePrefix = extensionSerDe.getNamespacePrefix(extensionVersion); @@ -315,54 +358,9 @@ private static XmlWriter createXmlWriter(Network n, OutputStream os, ExportOptio } } - private static Map>>> getRemovedExtension(Network n, ExportOptions options, ExtensionsSupplier extensionsSupplier) { - Map>>> mapMsa = new HashMap<>(); - var extensionsWithProvider = extensionsSupplier.get().getProviders(); - var version = options.getVersion(); - if (version.compareTo(IidmVersion.V_1_16) < 0) { - if (extensionsWithProvider.stream() - .anyMatch(VoltageRegulationSerDe.class::isInstance)) { - buildRemovedVoltageRegulationExtension(n, mapMsa); - } - if (extensionsWithProvider.stream() - .anyMatch(RemoteReactivePowerControlSerDe.class::isInstance)) { - n.getGeneratorStream() - .filter(g -> g.getVoltageRegulation() != null - && g.getVoltageRegulation().getTerminal() != null - && g.getVoltageRegulation().getMode() == RegulationMode.REACTIVE_POWER) - .forEach(g -> { - mapMsa.putIfAbsent(g.getId(), new HashSet<>()); - // - RemoteReactivePowerControl removedExtension = new RemoteReactivePowerControl(g, - g.getVoltageRegulation().getTargetValue(), - g.getVoltageRegulation().getTerminal(), - g.getVoltageRegulation().isRegulating()); - // - mapMsa.get(g.getId()).add(removedExtension); - }); - } - } - return mapMsa; - } - - private static void buildRemovedVoltageRegulationExtension(Network n, Map>>> mapMsa) { - n.getBatteryStream() - .filter(b -> b.getVoltageRegulation() != null) - .forEach(b -> { - mapMsa.putIfAbsent(b.getId(), new HashSet<>()); - // - VoltageRegulationExtension removedExtension = new VoltageRegulationExtension(b, - b.getVoltageRegulation().getTerminal(), - b.getVoltageRegulation().getMode() == RegulationMode.VOLTAGE, - b.getVoltageRegulation().getTargetValue()); - // - mapMsa.get(b.getId()).add(removedExtension); - }); - } - - private static JsonWriter createJsonWriter(OutputStream os, ExportOptions options, ExtensionsSupplier extensionsSupplier) { + private static JsonWriter createJsonWriter(OutputStream os, ExportOptions options, Set> usedExtensionSerDes) { try { - return new JsonWriter(os, options.isIndent(), options.getVersion().toString("."), createSingleNameToArrayNameMap(options, extensionsSupplier)); + return new JsonWriter(os, options.isIndent(), options.getVersion().toString("."), createSingleNameToArrayNameMap(options, usedExtensionSerDes)); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -380,9 +378,9 @@ private static void writeRootElement(Network n, NetworkSerializerContext context writeMainAttributes(n, context); } - private static Map getExtensionVersions(Network n, ExportOptions options, ExtensionsSupplier extensionsSupplier) { + private static Map getExtensionVersions(ExportOptions options, Set> usedExtensionSerDes) { Map extensionVersionsMap = new LinkedHashMap<>(); - for (ExtensionSerDe extensionSerDe : getExtensionSerializers(n, options, extensionsSupplier)) { + for (ExtensionSerDe extensionSerDe : usedExtensionSerDes) { String version = getExtensionVersion(extensionSerDe, options); extensionVersionsMap.put(extensionSerDe.getExtensionName(), version); } @@ -407,21 +405,21 @@ private static String getExtensionVersion(ExtensionSerDe extensionSerDe, E if (options.withNoExtension()) { return Collections.emptySet(); } - Map>>> phantomSerializers = getRemovedExtension(n, options, extensionsSupplier); + // Collect the SerDes for the extensions that don't exist anymore, but that should be exported for backward-compatibility + Stream> extinctExtensionSerDes = ExtinctExtensionSerDeService.findAll().stream() + .filter(s -> s.isExtensionNeededAndExportable(n, options)) + .map(s -> (ExtensionSerDe) s); + // Collect the SerDes for the "classic" extensions + // Extinct extensions won't be retrieved by the following stream: they don't exist anymore, so they cannot be present on the identifiables IidmVersion networkVersion = options.getVersion(); - return n.getIdentifiables().stream().flatMap(identifiable -> concatExtensionsAndRemovedExtensions(identifiable, phantomSerializers) - .stream() - .map(extension -> (ExtensionSerDe) getExtensionSerializer(options, extension, extensionsSupplier)) - .filter(exs -> canTheExtensionBeWritten(exs, networkVersion, options))) - .collect(Collectors.toCollection(LinkedHashSet::new)); - } + Stream> classicExtensionSerDes = n.getIdentifiables().stream() + .flatMap(identifiable -> identifiable.getExtensions().stream()) + .map(extension -> (ExtensionSerDe) getExtensionSerializer(options, extension, extensionsSupplier)); - private static Collection>> concatExtensionsAndRemovedExtensions(Identifiable identifiable, Map>>> removedExtensions) { - if (removedExtensions.containsKey(identifiable.getId())) { - return Stream.concat(identifiable.getExtensions().stream(), removedExtensions.get(identifiable.getId()).stream()).toList(); - } - return identifiable.getExtensions(); + return Stream.concat(classicExtensionSerDes, extinctExtensionSerDes) + .filter(exs -> canTheExtensionBeWritten(exs, networkVersion, options)) + .collect(Collectors.toCollection(LinkedHashSet::new)); } private static void writeBaseNetwork(Network n, NetworkSerializerContext context, ExtensionsSupplier extensionsSupplier) { @@ -581,10 +579,11 @@ private static void writeHvdcLines(Network n, NetworkSerializerContext context) context.getWriter().writeEndNodes(); } - private static TreeDataWriter createTreeDataWriter(Network n, ExportOptions options, OutputStream os, ExtensionsSupplier extensionsSupplier) { + private static TreeDataWriter createTreeDataWriter(Network n, ExportOptions options, OutputStream os, + Set> usedExtensionSerDes) { return switch (options.getFormat()) { - case XML -> createXmlWriter(n, os, options, extensionsSupplier); - case JSON -> createJsonWriter(os, options, extensionsSupplier); + case XML -> createXmlWriter(n, os, options, usedExtensionSerDes); + case JSON -> createJsonWriter(os, options, usedExtensionSerDes); case BIN -> createBinWriter(os, options); }; } @@ -605,9 +604,10 @@ public static Anonymizer write(Network n, ExportOptions options, OutputStream os } public static Anonymizer write(Network n, ExportOptions options, OutputStream os, ExtensionsSupplier extensionsSupplier) { - try (TreeDataWriter writer = createTreeDataWriter(n, options, os, extensionsSupplier)) { + Set> usedExtensionSerDes = getExtensionSerializers(n, options, extensionsSupplier); + try (TreeDataWriter writer = createTreeDataWriter(n, options, os, usedExtensionSerDes)) { NetworkSerializerContext context = createContext(n, options, writer); - writer.setVersions(getExtensionVersions(n, options, extensionsSupplier)); + writer.setVersions(getExtensionVersions(options, usedExtensionSerDes)); write(n, context, extensionsSupplier); return context.getAnonymizer(); } @@ -713,8 +713,9 @@ private static TreeDataReader createXmlReader(InputStream is, ImportOptions conf } } - private static Map createSingleNameToArrayNameMap(ExportOptions config, ExtensionsSupplier extensionsSupplier) { - return createArrayNameSingleNameBiMap(!config.withNoExtension(), extensionsSupplier).inverse(); + private static Map createSingleNameToArrayNameMap(ExportOptions config, Set> usedExtensionSerDes) { + return createArrayNameSingleNameBiMap(!config.withNoExtension(), + usedExtensionSerDes.stream().map(s -> (ExtensionSerDe) s).toList()).inverse(); } private static Map createArrayNameToSingleNameMap(ImportOptions config, ExtensionsSupplier extensionsSupplier) { @@ -722,63 +723,18 @@ private static Map createArrayNameToSingleNameMap(ImportOptions } private static BiMap createArrayNameSingleNameBiMap(boolean withExtensions, ExtensionsSupplier extensionsSupplier) { - Map basicMap = Map.ofEntries( - Map.entry(NETWORK_ARRAY_ELEMENT_NAME, NETWORK_ROOT_ELEMENT_NAME), - Map.entry(EXTENSION_ARRAY_ELEMENT_NAME, EXTENSION_ROOT_ELEMENT_NAME), - Map.entry(AbstractSwitchSerDe.ARRAY_ELEMENT_NAME, AbstractSwitchSerDe.ROOT_ELEMENT_NAME), - Map.entry(AbstractTransformerSerDe.STEP_ARRAY_ELEMENT_NAME, AbstractTransformerSerDe.STEP_ROOT_ELEMENT_NAME), - Map.entry(AliasesSerDe.ARRAY_ELEMENT_NAME, AliasesSerDe.ROOT_ELEMENT_NAME), - Map.entry(AreaSerDe.ARRAY_ELEMENT_NAME, AreaSerDe.ROOT_ELEMENT_NAME), - Map.entry(BatterySerDe.ARRAY_ELEMENT_NAME, BatterySerDe.ROOT_ELEMENT_NAME), - Map.entry(AreaBoundarySerDe.ARRAY_ELEMENT_NAME, AreaBoundarySerDe.ROOT_ELEMENT_NAME), - Map.entry(BusSerDe.ARRAY_ELEMENT_NAME, BusSerDe.ROOT_ELEMENT_NAME), - Map.entry(BusbarSectionSerDe.ARRAY_ELEMENT_NAME, BusbarSectionSerDe.ROOT_ELEMENT_NAME), - Map.entry(ConnectableSerDeUtil.TEMPORARY_LIMITS_ARRAY_ELEMENT_NAME, ConnectableSerDeUtil.TEMPORARY_LIMITS_ROOT_ELEMENT_NAME), - Map.entry(DanglingLineSerDe.ARRAY_ELEMENT_NAME, DanglingLineSerDe.ROOT_ELEMENT_NAME), - Map.entry(DcNodeSerDe.ARRAY_ELEMENT_NAME, DcNodeSerDe.ROOT_ELEMENT_NAME), - Map.entry(DcGroundSerDe.ARRAY_ELEMENT_NAME, DcGroundSerDe.ROOT_ELEMENT_NAME), - Map.entry(DcLineSerDe.ARRAY_ELEMENT_NAME, DcLineSerDe.ROOT_ELEMENT_NAME), - Map.entry(DcSwitchSerDe.ARRAY_ELEMENT_NAME, DcSwitchSerDe.ROOT_ELEMENT_NAME), - Map.entry(DroopCurveSerDe.ARRAY_ELEMENT_NAME, DroopCurveSerDe.ROOT_ELEMENT_NAME), - Map.entry(GeneratorSerDe.ARRAY_ELEMENT_NAME, GeneratorSerDe.ROOT_ELEMENT_NAME), - Map.entry(HvdcLineSerDe.ARRAY_ELEMENT_NAME, HvdcLineSerDe.ROOT_ELEMENT_NAME), - Map.entry(LccConverterStationSerDe.ARRAY_ELEMENT_NAME, LccConverterStationSerDe.ROOT_ELEMENT_NAME), - Map.entry(LineSerDe.ARRAY_ELEMENT_NAME, LineSerDe.ROOT_ELEMENT_NAME), - Map.entry(LineCommutatedConverterSerDe.ARRAY_ELEMENT_NAME, LineCommutatedConverterSerDe.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), //For backward compatibility with IIDM versions < 1.16 - Map.entry(AbstractShuntCompensatorSerDe.SECTION_ARRAY_ELEMENT_NAME, AbstractShuntCompensatorSerDe.SECTION_ROOT_ELEMENT_NAME), - Map.entry(ShuntCompensatorSerDe.ARRAY_ELEMENT_NAME, ShuntCompensatorSerDe.ROOT_ELEMENT_NAME), - Map.entry(StaticVarCompensatorSerDe.ARRAY_ELEMENT_NAME, StaticVarCompensatorSerDe.ROOT_ELEMENT_NAME), - Map.entry(SubstationSerDe.ARRAY_ELEMENT_NAME, SubstationSerDe.ROOT_ELEMENT_NAME), - Map.entry(ThreeWindingsTransformerSerDe.ARRAY_ELEMENT_NAME, ThreeWindingsTransformerSerDe.ROOT_ELEMENT_NAME), - Map.entry(TieLineSerDe.ARRAY_ELEMENT_NAME, TieLineSerDe.ROOT_ELEMENT_NAME), - Map.entry(TwoWindingsTransformerSerDe.ARRAY_ELEMENT_NAME, TwoWindingsTransformerSerDe.ROOT_ELEMENT_NAME), - Map.entry(VoltageAngleLimitSerDe.ARRAY_ELEMENT_NAME, VoltageAngleLimitSerDe.ROOT_ELEMENT_NAME), - Map.entry(VoltageLevelSerDe.ARRAY_ELEMENT_NAME, VoltageLevelSerDe.ROOT_ELEMENT_NAME), - Map.entry(VoltageLevelSerDe.INJ_ARRAY_ELEMENT_NAME, VoltageLevelSerDe.INJ_ROOT_ELEMENT_NAME), - Map.entry(VoltageSourceConverterSerDe.ARRAY_ELEMENT_NAME, VoltageSourceConverterSerDe.ROOT_ELEMENT_NAME), - Map.entry(VscConverterStationSerDe.ARRAY_ELEMENT_NAME, VscConverterStationSerDe.ROOT_ELEMENT_NAME), - Map.entry(GroundSerDe.ARRAY_ELEMENT_NAME, GroundSerDe.ROOT_ELEMENT_NAME), - Map.entry(ConnectableSerDeUtil.LIMITS_GROUPS, ConnectableSerDeUtil.LIMITS_GROUP), - Map.entry(ConnectableSerDeUtil.LIMITS_GROUPS_1, ConnectableSerDeUtil.LIMITS_GROUP_1), - Map.entry(ConnectableSerDeUtil.LIMITS_GROUPS_2, ConnectableSerDeUtil.LIMITS_GROUP_2), - Map.entry(ConnectableSerDeUtil.LIMITS_GROUPS_3, ConnectableSerDeUtil.LIMITS_GROUP_3) - ); + return createArrayNameSingleNameBiMap(withExtensions, extensionsSupplier.get().getProviders()); + } + private static BiMap createArrayNameSingleNameBiMap(boolean withExtensions, Collection extensionSerDes) { Map extensionsMap = new HashMap<>(); if (withExtensions) { - for (ExtensionSerDe e : extensionsSupplier.get().getProviders()) { + for (ExtensionSerDe e : extensionSerDes) { extensionsMap.putAll(e.getArrayNameToSingleNameMap()); } } - BiMap biMergedMap = HashBiMap.create(); - biMergedMap.putAll(basicMap); + biMergedMap.putAll(BASIC_MAP); biMergedMap.putAll(extensionsMap); return biMergedMap; } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerializerContext.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerializerContext.java index 041243e6b86..fa54782d9fc 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerializerContext.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerializerContext.java @@ -7,6 +7,7 @@ */ package com.powsybl.iidm.serde; +import com.powsybl.commons.extensions.Extension; import com.powsybl.commons.io.SerializerContext; import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.iidm.network.Identifiable; @@ -14,6 +15,7 @@ import com.powsybl.iidm.serde.anonymizer.Anonymizer; import java.util.*; +import java.util.stream.Stream; /** * @author Geoffroy Jamgotchian {@literal } @@ -26,6 +28,7 @@ public class NetworkSerializerContext extends AbstractNetworkSerDeContext exportedEquipments; private final Map voltageLevelExportTopologyLevel; + private final Map>>> extinctExtensionsToSerializeByIdentifiable; NetworkSerializerContext(Anonymizer anonymizer, TreeDataWriter writer, ExportOptions options, BusFilter filter, IidmVersion version, boolean valid) { super(anonymizer, version); @@ -35,6 +38,7 @@ public class NetworkSerializerContext extends AbstractNetworkSerDeContext(); this.voltageLevelExportTopologyLevel = new HashMap<>(); + this.extinctExtensionsToSerializeByIdentifiable = new HashMap<>(); } @Override @@ -84,4 +88,14 @@ public void addVoltageLevelExportTopologyLevel(String voltageLevelId, TopologyLe public TopologyLevel getVoltageLevelExportTopologyLevel(String voltageLevelId) { return voltageLevelExportTopologyLevel.get(voltageLevelId); } + + public void addExtinctExtensionsToSerialize(String identifiableId, Extension> extension) { + extinctExtensionsToSerializeByIdentifiable.computeIfAbsent(identifiableId, k -> new ArrayList<>()).add(extension); + } + + public Stream>> getExtinctExtensionsToSerialize(String identifiableId) { + return extinctExtensionsToSerializeByIdentifiable.containsKey(identifiableId) ? + extinctExtensionsToSerializeByIdentifiable.get(identifiableId).stream() + .map(e -> (Extension>) e) : Stream.empty(); + } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ExtinctExtensionSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ExtinctExtensionSerDe.java new file mode 100644 index 00000000000..5606e3b85ab --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ExtinctExtensionSerDe.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026, 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.extensions; + +import com.powsybl.commons.extensions.Extendable; +import com.powsybl.commons.extensions.Extension; +import com.powsybl.commons.extensions.ExtensionSerDe; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.serde.ExportOptions; +import com.powsybl.iidm.serde.IidmVersion; + +/** + *

Interface for the (de)serialization of extensions that no longer exists.

+ *

It is useful for backward compatibility purposes.

+ * + * @author Olivier Perrin {@literal } + */ +public interface ExtinctExtensionSerDe> extends ExtensionSerDe { + + default boolean isExtensionNeededAndExportable(Network network, ExportOptions options) { + return isExtensionExportable(options) && isExtensionNeeded(network); + } + + private boolean isExtensionExportable(ExportOptions options) { + return isExtensionExportable(options, getName(), getLastSupportedVersion()); + } + + static boolean isExtensionExportable(ExportOptions options, String extensionName, IidmVersion lastSupportedVersion) { + return options.withExtension(extensionName) && options.getVersion().compareTo(lastSupportedVersion) <= 0; + } + + IidmVersion getLastSupportedVersion(); + + boolean isExtensionNeeded(Network network); +} diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ExtinctExtensionSerDeService.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ExtinctExtensionSerDeService.java new file mode 100644 index 00000000000..69443c323f8 --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ExtinctExtensionSerDeService.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026, 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.extensions; + +import com.powsybl.commons.util.ServiceLoaderCache; + +import java.util.List; + +/** + * @author Olivier Perrin {@literal } + */ +public final class ExtinctExtensionSerDeService { + + private static final ServiceLoaderCache EXTINCT_SERDE_SERVICE = new ServiceLoaderCache<>(ExtinctExtensionSerDe.class); + + private ExtinctExtensionSerDeService() { + } + + public static List findAll() { + return EXTINCT_SERDE_SERVICE.getServices(); + } + +} diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/RemoteReactivePowerControlSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/RemoteReactivePowerControlSerDe.java index eceb5d30a7e..8090f79f3d3 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/RemoteReactivePowerControlSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/RemoteReactivePowerControlSerDe.java @@ -13,6 +13,7 @@ import com.powsybl.commons.io.DeserializerContext; import com.powsybl.commons.io.SerializerContext; import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.Terminal; import com.powsybl.iidm.network.extensions.removed.RemoteReactivePowerControl; import com.powsybl.iidm.network.regulation.RegulationMode; @@ -25,8 +26,11 @@ /** * @author Damien Jeandemange {@literal } */ -@AutoService(ExtensionSerDe.class) -public class RemoteReactivePowerControlSerDe extends AbstractExtensionSerDe { +@AutoService({ExtensionSerDe.class, ExtinctExtensionSerDe.class}) +public class RemoteReactivePowerControlSerDe extends AbstractExtensionSerDe + implements ExtinctExtensionSerDe { + + public static final IidmVersion LAST_SUPPORTED_VERSION = IidmVersion.V_1_15; public RemoteReactivePowerControlSerDe() { super(RemoteReactivePowerControl.NAME, "network", RemoteReactivePowerControl.class, @@ -62,4 +66,25 @@ public RemoteReactivePowerControl read(Generator extendable, DeserializerContext } return null; } + + @Override + public IidmVersion getLastSupportedVersion() { + return LAST_SUPPORTED_VERSION; + } + + @Override + public boolean isExtensionNeeded(Network n) { + return n.getGeneratorStream().anyMatch(RemoteReactivePowerControlSerDe::isExtensionNeeded); + } + + private static boolean isExtensionNeeded(Generator g) { + return g.getVoltageRegulation() != null + && g.getVoltageRegulation().getTerminal() != null + && g.getVoltageRegulation().getMode() == RegulationMode.REACTIVE_POWER; + } + + public static boolean isExtensionNeededAndExportable(Generator g, NetworkSerializerContext context) { + return ExtinctExtensionSerDe.isExtensionExportable(context.getOptions(), RemoteReactivePowerControl.NAME, LAST_SUPPORTED_VERSION) + && isExtensionNeeded(g); + } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/VoltageRegulationSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/VoltageRegulationSerDe.java index a1454835e01..1d49233c412 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/VoltageRegulationSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/VoltageRegulationSerDe.java @@ -12,6 +12,7 @@ import com.powsybl.commons.io.DeserializerContext; import com.powsybl.commons.io.SerializerContext; import com.powsybl.iidm.network.Battery; +import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.extensions.removed.VoltageRegulationExtension; import com.powsybl.iidm.network.regulation.RegulationMode; import com.powsybl.iidm.network.regulation.VoltageRegulation; @@ -24,8 +25,11 @@ /** * @author Coline Piloquet {@literal } */ -@AutoService(ExtensionSerDe.class) -public class VoltageRegulationSerDe extends AbstractVersionableNetworkExtensionSerDe { +@AutoService({ExtensionSerDe.class, ExtinctExtensionSerDe.class}) +public class VoltageRegulationSerDe extends AbstractVersionableNetworkExtensionSerDe + implements ExtinctExtensionSerDe { + + public static final IidmVersion LAST_SUPPORTED_VERSION = IidmVersion.V_1_15; public enum Version implements SerDeVersion { V_1_0_LEGACY("/xsd/voltageRegulation_V1_0_legacy.xsd", "http://www.itesla_project.eu/schema/iidm/ext/voltageregulation/1_0", @@ -117,4 +121,23 @@ protected Version getDefaultVersion() { // Default version is v1.1, the subsequent ones have been added without any change return Version.V_1_1; } + + @Override + public IidmVersion getLastSupportedVersion() { + return LAST_SUPPORTED_VERSION; + } + + @Override + public boolean isExtensionNeeded(Network n) { + return n.getBatteryStream().anyMatch(VoltageRegulationSerDe::isExtensionNeeded); + } + + private static boolean isExtensionNeeded(Battery b) { + return b.getVoltageRegulation() != null; + } + + public static boolean isExtensionNeededAndExportable(Battery b, NetworkSerializerContext context) { + return ExtinctExtensionSerDe.isExtensionExportable(context.getOptions(), VoltageRegulationExtension.NAME, LAST_SUPPORTED_VERSION) + && isExtensionNeeded(b); + } }