diff --git a/data/crac/crac-io/crac-io-json/src/main/java/com/powsybl/openrao/data/cracio/json/JsonCracCreationContext.java b/data/crac/crac-io/crac-io-json/src/main/java/com/powsybl/openrao/data/cracio/json/JsonCracCreationContext.java index 5543511c94..dca74e6ad6 100644 --- a/data/crac/crac-io/crac-io-json/src/main/java/com/powsybl/openrao/data/cracio/json/JsonCracCreationContext.java +++ b/data/crac/crac-io/crac-io-json/src/main/java/com/powsybl/openrao/data/cracio/json/JsonCracCreationContext.java @@ -17,7 +17,7 @@ * @author Viktor Terrier {@literal } * @author Peter Mitri {@literal } */ -class JsonCracCreationContext implements CracCreationContext { +public class JsonCracCreationContext implements CracCreationContext { private final boolean isCreationSuccessful; private final Crac crac; private final String networkName; diff --git a/data/rao-result/rao-result-api/src/main/java/com/powsybl/openrao/data/raoresultapi/RaoResult.java b/data/rao-result/rao-result-api/src/main/java/com/powsybl/openrao/data/raoresultapi/RaoResult.java index a8f8446398..5b06e19d6b 100644 --- a/data/rao-result/rao-result-api/src/main/java/com/powsybl/openrao/data/raoresultapi/RaoResult.java +++ b/data/rao-result/rao-result-api/src/main/java/com/powsybl/openrao/data/raoresultapi/RaoResult.java @@ -13,6 +13,7 @@ import com.powsybl.openrao.commons.PhysicalParameter; import com.powsybl.openrao.commons.Unit; import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.cracapi.CracCreationContext; import com.powsybl.openrao.data.cracapi.Instant; import com.powsybl.openrao.data.cracapi.RemedialAction; import com.powsybl.openrao.data.cracapi.State; @@ -33,6 +34,7 @@ import java.io.OutputStream; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; /** @@ -492,28 +494,61 @@ private static byte[] getBytesFromInputStream(InputStream inputStream) throws IO return baos.toByteArray(); } + /** + * Write CRAC data into a file + * + * @param exporters candidate CRAC exporters + * @param format desired output CRAC data type + * @param cracCreationContext CRAC creation context that contains the original CRAC + * @param properties specific information needed for export + * @param outputStream file where to write the CRAC data + */ + private void write(List exporters, String format, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) { + exporters.stream() + .filter(ex -> format.equals(ex.getFormat())) + .findAny() + .orElseThrow(() -> new OpenRaoException("Export format " + format + " not supported")) + .exportData(this, cracCreationContext, properties, outputStream); + } + + /** + * Write CRAC data into a file + * + * @param format desired output CRAC data type + * @param cracCreationContext CRAC creation context that contains the original CRAC + * @param properties specific information needed for export + * @param outputStream file where to write the CRAC data + */ + default void write(String format, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) { + write(new ServiceLoaderCache<>(Exporter.class).getServices(), format, cracCreationContext, properties, outputStream); + } + /** * Write CRAC data into a file * * @param exporters candidate CRAC exporters * @param format desired output CRAC data type + * @param crac CRAC data + * @param properties specific information needed for export * @param outputStream file where to write the CRAC data */ - private void write(List exporters, String format, Crac crac, Set flowUnits, OutputStream outputStream) { + private void write(List exporters, String format, Crac crac, Properties properties, OutputStream outputStream) { exporters.stream() .filter(ex -> format.equals(ex.getFormat())) .findAny() .orElseThrow(() -> new OpenRaoException("Export format " + format + " not supported")) - .exportData(this, crac, flowUnits, outputStream); + .exportData(this, crac, properties, outputStream); } /** * Write CRAC data into a file * * @param format desired output CRAC data type + * @param crac CRAC data + * @param properties specific information needed for export * @param outputStream file where to write the CRAC data */ - default void write(String format, Crac crac, Set flowUnits, OutputStream outputStream) { - write(new ServiceLoaderCache<>(Exporter.class).getServices(), format, crac, flowUnits, outputStream); + default void write(String format, Crac crac, Properties properties, OutputStream outputStream) { + write(new ServiceLoaderCache<>(Exporter.class).getServices(), format, crac, properties, outputStream); } } diff --git a/data/rao-result/rao-result-api/src/main/java/com/powsybl/openrao/data/raoresultapi/io/Exporter.java b/data/rao-result/rao-result-api/src/main/java/com/powsybl/openrao/data/raoresultapi/io/Exporter.java index a2134eff47..4fdaef04fe 100644 --- a/data/rao-result/rao-result-api/src/main/java/com/powsybl/openrao/data/raoresultapi/io/Exporter.java +++ b/data/rao-result/rao-result-api/src/main/java/com/powsybl/openrao/data/raoresultapi/io/Exporter.java @@ -7,11 +7,13 @@ package com.powsybl.openrao.data.raoresultapi.io; -import com.powsybl.openrao.commons.Unit; +import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.cracapi.CracCreationContext; import com.powsybl.openrao.data.raoresultapi.RaoResult; import java.io.OutputStream; +import java.util.Properties; import java.util.Set; /** @@ -23,5 +25,25 @@ public interface Exporter { */ String getFormat(); - void exportData(RaoResult raoResult, Crac crac, Set flowUnits, OutputStream outputStream); + Set getRequiredProperties(); + + Class getCracCreationContextClass(); + + default void validateDataToExport(CracCreationContext cracCreationContext, Properties properties) { + if (!getCracCreationContextClass().isInstance(cracCreationContext)) { + throw new OpenRaoException("%s exporter expects a %s.".formatted(getFormat(), getCracCreationContextClass().getSimpleName())); + } + if (!getRequiredProperties().isEmpty() && properties == null) { + throw new OpenRaoException("The export properties cannot be null for %s export.".formatted(getFormat())); + } + for (String requiredProperty : getRequiredProperties()) { + if (!properties.containsKey(requiredProperty)) { + throw new OpenRaoException("The mandatory %s property is missing for %s export.".formatted(requiredProperty, getFormat())); + } + } + } + + void exportData(RaoResult raoResult, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream); + + void exportData(RaoResult raoResult, Crac crac, Properties properties, OutputStream outputStream); } diff --git a/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/MockCracCreationContext.java b/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/MockCracCreationContext.java new file mode 100644 index 0000000000..cbe164d247 --- /dev/null +++ b/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/MockCracCreationContext.java @@ -0,0 +1,44 @@ +/* + * 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/. + */ + +package com.powsybl.openrao.data.raoresultapi; + +import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.cracapi.CracCreationContext; +import com.powsybl.openrao.data.cracapi.CracCreationReport; + +import java.time.OffsetDateTime; + +/** + * @author Thomas Bouquet {@literal } + */ +public class MockCracCreationContext implements CracCreationContext { + @Override + public boolean isCreationSuccessful() { + return false; + } + + @Override + public Crac getCrac() { + return null; + } + + @Override + public OffsetDateTime getTimeStamp() { + return null; + } + + @Override + public String getNetworkName() { + return null; + } + + @Override + public CracCreationReport getCreationReport() { + return null; + } +} diff --git a/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/MockRaoResultExporter.java b/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/MockRaoResultExporter.java index b77719a1ca..a8c123f67b 100644 --- a/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/MockRaoResultExporter.java +++ b/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/MockRaoResultExporter.java @@ -8,11 +8,12 @@ package com.powsybl.openrao.data.raoresultapi; import com.google.auto.service.AutoService; -import com.powsybl.openrao.commons.Unit; import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.cracapi.CracCreationContext; import com.powsybl.openrao.data.raoresultapi.io.Exporter; import java.io.OutputStream; +import java.util.Properties; import java.util.Set; /** @@ -26,7 +27,24 @@ public String getFormat() { } @Override - public void exportData(RaoResult raoResult, Crac crac, Set flowUnits, OutputStream outputStream) { + public Set getRequiredProperties() { + return Set.of("rao-result.export.mock.property-1", "rao-result.export.mock.property-2"); + } + + @Override + public Class getCracCreationContextClass() { + return MockCracCreationContext.class; + } + + @Override + public void exportData(RaoResult raoResult, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) { + if (raoResult instanceof MockRaoResult mockRaoResult) { + mockRaoResult.setExportSuccessful(); + } + } + + @Override + public void exportData(RaoResult raoResult, Crac crac, Properties properties, OutputStream outputStream) { if (raoResult instanceof MockRaoResult mockRaoResult) { mockRaoResult.setExportSuccessful(); } diff --git a/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/RaoResultExportTest.java b/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/RaoResultExportTest.java index ed34c51868..48638eec0a 100644 --- a/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/RaoResultExportTest.java +++ b/data/rao-result/rao-result-api/src/test/java/com/powsybl/openrao/data/raoresultapi/RaoResultExportTest.java @@ -8,11 +8,12 @@ package com.powsybl.openrao.data.raoresultapi; import com.powsybl.openrao.commons.OpenRaoException; -import com.powsybl.openrao.commons.Unit; +import com.powsybl.openrao.data.cracapi.CracCreationContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; -import java.util.Set; +import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -33,15 +34,32 @@ void setUp() { @Test void testExportWithUnknownExporter() { - Set emptySet = Set.of(); - OpenRaoException exception = assertThrows(OpenRaoException.class, () -> raoResult.write("unknownFormat", null, emptySet, null)); + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> raoResult.write("unknownFormat", (CracCreationContext) null, null, null)); assertEquals("Export format unknownFormat not supported", exception.getMessage()); assertFalse(raoResult.wasExportSuccessful()); } @Test void testExportCracWithValidExporter() { - raoResult.write("Mock", null, Set.of(), null); + raoResult.write("Mock", (CracCreationContext) null, new Properties(), null); assertTrue(raoResult.wasExportSuccessful()); } + + @Test + void testValidateExportData() { + OpenRaoException exception; + CracCreationContext cracCreationContext = Mockito.mock(CracCreationContext.class); + MockRaoResultExporter exporter = new MockRaoResultExporter(); + // wrong CRAC creation context class + exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(cracCreationContext, new Properties())); + assertEquals("Mock exporter expects a MockCracCreationContext.", exception.getMessage()); + // null properties + exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(new MockCracCreationContext(), null)); + assertEquals("The export properties cannot be null for Mock export.", exception.getMessage()); + // missing required properties + Properties properties = new Properties(); + properties.setProperty("rao-result.export.mock.property-1", "true"); + exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(new MockCracCreationContext(), properties)); + assertEquals("The mandatory rao-result.export.mock.property-2 property is missing for Mock export.", exception.getMessage()); + } } diff --git a/data/rao-result/rao-result-json/pom.xml b/data/rao-result/rao-result-json/pom.xml index 5686f32bfd..2722fee3b3 100644 --- a/data/rao-result/rao-result-json/pom.xml +++ b/data/rao-result/rao-result-json/pom.xml @@ -35,26 +35,25 @@ open-rao-search-tree-rao ${project.version} - - ${project.groupId} - open-rao-crac-impl + open-rao-crac-io-json ${project.version} - test + + ${project.groupId} open-rao-crac-impl ${project.version} test - test-jar ${project.groupId} - open-rao-crac-io-json + open-rao-crac-impl ${project.version} test + test-jar ${project.groupId} diff --git a/data/rao-result/rao-result-json/src/main/java/com/powsybl/openrao/data/raoresultjson/RaoResultJsonExporter.java b/data/rao-result/rao-result-json/src/main/java/com/powsybl/openrao/data/raoresultjson/RaoResultJsonExporter.java index 9c0f3f6e96..79dee22185 100644 --- a/data/rao-result/rao-result-json/src/main/java/com/powsybl/openrao/data/raoresultjson/RaoResultJsonExporter.java +++ b/data/rao-result/rao-result-json/src/main/java/com/powsybl/openrao/data/raoresultjson/RaoResultJsonExporter.java @@ -11,6 +11,8 @@ import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.Unit; import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.cracapi.CracCreationContext; +import com.powsybl.openrao.data.cracio.json.JsonCracCreationContext; import com.powsybl.openrao.data.raoresultapi.io.Exporter; import com.powsybl.openrao.data.raoresultapi.RaoResult; import com.powsybl.openrao.data.raoresultjson.serializers.RaoResultJsonSerializerModule; @@ -22,27 +24,65 @@ import java.io.IOException; import java.io.OutputStream; import java.io.UncheckedIOException; +import java.util.HashSet; +import java.util.Properties; import java.util.Set; /** + * Rao Result exporter in JSON format. + *

+ * Optional properties: + *

    + *
  • + * flows-in-amperes: boolean (default is "false"). + *
  • + *
  • + * flows-in-megawatts: boolean (default is "false"). + *
  • + *
+ * * @author Baptiste Seguinot {@literal } */ @AutoService(Exporter.class) public class RaoResultJsonExporter implements Exporter { + private static final String JSON_EXPORT_PROPERTIES_PREFIX = "rao-result.export.json."; + private static final String FLOWS_IN_AMPERES = "flows-in-amperes"; + private static final String FLOWS_IN_MEGAWATTS = "flows-in-megawatts"; + @Override public String getFormat() { return "JSON"; } @Override - public void exportData(RaoResult raoResult, Crac crac, Set flowUnits, OutputStream outputStream) { - if (flowUnits.isEmpty()) { - throw new OpenRaoException("At least one flow unit should be defined"); + public Set getRequiredProperties() { + return Set.of(); + } + + @Override + public Class getCracCreationContextClass() { + return JsonCracCreationContext.class; + } + + @Override + public void exportData(RaoResult raoResult, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) { + validateDataToExport(cracCreationContext, properties); + exportData(raoResult, cracCreationContext.getCrac(), properties, outputStream); + } + + @Override + public void exportData(RaoResult raoResult, Crac crac, Properties properties, OutputStream outputStream) { + boolean flowsInAmperes = Boolean.parseBoolean(properties.getProperty(JSON_EXPORT_PROPERTIES_PREFIX + FLOWS_IN_AMPERES, "false")); + boolean flowsInMegawatts = Boolean.parseBoolean(properties.getProperty(JSON_EXPORT_PROPERTIES_PREFIX + FLOWS_IN_MEGAWATTS, "false")); + if (!flowsInAmperes && !flowsInMegawatts) { + throw new OpenRaoException("At least one flow unit should be used. Please provide %s and/or %s in the properties.".formatted(JSON_EXPORT_PROPERTIES_PREFIX + FLOWS_IN_AMPERES, JSON_EXPORT_PROPERTIES_PREFIX + FLOWS_IN_MEGAWATTS)); + } + Set flowUnits = new HashSet<>(); + if (flowsInAmperes) { + flowUnits.add(Unit.AMPERE); } - if (flowUnits.stream().anyMatch(unit -> !unit.equals(Unit.AMPERE) && !unit.equals(Unit.MEGAWATT))) { - // TODO : we can actually handle all units with PhysicalParameter.FLOW - // but we'll have to add export feature for %Imax - throw new OpenRaoException("Flow unit should be AMPERE and/or MEGAWATT"); + if (flowsInMegawatts) { + flowUnits.add(Unit.MEGAWATT); } try { ObjectMapper objectMapper = JsonUtil.createObjectMapper(); diff --git a/data/rao-result/rao-result-json/src/test/java/com/powsybl/openrao/data/raoresultjson/RaoResultJsonExporterTest.java b/data/rao-result/rao-result-json/src/test/java/com/powsybl/openrao/data/raoresultjson/RaoResultJsonExporterTest.java new file mode 100644 index 0000000000..17198a67b8 --- /dev/null +++ b/data/rao-result/rao-result-json/src/test/java/com/powsybl/openrao/data/raoresultjson/RaoResultJsonExporterTest.java @@ -0,0 +1,54 @@ +/* + * 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/. + */ + + +package com.powsybl.openrao.data.raoresultjson; + +import com.powsybl.openrao.commons.OpenRaoException; +import com.powsybl.openrao.data.cracapi.CracCreationContext; +import com.powsybl.openrao.data.cracio.json.JsonCracCreationContext; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Thomas Bouquet {@literal } + */ +class RaoResultJsonExporterTest { + private final RaoResultJsonExporter exporter = new RaoResultJsonExporter(); + + @Test + void testFormat() { + assertEquals("JSON", exporter.getFormat()); + } + + @Test + void testExportWithWrongCracCreationContext() { + CracCreationContext cracCreationContext = Mockito.mock(CracCreationContext.class); + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.exportData(null, cracCreationContext, null, null)); + assertEquals("JSON exporter expects a JsonCracCreationContext.", exception.getMessage()); + } + + @Test + void testProperties() { + assertTrue(exporter.getRequiredProperties().isEmpty()); + } + + @Test + void testCracCreationContextClass() { + assertEquals(JsonCracCreationContext.class, exporter.getCracCreationContextClass()); + } + + @Test + void testWrongCracCreationContextClass() { + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(Mockito.mock(CracCreationContext.class), null)); + assertEquals("JSON exporter expects a JsonCracCreationContext.", exception.getMessage()); + } +} diff --git a/data/rao-result/rao-result-json/src/test/java/com/powsybl/openrao/data/raoresultjson/RaoResultRoundTripTest.java b/data/rao-result/rao-result-json/src/test/java/com/powsybl/openrao/data/raoresultjson/RaoResultRoundTripTest.java index d24e53655b..d3e3fc75d4 100644 --- a/data/rao-result/rao-result-json/src/test/java/com/powsybl/openrao/data/raoresultjson/RaoResultRoundTripTest.java +++ b/data/rao-result/rao-result-json/src/test/java/com/powsybl/openrao/data/raoresultjson/RaoResultRoundTripTest.java @@ -29,8 +29,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.Collections; import java.util.Map; +import java.util.Properties; import java.util.Set; import static com.powsybl.iidm.network.TwoSides.ONE; @@ -60,7 +60,10 @@ void explicitJsonRoundTripTest() { // export RaoResult ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - new RaoResultJsonExporter().exportData(raoResult, crac, Set.of(MEGAWATT, AMPERE), outputStream); + Properties properties = new Properties(); + properties.setProperty("rao-result.export.json.flows-in-amperes", "true"); + properties.setProperty("rao-result.export.json.flows-in-megawatts", "true"); + new RaoResultJsonExporter().exportData(raoResult, crac, properties, outputStream); ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream(); crac.write("JSON", outputStream2); @@ -79,7 +82,10 @@ void implicitJsonRoundTripTest() throws IOException { // export RaoResult ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - raoResult.write("JSON", crac, Set.of(MEGAWATT, AMPERE), outputStream); + Properties properties = new Properties(); + properties.setProperty("rao-result.export.json.flows-in-amperes", "true"); + properties.setProperty("rao-result.export.json.flows-in-megawatts", "true"); + raoResult.write("JSON", crac, properties, outputStream); ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream(); crac.write("JSON", outputStream2); @@ -423,7 +429,10 @@ void testExplicitRoundTripRangeActionsCrossResults() { // export RaoResult ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - new RaoResultJsonExporter().exportData(raoResult, crac, Set.of(MEGAWATT, AMPERE), outputStream); + Properties properties = new Properties(); + properties.setProperty("rao-result.export.json.flows-in-amperes", "true"); + properties.setProperty("rao-result.export.json.flows-in-megawatts", "true"); + new RaoResultJsonExporter().exportData(raoResult, crac, properties, outputStream); // import RaoResult ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); @@ -439,7 +448,10 @@ void testImplicitRoundTripRangeActionsCrossResults() throws IOException { // export RaoResult ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - raoResult.write("JSON", crac, Set.of(MEGAWATT, AMPERE), outputStream); + Properties properties = new Properties(); + properties.setProperty("rao-result.export.json.flows-in-amperes", "true"); + properties.setProperty("rao-result.export.json.flows-in-megawatts", "true"); + raoResult.write("JSON", crac, properties, outputStream); // import RaoResult ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); @@ -593,25 +605,9 @@ void testFailWithWrongFlowUnits() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); RaoResultJsonExporter raoResultExporter = new RaoResultJsonExporter(); - // Empty set - Set emptySet = Collections.emptySet(); - Exception exception = assertThrows(OpenRaoException.class, () -> raoResultExporter.exportData(raoResult, crac, emptySet, outputStream)); - assertEquals("At least one flow unit should be defined", exception.getMessage()); - - // "TAP" unit - Set tapSingleton = Set.of(TAP); - exception = assertThrows(OpenRaoException.class, () -> raoResultExporter.exportData(raoResult, crac, tapSingleton, outputStream)); - assertEquals("Flow unit should be AMPERE and/or MEGAWATT", exception.getMessage()); - - // "DEGREE" unit - Set degreeSingleton = Set.of(DEGREE); - exception = assertThrows(OpenRaoException.class, () -> raoResultExporter.exportData(raoResult, crac, degreeSingleton, outputStream)); - assertEquals("Flow unit should be AMPERE and/or MEGAWATT", exception.getMessage()); - - // "KILOVOLT" + "AMPERE" units - Set kvAndAmp = Set.of(KILOVOLT, AMPERE); - exception = assertThrows(OpenRaoException.class, () -> raoResultExporter.exportData(raoResult, crac, kvAndAmp, outputStream)); - assertEquals("Flow unit should be AMPERE and/or MEGAWATT", exception.getMessage()); + // Empty properties + Exception exception = assertThrows(OpenRaoException.class, () -> raoResultExporter.exportData(raoResult, crac, new Properties(), outputStream)); + assertEquals("At least one flow unit should be used. Please provide rao-result.export.json.flows-in-amperes and/or rao-result.export.json.flows-in-megawatts in the properties.", exception.getMessage()); } @Test @@ -622,7 +618,9 @@ void testExplicitRoundTripWithUnits() throws IOException { // RoundTrip with Ampere only ByteArrayOutputStream outputStreamAmpere = new ByteArrayOutputStream(); - new RaoResultJsonExporter().exportData(raoResult, crac, Set.of(AMPERE), outputStreamAmpere); + Properties propertiesAmperes = new Properties(); + propertiesAmperes.setProperty("rao-result.export.json.flows-in-amperes", "true"); + new RaoResultJsonExporter().exportData(raoResult, crac, propertiesAmperes, outputStreamAmpere); ByteArrayInputStream inputStreamAmpere = new ByteArrayInputStream(outputStreamAmpere.toByteArray()); RaoResult importedRaoResultAmpere = new RaoResultJsonImporter().importData(inputStreamAmpere, crac); @@ -631,7 +629,9 @@ void testExplicitRoundTripWithUnits() throws IOException { // RoundTrip with MW only ByteArrayOutputStream outputStreamMegawatt = new ByteArrayOutputStream(); - raoResult.write("JSON", crac, Set.of(MEGAWATT), outputStreamMegawatt); + Properties propertiesMegawatts = new Properties(); + propertiesMegawatts.setProperty("rao-result.export.json.flows-in-megawatts", "true"); + raoResult.write("JSON", crac, propertiesMegawatts, outputStreamMegawatt); ByteArrayInputStream inputStreamMegawatt = new ByteArrayInputStream(outputStreamMegawatt.toByteArray()); RaoResult importedRaoResultMegawatt = RaoResult.read(inputStreamMegawatt, crac); @@ -646,7 +646,9 @@ void testImplicitRoundTripWithUnits() throws IOException { // RoundTrip with Ampere only ByteArrayOutputStream outputStreamAmpere = new ByteArrayOutputStream(); - raoResult.write("JSON", crac, Set.of(AMPERE), outputStreamAmpere); + Properties propertiesAmperes = new Properties(); + propertiesAmperes.setProperty("rao-result.export.json.flows-in-amperes", "true"); + raoResult.write("JSON", crac, propertiesAmperes, outputStreamAmpere); ByteArrayInputStream inputStreamAmpere = new ByteArrayInputStream(outputStreamAmpere.toByteArray()); RaoResult importedRaoResultAmpere = RaoResult.read(inputStreamAmpere, crac); @@ -655,7 +657,9 @@ void testImplicitRoundTripWithUnits() throws IOException { // RoundTrip with MW only ByteArrayOutputStream outputStreamMegawatt = new ByteArrayOutputStream(); - raoResult.write("JSON", crac, Set.of(MEGAWATT), outputStreamMegawatt); + Properties propertiesMegawatts = new Properties(); + propertiesMegawatts.setProperty("rao-result.export.json.flows-in-megawatts", "true"); + raoResult.write("JSON", crac, propertiesMegawatts, outputStreamMegawatt); ByteArrayInputStream inputStreamMegawatt = new ByteArrayInputStream(outputStreamMegawatt.toByteArray()); RaoResult importedRaoResultMegawatt = RaoResult.read(inputStreamMegawatt, crac); @@ -679,4 +683,20 @@ private void checkContentMegawatt(RaoResult raoResult, FlowCnec cnecP) { assertTrue(Double.isNaN(raoResult.getFlow(null, cnecP, TWO, AMPERE))); assertTrue(Double.isNaN(raoResult.getMargin(null, cnecP, AMPERE))); } + + @Test + void exportWithInvalidProperties() { + // get exhaustive CRAC and RaoResult + Crac crac = ExhaustiveCracCreation.create(); + RaoResult raoResult = ExhaustiveRaoResultCreation.create(crac); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + RaoResultJsonExporter raoResultExporter = new RaoResultJsonExporter(); + + Properties properties = new Properties(); + properties.setProperty("rao-result.export.json.flows-in-amperes", "Hello world!"); + + Exception exception = assertThrows(OpenRaoException.class, () -> raoResultExporter.exportData(raoResult, crac, properties, outputStream)); + assertEquals("At least one flow unit should be used. Please provide rao-result.export.json.flows-in-amperes and/or rao-result.export.json.flows-in-megawatts in the properties.", exception.getMessage()); + } } diff --git a/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneConstants.java b/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneConstants.java index e31b0e7c8f..6c5dcdf3c8 100644 --- a/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneConstants.java +++ b/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneConstants.java @@ -7,6 +7,8 @@ package com.powsybl.openrao.data.cneexportercommons; +import java.util.Set; + /** * Constants used in the CNE file * @@ -98,5 +100,20 @@ public final class CneConstants { public static final String DIVERGENCE_CODE = "B40"; public static final String DIVERGENCE_TEXT = "Load flow divergence"; + /* Exporter properties */ + public static final String RELATIVE_POSITIVE_MARGINS = "relative-positive-margins"; + public static final String WITH_LOOP_FLOWS = "with-loop-flows"; + public static final String MNEC_ACCEPTABLE_MARGIN_DIMINUTION = "mnec-acceptable-margin-diminution"; + public static final String DOCUMENT_ID = "document-id"; + public static final String REVISION_NUMBER = "revision-number"; + public static final String DOMAIN_ID = "domain-id"; + public static final String PROCESS_TYPE = "process-type"; + public static final String SENDER_ID = "sender-id"; + public static final String SENDER_ROLE = "sender-role"; + public static final String RECEIVER_ID = "receiver-id"; + public static final String RECEIVER_ROLE = "receiver-role"; + public static final String TIME_INTERVAL = "time-interval"; + public static final Set CNE_REQUIRED_PROPERTIES = Set.of(DOCUMENT_ID, REVISION_NUMBER, DOMAIN_ID, PROCESS_TYPE, SENDER_ID, SENDER_ROLE, RECEIVER_ID, RECEIVER_ROLE, TIME_INTERVAL); + private CneConstants() { } } diff --git a/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneExporterParameters.java b/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneExporterParameters.java deleted file mode 100644 index 496205fdea..0000000000 --- a/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneExporterParameters.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2022, 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/. - */ - -package com.powsybl.openrao.data.cneexportercommons; - -/** - * Parameters for CNE export - * - * @author Peter Mitri {@literal } - */ -public class CneExporterParameters { - private String documentId; - private int revisionNumber; - private String domainId; - private ProcessType processType; - private String senderId; - private RoleType senderRole; - private String receiverId; - private RoleType receiverRole; - private String timeInterval; - - public enum RoleType { - CAPACITY_COORDINATOR("A36"), - REGIONAL_SECURITY_COORDINATOR("A44"), - SYSTEM_OPERATOR("A04"); - - private String code; - private RoleType(String code) { - this.code = code; - } - - public String getCode() { - return code; - } - - @Override - public String toString() { - return getCode(); - } - } - - public enum ProcessType { - DAY_AHEAD_CC("A48"), // Day-ahead capacity determination - Z01("Z01"); // Day-ahead capacity determination for SWE, does not exist in xsd - - private String code; - private ProcessType(String code) { - this.code = code; - } - - public String getCode() { - return code; - } - - @Override - public String toString() { - return getCode(); - } - } - - public CneExporterParameters(String documentId, int revisionNumber, String domainId, ProcessType processType, String senderId, RoleType senderRole, String receiverId, RoleType receiverRole, String timeInterval) { - this.documentId = documentId; - this.revisionNumber = revisionNumber; - this.domainId = domainId; - this.processType = processType; - this.senderId = senderId; - this.senderRole = senderRole; - this.receiverId = receiverId; - this.receiverRole = receiverRole; - this.timeInterval = timeInterval; - } - - public String getDocumentId() { - return documentId; - } - - public int getRevisionNumber() { - return revisionNumber; - } - - public String getDomainId() { - return domainId; - } - - public ProcessType getProcessType() { - return processType; - } - - public String getSenderId() { - return senderId; - } - - public RoleType getSenderRole() { - return senderRole; - } - - public String getReceiverId() { - return receiverId; - } - - public RoleType getReceiverRole() { - return receiverRole; - } - - public String getTimeInterval() { - return timeInterval; - } -} diff --git a/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneHelper.java b/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneHelper.java index 5ae5f45683..e6913c07c9 100644 --- a/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneHelper.java +++ b/data/result-exporter/cne-exporter-commons/src/main/java/com/powsybl/openrao/data/cneexportercommons/CneHelper.java @@ -8,67 +8,83 @@ package com.powsybl.openrao.data.cneexportercommons; import com.powsybl.openrao.data.cracapi.Crac; -import com.powsybl.openrao.data.cracapi.Instant; import com.powsybl.openrao.data.raoresultapi.RaoResult; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; -import com.powsybl.openrao.raoapi.parameters.extensions.LoopFlowParametersExtension; -import com.powsybl.openrao.raoapi.parameters.extensions.MnecParametersExtension; -import static com.powsybl.openrao.data.cneexportercommons.CneConstants.PATL_MEASUREMENT_TYPE; -import static com.powsybl.openrao.data.cneexportercommons.CneConstants.TATL_MEASUREMENT_TYPE; +import java.util.Properties; + +import static com.powsybl.openrao.data.cneexportercommons.CneConstants.*; /** * @author Viktor Terrier {@literal } * @author Peter Mitri {@literal } + * @author Thomas Bouquet {@literal } */ public class CneHelper { + private final Crac crac; + private final RaoResult raoResult; + private final Properties properties; + private final String propertiesPrefix; - private Crac crac; - private boolean relativePositiveMargins; - private boolean withLoopflows; - private RaoResult raoResult; - private CneExporterParameters exporterParameters; - private double mnecAcceptableMarginDiminution; - - public CneHelper(Crac crac, RaoResult raoResult, RaoParameters raoParameters, CneExporterParameters exporterParameters) { + public CneHelper(Crac crac, RaoResult raoResult, Properties properties, String propertiesPrefix) { this.crac = crac; this.raoResult = raoResult; - this.exporterParameters = exporterParameters; + this.properties = properties; + this.propertiesPrefix = propertiesPrefix; + } - relativePositiveMargins = raoParameters.getObjectiveFunctionParameters().getType().relativePositiveMargins(); - withLoopflows = raoParameters.hasExtension(LoopFlowParametersExtension.class); - mnecAcceptableMarginDiminution = raoParameters.hasExtension(MnecParametersExtension.class) ? raoParameters.getExtension(MnecParametersExtension.class).getAcceptableMarginDecrease() : 0; + public Crac getCrac() { + return crac; } public RaoResult getRaoResult() { return raoResult; } - public boolean isWithLoopflows() { - return withLoopflows; + public boolean isRelativePositiveMargins() { + return Boolean.parseBoolean(properties.getProperty(propertiesPrefix + RELATIVE_POSITIVE_MARGINS, "false")); + } + + public boolean isWithLoopFlows() { + return Boolean.parseBoolean(properties.getProperty(propertiesPrefix + WITH_LOOP_FLOWS, "false")); } public double getMnecAcceptableMarginDiminution() { - return mnecAcceptableMarginDiminution; + return Double.parseDouble(properties.getProperty(propertiesPrefix + MNEC_ACCEPTABLE_MARGIN_DIMINUTION, "0")); } - public Crac getCrac() { - return crac; + public String getDocumentId() { + return properties.getProperty(propertiesPrefix + DOCUMENT_ID); } - public String instantToCodeConverter(Instant instant) { - if (instant.isPreventive()) { // Before contingency - return PATL_MEASUREMENT_TYPE; - } else { // After contingency, before any post-contingency RA - return TATL_MEASUREMENT_TYPE; - } + public int getRevisionNumber() { + return Integer.parseInt(properties.getProperty(propertiesPrefix + REVISION_NUMBER)); } - public CneExporterParameters getExporterParameters() { - return exporterParameters; + public String getDomainId() { + return properties.getProperty(propertiesPrefix + DOMAIN_ID); } - public boolean isRelativePositiveMargins() { - return relativePositiveMargins; + public String getProcessType() { + return properties.getProperty(propertiesPrefix + PROCESS_TYPE); + } + + public String getSenderId() { + return properties.getProperty(propertiesPrefix + SENDER_ID); + } + + public String getSenderRole() { + return properties.getProperty(propertiesPrefix + SENDER_ROLE); + } + + public String getReceiverId() { + return properties.getProperty(propertiesPrefix + RECEIVER_ID); + } + + public String getReceiverRole() { + return properties.getProperty(propertiesPrefix + RECEIVER_ROLE); + } + + public String getTimeInterval() { + return properties.getProperty(propertiesPrefix + TIME_INTERVAL); } } diff --git a/data/result-exporter/cne-exporter-commons/src/test/java/com/powsybl/openrao/data/cneexportercommons/CneExporterParametersTest.java b/data/result-exporter/cne-exporter-commons/src/test/java/com/powsybl/openrao/data/cneexportercommons/CneExporterParametersTest.java deleted file mode 100644 index ef8db6eeb3..0000000000 --- a/data/result-exporter/cne-exporter-commons/src/test/java/com/powsybl/openrao/data/cneexportercommons/CneExporterParametersTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022, 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/. - */ - -package com.powsybl.openrao.data.cneexportercommons; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Peter Mitri {@literal } - */ -class CneExporterParametersTest { - @Test - void basicTest() { - CneExporterParameters params = new CneExporterParameters( - "a", 3, "b", CneExporterParameters.ProcessType.DAY_AHEAD_CC, - "c", CneExporterParameters.RoleType.REGIONAL_SECURITY_COORDINATOR, - "e", CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "timeInterval"); - - assertEquals("a", params.getDocumentId()); - assertEquals(3, params.getRevisionNumber()); - assertEquals("b", params.getDomainId()); - - assertEquals(CneExporterParameters.ProcessType.DAY_AHEAD_CC, params.getProcessType()); - assertEquals("A48", params.getProcessType().getCode()); - assertEquals("A48", params.getProcessType().toString()); - - assertEquals("c", params.getSenderId()); - assertEquals(CneExporterParameters.RoleType.REGIONAL_SECURITY_COORDINATOR, params.getSenderRole()); - assertEquals("A44", params.getSenderRole().getCode()); - assertEquals("A44", params.getSenderRole().toString()); - - assertEquals("e", params.getReceiverId()); - assertEquals(CneExporterParameters.RoleType.CAPACITY_COORDINATOR, params.getReceiverRole()); - assertEquals("A36", params.getReceiverRole().getCode()); - assertEquals("A36", params.getReceiverRole().toString()); - - assertEquals("timeInterval", params.getTimeInterval()); - } -} diff --git a/data/result-exporter/cne-exporter-commons/src/test/java/com/powsybl/openrao/data/cneexportercommons/CneHelperTest.java b/data/result-exporter/cne-exporter-commons/src/test/java/com/powsybl/openrao/data/cneexportercommons/CneHelperTest.java new file mode 100644 index 0000000000..d0b39309f5 --- /dev/null +++ b/data/result-exporter/cne-exporter-commons/src/test/java/com/powsybl/openrao/data/cneexportercommons/CneHelperTest.java @@ -0,0 +1,60 @@ +/* + * 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/. + */ + +package com.powsybl.openrao.data.cneexportercommons; + +import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.raoresultapi.RaoResult; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Thomas Bouquet {@literal } + */ +class CneHelperTest { + @Test + void testInitFromProperties() { + Crac crac = Mockito.mock(Crac.class); + RaoResult raoResult = Mockito.mock(RaoResult.class); + + Properties properties = new Properties(); + properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "true"); + properties.setProperty("rao-result.export.core-cne.with-loop-flows", "false"); + properties.setProperty("rao-result.export.core-cne.mnec-acceptable-margin-diminution", "50"); + properties.setProperty("rao-result.export.core-cne.document-id", "documentId"); + properties.setProperty("rao-result.export.core-cne.revision-number", "1"); + properties.setProperty("rao-result.export.core-cne.domain-id", "domainId"); + properties.setProperty("rao-result.export.core-cne.process-type", "Z01"); + properties.setProperty("rao-result.export.core-cne.sender-id", "senderId"); + properties.setProperty("rao-result.export.core-cne.sender-role", "A04"); + properties.setProperty("rao-result.export.core-cne.receiver-id", "receiverId"); + properties.setProperty("rao-result.export.core-cne.receiver-role", "A36"); + properties.setProperty("rao-result.export.core-cne.time-interval", "2021-04-02T12:00:00Z/2021-04-02T13:00:00Z"); + + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, "rao-result.export.core-cne."); + + assertEquals(crac, cneHelper.getCrac()); + assertEquals(raoResult, cneHelper.getRaoResult()); + assertTrue(cneHelper.isRelativePositiveMargins()); + assertFalse(cneHelper.isWithLoopFlows()); + assertEquals(50d, cneHelper.getMnecAcceptableMarginDiminution()); + assertEquals("documentId", cneHelper.getDocumentId()); + assertEquals(1, cneHelper.getRevisionNumber()); + assertEquals("Z01", cneHelper.getProcessType()); + assertEquals("senderId", cneHelper.getSenderId()); + assertEquals("A04", cneHelper.getSenderRole()); + assertEquals("receiverId", cneHelper.getReceiverId()); + assertEquals("A36", cneHelper.getReceiverRole()); + assertEquals("2021-04-02T12:00:00Z/2021-04-02T13:00:00Z", cneHelper.getTimeInterval()); + } +} diff --git a/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCne.java b/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCne.java index ec550e112a..fe96632a78 100644 --- a/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCne.java +++ b/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCne.java @@ -8,23 +8,21 @@ package com.powsybl.openrao.data.corecneexporter; import com.powsybl.openrao.commons.OpenRaoException; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.cneexportercommons.CneHelper; import com.powsybl.openrao.data.cneexportercommons.CneUtil; import com.powsybl.openrao.data.corecneexporter.xsd.ConstraintSeries; import com.powsybl.openrao.data.corecneexporter.xsd.CriticalNetworkElementMarketDocument; import com.powsybl.openrao.data.corecneexporter.xsd.Point; import com.powsybl.openrao.data.corecneexporter.xsd.SeriesPeriod; -import com.powsybl.openrao.data.cracapi.Crac; import com.powsybl.openrao.data.cracio.commons.api.stdcreationcontext.UcteCracCreationContext; import com.powsybl.openrao.data.raoresultapi.RaoResult; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; import javax.xml.datatype.DatatypeConfigurationException; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Properties; import static com.powsybl.openrao.data.cneexportercommons.CneConstants.*; import static com.powsybl.openrao.data.cneexportercommons.CneUtil.createXMLGregorianCalendarNow; @@ -42,9 +40,9 @@ public class CoreCne { private final CneHelper cneHelper; private final UcteCracCreationContext cracCreationContext; - public CoreCne(Crac crac, UcteCracCreationContext cracCreationContext, RaoResult raoResult, RaoParameters raoParameters, CneExporterParameters exporterParameters) { + public CoreCne(UcteCracCreationContext cracCreationContext, RaoResult raoResult, Properties properties) { marketDocument = new CriticalNetworkElementMarketDocument(); - cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + cneHelper = new CneHelper(cracCreationContext.getCrac(), raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); this.cracCreationContext = cracCreationContext; } @@ -74,17 +72,17 @@ public void generate() { // fills the header of the CNE private void fillHeader() { - marketDocument.setMRID(cneHelper.getExporterParameters().getDocumentId()); - marketDocument.setRevisionNumber(String.valueOf(cneHelper.getExporterParameters().getRevisionNumber())); + marketDocument.setMRID(cneHelper.getDocumentId()); + marketDocument.setRevisionNumber(String.valueOf(cneHelper.getRevisionNumber())); marketDocument.setType(CNE_TYPE); - marketDocument.setProcessProcessType(cneHelper.getExporterParameters().getProcessType().getCode()); - marketDocument.setSenderMarketParticipantMRID(createPartyIDString(A01_CODING_SCHEME, cneHelper.getExporterParameters().getSenderId())); - marketDocument.setSenderMarketParticipantMarketRoleType(cneHelper.getExporterParameters().getSenderRole().getCode()); - marketDocument.setReceiverMarketParticipantMRID(createPartyIDString(A01_CODING_SCHEME, cneHelper.getExporterParameters().getReceiverId())); - marketDocument.setReceiverMarketParticipantMarketRoleType(cneHelper.getExporterParameters().getReceiverRole().getCode()); + marketDocument.setProcessProcessType(cneHelper.getProcessType()); + marketDocument.setSenderMarketParticipantMRID(createPartyIDString(A01_CODING_SCHEME, cneHelper.getSenderId())); + marketDocument.setSenderMarketParticipantMarketRoleType(cneHelper.getSenderRole()); + marketDocument.setReceiverMarketParticipantMRID(createPartyIDString(A01_CODING_SCHEME, cneHelper.getReceiverId())); + marketDocument.setReceiverMarketParticipantMarketRoleType(cneHelper.getReceiverRole()); marketDocument.setCreatedDateTime(createXMLGregorianCalendarNow()); - marketDocument.setTimePeriodTimeInterval(createEsmpDateTimeIntervalForWholeDay(cneHelper.getExporterParameters().getTimeInterval())); - marketDocument.setDomainMRID(createAreaIDString(A01_CODING_SCHEME, cneHelper.getExporterParameters().getDomainId())); + marketDocument.setTimePeriodTimeInterval(createEsmpDateTimeIntervalForWholeDay(cneHelper.getTimeInterval())); + marketDocument.setDomainMRID(createAreaIDString(A01_CODING_SCHEME, cneHelper.getDomainId())); } // creates and adds the TimeSeries to the CNE diff --git a/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneCnecsCreator.java b/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneCnecsCreator.java index 40002cc3bf..17fe7a429f 100644 --- a/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneCnecsCreator.java +++ b/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneCnecsCreator.java @@ -189,7 +189,7 @@ private List createFlowMeasurementsOfFlowCnec(FlowCnec cnec, Instant opt } // A03 measurements.add(createFrmMeasurement(cnec)); - if (cneHelper.isWithLoopflows()) { + if (cneHelper.isWithLoopFlows()) { // Z16 & Z17 measurements.addAll(createLoopflowMeasurements(cnec, optimizedInstant, shouldInvertBranchDirection)); } diff --git a/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneExporter.java b/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneExporter.java index b23d4a3e21..850450db47 100644 --- a/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneExporter.java +++ b/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneExporter.java @@ -7,14 +7,16 @@ package com.powsybl.openrao.data.corecneexporter; +import com.google.auto.service.AutoService; import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.corecneexporter.xsd.CriticalNetworkElementMarketDocument; import com.powsybl.openrao.data.cracapi.Crac; +import com.powsybl.openrao.data.cracapi.CracCreationContext; import com.powsybl.openrao.data.cracio.commons.api.stdcreationcontext.UcteCracCreationContext; import com.powsybl.openrao.data.raoresultapi.RaoResult; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; +import com.powsybl.openrao.data.raoresultapi.io.Exporter; +import org.apache.commons.lang3.NotImplementedException; import org.xml.sax.SAXException; import javax.xml.XMLConstants; @@ -33,22 +35,83 @@ import java.io.StringReader; import java.io.StringWriter; import java.util.Objects; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; import static com.powsybl.openrao.data.cneexportercommons.CneConstants.*; +import static com.powsybl.openrao.data.corecneexporter.CoreCneUtil.CORE_CNE_EXPORT_PROPERTIES_PREFIX; /** - * Xml export of the CNE file + * CORE-CNE Rao Result exporter in XML format. + *

+ * Optional properties: + *

    + *
  • + * relative-positive-margins: boolean (default is "false"). + *
  • + *
  • + * with-loop-flows: boolean (default is "false"). + *
  • + *
  • + * mnec-acceptable-margin-diminution: double (default is "0"). + *
  • + *
+ * Required properties: + *
    + *
  • + * document-id: string + *
  • + *
  • + * revision-number: integer + *
  • + *
  • + * domain-id: string + *
  • + *
  • + * process-type: string + *
  • + *
  • + * sender-id: string + *
  • + *
  • + * sender-role: string + *
  • + *
  • + * receiver-id: string + *
  • + *
  • + * receiver-role: string + *
  • + *
  • + * time-interval: string + *
  • + *
* * @author Viktor Terrier {@literal } * @author Peter Mitri {@literal } */ -public class CoreCneExporter { +@AutoService(Exporter.class) +public class CoreCneExporter implements Exporter { + @Override + public String getFormat() { + return "CORE-CNE"; + } + + @Override + public Set getRequiredProperties() { + return CNE_REQUIRED_PROPERTIES.stream().map(propertyName -> CORE_CNE_EXPORT_PROPERTIES_PREFIX + propertyName).collect(Collectors.toSet()); + } - public void exportCne(Crac crac, - UcteCracCreationContext cracCreationContext, - RaoResult raoResult, RaoParameters raoParameters, - CneExporterParameters exporterParameters, OutputStream outputStream) { - CoreCne cne = new CoreCne(crac, cracCreationContext, raoResult, raoParameters, exporterParameters); + @Override + public Class getCracCreationContextClass() { + return UcteCracCreationContext.class; + } + + @Override + public void exportData(RaoResult raoResult, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) { + validateDataToExport(cracCreationContext, properties); + CoreCne cne = new CoreCne((UcteCracCreationContext) cracCreationContext, raoResult, properties); cne.generate(); CriticalNetworkElementMarketDocument marketDocument = cne.getMarketDocument(); StringWriter stringWriter = new StringWriter(); @@ -79,6 +142,11 @@ public void exportCne(Crac crac, } } + @Override + public void exportData(RaoResult raoResult, Crac crac, Properties properties, OutputStream outputStream) { + throw new NotImplementedException("CracCreationContext is required for CNE export."); + } + private static String getSchemaFile(String schemaName) { return Objects.requireNonNull(CoreCneExporter.class.getResource("/xsd/" + schemaName)).toExternalForm(); } @@ -90,8 +158,8 @@ public static boolean validateCNESchema(String xmlContent) { factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); Source[] source = {new StreamSource(getSchemaFile(CNE_XSD_2_4)), - new StreamSource(getSchemaFile(CODELISTS_XSD)), - new StreamSource(getSchemaFile(LOCALTYPES_XSD))}; + new StreamSource(getSchemaFile(CODELISTS_XSD)), + new StreamSource(getSchemaFile(LOCALTYPES_XSD))}; Schema schema = factory.newSchema(source); factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); diff --git a/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneUtil.java b/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneUtil.java index 82a1a3108c..dd924cfa09 100644 --- a/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneUtil.java +++ b/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneUtil.java @@ -30,6 +30,8 @@ public final class CoreCneUtil { private CoreCneUtil() { } + public static final String CORE_CNE_EXPORT_PROPERTIES_PREFIX = "rao-result.export.core-cne."; + // Creation of time interval public static ESMPDateTimeInterval createEsmpDateTimeInterval(OffsetDateTime offsetDateTime) { DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'"); diff --git a/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneCnecsCreatorTest.java b/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneCnecsCreatorTest.java index 34829e7bd1..39389cf41a 100644 --- a/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneCnecsCreatorTest.java +++ b/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneCnecsCreatorTest.java @@ -11,7 +11,6 @@ import com.powsybl.contingency.ContingencyElementType; import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.Unit; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.cneexportercommons.CneHelper; import com.powsybl.openrao.data.cneexportercommons.CneUtil; import com.powsybl.openrao.data.corecneexporter.xsd.*; @@ -23,16 +22,16 @@ import com.powsybl.openrao.data.raoresultapi.RaoResult; import com.powsybl.openrao.raoapi.parameters.ObjectiveFunctionParameters; import com.powsybl.openrao.raoapi.parameters.RaoParameters; -import com.powsybl.openrao.raoapi.parameters.extensions.LoopFlowParametersExtension; -import com.powsybl.iidm.network.Network; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.List; import java.util.Optional; +import java.util.Properties; import java.util.Set; +import static com.powsybl.openrao.data.corecneexporter.CoreCneUtil.CORE_CNE_EXPORT_PROPERTIES_PREFIX; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -46,16 +45,14 @@ class CoreCneCnecsCreatorTest { private static final String CURATIVE_INSTANT_ID = "curative"; private Crac crac; - private Network network; private RaoResult raoResult; private RaoParameters raoParameters; - private CneExporterParameters exporterParameters; private Instant curativeInstant; + private Properties properties; @BeforeEach public void setUp() { CneUtil.initUniqueIds(); - network = Network.read("TestCase12Nodes.uct", getClass().getResourceAsStream("/TestCase12Nodes.uct")); crac = CracFactory.findDefault().create("test-crac") .newInstant(PREVENTIVE_INSTANT_ID, InstantKind.PREVENTIVE) .newInstant(OUTAGE_INSTANT_ID, InstantKind.OUTAGE) @@ -64,8 +61,19 @@ public void setUp() { curativeInstant = crac.getInstant(CURATIVE_INSTANT_ID); raoResult = Mockito.mock(RaoResult.class); raoParameters = new RaoParameters(); - exporterParameters = new CneExporterParameters("22XCORESO------S-20211115-F299v1", 2, "10YDOM-REGION-1V", CneExporterParameters.ProcessType.DAY_AHEAD_CC, - "22XCORESO------S", CneExporterParameters.RoleType.REGIONAL_SECURITY_COORDINATOR, "17XTSO-CS------W", CneExporterParameters.RoleType.CAPACITY_COORDINATOR, "2021-10-30T22:00Z/2021-10-31T23:00Z"); + + properties = new Properties(); + properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "true"); + properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "true"); + properties.setProperty("rao-result.export.core-cne.document-id", "22XCORESO------S-20211115-F299v1"); + properties.setProperty("rao-result.export.core-cne.revision-number", "2"); + properties.setProperty("rao-result.export.core-cne.domain-id", "10YDOM-REGION-1V"); + properties.setProperty("rao-result.export.core-cne.process-type", "A48"); + properties.setProperty("rao-result.export.core-cne.sender-id", "22XCORESO------S"); + properties.setProperty("rao-result.export.core-cne.sender-role", "A44"); + properties.setProperty("rao-result.export.core-cne.receiver-id", "17XTSO-CS------W"); + properties.setProperty("rao-result.export.core-cne.receiver-role", "A36"); + properties.setProperty("rao-result.export.core-cne.time-interval", "2021-10-30T22:00Z/2021-10-31T23:00Z"); } private void checkConstraintSeriesContent(ConstraintSeries cs, FlowCnec cnec, String businessType, List countries, boolean asMnec, @@ -171,8 +179,7 @@ void testExportTwoPreventiveCnecs() { mockCnecResult(cnec2, 800, -200, -999999999, .2); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneCnecsCreator cneCnecsCreator = new CoreCneCnecsCreator(cneHelper, new MockCracCreationContext(crac)); List cnecsConstraintSeries = cneCnecsCreator.generate(); @@ -224,7 +231,7 @@ void testExportPreventivePureMnec() { mockCnecResult(cnec1, 80, 20, 200, .1); raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneCnecsCreator cneCnecsCreator = new CoreCneCnecsCreator(cneHelper, new MockCracCreationContext(crac)); List cnecsConstraintSeries = cneCnecsCreator.generate(); @@ -262,7 +269,7 @@ void testExportPreventiveCnecAndMnec() { mockCnecResult(cnec1, 80, 20, 200, .1); raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneCnecsCreator cneCnecsCreator = new CoreCneCnecsCreator(cneHelper, new MockCracCreationContext(crac)); List cnecsConstraintSeries = cneCnecsCreator.generate(); @@ -338,9 +345,8 @@ void testCurativeCnecs() { mockCnecResult(cnecOutage, 85, 25, 205, .1); mockCnecResult(cnecCur, 85, 28, 208, .1); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); when(raoResult.getActivatedNetworkActionsDuringState(crac.getState(cnecCur.getState().getContingency().orElseThrow(), curativeInstant))).thenReturn(Set.of(Mockito.mock(NetworkAction.class))); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneCnecsCreator cneCnecsCreator = new CoreCneCnecsCreator(cneHelper, new MockCracCreationContext(crac)); List cnecsConstraintSeries = cneCnecsCreator.generate(); @@ -391,9 +397,9 @@ void testWithLoopFlow() { mockCnecResult(cnec1, 80, 20, 200, .1); Mockito.when(raoResult.getLoopFlow(any(), eq(cnec1), eq(TwoSides.TWO), eq(Unit.MEGAWATT))).thenReturn(123.); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - raoParameters.addExtension(LoopFlowParametersExtension.class, new LoopFlowParametersExtension()); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + properties.setProperty("rao-result.export.core-cne.with-loop-flows", "true"); + + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneCnecsCreator cneCnecsCreator = new CoreCneCnecsCreator(cneHelper, new MockCracCreationContext(crac)); List cnecsConstraintSeries = cneCnecsCreator.generate(); diff --git a/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneExporterTest.java b/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneExporterTest.java new file mode 100644 index 0000000000..e21751001e --- /dev/null +++ b/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneExporterTest.java @@ -0,0 +1,77 @@ +/* + * 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/. + */ + + +package com.powsybl.openrao.data.corecneexporter; + +import com.powsybl.openrao.commons.OpenRaoException; +import com.powsybl.openrao.data.cracapi.CracCreationContext; +import com.powsybl.openrao.data.cracio.commons.api.stdcreationcontext.UcteCracCreationContext; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.Properties; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * @author Thomas Bouquet {@literal } + */ +class CoreCneExporterTest { + private final CoreCneExporter exporter = new CoreCneExporter(); + + @Test + void testFormat() { + assertEquals("CORE-CNE", exporter.getFormat()); + } + + @Test + void testExportWithWrongCracCreationContext() { + CracCreationContext cracCreationContext = Mockito.mock(CracCreationContext.class); + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.exportData(null, cracCreationContext, null, null)); + assertEquals("CORE-CNE exporter expects a UcteCracCreationContext.", exception.getMessage()); + } + + @Test + void testProperties() { + assertEquals(Set.of("rao-result.export.core-cne.document-id", "rao-result.export.core-cne.revision-number", "rao-result.export.core-cne.domain-id", "rao-result.export.core-cne.process-type", "rao-result.export.core-cne.sender-id", "rao-result.export.core-cne.sender-role", "rao-result.export.core-cne.receiver-id", "rao-result.export.core-cne.receiver-role", "rao-result.export.core-cne.time-interval"), exporter.getRequiredProperties()); + } + + @Test + void testCracCreationContextClass() { + assertEquals(UcteCracCreationContext.class, exporter.getCracCreationContextClass()); + } + + @Test + void testWrongCracCreationContextClass() { + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(Mockito.mock(CracCreationContext.class), null)); + assertEquals("CORE-CNE exporter expects a UcteCracCreationContext.", exception.getMessage()); + } + + @Test + void testNullProperties() { + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(Mockito.mock(UcteCracCreationContext.class), null)); + assertEquals("The export properties cannot be null for CORE-CNE export.", exception.getMessage()); + } + + @Test + void testMissingRequiredProperty() { + Properties properties = new Properties(); + properties.setProperty("rao-result.export.core-cne.document-id", ""); + properties.setProperty("rao-result.export.core-cne.revision-number", ""); + properties.setProperty("rao-result.export.core-cne.process-type", ""); + properties.setProperty("rao-result.export.core-cne.sender-id", ""); + properties.setProperty("rao-result.export.core-cne.sender-role", ""); + properties.setProperty("rao-result.export.core-cne.receiver-id", ""); + properties.setProperty("rao-result.export.core-cne.receiver-role", ""); + properties.setProperty("rao-result.export.core-cne.time-interval", ""); + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(Mockito.mock(UcteCracCreationContext.class), properties)); + assertEquals("The mandatory rao-result.export.core-cne.domain-id property is missing for CORE-CNE export.", exception.getMessage()); + } +} diff --git a/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneRemedialActionsCreatorTest.java b/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneRemedialActionsCreatorTest.java index 66dad848c9..9c25879335 100644 --- a/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneRemedialActionsCreatorTest.java +++ b/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneRemedialActionsCreatorTest.java @@ -9,7 +9,6 @@ import com.powsybl.contingency.ContingencyElementType; import com.powsybl.openrao.commons.Unit; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.cneexportercommons.CneHelper; import com.powsybl.openrao.data.cneexportercommons.CneUtil; import com.powsybl.openrao.data.corecneexporter.xsd.ConstraintSeries; @@ -27,15 +26,13 @@ import com.powsybl.openrao.data.cracapi.usagerule.UsageMethod; import com.powsybl.openrao.data.cracio.commons.api.stdcreationcontext.UcteCracCreationContext; import com.powsybl.openrao.data.raoresultapi.RaoResult; -import com.powsybl.openrao.raoapi.parameters.ObjectiveFunctionParameters; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; -import com.powsybl.iidm.network.Network; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.*; +import static com.powsybl.openrao.data.corecneexporter.CoreCneUtil.CORE_CNE_EXPORT_PROPERTIES_PREFIX; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.any; @@ -49,22 +46,16 @@ class CoreCneRemedialActionsCreatorTest { private static final String CURATIVE_INSTANT_ID = "curative"; private Crac crac; - private Network network; private RaoResult raoResult; - private RaoParameters raoParameters; private List cnecsConstraintSeries; - private CneExporterParameters exporterParameters; private Instant outageInstant; private Instant curativeInstant; + private Properties properties; @BeforeEach public void setUp() { CneUtil.initUniqueIds(); - network = Network.read("TestCase12Nodes.uct", getClass().getResourceAsStream("/TestCase12Nodes.uct")); - exporterParameters = new CneExporterParameters("22XCORESO------S-20211115-F299v1", 10, "10YDOM-REGION-1V", CneExporterParameters.ProcessType.DAY_AHEAD_CC, - "22XCORESO------S", CneExporterParameters.RoleType.REGIONAL_SECURITY_COORDINATOR, "17XTSO-CS------W", CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "2021-10-30T22:00Z/2021-10-31T23:00Z"); - crac = CracFactory.findDefault().create("test-crac") + crac = CracFactory.findDefault().create("test-getCrac") .newInstant(PREVENTIVE_INSTANT_ID, InstantKind.PREVENTIVE) .newInstant(OUTAGE_INSTANT_ID, InstantKind.OUTAGE) .newInstant(AUTO_INSTANT_ID, InstantKind.AUTO) @@ -84,7 +75,6 @@ public void setUp() { .newThreshold().withUnit(Unit.MEGAWATT).withMax(100.).withSide(TwoSides.TWO).add() .add(); raoResult = Mockito.mock(RaoResult.class); - raoParameters = new RaoParameters(); ContingencySeries contingencySeries = new ContingencySeries(); contingencySeries.setName("contingency-id"); @@ -102,6 +92,18 @@ public void setUp() { cnecsConstraintSeries.get(2).getContingencySeries().add(contingencySeries); cnecsConstraintSeries.add(new ConstraintSeries()); cnecsConstraintSeries.get(3).setBusinessType("B54"); + + properties = new Properties(); + properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "true"); + properties.setProperty("rao-result.export.core-cne.document-id", "22XCORESO------S-20211115-F299v1"); + properties.setProperty("rao-result.export.core-cne.revision-number", "10"); + properties.setProperty("rao-result.export.core-cne.domain-id", "10YDOM-REGION-1V"); + properties.setProperty("rao-result.export.core-cne.process-type", "A48"); + properties.setProperty("rao-result.export.core-cne.sender-id", "22XCORESO------S"); + properties.setProperty("rao-result.export.core-cne.sender-role", "A44"); + properties.setProperty("rao-result.export.core-cne.receiver-id", "17XTSO-CS------W"); + properties.setProperty("rao-result.export.core-cne.receiver-role", "A36"); + properties.setProperty("rao-result.export.core-cne.time-interval", "2021-10-30T22:00Z/2021-10-31T23:00Z"); } @Test @@ -116,8 +118,7 @@ void testPstInitialSetpoint() { Mockito.when(raoResult.isActivatedDuringState(crac.getStates().iterator().next(), pstRangeAction)).thenReturn(true); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneRemedialActionsCreator cneRemedialActionsCreator = new CoreCneRemedialActionsCreator(cneHelper, new MockCracCreationContext(crac), new ArrayList<>()); List constraintSeriesList = cneRemedialActionsCreator.generate(); @@ -152,8 +153,7 @@ void testPstInitialSetpointUnused() { Mockito.when(raoResult.isActivatedDuringState(crac.getStates().iterator().next(), pstRangeAction)).thenReturn(false); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneRemedialActionsCreator cneRemedialActionsCreator = new CoreCneRemedialActionsCreator(cneHelper, new MockCracCreationContext(crac), new ArrayList<>()); List constraintSeriesList = cneRemedialActionsCreator.generate(); @@ -174,8 +174,7 @@ void testIgnorePstWithNoUsageRule() { Mockito.when(raoResult.getActivatedRangeActionsDuringState(any())).thenReturn(Set.of(pstRangeAction)); Mockito.when(raoResult.isActivatedDuringState(crac.getStates().iterator().next(), pstRangeAction)).thenReturn(true); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneRemedialActionsCreator cneRemedialActionsCreator = new CoreCneRemedialActionsCreator(cneHelper, new MockCracCreationContext(crac), new ArrayList<>()); List constraintSeriesList = cneRemedialActionsCreator.generate(); @@ -200,8 +199,7 @@ void testPstUsedInPreventive() { Mockito.when(raoResult.getOptimizedTapOnState(crac.getPreventiveState(), pstRangeAction)).thenReturn(16); Mockito.when(raoResult.isActivatedDuringState(crac.getStates().iterator().next(), pstRangeAction)).thenReturn(true); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneRemedialActionsCreator cneRemedialActionsCreator = new CoreCneRemedialActionsCreator(cneHelper, new MockCracCreationContext(crac), cnecsConstraintSeries); @@ -253,8 +251,7 @@ void testPstUsedInCurative() { Mockito.when(raoResult.getOptimizedTapOnState(crac.getState("contingency-id", curativeInstant), pstRangeAction)).thenReturn(16); Mockito.when(raoResult.isActivatedDuringState(crac.getStates().iterator().next(), pstRangeAction)).thenReturn(true); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneRemedialActionsCreator cneRemedialActionsCreator = new CoreCneRemedialActionsCreator(cneHelper, new MockCracCreationContext(crac), cnecsConstraintSeries); @@ -301,8 +298,7 @@ void testIgnoreNetworkActionWithNoUsageRule() { Mockito.when(raoResult.getActivatedNetworkActionsDuringState(any())).thenReturn(Set.of(networkAction)); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneRemedialActionsCreator cneRemedialActionsCreator = new CoreCneRemedialActionsCreator(cneHelper, new MockCracCreationContext(crac), new ArrayList<>()); List constraintSeriesList = cneRemedialActionsCreator.generate(); @@ -321,8 +317,7 @@ void testNetworkActionUsedInPreventive() { Mockito.when(raoResult.getActivatedNetworkActionsDuringState(crac.getPreventiveState())).thenReturn(Set.of(networkAction)); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneRemedialActionsCreator cneRemedialActionsCreator = new CoreCneRemedialActionsCreator(cneHelper, new MockCracCreationContext(crac), cnecsConstraintSeries); @@ -365,8 +360,7 @@ void testNetworkActionUsedInCurative() { Mockito.when(raoResult.getActivatedNetworkActionsDuringState(crac.getState("contingency-id", outageInstant))).thenReturn(new HashSet()); Mockito.when(raoResult.getActivatedNetworkActionsDuringState(crac.getState("contingency-id", curativeInstant))).thenReturn(Set.of(networkAction)); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); CoreCneRemedialActionsCreator cneRemedialActionsCreator = new CoreCneRemedialActionsCreator(cneHelper, new MockCracCreationContext(crac), cnecsConstraintSeries); @@ -411,8 +405,7 @@ void testPstInitialSetpointInverted() { Mockito.when(raoResult.isActivatedDuringState(crac.getStates().iterator().next(), pstRangeAction)).thenReturn(true); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); UcteCracCreationContext cracCreationContext = new MockCracCreationContext(crac); MockCracCreationContext.MockRemedialActionCreationContext raContext = (MockCracCreationContext.MockRemedialActionCreationContext) cracCreationContext.getRemedialActionCreationContexts().get(0); raContext.setInverted(true); @@ -455,8 +448,7 @@ void testPstUsedInPreventiveInverted() { Mockito.when(raoResult.getOptimizedTapOnState(crac.getPreventiveState(), pstRangeAction)).thenReturn(16); Mockito.when(raoResult.isActivatedDuringState(crac.getStates().iterator().next(), pstRangeAction)).thenReturn(true); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); UcteCracCreationContext cracCreationContext = new MockCracCreationContext(crac); MockCracCreationContext.MockRemedialActionCreationContext raContext = (MockCracCreationContext.MockRemedialActionCreationContext) cracCreationContext.getRemedialActionCreationContexts().get(0); raContext.setInverted(true); @@ -511,8 +503,7 @@ void testPstUsedInCurativeInverted() { Mockito.when(raoResult.getOptimizedTapOnState(crac.getState("contingency-id", curativeInstant), pstRangeAction)).thenReturn(16); Mockito.when(raoResult.isActivatedDuringState(crac.getState("contingency-id", curativeInstant), pstRangeAction)).thenReturn(true); - raoParameters.getObjectiveFunctionParameters().setType(ObjectiveFunctionParameters.ObjectiveFunctionType.MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT); - CneHelper cneHelper = new CneHelper(crac, raoResult, raoParameters, exporterParameters); + CneHelper cneHelper = new CneHelper(crac, raoResult, properties, CORE_CNE_EXPORT_PROPERTIES_PREFIX); UcteCracCreationContext cracCreationContext = new MockCracCreationContext(crac); MockCracCreationContext.MockRemedialActionCreationContext raContext = (MockCracCreationContext.MockRemedialActionCreationContext) cracCreationContext.getRemedialActionCreationContexts().get(0); raContext.setInverted(true); diff --git a/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneTest.java b/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneTest.java index 4dfefbdfe7..b6f60721f2 100644 --- a/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneTest.java +++ b/data/result-exporter/core-cne-exporter/src/test/java/com/powsybl/openrao/data/corecneexporter/CoreCneTest.java @@ -7,15 +7,12 @@ package com.powsybl.openrao.data.corecneexporter; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.cneexportercommons.CneUtil; import com.powsybl.openrao.data.corecneexporter.xsd.CriticalNetworkElementMarketDocument; import com.powsybl.openrao.data.cracapi.Crac; import com.powsybl.openrao.data.cracapi.CracFactory; import com.powsybl.openrao.data.cracio.commons.api.stdcreationcontext.UcteCracCreationContext; import com.powsybl.openrao.data.raoresultapi.RaoResult; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; -import com.powsybl.iidm.network.Network; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -23,6 +20,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.ArrayList; +import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,20 +29,16 @@ */ class CoreCneTest { private Crac crac; - private Network network; private RaoResult raoResult; - private RaoParameters raoParameters; - private CneExporterParameters exporterParameters; private UcteCracCreationContext cracCreationContext; @BeforeEach public void setUp() { CneUtil.initUniqueIds(); - network = Network.read("TestCase12Nodes.uct", getClass().getResourceAsStream("/TestCase12Nodes.uct")); crac = CracFactory.findDefault().create("test-crac"); raoResult = Mockito.mock(RaoResult.class); - raoParameters = new RaoParameters(); cracCreationContext = Mockito.mock(UcteCracCreationContext.class); + Mockito.when(cracCreationContext.getCrac()).thenReturn(crac); Mockito.when(cracCreationContext.getBranchCnecCreationContexts()).thenReturn(new ArrayList<>()); Mockito.when(cracCreationContext.getRemedialActionCreationContexts()).thenReturn(new ArrayList<>()); Mockito.when(cracCreationContext.getTimeStamp()).thenReturn(OffsetDateTime.of(2021, 11, 15, 11, 50, 0, 0, ZoneOffset.of("+1"))); @@ -52,10 +46,19 @@ public void setUp() { @Test void testHeader() { - exporterParameters = new CneExporterParameters("22XCORESO------S-20211115-F299v1", 2, "10YDOM-REGION-1V", CneExporterParameters.ProcessType.DAY_AHEAD_CC, - "22XCORESO------S", CneExporterParameters.RoleType.REGIONAL_SECURITY_COORDINATOR, "17XTSO-CS------W", CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "2021-10-30T22:00:00Z/2021-10-31T23:00:00Z"); - CoreCne cne = new CoreCne(crac, cracCreationContext, raoResult, raoParameters, exporterParameters); + Properties properties = new Properties(); + properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "true"); + properties.setProperty("rao-result.export.core-cne.document-id", "22XCORESO------S-20211115-F299v1"); + properties.setProperty("rao-result.export.core-cne.revision-number", "2"); + properties.setProperty("rao-result.export.core-cne.domain-id", "10YDOM-REGION-1V"); + properties.setProperty("rao-result.export.core-cne.process-type", "A48"); + properties.setProperty("rao-result.export.core-cne.sender-id", "22XCORESO------S"); + properties.setProperty("rao-result.export.core-cne.sender-role", "A44"); + properties.setProperty("rao-result.export.core-cne.receiver-id", "17XTSO-CS------W"); + properties.setProperty("rao-result.export.core-cne.receiver-role", "A36"); + properties.setProperty("rao-result.export.core-cne.time-interval", "2021-10-30T22:00Z/2021-10-31T23:00Z"); + + CoreCne cne = new CoreCne(cracCreationContext, raoResult, properties); cne.generate(); CriticalNetworkElementMarketDocument marketDocument = cne.getMarketDocument(); assertEquals("22XCORESO------S-20211115-F299v1", marketDocument.getMRID()); diff --git a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCne.java b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCne.java index d7e65d8a5d..213b87e489 100644 --- a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCne.java +++ b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCne.java @@ -9,19 +9,17 @@ import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.PhysicalParameter; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.cneexportercommons.CneUtil; -import com.powsybl.openrao.data.cracapi.Crac; import com.powsybl.openrao.data.cracio.cim.craccreator.CimCracCreationContext; import com.powsybl.openrao.data.raoresultapi.ComputationStatus; import com.powsybl.openrao.data.raoresultapi.RaoResult; import com.powsybl.openrao.data.swecneexporter.xsd.*; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; import javax.xml.datatype.DatatypeConfigurationException; import java.time.OffsetDateTime; import java.util.List; import java.util.Objects; +import java.util.Properties; import static com.powsybl.openrao.data.cneexportercommons.CneConstants.*; import static com.powsybl.openrao.data.cneexportercommons.CneUtil.createXMLGregorianCalendarNow; @@ -39,9 +37,9 @@ public class SweCne { private final SweCneHelper sweCneHelper; private final CimCracCreationContext cracCreationContext; - public SweCne(Crac crac, CimCracCreationContext cracCreationContext, RaoResult raoResult, RaoParameters raoParameters, CneExporterParameters exporterParameters) { + public SweCne(CimCracCreationContext cracCreationContext, RaoResult raoResult, Properties properties) { marketDocument = new CriticalNetworkElementMarketDocument(); - sweCneHelper = new SweCneHelper(crac, raoResult, raoParameters, exporterParameters); + sweCneHelper = new SweCneHelper(cracCreationContext.getCrac(), raoResult, properties); this.cracCreationContext = cracCreationContext; } @@ -74,14 +72,14 @@ public void generate() { // fills the header of the CNE private void fillHeader(OffsetDateTime offsetDateTime) { - marketDocument.setMRID(sweCneHelper.getExporterParameters().getDocumentId()); - marketDocument.setRevisionNumber(String.valueOf(sweCneHelper.getExporterParameters().getRevisionNumber())); + marketDocument.setMRID(sweCneHelper.getDocumentId()); + marketDocument.setRevisionNumber(String.valueOf(sweCneHelper.getRevisionNumber())); marketDocument.setType(CNE_TYPE); - marketDocument.setProcessProcessType(sweCneHelper.getExporterParameters().getProcessType().getCode()); - marketDocument.setSenderMarketParticipantMRID(createPartyIDString(A01_CODING_SCHEME, sweCneHelper.getExporterParameters().getSenderId())); - marketDocument.setSenderMarketParticipantMarketRoleType(sweCneHelper.getExporterParameters().getSenderRole().getCode()); - marketDocument.setReceiverMarketParticipantMRID(createPartyIDString(A01_CODING_SCHEME, sweCneHelper.getExporterParameters().getReceiverId())); - marketDocument.setReceiverMarketParticipantMarketRoleType(sweCneHelper.getExporterParameters().getReceiverRole().getCode()); + marketDocument.setProcessProcessType(sweCneHelper.getProcessType()); + marketDocument.setSenderMarketParticipantMRID(createPartyIDString(A01_CODING_SCHEME, sweCneHelper.getSenderId())); + marketDocument.setSenderMarketParticipantMarketRoleType(sweCneHelper.getSenderRole()); + marketDocument.setReceiverMarketParticipantMRID(createPartyIDString(A01_CODING_SCHEME, sweCneHelper.getReceiverId())); + marketDocument.setReceiverMarketParticipantMarketRoleType(sweCneHelper.getReceiverRole()); marketDocument.setCreatedDateTime(createXMLGregorianCalendarNow()); marketDocument.setTimePeriodTimeInterval(SweCneUtil.createEsmpDateTimeInterval(offsetDateTime)); } diff --git a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneExporter.java b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneExporter.java index a7a3e40930..d051741e8f 100644 --- a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneExporter.java +++ b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneExporter.java @@ -7,14 +7,16 @@ package com.powsybl.openrao.data.swecneexporter; +import com.google.auto.service.AutoService; import com.powsybl.openrao.commons.OpenRaoException; import com.powsybl.openrao.commons.logs.OpenRaoLoggerProvider; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; +import com.powsybl.openrao.data.cracapi.CracCreationContext; import com.powsybl.openrao.data.cracio.cim.craccreator.CimCracCreationContext; import com.powsybl.openrao.data.raoresultapi.RaoResult; +import com.powsybl.openrao.data.raoresultapi.io.Exporter; import com.powsybl.openrao.data.swecneexporter.xsd.CriticalNetworkElementMarketDocument; import com.powsybl.openrao.data.cracapi.Crac; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; +import org.apache.commons.lang3.NotImplementedException; import org.xml.sax.SAXException; import javax.xml.XMLConstants; @@ -33,22 +35,83 @@ import java.io.StringReader; import java.io.StringWriter; import java.util.Objects; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; import static com.powsybl.openrao.data.cneexportercommons.CneConstants.*; +import static com.powsybl.openrao.data.swecneexporter.SweCneUtil.SWE_CNE_EXPORT_PROPERTIES_PREFIX; /** - * Xml export of the CNE file + * SWE-CNE Rao Result exporter in XML format. + *

+ * Optional properties: + *

    + *
  • + * relative-positive-margins: boolean (default is "false"). + *
  • + *
  • + * with-loop-flows: boolean (default is "false"). + *
  • + *
  • + * mnec-acceptable-margin-diminution: double (default is "0"). + *
  • + *
* + * Required properties: + *
    + *
  • + * document-id: string + *
  • + *
  • + * revision-number: integer + *
  • + *
  • + * domain-id: string + *
  • + *
  • + * process-type: string + *
  • + *
  • + * sender-id: string + *
  • + *
  • + * sender-role: string + *
  • + *
  • + * receiver-id: string + *
  • + *
  • + * receiver-role: string + *
  • + *
  • + * time-interval: string + *
  • + *
* @author Viktor Terrier {@literal } * @author Peter Mitri {@literal } */ -public class SweCneExporter { +@AutoService(Exporter.class) +public class SweCneExporter implements Exporter { + @Override + public String getFormat() { + return "SWE-CNE"; + } + + @Override + public Set getRequiredProperties() { + return CNE_REQUIRED_PROPERTIES.stream().map(propertyName -> SWE_CNE_EXPORT_PROPERTIES_PREFIX + propertyName).collect(Collectors.toSet()); + } - public void exportCne(Crac crac, - CimCracCreationContext cracCreationContext, - RaoResult raoResult, RaoParameters raoParameters, - CneExporterParameters exporterParameters, OutputStream outputStream) { - SweCne cne = new SweCne(crac, cracCreationContext, raoResult, raoParameters, exporterParameters); + @Override + public Class getCracCreationContextClass() { + return CimCracCreationContext.class; + } + + @Override + public void exportData(RaoResult raoResult, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) { + validateDataToExport(cracCreationContext, properties); + SweCne cne = new SweCne((CimCracCreationContext) cracCreationContext, raoResult, properties); cne.generate(); CriticalNetworkElementMarketDocument marketDocument = cne.getMarketDocument(); StringWriter stringWriter = new StringWriter(); @@ -79,6 +142,11 @@ public void exportCne(Crac crac, } } + @Override + public void exportData(RaoResult raoResult, Crac crac, Properties properties, OutputStream outputStream) { + throw new NotImplementedException("CracCreationContext is required for CNE export."); + } + private static String getSchemaFile(String schemaName) { return Objects.requireNonNull(SweCneExporter.class.getResource("/xsd/" + schemaName)).toExternalForm(); } diff --git a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneHelper.java b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneHelper.java index 93a4a20d2e..87607006b6 100644 --- a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneHelper.java +++ b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneHelper.java @@ -7,27 +7,28 @@ package com.powsybl.openrao.data.swecneexporter; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.cneexportercommons.CneHelper; import com.powsybl.contingency.Contingency; import com.powsybl.openrao.data.cracapi.Crac; import com.powsybl.openrao.data.raoresultapi.ComputationStatus; import com.powsybl.openrao.data.raoresultapi.RaoResult; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; import java.util.HashMap; import java.util.Map; +import java.util.Properties; import java.util.function.Function; import java.util.stream.Collectors; +import static com.powsybl.openrao.data.swecneexporter.SweCneUtil.SWE_CNE_EXPORT_PROPERTIES_PREFIX; + /** * @author Godelaine de Montmorillon {@literal } */ public class SweCneHelper extends CneHelper { private Map contingencyFailureMap = new HashMap<>(); - public SweCneHelper(Crac crac, RaoResult raoResult, RaoParameters raoParameters, CneExporterParameters exporterParameters) { - super(crac, raoResult, raoParameters, exporterParameters); + public SweCneHelper(Crac crac, RaoResult raoResult, Properties properties) { + super(crac, raoResult, properties, SWE_CNE_EXPORT_PROPERTIES_PREFIX); defineContingencyFailureMap(); } diff --git a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneUtil.java b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneUtil.java index 013c72de07..38d71ead46 100644 --- a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneUtil.java +++ b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneUtil.java @@ -36,6 +36,7 @@ private SweCneUtil() { } public static int DEFAULT_DECIMALS_FOR_ROUNDING = 1; + public static final String SWE_CNE_EXPORT_PROPERTIES_PREFIX = "rao-result.export.swe-cne."; // Creation of time interval public static ESMPDateTimeInterval createEsmpDateTimeInterval(OffsetDateTime offsetDateTime) { diff --git a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweRemedialActionSeriesCreator.java b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweRemedialActionSeriesCreator.java index 35e30f796f..3c331de48e 100644 --- a/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweRemedialActionSeriesCreator.java +++ b/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweRemedialActionSeriesCreator.java @@ -153,7 +153,7 @@ private RemedialActionSeries generateRaSeries(State state, RemedialActionSeriesC } else if (usedRa instanceof PstRangeAction pstRangeAction) { return generatePstRaSeries(pstRangeAction, state, context, onlyReference); } else if (usedRa instanceof HvdcRangeAction hvdcRangeAction) { - // In case of an HVDC, the native crac has one series per direction, we select the one that corresponds to the sign of the setpoint + // In case of an HVDC, the native getCrac has one series per direction, we select the one that corresponds to the sign of the setpoint if (context.isInverted() == (raoResult.getOptimizedSetPointOnState(state, hvdcRangeAction) < 0)) { return generateHvdcRaSeries(hvdcRangeAction, state, context); } diff --git a/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneDivergentAngleMonitoringTest.java b/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneDivergentAngleMonitoringTest.java index fb5db3e091..c011a6bccc 100644 --- a/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneDivergentAngleMonitoringTest.java +++ b/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneDivergentAngleMonitoringTest.java @@ -9,21 +9,18 @@ import com.powsybl.openrao.commons.PhysicalParameter; import com.powsybl.openrao.commons.Unit; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.cracapi.Crac; import com.powsybl.openrao.data.cracapi.CracCreationContext; import com.powsybl.openrao.data.cracapi.InstantKind; import com.powsybl.openrao.data.cracapi.cnec.Cnec; import com.powsybl.openrao.data.cracapi.parameters.CracCreationParameters; import com.powsybl.openrao.data.cracimpl.AngleCnecValue; -import com.powsybl.openrao.data.cracio.cim.craccreator.CimCracCreationContext; import com.powsybl.openrao.data.cracio.cim.parameters.CimCracCreationParameters; import com.powsybl.openrao.data.cracio.cim.parameters.RangeActionSpeed; import com.powsybl.openrao.data.raoresultapi.RaoResult; import com.powsybl.openrao.monitoring.results.CnecResult; import com.powsybl.openrao.monitoring.results.MonitoringResult; import com.powsybl.openrao.monitoring.results.RaoResultWithAngleMonitoring; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; import com.powsybl.iidm.network.Network; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -32,6 +29,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Map; +import java.util.Properties; import java.util.Set; import static com.powsybl.openrao.data.swecneexporter.SweCneTest.compareCneFiles; @@ -64,13 +62,19 @@ void testExport() throws IOException { Cnec.SecurityStatus.FAILURE); RaoResultWithAngleMonitoring raoResultWithAngleMonitoring = new RaoResultWithAngleMonitoring(raoResult, monitoringResult); - CneExporterParameters params = new CneExporterParameters( - "documentId", 1, null, CneExporterParameters.ProcessType.Z01, - "senderId", CneExporterParameters.RoleType.SYSTEM_OPERATOR, - "receiverId", CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "2021-04-02T12:00:00Z/2021-04-02T13:00:00Z"); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - new SweCneExporter().exportCne(crac, (CimCracCreationContext) cracCreationContext, raoResultWithAngleMonitoring, new RaoParameters(), params, outputStream); + Properties properties = new Properties(); + properties.setProperty("rao-result.export.swe-cne.document-id", "documentId"); + properties.setProperty("rao-result.export.swe-cne.revision-number", "1"); + properties.setProperty("rao-result.export.swe-cne.domain-id", "domainId"); + properties.setProperty("rao-result.export.swe-cne.process-type", "Z01"); + properties.setProperty("rao-result.export.swe-cne.sender-id", "senderId"); + properties.setProperty("rao-result.export.swe-cne.sender-role", "A04"); + properties.setProperty("rao-result.export.swe-cne.receiver-id", "receiverId"); + properties.setProperty("rao-result.export.swe-cne.receiver-role", "A36"); + properties.setProperty("rao-result.export.swe-cne.time-interval", "2021-04-02T12:00:00Z/2021-04-02T13:00:00Z"); + new SweCneExporter().exportData(raoResultWithAngleMonitoring, cracCreationContext, properties, outputStream); try { InputStream expectedCneInputStream = new FileInputStream(SweCneDivergentAngleMonitoringTest.class.getResource("/SweCNEDivergentAngleMonitoring_Z01.xml").getFile()); compareCneFiles(expectedCneInputStream, new ByteArrayInputStream(outputStream.toByteArray())); diff --git a/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneExporterTest.java b/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneExporterTest.java new file mode 100644 index 0000000000..364e2bf8a5 --- /dev/null +++ b/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneExporterTest.java @@ -0,0 +1,77 @@ +/* + * 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/. + */ + + +package com.powsybl.openrao.data.swecneexporter; + +import com.powsybl.openrao.commons.OpenRaoException; +import com.powsybl.openrao.data.cracapi.CracCreationContext; +import com.powsybl.openrao.data.cracio.cim.craccreator.CimCracCreationContext; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.Properties; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * @author Thomas Bouquet {@literal } + */ +class SweCneExporterTest { + private final SweCneExporter exporter = new SweCneExporter(); + + @Test + void testFormat() { + assertEquals("SWE-CNE", exporter.getFormat()); + } + + @Test + void testExportWithWrongCracCreationContext() { + CracCreationContext cracCreationContext = Mockito.mock(CracCreationContext.class); + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.exportData(null, cracCreationContext, null, null)); + assertEquals("SWE-CNE exporter expects a CimCracCreationContext.", exception.getMessage()); + } + + @Test + void testProperties() { + assertEquals(Set.of("rao-result.export.swe-cne.document-id", "rao-result.export.swe-cne.revision-number", "rao-result.export.swe-cne.domain-id", "rao-result.export.swe-cne.process-type", "rao-result.export.swe-cne.sender-id", "rao-result.export.swe-cne.sender-role", "rao-result.export.swe-cne.receiver-id", "rao-result.export.swe-cne.receiver-role", "rao-result.export.swe-cne.time-interval"), exporter.getRequiredProperties()); + } + + @Test + void testCracCreationContextClass() { + assertEquals(CimCracCreationContext.class, exporter.getCracCreationContextClass()); + } + + @Test + void testWrongCracCreationContextClass() { + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(Mockito.mock(CracCreationContext.class), null)); + assertEquals("SWE-CNE exporter expects a CimCracCreationContext.", exception.getMessage()); + } + + @Test + void testNullProperties() { + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(Mockito.mock(CimCracCreationContext.class), null)); + assertEquals("The export properties cannot be null for SWE-CNE export.", exception.getMessage()); + } + + @Test + void testMissingRequiredProperty() { + Properties properties = new Properties(); + properties.setProperty("rao-result.export.swe-cne.document-id", ""); + properties.setProperty("rao-result.export.swe-cne.revision-number", ""); + properties.setProperty("rao-result.export.swe-cne.domain-id", ""); + properties.setProperty("rao-result.export.swe-cne.sender-id", ""); + properties.setProperty("rao-result.export.swe-cne.sender-role", ""); + properties.setProperty("rao-result.export.swe-cne.receiver-id", ""); + properties.setProperty("rao-result.export.swe-cne.receiver-role", ""); + properties.setProperty("rao-result.export.swe-cne.time-interval", ""); + OpenRaoException exception = assertThrows(OpenRaoException.class, () -> exporter.validateDataToExport(Mockito.mock(CimCracCreationContext.class), properties)); + assertEquals("The mandatory rao-result.export.swe-cne.process-type property is missing for SWE-CNE export.", exception.getMessage()); + } +} diff --git a/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneTest.java b/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneTest.java index b4c8f93340..438d1b0114 100644 --- a/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneTest.java +++ b/data/result-exporter/swe-cne-exporter/src/test/java/com/powsybl/openrao/data/swecneexporter/SweCneTest.java @@ -10,21 +10,18 @@ import com.powsybl.iidm.network.Network; import com.powsybl.openrao.commons.PhysicalParameter; import com.powsybl.openrao.commons.Unit; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.cracapi.Crac; import com.powsybl.openrao.data.cracapi.CracCreationContext; import com.powsybl.openrao.data.cracapi.InstantKind; import com.powsybl.openrao.data.cracapi.cnec.Cnec; import com.powsybl.openrao.data.cracapi.parameters.CracCreationParameters; import com.powsybl.openrao.data.cracimpl.AngleCnecValue; -import com.powsybl.openrao.data.cracio.cim.craccreator.CimCracCreationContext; import com.powsybl.openrao.data.cracio.cim.parameters.CimCracCreationParameters; import com.powsybl.openrao.data.cracio.cim.parameters.RangeActionSpeed; import com.powsybl.openrao.data.raoresultapi.RaoResult; import com.powsybl.openrao.monitoring.results.CnecResult; import com.powsybl.openrao.monitoring.results.MonitoringResult; import com.powsybl.openrao.monitoring.results.RaoResultWithAngleMonitoring; -import com.powsybl.openrao.raoapi.parameters.RaoParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -39,6 +36,7 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Map; +import java.util.Properties; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -53,6 +51,7 @@ class SweCneTest { private Network network; private RaoResultWithAngleMonitoring raoResultWithAngle; private RaoResultWithAngleMonitoring raoResultFailureWithAngle; + private Properties properties; @BeforeEach public void setUp() throws IOException { @@ -91,17 +90,23 @@ public void setUp() throws IOException { raoResultWithAngle = new RaoResultWithAngleMonitoring(raoResult, monitoringResult); raoResultFailureWithAngle = new RaoResultWithAngleMonitoring(raoResultWithFailure, monitoringResult); + + properties = new Properties(); + properties.setProperty("rao-result.export.swe-cne.document-id", "documentId"); + properties.setProperty("rao-result.export.swe-cne.revision-number", "1"); + properties.setProperty("rao-result.export.swe-cne.domain-id", "domainId"); + properties.setProperty("rao-result.export.swe-cne.process-type", "Z01"); + properties.setProperty("rao-result.export.swe-cne.sender-id", "senderId"); + properties.setProperty("rao-result.export.swe-cne.sender-role", "A04"); + properties.setProperty("rao-result.export.swe-cne.receiver-id", "receiverId"); + properties.setProperty("rao-result.export.swe-cne.receiver-role", "A36"); + properties.setProperty("rao-result.export.swe-cne.time-interval", "2021-04-02T12:00:00Z/2021-04-02T13:00:00Z"); } @Test void testExport() { - CneExporterParameters params = new CneExporterParameters( - "documentId", 1, null, CneExporterParameters.ProcessType.Z01, - "senderId", CneExporterParameters.RoleType.SYSTEM_OPERATOR, - "receiverId", CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "2021-04-02T12:00:00Z/2021-04-02T13:00:00Z"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - new SweCneExporter().exportCne(crac, (CimCracCreationContext) cracCreationContext, raoResultWithAngle, new RaoParameters(), params, outputStream); + new SweCneExporter().exportData(raoResultWithAngle, cracCreationContext, properties, outputStream); try { InputStream inputStream = new FileInputStream(SweCneTest.class.getResource("/SweCNE_Z01.xml").getFile()); compareCneFiles(inputStream, new ByteArrayInputStream(outputStream.toByteArray())); @@ -112,13 +117,8 @@ void testExport() { @Test void testExportWithFailure() { - CneExporterParameters params = new CneExporterParameters( - "documentId", 1, null, CneExporterParameters.ProcessType.Z01, - "senderId", CneExporterParameters.RoleType.SYSTEM_OPERATOR, - "receiverId", CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "2021-04-02T12:00:00Z/2021-04-02T13:00:00Z"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - new SweCneExporter().exportCne(crac, (CimCracCreationContext) cracCreationContext, raoResultFailureWithAngle, new RaoParameters(), params, outputStream); + new SweCneExporter().exportData(raoResultFailureWithAngle, cracCreationContext, properties, outputStream); try { InputStream inputStream = new FileInputStream(SweCneTest.class.getResource("/SweCNEWithFailure_Z01.xml").getFile()); compareCneFiles(inputStream, new ByteArrayInputStream(outputStream.toByteArray())); diff --git a/docs/output-data/core-cne/introduction.md b/docs/output-data/core-cne/introduction.md index f73181b6fa..b201b607a3 100644 --- a/docs/output-data/core-cne/introduction.md +++ b/docs/output-data/core-cne/introduction.md @@ -1,7 +1,6 @@ The CORE CNE file is the standard RAO output file for the CORE CC process. The [OpenRAO toolbox](https://github.com/powsybl/powsybl-open-rao/tree/main/data/result-exporter/core-cne-exporter) -allows exporting RAO results in a CORE CNE file using a network, an internal [RAO result](/output-data/rao-result.md), -an [internal CRAC](/input-data/crac/json.md), a [UcteCracCreationContext](/input-data/crac/creation-context.md#ucte-implementation), -and [RAO parameters](/parameters.md). +allows exporting [RAO results](/output-data/rao-result.md) in a CORE CNE file using a [UcteCracCreationContext](/input-data/crac/creation-context.md#ucte-implementation) +and specific properties. ![CORE CNE](/_static/img/core-cne.png){.forced-white-background} \ No newline at end of file diff --git a/docs/output-data/core-cne/java-api.md b/docs/output-data/core-cne/java-api.md index 2fd50fc7ed..a0482c931c 100644 --- a/docs/output-data/core-cne/java-api.md +++ b/docs/output-data/core-cne/java-api.md @@ -1,41 +1,39 @@ -After completing the RAO, the user can export the CORE CNE file using this method of [CoreCneExporter](https://github.com/powsybl/powsybl-open-rao/blob/main/data/result-exporter/core-cne-exporter/src/main/java/com/powsybl/openrao/data/corecneexporter/CoreCneExporter.java): +After completing the RAO, the user can export the [`RaoResult`](/output-data/rao-result.md) object as a CORE CNE file using the `write` method with the `"CORE-CNE""` format: ~~~java -public void exportCne(Crac crac, - UcteCracCreationContext cracCreationContext, - RaoResult raoResult, RaoParameters raoParameters, - CneExporterParameters exporterParameters, OutputStream outputStream) +// RaoResult interface +public void write(String format, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) ~~~ With: -- **crac**: the [CRAC object](/input-data/crac/json.md) used for the RAO. +- **raoResult**: the [RaoResult](/output-data/rao-result.md) object containing selected remedial actions and flow results. - **cracCreationContext**: the [CracCreationContext object](/input-data/crac/creation-context.md) generated during [CRAC creation](/input-data/crac/import.md). CORE CNE export only handles [UcteCracCreationContext](/input-data/crac/creation-context.md#ucte-implementation) subtype, because it follows the UCTE conventions. -- **raoResult**: the [RaoResult](/output-data/rao-result.md) object containing selected remedial actions and flow - results. -- **raoParameters**: the [RaoParameters](/parameters.md) used in the RAO. -- **exporterParameters**: a specific object that te user should define, containing meta-information that will be written - in the header of the CNE file: - - **documentId**: document ID to be written in "mRID" field - - **revisionNumber**: integer to be written in "revisionNumber" field - - **domainId**: domain ID to be written in "domain.mRID" field (usually an [ENTSO-E EICode](https://www.entsoe.eu/data/energy-identification-codes-eic/)) - - **processType**: the ENTSO-E code of the process type, to be written in "process.processType" field: +- **properties**: a specific object that te user should define, containing meta-information that will be written + in the header of the CNE file as well as relevant RAO parameters: + - **`"rao-result.export.core-cne.document-id"`**: document ID to be written in "mRID" field + - **`"rao-result.export.core-cne.revision-number"`**: integer to be written in "revisionNumber" field + - **`"rao-result.export.core-cne.domain-id"`**: domain ID to be written in "domain.mRID" field (usually an [ENTSO-E EICode](https://www.entsoe.eu/data/energy-identification-codes-eic/)) + - **`"rao-result.export.core-cne.process-type"`**: the ENTSO-E code of the process type, to be written in "process.processType" field: - **A48**: Day-ahead capacity determination, used for CORE region - - ~~**Z01**~~: Day-ahead capacity determination, used for SWE region (so don't use it here) - - **senderId**: ID of the sender of the CNE document, to be written in "sender_MarketParticipant.mRID" field + - ~~**Z01**~~: Day-ahead capacity determination, used for SWE region (so don't use it here) + - **`"rao-result.export.core-cne.sender-id"`**: ID of the sender of the CNE document, to be written in "sender_MarketParticipant.mRID" field (usually an [ENTSO-E EICode](https://www.entsoe.eu/data/energy-identification-codes-eic/)) - - **senderRole**: ENTSO-E code defining the role of the sender of the CNE document, to be written in + - **`"rao-result.export.core-cne.sender-role"`**: ENTSO-E code defining the role of the sender of the CNE document, to be written in "sender_MarketParticipant.marketRole.type" field: - **A04**: system operator - **A36**: capacity coordinator - **A44**: regional security coordinator - - **receiverId**: ID of the receiver of the CNE document, to be written in "receiver_MarketParticipant.mRID" field + - **`"rao-result.export.core-cne.receiver-id"`**: ID of the receiver of the CNE document, to be written in "receiver_MarketParticipant.mRID" field (usually an [ENTSO-E EICode](https://www.entsoe.eu/data/energy-identification-codes-eic/)) - - **receiverRole**: ENTSO-E code defining the role of the receiver of the CNE document, to be written in + - **`"rao-result.export.core-cne.receiver-role"`**: ENTSO-E code defining the role of the receiver of the CNE document, to be written in "receiver_MarketParticipant.marketRole.type" field. Same value options as senderRole. - - **timeInterval**: time interval of document applicability, to be written in "time_Period.timeInterval" field. It should + - **`"rao-result.export.core-cne.time-interval"`**: time interval of document applicability, to be written in "time_Period.timeInterval" field. It should be formatted as follows: "YYYY-MM-DDTHH:MMZ/YYYY-MM-DDTHH:MMZ" (start date / end date). + - **`"rao-result.export.core-cne.relative-positive-margins"`** (optional, default is `"false"`) + - **`"rao-result.export.core-cne.with-loop-flows"`** (optional, default is `"false"`) + - **`"rao-result.export.core-cne.mnec-acceptable-margin-diminution"`** (optional, default is `"0"`) Here is a complete example: @@ -48,13 +46,30 @@ CracCreationContext cracCreationContext = CracCreators.createCrac(...); Crac crac = cracCreationContext.getCrac(); // Run RAO RaoResult raoResult = Rao.find(...).run(...) -// Set CNE header parameters -CneExporterParameters exporterParameters = new CneExporterParameters("DOCUMENT_ID", 1, "DOMAIN_ID", - CneExporterParameters.ProcessType.DAY_AHEAD_CC, "SENDER_ID", - CneExporterParameters.RoleType.REGIONAL_SECURITY_COORDINATOR, "RECEIVER_ID", - CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "2021-10-30T22:00Z/2021-10-31T23:00Z"); +// Set CORE-CNE export properties +Properties properties = new Properties(); +properties.setProperty("rao-result.export.core-cne.document-id", "DOCUMENT_ID"); +properties.setProperty("rao-result.export.core-cne.revision-number", "1"); +properties.setProperty("rao-result.export.core-cne.domain-id", "DOMAIN_ID"); +properties.setProperty("rao-result.export.core-cne.process-type", "A48"); // DAY_AHEAD_CC +properties.setProperty("rao-result.export.core-cne.sender-id", "SENDER_ID"); +properties.setProperty("rao-result.export.core-cne.sender-role", "A44"); // REGIONAL_SECURITY_COORDINATOR +properties.setProperty("rao-result.export.core-cne.receiver-id", "RECEIVER_ID"); +properties.setProperty("rao-result.export.core-cne.receiver-role", "A36"); // CAPACITY_COORDINATOR +properties.setProperty("rao-result.export.core-cne.time-interval", "2021-10-30T22:00Z/2021-10-31T23:00Z"); +switch (raoParameters.getObjectiveFunctionParameters().getType()) { + case MAX_MIN_RELATIVE_MARGIN_IN_AMPERE -> properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "true"); + case MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT -> properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "true"); + case MAX_MIN_MARGIN_IN_AMPERE -> properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "false"); + case MAX_MIN_MARGIN_IN_MEGAWATT -> properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "false"); +} +if (raoParameters.hasExtension(LoopFlowParametersExtension.class)) { + properties.setProperty("rao-result.export.core-cne.with-loop-flows", "true"); +} +if (raoParameters.hasExtension(MnecParametersExtension.class)) { + properties.setProperty("rao-result.export.core-cne.mnec-acceptable-margin-diminution", String.valueOf(raoParameters.getExtension(MnecParametersExtension.class).getAcceptableMarginDecrease())); +} // Export CNE to output stream OutputStream os = ... -new CoreCneExporter().exportCne(crac, cracCreationContext, raoResult, raoParameters, exporterParameters, os); +raoResult.write("CORE-CNE", cracCreationContext, properties, os); ~~~ diff --git a/docs/output-data/rao-result.md b/docs/output-data/rao-result.md index 782c258825..4f4ebc7fdf 100644 --- a/docs/output-data/rao-result.md +++ b/docs/output-data/rao-result.md @@ -32,15 +32,19 @@ A RaoResult JSON file can be imported into a [RaoResultImpl](https://github.com/ Example: ~~~java -raoResult.write("JSON", crac, flowUnits, outputStream); +raoResult.write("JSON", crac, properties, outputStream); ~~~ Where: - **`raoResult`** is the RaoResult object you obtained from the RaoProvider; - **`crac`** is the CRAC object you used in the RAO; -- **`flowUnits`** is the set of units in which the flow measurements should be exported (either `AMPERE` or `MEGAWATT`, or both); +- **`properties`** is a set of specific parameters for the JSON export, currently two are defined: + - `"rao-result.export.json.flows-in-amperes"` (optional, default is `"false"`): whether to export the flow measurements in `AMPERE` + - `"rao-result.export.json.flows-in-megawatts"` (optional, default is `"false"`): whether to export the flow measurements in `MEGAWATT` - **`outputStream`** is the `java.io.OutputStream` you want to write the JSON file into. +> At least one of `"rao-result.export.json.flows-in-amperes"` or `"rao-result.export.json.flows-in-megawatts"` must be true for the export to work properly. + ### Import Example: diff --git a/docs/output-data/swe-cne/introduction.md b/docs/output-data/swe-cne/introduction.md index 723ff27863..683123e928 100644 --- a/docs/output-data/swe-cne/introduction.md +++ b/docs/output-data/swe-cne/introduction.md @@ -1,7 +1,6 @@ The SWE CNE file is the standard RAO output file for the SWE CC process. The [OpenRAO toolbox](https://github.com/powsybl/powsybl-open-rao/tree/main/data/result-exporter/swe-cne-exporter) -allows exporting RAO results in a SWE CNE file using an [internal CRAC](/input-data/crac/json.md), a network, an internal [RAO result](/output-data/rao-result.md) -(containing [angle results](/castor/monitoring/result.md) if the CRAC contains [Angle CNECs](/input-data/crac/json.md#angle-cnecs)), -a [CimCracCreationContext](/input-data/crac/creation-context.md#cim-implementation), and a [RAO parameters](/parameters.md). +allows exporting [RAO results](/output-data/rao-result.md) (containing [angle results](/castor/monitoring/angle-monitoring/result.md) if the CRAC contains [Angle CNECs](/input-data/crac/json.md#angle-cnecs)) +in a SWE CNE file using a [CimCracCreationContext](/input-data/crac/creation-context.md#cim-implementation), and specific properties. ![SWE CNE](/_static/img/swe-cne.png){.forced-white-background} \ No newline at end of file diff --git a/docs/output-data/swe-cne/java-api.md b/docs/output-data/swe-cne/java-api.md index bae709aaae..76ae154af8 100644 --- a/docs/output-data/swe-cne/java-api.md +++ b/docs/output-data/swe-cne/java-api.md @@ -1,70 +1,66 @@ -After completing the RAO, the user can export the SWE CNE file using this method of [SweCneExporter](https://github.com/powsybl/powsybl-open-rao/blob/main/data/result-exporter/swe-cne-exporter/src/main/java/com/powsybl/openrao/data/swecneexporter/SweCneExporter.java): +After completing the RAO, the user can export the [`RaoResult`](/output-data/rao-result.md) object as a SWE CNE file using the `write` method with the `"SWE-CNE""` format: ~~~java -public void exportCne(Crac crac, - CimCracCreationContext cracCreationContext, - RaoResult raoResult, RaoParameters raoParameters, - CneExporterParameters exporterParameters, OutputStream outputStream) +// RaoResult interface +public void write(String format, CracCreationContext cracCreationContext, Properties properties, OutputStream outputStream) ~~~ With: -- **crac**: the [CRAC object](/input-data/crac/json.md) used for the RAO. +- **raoResult**: the [RaoResult](/output-data/rao-result.md) object containing selected remedial actions and flow + results, as well as [angle results](/castor/monitoring/angle-monitoring.md) if the CRAC contains [Angle CNECs](/input-data/crac/json.md#angle-cnecs) - **cracCreationContext**: the [CimCracCreationContext object](/input-data/crac/creation-context.md#cim-implementation) generated during [CRAC creation](/input-data/crac/import.md) from a native [CIM CRAC file](/input-data/crac/cim.md). -- **raoResult**: the [RaoResult](/output-data/rao-result.md) object containing selected remedial actions and flow - results, as well as [angle results](/castor/monitoring.md) if the CRAC contains [Angle CNECs](/input-data/crac/json.md#angle-cnecs) > ⚠️ **NOTE** > The exporter will fail if angle CNECs are present in the CRAC, but the RAO result does not contain angle results. - > See how to compute angle results [here](/castor/monitoring-algorithm.md). -- **raoParameters**: the [RaoParameters](/parameters.md) used in the RAO. -- **exporterParameters**: a specific object that the user should define, containing meta-information that will be written - in the header of the CNE file: - - **documentId**: document ID to be written in "mRID" field - - **revisionNumber**: integer to be written in "revisionNumber" field - - **domainId**: domain ID to be written in "domain.mRID" field (usually an [ENTSO-E EICode](https://www.entsoe.eu/data/energy-identification-codes-eic/)) - - **processType**: the ENTSO-E code of the process type, to be written in "process.processType" field: + > See how to compute angle results [here](/castor/monitoring/angle-monitoring/algorithm.md). +- **properties**: a specific object that te user should define, containing meta-information that will be written + in the header of the CNE file as well as relevant RAO parameters: + - **`"document-id"`**: document ID to be written in "mRID" field + - **`"revision-number"`**: integer to be written in "revisionNumber" field + - **`"domain-id"`**: domain ID to be written in "domain.mRID" field (usually an [ENTSO-E EICode](https://www.entsoe.eu/data/energy-identification-codes-eic/)) + - **`"process-type"`**: the ENTSO-E code of the process type, to be written in "process.processType" field: - ~~**A48**~~: Day-ahead capacity determination, used for CORE region (so don't use it here) - - **Z01**: Day-ahead capacity determination, used for SWE region - - **senderId**: ID of the sender of the CNE document, to be written in "sender_MarketParticipant.mRID" field + - **Z01**: Day-ahead capacity determination, used for SWE region + - **`"sender-id"`**: ID of the sender of the CNE document, to be written in "sender_MarketParticipant.mRID" field (usually an [ENTSO-E EICode](https://www.entsoe.eu/data/energy-identification-codes-eic/)) - - **senderRole**: ENTSO-E code defining the role of the sender of the CNE document, to be written in + - **`"sender-role"`**: ENTSO-E code defining the role of the sender of the CNE document, to be written in "sender_MarketParticipant.marketRole.type" field: - **A04**: system operator - **A36**: capacity coordinator - **A44**: regional security coordinator - - **receiverId**: ID of the receiver of the CNE document, to be written in "receiver_MarketParticipant.mRID" field + - **`"receiver-id"`**: ID of the receiver of the CNE document, to be written in "receiver_MarketParticipant.mRID" field (usually an [ENTSO-E EICode](https://www.entsoe.eu/data/energy-identification-codes-eic/)) - - **receiverRole**: ENTSO-E code defining the role of the receiver of the CNE document, to be written in + - **`"receiver-role"`**: ENTSO-E code defining the role of the receiver of the CNE document, to be written in "receiver_MarketParticipant.marketRole.type" field. Same value options as senderRole. - - **timeInterval**: time interval of document applicability, to be written in "time_Period.timeInterval" field. It should + - **`"time-interval"`**: time interval of document applicability, to be written in "time_Period.timeInterval" field. It should be formatted as follows: "YYYY-MM-DDTHH:MMZ/YYYY-MM-DDTHH:MMZ" (start date / end date). + - **`"objective-function-type"`** (optional, default is `"max-min-relative-margin-in-megawatt"`, should match the input RaoParameters) + - **`"with-loop-flows"`** (optional, default is `"false"`, should match the input RaoParameters) + - **`"mnec-acceptable-margin-diminution"`** (optional, default is `"0"`, should match the input RaoParameters) Here is a complete example: ~~~java -// Fetch input data (network, glsk) and parameters +// Fetch input data (network) and parameters Network network = ... -CimGlskDocument glsk = ... -OffsetDateTime glskOffsetDateTime = ... -LoadFlowParameters loadFlowParameters = ... RaoParameters raoParameters = ... // Create CRAC -CimCracCreationContext cracCreationContext = new CimCracCreator().createCrac(...); +CracCreationContext cracCreationContext = CracCreators.createCrac(...); Crac crac = cracCreationContext.getCrac(); // Run RAO RaoResult raoResult = Rao.find(...).run(...) -// Convert glsk to zonal data -ZonalData<'Scalable> scalableZonalData = glsk.getZonalScalable(network); -// Run angle monitoring and update RAO result -MonitoringInput angleMonitoringInput = new MonitoringInput.MonitoringInputBuilder().withCrac(crac).withNetwork(network).withRaoResult(raoResult).withPhysicalParameter(PhysicalParameter.ANGLE).withScalableZonalData(scalableZonalData).build(); -RaoResult raoResultWithAngleMonitoring = Monitoring.runAngleAndUpdateRaoResult("OpenLoadFlow", loadFlowParameters, 2, angleMonitoringInput); -// Set CNE header parameters -CneExporterParameters exporterParameters = new CneExporterParameters("DOCUMENT_ID", 1, "DOMAIN_ID", - CneExporterParameters.ProcessType.DAY_AHEAD_CC, "SENDER_ID", - CneExporterParameters.RoleType.REGIONAL_SECURITY_COORDINATOR, "RECEIVER_ID", - CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "2021-10-30T22:00Z/2021-10-31T23:00Z"); +// Set SWE-CNE export properties +Properties properties = new Properties(); +properties.setProperty("rao-result.export.swe-cne.document-id", "DOCUMENT_ID"); +properties.setProperty("rao-result.export.swe-cne.revision-number", "1"); +properties.setProperty("rao-result.export.swe-cne.domain-id", "DOMAIN_ID"); +properties.setProperty("rao-result.export.swe-cne.process-type", "Z01"); +properties.setProperty("rao-result.export.swe-cne.sender-id", "SENDER_ID"); +properties.setProperty("rao-result.export.swe-cne.sender-role", "A44"); // REGIONAL_SECURITY_COORDINATOR +properties.setProperty("rao-result.export.swe-cne.receiver-id", "RECEIVER_ID"); +properties.setProperty("rao-result.export.swe-cne.receiver-role", "A36"); // CAPACITY_COORDINATOR +properties.setProperty("rao-result.export.swe-cne.time-interval", "2021-10-30T22:00Z/2021-10-31T23:00Z"); // Export CNE to output stream OutputStream os = ... -new SweCneExporter().exportCne(crac, cracCreationContext, raoResult, raoParameters, exporterParameters, os); +raoResult.write("SWE-CNE", cracCreationContext, properties, os); ~~~ diff --git a/tests/src/test/java/com/powsybl/openrao/tests/steps/CneExportSteps.java b/tests/src/test/java/com/powsybl/openrao/tests/steps/CneExportSteps.java index 6bd79c8ad1..decf7687f7 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/steps/CneExportSteps.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/steps/CneExportSteps.java @@ -31,7 +31,7 @@ public class CneExportSteps { public void iExportCoreCne(String timestamp) throws IOException { cneVersion = CneHelper.CneVersion.CORE; CommonTestData.loadData(timestamp); - exportedCne = CneHelper.exportCoreCne(CommonTestData.getCrac(), CommonTestData.getCracCreationContext(), CommonTestData.getNetwork(), CommonTestData.getRaoResult(), CommonTestData.getRaoParameters()); + exportedCne = CneHelper.exportCoreCne(CommonTestData.getCracCreationContext(), CommonTestData.getRaoResult(), CommonTestData.getRaoParameters()); } @Then("the CORE CNE file is xsd-compliant") @@ -54,7 +54,7 @@ private void exportSweCne(String dataTimestamp) throws IOException { if (dataTimestamp != null) { CommonTestData.loadData(dataTimestamp); } - exportedCne = CneHelper.exportSweCne(CommonTestData.getCrac(), CommonTestData.getCracCreationContext(), CommonTestData.getNetwork(), CommonTestData.getRaoResult(), CommonTestData.getRaoParameters()); + exportedCne = CneHelper.exportSweCne(CommonTestData.getCracCreationContext(), CommonTestData.getRaoResult()); // The following crashes when running cucumber tests from jar-with-dependencies, // maybe because "urn-entsoe-eu-local-extension-types.xsd" is missing in the jar. // We don't really need to fix this (will be moved to gridcapa) diff --git a/tests/src/test/java/com/powsybl/openrao/tests/utils/CneHelper.java b/tests/src/test/java/com/powsybl/openrao/tests/utils/CneHelper.java index af16983ab6..231ce26093 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/utils/CneHelper.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/utils/CneHelper.java @@ -6,17 +6,16 @@ */ package com.powsybl.openrao.tests.utils; -import com.powsybl.iidm.network.Network; import com.powsybl.openrao.commons.OpenRaoException; -import com.powsybl.openrao.data.cneexportercommons.CneExporterParameters; import com.powsybl.openrao.data.corecneexporter.CoreCneExporter; -import com.powsybl.openrao.data.cracapi.Crac; import com.powsybl.openrao.data.cracapi.CracCreationContext; import com.powsybl.openrao.data.cracio.cim.craccreator.CimCracCreationContext; import com.powsybl.openrao.data.cracio.commons.api.stdcreationcontext.UcteCracCreationContext; import com.powsybl.openrao.data.raoresultapi.RaoResult; import com.powsybl.openrao.data.swecneexporter.SweCneExporter; import com.powsybl.openrao.raoapi.parameters.RaoParameters; +import com.powsybl.openrao.raoapi.parameters.extensions.LoopFlowParametersExtension; +import com.powsybl.openrao.raoapi.parameters.extensions.MnecParametersExtension; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -28,6 +27,7 @@ import java.io.InputStream; import java.util.HashSet; import java.util.Map; +import java.util.Properties; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -50,32 +50,66 @@ private CneHelper() { // should not be instantiated } - public static String exportCoreCne(Crac crac, CracCreationContext cracCreationContext, Network network, RaoResult raoResult, RaoParameters raoParameters) { + public static String exportCoreCne(CracCreationContext cracCreationContext, RaoResult raoResult, RaoParameters raoParameters) { if (!(cracCreationContext instanceof UcteCracCreationContext)) { throw new OpenRaoException("CORE CNE export can only handle UcteCracCreationContext"); } - CneExporterParameters exporterParameters = new CneExporterParameters("22XCORESO------S-20211115-F299v1", 1, "10YDOM-REGION-1V", CneExporterParameters.ProcessType.DAY_AHEAD_CC, - "22XCORESO------S", CneExporterParameters.RoleType.REGIONAL_SECURITY_COORDINATOR, "17XTSO-CS------W", CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "2021-10-30T22:00Z/2021-10-31T23:00Z"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - (new CoreCneExporter()).exportCne(crac, (UcteCracCreationContext) cracCreationContext, raoResult, raoParameters, exporterParameters, outputStream); + Properties properties = new Properties(); + fillPropertiesWithCoreCneExporterParameters(properties); + fillPropertiesWithRaoParameters(properties, raoParameters); + raoResult.write("CORE-CNE", cracCreationContext, properties, outputStream); return outputStream.toString(); } - public static String exportSweCne(Crac crac, CracCreationContext cracCreationContext, Network network, RaoResult raoResult, RaoParameters raoParameters) { + private static void fillPropertiesWithCoreCneExporterParameters(Properties properties) { + properties.setProperty("rao-result.export.core-cne.document-id", "22XCORESO------S-20211115-F299v1"); + properties.setProperty("rao-result.export.core-cne.revision-number", "1"); + properties.setProperty("rao-result.export.core-cne.domain-id", "10YDOM-REGION-1V"); + properties.setProperty("rao-result.export.core-cne.process-type", "A48"); + properties.setProperty("rao-result.export.core-cne.sender-id", "22XCORESO------S"); + properties.setProperty("rao-result.export.core-cne.sender-role", "A44"); + properties.setProperty("rao-result.export.core-cne.receiver-id", "17XTSO-CS------W"); + properties.setProperty("rao-result.export.core-cne.receiver-role", "A36"); + properties.setProperty("rao-result.export.core-cne.time-interval", "2021-10-30T22:00Z/2021-10-31T23:00Z"); + } + + private static void fillPropertiesWithRaoParameters(Properties properties, RaoParameters raoParameters) { + switch (raoParameters.getObjectiveFunctionParameters().getType()) { + case MAX_MIN_RELATIVE_MARGIN_IN_AMPERE, MAX_MIN_RELATIVE_MARGIN_IN_MEGAWATT -> properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "true"); + case MAX_MIN_MARGIN_IN_AMPERE, MAX_MIN_MARGIN_IN_MEGAWATT -> properties.setProperty("rao-result.export.core-cne.relative-positive-margins", "false"); + } + if (raoParameters.hasExtension(LoopFlowParametersExtension.class)) { + properties.setProperty("rao-result.export.core-cne.with-loop-flows", "true"); + } + if (raoParameters.hasExtension(MnecParametersExtension.class)) { + properties.setProperty("rao-result.export.core-cne.mnec-acceptable-margin-diminution", String.valueOf(raoParameters.getExtension(MnecParametersExtension.class).getAcceptableMarginDecrease())); + } + } + + public static String exportSweCne(CracCreationContext cracCreationContext, RaoResult raoResult) { if (!(cracCreationContext instanceof CimCracCreationContext)) { throw new OpenRaoException("SWE CNE export can only handle CimCracCreationContext"); } - CneExporterParameters exporterParameters = new CneExporterParameters( - "documentId", 3, "domainId", CneExporterParameters.ProcessType.DAY_AHEAD_CC, - "senderId", CneExporterParameters.RoleType.REGIONAL_SECURITY_COORDINATOR, - "receiverId", CneExporterParameters.RoleType.CAPACITY_COORDINATOR, - "2021-04-02T12:00:00Z/2021-04-02T13:00:00Z"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - (new SweCneExporter()).exportCne(crac, (CimCracCreationContext) cracCreationContext, raoResult, raoParameters, exporterParameters, outputStream); + Properties properties = new Properties(); + fillPropertiesWithSweCneExporterParameters(properties); + raoResult.write("SWE-CNE", cracCreationContext, properties, outputStream); return outputStream.toString(); } + private static void fillPropertiesWithSweCneExporterParameters(Properties properties) { + properties.setProperty("rao-result.export.swe-cne.document-id", "documentId"); + properties.setProperty("rao-result.export.swe-cne.revision-number", "3"); + properties.setProperty("rao-result.export.swe-cne.domain-id", "domainId"); + properties.setProperty("rao-result.export.swe-cne.process-type", "A48"); + properties.setProperty("rao-result.export.swe-cne.sender-id", "senderId"); + properties.setProperty("rao-result.export.swe-cne.sender-role", "A44"); + properties.setProperty("rao-result.export.swe-cne.receiver-id", "receiverId"); + properties.setProperty("rao-result.export.swe-cne.receiver-role", "A36"); + properties.setProperty("rao-result.export.swe-cne.time-interval", "2021-04-02T12:00:00Z/2021-04-02T13:00:00Z"); + } + public static boolean isCoreCneValid(String cneContent) { return CoreCneExporter.validateCNESchema(cneContent); } diff --git a/tests/src/test/java/com/powsybl/openrao/tests/utils/RaoUtils.java b/tests/src/test/java/com/powsybl/openrao/tests/utils/RaoUtils.java index 7aa066aedc..f4e5b6370b 100644 --- a/tests/src/test/java/com/powsybl/openrao/tests/utils/RaoUtils.java +++ b/tests/src/test/java/com/powsybl/openrao/tests/utils/RaoUtils.java @@ -27,7 +27,7 @@ import java.io.IOException; import java.util.Objects; import java.util.Optional; -import java.util.Set; +import java.util.Properties; import static java.lang.String.format; @@ -116,7 +116,10 @@ private static RaoResult roundTripOnRaoResult(RaoResult raoResult, Crac crac) th // export RaoResult ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - raoResult.write("JSON", crac, Set.of(Unit.AMPERE, Unit.MEGAWATT), outputStream); + Properties properties = new Properties(); + properties.setProperty("rao-result.export.json.flows-in-amperes", "true"); + properties.setProperty("rao-result.export.json.flows-in-megawatts", "true"); + raoResult.write("JSON", crac, properties, outputStream); // import RaoResult ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());