From 97412226931429d6af5ff26af3e037b3584c2714 Mon Sep 17 00:00:00 2001 From: Geoffroy Jamgotchian Date: Mon, 17 Jun 2024 17:10:29 +0200 Subject: [PATCH 01/57] Add extensions getter to Importer Signed-off-by: Geoffroy Jamgotchian --- .../java/com/powsybl/cgmes/conversion/CgmesImport.java | 5 +++++ .../com/powsybl/ieeecdf/converter/IeeeCdfImporter.java | 5 +++++ .../src/main/java/com/powsybl/iidm/network/Importer.java | 4 ++++ .../test/java/com/powsybl/iidm/network/TestImporter.java | 6 ++++++ .../main/java/com/powsybl/iidm/serde/BinaryImporter.java | 6 ++++++ .../src/main/java/com/powsybl/iidm/serde/JsonImporter.java | 7 +++++++ .../src/main/java/com/powsybl/iidm/serde/XMLImporter.java | 7 +++++++ .../com/powsybl/matpower/converter/MatpowerImporter.java | 5 +++++ .../powerfactory/converter/PowerFactoryImporter.java | 5 +++++ .../main/java/com/powsybl/psse/converter/PsseImporter.java | 5 +++++ .../main/java/com/powsybl/ucte/converter/UcteImporter.java | 5 +++++ 11 files changed, 60 insertions(+) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java index 4f9fd5a7222..8062bfd15f8 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java @@ -146,6 +146,11 @@ public String getFormat() { return FORMAT; } + @Override + public List getSupportedExtensions() { + return List.of(".xml"); + } + @Override public Network importData(ReadOnlyDataSource ds, NetworkFactory networkFactory, Properties p, ReportNode reportNode) { Objects.requireNonNull(ds); diff --git a/ieee-cdf/ieee-cdf-converter/src/main/java/com/powsybl/ieeecdf/converter/IeeeCdfImporter.java b/ieee-cdf/ieee-cdf-converter/src/main/java/com/powsybl/ieeecdf/converter/IeeeCdfImporter.java index 0d410b6082e..6671dd47de3 100644 --- a/ieee-cdf/ieee-cdf-converter/src/main/java/com/powsybl/ieeecdf/converter/IeeeCdfImporter.java +++ b/ieee-cdf/ieee-cdf-converter/src/main/java/com/powsybl/ieeecdf/converter/IeeeCdfImporter.java @@ -67,6 +67,11 @@ public String getFormat() { return FORMAT; } + @Override + public List getSupportedExtensions() { + return List.of(EXT); + } + @Override public List getParameters() { return Collections.singletonList(IGNORE_BASE_VOLTAGE_PARAMETER); diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java index a481fa44606..557dfa601e4 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java @@ -276,6 +276,10 @@ static Importer find(ReadOnlyDataSource dataSource) { */ String getFormat(); + default List getSupportedExtensions() { + return Collections.emptyList(); + } + /** * Get a description of import parameters * @return diff --git a/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/TestImporter.java b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/TestImporter.java index 1e36b7cc62c..a81dd8f84ab 100644 --- a/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/TestImporter.java +++ b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/TestImporter.java @@ -13,6 +13,7 @@ import java.io.IOException; import java.io.UncheckedIOException; +import java.util.List; import java.util.Properties; /** @@ -26,6 +27,11 @@ public String getFormat() { return "TST"; } + @Override + public List getSupportedExtensions() { + return List.of("tst"); + } + @Override public String getComment() { return "Dummy importer to test Importers"; diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java index 27460f369d1..5c39738d44a 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java @@ -15,6 +15,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import java.util.List; import java.util.Properties; import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; @@ -39,6 +40,11 @@ public String getFormat() { return "BIIDM"; } + @Override + public List getSupportedExtensions() { + return Arrays.asList(EXTENSIONS); + } + @Override public String getComment() { return "IIDM binary v " + CURRENT_IIDM_VERSION.toString(".") + " importer"; diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java index 92eda22af8a..f98db04cd28 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java @@ -18,6 +18,8 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; +import java.util.List; import java.util.Properties; import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; @@ -40,6 +42,11 @@ public String getFormat() { return "JIIDM"; } + @Override + public List getSupportedExtensions() { + return Arrays.asList(EXTENSIONS); + } + @Override public String getComment() { return "IIDM JSON v " + CURRENT_IIDM_VERSION.toString(".") + " importer"; diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java index 0d50956f6f0..09fde66219f 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java @@ -22,6 +22,8 @@ import javax.xml.stream.XMLStreamReader; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; +import java.util.List; import java.util.function.Supplier; import java.util.stream.Stream; @@ -56,6 +58,11 @@ public String getFormat() { return "XIIDM"; } + @Override + public List getSupportedExtensions() { + return Arrays.asList(EXTENSIONS); + } + @Override public String getComment() { return "IIDM XML v " + CURRENT_IIDM_VERSION.toString(".") + " importer"; diff --git a/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerImporter.java b/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerImporter.java index 40ca6a38e3a..237422896e2 100644 --- a/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerImporter.java +++ b/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerImporter.java @@ -532,6 +532,11 @@ public String getFormat() { return MatpowerConstants.FORMAT; } + @Override + public List getSupportedExtensions() { + return Collections.singletonList(MatpowerConstants.EXT); + } + @Override public void copy(ReadOnlyDataSource fromDataSource, DataSource toDataSource) { Objects.requireNonNull(fromDataSource); diff --git a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java index c3aaf1b2bf7..672f7747c9b 100644 --- a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java +++ b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java @@ -51,6 +51,11 @@ public String getFormat() { return FORMAT; } + @Override + public List getSupportedExtensions() { + return PowerFactoryDataLoader.find(StudyCase.class).stream().map(PowerFactoryDataLoader::getExtension).toList(); + } + @Override public List getParameters() { return Collections.emptyList(); diff --git a/psse/psse-converter/src/main/java/com/powsybl/psse/converter/PsseImporter.java b/psse/psse-converter/src/main/java/com/powsybl/psse/converter/PsseImporter.java index ef1fecaeec0..bb611714df8 100644 --- a/psse/psse-converter/src/main/java/com/powsybl/psse/converter/PsseImporter.java +++ b/psse/psse-converter/src/main/java/com/powsybl/psse/converter/PsseImporter.java @@ -61,6 +61,11 @@ public String getFormat() { return FORMAT; } + @Override + public List getSupportedExtensions() { + return Arrays.asList(EXTENSIONS); + } + @Override public List getParameters() { return Collections.singletonList(IGNORE_BASE_VOLTAGE_PARAMETER); diff --git a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java index 2f9db7339ca..fecef860095 100644 --- a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java +++ b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java @@ -939,6 +939,11 @@ public String getFormat() { return "UCTE"; } + @Override + public List getSupportedExtensions() { + return Arrays.asList(EXTENSIONS); + } + @Override public String getComment() { return "UCTE-DEF"; From 344d97b51b209f34e698bd745334c49ff645994e Mon Sep 17 00:00:00 2001 From: Geoffroy Jamgotchian Date: Wed, 19 Jun 2024 08:42:19 +0200 Subject: [PATCH 02/57] Add unit tests Signed-off-by: Geoffroy Jamgotchian --- .../powsybl/cgmes/conversion/CgmesImport.java | 2 +- .../test/CgmesImporterMetaInfoTest.java | 35 +++++++++++++++++++ .../converter/IeeeCdfImporterTest.java | 2 ++ .../iidm/serde/BinaryImporterTest.java | 30 ++++++++++++++++ .../powsybl/iidm/serde/JsonImporterTest.java | 30 ++++++++++++++++ .../powsybl/iidm/serde/XMLImporterTest.java | 14 +++----- .../converter/MatpowerImporterTest.java | 2 ++ .../converter/PowerFactoryImporterTest.java | 2 ++ .../psse/converter/PsseImporterTest.java | 2 ++ .../ucte/converter/UcteImporterTest.java | 15 ++++++++ 10 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/CgmesImporterMetaInfoTest.java create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/BinaryImporterTest.java create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/JsonImporterTest.java diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java index 8062bfd15f8..d2c5d68985e 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java @@ -148,7 +148,7 @@ public String getFormat() { @Override public List getSupportedExtensions() { - return List.of(".xml"); + return List.of("xml"); } @Override diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/CgmesImporterMetaInfoTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/CgmesImporterMetaInfoTest.java new file mode 100644 index 00000000000..afff6a46858 --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/CgmesImporterMetaInfoTest.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.cgmes.conversion.test; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.cgmes.conversion.CgmesImport; +import com.powsybl.commons.config.InMemoryPlatformConfig; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Geoffroy Jamgotchian {@literal } + */ +class CgmesImporterMetaInfoTest { + + @Test + void test() throws IOException { + try (var fs = Jimfs.newFileSystem(Configuration.unix())) { + var importer = new CgmesImport(new InMemoryPlatformConfig(fs)); + assertEquals("CGMES", importer.getFormat()); + assertEquals("ENTSO-E CGMES version 2.4.15", importer.getComment()); + assertEquals(List.of("xml"), importer.getSupportedExtensions()); + } + } +} diff --git a/ieee-cdf/ieee-cdf-converter/src/test/java/com/powsybl/ieeecdf/converter/IeeeCdfImporterTest.java b/ieee-cdf/ieee-cdf-converter/src/test/java/com/powsybl/ieeecdf/converter/IeeeCdfImporterTest.java index 61212ebec9e..53af75e5e3c 100644 --- a/ieee-cdf/ieee-cdf-converter/src/test/java/com/powsybl/ieeecdf/converter/IeeeCdfImporterTest.java +++ b/ieee-cdf/ieee-cdf-converter/src/test/java/com/powsybl/ieeecdf/converter/IeeeCdfImporterTest.java @@ -25,6 +25,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import static com.powsybl.commons.test.ComparisonUtils.assertXmlEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -40,6 +41,7 @@ void baseTest() { Importer importer = new IeeeCdfImporter(); assertEquals("IEEE-CDF", importer.getFormat()); assertEquals("IEEE Common Data Format to IIDM converter", importer.getComment()); + assertEquals(List.of("txt"), importer.getSupportedExtensions()); assertEquals(1, importer.getParameters().size()); assertEquals("ignore-base-voltage", importer.getParameters().get(0).getName()); } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/BinaryImporterTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/BinaryImporterTest.java new file mode 100644 index 00000000000..d8ad2cac407 --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/BinaryImporterTest.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Geoffroy Jamgotchian {@literal } + */ +class BinaryImporterTest { + + @Test + void testMetaInfos() { + var importer = new BinaryImporter(); + assertEquals("BIIDM", importer.getFormat()); + assertEquals("IIDM binary v " + CURRENT_IIDM_VERSION.toString(".") + " importer", importer.getComment()); + assertEquals(List.of("biidm", "bin", "iidm.bin"), importer.getSupportedExtensions()); + assertEquals(5, importer.getParameters().size()); + } +} diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/JsonImporterTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/JsonImporterTest.java new file mode 100644 index 00000000000..9888d723d7e --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/JsonImporterTest.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Geoffroy Jamgotchian {@literal } + */ +class JsonImporterTest { + + @Test + void testMetaInfos() { + var importer = new JsonImporter(); + assertEquals("JIIDM", importer.getFormat()); + assertEquals("IIDM JSON v " + CURRENT_IIDM_VERSION.toString(".") + " importer", importer.getComment()); + assertEquals(List.of("jiidm", "json", "iidm.json"), importer.getSupportedExtensions()); + assertEquals(5, importer.getParameters().size()); + } +} diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java index 1de9946b34b..34b41271586 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java @@ -22,6 +22,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Arrays; +import java.util.List; import java.util.Properties; import static com.powsybl.commons.test.TestUtil.normalizeLineSeparator; @@ -132,22 +133,15 @@ void backwardCompatibilityTest() throws IOException { } @Test - void getFormat() { + void testMetaInfos() { assertEquals("XIIDM", importer.getFormat()); - } - - @Test - void getParameters() { + assertEquals("IIDM XML v " + CURRENT_IIDM_VERSION.toString(".") + " importer", importer.getComment()); + assertEquals(List.of("xiidm", "iidm", "xml", "iidm.xml"), importer.getSupportedExtensions()); assertEquals(5, importer.getParameters().size()); assertEquals("iidm.import.xml.throw-exception-if-extension-not-found", importer.getParameters().get(0).getName()); assertEquals(Arrays.asList("iidm.import.xml.throw-exception-if-extension-not-found", "throwExceptionIfExtensionNotFound"), importer.getParameters().get(0).getNames()); } - @Test - void getComment() { - assertEquals("IIDM XML v " + CURRENT_IIDM_VERSION.toString(".") + " importer", importer.getComment()); - } - @Test void exists() { assertTrue(importer.exists(new FileDataSource(fileSystem.getPath("/"), "test0"))); diff --git a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerImporterTest.java b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerImporterTest.java index 8ce416a8f4a..84b593c55bb 100644 --- a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerImporterTest.java +++ b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerImporterTest.java @@ -33,6 +33,7 @@ import java.time.Month; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.List; import java.util.Properties; import static com.powsybl.commons.test.ComparisonUtils.assertXmlEquals; @@ -50,6 +51,7 @@ void baseTest() { Importer importer = new MatpowerImporter(); assertEquals("MATPOWER", importer.getFormat()); assertEquals("MATPOWER Format to IIDM converter", importer.getComment()); + assertEquals(List.of("mat"), importer.getSupportedExtensions()); assertEquals(1, importer.getParameters().size()); assertEquals("matpower.import.ignore-base-voltage", importer.getParameters().get(0).getName()); } diff --git a/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java b/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java index 51af0b78f8c..edab8f77716 100644 --- a/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java +++ b/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java @@ -25,6 +25,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.ZonedDateTime; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -44,6 +45,7 @@ void testBase() { assertEquals("POWER-FACTORY", importer.getFormat()); assertTrue(importer.getParameters().isEmpty()); assertEquals("PowerFactory to IIDM converter", importer.getComment()); + assertEquals(List.of("json", "dgs"), importer.getSupportedExtensions()); } @Test diff --git a/psse/psse-converter/src/test/java/com/powsybl/psse/converter/PsseImporterTest.java b/psse/psse-converter/src/test/java/com/powsybl/psse/converter/PsseImporterTest.java index cd8321a2b5e..d20d62d3024 100644 --- a/psse/psse-converter/src/test/java/com/powsybl/psse/converter/PsseImporterTest.java +++ b/psse/psse-converter/src/test/java/com/powsybl/psse/converter/PsseImporterTest.java @@ -28,6 +28,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.Properties; import static com.powsybl.commons.test.ComparisonUtils.assertXmlEquals; @@ -44,6 +45,7 @@ void baseTest() { Importer importer = new PsseImporter(); assertEquals("PSS/E", importer.getFormat()); assertEquals("PSS/E Format to IIDM converter", importer.getComment()); + assertEquals(List.of("raw", "RAW", "rawx", "RAWX"), importer.getSupportedExtensions()); assertEquals(1, importer.getParameters().size()); assertEquals("psse.import.ignore-base-voltage", importer.getParameters().get(0).getName()); } diff --git a/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteImporterTest.java b/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteImporterTest.java index 78a1c315927..2f739d34710 100644 --- a/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteImporterTest.java +++ b/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteImporterTest.java @@ -7,7 +7,10 @@ */ package com.powsybl.ucte.converter; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.config.InMemoryPlatformConfig; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.datasource.ResourceDataSource; import com.powsybl.commons.datasource.ResourceSet; @@ -18,6 +21,8 @@ import com.powsybl.ucte.converter.util.UcteConstants; import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.util.List; import java.util.Properties; import static org.junit.jupiter.api.Assertions.*; @@ -285,4 +290,14 @@ void lineBetweenTwoXnodesTest() { PowsyblException e = assertThrows(PowsyblException.class, () -> importer.importData(dataSource, networkFactory, null)); assertEquals("Line between 2 X-nodes: 'XXNODE11' and 'XXNODE12'", e.getMessage()); } + + @Test + void testMetaInfos() throws IOException { + try (var fs = Jimfs.newFileSystem(Configuration.unix())) { + var importer = new UcteImporter(new InMemoryPlatformConfig(fs)); + assertEquals("UCTE", importer.getFormat()); + assertEquals("UCTE-DEF", importer.getComment()); + assertEquals(List.of("uct", "UCT"), importer.getSupportedExtensions()); + } + } } From 46b2bdf0ff8b43783ba08c0f240bbf58d10dd4b1 Mon Sep 17 00:00:00 2001 From: Geoffroy Jamgotchian Date: Wed, 19 Jun 2024 09:21:03 +0200 Subject: [PATCH 03/57] Fix code duplication Signed-off-by: Geoffroy Jamgotchian --- .../powsybl/iidm/serde/AbstractTreeDataImporter.java | 10 ++++++---- .../java/com/powsybl/iidm/serde/BinaryImporter.java | 6 ------ .../main/java/com/powsybl/iidm/serde/JsonImporter.java | 7 ------- .../main/java/com/powsybl/iidm/serde/XMLImporter.java | 7 ------- 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java index 621c93a5a43..e4eaf0241ba 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java @@ -31,10 +31,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Properties; +import java.util.*; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -106,6 +103,11 @@ private String findExtension(ReadOnlyDataSource dataSource) throws IOException { protected abstract String[] getExtensions(); + @Override + public List getSupportedExtensions() { + return Arrays.asList(getExtensions()); + } + @Override public boolean exists(ReadOnlyDataSource dataSource) { try { diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java index 5c39738d44a..27460f369d1 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java @@ -15,7 +15,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; -import java.util.List; import java.util.Properties; import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; @@ -40,11 +39,6 @@ public String getFormat() { return "BIIDM"; } - @Override - public List getSupportedExtensions() { - return Arrays.asList(EXTENSIONS); - } - @Override public String getComment() { return "IIDM binary v " + CURRENT_IIDM_VERSION.toString(".") + " importer"; diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java index f98db04cd28..92eda22af8a 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java @@ -18,8 +18,6 @@ import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; -import java.util.List; import java.util.Properties; import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; @@ -42,11 +40,6 @@ public String getFormat() { return "JIIDM"; } - @Override - public List getSupportedExtensions() { - return Arrays.asList(EXTENSIONS); - } - @Override public String getComment() { return "IIDM JSON v " + CURRENT_IIDM_VERSION.toString(".") + " importer"; diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java index 09fde66219f..0d50956f6f0 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java @@ -22,8 +22,6 @@ import javax.xml.stream.XMLStreamReader; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; -import java.util.List; import java.util.function.Supplier; import java.util.stream.Stream; @@ -58,11 +56,6 @@ public String getFormat() { return "XIIDM"; } - @Override - public List getSupportedExtensions() { - return Arrays.asList(EXTENSIONS); - } - @Override public String getComment() { return "IIDM XML v " + CURRENT_IIDM_VERSION.toString(".") + " importer"; From 82e413b8a58c4f5b61907c2796c75f3ff052aa24 Mon Sep 17 00:00:00 2001 From: m-guibert <117466293+m-guibert@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:08:56 +0200 Subject: [PATCH 04/57] Generic Area model in IIDM (#2955) * Generic Area model in IIDM Signed-off-by: mguibert Co-authored-by: vmouradian Co-authored-by: jeandemanged --- docs/grid_model/network_subnetwork.md | 50 ++ .../java/com/powsybl/iidm/network/Area.java | 191 +++++++ .../com/powsybl/iidm/network/AreaAdder.java | 65 +++ .../powsybl/iidm/network/AreaBoundary.java | 96 ++++ .../iidm/network/AreaBoundaryAdder.java | 49 ++ .../iidm/network/IdentifiableType.java | 1 + .../com/powsybl/iidm/network/Network.java | 42 ++ .../powsybl/iidm/network/VoltageLevel.java | 50 +- .../network/impl/AbstractVoltageLevel.java | 75 ++- .../iidm/network/impl/AreaAdderImpl.java | 94 ++++ .../network/impl/AreaBoundaryAdderImpl.java | 72 +++ .../iidm/network/impl/AreaBoundaryImpl.java | 71 +++ .../powsybl/iidm/network/impl/AreaImpl.java | 285 ++++++++++ .../iidm/network/impl/NetworkImpl.java | 40 ++ .../iidm/network/impl/SubnetworkImpl.java | 49 +- .../iidm/network/impl/tck/AreaTest.java | 16 + .../powsybl/iidm/serde/AreaBoundarySerDe.java | 56 ++ .../com/powsybl/iidm/serde/AreaSerDe.java | 76 +++ .../powsybl/iidm/serde/BoundaryRefSerDe.java | 41 ++ .../com/powsybl/iidm/serde/NetworkSerDe.java | 23 + .../iidm/serde/VoltageLevelRefSerDe.java | 40 ++ .../src/main/resources/xsd/iidm_V1_13.xsd | 24 + .../resources/xsd/iidm_equipment_V1_13.xsd | 24 + .../com/powsybl/iidm/serde/AreaSerDeTest.java | 69 +++ .../test/resources/V1_11/areaRoundTripRef.xml | 24 + .../test/resources/V1_12/areaRoundTripRef.xml | 24 + .../test/resources/V1_13/areaRoundTripRef.xml | 38 ++ .../iidm/network/tck/AbstractAreaTest.java | 489 ++++++++++++++++++ .../network/tck/AbstractMergeNetworkTest.java | 67 +++ .../tck/AbstractSubnetworksCreationTest.java | 36 ++ .../AbstractSubnetworksExplorationTest.java | 30 ++ .../test/EurostagTutorialExample1Factory.java | 54 +- 32 files changed, 2347 insertions(+), 14 deletions(-) create mode 100644 iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Area.java create mode 100644 iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaAdder.java create mode 100644 iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaBoundary.java create mode 100644 iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaBoundaryAdder.java create mode 100644 iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaAdderImpl.java create mode 100644 iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaBoundaryAdderImpl.java create mode 100644 iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaBoundaryImpl.java create mode 100644 iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java create mode 100644 iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/tck/AreaTest.java create mode 100644 iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaBoundarySerDe.java create mode 100644 iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaSerDe.java create mode 100644 iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BoundaryRefSerDe.java create mode 100644 iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelRefSerDe.java create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AreaSerDeTest.java create mode 100644 iidm/iidm-serde/src/test/resources/V1_11/areaRoundTripRef.xml create mode 100644 iidm/iidm-serde/src/test/resources/V1_12/areaRoundTripRef.xml create mode 100644 iidm/iidm-serde/src/test/resources/V1_13/areaRoundTripRef.xml create mode 100644 iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java diff --git a/docs/grid_model/network_subnetwork.md b/docs/grid_model/network_subnetwork.md index 0b16d86a042..f8d3fcbd2af 100644 --- a/docs/grid_model/network_subnetwork.md +++ b/docs/grid_model/network_subnetwork.md @@ -99,6 +99,56 @@ When defining the model, the user has to specify how the different equipment con - [Identifiable Short-Circuit](extensions.md#identifiable-short-circuit) - [Slack Terminal](extensions.md#slack-terminal) +(area)= +## Area +[![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Area.html) + +An Area is a geographical zone of a given type. + +An Area is composed of a collection of [voltage levels](#voltage-level), and a collection of area boundaries. +Area boundaries can be terminals of equipments or `Boundary` objects from [dangling lines](#dangling-line). + +The area type is used to distinguish between various area concepts of different granularity. For instance: control areas, bidding zones, countries... + +A [voltage level](#voltage-level) can belong to several areas, as long as all areas are of different type. + +The area boundaries define how interchange are to be calculated for the area. +Area interchange is calculated by summing the active power flows across the area boundaries and can be obtained for AC part only (considering only AC boundaries), +for DC part only (considering only DC boundaries) and in total (AC+DC). +Note that if the Area has no boundary explicitly defined, the interchange is considered 0 MW. + +For area types that are meant to be used for area interchange control, e.g. in Load Flow simulations, the interchange target of the area can be specified as an input for the simulation. +Note that this target interchange is for only the AC part of the interchange. + +All area interchange values use the load sign convention: positive values indicate that the area is importing, negative values that the area is exporting. + +**Characteristics of an Area** + +| Attribute | Unit | Description | +|-----------------------|------|----------------------------------------------------------------| +| $AreaType$ | | To specify the type of Area (eg. ControlArea, BiddingZone ...) | +| $AcInterchangeTarget$ | MW | Target AC active power interchange | +| $VoltageLevels$ | | List of voltage levels of the area | +| $AreaBoundaries$ | | List of area boundaries of the area | + +**Characteristics of an AreaBoundary** + +An area boundary is modeled by an `AreaBoundary` instance. +It is composed of either DanglingLine Boundary or a Terminal, and boolean telling if the area boundary +is to be considered as AC or DC. + +The `Ac` flag is informative and is present to support the use case where boundaries are defined on AC components even though +the boundary is related to an HVDC link. An example for this is a DanglingLine (which is an AC equipment) that may actually represent +an HVDC interconnection that is not explicitly described in the network model. This information is used when computing area interchanges, +which are then separated for AC and DC parts. + +| Attribute | Unit | Description | +|------------|------|-----------------------------------------------------------------------------| +| $Area$ | | The area of this boundary | +| $Boundary$ | | Boundary of a DanglingLine (mutually exclusive with the Terminal attribute) | +| $Terminal$ | | Terminal of an equipment (mutually exclusive with the Boundary attribute) | +| $Ac$ | | True if AreaBoundary is to be considered AC, false otherwise | + (generator)= ## Generator [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Generator.html) diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Area.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Area.java new file mode 100644 index 00000000000..b08d9e6e363 --- /dev/null +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Area.java @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network; + +import java.util.OptionalDouble; +import java.util.stream.Stream; + +/** + * An Area is a geographical zone of a given type. + *

An Area is composed of a collection of voltage levels, and a collection of area boundaries. + *

The area type is used to distinguish between various area concepts of different granularity. + * For instance: control areas, bidding zones, countries... + *

To create an Area, see {@link AreaAdder} + * + *

+ * Characteristics + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AttributeTypeUnitRequiredDefault valueDescription
IdString - yes - Unique identifier of the Area
NameString-no - Human-readable name of the Area
AreaTypeString-yes - The type of Area. For instance: "ControlArea", "BiddingZone", "Country"...
AcInterchangeTargetDouble-no - The optional target AC Interchange of this area in MW, using load sign convention (negative is export, positive is import)
+ * + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + * @see VoltageLevel + * @see AreaBoundary + * @see AreaAdder + */ +public interface Area extends Identifiable { + + /** + * Get the type of this area + */ + String getAreaType(); + + /** + * Get all area voltage levels. + */ + Iterable getVoltageLevels(); + + /** + * Get all area voltage levels. + */ + Stream getVoltageLevelStream(); + + /** + * Adds a voltage level to the area. + * @param voltageLevel voltage level to be added + */ + Area addVoltageLevel(VoltageLevel voltageLevel); + + /** + * Removes the provided VoltageLevel from the area. The VoltageLevel is not removed from the network, + * the VoltageLevel is not part of the Area anymore. + * @param voltageLevel the VoltageLevel to be removed from the Area. + */ + Area removeVoltageLevel(VoltageLevel voltageLevel); + + /** + * @return adder to create a new area boundary + */ + AreaBoundaryAdder newAreaBoundary(); + + /** + * If exists, remove the area boundary associated with the provided terminal. + * The Terminal and its Connectable are not removed from the network, but are not part of the Area anymore. + * @param terminal terminal + */ + Area removeAreaBoundary(Terminal terminal); + + /** + * If exists, remove the area boundary associated with the provided DanglingLine's Boundary. + * The DanglingLine and its Boundary are not removed from the network, but are not part of the Area anymore. + * @param boundary DanglingLine's boundary + */ + Area removeAreaBoundary(Boundary boundary); + + /** + * If found, returns the area boundary associated with the provided DanglingLine's Boundary. + * Otherwise, null is returned. + * @param boundary DanglingLine's boundary + */ + AreaBoundary getAreaBoundary(Boundary boundary); + + /** + * If found, returns the area boundary associated with the provided Terminal. + * Otherwise, null is returned. + * @param terminal terminal + */ + AreaBoundary getAreaBoundary(Terminal terminal); + + /** + * Get all area boundaries. + */ + Iterable getAreaBoundaries(); + + /** + * Get all area boundaries. + */ + Stream getAreaBoundaryStream(); + + @Override + default IdentifiableType getType() { + return IdentifiableType.AREA; + } + + /** + * Get the optional target AC Interchange of this area in MW, in load sign convention (negative is export, positive is import). + *

Depends on the working variant.

+ * + * @return the AC Interchange target (MW) + */ + OptionalDouble getAcInterchangeTarget(); + + /** + * Set the target AC Interchange of this area in MW, in load sign convention (negative is export, positive is import). + * Providing Double.NaN removes the target. + *

Depends on the working variant.

+ * @param acInterchangeTarget new AC interchange target (MW) + */ + Area setAcInterchangeTarget(double acInterchangeTarget); + + /** + * Get the current AC Interchange of this area in MW, in load sign convention (negative is export, positive is import) + * @return the AC position (MW, 0 MW if no boundary) + */ + double getAcInterchange(); + + /** + * Get the current DC Interchange of this area in MW, in load sign convention (negative is export, positive is import) + * @return the DC position (MW, 0 MW if no boundary) + */ + double getDcInterchange(); + + /** + * Get the current total (AC+DC) Interchange of this area in MW, in load sign convention (negative is export, positive is import) + * @return the total position (MW, 0 MW if no boundary) + */ + double getTotalInterchange(); + + void remove(); + +} diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaAdder.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaAdder.java new file mode 100644 index 00000000000..316355c72fa --- /dev/null +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaAdder.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network; + +/** + * To create an Area, from a Network instance call + * the {@link Network#newArea()} method to get an Area builder instance. + *

+ * Example: + *

+ *    Network n = ...
+ *    Area a = n.newArea()
+ *            .setId("FR")
+ *            ...
+ *        .add();
+ *
+ * + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + * @see Area + * @see Network + */ +public interface AreaAdder extends IdentifiableAdder { + + /** + * Set the Area type + */ + AreaAdder setAreaType(String areaType); + + /** + * Set the target AC Interchange of this area in MW, in load sign convention (negative is export, positive is import). + */ + AreaAdder setAcInterchangeTarget(double acInterchangeTarget); + + /** + * add a VoltageLevel to the Area + */ + AreaAdder addVoltageLevel(VoltageLevel voltageLevel); + + /** + * add a Terminal as an area boundary + */ + AreaAdder addAreaBoundary(Terminal terminal, boolean ac); + + /** + * add a DanglingLine boundary as an area boundary + */ + AreaAdder addAreaBoundary(Boundary boundary, boolean ac); + + /** + * Build the Area object. + *

These are the checks that are performed before creating the object :

+ *
    + *
  • areaType is not null;
  • + *
+ * @return {@link Area} + */ + @Override + Area add(); +} diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaBoundary.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaBoundary.java new file mode 100644 index 00000000000..693f8244cc4 --- /dev/null +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaBoundary.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network; + +import java.util.Optional; + +/** + * An AreaBoundary is a boundary of an Area. + *

It is composed of a terminal or a DanglingLine Boundary, associated with a boolean telling if it is an AC or DC boundary. + *

To create and add an AreaBoundary, see {@link AreaAdder#addAreaBoundary(Terminal, boolean)} or {@link AreaAdder#addAreaBoundary(Boundary, boolean)} + * + *

+ * Characteristics + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * +* + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AttributeTypeUnitRequiredDefault valueDescription
TerminalTerminal - no - Terminal at the border of an Area/td> + *
BoundaryBoundary - no - Boundary at the border of an Area
ACboolean-yes - True if this corresponds to an AC AreaBoundary, false otherwise
+ * + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + * @see Area + * @see Boundary + */ +public interface AreaBoundary { + + /** + * @return the Area this AreaBoundary belongs to + */ + Area getArea(); + + /** + * @return If the boundary is defined by a Terminal, the Terminal of this AreaBoundary + */ + Optional getTerminal(); + + /** + * @return If the boundary is defined by a DanglingLine's Boundary, the DanglingLine's Boundary of this AreaBoundary + */ + Optional getBoundary(); + + /** + * @return true is the boundary was defined as AC + */ + boolean isAc(); + + /** + * @return active power through area boundary + */ + double getP(); + + /** + * @return reactive power through area boundary + */ + double getQ(); + +} diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaBoundaryAdder.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaBoundaryAdder.java new file mode 100644 index 00000000000..e9bb3efbcc8 --- /dev/null +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaBoundaryAdder.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network; + +/** + * To create an AreaBoundary, from a Area instance call + * the {@link Area#newAreaBoundary()} method to get an AreaBoundary builder instance. + *

+ * Example: + *

+ *    Area a = ...
+ *    AreaBoundary ab = a.newAreaBoundary()
+ *            .setAc(true)
+ *            ...
+ *        .add();
+ *
+ * + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ + +public interface AreaBoundaryAdder { + + /** + * Set a DanglingLine's Boundary to be used as the AreaBoundary + */ + AreaBoundaryAdder setBoundary(Boundary boundary); + + /** + * Set a Terminal to be used as the AreaBoundary + */ + AreaBoundaryAdder setTerminal(Terminal terminal); + + /** + * Set whether the AreaBoundary is to be considered as AC or DC + */ + AreaBoundaryAdder setAc(boolean ac); + + /** + * Build the AreaBoundary and add it to the Area. + * @return {@link Area} + */ + Area add(); +} diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/IdentifiableType.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/IdentifiableType.java index 16565ebe020..371b0ae061d 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/IdentifiableType.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/IdentifiableType.java @@ -15,6 +15,7 @@ public enum IdentifiableType { NETWORK, SUBSTATION, VOLTAGE_LEVEL, + AREA, HVDC_LINE, BUS, SWITCH, diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Network.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Network.java index 88fa515fa09..8bae3870328 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Network.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Network.java @@ -618,6 +618,48 @@ static PrettyNetworkFactory with(String name) { */ int getCountryCount(); + /** + * Get all areaTypes. + */ + Iterable getAreaTypes(); + + /** + * Get all areaTypes. + */ + Stream getAreaTypeStream(); + + /** + * Get the areaType count. + */ + int getAreaTypeCount(); + + /** + * Get a builder to create a new area. + * @return a builder to create a new area + */ + AreaAdder newArea(); + + /** + * @return all existing areas, which may include several areas for each area type + */ + Iterable getAreas(); + + /** + * @return all existing areas, which may include several areas for each area type + */ + Stream getAreaStream(); + + /** + * Get an area. + * @param id the id or an alias of the area + */ + Area getArea(String id); + + /** + * Get the area count. + */ + int getAreaCount(); + /** * Get a builder to create a new substation. * @return a builder to create a new substation diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/VoltageLevel.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/VoltageLevel.java index 279cb234727..d70e735f2df 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/VoltageLevel.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/VoltageLevel.java @@ -15,10 +15,7 @@ import java.io.PrintStream; import java.io.Writer; import java.nio.file.Path; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.Random; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -148,6 +145,14 @@ * - * The kind of topology * + * + * Areas + * Set of Areas + * - + * no + * - + * List of Areas it belongs to (at most one area for each area type) + * * * * @@ -900,6 +905,43 @@ interface BusView { Optional getSubstation(); + /** + * Get an iterable on all the Areas that this voltage level belongs to. + * + * @return all the areas + */ + Iterable getAreas(); + + /** + * Get a stream on all the Areas that this voltage level belongs to. + * + * @return all the areas + */ + Stream getAreasStream(); + + /** + * Get the Area that this voltage level belongs to for a given area type. + * + * @param areaType the area type + * @return the optional area or empty if not found + */ + Optional getArea(String areaType); + + /** + * Add the voltage level to an area. + * + * @param area the area + * @throws PowsyblException if the area is in another network or if the voltage level already belongs to an area of the same type + */ + void addArea(Area area); + + /** + * Remove the voltage level from an area. + * + * @param area the area + */ + void removeArea(Area area); + default Substation getNullableSubstation() { return getSubstation().orElse(null); } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractVoltageLevel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractVoltageLevel.java index 9f29e0eee72..d438ab56d18 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractVoltageLevel.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractVoltageLevel.java @@ -15,9 +15,7 @@ import com.powsybl.iidm.network.*; import com.powsybl.commons.ref.Ref; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,6 +40,9 @@ abstract class AbstractVoltageLevel extends AbstractIdentifiable i private double highVoltageLimit; + /** Areas associated to this VoltageLevel, with at most one area for each area type */ + private final Set areas = new LinkedHashSet<>(); + private boolean removed = false; AbstractVoltageLevel(String id, String name, boolean fictitious, SubstationImpl substation, Ref networkRef, @@ -85,6 +86,72 @@ public Optional getSubstation() { return Optional.ofNullable(substation); } + @Override + public Iterable getAreas() { + if (removed) { + throwAreasRemovedVoltageLevel(); + } + return areas; + } + + @Override + public Stream getAreasStream() { + if (removed) { + throwAreasRemovedVoltageLevel(); + } + return areas.stream(); + } + + @Override + public Optional getArea(String areaType) { + Objects.requireNonNull(areaType); + if (removed) { + throwAreasRemovedVoltageLevel(); + } + return areas.stream().filter(area -> Objects.equals(area.getAreaType(), areaType)).findFirst(); + } + + private void throwAreasRemovedVoltageLevel() { + throw new PowsyblException("Cannot access areas of removed voltage level " + id); + } + + @Override + public void addArea(Area area) { + Objects.requireNonNull(area); + if (removed) { + throw new PowsyblException("Cannot add areas to removed voltage level " + id); + } + if (areas.contains(area)) { + // Nothing to do + return; + } + + // Check that the VoltageLevel belongs to the same network or subnetwork + if (area.getParentNetwork() != getParentNetwork()) { + throw new PowsyblException("VoltageLevel " + getId() + " cannot be added to Area " + area.getId() + ". It does not belong to the same network or subnetwork."); + } + + // Check if the voltageLevel is already in another Area of the same type + final Optional previousArea = getArea(area.getAreaType()); + if (previousArea.isPresent() && previousArea.get() != area) { + // This instance already has a different area with the same AreaType + throw new PowsyblException("VoltageLevel " + getId() + " is already in Area of the same type=" + previousArea.get().getAreaType() + " with id=" + previousArea.get().getId()); + } + // No conflict, add the area to this voltageLevel and vice versa + areas.add(area); + area.addVoltageLevel(this); + } + + @Override + public void removeArea(Area area) { + Objects.requireNonNull(area); + if (removed) { + throw new PowsyblException("Cannot remove areas from removed voltage level " + id); + } + areas.remove(area); + area.removeVoltageLevel(this); + } + @Override public Substation getNullableSubstation() { return substation; @@ -503,6 +570,8 @@ public void remove() { // Remove the topology removeTopology(); + // Remove this voltage level from the areas + getAreas().forEach(area -> area.removeVoltageLevel(this)); // Remove this voltage level from the network getSubstation().map(SubstationImpl.class::cast).ifPresent(s -> s.remove(this)); network.getIndex().remove(this); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaAdderImpl.java new file mode 100644 index 00000000000..b2df4429e1b --- /dev/null +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaAdderImpl.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.impl; + +import com.powsybl.commons.ref.Ref; +import com.powsybl.commons.ref.RefChain; +import com.powsybl.iidm.network.*; + +import java.util.*; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ +public class AreaAdderImpl extends AbstractIdentifiableAdder implements AreaAdder { + + private final Ref networkRef; + private final Ref subnetworkRef; + + private String areaType; + + private double acInterchangeTarget; + + private final Set voltageLevels; + + private final Map terminalAreaBoundaries; + + private final Map boundaryAreaBoundaries; + + AreaAdderImpl(Ref networkRef, final RefChain subnetworkRef) { + this.networkRef = networkRef; + this.subnetworkRef = subnetworkRef; + this.terminalAreaBoundaries = new HashMap<>(); + this.boundaryAreaBoundaries = new HashMap<>(); + this.voltageLevels = new HashSet<>(); + this.acInterchangeTarget = Double.NaN; + } + + public NetworkImpl getNetwork() { + return networkRef.get(); + } + + public AreaAdder setAreaType(String areaType) { + this.areaType = areaType; + return this; + } + + public AreaAdder setAcInterchangeTarget(double acInterchangeTarget) { + this.acInterchangeTarget = acInterchangeTarget; + return this; + } + + public AreaAdder addVoltageLevel(VoltageLevel voltageLevel) { + this.voltageLevels.add(voltageLevel); + return this; + } + + public AreaAdder addAreaBoundary(Terminal terminal, boolean ac) { + this.terminalAreaBoundaries.put(terminal, ac); + return this; + } + + public AreaAdder addAreaBoundary(Boundary boundary, boolean ac) { + this.boundaryAreaBoundaries.put(boundary, ac); + return this; + } + + protected Set getVoltageLevels() { + return voltageLevels; + } + + @Override + public Area add() { + String id = checkAndGetUniqueId(); + AreaImpl area = new AreaImpl(networkRef, subnetworkRef, id, getName(), isFictitious(), areaType, acInterchangeTarget); + terminalAreaBoundaries.forEach((terminal, ac) -> area.newAreaBoundary().setTerminal(terminal).setAc(ac).add()); + boundaryAreaBoundaries.forEach((boundary, ac) -> area.newAreaBoundary().setBoundary(boundary).setAc(ac).add()); + voltageLevels.forEach(area::addVoltageLevel); + getNetwork().getIndex().checkAndAdd(area); + getNetwork().getListeners().notifyCreation(area); + return area; + } + + @Override + protected String getTypeDescription() { + return "Area"; + } +} + diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaBoundaryAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaBoundaryAdderImpl.java new file mode 100644 index 00000000000..4db8d983787 --- /dev/null +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaBoundaryAdderImpl.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ + +package com.powsybl.iidm.network.impl; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.*; +import java.util.Objects; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ + +public class AreaBoundaryAdderImpl implements AreaBoundaryAdder { + + AreaImpl area; + + Boundary boundary; + + Terminal terminal; + + Boolean ac; + + AreaBoundaryAdderImpl(AreaImpl area) { + this.area = Objects.requireNonNull(area); + } + + @Override + public AreaBoundaryAdder setBoundary(Boundary boundary) { + this.boundary = boundary; + this.terminal = null; + return this; + } + + @Override + public AreaBoundaryAdder setTerminal(Terminal terminal) { + this.terminal = terminal; + this.boundary = null; + return this; + } + + @Override + public AreaBoundaryAdder setAc(boolean ac) { + this.ac = ac; + return this; + } + + @Override + public Area add() { + if (ac == null) { + throw new PowsyblException("AreaBoundary AC flag is not set."); + } + // we remove before adding, to forbid duplicates and allow updating ac to true/false + if (boundary != null) { + area.removeAreaBoundary(boundary); + area.addAreaBoundary(new AreaBoundaryImpl(area, boundary, ac)); + } else if (terminal != null) { + area.removeAreaBoundary(terminal); + area.addAreaBoundary(new AreaBoundaryImpl(area, terminal, ac)); + } else { + throw new PowsyblException("No AreaBoundary element (terminal or boundary) is set."); + } + return area; + } + +} diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaBoundaryImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaBoundaryImpl.java new file mode 100644 index 00000000000..277bd3e6e7b --- /dev/null +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaBoundaryImpl.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.impl; + +import com.powsybl.iidm.network.Area; +import com.powsybl.iidm.network.AreaBoundary; +import com.powsybl.iidm.network.Boundary; +import com.powsybl.iidm.network.Terminal; + +import java.util.Objects; +import java.util.Optional; + +public class AreaBoundaryImpl implements AreaBoundary { + + final Area area; + + final Terminal terminal; + + final Boundary boundary; + + final boolean ac; + + AreaBoundaryImpl(Area area, Terminal terminal, boolean ac) { + this.area = Objects.requireNonNull(area); + this.terminal = Objects.requireNonNull(terminal); + this.boundary = null; + this.ac = ac; + } + + AreaBoundaryImpl(Area area, Boundary boundary, boolean ac) { + this.area = Objects.requireNonNull(area); + this.boundary = Objects.requireNonNull(boundary); + this.terminal = null; + this.ac = ac; + } + + @Override + public Area getArea() { + return area; + } + + @Override + public Optional getTerminal() { + return Optional.ofNullable(terminal); + } + + @Override + public Optional getBoundary() { + return Optional.ofNullable(boundary); + } + + @Override + public boolean isAc() { + return ac; + } + + @Override + public double getP() { + return boundary != null ? boundary.getP() : terminal.getP(); + } + + @Override + public double getQ() { + return boundary != null ? boundary.getQ() : terminal.getQ(); + } +} diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java new file mode 100644 index 00000000000..1bc2eac8173 --- /dev/null +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java @@ -0,0 +1,285 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.impl; + +import com.powsybl.commons.ref.Ref; +import com.google.common.collect.Iterables; +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.*; +import gnu.trove.list.array.TDoubleArrayList; + +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ +public class AreaImpl extends AbstractIdentifiable implements Area { + private final Ref networkRef; + private final Ref subnetworkRef; + private final String areaType; + private final List areaBoundaries; + private final Set voltageLevels; + + protected boolean removed = false; + + // attributes depending on the variant + + private final TDoubleArrayList acInterchangeTarget; + + private final class AreaListener extends DefaultNetworkListener { + @Override + public void beforeRemoval(Identifiable identifiable) { + if (identifiable instanceof DanglingLine danglingLine) { + // if dangling line removed from network, remove its boundary from this extension + AreaImpl.this.removeAreaBoundary(danglingLine.getBoundary()); + } else if (identifiable instanceof Connectable connectable) { + // if connectable removed from network, remove its terminals from this extension + connectable.getTerminals().forEach(AreaImpl.this::removeAreaBoundary); + } + // When a VoltageLevel is removed, its areas' voltageLevels attributes are directly updated. This is not managed via this listener. + } + } + + private final NetworkListener areaListener; + + public AreaImpl(Ref ref, Ref subnetworkRef, String id, String name, boolean fictitious, String areaType, + double acInterchangeTarget) { + super(id, name, fictitious); + this.networkRef = Objects.requireNonNull(ref); + this.subnetworkRef = subnetworkRef; + this.areaType = Objects.requireNonNull(areaType); + this.voltageLevels = new LinkedHashSet<>(); + this.areaBoundaries = new ArrayList<>(); + + int variantArraySize = networkRef.get().getVariantManager().getVariantArraySize(); + this.acInterchangeTarget = new TDoubleArrayList(variantArraySize); + for (int i = 0; i < variantArraySize; i++) { + this.acInterchangeTarget.add(acInterchangeTarget); + } + this.areaListener = new AreaListener(); + getNetwork().addListener(this.areaListener); + } + + @Override + public NetworkImpl getNetwork() { + throwIfRemoved("network"); + return networkRef.get(); + } + + @Override + public Network getParentNetwork() { + throwIfRemoved("network"); + return Optional.ofNullable((Network) subnetworkRef.get()).orElse(getNetwork()); + } + + @Override + protected String getTypeDescription() { + return "Area"; + } + + @Override + public String getAreaType() { + throwIfRemoved("area type"); + return areaType; + } + + @Override + public Iterable getVoltageLevels() { + throwIfRemoved("voltage levels"); + return voltageLevels; + } + + @Override + public Stream getVoltageLevelStream() { + throwIfRemoved("voltage levels"); + return voltageLevels.stream(); + } + + @Override + public OptionalDouble getAcInterchangeTarget() { + throwIfRemoved("AC interchange target"); + double target = acInterchangeTarget.get(getNetwork().getVariantIndex()); + if (Double.isNaN(target)) { + return OptionalDouble.empty(); + } + return OptionalDouble.of(target); + } + + @Override + public Area setAcInterchangeTarget(double acInterchangeTarget) { + NetworkImpl n = getNetwork(); + int variantIndex = n.getVariantIndex(); + double oldValue = this.acInterchangeTarget.set(variantIndex, acInterchangeTarget); + String variantId = n.getVariantManager().getVariantId(variantIndex); + notifyUpdate("acInterchangeTarget", variantId, oldValue, acInterchangeTarget); + return this; + } + + @Override + public double getAcInterchange() { + throwIfRemoved("AC interchange"); + return getInterchange(AreaBoundary::isAc); + } + + @Override + public double getDcInterchange() { + throwIfRemoved("DC interchange"); + return getInterchange(areaBoundary -> !areaBoundary.isAc()); + } + + @Override + public double getTotalInterchange() { + throwIfRemoved("total interchange"); + return getInterchange(areaBoundary -> true); + } + + double getInterchange(Predicate predicate) { + return areaBoundaries.stream().filter(predicate).mapToDouble(AreaBoundary::getP).filter(p -> !Double.isNaN(p)).sum(); + } + + /** + * Adds a VoltageLevel to this Area. + * @throws PowsyblException if the VoltageLevel is already in another Area of the same type + * @param voltageLevel the VoltageLevel to be added to this Area + */ + @Override + public Area addVoltageLevel(VoltageLevel voltageLevel) { + Objects.requireNonNull(voltageLevel); + if (voltageLevels.add(voltageLevel)) { + voltageLevel.addArea(this); + } + return this; + } + + @Override + public Area removeVoltageLevel(VoltageLevel voltageLevel) { + Objects.requireNonNull(voltageLevel); + voltageLevels.remove(voltageLevel); + if (Iterables.contains(voltageLevel.getAreas(), this)) { + voltageLevel.removeArea(this); + } + return this; + } + + @Override + public AreaBoundaryAdderImpl newAreaBoundary() { + return new AreaBoundaryAdderImpl(this); + } + + @Override + public Area removeAreaBoundary(Terminal terminal) { + Objects.requireNonNull(terminal); + areaBoundaries.removeIf(b -> Objects.equals(b.getTerminal().orElse(null), terminal)); + return this; + } + + @Override + public Area removeAreaBoundary(Boundary boundary) { + Objects.requireNonNull(boundary); + areaBoundaries.removeIf(b -> Objects.equals(b.getBoundary().orElse(null), boundary)); + return this; + } + + @Override + public AreaBoundary getAreaBoundary(Boundary boundary) { + Objects.requireNonNull(boundary); + return areaBoundaries.stream() + .filter(ab -> Objects.equals(ab.getBoundary().orElse(null), boundary)) + .findFirst().orElse(null); + } + + @Override + public AreaBoundary getAreaBoundary(Terminal terminal) { + Objects.requireNonNull(terminal); + return areaBoundaries.stream() + .filter(ab -> Objects.equals(ab.getTerminal().orElse(null), terminal)) + .findFirst().orElse(null); + } + + @Override + public Iterable getAreaBoundaries() { + throwIfRemoved("area boundaries"); + return areaBoundaries; + } + + @Override + public Stream getAreaBoundaryStream() { + throwIfRemoved("area boundaries"); + return areaBoundaries.stream(); + } + + protected void addAreaBoundary(AreaBoundaryImpl areaBoundary) { + Optional terminal = areaBoundary.getTerminal(); + Optional boundary = areaBoundary.getBoundary(); + boundary.ifPresent(b -> checkBoundaryNetwork(b.getDanglingLine().getParentNetwork(), "Boundary of DanglingLine" + b.getDanglingLine().getId())); + terminal.ifPresent(t -> checkBoundaryNetwork(t.getConnectable().getParentNetwork(), "Terminal of connectable " + t.getConnectable().getId())); + areaBoundaries.add(areaBoundary); + } + + void checkBoundaryNetwork(Network network, String boundaryTypeAndId) { + if (getParentNetwork() != network) { + throw new PowsyblException(boundaryTypeAndId + " cannot be added to Area " + getId() + " boundaries. It does not belong to the same network or subnetwork."); + } + } + + @Override + public void remove() { + NetworkImpl network = getNetwork(); + network.getListeners().notifyBeforeRemoval(this); + network.getIndex().remove(this); + for (VoltageLevel voltageLevel : new HashSet<>(voltageLevels)) { + voltageLevel.removeArea(this); + } + network.getListeners().notifyAfterRemoval(id); + network.removeListener(this.areaListener); + removed = true; + } + + void throwIfRemoved(String attribute) { + if (removed) { + throw new PowsyblException("Cannot access " + attribute + " of removed area " + id); + } + } + + protected void notifyUpdate(String attribute, String variantId, Object oldValue, Object newValue) { + getNetwork().getListeners().notifyUpdate(this, attribute, variantId, oldValue, newValue); + } + + @Override + public void extendVariantArraySize(int initVariantArraySize, int number, int sourceIndex) { + super.extendVariantArraySize(initVariantArraySize, number, sourceIndex); + acInterchangeTarget.ensureCapacity(acInterchangeTarget.size() + number); + for (int i = 0; i < number; i++) { + acInterchangeTarget.add(acInterchangeTarget.get(sourceIndex)); + } + } + + @Override + public void reduceVariantArraySize(int number) { + super.reduceVariantArraySize(number); + acInterchangeTarget.remove(acInterchangeTarget.size() - number, number); + } + + @Override + public void deleteVariantArrayElement(int index) { + super.deleteVariantArrayElement(index); + // nothing to do + } + + @Override + public void allocateVariantArrayElement(int[] indexes, int sourceIndex) { + super.allocateVariantArrayElement(indexes, sourceIndex); + for (int index : indexes) { + acInterchangeTarget.set(index, acInterchangeTarget.get(sourceIndex)); + } + } + +} diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java index c31275c3c98..45845e53fa6 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java @@ -254,6 +254,46 @@ public int getCountryCount() { return getCountries().size(); } + @Override + public Iterable getAreaTypes() { + return getAreaTypeStream().toList(); + } + + @Override + public Stream getAreaTypeStream() { + return getAreaStream().map(Area::getAreaType).distinct(); + } + + @Override + public int getAreaTypeCount() { + return (int) getAreaTypeStream().count(); + } + + @Override + public AreaAdder newArea() { + return new AreaAdderImpl(ref, subnetworkRef); + } + + @Override + public Iterable getAreas() { + return Collections.unmodifiableCollection(index.getAll(AreaImpl.class)); + } + + @Override + public Stream getAreaStream() { + return index.getAll(AreaImpl.class).stream().map(Function.identity()); + } + + @Override + public Area getArea(String id) { + return index.get(id, AreaImpl.class); + } + + @Override + public int getAreaCount() { + return index.getAll(AreaImpl.class).size(); + } + @Override public SubstationAdder newSubstation() { return new SubstationAdderImpl(ref, subnetworkRef); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubnetworkImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubnetworkImpl.java index e6bbd75079f..a19d931c1c9 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubnetworkImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubnetworkImpl.java @@ -108,10 +108,51 @@ public int getCountryCount() { private Stream getCountryStream() { return getNetwork().getSubstationStream() - .filter(this::contains) - .map(s -> s.getCountry().orElse(null)) - .filter(Objects::nonNull) - .distinct(); + .filter(this::contains) + .map(s -> s.getCountry().orElse(null)) + .filter(Objects::nonNull) + .distinct(); + } + + @Override + public Iterable getAreaTypes() { + return getAreaTypeStream().toList(); + } + + @Override + public Stream getAreaTypeStream() { + return getAreaStream().map(Area::getAreaType).distinct(); + } + + @Override + public int getAreaTypeCount() { + return (int) getAreaTypeStream().count(); + } + + @Override + public AreaAdder newArea() { + return new AreaAdderImpl(rootNetworkRef, ref); + } + + @Override + public Iterable getAreas() { + return getAreaStream().toList(); + } + + @Override + public Stream getAreaStream() { + return getNetwork().getAreaStream().filter(this::contains); + } + + @Override + public Area getArea(String id) { + Area area = getNetwork().getArea(id); + return contains(area) ? area : null; + } + + @Override + public int getAreaCount() { + return (int) getAreaStream().count(); } @Override diff --git a/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/tck/AreaTest.java b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/tck/AreaTest.java new file mode 100644 index 00000000000..878e7b51b1a --- /dev/null +++ b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/tck/AreaTest.java @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.impl.tck; + +import com.powsybl.iidm.network.tck.AbstractAreaTest; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ +class AreaTest extends AbstractAreaTest { } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaBoundarySerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaBoundarySerDe.java new file mode 100644 index 00000000000..88ee560670b --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaBoundarySerDe.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.io.TreeDataWriter; +import com.powsybl.iidm.network.*; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ +public class AreaBoundarySerDe { + + static final AreaBoundarySerDe INSTANCE = new AreaBoundarySerDe(); + static final String ROOT_ELEMENT_NAME = "areaBoundary"; + static final String ARRAY_ELEMENT_NAME = "areaBoundaries"; + + public static final String TERMINAL_REF = "terminalRef"; + + protected void write(final Area holder, final NetworkSerializerContext context) { + final TreeDataWriter writer = context.getWriter(); + writer.writeStartNodes(); + for (AreaBoundary boundary : holder.getAreaBoundaries()) { + writer.writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), ROOT_ELEMENT_NAME); + writer.writeBooleanAttribute("ac", boundary.isAc()); + boundary.getTerminal().ifPresent(terminal -> { + writer.writeStringAttribute("type", TERMINAL_REF); + TerminalRefSerDe.writeTerminalRefAttribute(terminal, context, writer); + }); + boundary.getBoundary().ifPresent(danglingLineBoundary -> { + writer.writeStringAttribute("type", BoundaryRefSerDe.ROOT_ELEMENT_NAME); + BoundaryRefSerDe.writeBoundaryRefAttributes(danglingLineBoundary, context); + }); + writer.writeEndNode(); + } + writer.writeEndNodes(); + } + + protected void read(final Area holder, final NetworkDeserializerContext context) { + boolean ac = context.getReader().readBooleanAttribute("ac"); + AreaBoundaryAdder adder = holder.newAreaBoundary().setAc(ac); + String type = context.getReader().readStringAttribute("type"); + switch (type) { + case TERMINAL_REF -> TerminalRefSerDe.readTerminalRef(context, holder.getNetwork(), adder::setTerminal); + case BoundaryRefSerDe.ROOT_ELEMENT_NAME -> BoundaryRefSerDe.readBoundaryRef(context, holder.getNetwork(), adder::setBoundary); + default -> throw new PowsyblException("Unexpected element for AreaBoundary: " + type + ". Should be " + BoundaryRefSerDe.ROOT_ELEMENT_NAME + " or " + TERMINAL_REF); + } + context.getEndTasks().add(adder::add); + } +} diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaSerDe.java new file mode 100644 index 00000000000..092a09bd8f9 --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaSerDe.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.serde.util.IidmSerDeUtil; + +import java.util.OptionalDouble; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ +public class AreaSerDe extends AbstractSimpleIdentifiableSerDe { + + static final AreaSerDe INSTANCE = new AreaSerDe(); + static final String ROOT_ELEMENT_NAME = "area"; + static final String ARRAY_ELEMENT_NAME = "areas"; + + @Override + protected String getRootElementName() { + return ROOT_ELEMENT_NAME; + } + + @Override + protected void writeRootElementAttributes(final Area area, final Network parent, final NetworkSerializerContext context) { + context.getWriter().writeStringAttribute("areaType", context.getAnonymizer().anonymizeString(area.getAreaType())); + Double acInterchangeTarget = null; + if (area.getAcInterchangeTarget().isPresent()) { + acInterchangeTarget = area.getAcInterchangeTarget().getAsDouble(); + } + context.getWriter().writeOptionalDoubleAttribute("acInterchangeTarget", acInterchangeTarget); + } + + @Override + protected void writeSubElements(final Area area, final Network parent, final NetworkSerializerContext context) { + writeVoltageLevels(area, context); + AreaBoundarySerDe.INSTANCE.write(area, context); + } + + private void writeVoltageLevels(Area area, NetworkSerializerContext context) { + for (VoltageLevel voltageLevel : IidmSerDeUtil.sorted(area.getVoltageLevels(), context.getOptions())) { + VoltageLevelRefSerDe.writeVoltageLevelRef(voltageLevel, context); + } + } + + @Override + protected AreaAdder createAdder(final Network network) { + return network.newArea(); + } + + @Override + protected Area readRootElementAttributes(final AreaAdder adder, final Network parent, final NetworkDeserializerContext context) { + String areaType = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("areaType")); + adder.setAreaType(areaType); + OptionalDouble acInterchangeTarget = context.getReader().readOptionalDoubleAttribute("acInterchangeTarget"); + acInterchangeTarget.ifPresent(adder::setAcInterchangeTarget); + return adder.add(); + } + + @Override + protected void readSubElements(final Area area, final NetworkDeserializerContext context) { + context.getReader().readChildNodes(elementName -> { + switch (elementName) { + case VoltageLevelRefSerDe.ROOT_ELEMENT_NAME -> VoltageLevelRefSerDe.readVoltageLevelRef(context, area.getNetwork(), area::addVoltageLevel); + case AreaBoundarySerDe.ROOT_ELEMENT_NAME -> AreaBoundarySerDe.INSTANCE.read(area, context); + default -> readSubElement(elementName, area, context); + } + }); + } +} diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BoundaryRefSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BoundaryRefSerDe.java new file mode 100644 index 00000000000..955d282d0f5 --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BoundaryRefSerDe.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import com.powsybl.iidm.network.Boundary; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.Network; + +import java.util.function.Consumer; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ +public final class BoundaryRefSerDe { + + private static final String ID = "id"; + + public static final String ROOT_ELEMENT_NAME = "boundaryRef"; + + public static void readBoundaryRef(NetworkDeserializerContext context, Network network, Consumer endTaskTerminalConsumer) { + String id = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(ID)); + context.getReader().readEndNode(); + context.getEndTasks().add(() -> { + DanglingLine danglingLine = network.getDanglingLine(id); + endTaskTerminalConsumer.accept(danglingLine.getBoundary()); + }); + } + + public static void writeBoundaryRefAttributes(Boundary boundary, NetworkSerializerContext context) { + context.getWriter().writeStringAttribute(ID, context.getAnonymizer().anonymizeString(boundary.getDanglingLine().getId())); + } + + private BoundaryRefSerDe() { + } +} diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java index d7e5babe16f..c9e243c829e 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java @@ -350,6 +350,8 @@ private static void writeBaseNetwork(Network n, NetworkSerializerContext context writeLines(n, context); writeTieLines(n, context); writeHvdcLines(n, context); + + IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_13, context, () -> writeAreas(n, context)); } private static void writeSubnetworks(Network n, NetworkSerializerContext context) { @@ -362,6 +364,18 @@ private static void writeSubnetworks(Network n, NetworkSerializerContext context context.getWriter().writeEndNodes(); } + private static void writeAreas(Network n, NetworkSerializerContext context) { + context.getWriter().writeStartNodes(); + for (Area area : IidmSerDeUtil.sorted(n.getAreas(), context.getOptions())) { + if (isElementWrittenInsideNetwork(area, n, context)) { + IidmSerDeUtil.assertMinimumVersion(NETWORK_ROOT_ELEMENT_NAME, AreaSerDe.ROOT_ELEMENT_NAME, + IidmSerDeUtil.ErrorMessage.NOT_SUPPORTED, IidmVersion.V_1_13, context); + AreaSerDe.INSTANCE.write(area, n, context); + } + } + context.getWriter().writeEndNodes(); + } + private static void writeVoltageLevels(Network n, NetworkSerializerContext context) { context.getWriter().writeStartNodes(); for (VoltageLevel voltageLevel : IidmSerDeUtil.sorted(n.getVoltageLevels(), context.getOptions())) { @@ -556,7 +570,9 @@ private static BiMap createArrayNameSingleNameBiMap(boolean with Map.entry(AbstractSwitchSerDe.ARRAY_ELEMENT_NAME, AbstractSwitchSerDe.ROOT_ELEMENT_NAME), Map.entry(AbstractTransformerSerDe.STEP_ARRAY_ELEMENT_NAME, AbstractTransformerSerDe.STEP_ROOT_ELEMENT_NAME), Map.entry(AliasesSerDe.ARRAY_ELEMENT_NAME, AliasesSerDe.ROOT_ELEMENT_NAME), + Map.entry(AreaSerDe.ARRAY_ELEMENT_NAME, AreaSerDe.ROOT_ELEMENT_NAME), Map.entry(BatterySerDe.ARRAY_ELEMENT_NAME, BatterySerDe.ROOT_ELEMENT_NAME), + Map.entry(AreaBoundarySerDe.ARRAY_ELEMENT_NAME, AreaBoundarySerDe.ROOT_ELEMENT_NAME), Map.entry(BusSerDe.ARRAY_ELEMENT_NAME, BusSerDe.ROOT_ELEMENT_NAME), Map.entry(BusbarSectionSerDe.ARRAY_ELEMENT_NAME, BusbarSectionSerDe.ROOT_ELEMENT_NAME), Map.entry(ConnectableSerDeUtil.TEMPORARY_LIMITS_ARRAY_ELEMENT_NAME, ConnectableSerDeUtil.TEMPORARY_LIMITS_ROOT_ELEMENT_NAME), @@ -617,6 +633,7 @@ private static void readNetworkElement(String elementName, Deque networ case AliasesSerDe.ROOT_ELEMENT_NAME -> checkSupportedAndReadAlias(networks.peek(), context); case PropertiesSerDe.ROOT_ELEMENT_NAME -> PropertiesSerDe.read(networks.peek(), context); case NETWORK_ROOT_ELEMENT_NAME -> checkSupportedAndReadSubnetwork(networks, networkFactory, context, extensionNamesImported, extensionNamesNotFound); + case AreaSerDe.ROOT_ELEMENT_NAME -> checkSupportedAndReadArea(context, networks); case VoltageLevelSerDe.ROOT_ELEMENT_NAME -> checkSupportedAndReadVoltageLevel(context, networks); case SubstationSerDe.ROOT_ELEMENT_NAME -> SubstationSerDe.INSTANCE.read(networks.peek(), context); case LineSerDe.ROOT_ELEMENT_NAME -> LineSerDe.INSTANCE.read(networks.peek(), context); @@ -650,6 +667,12 @@ private static void checkSupportedAndReadSubnetwork(Deque networks, Net networks.pop(); } + private static void checkSupportedAndReadArea(NetworkDeserializerContext context, Deque networks) { + IidmSerDeUtil.assertMinimumVersion(NETWORK_ROOT_ELEMENT_NAME, AreaSerDe.ROOT_ELEMENT_NAME, + IidmSerDeUtil.ErrorMessage.NOT_SUPPORTED, IidmVersion.V_1_13, context); + AreaSerDe.INSTANCE.read(networks.peek(), context); + } + private static void checkSupportedAndReadVoltageLevel(NetworkDeserializerContext context, Deque networks) { IidmSerDeUtil.assertMinimumVersion(NETWORK_ROOT_ELEMENT_NAME, VoltageLevelSerDe.ROOT_ELEMENT_NAME, IidmSerDeUtil.ErrorMessage.NOT_SUPPORTED, IidmVersion.V_1_6, context); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelRefSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelRefSerDe.java new file mode 100644 index 00000000000..1afc5aee13d --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelRefSerDe.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import com.powsybl.iidm.network.*; + +import java.util.function.Consumer; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ +public final class VoltageLevelRefSerDe { + private static final String ID = "id"; + + public static final String ROOT_ELEMENT_NAME = "voltageLevelRef"; + + public static void readVoltageLevelRef(NetworkDeserializerContext context, Network network, Consumer endTaskTerminalConsumer) { + String id = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(ID)); + context.getReader().readEndNode(); + context.getEndTasks().add(() -> { + VoltageLevel voltageLevel = network.getVoltageLevel(id); + endTaskTerminalConsumer.accept(voltageLevel); + }); + } + + public static void writeVoltageLevelRef(VoltageLevel voltageLevel, NetworkSerializerContext context) { + context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), ROOT_ELEMENT_NAME); + context.getWriter().writeStringAttribute(ID, context.getAnonymizer().anonymizeString(voltageLevel.getId())); + context.getWriter().writeEndNode(); + } + + private VoltageLevelRefSerDe() { + } +} diff --git a/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd b/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd index 51098c25e4e..2b2c7fc657a 100644 --- a/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd +++ b/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd @@ -35,6 +35,7 @@ + @@ -701,4 +702,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd b/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd index 6c34a516232..cb3d503842d 100644 --- a/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd +++ b/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd @@ -35,6 +35,7 @@ + @@ -701,4 +702,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AreaSerDeTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AreaSerDeTest.java new file mode 100644 index 00000000000..100bd2515d9 --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AreaSerDeTest.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import com.powsybl.iidm.network.*; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.time.ZonedDateTime; + +import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ +class AreaSerDeTest extends AbstractIidmSerDeTest { + + @Test + void testNetworkAreas() throws IOException { + Network network = createBaseNetworkWithAreas(); + allFormatsRoundTripTest(network, "/areaRoundTripRef.xml", CURRENT_IIDM_VERSION); + allFormatsRoundTripTest(network, "/areaRoundTripRef.xml", IidmVersion.V_1_12); + // backward compatibility (checks versions 11 and 12) + allFormatsRoundTripFromVersionedXmlFromMinToCurrentVersionTest("/areaRoundTripRef.xml", IidmVersion.V_1_11); + } + + private static Network createBaseNetworkWithAreas() { + // Create a base network + Network network = Network.create("test", "test"); + network.setCaseDate(ZonedDateTime.parse("2020-03-04T13:20:30.476+01:00")); + Network subnetwork = network.createSubnetwork("sub", "Sub", "format"); + subnetwork.setCaseDate(ZonedDateTime.parse("2020-03-04T13:20:30.476+01:00")); + Substation s1 = network.newSubstation().setId("sub1").add(); + VoltageLevel vl1 = s1.newVoltageLevel().setId("VL1").setNominalV(1).setTopologyKind(TopologyKind.BUS_BREAKER).add(); + network.newSubstation().setId("sub2").add(); + vl1.getBusBreakerView().newBus().setId("N1").add(); + vl1.getBusBreakerView().newBus().setId("N2").add(); + Line line = network.newLine().setId("Line1").setVoltageLevel1("VL1").setBus1("N1").setVoltageLevel2("VL1").setBus2("N2").setR(0.0).setX(0.0).add(); + + Substation s2 = subnetwork.newSubstation().setId("sub3").add(); + VoltageLevel vl2 = s2.newVoltageLevel().setId("VL2").setNominalV(1).setTopologyKind(TopologyKind.BUS_BREAKER).add(); + vl2.getBusBreakerView().newBus().setId("N3").add(); + + final Load load1 = vl1.newLoad().setId("L1").setBus("N1").setP0(0).setQ0(0).add(); + final DanglingLine danglingLine = vl1.newDanglingLine().setId("DL1").setBus("N2").setR(0.0).setX(0.0).setP0(0).setQ0(0).add(); + + // Add area types and areas to the network + final String biddingZoneType = "BiddingZone"; + final String controlAreaType = "ControlArea"; + network.newArea().setAreaType(biddingZoneType).setId("BidZoneId1").setName("BidZoneName1").addAreaBoundary(load1.getTerminal(), true) + .addAreaBoundary(danglingLine.getBoundary(), false).add(); + network.newArea().setAreaType(biddingZoneType).setId("BidZoneId2").setName("BidZoneName2").addAreaBoundary(line.getTerminal1(), true) + .setAcInterchangeTarget(100.).add(); + network.newArea().setAreaType(controlAreaType).setId("ControlAreaId1").setName("ControlAreaName1").add(); + vl1.addArea(network.getArea("BidZoneId1")); + vl1.addArea(network.getArea("ControlAreaId1")); + + subnetwork.newArea().setAreaType(biddingZoneType).setId("BidZoneId3").setName("BidZoneName3").add(); + vl2.addArea(subnetwork.getArea("BidZoneId3")); + return network; + } + +} diff --git a/iidm/iidm-serde/src/test/resources/V1_11/areaRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_11/areaRoundTripRef.xml new file mode 100644 index 00000000000..b76556e067e --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_11/areaRoundTripRef.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/iidm/iidm-serde/src/test/resources/V1_12/areaRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_12/areaRoundTripRef.xml new file mode 100644 index 00000000000..e68deccc15e --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_12/areaRoundTripRef.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/iidm/iidm-serde/src/test/resources/V1_13/areaRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_13/areaRoundTripRef.xml new file mode 100644 index 00000000000..43513dcc01a --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_13/areaRoundTripRef.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java new file mode 100644 index 00000000000..012a29725cb --- /dev/null +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java @@ -0,0 +1,489 @@ +/** + * Copyright (c) 2024, Coreso SA (https://www.coreso.eu/) and TSCNET Services GmbH (https://www.tscnet.eu/) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.tck; + +import com.google.common.collect.Iterables; +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Marine Guibert {@literal } + * @author Valentin Mouradian {@literal } + */ +public abstract class AbstractAreaTest { + + private static final String CONTROL_AREA_TYPE = "ControlArea"; + private static final String REGION_AREA_TYPE = "Region"; + public static final double DELTA = 1e-2; + Network network; + Area controlAreaA; + Area controlAreaB; + Area regionAB; + VoltageLevel vlgen; + VoltageLevel vlhv1; + VoltageLevel vlhv2; + VoltageLevel vlload; + DanglingLine dlXnode1A; + DanglingLine dlXnode1B; + DanglingLine dlXnode2A; + DanglingLine dlXnode2B; + TieLine tieLine1; + TieLine tieLine2; + + @BeforeEach + void setUp() { + network = EurostagTutorialExample1Factory.createWithTieLinesAndAreas(); + controlAreaA = network.getArea("ControlArea_A"); + controlAreaB = network.getArea("ControlArea_B"); + regionAB = network.getArea("Region_AB"); + vlgen = network.getVoltageLevel("VLGEN"); + vlhv1 = network.getVoltageLevel("VLHV1"); + vlhv2 = network.getVoltageLevel("VLHV2"); + vlload = network.getVoltageLevel("VLLOAD"); + dlXnode1A = network.getDanglingLine("NHV1_XNODE1"); + dlXnode1B = network.getDanglingLine("XNODE1_NHV2"); + dlXnode2A = network.getDanglingLine("NVH1_XNODE2"); + dlXnode2B = network.getDanglingLine("XNODE2_NHV2"); + tieLine1 = network.getTieLine("NHV1_NHV2_1"); + tieLine2 = network.getTieLine("NHV1_NHV2_2"); + } + + @Test + public void areaAttributes() { + assertEquals(IdentifiableType.AREA, controlAreaA.getType()); + + assertEquals("ControlArea_A", controlAreaA.getId()); + assertEquals("Control Area A", controlAreaA.getOptionalName().orElseThrow()); + assertEquals(CONTROL_AREA_TYPE, controlAreaA.getAreaType()); + assertEquals(-602.6, controlAreaA.getAcInterchangeTarget().orElseThrow()); + assertEquals(Set.of(vlgen, vlhv1), controlAreaA.getVoltageLevels()); + assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); + + assertEquals("ControlArea_B", controlAreaB.getId()); + assertEquals("Control Area B", controlAreaB.getOptionalName().orElseThrow()); + assertEquals(CONTROL_AREA_TYPE, controlAreaB.getAreaType()); + assertEquals(+602.6, controlAreaB.getAcInterchangeTarget().orElseThrow()); + assertEquals(Set.of(vlhv2, vlload), controlAreaB.getVoltageLevels()); + assertEquals(2, controlAreaB.getAreaBoundaryStream().count()); + + assertEquals("Region_AB", regionAB.getId()); + assertEquals("Region AB", regionAB.getOptionalName().orElseThrow()); + assertEquals(REGION_AREA_TYPE, regionAB.getAreaType()); + assertFalse(regionAB.getAcInterchangeTarget().isPresent()); + assertEquals(Set.of(vlgen, vlhv1, vlhv2, vlload), regionAB.getVoltageLevels()); + assertEquals(0, regionAB.getAreaBoundaryStream().count()); + } + + @Test + public void testSetterGetter() { + controlAreaA.setAcInterchangeTarget(123.0); + assertTrue(controlAreaA.getAcInterchangeTarget().isPresent()); + assertEquals(123.0, controlAreaA.getAcInterchangeTarget().getAsDouble()); + controlAreaA.setAcInterchangeTarget(Double.NaN); + assertTrue(controlAreaA.getAcInterchangeTarget().isEmpty()); + } + + @Test + public void testChangesNotification() { + // Changes listener + NetworkListener mockedListener = Mockito.mock(DefaultNetworkListener.class); + // Add observer changes to current network + network.addListener(mockedListener); + + // Get initial values + double oldValue = controlAreaA.getAcInterchangeTarget().orElseThrow(); + // Change values + controlAreaA.setAcInterchangeTarget(Double.NaN); + + // Check update notification + Mockito.verify(mockedListener, Mockito.times(1)) + .onUpdate(controlAreaA, "acInterchangeTarget", VariantManagerConstants.INITIAL_VARIANT_ID, oldValue, Double.NaN); + + // Change values + controlAreaA.setAcInterchangeTarget(123.4); + + // Check update notification + Mockito.verify(mockedListener, Mockito.times(1)) + .onUpdate(controlAreaA, "acInterchangeTarget", VariantManagerConstants.INITIAL_VARIANT_ID, Double.NaN, 123.4); + + // After this point, no more changes are taken into account. + + // Simulate exception for onUpdate calls + Mockito.doThrow(new PowsyblException()).when(mockedListener) + .onUpdate(controlAreaA, "acInterchangeTarget", VariantManagerConstants.INITIAL_VARIANT_ID, oldValue, 123.4); + + // Case when same value is set + controlAreaA.setAcInterchangeTarget(123.4); + // Case when no listener is registered + network.removeListener(mockedListener); + controlAreaA.setAcInterchangeTarget(456.7); + + // Check no notification + Mockito.verifyNoMoreInteractions(mockedListener); + } + + @Test + public void testSetterGetterInMultiVariants() { + // Changes listener + NetworkListener mockedListener = Mockito.mock(DefaultNetworkListener.class); + // Set observer changes + network.addListener(mockedListener); + + // Init variant manager + VariantManager variantManager = network.getVariantManager(); + List variantsToAdd = Arrays.asList("s1", "s2", "s3", "s4"); + variantManager.cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, variantsToAdd); + + variantManager.setWorkingVariant("s4"); + // check values cloned by extend + assertEquals(-602.6, controlAreaA.getAcInterchangeTarget().orElseThrow(), 0.0); + // change values in s4 + controlAreaA.setAcInterchangeTarget(123.0); + // Check P0 & Q0 update notification + Mockito.verify(mockedListener, Mockito.times(1)).onUpdate(controlAreaA, "acInterchangeTarget", "s4", -602.6, 123.0); + + // remove s2 + variantManager.removeVariant("s2"); + + variantManager.cloneVariant("s4", "s2b"); + variantManager.setWorkingVariant("s2b"); + // check values cloned by allocate + assertEquals(123.0, controlAreaA.getAcInterchangeTarget().orElseThrow(), 0.0); + // recheck initial variant value + variantManager.setWorkingVariant(VariantManagerConstants.INITIAL_VARIANT_ID); + assertEquals(-602.6, controlAreaA.getAcInterchangeTarget().orElseThrow(), 0.0); + + // remove working variant s4 + variantManager.setWorkingVariant("s4"); + variantManager.removeVariant("s4"); + try { + controlAreaA.getAcInterchangeTarget(); + fail(); + } catch (Exception ignored) { + // ignore + } + + // Remove observer changes + network.removeListener(mockedListener); + } + + @Test + public void testGetAreaBoundary() { + AreaBoundary areaBoundary = controlAreaA.getAreaBoundary(dlXnode1A.getBoundary()); + assertNotNull(areaBoundary); + assertTrue(areaBoundary.isAc()); + assertEquals(controlAreaA.getId(), areaBoundary.getArea().getId()); + assertEquals(-301.47, areaBoundary.getP(), DELTA); + assertEquals(-116.52, areaBoundary.getQ(), DELTA); + + controlAreaA.removeAreaBoundary(dlXnode1A.getBoundary()); + assertNull(controlAreaA.getAreaBoundary(dlXnode1A.getBoundary())); + } + + @Test + public void areaInterchangeComputation() { + assertEquals(-602.94, controlAreaA.getAcInterchange(), DELTA); + assertEquals(0.0, controlAreaA.getDcInterchange()); + assertEquals(-602.94, controlAreaA.getTotalInterchange(), DELTA); + + assertEquals(+602.94, controlAreaB.getAcInterchange(), DELTA); + assertEquals(0.0, controlAreaB.getDcInterchange()); + assertEquals(+602.94, controlAreaB.getTotalInterchange(), DELTA); + + // no boundaries defined + assertEquals(0.0, regionAB.getAcInterchange()); + assertEquals(0.0, regionAB.getDcInterchange()); + assertEquals(0.0, regionAB.getTotalInterchange()); + + // verify NaN do not mess up the calculation + dlXnode1A.getTerminal().setP(Double.NaN); + assertEquals(-301.47, controlAreaA.getAcInterchange(), DELTA); + assertEquals(0.0, controlAreaA.getDcInterchange()); + assertEquals(-301.47, controlAreaA.getTotalInterchange(), DELTA); + } + + @Test + public void areaIterableAndStreamGetterCheck() { + List areas = List.of(controlAreaA, controlAreaB, regionAB); + List areaTypes = List.of(CONTROL_AREA_TYPE, REGION_AREA_TYPE); + + assertEquals(areas, network.getAreaStream().toList()); + assertEquals(areaTypes, network.getAreaTypeStream().toList()); + assertEquals(areaTypes, network.getAreaTypes()); + + assertEquals(3, Iterables.size(network.getAreas())); + areas.forEach(area -> assertTrue(Iterables.contains(network.getAreas(), area))); + assertEquals(2, Iterables.size(network.getAreaTypes())); + areaTypes.forEach(areaType -> assertTrue(Iterables.contains(network.getAreaTypes(), areaType))); + } + + @Test + public void addVoltageLevelsToArea() { + var newVl = vlhv1.getNullableSubstation().newVoltageLevel() + .setId("NewVl") + .setNominalV(400.0) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + + // we can add new VL from the area + controlAreaA.addVoltageLevel(newVl); + // or from the new VL + newVl.addArea(regionAB); + + assertEquals(Set.of(controlAreaA, regionAB), newVl.getAreas()); + assertEquals(Set.of(vlgen, vlhv1, newVl), controlAreaA.getVoltageLevels()); + assertEquals(Set.of(vlhv2, vlload), controlAreaB.getVoltageLevels()); + assertEquals(Set.of(vlgen, vlhv1, newVl, vlhv2, vlload), regionAB.getVoltageLevels()); + } + + @Test + public void addSameVoltageLevelToArea() { + // vlhv1 already in controlAreaA, no-op + controlAreaA.addVoltageLevel(vlhv1); + vlhv1.addArea(controlAreaA); + + assertEquals(Set.of(controlAreaA, regionAB), vlhv1.getAreas()); + assertEquals(Set.of(vlgen, vlhv1), controlAreaA.getVoltageLevels()); + } + + @Test + public void testWithTerminals() { + TwoWindingsTransformer ngenNhv1 = network.getTwoWindingsTransformer("NGEN_NHV1"); + + // change boundary to be at transformer NGEN_NHV1 side 2 + controlAreaA + .removeAreaBoundary(dlXnode1A.getBoundary()) + .removeAreaBoundary(dlXnode2A.getBoundary()); + controlAreaB + .removeAreaBoundary(dlXnode1B.getBoundary()) + .removeAreaBoundary(dlXnode2B.getBoundary()); + + assertNull(controlAreaA.getAreaBoundary(ngenNhv1.getTerminal2())); + + controlAreaA.newAreaBoundary().setTerminal(ngenNhv1.getTerminal2()).setAc(true).add(); + controlAreaB + .newAreaBoundary().setTerminal(dlXnode1A.getTerminal()).setAc(true).add() + .newAreaBoundary().setTerminal(dlXnode2A.getTerminal()).setAc(true).add(); + + AreaBoundary areaBoundary = controlAreaA.getAreaBoundary(ngenNhv1.getTerminal2()); + assertNotNull(areaBoundary); + assertEquals(controlAreaA.getId(), areaBoundary.getArea().getId()); + assertEquals(-604.89, areaBoundary.getP(), DELTA); + assertEquals(-197.48, areaBoundary.getQ(), DELTA); + + assertEquals(-604.89, controlAreaA.getAcInterchange(), DELTA); + assertEquals(0.0, controlAreaA.getDcInterchange()); + assertEquals(-604.89, controlAreaA.getTotalInterchange(), DELTA); + + assertEquals(+604.89, controlAreaB.getAcInterchange(), DELTA); + assertEquals(0.0, controlAreaB.getDcInterchange()); + assertEquals(+604.89, controlAreaB.getTotalInterchange(), DELTA); + + // verify NaN do not mess up the calculation + ngenNhv1.getTerminal2().setP(Double.NaN); + assertEquals(0.0, controlAreaA.getAcInterchange()); + assertEquals(0.0, controlAreaA.getDcInterchange()); + assertEquals(0.0, controlAreaA.getTotalInterchange()); + + // test removing Terminal boundaries + controlAreaB + .removeAreaBoundary(dlXnode1A.getTerminal()) + .removeAreaBoundary(dlXnode2A.getTerminal()); + assertEquals(0, controlAreaB.getAreaBoundaryStream().count()); + } + + @Test + public void testAddSameBoundary() { + assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); + // re-add + controlAreaA + .newAreaBoundary().setBoundary(dlXnode1A.getBoundary()).setAc(true).add() + .newAreaBoundary().setBoundary(dlXnode2A.getBoundary()).setAc(true).add(); + // no change + assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); + assertEquals(-602.94, controlAreaA.getAcInterchange(), DELTA); + assertEquals(0.0, controlAreaA.getDcInterchange()); + assertEquals(-602.94, controlAreaA.getTotalInterchange(), DELTA); + + // change them to DC + controlAreaA + .newAreaBoundary().setBoundary(dlXnode1A.getBoundary()).setAc(false).add() + .newAreaBoundary().setBoundary(dlXnode2A.getBoundary()).setAc(false).add(); + assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); + assertEquals(0.0, controlAreaA.getAcInterchange()); + assertEquals(-602.94, controlAreaA.getDcInterchange(), DELTA); + assertEquals(-602.94, controlAreaA.getTotalInterchange(), DELTA); + } + + @Test + public void testWithDc() { + // remove entirely control area B, set one dangling line AC, the other one DC + controlAreaB.remove(); + regionAB.remove(); + tieLine1.remove(); + tieLine2.remove(); + network.getSubstation("P2").remove(); + controlAreaA.newAreaBoundary().setBoundary(dlXnode2A.getBoundary()).setAc(false).add(); + dlXnode1A.setP0(290.0); + dlXnode2A.setP0(310.0); + assertEquals(-290.0, controlAreaA.getAcInterchange(), DELTA); + assertEquals(-310.0, controlAreaA.getDcInterchange()); + assertEquals(-600.0, controlAreaA.getTotalInterchange(), DELTA); + } + + @Test + public void testRemoveVoltageLevel() { + assertEquals(2, controlAreaA.getVoltageLevelStream().count()); + controlAreaA + .removeVoltageLevel(vlgen) + .removeVoltageLevel(vlhv1); + assertEquals(0, controlAreaA.getVoltageLevelStream().count()); + } + + @Test + public void throwAddNewAreaSameType() { + var e1 = assertThrows(PowsyblException.class, () -> controlAreaA.addVoltageLevel(vlhv2)); + assertEquals("VoltageLevel VLHV2 is already in Area of the same type=ControlArea with id=ControlArea_B", e1.getMessage()); + var e2 = assertThrows(PowsyblException.class, () -> vlhv1.addArea(controlAreaB)); + assertEquals("VoltageLevel VLHV1 is already in Area of the same type=ControlArea with id=ControlArea_A", e2.getMessage()); + } + + @Test + public void throwRemovedVoltageLevel() { + VoltageLevel newVoltageLevel = network.newVoltageLevel() + .setId("newVoltageLevel") + .setNominalV(400.0) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + + newVoltageLevel.addArea(controlAreaB); + assertEquals(Set.of(vlhv2, vlload, newVoltageLevel), controlAreaB.getVoltageLevels()); + newVoltageLevel.remove(); + assertEquals(Set.of(vlhv2, vlload), controlAreaB.getVoltageLevels()); + + Throwable e1 = assertThrows(PowsyblException.class, () -> newVoltageLevel.getArea(CONTROL_AREA_TYPE)); + Throwable e2 = assertThrows(PowsyblException.class, newVoltageLevel::getAreas); + Throwable e3 = assertThrows(PowsyblException.class, newVoltageLevel::getAreasStream); + Throwable e4 = assertThrows(PowsyblException.class, () -> newVoltageLevel.addArea(controlAreaA)); + + String expectedMessage = "Cannot access areas of removed voltage level newVoltageLevel"; + assertEquals(expectedMessage, e1.getMessage()); + assertEquals(expectedMessage, e2.getMessage()); + assertEquals(expectedMessage, e3.getMessage()); + assertEquals("Cannot add areas to removed voltage level newVoltageLevel", e4.getMessage()); + } + + @Test + public void throwAddVoltageLevelOtherNetwork() { + Network subnetwork = network.createSubnetwork("subnetwork_id", "Subnetwork", "code"); + VoltageLevel newVoltageLevel = subnetwork.newVoltageLevel() + .setId("newVoltageLevel") + .setNominalV(400.0) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + + Throwable e = assertThrows(PowsyblException.class, () -> controlAreaA.addVoltageLevel(newVoltageLevel)); + assertEquals("VoltageLevel newVoltageLevel cannot be added to Area ControlArea_A. It does not belong to the same network or subnetwork.", e.getMessage()); + } + + @Test + public void removeAreaBoundaries() { + assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); + + controlAreaA + .removeAreaBoundary(dlXnode1A.getBoundary()) + .removeAreaBoundary(dlXnode2A.getBoundary()); + + assertEquals(0, controlAreaA.getAreaBoundaryStream().count()); + } + + @Test + public void removeEquipmentRemovesAreaBoundary() { + assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); + assertEquals(2, controlAreaB.getAreaBoundaryStream().count()); + + // Deleting equipment from the network should automatically delete their AreaBoundary-s + // here we remove only one side of the tie-line, on control area A side + tieLine1.remove(); + dlXnode1A.remove(); + + assertEquals(1, controlAreaA.getAreaBoundaryStream().count()); + assertEquals(2, controlAreaB.getAreaBoundaryStream().count()); + } + + @Test + public void throwBoundaryOtherNetwork() { + Network subnetwork = network.createSubnetwork("subnetwork_id", "Subnetwork", "json"); + VoltageLevel sn1VL1 = subnetwork.newVoltageLevel() + .setId("sub1_vl1") + .setNominalV(400.0) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus = sn1VL1.getBusBreakerView().newBus() + .setId("sub1_bus") + .add(); + DanglingLine danglingLine = sn1VL1.newDanglingLine() + .setId("sub1_dl") + .setP0(0.0) + .setQ0(0.0) + .setR(1.0) + .setX(1.0) + .setG(0.0) + .setB(0.0) + .setBus(bus.getId()) + .setPairingKey("XNODE") + .add(); + AreaBoundaryAdder areaBoundaryAdder = controlAreaA.newAreaBoundary().setBoundary(danglingLine.getBoundary()).setAc(true); + Throwable e = assertThrows(PowsyblException.class, areaBoundaryAdder::add); + assertEquals("Boundary of DanglingLinesub1_dl cannot be added to Area ControlArea_A boundaries. It does not belong to the same network or subnetwork.", e.getMessage()); + } + + @Test + public void throwBoundaryAttributeNotSet() { + AreaBoundaryAdder areaBoundaryAdder1 = controlAreaA.newAreaBoundary().setAc(true); + Throwable e1 = assertThrows(PowsyblException.class, areaBoundaryAdder1::add); + assertEquals("No AreaBoundary element (terminal or boundary) is set.", e1.getMessage()); + AreaBoundaryAdder areaBoundaryAdder2 = controlAreaA.newAreaBoundary().setBoundary(dlXnode1A.getBoundary()); + Throwable e2 = assertThrows(PowsyblException.class, areaBoundaryAdder2::add); + assertEquals("AreaBoundary AC flag is not set.", e2.getMessage()); + } + + @Test + public void removeArea() { + controlAreaA.remove(); + assertFalse(Iterables.contains(network.getAreas(), controlAreaA)); + assertFalse(network.getAreaStream().toList().contains(controlAreaA)); + + Throwable e1 = assertThrows(PowsyblException.class, controlAreaA::getAreaType); + assertEquals("Cannot access area type of removed area ControlArea_A", e1.getMessage()); + Throwable e2 = assertThrows(PowsyblException.class, controlAreaA::getAcInterchangeTarget); + assertEquals("Cannot access AC interchange target of removed area ControlArea_A", e2.getMessage()); + Throwable e3 = assertThrows(PowsyblException.class, controlAreaA::getAcInterchange); + assertEquals("Cannot access AC interchange of removed area ControlArea_A", e3.getMessage()); + Throwable e4 = assertThrows(PowsyblException.class, controlAreaA::getDcInterchange); + assertEquals("Cannot access DC interchange of removed area ControlArea_A", e4.getMessage()); + Throwable e5 = assertThrows(PowsyblException.class, controlAreaA::getTotalInterchange); + assertEquals("Cannot access total interchange of removed area ControlArea_A", e5.getMessage()); + Throwable e6 = assertThrows(PowsyblException.class, controlAreaA::getVoltageLevels); + assertEquals("Cannot access voltage levels of removed area ControlArea_A", e6.getMessage()); + Throwable e7 = assertThrows(PowsyblException.class, controlAreaA::getVoltageLevelStream); + assertEquals("Cannot access voltage levels of removed area ControlArea_A", e7.getMessage()); + Throwable e8 = assertThrows(PowsyblException.class, controlAreaA::getAreaBoundaries); + assertEquals("Cannot access area boundaries of removed area ControlArea_A", e8.getMessage()); + Throwable e9 = assertThrows(PowsyblException.class, controlAreaA::getAreaBoundaryStream); + assertEquals("Cannot access area boundaries of removed area ControlArea_A", e9.getMessage()); + } +} diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMergeNetworkTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMergeNetworkTest.java index 77969e9bee1..c590f3297dc 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMergeNetworkTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMergeNetworkTest.java @@ -413,6 +413,43 @@ public void failMergeContainingSubnetworks() { assertTrue(e.getMessage().contains("already contains subnetworks")); } + @Test + public void testMergeAndDetachWithDistinctAreas() { + addCommonSubstationsAndVoltageLevels(); + Area n1TsoA = addArea(n1, "tsoA", "tso"); + Area n2BzB = n2.newArea().setId("bzB").setName("bzB_Name").setAreaType("bz").setFictitious(true).setAcInterchangeTarget(100.).add(); + n1TsoA.addVoltageLevel(n1.getVoltageLevel("vl1")); + n2BzB.addVoltageLevel(n2.getVoltageLevel("vl2")); + + // Merge + Network merged = Network.merge(n1, n2); + checkAreas(merged, List.of("tsoA", "bzB")); + checkAreaTypes(merged, List.of("tso", "bz")); + final Area mergedBzB = merged.getArea("bzB"); + assertNotNull(mergedBzB); + assertTrue(mergedBzB.isFictitious()); + assertTrue(mergedBzB.getAcInterchangeTarget().isPresent()); + assertEquals(100., mergedBzB.getAcInterchangeTarget().getAsDouble()); + + // Detach n1, and check its content + Network n1Detached = merged.getSubnetwork(n1.getId()).detach(); + checkAreas(n1Detached, List.of("tsoA")); + checkAreaTypes(n1Detached, List.of("tso")); + // Detached elements were removed from merged network + checkAreas(merged, List.of("bzB")); + checkAreaTypes(merged, List.of("bz")); + + // Detach n2, and check its content + Network n2Detached = merged.getSubnetwork(n2.getId()).detach(); + checkAreas(n2Detached, List.of("bzB")); + checkAreaTypes(n2Detached, List.of("bz")); + final Area detachedBzB = n2Detached.getArea("bzB"); + assertNotNull(detachedBzB); + assertTrue(detachedBzB.isFictitious()); + assertTrue(detachedBzB.getAcInterchangeTarget().isPresent()); + assertEquals(100., detachedBzB.getAcInterchangeTarget().getAsDouble()); + } + @Test public void testNoEmptyAdditionalSubnetworkIsCreated() { Network merge = Network.merge(MERGE, n1, n2); @@ -422,6 +459,28 @@ public void testNoEmptyAdditionalSubnetworkIsCreated() { assertNotNull(merge.getSubnetwork(N2)); } + private static void checkAreas(final Network merged, final List expectedAreaIds) { + assertEquals(expectedAreaIds.size(), merged.getAreaStream().count()); + assertTrue(merged.getAreaStream().map(Area::getId).toList().containsAll(expectedAreaIds)); + final List expectedNames = expectedAreaIds.stream().map(id -> id + "_Name").toList(); + assertTrue(merged.getAreaStream().map(Area::getNameOrId).toList().containsAll(expectedNames)); + } + + private static void checkAreaTypes(final Network merged, final List expectedAreaTypeIds) { + assertEquals(expectedAreaTypeIds.size(), merged.getAreaTypeStream().count()); + assertTrue(merged.getAreaTypeStream().toList().containsAll(expectedAreaTypeIds)); + } + + @Test + public void failMergeWithCommonAreaConflict() { + addArea(n1, "bza", "bz"); + addArea(n2, "bza", "tso"); + + // Merge should fail, because area "bza" is present in both network but with different attribute values + PowsyblException e = assertThrows(PowsyblException.class, () -> Network.merge(n1, n2)); + assertEquals("The following object(s) of type AreaImpl exist(s) in both networks: [bza]", e.getMessage()); + } + @Test public void testListeners() { MutableBoolean listenerCalled = new MutableBoolean(false); @@ -467,6 +526,14 @@ public void onCreation(Identifiable identifiable) { assertTrue(listenerCalled.booleanValue()); } + private static Area addArea(Network network, String id, String areaType) { + return network.newArea() + .setId(id) + .setName(id + "_Name") + .setAreaType(areaType) + .add(); + } + private void addSubstation(Network network, String substationId) { network.newSubstation() .setId(substationId) diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSubnetworksCreationTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSubnetworksCreationTest.java index a82f2ad32b9..8470a80103f 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSubnetworksCreationTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSubnetworksCreationTest.java @@ -37,6 +37,22 @@ public void setup() { subnetwork2 = network.createSubnetwork("Sub2", "Sub2", "format2"); } + @Test + public void testAreaCreation() { + // On root network level + addArea(network, "a0", "Area0", "AreaType0"); + assertAreaCounts(1, 0, 0); + assertAreaTypeCounts(1, 0, 0); + + // On subnetwork level + addArea(subnetwork1, "a1", "Area1", "AreaType1"); + assertAreaCounts(2, 1, 0); + assertAreaTypeCounts(2, 1, 0); + + Throwable e = assertThrows(PowsyblException.class, () -> addArea(subnetwork1, "a0", "Area2", "AreaType2")); + assertTrue(e.getMessage().contains("The network Root already contains an object 'AreaImpl' with the id 'a0'")); + } + @Test public void testSubstationCreation() { // On root network level @@ -444,6 +460,14 @@ void assertValidationLevels(ValidationLevel expected) { assertEquals(expected, subnetwork2.getValidationLevel()); } + private Area addArea(Network network, String id, String name, String areaType) { + return network.newArea() + .setId(id) + .setName(name) + .setAreaType(areaType) + .add(); + } + private Substation addSubstation(Network network, String substationId) { return network.newSubstation() .setId(substationId) @@ -541,6 +565,18 @@ private String getBusId(String vlId3) { return "bus_" + vlId3; } + void assertAreaTypeCounts(int total, int onSubnetwork1, int onSubnetwork2) { + assertEquals(total, network.getAreaTypeCount()); + assertEquals(onSubnetwork1, subnetwork1.getAreaTypeCount()); + assertEquals(onSubnetwork2, subnetwork2.getAreaTypeCount()); + } + + void assertAreaCounts(int total, int onSubnetwork1, int onSubnetwork2) { + assertEquals(total, network.getAreaCount()); + assertEquals(onSubnetwork1, subnetwork1.getAreaCount()); + assertEquals(onSubnetwork2, subnetwork2.getAreaCount()); + } + void assertSubstationCounts(int total, int onSubnetwork1, int onSubnetwork2) { assertEquals(total, network.getSubstationCount()); assertEquals(onSubnetwork1, subnetwork1.getSubstationCount()); diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSubnetworksExplorationTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSubnetworksExplorationTest.java index bc0988e0665..d26dfbc7e58 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSubnetworksExplorationTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSubnetworksExplorationTest.java @@ -52,7 +52,13 @@ static void setUpClass() { private static Network createNetwork(String networkId, Country otherSubstationCountry) { Network n = NetworkTest1Factory.create(networkId); + Area area1 = n.newArea() + .setId(id("area1", networkId)) + .setName("AREA") + .setAreaType(id("areaType1", networkId)) + .add(); VoltageLevel voltageLevel1 = n.getVoltageLevel(id("voltageLevel1", networkId)); + voltageLevel1.addArea(area1); voltageLevel1.newBattery() .setId(id("battery1", networkId)) .setMaxP(20.0) @@ -277,6 +283,30 @@ public void testExploreCountries() { assertCollection(List.of(Country.FR, Country.BE, Country.DE), subnetwork2.getCountries()); } + @Test + public void testExploreAreaTypes() { + assertEquals(2, merged.getAreaTypeCount()); + assertEquals(1, subnetwork1.getAreaTypeCount()); + assertEquals(1, subnetwork2.getAreaTypeCount()); + String areaTypeId1 = id("areaType1", ID_1); + String areaTypeId2 = id("areaType1", ID_2); + assertCollection(List.of(areaTypeId1, areaTypeId2), merged.getAreaTypeStream().toList()); + assertCollection(List.of(areaTypeId1), subnetwork1.getAreaTypeStream().toList()); + assertCollection(List.of(areaTypeId2), subnetwork2.getAreaTypeStream().toList()); + } + + @Test + public void testExploreAreas() { + var expectedIdsForSubnetwork1 = List.of(id("area1", ID_1)); + var expectedIdsForSubnetwork2 = List.of(id("area1", ID_2)); + + testExploreElements(expectedIdsForSubnetwork1, expectedIdsForSubnetwork2, + Network::getAreas, + Network::getAreaStream, + Network::getAreaCount, + Network::getArea); + } + @Test public void testExploreSubstations() { String n1Substation1 = id("substation1", ID_1); diff --git a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java index f96b85864cf..b10ca53ebfa 100644 --- a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java +++ b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java @@ -18,6 +18,7 @@ public final class EurostagTutorialExample1Factory { private static final String VLGEN = "VLGEN"; + private static final String VLLOAD = "VLLOAD"; public static final String CASE_DATE = "2018-01-01T11:00:00+01:00"; public static final String DANGLING_LINE_XNODE1_1 = "NHV1_XNODE1"; public static final String DANGLING_LINE_XNODE1_2 = "XNODE1_NHV2"; @@ -29,6 +30,7 @@ public final class EurostagTutorialExample1Factory { public static final String NHV1_NHV2_2 = "NHV1_NHV2_2"; public static final String NGEN_NHV1 = "NGEN_NHV1"; public static final String NHV2_NLOAD = "NHV2_NLOAD"; + public static final String XNODE_1 = "XNODE1"; private EurostagTutorialExample1Factory() { } @@ -67,7 +69,7 @@ public static Network create(NetworkFactory networkFactory) { .setTopologyKind(TopologyKind.BUS_BREAKER) .add(); VoltageLevel vlload = p2.newVoltageLevel() - .setId("VLLOAD") + .setId(VLLOAD) .setNominalV(150.0) .setTopologyKind(TopologyKind.BUS_BREAKER) .add(); @@ -219,7 +221,7 @@ public static Network createWithTieLines(NetworkFactory networkFactory) { .setG(1E-6) .setB(386E-6 / 2) .setBus("NHV1") - .setPairingKey("XNODE1") + .setPairingKey(XNODE_1) .add(); DanglingLine xnode1nhv2 = network.getVoltageLevel(VLHV2).newDanglingLine() .setId(DANGLING_LINE_XNODE1_2) @@ -230,7 +232,7 @@ public static Network createWithTieLines(NetworkFactory networkFactory) { .setG(2E-6) .setB(386E-6 / 2) .setBus("NHV2") - .setPairingKey("XNODE1") + .setPairingKey(XNODE_1) .add(); network.newTieLine() .setId(NHV1_NHV2_1) @@ -878,4 +880,50 @@ public static Network createWithVoltageAngleLimit() { return network; } + + public static Network createWithTieLinesAndAreas() { + return createWithTieLinesAndAreas(NetworkFactory.findDefault()); + } + + public static Network createWithTieLinesAndAreas(NetworkFactory networkFactory) { + Network network = createWithTieLines(networkFactory); + + // createWithTieLines sets non-zero G for dangling lines, while the load flow solution included is for zero G. + // Here we set all DanglingLine's G to zero. Doing this the DanglingLine's P and Q at boundary side (calculated by iIDM) + // are consistent with included load flow results. In particular, flows of the 2 DanglingLines of a tie-line are consistent + // (verifying dl1.getBoundary().getP() ~= -1.0 * dl2.getBoundary().getP()) + network.getDanglingLineStream().forEach(dl -> dl.setG(0.0)); + + network.newArea() + .setId("ControlArea_A") + .setName("Control Area A") + .setAreaType("ControlArea") + .setAcInterchangeTarget(-602.6) + .addVoltageLevel(network.getVoltageLevel(VLGEN)) + .addVoltageLevel(network.getVoltageLevel(VLHV1)) + .addAreaBoundary(network.getDanglingLine(DANGLING_LINE_XNODE1_1).getBoundary(), true) + .addAreaBoundary(network.getDanglingLine(DANGLING_LINE_XNODE2_1).getBoundary(), true) + .add(); + network.newArea() + .setId("ControlArea_B") + .setName("Control Area B") + .setAreaType("ControlArea") + .setAcInterchangeTarget(+602.6) + .addVoltageLevel(network.getVoltageLevel(VLHV2)) + .addVoltageLevel(network.getVoltageLevel(VLLOAD)) + .addAreaBoundary(network.getDanglingLine(DANGLING_LINE_XNODE1_2).getBoundary(), true) + .addAreaBoundary(network.getDanglingLine(DANGLING_LINE_XNODE2_2).getBoundary(), true) + .add(); + network.newArea() + .setId("Region_AB") + .setName("Region AB") + .setAreaType("Region") + .addVoltageLevel(network.getVoltageLevel(VLGEN)) + .addVoltageLevel(network.getVoltageLevel(VLHV1)) + .addVoltageLevel(network.getVoltageLevel(VLHV2)) + .addVoltageLevel(network.getVoltageLevel(VLLOAD)) + .add(); + + return network; + } } From 4fd72a12aa841167f7be5c59c265859fae7f96fc Mon Sep 17 00:00:00 2001 From: Luma Date: Wed, 19 Jun 2024 13:12:54 +0200 Subject: [PATCH 05/57] CGMES export: handle quick CGM export for SSHs and SV (#2927) Signed-off-by: Luma Co-authored-by: Romain Courtier --- .../CreateMissingContainersPreProcessor.java | 6 +- .../powsybl/cgmes/conversion/CgmesExport.java | 546 +++++++++++---- .../conversion/export/CgmesExportContext.java | 205 +++--- .../conversion/export/CgmesExportUtil.java | 18 +- .../conversion/export/EquipmentExport.java | 11 +- .../export/StateVariablesExport.java | 10 +- .../export/SteadyStateHypothesisExport.java | 10 +- .../conversion/export/TopologyExport.java | 10 +- .../CgmesConformity1ConversionTest.java | 2 - .../CgmesConformity3ConversionTest.java | 1 - .../test/export/CgmesExportContextTest.java | 54 +- .../export/CommonGridModelExportTest.java | 629 ++++++++++++++++++ .../LegacyCommonGridModelExportTest.java | 36 +- .../test/export/StateVariablesExportTest.java | 33 +- .../SteadyStateHypothesisExportTest.java | 56 +- .../test/export/issues/ModelIdTest.java | 1 - .../CgmesMetadataModelsAdderImpl.java | 3 - docs/grid_exchange_formats/cgmes/export.md | 160 ++++- 18 files changed, 1385 insertions(+), 406 deletions(-) create mode 100644 cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java diff --git a/cgmes/cgmes-completion/src/main/java/com/powsybl/cgmes/completion/CreateMissingContainersPreProcessor.java b/cgmes/cgmes-completion/src/main/java/com/powsybl/cgmes/completion/CreateMissingContainersPreProcessor.java index 7ae59d69cb0..c813fe1c027 100644 --- a/cgmes/cgmes-completion/src/main/java/com/powsybl/cgmes/completion/CreateMissingContainersPreProcessor.java +++ b/cgmes/cgmes-completion/src/main/java/com/powsybl/cgmes/completion/CreateMissingContainersPreProcessor.java @@ -8,12 +8,14 @@ package com.powsybl.cgmes.completion; import com.google.auto.service.AutoService; +import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.CgmesImportPreProcessor; import com.powsybl.cgmes.conversion.export.CgmesExportContext; import com.powsybl.cgmes.conversion.export.CgmesExportUtil; import com.powsybl.cgmes.conversion.export.elements.*; import com.powsybl.cgmes.extensions.CgmesTopologyKind; import com.powsybl.cgmes.extensions.CimCharacteristicsAdder; +import com.powsybl.cgmes.model.CgmesMetadataModel; import com.powsybl.cgmes.model.CgmesModel; import com.powsybl.cgmes.model.CgmesNames; import com.powsybl.cgmes.model.CgmesSubset; @@ -220,7 +222,9 @@ private static void writeHeader(Network network, XMLStreamWriter writer, CgmesEx String euNamespace = context.getCim().getEuNamespace(); CgmesExportUtil.writeRdfRoot(cimNamespace, context.getCim().getEuPrefix(), euNamespace, writer); if (context.getCimVersion() >= 16) { - CgmesExportUtil.writeModelDescription(network, CgmesSubset.EQUIPMENT, writer, context.getExportedEQModel(), context); + CgmesMetadataModel eqModel = CgmesExport.initializeModelForExport( + network, CgmesSubset.EQUIPMENT, context, true, false); + CgmesExportUtil.writeModelDescription(network, CgmesSubset.EQUIPMENT, writer, eqModel, context); } } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java index e23f900d6dc..99280462817 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java @@ -12,6 +12,9 @@ import com.powsybl.cgmes.conversion.export.*; import com.powsybl.cgmes.conversion.naming.NamingStrategy; import com.powsybl.cgmes.conversion.naming.NamingStrategyFactory; +import com.powsybl.cgmes.extensions.CgmesMetadataModels; +import com.powsybl.cgmes.model.CgmesMetadataModel; +import com.powsybl.cgmes.model.CgmesMetadataModelImpl; import com.powsybl.cgmes.model.CgmesNamespace; import com.powsybl.cgmes.model.CgmesSubset; import com.powsybl.commons.config.PlatformConfig; @@ -33,6 +36,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.UncheckedIOException; +import java.time.ZonedDateTime; import java.util.*; import java.util.stream.Collectors; @@ -44,8 +48,6 @@ @AutoService(Exporter.class) public class CgmesExport implements Exporter { - private static final String INDENT = " "; - private final ParameterDefaultValueConfig defaultValueConfig; private final CgmesImport importer; @@ -64,128 +66,296 @@ public List getParameters() { return STATIC_PARAMETERS; } + /** + * Export the requested network to the CGMES format. + * @param network The network to export. + * @param parameters Optional parameters that influence the export. + * @param dataSource The dataSource used by the export. + * @param reportNode The reportNode used for functional logs. + */ @Override - public void export(Network network, Properties params, DataSource ds, ReportNode reportNode) { + public void export(Network network, Properties parameters, DataSource dataSource, ReportNode reportNode) { Objects.requireNonNull(network); - // Reference data (if required) will come from imported boundaries - // We may have received a sourcing actor as a parameter - String sourcingActorName = Parameter.readString(getFormat(), params, SOURCING_ACTOR_PARAMETER, defaultValueConfig); - String countryName = null; - if (sourcingActorName == null || sourcingActorName.isEmpty()) { - // If not given explicitly, - // the reference data provider can try to obtain it from the country of the network - // If we have multiple countries we do not pass this info to the reference data provider - Set countries = network.getSubstationStream() - .map(Substation::getCountry) - .flatMap(Optional::stream) - .map(Enum::name) - .collect(Collectors.toUnmodifiableSet()); - if (countries.size() == 1) { - countryName = countries.iterator().next(); - } + // Determine reference data (boundaries, base voltages and other sourcing references) for the export + String sourcingActorName = Parameter.readString(getFormat(), parameters, SOURCING_ACTOR_PARAMETER, defaultValueConfig); + String countryName = getCountry(network); + ReferenceDataProvider referenceDataProvider = new ReferenceDataProvider(sourcingActorName, countryName, importer, parameters); + + // Create the context (the object that stores relevant data for the export) + String namingStrategyImpl = Parameter.readString(getFormat(), parameters, NAMING_STRATEGY_PARAMETER, defaultValueConfig); + UUID uuidNamespace = UUID.fromString(Parameter.readString(getFormat(), parameters, UUID_NAMESPACE_PARAMETER, defaultValueConfig)); + NamingStrategy namingStrategy = NamingStrategyFactory.create(namingStrategyImpl, uuidNamespace); + CgmesExportContext context = new CgmesExportContext(network, referenceDataProvider, namingStrategy); + addParametersToContext(context, parameters, reportNode, referenceDataProvider); + + // Export the network + if (Parameter.readBoolean(getFormat(), parameters, CGM_EXPORT_PARAMETER, defaultValueConfig)) { + exportCGM(network, dataSource, context); + } else { + exportIGM(network, dataSource, context); } - ReferenceDataProvider referenceDataProvider = new ReferenceDataProvider(sourcingActorName, countryName, importer, params); - - // The UUID namespace parameter must be a valid UUID itself - UUID uuidNamespace = UUID.fromString(Parameter.readString(getFormat(), params, UUID_NAMESPACE_PARAMETER, defaultValueConfig)); - NamingStrategy namingStrategy = NamingStrategyFactory.create( - Parameter.readString(getFormat(), params, NAMING_STRATEGY_PARAMETER, defaultValueConfig), - uuidNamespace); - CgmesExportContext context = new CgmesExportContext(network, referenceDataProvider, namingStrategy) - .setExportBoundaryPowerFlows(Parameter.readBoolean(getFormat(), params, EXPORT_BOUNDARY_POWER_FLOWS_PARAMETER, defaultValueConfig)) - .setExportFlowsForSwitches(Parameter.readBoolean(getFormat(), params, EXPORT_POWER_FLOWS_FOR_SWITCHES_PARAMETER, defaultValueConfig)) - .setExportTransformersWithHighestVoltageAtEnd1(Parameter.readBoolean(getFormat(), params, EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1_PARAMETER, defaultValueConfig)) - .setExportLoadFlowStatus(Parameter.readBoolean(getFormat(), params, EXPORT_LOAD_FLOW_STATUS_PARAMETER, defaultValueConfig)) - .setMaxPMismatchConverged(Parameter.readDouble(getFormat(), params, MAX_P_MISMATCH_CONVERGED_PARAMETER, defaultValueConfig)) - .setMaxQMismatchConverged(Parameter.readDouble(getFormat(), params, MAX_Q_MISMATCH_CONVERGED_PARAMETER, defaultValueConfig)) - .setExportSvInjectionsForSlacks(Parameter.readBoolean(getFormat(), params, EXPORT_SV_INJECTIONS_FOR_SLACKS_PARAMETER, defaultValueConfig)) - .setEncodeIds(Parameter.readBoolean(getFormat(), params, ENCODE_IDS_PARAMETERS, defaultValueConfig)) - .setBoundaryEqId(getBoundaryId("EQ", network, params, BOUNDARY_EQ_ID_PARAMETER, referenceDataProvider)) - .setBoundaryTpId(getBoundaryId("TP", network, params, BOUNDARY_TP_ID_PARAMETER, referenceDataProvider)) - .setReportNode(reportNode) - .setBusinessProcess(Parameter.readString(getFormat(), params, BUSINESS_PROCESS_PARAMETER, defaultValueConfig)) - .setUpdateDependencies(Parameter.readBoolean(getFormat(), params, UPDATE_DEPENDENCIES_PARAMETER, defaultValueConfig)); + } - // If sourcing actor data has been found and the modeling authority set has not been specified explicitly, set it - String masUri = Parameter.readString(getFormat(), params, MODELING_AUTHORITY_SET_PARAMETER, defaultValueConfig); - PropertyBag sourcingActor = referenceDataProvider.getSourcingActor(); - if (sourcingActor.containsKey("masUri") && masUri.equals(CgmesExportContext.DEFAULT_MODELING_AUTHORITY_SET_VALUE)) { - masUri = sourcingActor.get("masUri"); + /** + * Common Grid Model export. + * This consists in providing an updated SSH for the IGMs (subnetworks) and an updated SV for the CGM (network). + * @param network The network to export. This is the parent network that contains the subnetworks. + * @param dataSource The dataSource used by the export. + * @param context The context that stores relevant data for the export. + */ + private void exportCGM(Network network, DataSource dataSource, CgmesExportContext context) { + checkCgmConsistency(network, context); + + // Initialize models for export. The original IGM TP and SSH don't get exported, + // but we need to init their models to retrieve their IDs when building the dependencies. + Map igmModels = new HashMap<>(); + for (Network subnetwork : network.getSubnetworks()) { + IgmModelsForCgm igmModelsForCgm = new IgmModelsForCgm( + initializeModelForExport(subnetwork, CgmesSubset.STEADY_STATE_HYPOTHESIS, context, false, false), + initializeModelForExport(subnetwork, CgmesSubset.STEADY_STATE_HYPOTHESIS, context, false, true), + initializeModelForExport(subnetwork, CgmesSubset.TOPOLOGY, context, false, false) + ); + igmModels.put(subnetwork, igmModelsForCgm); } - // Only update if masUri is not the default value - if (!masUri.equals(CgmesExportContext.DEFAULT_MODELING_AUTHORITY_SET_VALUE)) { - context.getExportedEQModel().setModelingAuthoritySet(masUri); - context.getExportedTPModel().setModelingAuthoritySet(masUri); - context.getExportedSSHModel().setModelingAuthoritySet(masUri); - context.getExportedSVModel().setModelingAuthoritySet(masUri); + CgmesMetadataModel updatedCgmSvModel = initializeModelForExport(network, CgmesSubset.STATE_VARIABLES, context, true, true); + + // Update dependencies + if (context.updateDependencies()) { + updateDependenciesCGM(igmModels.values(), updatedCgmSvModel); + } + + // Export the SSH for the IGMs and the SV for the CGM + String baseName = getBaseName(context, dataSource, network); + for (Network subnetwork : network.getSubnetworks()) { + context.addIidmMappings(subnetwork); + + String country = getCountry(subnetwork); + String igmName = country != null ? country : subnetwork.getNameOrId(); + String igmSshFileName = baseName + "_" + igmName + "_" + CgmesSubset.STEADY_STATE_HYPOTHESIS.getIdentifier() + ".xml"; + subsetExport(subnetwork, CgmesSubset.STEADY_STATE_HYPOTHESIS, igmSshFileName, dataSource, context, igmModels.get(subnetwork).updatedSsh); } - String modelDescription = Parameter.readString(getFormat(), params, MODEL_DESCRIPTION_PARAMETER, defaultValueConfig); - if (modelDescription != null) { - context.getExportedEQModel().setDescription(modelDescription); - context.getExportedTPModel().setDescription(modelDescription); - context.getExportedSSHModel().setDescription(modelDescription); - context.getExportedSVModel().setDescription(modelDescription); + String cgmSvFileName = baseName + "_" + CgmesSubset.STATE_VARIABLES.getIdentifier() + ".xml"; + subsetExport(network, CgmesSubset.STATE_VARIABLES, cgmSvFileName, dataSource, context, updatedCgmSvModel); + } + + /** + * Individual Grid Model export. + * This consists in providing the requested subsets among EQ, TP, SSH, SV. + * @param network The network to export. + * @param dataSource The dataSource used by the export. + * @param context The context that stores relevant data for the export. + */ + private void exportIGM(Network network, DataSource dataSource, CgmesExportContext context) { + List requestedSubsets = Arrays.stream(CgmesSubset.values()).filter(s -> context.getProfiles().contains(s.getIdentifier())).toList(); + checkIgmConsistency(requestedSubsets, network, context); + + // Init all exportable subsets (even the ones that don't get exported) + // in order to retrieve their IDs when building the dependencies + Map subsetModels = new EnumMap<>(CgmesSubset.class); + for (CgmesSubset exportableSubset : List.of( + CgmesSubset.EQUIPMENT, CgmesSubset.TOPOLOGY, CgmesSubset.STEADY_STATE_HYPOTHESIS, CgmesSubset.STATE_VARIABLES)) { + CgmesMetadataModel subsetModel = initializeModelForExport(network, exportableSubset, context, true, false); + subsetModels.put(exportableSubset, subsetModel); } - String cimVersionParam = Parameter.readString(getFormat(), params, CIM_VERSION_PARAMETER, defaultValueConfig); - if (cimVersionParam != null) { - context.setCimVersion(Integer.parseInt(cimVersionParam)); + + // Update dependencies + if (context.updateDependencies()) { + updateDependenciesIGM(subsetModels, context.getBoundaryEqId(), context.getBoundaryTpId()); } - String modelVersion = Parameter.readString(getFormat(), params, MODEL_VERSION_PARAMETER, defaultValueConfig); - if (modelVersion != null) { - context.getExportedEQModel().setVersion(Integer.parseInt(modelVersion)); - context.getExportedTPModel().setVersion(Integer.parseInt(modelVersion)); - context.getExportedSSHModel().setVersion(Integer.parseInt(modelVersion)); - context.getExportedSVModel().setVersion(Integer.parseInt(modelVersion)); + // Export requested subsets + context.setExportEquipment(requestedSubsets.contains(CgmesSubset.EQUIPMENT)); + String baseName = getBaseName(context, dataSource, network); + for (CgmesSubset subset : requestedSubsets) { + String fileName = baseName + "_" + subset.getIdentifier() + ".xml"; + subsetExport(network, subset, fileName, dataSource, context, subsetModels.get(subset)); } - // Export the file according to the profile - writeFiles(context, params, ds, network); + context.getNamingStrategy().debug(baseName, dataSource); } - private void writeFiles(CgmesExportContext context, Properties params, DataSource ds, Network network) { - String baseName = baseName(params, ds, network); - String filenameEq = baseName + "_EQ.xml"; - String filenameTp = baseName + "_TP.xml"; - String filenameSsh = baseName + "_SSH.xml"; - String filenameSv = baseName + "_SV.xml"; - - try { - List profiles = Parameter.readStringList(getFormat(), params, PROFILES_PARAMETER, defaultValueConfig); - checkConsistency(profiles, network, context); - if (profiles.contains("EQ")) { - try (OutputStream out = new BufferedOutputStream(ds.newOutputStream(filenameEq, false))) { - XMLStreamWriter writer = XmlUtil.initializeWriter(true, INDENT, out); - EquipmentExport.write(network, writer, context); - } - } else { - saveLegacyIdsFromPropertiesForSvDependencies(network, CgmesSubset.EQUIPMENT, context); - context.getExportedEQModel().setId(context.getNamingStrategy().getCgmesId(network)); - } - if (profiles.contains("TP")) { - try (OutputStream out = new BufferedOutputStream(ds.newOutputStream(filenameTp, false))) { - XMLStreamWriter writer = XmlUtil.initializeWriter(true, INDENT, out); - TopologyExport.write(network, writer, context); - } - } else { - saveLegacyIdsFromPropertiesForSvDependencies(network, CgmesSubset.TOPOLOGY, context); + /** + * Initialize the model (= the metadata information) that is used by the export. + * If existing, the network model extension is used for the initialization. + * If existing, optional parameters are also used for the initialization. + * If both are present, the optional parameters prevail the values in the network extension. + * @param network The network in which to look for an existing model extension as basis for initialization. + * @param subset The subset of the model to initialize. + * @param context The context used by the export. + * @param mainNetwork A boolean indicating whether the exported network is the main network (not a subnetwork). + * @param modelUpdate A boolean indicating whether the model version and ID should be updated. + * @return A model with all necessary metadata information for the export. + */ + public static CgmesMetadataModel initializeModelForExport( + Network network, CgmesSubset subset, CgmesExportContext context, boolean mainNetwork, boolean modelUpdate) { + // Initialize a new model for the export + CgmesMetadataModel modelForExport = new CgmesMetadataModelImpl(subset, CgmesExportContext.DEFAULT_MODELING_AUTHORITY_SET_VALUE); + modelForExport.setProfile(context.getCim().getProfileUri(subset.getIdentifier())); + + // If a model extension exists, use it as basis for the export + CgmesMetadataModels networkModels = network.getExtension(CgmesMetadataModels.class); + Optional networkSubsetModel = networkModels != null ? + networkModels.getModelForSubset(subset) : + Optional.empty(); + networkSubsetModel.ifPresent(m -> modelForExport.setId(m.getId())); + networkSubsetModel.ifPresent(m -> modelForExport.setDescription(m.getDescription())); + networkSubsetModel.ifPresent(m -> modelForExport.setVersion(m.getVersion())); + networkSubsetModel.ifPresent(m -> modelForExport.addDependentOn(m.getDependentOn())); + networkSubsetModel.ifPresent(m -> modelForExport.addSupersedes(m.getSupersedes())); + networkSubsetModel.ifPresent(m -> modelForExport.addProfiles(m.getProfiles())); + networkSubsetModel.ifPresent(m -> modelForExport.setModelingAuthoritySet(m.getModelingAuthoritySet())); + + // Use parameters if they have been defined. + // It doesn't apply to subnetworks, except for the version of updated SSH of a CGM export + if ((mainNetwork || modelUpdate) && context.getModelDescription() != null) { + modelForExport.setDescription(context.getModelDescription()); + } + if ((mainNetwork || modelUpdate) && context.getModelVersion() != null) { + modelForExport.setVersion(Integer.parseInt(context.getModelVersion())); + } + if (mainNetwork && context.getModelingAuthoritySet() != null) { + modelForExport.setModelingAuthoritySet(context.getModelingAuthoritySet()); + } + + // Initialize the model id in the case of an updated SSH/SV of a CGM export, or if it isn't present + if (modelUpdate || modelForExport.getId() == null) { + CgmesExportUtil.initializeModelId(network, modelForExport, context); + } + + return modelForExport; + } + + /** + * Update cross dependencies between the subset models through the dependentOn relationship. + * The IGMs updated SSH supersede the original ones. + * The CGM updated SV depends on the IGMs updated SSH and on the IGMs original TP. + * @param igmModels For each IGM: the original SSH model, the updated SSH model and the original TP model. + * @param updatedCgmSvModel The SV model for the CGM. + */ + private void updateDependenciesCGM(Collection igmModels, CgmesMetadataModel updatedCgmSvModel) { + // Each updated SSH model supersedes the original one + igmModels.forEach(m -> m.updatedSsh.clearDependencies()); + igmModels.forEach(m -> m.updatedSsh.addSupersedes(m.originalSsh.getId())); + + // Updated SV model depends on updated SSH models and original TP models + updatedCgmSvModel.addDependentOn(igmModels.stream().map(m -> m.updatedSsh.getId()).collect(Collectors.toSet())); + updatedCgmSvModel.addDependentOn(igmModels.stream().map(m -> m.originalTp.getId()).collect(Collectors.toSet())); + } + + /** + * Update cross dependencies between the subset models (including boundaries) through the dependentOn relationship. + * @param subsetModels The models for the following subsets: EQ, TP, SSH, SV. + * @param boundaryEqId The model id for the EQ_BD subset. + * @param boundaryTpId The model id for the TP_BD subset. + */ + private void updateDependenciesIGM(Map subsetModels, String boundaryEqId, String boundaryTpId) { + // Retrieve EQ model ID + String eqModelId = subsetModels.get(CgmesSubset.EQUIPMENT).getId(); + if (eqModelId == null || eqModelId.isEmpty()) { + return; + } + + // TP and SSH depend on EQ + subsetModels.get(CgmesSubset.TOPOLOGY).addDependentOn(eqModelId); + subsetModels.get(CgmesSubset.STEADY_STATE_HYPOTHESIS).addDependentOn(eqModelId); + + // SV depends on TP and SSH + subsetModels.get(CgmesSubset.STATE_VARIABLES) + .addDependentOn(subsetModels.get(CgmesSubset.TOPOLOGY).getId()) + .addDependentOn(subsetModels.get(CgmesSubset.STEADY_STATE_HYPOTHESIS).getId()); + + // EQ depends on EQ_BD (if present) + if (boundaryEqId != null) { + subsetModels.get(CgmesSubset.EQUIPMENT).addDependentOn(boundaryEqId); + } + + // SV depends on TP_BD (if present) + if (boundaryTpId != null) { + subsetModels.get(CgmesSubset.STATE_VARIABLES).addDependentOn(boundaryTpId); + } + } + + /** + * Check that the given network is consistent with a CGM export. + * @param network The network to export as a CGM. + * @param context The context used by the export. + */ + private void checkCgmConsistency(Network network, CgmesExportContext context) { + // Check that the network has subnetworks + if (network.getSubnetworks().size() < 2) { + LOG.error("Network {} must have at least 2 subnetworks for a CGM export.", network.getId()); + } + + // QoCDC, rule CgmSvSshVersionMismatch: SSHs and SV must have same scenario time + ZonedDateTime scenarioTime = network.getCaseDate(); + for (Network subnetwork : network.getSubnetworks()) { + if (!subnetwork.getCaseDate().equals(scenarioTime)) { + LOG.error("Parent network doesn't have the same scenarioTime as subnetwork: {}.", subnetwork.getId()); } - if (profiles.contains("SSH")) { - try (OutputStream out = new BufferedOutputStream(ds.newOutputStream(filenameSsh, false))) { - XMLStreamWriter writer = XmlUtil.initializeWriter(true, INDENT, out); - SteadyStateHypothesisExport.write(network, writer, context); - } - } else { - saveLegacyIdsFromPropertiesForSvDependencies(network, CgmesSubset.STEADY_STATE_HYPOTHESIS, context); + } + + // QoCDC, rule CgmSvSshVersionMismatch: SSHs and SV must have same version + // If version has been provided as a parameter, use it, otherwise take the max version of models and increment + if (context.getModelVersion() == null || context.getModelVersion().isEmpty()) { + // If there is no version number found in the input files getVersionNumber will return -1 + int currentVersion = getVersionNumber(network, CgmesSubset.STATE_VARIABLES); + for (Network subnetwork : network.getSubnetworks()) { + currentVersion = Math.max(getVersionNumber(subnetwork, CgmesSubset.STEADY_STATE_HYPOTHESIS), currentVersion); } - if (profiles.contains("SV")) { - try (OutputStream out = new BufferedOutputStream(ds.newOutputStream(filenameSv, false))) { - XMLStreamWriter writer = XmlUtil.initializeWriter(true, INDENT, out); - StateVariablesExport.write(network, writer, context); - } + // If no version number has been read, it is assumed everything was at version "1", so next version is "2" + // Although from QoCDC it is not clear if first version of IGMs should be "0" or "1" + int nextVersion = currentVersion >= 0 ? currentVersion + 1 : 2; + context.setModelVersion(String.valueOf(nextVersion)); + } + } + + /** + * Check that the given network is consistent with an IGM export for the requested subsets. + * @param requestedSubsets The subsets to export for that network. + * @param network The network to export as an IGM. + * @param context The context used by the export. + */ + private void checkIgmConsistency(List requestedSubsets, Network network, CgmesExportContext context) { + boolean networkIsNodeBreaker = network.getVoltageLevelStream() + .map(VoltageLevel::getTopologyKind) + .anyMatch(tk -> tk == TopologyKind.NODE_BREAKER); + if (networkIsNodeBreaker + && (requestedSubsets.contains(CgmesSubset.STEADY_STATE_HYPOTHESIS) || requestedSubsets.contains(CgmesSubset.STATE_VARIABLES)) + && !requestedSubsets.contains(CgmesSubset.TOPOLOGY)) { + inconsistentProfilesTPRequiredReport(context.getReportNode(), network.getId()); + LOG.error("Network {} contains node/breaker information. References to Topological Nodes in SSH/SV files will not be valid if TP is not exported.", network.getId()); + } + } + + /** + * Export a CGMES subset of a network. + * @param network The network whose subset is to be exported. + * @param subset The CGMES subset to export (accepted values are: EQ, TP, SSH, SV). + * @param fileName The name of the exported file. + * @param dataSource The data source used by the export. + * @param context The context used by the export. + * @param model The model (= metadata information) to use. + */ + private void subsetExport(Network network, CgmesSubset subset, String fileName, DataSource dataSource, CgmesExportContext context, CgmesMetadataModel model) { + try (OutputStream out = new BufferedOutputStream(dataSource.newOutputStream(fileName, false))) { + XMLStreamWriter writer = XmlUtil.initializeWriter(true, " ", out); + switch (subset) { + case EQUIPMENT: + EquipmentExport.write(network, writer, context, model); + break; + case TOPOLOGY: + TopologyExport.write(network, writer, context, model); + break; + case STEADY_STATE_HYPOTHESIS: + SteadyStateHypothesisExport.write(network, writer, context, model); + break; + case STATE_VARIABLES: + StateVariablesExport.write(network, writer, context, model); + break; + default: + throw new IllegalArgumentException("Invalid subset, one of the following value is expected: EQ/TP/SSH/SV."); } - context.getNamingStrategy().debug(baseName, ds); } catch (IOException e) { throw new UncheckedIOException(e); } catch (XMLStreamException e) { @@ -193,53 +363,142 @@ private void writeFiles(CgmesExportContext context, Properties params, DataSourc } } - private String getBoundaryId(String profile, Network network, Properties params, Parameter parameter, ReferenceDataProvider referenceDataProvider) { - if (network.hasProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + profile + "_BD_ID")) { - return network.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + profile + "_BD_ID"); + /** + * Get the version number of a network subset model + * @param network The network for which the subset model is looked for. + * @param subset The subset of the network to look for. + * @return The version number of a network subset model, -1 if no version is found + */ + private int getVersionNumber(Network network, CgmesSubset subset) { + // Retrieve model version + // In the case of a CGM export, the SSH subsets are updated and their version number is incremented + CgmesMetadataModels networkModels = network.getExtension(CgmesMetadataModels.class); + Optional networkSubsetModel = networkModels != null ? + networkModels.getModelForSubset(subset) : + Optional.empty(); + return networkSubsetModel.map(CgmesMetadataModel::getVersion).orElse(-1); + } + + /** + * Retrieve the country of a network if it's unique. + * @param network The network for which the country is being looked for. + * @return The network country if it's unique inside the network, else null. + */ + private static String getCountry(Network network) { + Set countries = network.getSubstationStream() + .map(Substation::getCountry) + .flatMap(Optional::stream) + .map(Enum::name) + .collect(Collectors.toUnmodifiableSet()); + + if (countries.size() == 1) { + return countries.iterator().next(); + } else { + return null; + } + } + + /** + * Read the parameters and store them as properties in the context that is used by the export. + * @param context The context that is used by the export. + * @param params The optional parameters to read and store. + * @param reportNode The reportNode used for functional logs. + * @param referenceDataProvider The reference data such as boundaries or base voltage. + */ + private void addParametersToContext(CgmesExportContext context, Properties params, ReportNode reportNode, ReferenceDataProvider referenceDataProvider) { + context.setExportBoundaryPowerFlows(Parameter.readBoolean(getFormat(), params, EXPORT_BOUNDARY_POWER_FLOWS_PARAMETER, defaultValueConfig)) + .setExportFlowsForSwitches(Parameter.readBoolean(getFormat(), params, EXPORT_POWER_FLOWS_FOR_SWITCHES_PARAMETER, defaultValueConfig)) + .setExportTransformersWithHighestVoltageAtEnd1(Parameter.readBoolean(getFormat(), params, EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1_PARAMETER, defaultValueConfig)) + .setExportLoadFlowStatus(Parameter.readBoolean(getFormat(), params, EXPORT_LOAD_FLOW_STATUS_PARAMETER, defaultValueConfig)) + .setMaxPMismatchConverged(Parameter.readDouble(getFormat(), params, MAX_P_MISMATCH_CONVERGED_PARAMETER, defaultValueConfig)) + .setMaxQMismatchConverged(Parameter.readDouble(getFormat(), params, MAX_Q_MISMATCH_CONVERGED_PARAMETER, defaultValueConfig)) + .setExportSvInjectionsForSlacks(Parameter.readBoolean(getFormat(), params, EXPORT_SV_INJECTIONS_FOR_SLACKS_PARAMETER, defaultValueConfig)) + .setEncodeIds(Parameter.readBoolean(getFormat(), params, ENCODE_IDS_PARAMETERS, defaultValueConfig)) + .setBusinessProcess(Parameter.readString(getFormat(), params, BUSINESS_PROCESS_PARAMETER, defaultValueConfig)) + .setModelDescription(Parameter.readString(getFormat(), params, MODEL_DESCRIPTION_PARAMETER, defaultValueConfig)) + .setModelVersion(Parameter.readString(getFormat(), params, MODEL_VERSION_PARAMETER, defaultValueConfig)) + .setModelingAuthoritySet(Parameter.readString(getFormat(), params, MODELING_AUTHORITY_SET_PARAMETER, defaultValueConfig)) + .setProfiles(Parameter.readStringList(getFormat(), params, PROFILES_PARAMETER, defaultValueConfig)) + .setBaseName(Parameter.readString(getFormat(), params, BASE_NAME_PARAMETER)) + .setReportNode(reportNode) + .setUpdateDependencies(Parameter.readBoolean(getFormat(), params, UPDATE_DEPENDENCIES_PARAMETER, defaultValueConfig)); + + // If sourcing actor data has been found and the modeling authority set has not been specified explicitly, set it + PropertyBag sourcingActor = referenceDataProvider.getSourcingActor(); + if (sourcingActor.containsKey("masUri") && context.getModelingAuthoritySet() == null) { + context.setModelingAuthoritySet(sourcingActor.get("masUri")); + } + + // Set CIM version + String cimVersion = Parameter.readString(getFormat(), params, CIM_VERSION_PARAMETER, defaultValueConfig); + if (cimVersion != null) { + context.setCimVersion(Integer.parseInt(cimVersion)); + } + + // Set boundaries + String boundaryEqId = getBoundaryId(CgmesSubset.EQUIPMENT_BOUNDARY, params, BOUNDARY_EQ_ID_PARAMETER, referenceDataProvider); + if (boundaryEqId != null && context.getBoundaryEqId() == null) { + context.setBoundaryEqId(boundaryEqId); + } + String boundaryTpId = getBoundaryId(CgmesSubset.TOPOLOGY_BOUNDARY, params, BOUNDARY_TP_ID_PARAMETER, referenceDataProvider); + if (boundaryTpId != null && context.getBoundaryTpId() == null) { + context.setBoundaryTpId(boundaryTpId); } + } + + /** + * Get the boundary id for the given subset, + * preferentially from the corresponding optional parameter, otherwise from the reference data. + * @param subset The boundary subset (EQ_BD or TP_BD) for which the id is being looked for. + * @param params The optional parameters set for that export. + * @param parameter The boundary ID parameter. + * @param referenceDataProvider The reference data such as boundaries or base voltage. + * @return If found, the id of the boundary subset, else null. + */ + private String getBoundaryId(CgmesSubset subset, Properties params, Parameter parameter, ReferenceDataProvider referenceDataProvider) { String id = Parameter.readString(getFormat(), params, parameter, defaultValueConfig); // If not specified through a parameter, try to load it from reference data if (id == null && referenceDataProvider != null) { - if ("EQ".equals(profile)) { + if (CgmesSubset.EQUIPMENT_BOUNDARY.equals(subset)) { id = referenceDataProvider.getEquipmentBoundaryId(); - } else if ("TP".equals(profile)) { + } else if (CgmesSubset.TOPOLOGY_BOUNDARY.equals(subset)) { id = referenceDataProvider.getTopologyBoundaryId(); } } return id; } - private static void saveLegacyIdsFromPropertiesForSvDependencies(Network network, CgmesSubset subset, CgmesExportContext context) { - String propertyName = String.format("%s%s_ID", - Conversion.CGMES_PREFIX_ALIAS_PROPERTIES, - subset.getIdentifier()); - List ids = network.getPropertyNames().stream() - .filter(p -> p.startsWith(propertyName)) - .map(network::getProperty) - .toList(); - context.setLegacyIdsForSvDependencies(subset, ids); - } - - private static void checkConsistency(List profiles, Network network, CgmesExportContext context) { - boolean networkIsNodeBreaker = network.getVoltageLevelStream() - .map(VoltageLevel::getTopologyKind) - .anyMatch(tk -> tk == TopologyKind.NODE_BREAKER); - if (networkIsNodeBreaker - && (profiles.contains("SSH") || profiles.contains("SV")) - && !profiles.contains("TP")) { - inconsistentProfilesTPRequiredReport(context.getReportNode(), network.getId()); - LOG.error("Network {} contains node/breaker information. References to Topological Nodes in SSH/SV files will not be valid if TP is not exported.", network.getId()); + /** + * Get the base name for the exported file. + * @param context The context used by the export. It may store an optional parameter for base name. + * @param dataSource The dataSoure used by the export. It may also contain the base name. + * @param network The exported network. If no base name has been provided, the network name or id is used. + * @return A base name for the exported file. + */ + private String getBaseName(CgmesExportContext context, DataSource dataSource, Network network) { + if (context.getBaseName() != null) { + return context.getBaseName(); + } else if (dataSource.getBaseName() != null && !dataSource.getBaseName().isEmpty()) { + return dataSource.getBaseName(); + } else { + return network.getNameOrId(); } } - private String baseName(Properties params, DataSource ds, Network network) { - String baseName = Parameter.readString(getFormat(), params, BASE_NAME_PARAMETER); - if (baseName != null) { - return baseName; - } else if (ds.getBaseName() != null && !ds.getBaseName().isEmpty()) { - return ds.getBaseName(); + /** + * A small class to manipulate models of an IGM + * when setting the relationships (dependOn, supersedes) between them in a CGM export. + */ + private static class IgmModelsForCgm { + CgmesMetadataModel originalSsh; + CgmesMetadataModel updatedSsh; + CgmesMetadataModel originalTp; + + public IgmModelsForCgm(CgmesMetadataModel originalSsh, CgmesMetadataModel updatedSsh, CgmesMetadataModel originalTp) { + this.originalSsh = originalSsh; + this.updatedSsh = updatedSsh; + this.originalTp = originalTp; } - return network.getNameOrId(); } @Override @@ -261,6 +520,7 @@ public String getFormat() { public static final String EXPORT_POWER_FLOWS_FOR_SWITCHES = "iidm.export.cgmes.export-power-flows-for-switches"; public static final String NAMING_STRATEGY = "iidm.export.cgmes.naming-strategy"; public static final String PROFILES = "iidm.export.cgmes.profiles"; + public static final String CGM_EXPORT = "iidm.export.cgmes.cgm_export"; public static final String MODELING_AUTHORITY_SET = "iidm.export.cgmes.modeling-authority-set"; public static final String MODEL_DESCRIPTION = "iidm.export.cgmes.model-description"; public static final String EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1 = "iidm.export.cgmes.export-transformers-with-highest-voltage-at-end1"; @@ -312,6 +572,11 @@ public String getFormat() { "Profiles to export", List.of("EQ", "TP", "SSH", "SV"), List.of("EQ", "TP", "SSH", "SV")); + private static final Parameter CGM_EXPORT_PARAMETER = new Parameter( + CGM_EXPORT, + ParameterType.BOOLEAN, + "True for a CGM export, False for an IGM export", + CgmesExportContext.CGM_EXPORT_VALUE); private static final Parameter BOUNDARY_EQ_ID_PARAMETER = new Parameter( BOUNDARY_EQ_ID, ParameterType.STRING, @@ -326,7 +591,7 @@ public String getFormat() { MODELING_AUTHORITY_SET, ParameterType.STRING, "Modeling authority set", - CgmesExportContext.DEFAULT_MODELING_AUTHORITY_SET_VALUE); + null); private static final Parameter MODEL_DESCRIPTION_PARAMETER = new Parameter( MODEL_DESCRIPTION, ParameterType.STRING, @@ -396,6 +661,7 @@ public String getFormat() { EXPORT_POWER_FLOWS_FOR_SWITCHES_PARAMETER, NAMING_STRATEGY_PARAMETER, PROFILES_PARAMETER, + CGM_EXPORT_PARAMETER, BOUNDARY_EQ_ID_PARAMETER, BOUNDARY_TP_ID_PARAMETER, MODELING_AUTHORITY_SET_PARAMETER, diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java index ce2b52d5635..2af33a231dc 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java @@ -51,22 +51,23 @@ public class CgmesExportContext { private static final String REGION_NAME = "regionName"; private static final String DEFAULT_REGION = "default region"; public static final String SUB_REGION_ID = "subRegionId"; + private static final String BOUNDARY_EQ_ID_PROPERTY = Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "EQ_BD_ID"; + private static final String BOUNDARY_TP_ID_PROPERTY = Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "TP_BD_ID"; private CgmesNamespace.Cim cim = CgmesNamespace.CIM_16; private CgmesTopologyKind topologyKind = CgmesTopologyKind.BUS_BRANCH; private ZonedDateTime scenarioTime = ZonedDateTime.now(); private ReportNode reportNode = ReportNode.NO_OP; - private String boundaryEqId; // may be null - private String boundaryTpId; // may be null private String businessProcess = DEFAULT_BUSINESS_PROCESS; - - private final CgmesMetadataModel exportedEQModel = new CgmesMetadataModelImpl(CgmesSubset.EQUIPMENT, DEFAULT_MODELING_AUTHORITY_SET_VALUE); - private final CgmesMetadataModel exportedTPModel = new CgmesMetadataModelImpl(CgmesSubset.TOPOLOGY, DEFAULT_MODELING_AUTHORITY_SET_VALUE); - private final CgmesMetadataModel exportedSVModel = new CgmesMetadataModelImpl(CgmesSubset.STATE_VARIABLES, DEFAULT_MODELING_AUTHORITY_SET_VALUE); - private final CgmesMetadataModel exportedSSHModel = new CgmesMetadataModelImpl(CgmesSubset.STEADY_STATE_HYPOTHESIS, DEFAULT_MODELING_AUTHORITY_SET_VALUE); - private NamingStrategy namingStrategy = new NamingStrategy.Identity(); - + private String modelingAuthoritySet = null; + private String modelDescription = null; + private String modelVersion = null; + private String boundaryEqId = null; + private String boundaryTpId = null; + private List profiles = null; + private String baseName = null; + public static final boolean CGM_EXPORT_VALUE = false; public static final boolean EXPORT_BOUNDARY_POWER_FLOWS_DEFAULT_VALUE = true; public static final boolean EXPORT_POWER_FLOWS_FOR_SWITCHES_DEFAULT_VALUE = true; public static final boolean EXPORT_TRANSFORMERS_WITH_HIGHEST_VOLTAGE_AT_END1_DEFAULT_VALUE = false; @@ -99,59 +100,6 @@ public class CgmesExportContext { private final Map fictitiousContainers = new HashMap<>(); private final Map topologicalNodes = new HashMap<>(); private final ReferenceDataProvider referenceDataProvider; - private final EnumMap> legacyIdsForSvDependencies = new EnumMap<>(CgmesSubset.class); - - /** - * Update dependencies in a way that: - * SV depends on TP and SSH - * TP depends on EQ - * SSH depends on EQ - * If the boundaries subset have been defined: - * EQ depends on EQ_BD - * SV depends on TP_BD - */ - public void updateDependencies() { - if (!updateDependencies) { - return; - } - String eqModelId = getExportedEQModel().getId(); - if (eqModelId == null || eqModelId.isEmpty()) { - return; - } - - getExportedTPModel() - .clearDependencies() - .addDependentOn(eqModelId); - - getExportedSSHModel() - .clearDependencies() - .addDependentOn(eqModelId); - - getExportedSVModel().clearDependencies(); - List tpIds = legacyIdsForSvDependencies.get(CgmesSubset.TOPOLOGY); - if (tpIds != null) { - // If the list of SV dependencies from TP files has been set, even if it is empty, - // use it and ignore the exported TP model - getExportedSVModel().addDependentOn(tpIds); - } else { - getExportedSVModel().addDependentOn(getExportedTPModel().getId()); - } - List sshIds = legacyIdsForSvDependencies.get(CgmesSubset.STEADY_STATE_HYPOTHESIS); - if (sshIds != null) { - // If the list of SV dependencies from SSH files has been set, even if it is empty, - // use it and ignore the exported SSH model - getExportedSVModel().addDependentOn(sshIds); - } else { - getExportedSVModel().addDependentOn(getExportedSSHModel().getId()); - } - - if (boundaryEqId != null) { - getExportedEQModel().addDependentOn(boundaryEqId); - } - if (boundaryTpId != null) { - getExportedSVModel().addDependentOn(boundaryTpId); - } - } public String getFictitiousContainerFor(Identifiable id) { return fictitiousContainers.get(id.getId()); @@ -162,7 +110,6 @@ public void setFictitiousContainerFor(Identifiable id, String containerId) { } public CgmesExportContext() { - initializeExportedModelProfiles(this.cim); referenceDataProvider = null; } @@ -183,7 +130,6 @@ public CgmesExportContext(Network network, ReferenceDataProvider referenceDataPr } public CgmesExportContext(Network network, ReferenceDataProvider referenceDataProvider, NamingStrategy namingStrategy) { - initializeExportedModelProfiles(this.cim); this.referenceDataProvider = referenceDataProvider; this.namingStrategy = namingStrategy; CimCharacteristics cimCharacteristics = network.getExtension(CimCharacteristics.class); @@ -194,41 +140,14 @@ public CgmesExportContext(Network network, ReferenceDataProvider referenceDataPr topologyKind = networkTopologyKind(network); } scenarioTime = network.getCaseDate(); - CgmesMetadataModels models = network.getExtension(CgmesMetadataModels.class); - if (models != null) { - models.getModelForSubset(CgmesSubset.EQUIPMENT).ifPresent(eq -> prepareExportedModelFrom(exportedEQModel, eq)); - models.getModelForSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS).ifPresent(ssh -> prepareExportedModelFrom(exportedSSHModel, ssh)); - models.getModelForSubset(CgmesSubset.TOPOLOGY).ifPresent(tp -> prepareExportedModelFrom(exportedTPModel, tp)); - models.getModelForSubset(CgmesSubset.STATE_VARIABLES).ifPresent(sv -> prepareExportedModelFrom(exportedSVModel, sv)); - } addIidmMappings(network); - } - /** - * Set for each exported model the profile relative to the cim version. - * @param cim The cim version from which depends the models profiles uri. - */ - private void initializeExportedModelProfiles(CgmesNamespace.Cim cim) { - if (cim.hasProfiles()) { - exportedEQModel.setProfile(cim.getProfileUri("EQ")); - exportedTPModel.setProfile(cim.getProfileUri("TP")); - exportedSVModel.setProfile(cim.getProfileUri("SV")); - exportedSSHModel.setProfile(cim.getProfileUri("SSH")); + if (network.hasProperty(BOUNDARY_EQ_ID_PROPERTY)) { + setBoundaryEqId(network.getProperty(BOUNDARY_EQ_ID_PROPERTY)); + } + if (network.hasProperty(BOUNDARY_TP_ID_PROPERTY)) { + setBoundaryTpId(network.getProperty(BOUNDARY_TP_ID_PROPERTY)); } - } - - /** - * Update the model used for the export according to the network model. - * All the metadata information will be duplicated, except for the version that will be incremented. - * @param exportedModel The {@link CgmesMetadataModel} used for the export. - * @param fromModel The {@link CgmesMetadataModel} attached to the network. - */ - private void prepareExportedModelFrom(CgmesMetadataModel exportedModel, CgmesMetadataModel fromModel) { - exportedModel.setDescription(fromModel.getDescription()); - exportedModel.setVersion(fromModel.getVersion() + 1); - exportedModel.addSupersedes(fromModel.getId()); - exportedModel.addDependentOn(fromModel.getDependentOn()); - exportedModel.setModelingAuthoritySet(fromModel.getModelingAuthoritySet()); } private CgmesTopologyKind networkTopologyKind(Network network) { @@ -367,16 +286,6 @@ public boolean isExportedEquipment(Identifiable c) { return !ignored; } - public CgmesExportContext setBoundaryEqId(String boundaryEqId) { - this.boundaryEqId = boundaryEqId; - return this; - } - - public CgmesExportContext setBoundaryTpId(String boundaryTpId) { - this.boundaryTpId = boundaryTpId; - return this; - } - private void addIidmMappingsSwitchTerminals(Network network) { for (Switch sw : network.getSwitches()) { String terminal1Id = sw.getAliasFromType(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + CgmesNames.TERMINAL + "1").orElse(null); @@ -637,7 +546,6 @@ public int getCimVersion() { public CgmesExportContext setCimVersion(int cimVersion) { cim = CgmesNamespace.getCim(cimVersion); - initializeExportedModelProfiles(cim); return this; } @@ -659,22 +567,6 @@ public CgmesExportContext setScenarioTime(ZonedDateTime scenarioTime) { return this; } - public CgmesMetadataModel getExportedEQModel() { - return exportedEQModel; - } - - public CgmesMetadataModel getExportedTPModel() { - return exportedTPModel; - } - - public CgmesMetadataModel getExportedSVModel() { - return exportedSVModel; - } - - public CgmesMetadataModel getExportedSSHModel() { - return exportedSSHModel; - } - public boolean exportBoundaryPowerFlows() { return exportBoundaryPowerFlows; } @@ -823,13 +715,76 @@ public CgmesExportContext setBusinessProcess(String businessProcess) { return this; } - public void setLegacyIdsForSvDependencies(CgmesSubset subset, List ids) { - legacyIdsForSvDependencies.put(subset, ids); + public String getModelingAuthoritySet() { + return modelingAuthoritySet; + } + + public CgmesExportContext setModelingAuthoritySet(String modelingAuthoritySet) { + this.modelingAuthoritySet = modelingAuthoritySet; + return this; + } + + public String getModelDescription() { + return modelDescription; + } + + public CgmesExportContext setModelDescription(String modelDescription) { + this.modelDescription = modelDescription; + return this; + } + + public String getModelVersion() { + return modelVersion; + } + + public CgmesExportContext setModelVersion(String modelVersion) { + this.modelVersion = modelVersion; + return this; + } + + public String getBoundaryEqId() { + return boundaryEqId; + } + + public CgmesExportContext setBoundaryEqId(String boundaryEqId) { + this.boundaryEqId = boundaryEqId; + return this; + } + + public String getBoundaryTpId() { + return boundaryTpId; + } + + public CgmesExportContext setBoundaryTpId(String boundaryTpId) { + this.boundaryTpId = boundaryTpId; + return this; + } + + public List getProfiles() { + return profiles; + } + + public CgmesExportContext setProfiles(List profiles) { + this.profiles = profiles; + return this; + } + + public String getBaseName() { + return baseName; + } + + public CgmesExportContext setBaseName(String baseName) { + this.baseName = baseName; + return this; } public CgmesExportContext setUpdateDependencies(boolean updateDependencies) { this.updateDependencies = updateDependencies; return this; } + + public boolean updateDependencies() { + return updateDependencies; + } } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java index 57a7fa7c58b..7507fc001f5 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java @@ -116,24 +116,28 @@ public static void writeRdfRoot(String cimNamespace, String euPrefix, String euN writer.writeNamespace("md", MD_NAMESPACE); } - public static void writeModelDescription(Network network, CgmesSubset subset, XMLStreamWriter writer, CgmesMetadataModel modelDescription, CgmesExportContext context) throws XMLStreamException { + public static void initializeModelId(Network network, CgmesMetadataModel model, CgmesExportContext context) { // The ref to build a unique model id must contain: // the network, the subset (EQ, SSH, SV, ...), the time of the scenario, the version, the business process and the FULL_MODEL part // If we use name-based UUIDs this ensures that the UUID for the model will be specific enough CgmesObjectReference[] modelRef = { refTyped(network), - ref(subset), + ref(model.getSubset()), ref(DATE_TIME_FORMATTER.format(context.getScenarioTime())), - ref(format(modelDescription.getVersion())), + ref(String.valueOf(model.getVersion())), ref(context.getBusinessProcess()), Part.FULL_MODEL}; String modelId = "urn:uuid:" + context.getNamingStrategy().getCgmesId(modelRef); - modelDescription.setId(modelId); - context.updateDependencies(); + model.setId(modelId); + } + public static void writeModelDescription(Network network, CgmesSubset subset, XMLStreamWriter writer, CgmesMetadataModel modelDescription, CgmesExportContext context) throws XMLStreamException { + if (modelDescription.getId() == null || modelDescription.getId().isEmpty()) { + initializeModelId(network, modelDescription, context); + } writer.writeStartElement(MD_NAMESPACE, "FullModel"); - writer.writeAttribute(RDF_NAMESPACE, CgmesNames.ABOUT, modelId); - context.getReportNode().newReportNode().withMessageTemplate("CgmesId", modelId).add(); + writer.writeAttribute(RDF_NAMESPACE, CgmesNames.ABOUT, modelDescription.getId()); + context.getReportNode().newReportNode().withMessageTemplate("CgmesId", modelDescription.getId()).add(); writer.writeStartElement(MD_NAMESPACE, CgmesNames.SCENARIO_TIME); writer.writeCharacters(DATE_TIME_FORMATTER.format(context.getScenarioTime())); writer.writeEndElement(); diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java index bb2ed504d6f..2355ac9b16c 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java @@ -7,11 +7,13 @@ */ package com.powsybl.cgmes.conversion.export; +import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.Conversion; import com.powsybl.cgmes.conversion.naming.CgmesObjectReference; import com.powsybl.cgmes.conversion.naming.NamingStrategy; import com.powsybl.cgmes.conversion.export.elements.*; import com.powsybl.cgmes.extensions.*; +import com.powsybl.cgmes.model.CgmesMetadataModel; import com.powsybl.cgmes.model.CgmesNames; import com.powsybl.cgmes.model.CgmesSubset; import com.powsybl.commons.PowsyblException; @@ -54,7 +56,12 @@ public static void write(Network network, XMLStreamWriter writer) { } public static void write(Network network, XMLStreamWriter writer, CgmesExportContext context) { - context.setExportEquipment(true); + CgmesMetadataModel model = CgmesExport.initializeModelForExport( + network, CgmesSubset.EQUIPMENT, context, true, false); + write(network, writer, context, model); + } + + public static void write(Network network, XMLStreamWriter writer, CgmesExportContext context, CgmesMetadataModel model) { try { boolean writeConnectivityNodes = context.writeConnectivityNodes(); @@ -68,7 +75,7 @@ public static void write(Network network, XMLStreamWriter writer, CgmesExportCon CgmesExportUtil.writeRdfRoot(cimNamespace, context.getCim().getEuPrefix(), euNamespace, writer); if (context.getCimVersion() >= 16) { - CgmesExportUtil.writeModelDescription(network, CgmesSubset.EQUIPMENT, writer, context.getExportedEQModel(), context); + CgmesExportUtil.writeModelDescription(network, CgmesSubset.EQUIPMENT, writer, model, context); } Map mapNodeKey2NodeId = new HashMap<>(); diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/StateVariablesExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/StateVariablesExport.java index e4692225ccc..d6418aa9790 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/StateVariablesExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/StateVariablesExport.java @@ -7,9 +7,11 @@ */ package com.powsybl.cgmes.conversion.export; +import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.Conversion; import com.powsybl.cgmes.extensions.CgmesTapChanger; import com.powsybl.cgmes.extensions.CgmesTapChangers; +import com.powsybl.cgmes.model.CgmesMetadataModel; import com.powsybl.cgmes.model.CgmesNames; import com.powsybl.cgmes.model.CgmesSubset; import com.powsybl.commons.PowsyblException; @@ -47,12 +49,18 @@ public static void write(Network network, XMLStreamWriter writer) { } public static void write(Network network, XMLStreamWriter writer, CgmesExportContext context) { + CgmesMetadataModel model = CgmesExport.initializeModelForExport( + network, CgmesSubset.STATE_VARIABLES, context, true, false); + write(network, writer, context, model); + } + + public static void write(Network network, XMLStreamWriter writer, CgmesExportContext context, CgmesMetadataModel model) { try { String cimNamespace = context.getCim().getNamespace(); CgmesExportUtil.writeRdfRoot(cimNamespace, context.getCim().getEuPrefix(), context.getCim().getEuNamespace(), writer); if (context.getCimVersion() >= 16) { - CgmesExportUtil.writeModelDescription(network, CgmesSubset.STATE_VARIABLES, writer, context.getExportedSVModel(), context); + CgmesExportUtil.writeModelDescription(network, CgmesSubset.STATE_VARIABLES, writer, model, context); writeTopologicalIslands(network, context, writer); // Note: unmapped topological nodes (node breaker) & boundary topological nodes are not written in topological islands } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java index f303508479e..b91825b82f6 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java @@ -7,11 +7,13 @@ */ package com.powsybl.cgmes.conversion.export; +import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.Conversion; import com.powsybl.cgmes.extensions.CgmesControlArea; import com.powsybl.cgmes.extensions.CgmesControlAreas; import com.powsybl.cgmes.extensions.CgmesTapChanger; import com.powsybl.cgmes.extensions.CgmesTapChangers; +import com.powsybl.cgmes.model.CgmesMetadataModel; import com.powsybl.cgmes.model.CgmesNames; import com.powsybl.cgmes.model.CgmesSubset; import com.powsybl.commons.PowsyblException; @@ -46,6 +48,12 @@ private SteadyStateHypothesisExport() { } public static void write(Network network, XMLStreamWriter writer, CgmesExportContext context) { + CgmesMetadataModel model = CgmesExport.initializeModelForExport( + network, CgmesSubset.STEADY_STATE_HYPOTHESIS, context, true, false); + write(network, writer, context, model); + } + + public static void write(Network network, XMLStreamWriter writer, CgmesExportContext context, CgmesMetadataModel model) { final Map> regulatingControlViews = new HashMap<>(); String cimNamespace = context.getCim().getNamespace(); @@ -53,7 +61,7 @@ public static void write(Network network, XMLStreamWriter writer, CgmesExportCon CgmesExportUtil.writeRdfRoot(cimNamespace, context.getCim().getEuPrefix(), context.getCim().getEuNamespace(), writer); if (context.getCimVersion() >= 16) { - CgmesExportUtil.writeModelDescription(network, CgmesSubset.STEADY_STATE_HYPOTHESIS, writer, context.getExportedSSHModel(), context); + CgmesExportUtil.writeModelDescription(network, CgmesSubset.STEADY_STATE_HYPOTHESIS, writer, model, context); } writeLoads(network, cimNamespace, writer, context); diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/TopologyExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/TopologyExport.java index 0709a7f95dd..2c419f813c4 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/TopologyExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/TopologyExport.java @@ -7,7 +7,9 @@ */ package com.powsybl.cgmes.conversion.export; +import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.Conversion; +import com.powsybl.cgmes.model.CgmesMetadataModel; import com.powsybl.cgmes.model.CgmesNames; import com.powsybl.cgmes.model.CgmesSubset; import com.powsybl.commons.PowsyblException; @@ -40,12 +42,18 @@ public static void write(Network network, XMLStreamWriter writer) { } public static void write(Network network, XMLStreamWriter writer, CgmesExportContext context) { + CgmesMetadataModel model = CgmesExport.initializeModelForExport( + network, CgmesSubset.TOPOLOGY, context, true, false); + write(network, writer, context, model); + } + + public static void write(Network network, XMLStreamWriter writer, CgmesExportContext context, CgmesMetadataModel model) { try { String cimNamespace = context.getCim().getNamespace(); CgmesExportUtil.writeRdfRoot(cimNamespace, context.getCim().getEuPrefix(), context.getCim().getEuNamespace(), writer); if (context.getCimVersion() >= 16) { - CgmesExportUtil.writeModelDescription(network, CgmesSubset.TOPOLOGY, writer, context.getExportedTPModel(), context); + CgmesExportUtil.writeModelDescription(network, CgmesSubset.TOPOLOGY, writer, model, context); } writeTopologicalNodes(network, cimNamespace, writer, context); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/CgmesConformity1ConversionTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/CgmesConformity1ConversionTest.java index 0b968bbe2c7..b2dd05d13cb 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/CgmesConformity1ConversionTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/CgmesConformity1ConversionTest.java @@ -102,7 +102,6 @@ void microGridBaseCaseBERoundtripBoundary() throws IOException { new ComparisonConfig() .tolerance(1e-5) .checkNetworkId(false) - .incrementVersions(true) .exportedSubset(Set.of(CgmesSubset.STEADY_STATE_HYPOTHESIS, CgmesSubset.STATE_VARIABLES))); t.setTestExportImportCgmes(true); Network expected = null; @@ -121,7 +120,6 @@ void microGridBaseCaseBERoundtrip() throws IOException { new ComparisonConfig() .tolerance(1e-5) .checkNetworkId(false) - .incrementVersions(true) .exportedSubset(Set.of(CgmesSubset.STEADY_STATE_HYPOTHESIS, CgmesSubset.STATE_VARIABLES))); t.setTestExportImportCgmes(true); t.testConversion(CgmesConformity1NetworkCatalog.microBaseCaseBE(), CgmesConformity1Catalog.microGridBaseCaseBE()); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/CgmesConformity3ConversionTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/CgmesConformity3ConversionTest.java index 54c2af65bdf..27649af974c 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/CgmesConformity3ConversionTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/CgmesConformity3ConversionTest.java @@ -106,7 +106,6 @@ void microGridBaseCaseAssembledSeparatingByModelingAuthority() { private void checkExportSvTerminals(Network network) { CgmesExportContext context = new CgmesExportContext(network); - context.getExportedSVModel().setVersion(2); context.setExportBoundaryPowerFlows(true); context.setExportFlowsForSwitches(true); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportContextTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportContextTest.java index 12a1aa3c466..c58a3ee447c 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportContextTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportContextTest.java @@ -9,20 +9,15 @@ import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.export.CgmesExportContext; -import com.powsybl.cgmes.extensions.CgmesMetadataModelsAdder; import com.powsybl.cgmes.extensions.CgmesTopologyKind; import com.powsybl.cgmes.extensions.CimCharacteristicsAdder; import com.powsybl.cgmes.model.CgmesNamespace; -import com.powsybl.cgmes.model.CgmesSubset; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import java.time.ZonedDateTime; import java.time.Duration; import org.junit.jupiter.api.Test; -import java.util.Arrays; -import java.util.List; - import static org.junit.jupiter.api.Assertions.*; /** @@ -34,7 +29,7 @@ class CgmesExportContextTest { void testExporter() { var exporter = new CgmesExport(); assertEquals("ENTSO-E CGMES version 2.4.15", exporter.getComment()); - assertEquals(20, exporter.getParameters().size()); + assertEquals(21, exporter.getParameters().size()); } @Test @@ -47,29 +42,12 @@ void networkConstructor() { assertEquals(CgmesNamespace.CIM_16_NAMESPACE, context1.getCim().getNamespace()); assertEquals(CgmesTopologyKind.BUS_BRANCH, context1.getTopologyKind()); assertEquals(network.getCaseDate(), context1.getScenarioTime()); - assertEquals("SV Model", context1.getExportedSVModel().getDescription()); - assertEquals(1, context1.getExportedSVModel().getVersion()); - assertTrue(context1.getExportedSVModel().getDependentOn().isEmpty()); - assertEquals("powsybl.org", context1.getExportedSVModel().getModelingAuthoritySet()); - assertEquals(1, context1.getExportedEQModel().getVersion()); assertEquals("1D", context1.getBusinessProcess()); network.newExtension(CimCharacteristicsAdder.class) .setCimVersion(14) .setTopologyKind(CgmesTopologyKind.NODE_BREAKER) .add(); - network.newExtension(CgmesMetadataModelsAdder.class) - .newModel() - .setId("testId") - .setSubset(CgmesSubset.STATE_VARIABLES) - .setDescription("test") - .setVersion(2) - .addProfile("testProfile") - .addDependentOn("otherModel1") - .addDependentOn("otherModel2") - .setModelingAuthoritySet("cgmes.org") - .add() - .add(); CgmesExportContext context2 = new CgmesExportContext(network); @@ -77,12 +55,6 @@ void networkConstructor() { assertEquals(CgmesNamespace.CIM_14_NAMESPACE, context2.getCim().getNamespace()); assertEquals(CgmesTopologyKind.NODE_BREAKER, context2.getTopologyKind()); assertEquals(network.getCaseDate(), context2.getScenarioTime()); - assertEquals("test", context2.getExportedSVModel().getDescription()); - assertEquals(3, context2.getExportedSVModel().getVersion()); - assertEquals(2, context2.getExportedSVModel().getDependentOn().size()); - assertTrue(context2.getExportedSVModel().getDependentOn().contains("otherModel1")); - assertTrue(context2.getExportedSVModel().getDependentOn().contains("otherModel2")); - assertEquals("cgmes.org", context2.getExportedSVModel().getModelingAuthoritySet()); } @Test @@ -92,10 +64,6 @@ void emptyConstructor() { assertEquals(CgmesNamespace.CIM_16_NAMESPACE, context.getCim().getNamespace()); assertEquals(CgmesTopologyKind.BUS_BRANCH, context.getTopologyKind()); assertTrue(Duration.between(ZonedDateTime.now(), context.getScenarioTime()).toMinutes() < 1); - assertEquals("SV Model", context.getExportedSVModel().getDescription()); - assertEquals(1, context.getExportedSVModel().getVersion()); - assertTrue(context.getExportedSVModel().getDependentOn().isEmpty()); - assertEquals("powsybl.org", context.getExportedSVModel().getModelingAuthoritySet()); assertTrue(context.exportBoundaryPowerFlows()); assertEquals("1D", context.getBusinessProcess()); } @@ -109,32 +77,12 @@ void getSet() { .setExportBoundaryPowerFlows(true) .setExportFlowsForSwitches(false) .setBusinessProcess("2D"); - context.getExportedSVModel() - .setDescription("test") - .setVersion(2) - .addDependentOn("powsybl.test.org") - .addDependentOn("cgmes") - .setModelingAuthoritySet("cgmes.org"); assertEquals(14, context.getCimVersion()); assertEquals(CgmesNamespace.CIM_14_NAMESPACE, context.getCim().getNamespace()); assertEquals(CgmesTopologyKind.NODE_BREAKER, context.getTopologyKind()); assertEquals(ZonedDateTime.parse("2020-09-22T17:21:11.381+02:00"), context.getScenarioTime()); - assertEquals("test", context.getExportedSVModel().getDescription()); - assertEquals(2, context.getExportedSVModel().getVersion()); - assertEquals(2, context.getExportedSVModel().getDependentOn().size()); - assertTrue(context.getExportedSVModel().getDependentOn().contains("powsybl.test.org")); - assertTrue(context.getExportedSVModel().getDependentOn().contains("cgmes")); - assertEquals("cgmes.org", context.getExportedSVModel().getModelingAuthoritySet()); assertTrue(context.exportBoundaryPowerFlows()); assertEquals("2D", context.getBusinessProcess()); - - List dependencies = Arrays.asList("test1", "test2", "test3"); - context.getExportedSVModel().addDependentOn(dependencies); - assertEquals(5, context.getExportedSVModel().getDependentOn().size()); - assertTrue(context.getExportedSVModel().getDependentOn().containsAll(dependencies)); - - context.getExportedSVModel().clearDependencies(); - assertTrue(context.getExportedSVModel().getDependentOn().isEmpty()); } } diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java new file mode 100644 index 00000000000..f1c577d282f --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java @@ -0,0 +1,629 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.cgmes.conversion.test.export; + +import com.powsybl.cgmes.conformity.CgmesConformity1Catalog; +import com.powsybl.cgmes.conversion.CgmesExport; +import com.powsybl.cgmes.extensions.CgmesMetadataModels; +import com.powsybl.cgmes.extensions.CgmesMetadataModelsAdder; +import com.powsybl.cgmes.model.CgmesMetadataModel; +import com.powsybl.cgmes.model.CgmesSubset; +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.datasource.DataSource; +import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.MemDataSource; +import com.powsybl.commons.datasource.ReadOnlyDataSource; +import com.powsybl.commons.test.AbstractSerDeTest; +import com.powsybl.iidm.network.*; +import org.junit.jupiter.api.Test; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.ZonedDateTime; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Summary from CGM Building Process Implementation Guide: + * A CGM is created by assembling a set of IGMs for the same scenarioTime. + * A Merging Agent is responsible for building the CGM. + * The IGMs are first validated for plausibility by solving a power flow. + * The CGM is then assembled from IGMs and a power flow is solved, + * potentially adjusting some of the IGMs power flow hypothesis. + * The Merging Agent provides an updated SSH for each IGM, + * containing a md:Model.Supersedes reference to the IGM’s original SSH CIMXML file + * and a single SV for the whole CGM, containing the results of power flow calculation. + * The SV file must contain a md:Model.DependentOn reference to each IGM’s updated SSH. + * The power flow of a CGM is calculated without topology processing. No new TP CIMXML + * files are created. IGM TP files are used as an input. No TP fie is created as the result of CGM building. + * This means that the CGM SV file must contain a md:Model.DependentOn reference to each IGM’s original TP. + * + * @author Luma Zamarreño {@literal } + */ +class CommonGridModelExportTest extends AbstractSerDeTest { + + private static final Pattern REGEX_SCENARIO_TIME = Pattern.compile("Model.scenarioTime>(.*?)<"); + private static final Pattern REGEX_DESCRIPTION = Pattern.compile("Model.description>(.*?)<"); + private static final Pattern REGEX_VERSION = Pattern.compile("Model.version>(.*?)<"); + private static final Pattern REGEX_DEPENDENT_ON = Pattern.compile("Model.DependentOn rdf:resource=\"(.*?)\""); + private static final Pattern REGEX_SUPERSEDES = Pattern.compile("Model.Supersedes rdf:resource=\"(.*?)\""); + private static final Pattern REGEX_PROFILE = Pattern.compile("Model.profile>(.*?)<"); + private static final Pattern REGEX_MAS = Pattern.compile("Model.modelingAuthoritySet>(.*?)<"); + + @Test + void testIgmExportNoModelsNoPropertiesVersion() throws IOException { + Network network = bareNetwork2Subnetworks(); + // Perform a simple IGM export and read the exported files + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.CGM_EXPORT, false); + String basename = "test_bare_igm_be"; + network.getSubnetwork("Network_BE").write("CGMES", exportParams, tmpDir.resolve(basename)); + String exportedBeSshXml = Files.readString(tmpDir.resolve(basename + "_SSH.xml")); + String exportedBeSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml")); + + // There is no version number for original models, so if exported as IGM they would have version equals to 1 + assertEquals("1", getFirstOccurrence(exportedBeSshXml, REGEX_VERSION)); + assertEquals("1", getFirstOccurrence(exportedBeSvXml, REGEX_VERSION)); + } + + @Test + void testCgmExportWithModelsVersion() throws IOException { + Network network = bareNetwork2Subnetworks(); + addModelsForSubnetworks(network, 0); + + // Perform a CGM export and read the exported files + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.CGM_EXPORT, true); + String basename = "test_bare_models_0"; + network.write("CGMES", exportParams, tmpDir.resolve(basename)); + String updatedBeSshXml = Files.readString(tmpDir.resolve(basename + "_BE_SSH.xml")); + String updatedNlSshXml = Files.readString(tmpDir.resolve(basename + "_NL_SSH.xml")); + String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml")); + + // Version number should be increased from original models and be the same for all instance files + assertEquals("1", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION)); + assertEquals("1", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION)); + assertEquals("1", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION)); + } + + @Test + void testCgmExportNoModelsNoProperties() throws IOException { + // Create a simple network with two subnetworks + Network network = bareNetwork2Subnetworks(); + + // Perform a CGM export and read the exported files + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.CGM_EXPORT, true); + String basename = "test_bare"; + network.write("CGMES", exportParams, tmpDir.resolve(basename)); + String updatedBeSshXml = Files.readString(tmpDir.resolve(basename + "_BE_SSH.xml")); + String updatedNlSshXml = Files.readString(tmpDir.resolve(basename + "_NL_SSH.xml")); + String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml")); + + // Scenario time should be the same for all models + assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME)); + + // Description should be the default one + assertEquals("SSH Model", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION)); + assertEquals("SSH Model", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION)); + assertEquals("SV Model", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION)); + + // There is no version number for original models, so if exported as IGM they would have version equals to 1 + // Version number for updated models is increased by 1, so it equals to 2 in the end + assertEquals("2", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION)); + assertEquals("2", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION)); + assertEquals("2", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION)); + + // The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP + // Here the version number part of the id 1 for original models and 2 for updated ones + String updatedBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_2_1D__FM"; + String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_2_1D__FM"; + String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; + String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId); + assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); + + // Each updated IGM SSH should supersede the original one + String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); + assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + + // Profiles should be consistent with the instance files + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE)); + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE)); + assertEquals("http://entsoe.eu/CIM/StateVariables/4/1", getFirstOccurrence(updatedCgmSvXml, REGEX_PROFILE)); + + // All MAS should be equal to the default one since none has been provided + assertEquals("powsybl.org", getFirstOccurrence(updatedBeSshXml, REGEX_MAS)); + assertEquals("powsybl.org", getFirstOccurrence(updatedNlSshXml, REGEX_MAS)); + assertEquals("powsybl.org", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS)); + } + + @Test + void testCgmExportWithModelsForSubnetworks() throws IOException { + // Create a simple network with two subnetworks + Network network = bareNetwork2Subnetworks(); + addModelsForSubnetworks(network, 1); + + // Perform a CGM export and read the exported files + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.CGM_EXPORT, true); + String basename = "test_bare+submodels"; + network.write("CGMES", exportParams, tmpDir.resolve(basename)); + String updatedBeSshXml = Files.readString(tmpDir.resolve(basename + "_BE_SSH.xml")); + String updatedNlSshXml = Files.readString(tmpDir.resolve(basename + "_NL_SSH.xml")); + String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml")); + + // Scenario time should be the same for all models + assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME)); + + // IGM descriptions should be the ones provided in subnetwork models, CGM description should be the default one + assertEquals("BE network description", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION)); + assertEquals("NL network description", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION)); + assertEquals("SV Model", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION)); + + // Version number should be increased from original models and be the same for all instance files + assertEquals("2", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION)); + assertEquals("2", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION)); + assertEquals("2", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION)); + + // The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP + String updatedBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_2_1D__FM"; + String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_2_1D__FM"; + String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; + String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId); + assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); + + // Each updated IGM SSH should supersede the original one + String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); + assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + + // Profiles should be consistent with the instance files + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE)); + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE)); + assertEquals("http://entsoe.eu/CIM/StateVariables/4/1", getFirstOccurrence(updatedCgmSvXml, REGEX_PROFILE)); + + // IGM MAS should be the ones provided in subnetwork models, CGM MAS should be the default one + assertEquals("http://elia.be/CGMES/2.4.15", getFirstOccurrence(updatedBeSshXml, REGEX_MAS)); + assertEquals("http://tennet.nl/CGMES/2.4.15", getFirstOccurrence(updatedNlSshXml, REGEX_MAS)); + assertEquals("powsybl.org", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS)); + } + + @Test + void testCgmExportWithModelsForAllNetworks() throws IOException { + // Create a simple network with two subnetworks + Network network = bareNetwork2Subnetworks(); + addModelsForSubnetworks(network, 1); + addModelForNetwork(network, 3); + + // Perform a CGM export and read the exported files + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.CGM_EXPORT, true); + String basename = "test_bare+models"; + network.write("CGMES", exportParams, tmpDir.resolve(basename)); + String updatedBeSshXml = Files.readString(tmpDir.resolve(basename + "_BE_SSH.xml")); + String updatedNlSshXml = Files.readString(tmpDir.resolve(basename + "_NL_SSH.xml")); + String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml")); + + // The main network has a different scenario time than the subnetworks + // All updated models should get that scenario time + assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME)); + + // IGM descriptions should be the ones provided in subnetwork models, CGM description should be the one provided in main network model + assertEquals("BE network description", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION)); + assertEquals("NL network description", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION)); + assertEquals("Merged network description", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION)); + + // The main network has a different version number (3) than the subnetworks (1) + // Updated models should use next version taking into account the max version number of inputs (next version is 4) + assertEquals("4", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION)); + assertEquals("4", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION)); + assertEquals("4", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION)); + + // The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP + // The model of the main network brings an additional dependency + String updatedBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_4_1D__FM"; + String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_4_1D__FM"; + String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2022-03-04T05:30:00Z_1_1D__FM"; + String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2022-03-04T05:30:00Z_1_1D__FM"; + String additionalDependency = "Additional dependency"; + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, additionalDependency); + assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); + + // Each updated IGM SSH should supersede the original one + String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM"; + String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM"; + assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); + assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + + // Profiles should be consistent with the instance files + // The model of the main network brings an additional profile + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE)); + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE)); + Set expectedProfiles = Set.of("Additional profile", "http://entsoe.eu/CIM/StateVariables/4/1"); + assertEquals(expectedProfiles, getOccurrences(updatedCgmSvXml, REGEX_PROFILE)); + + // IGM MAS should be the ones provided in subnetwork models, CGM MAS should be the one provided in main network model + assertEquals("http://elia.be/CGMES/2.4.15", getFirstOccurrence(updatedBeSshXml, REGEX_MAS)); + assertEquals("http://tennet.nl/CGMES/2.4.15", getFirstOccurrence(updatedNlSshXml, REGEX_MAS)); + assertEquals("Modeling Authority", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS)); + } + + @Test + void testCgmExportWithProperties() throws IOException { + // Create a simple network with two subnetworks + Network network = bareNetwork2Subnetworks(); + + // Perform a CGM export and read the exported files + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.CGM_EXPORT, true); + exportParams.put(CgmesExport.MODELING_AUTHORITY_SET, "Regional Coordination Center"); + exportParams.put(CgmesExport.MODEL_DESCRIPTION, "Common Grid Model export"); + exportParams.put(CgmesExport.MODEL_VERSION, "4"); + String basename = "test_bare+properties"; + network.write("CGMES", exportParams, tmpDir.resolve(basename)); + String updatedBeSshXml = Files.readString(tmpDir.resolve(basename + "_BE_SSH.xml")); + String updatedNlSshXml = Files.readString(tmpDir.resolve(basename + "_NL_SSH.xml")); + String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml")); + + // Scenario time should be the same for all models + assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2021-02-03T04:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME)); + + // Description should be the one provided as parameter and be the same for all instance files + assertEquals("Common Grid Model export", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION)); + assertEquals("Common Grid Model export", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION)); + assertEquals("Common Grid Model export", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION)); + + // Version number should be the one provided as parameter and be the same for all instance files + assertEquals("4", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION)); + assertEquals("4", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION)); + assertEquals("4", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION)); + + // The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP + String updatedBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_4_1D__FM"; + String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_4_1D__FM"; + String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; + String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId); + assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); + + // Each updated IGM SSH should supersede the original one + String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); + assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + + // Profiles should be consistent with the instance files + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE)); + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE)); + assertEquals("http://entsoe.eu/CIM/StateVariables/4/1", getFirstOccurrence(updatedCgmSvXml, REGEX_PROFILE)); + + // IGM MAS should be the default ones, CGM MAS should be the one provided as parameter + assertEquals("powsybl.org", getFirstOccurrence(updatedBeSshXml, REGEX_MAS)); + assertEquals("powsybl.org", getFirstOccurrence(updatedNlSshXml, REGEX_MAS)); + assertEquals("Regional Coordination Center", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS)); + } + + @Test + void testCgmExportWithModelsAndProperties() throws IOException { + // Create a simple network with two subnetworks + Network network = bareNetwork2Subnetworks(); + addModelsForSubnetworks(network, 1); + addModelForNetwork(network, 2); + + // Perform a CGM export and read the exported files + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.CGM_EXPORT, true); + exportParams.put(CgmesExport.MODELING_AUTHORITY_SET, "Regional Coordination Center"); + exportParams.put(CgmesExport.MODEL_DESCRIPTION, "Common Grid Model export"); + exportParams.put(CgmesExport.MODEL_VERSION, "4"); + String basename = "test_bare+models+properties"; + network.write("CGMES", exportParams, tmpDir.resolve(basename)); + String updatedBeSshXml = Files.readString(tmpDir.resolve(basename + "_BE_SSH.xml")); + String updatedNlSshXml = Files.readString(tmpDir.resolve(basename + "_NL_SSH.xml")); + String updatedCgmSvXml = Files.readString(tmpDir.resolve(basename + "_SV.xml")); + + // The main network has a different scenario time than the subnetworks + // All updated models should get that scenario time + assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedBeSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedNlSshXml, REGEX_SCENARIO_TIME)); + assertEquals("2022-03-04T05:30:00Z", getFirstOccurrence(updatedCgmSvXml, REGEX_SCENARIO_TIME)); + + // Both the models and a property define the description. The property should prevail. + assertEquals("Common Grid Model export", getFirstOccurrence(updatedBeSshXml, REGEX_DESCRIPTION)); + assertEquals("Common Grid Model export", getFirstOccurrence(updatedNlSshXml, REGEX_DESCRIPTION)); + assertEquals("Common Grid Model export", getFirstOccurrence(updatedCgmSvXml, REGEX_DESCRIPTION)); + + // Both the models and a property define the version number. The property should prevail. + assertEquals("4", getFirstOccurrence(updatedBeSshXml, REGEX_VERSION)); + assertEquals("4", getFirstOccurrence(updatedNlSshXml, REGEX_VERSION)); + assertEquals("4", getFirstOccurrence(updatedCgmSvXml, REGEX_VERSION)); + + // The updated CGM SV should depend on the updated IGMs SSH and on the original IGMs TP + // The model of the main network brings an additional dependency + String updatedBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_4_1D__FM"; + String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_4_1D__FM"; + String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2022-03-04T05:30:00Z_1_1D__FM"; + String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2022-03-04T05:30:00Z_1_1D__FM"; + String additionalDependency = "Additional dependency"; + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, additionalDependency); + assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); + + // Each updated IGM SSH should supersede the original one + String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM"; + String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM"; + assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); + assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + + // Profiles should be consistent with the instance files, CGM SV has an additional profile + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE)); + assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedNlSshXml, REGEX_PROFILE)); + Set expectedProfiles = Set.of("Additional profile", "http://entsoe.eu/CIM/StateVariables/4/1"); + assertEquals(expectedProfiles, getOccurrences(updatedCgmSvXml, REGEX_PROFILE)); + + // Both the model and a property define the main network MAS. The property should prevail. + assertEquals("http://elia.be/CGMES/2.4.15", getFirstOccurrence(updatedBeSshXml, REGEX_MAS)); + assertEquals("http://tennet.nl/CGMES/2.4.15", getFirstOccurrence(updatedNlSshXml, REGEX_MAS)); + assertEquals("Regional Coordination Center", getFirstOccurrence(updatedCgmSvXml, REGEX_MAS)); + } + + @Test + void testFaraoUseCase() { + // We use MicroGrid base case assembled as the CGM model to export + ReadOnlyDataSource ds = CgmesConformity1Catalog.microGridBaseCaseAssembled().dataSource(); + Network cgmNetwork = Network.read(ds); + + // Check the version of the individual files in the test case + int currentVersion = readVersion(ds, "MicroGridTestConfiguration_BC_BE_SSH_V2.xml").orElseThrow(); + int sshNLVersion = readVersion(ds, "MicroGridTestConfiguration_BC_NL_SSH_V2.xml").orElseThrow(); + int svVersion = readVersion(ds, "MicroGridTestConfiguration_BC_Assembled_SV_V2.xml").orElseThrow(); + assertEquals(currentVersion, sshNLVersion); + assertEquals(currentVersion, svVersion); + Network[] ns = cgmNetwork.getSubnetworks().toArray(new Network[] {}); + assertEquals(currentVersion, ns[0].getExtension(CgmesMetadataModels.class).getModelForSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS).orElseThrow().getVersion()); + assertEquals(currentVersion, ns[1].getExtension(CgmesMetadataModels.class).getModelForSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS).orElseThrow().getVersion()); + + // If we export this CGM we expected the output files to on current version + 1 + int expectedOutputVersion = currentVersion + 1; + + // Export with the same parameters of the FARAO use case + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.EXPORT_BOUNDARY_POWER_FLOWS, true); + exportParams.put(CgmesExport.NAMING_STRATEGY, "cgmes"); + exportParams.put(CgmesExport.CGM_EXPORT, true); + exportParams.put(CgmesExport.UPDATE_DEPENDENCIES, true); + exportParams.put(CgmesExport.MODELING_AUTHORITY_SET, "MAS1"); + + MemDataSource memDataSource = new MemDataSource(); + cgmNetwork.write("CGMES", exportParams, memDataSource); + for (Map.Entry entry : TSO_BY_COUNTRY.entrySet()) { + Country country = entry.getKey(); + // For this unit test we do not need the TSO from the entry value + String filenameFromCgmesExport = cgmNetwork.getNameOrId() + "_" + country.toString() + "_SSH.xml"; + + // The CGM network does not have metadata models, only the subnetworks + assertEquals((CgmesMetadataModels) null, cgmNetwork.getExtension(CgmesMetadataModels.class)); + + // The metadata models in memory are NEVER updated after export (this is by design) + // Look for the subnetwork of the current country + Network nc = cgmNetwork.getSubnetworks().stream().filter(n -> country == n.getSubstations().iterator().next().getCountry().orElseThrow()).toList().get(0); + int sshVersionInNetworkMetadataModels = nc.getExtension(CgmesMetadataModels.class).getModelForSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS).orElseThrow().getVersion(); + assertEquals(currentVersion, sshVersionInNetworkMetadataModels); + + // To know the exported version we should read what has been written in the output files + int sshVersionInOutput = readVersion(memDataSource, filenameFromCgmesExport).orElseThrow(); + assertEquals(expectedOutputVersion, sshVersionInOutput); + } + String filenameFromCgmesExport = cgmNetwork.getNameOrId() + "_SV.xml"; + // We have to read it from inside the SV file ... + int svVersionInOutput = readVersion(memDataSource, filenameFromCgmesExport).orElseThrow(); + assertEquals(expectedOutputVersion, svVersionInOutput); + } + + @Test + void testFaraoUseCaseManualExport() throws IOException { + Network cgmNetwork = bareNetwork2Subnetworks(); + addModelsForSubnetworks(cgmNetwork, 2); + + // We set the version manually + int exportedVersion = 18; + + // Common export parameters + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.EXPORT_BOUNDARY_POWER_FLOWS, true); + exportParams.put(CgmesExport.NAMING_STRATEGY, "cgmes"); + // We do not want a quick CGM export + exportParams.put(CgmesExport.CGM_EXPORT, false); + exportParams.put(CgmesExport.UPDATE_DEPENDENCIES, false); + + // For each subnetwork, prepare the metadata for SSH and export it + Path tmpFolder = tmpDir.resolve("tmp-manual"); + String basename = "manualBase"; + Files.createDirectories(tmpFolder); + for (Network n : cgmNetwork.getSubnetworks()) { + String country = n.getSubstations().iterator().next().getCountry().orElseThrow().toString(); + CgmesMetadataModel sshModel = n.getExtension(CgmesMetadataModels.class).getModelForSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS).orElseThrow(); + sshModel.clearDependencies() + .addDependentOn("myDependency") + .addSupersedes("mySupersede") + .setVersion(exportedVersion) + .setModelingAuthoritySet("myModellingAuthority"); + exportParams.put(CgmesExport.PROFILES, List.of("SSH")); + n.write("CGMES", exportParams, new FileDataSource(tmpFolder, basename + "_" + country)); + } + + // In the main network, CREATE the metadata for SV and export it + cgmNetwork.newExtension(CgmesMetadataModelsAdder.class) + .newModel() + .setSubset(CgmesSubset.STATE_VARIABLES) + .addProfile("http://entsoe.eu/CIM/StateVariables/4/1") + .setId("mySvId") + .setVersion(exportedVersion) + .setModelingAuthoritySet("myModellinAuthority") + .addDependentOn("mySvDependency1") + .addDependentOn("mySvDependency2") + .add() + .add(); + exportParams.put(CgmesExport.PROFILES, List.of("SV")); + DataSource dataSource = new FileDataSource(tmpFolder, basename); + cgmNetwork.write("CGMES", exportParams, dataSource); + + int expectedOutputVersion = exportedVersion; + for (Map.Entry entry : TSO_BY_COUNTRY.entrySet()) { + Country country = entry.getKey(); + // For this unit test we do not need the TSO from the entry value + String filenameFromCgmesExport = basename + "_" + country.toString() + "_SSH.xml"; + + // To know the exported version we should read what has been written in the output files + int sshVersionInOutput = readVersion(dataSource, filenameFromCgmesExport).orElseThrow(); + assertEquals(expectedOutputVersion, sshVersionInOutput); + + String outputSshXml = Files.readString(tmpFolder.resolve(filenameFromCgmesExport)); + assertEquals(Set.of("myDependency"), getOccurrences(outputSshXml, REGEX_DEPENDENT_ON)); + assertEquals(Set.of("mySupersede"), getOccurrences(outputSshXml, REGEX_SUPERSEDES)); + } + String filenameFromCgmesExport = basename + "_SV.xml"; + // We read it from inside the SV file ... + int svVersionInOutput = readVersion(dataSource, filenameFromCgmesExport).orElseThrow(); + assertEquals(expectedOutputVersion, svVersionInOutput); + // Check the dependencies + String outputSvXml = Files.readString(tmpFolder.resolve(filenameFromCgmesExport)); + assertEquals(Set.of("mySvDependency1", "mySvDependency2"), getOccurrences(outputSvXml, REGEX_DEPENDENT_ON)); + } + + private static final Map TSO_BY_COUNTRY = Map.of( + Country.BE, "Elia", + Country.NL, "Tennet"); + + private static Optional readVersion(ReadOnlyDataSource ds, String filename) { + try { + return readVersion(ds.newInputStream(filename)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static Optional readVersion(InputStream is) { + try { + XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(is); + boolean insideModelVersion = false; + while (reader.hasNext()) { + int next = reader.next(); + if (next == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("Model.version")) { + insideModelVersion = true; + } else if (next == XMLStreamConstants.END_ELEMENT) { + insideModelVersion = false; + } else if (next == XMLStreamConstants.CHARACTERS && insideModelVersion) { + String version = reader.getText(); + reader.close(); + return Optional.of(Integer.parseInt(version)); + } + } + reader.close(); + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } + return Optional.empty(); + } + + private Network bareNetwork2Subnetworks() { + Network network1 = Network + .create("Network_BE", "test") + .newSubstation().setId("Substation1").setCountry(Country.BE).add() + .getNetwork(); + network1.setCaseDate(ZonedDateTime.parse("2021-02-03T04:30:00.000+00:00")); + + Network network2 = Network + .create("Network_NL", "test") + .newSubstation().setId("Substation2").setCountry(Country.NL).add() + .getNetwork(); + network2.setCaseDate(ZonedDateTime.parse("2021-02-03T04:30:00.000+00:00")); + + return Network.merge(network1, network2); + } + + private void addModelsForSubnetworks(Network network, int version) { + // Add a model to the 2 Subnetworks + network.getSubnetwork("Network_BE") + .newExtension(CgmesMetadataModelsAdder.class) + .newModel() + .setSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS) + .setDescription("BE network description") + .setVersion(version) + .setModelingAuthoritySet("http://elia.be/CGMES/2.4.15") + .addDependentOn("BE EQ model ID") + .addProfile("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1") + .add() + .add(); + network.getSubnetwork("Network_NL") + .newExtension(CgmesMetadataModelsAdder.class) + .newModel() + .setSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS) + .setDescription("NL network description") + .setVersion(version) + .setModelingAuthoritySet("http://tennet.nl/CGMES/2.4.15") + .addDependentOn("NL EQ model ID") + .addProfile("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1") + .add() + .add(); + } + + private void addModelForNetwork(Network network, int version) { + // Add a model to the merged network + network.setCaseDate(ZonedDateTime.parse("2022-03-04T05:30:00.000+00:00")) + .newExtension(CgmesMetadataModelsAdder.class) + .newModel() + .setSubset(CgmesSubset.STATE_VARIABLES) + .setDescription("Merged network description") + .setVersion(version) + .setModelingAuthoritySet("Modeling Authority") + .addDependentOn("Additional dependency") + .addProfile("Additional profile") + .add() + .add(); + } + + private String getFirstOccurrence(String xml, Pattern pattern) { + Matcher matcher = pattern.matcher(xml); + if (matcher.find()) { + return matcher.group(1); + } + throw new PowsyblException("Pattern not found " + pattern); + } + + private Set getOccurrences(String xml, Pattern pattern) { + Set matches = new HashSet<>(); + Matcher matcher = pattern.matcher(xml); + while (matcher.find()) { + matches.add(matcher.group(1)); + } + return matches; + } + +} diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/LegacyCommonGridModelExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/LegacyCommonGridModelExportTest.java index ea7e56a2f75..c9762cc22e7 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/LegacyCommonGridModelExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/LegacyCommonGridModelExportTest.java @@ -33,13 +33,39 @@ */ class LegacyCommonGridModelExportTest extends AbstractSerDeTest { + @Test + void testExportCgmSvDependenciesNotUpdated() throws IOException { + // We check that prepared SV dependencies have not been modified even if we export as CGM (SSH + SV) + // This is really not useful, since in this scenario we would want to export only SV + // But we want to let the parameter "update-dependencies" be valid in both types of export: IGM and CGM + + ReadOnlyDataSource ds = CgmesConformity1ModifiedCatalog.microGridBaseCaseAssembledSvWithMas().dataSource(); + Network network = Network.read(ds); + + List sshIds = List.of("ssh-dep1", "ssh-dep2", "ssh-dep3"); + buildSvDependenciesManaginMetadataModels(network, sshIds); + + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.CGM_EXPORT, "True"); + exportParams.put(CgmesExport.UPDATE_DEPENDENCIES, "False"); + String exportBasename = "tmp-micro-bc-CGM"; + network.write("CGMES", exportParams, tmpDir.resolve(exportBasename)); + + String sv = Files.readString(tmpDir.resolve(exportBasename + "_SV.xml")); + + Set deps = findAll(REGEX_DEPENDENT_ON, sv); + Set prepared = network.getExtension(CgmesMetadataModels.class).getModelForSubset(CgmesSubset.STATE_VARIABLES).orElseThrow().getDependentOn(); + assertEquals(prepared, deps); + } + @Test void testExportCgmSvDependenciesOnNetworkProperties() throws IOException { ReadOnlyDataSource ds = CgmesConformity1ModifiedCatalog.microGridBaseCaseAssembledSvWithMas().dataSource(); Network network = Network.read(ds); // This is the legacy way of preparing dependencies for SV externally, - // used by projects in the FARAO community + // It was used by projects in the FARAO community + // The support for this way of preparing dependencies has been dropped network.setProperty(Identifiables.getUniqueId(CGMES_PREFIX_ALIAS_PROPERTIES + "SSH_ID", network::hasProperty), "ssh-updated-dep1"); network.setProperty(Identifiables.getUniqueId(CGMES_PREFIX_ALIAS_PROPERTIES + "SSH_ID", network::hasProperty), "ssh-updated-dep2"); network.setProperty(Identifiables.getUniqueId(CGMES_PREFIX_ALIAS_PROPERTIES + "SSH_ID", network::hasProperty), "ssh-updated-dep3"); @@ -53,7 +79,9 @@ void testExportCgmSvDependenciesOnNetworkProperties() throws IOException { network.write("CGMES", exportParams, tmpDir.resolve(exportBasename)); Set deps = findAll(REGEX_DEPENDENT_ON, Files.readString(tmpDir.resolve(exportBasename + "_SV.xml"))); - assertEquals(Set.of("ssh-updated-dep1", "ssh-updated-dep2", "ssh-updated-dep3", "tp-initial-dep1", "tp-initial-dep2", "tp-initial-dep3"), deps); + // We ensure that written dependencies do not match any of the prepared through properties + Set prepared = Set.of("ssh-updated-dep1", "ssh-updated-dep2", "ssh-updated-dep3", "tp-initial-dep1", "tp-initial-dep2", "tp-initial-dep3"); + assertFalse(deps.stream().anyMatch(prepared::contains)); } @Test @@ -82,6 +110,10 @@ void testExportCgmSvDependenciesOnMetadataModelsExtension() throws IOException { assertTrue(deps.containsAll(updatedSshIds)); initialSshIds.forEach(initialSshId -> assertFalse(deps.contains(initialSshId))); + // Ensure that only the prepared dependencies have been put in the output + Set prepared = network.getExtension(CgmesMetadataModels.class).getModelForSubset(CgmesSubset.STATE_VARIABLES).orElseThrow().getDependentOn(); + assertEquals(prepared, deps); + assertEquals("MAS1", findFirst(MODELING_AUTHORITY, sv)); } diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/StateVariablesExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/StateVariablesExportTest.java index 393de79b1f0..d4dfeacc6ef 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/StateVariablesExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/StateVariablesExportTest.java @@ -68,7 +68,6 @@ public void setUp() throws IOException { void microGridBE() throws IOException, XMLStreamException { assertTrue(test(CgmesConformity1Catalog.microGridBaseCaseBE().dataSource(), false, - 2, false, StateVariablesExportTest::addRepackagerFiles)); } @@ -79,7 +78,6 @@ void microGridBEFlowsForSwitches() throws IOException, XMLStreamException { // Writing flows for all switches has impact on performance assertTrue(test(CgmesConformity1Catalog.microGridBaseCaseNL().dataSource(), false, - 2, true, StateVariablesExportTest::addRepackagerFiles)); } @@ -112,7 +110,7 @@ void minimalNodeBreakerFlowsForSwitches() throws XMLStreamException { b1.setV(99.7946677).setAngle(-0.568404640); new LoadFlowResultsCompletion(new LoadFlowResultsCompletionParameters(), new LoadFlowParameters()).run(n, null); - String sv = exportSvAsString(n, 1, true); + String sv = exportSvAsString(n, true); assertEqualsPowerFlow(new PowerFlow(10, 1), extractSvPowerFlow(sv, cgmesTerminal(n, "LOAD", 1))); assertEqualsPowerFlow(new PowerFlow(-10, -1), extractSvPowerFlow(sv, cgmesTerminal(n, "BK11", 1))); @@ -157,7 +155,6 @@ private static PowerFlow extractSvPowerFlow(String sv, String terminalId) { void microGridAssembled() throws IOException, XMLStreamException { assertTrue(test(CgmesConformity1Catalog.microGridBaseCaseAssembled().dataSource(), false, - 4, false, r -> { addRepackagerFiles("NL", r); @@ -169,7 +166,6 @@ void microGridAssembled() throws IOException, XMLStreamException { void smallGridBusBranch() throws IOException, XMLStreamException { assertTrue(test(CgmesConformity1Catalog.smallBusBranch().dataSource(), false, - 4, false, StateVariablesExportTest::addRepackagerFiles)); } @@ -178,7 +174,6 @@ void smallGridBusBranch() throws IOException, XMLStreamException { void smallGridNodeBreakerHVDC() throws IOException, XMLStreamException { assertTrue(test(CgmesConformity1Catalog.smallNodeBreakerHvdc().dataSource(), true, - 4, false, StateVariablesExportTest::addRepackagerFilesExcludeTp)); } @@ -187,7 +182,6 @@ void smallGridNodeBreakerHVDC() throws IOException, XMLStreamException { void smallGridNodeBreaker() throws IOException, XMLStreamException { assertTrue(test(CgmesConformity1Catalog.smallNodeBreaker().dataSource(), true, - 4, false, StateVariablesExportTest::addRepackagerFilesExcludeTp)); } @@ -196,7 +190,6 @@ void smallGridNodeBreaker() throws IOException, XMLStreamException { void miniBusBranchWithSvInjection() throws IOException, XMLStreamException { assertTrue(test(CgmesConformity1ModifiedCatalog.smallBusBranchWithSvInjection().dataSource(), false, - 4, false, StateVariablesExportTest::addRepackagerFiles)); } @@ -213,17 +206,17 @@ void miniBusBranchWithSvInjectionExportPQ() throws XMLStreamException { load.getTerminal().setP(-0.12); load.getTerminal().setQ(-13.03); - String sv = exportSvAsString(network, 4); + String sv = exportSvAsString(network); assertTrue(sv.contains(cgmesTerminal)); load.getTerminal().setP(Double.NaN); load.getTerminal().setQ(-13.03); - String sv1 = exportSvAsString(network, 4); + String sv1 = exportSvAsString(network); assertTrue(sv1.contains(cgmesTerminal)); load.getTerminal().setP(-0.12); load.getTerminal().setQ(Double.NaN); - String sv2 = exportSvAsString(network, 4); + String sv2 = exportSvAsString(network); assertTrue(sv2.contains(cgmesTerminal)); } @@ -238,14 +231,14 @@ void miniBusBranchWithSvInjectionExportQ() throws XMLStreamException { // If P and Q both are NaN is not exported shuntCompensator.getTerminal().setQ(-13.03); - String sv = exportSvAsString(network, 4); + String sv = exportSvAsString(network); assertTrue(sv.contains(cgmesTerminal)); } @Test void microGridBEWithHiddenTapChangers() throws XMLStreamException { Network network = importNetwork(CgmesConformity1ModifiedCatalog.microGridBaseCaseBEHiddenTapChangers().dataSource()); - String sv = exportSvAsString(network, 2); + String sv = exportSvAsString(network); String hiddenTapChangerId = "_6ebbef67-3061-4236-a6fd-6ccc4595f6c3-x"; assertTrue(sv.contains(hiddenTapChangerId)); } @@ -255,7 +248,7 @@ void equivalentShuntTest() throws XMLStreamException { ReadOnlyDataSource ds = CgmesConformity1ModifiedCatalog.microGridBaseCaseBEEquivalentShunt().dataSource(); Network network = new CgmesImport().importData(ds, NetworkFactory.findDefault(), importParams); - String sv = exportSvAsString(network, 2); + String sv = exportSvAsString(network); String equivalentShuntId = "d771118f-36e9-4115-a128-cc3d9ce3e3da"; assertNotNull(network.getShuntCompensator(equivalentShuntId)); @@ -360,7 +353,7 @@ void cgmes3MiniGridwithTransformersWithRtcAndPtc() throws XMLStreamException { t3wt.addAlias(t3ptcId, Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + CgmesNames.PHASE_TAP_CHANGER + 1); String expected = buildNetworkSvTapStepsString(network); - String sv = exportSvAsString(network, 2); + String sv = exportSvAsString(network); String actual = readSvTapSteps(sv).toSortedString(); assertEquals(expected, actual); } @@ -552,15 +545,14 @@ private static Network importNetwork(ReadOnlyDataSource ds) { return new CgmesImport().importData(ds, NetworkFactory.findDefault(), importParams); } - private String exportSvAsString(Network network, int svVersion) throws XMLStreamException { - return exportSvAsString(network, svVersion, false); + private String exportSvAsString(Network network) throws XMLStreamException { + return exportSvAsString(network, false); } - private String exportSvAsString(Network network, int svVersion, boolean exportFlowsForSwitches) throws XMLStreamException { + private String exportSvAsString(Network network, boolean exportFlowsForSwitches) throws XMLStreamException { CgmesExportContext context = new CgmesExportContext(network); StringWriter stringWriter = new StringWriter(); XMLStreamWriter writer = XmlUtil.initializeWriter(true, " ", stringWriter); - context.getExportedSVModel().setVersion(svVersion); context.setExportBoundaryPowerFlows(true); context.setExportFlowsForSwitches(exportFlowsForSwitches); StateVariablesExport.write(network, writer, context); @@ -589,7 +581,7 @@ private static void addRepackagerFilesExcludeTp(Repackager repackager) { .with("test_SSH.xml", Repackager::ssh); } - private boolean test(ReadOnlyDataSource dataSource, boolean exportTp, int svVersion, boolean exportFlowsForSwitches, Consumer repackagerConsumer) throws XMLStreamException, IOException { + private boolean test(ReadOnlyDataSource dataSource, boolean exportTp, boolean exportFlowsForSwitches, Consumer repackagerConsumer) throws XMLStreamException, IOException { // Import original importParams.put("iidm.import.cgmes.create-cgmes-export-mapping", "true"); Network expected0 = new CgmesImport().importData(dataSource, NetworkFactory.findDefault(), importParams); @@ -604,7 +596,6 @@ private boolean test(ReadOnlyDataSource dataSource, boolean exportTp, int svVers // Export SV CgmesExportContext context = new CgmesExportContext(expected); - context.getExportedSVModel().setVersion(svVersion); context.setExportBoundaryPowerFlows(true); context.setExportFlowsForSwitches(exportFlowsForSwitches); context.setExportSvInjectionsForSlacks(false); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java index 50246a14d6b..43aaa9eefbc 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java @@ -64,7 +64,7 @@ void microGridBE() throws IOException, XMLStreamException { DifferenceEvaluator knownDiffsXiidm = DifferenceEvaluators.chain( DifferenceEvaluators.Default, ExportXmlCompare::ignoringCgmesMetadataModels); - assertTrue(test(CgmesConformity1Catalog.microGridBaseCaseBE().dataSource(), 2, knownDiffsSsh, knownDiffsXiidm)); + assertTrue(test(CgmesConformity1Catalog.microGridBaseCaseBE().dataSource(), knownDiffsSsh, knownDiffsXiidm)); } @Test @@ -77,7 +77,7 @@ void microGridBEWithHiddenTapChangers() throws IOException, XMLStreamException { DifferenceEvaluator knownDiffsXiidm = DifferenceEvaluators.chain( DifferenceEvaluators.Default, ExportXmlCompare::ignoringCgmesMetadataModels); - assertTrue(test(CgmesConformity1ModifiedCatalog.microGridBaseCaseBEHiddenTapChangers().dataSource(), 2, knownDiffsSsh, knownDiffsXiidm)); + assertTrue(test(CgmesConformity1ModifiedCatalog.microGridBaseCaseBEHiddenTapChangers().dataSource(), knownDiffsSsh, knownDiffsXiidm)); } @Test @@ -89,7 +89,7 @@ void microGridBEWithSharedRegulatingControl() throws IOException, XMLStreamExcep DifferenceEvaluator knownDiffsXiidm = DifferenceEvaluators.chain( DifferenceEvaluators.Default, ExportXmlCompare::ignoringCgmesMetadataModels); - assertTrue(test(CgmesConformity1ModifiedCatalog.microGridBaseCaseBESharedRegulatingControl().dataSource(), 2, knownDiffsSsh, knownDiffsXiidm)); + assertTrue(test(CgmesConformity1ModifiedCatalog.microGridBaseCaseBESharedRegulatingControl().dataSource(), knownDiffsSsh, knownDiffsXiidm)); } @Test @@ -98,7 +98,7 @@ void smallGrid() throws IOException, XMLStreamException { ExportXmlCompare::sameScenarioTime, ExportXmlCompare::ensuringIncreasedModelVersion, ExportXmlCompare::ignoringJunctionOrBusbarTerminals); - assertTrue(test(CgmesConformity1Catalog.smallBusBranch().dataSource(), 4, knownDiffs, DifferenceEvaluators.chain( + assertTrue(test(CgmesConformity1Catalog.smallBusBranch().dataSource(), knownDiffs, DifferenceEvaluators.chain( DifferenceEvaluators.Default, ExportXmlCompare::numericDifferenceEvaluator, ExportXmlCompare::ignoringCgmesMetadataModels, @@ -111,7 +111,7 @@ void smallGridHVDC() throws IOException, XMLStreamException { ExportXmlCompare::sameScenarioTime, ExportXmlCompare::ensuringIncreasedModelVersion, ExportXmlCompare::ignoringJunctionOrBusbarTerminals); - assertTrue(test(CgmesConformity1Catalog.smallNodeBreakerHvdc().dataSource(), 4, knownDiffs, DifferenceEvaluators.chain( + assertTrue(test(CgmesConformity1Catalog.smallNodeBreakerHvdc().dataSource(), knownDiffs, DifferenceEvaluators.chain( DifferenceEvaluators.Default, ExportXmlCompare::numericDifferenceEvaluator, ExportXmlCompare::ignoringControlAreaNetInterchange, @@ -119,7 +119,7 @@ void smallGridHVDC() throws IOException, XMLStreamException { ExportXmlCompare::ignoringHvdcLinePmax))); } - private boolean test(ReadOnlyDataSource dataSource, int version, DifferenceEvaluator knownDiffsSsh, DifferenceEvaluator knownDiffsIidm) throws IOException, XMLStreamException { + private boolean test(ReadOnlyDataSource dataSource, DifferenceEvaluator knownDiffsSsh, DifferenceEvaluator knownDiffsIidm) throws IOException, XMLStreamException { // Import original importParams.put("iidm.import.cgmes.create-cgmes-export-mapping", "true"); Network expected = new CgmesImport().importData(dataSource, NetworkFactory.findDefault(), importParams); @@ -129,7 +129,6 @@ private boolean test(ReadOnlyDataSource dataSource, int version, DifferenceEvalu try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(exportedSsh))) { XMLStreamWriter writer = XmlUtil.initializeWriter(true, " ", os); CgmesExportContext context = new CgmesExportContext(expected); - context.getExportedSSHModel().setVersion(version); SteadyStateHypothesisExport.write(expected, writer, context); } @@ -176,7 +175,7 @@ void equivalentShuntTest() throws XMLStreamException { ReadOnlyDataSource ds = CgmesConformity1ModifiedCatalog.microGridBaseCaseBEEquivalentShunt().dataSource(); Network network = new CgmesImport().importData(ds, NetworkFactory.findDefault(), importParams); - String ssh = exportSshAsString(network, 2); + String ssh = exportSshAsString(network); // Equivalent shunts should not have entries in SSH String equivalentShuntId = "d771118f-36e9-4115-a128-cc3d9ce3e3da"; @@ -186,11 +185,10 @@ void equivalentShuntTest() throws XMLStreamException { assertFalse(sshLinearShuntCompensators.map.containsKey(equivalentShuntId)); } - private static String exportSshAsString(Network network, int sshVersion) throws XMLStreamException { + private static String exportSshAsString(Network network) throws XMLStreamException { CgmesExportContext context = new CgmesExportContext(network); StringWriter stringWriter = new StringWriter(); XMLStreamWriter writer = XmlUtil.initializeWriter(true, " ", stringWriter); - context.getExportedSSHModel().setVersion(sshVersion); SteadyStateHypothesisExport.write(network, writer, context); return stringWriter.toString(); @@ -277,10 +275,6 @@ void testUpdateControlArea() throws IOException { SshExportedControlArea sshExportedControlArea = sshExportedControlAreas.iterator().next(); assertEquals(473.9596, sshExportedControlArea.netInterchange, 1e-10); assertEquals(5, sshExportedControlArea.pTolerance, 1e-10); - - // Check that SSH full model contains a reference to the original SSH that is superseding - String modelSupersedes = readSshModelSupersedes(outputPath.resolve("BE_SSH.xml")); - assertEquals("urn:uuid:1b092ff0-f8a0-49da-82d3-75eff5f1e820", modelSupersedes); } static class SshExportedControlArea { @@ -324,30 +318,6 @@ private static Collection readSshControlAreas(Path ssh) return sshExportedControlAreas; } - private static final String ATTR_RESOURCE = "resource"; - private static final String MODEL_SUPERSEDES = "Model.Supersedes"; - - private static String readSshModelSupersedes(Path ssh) { - String modelSupersedes; - try (InputStream is = Files.newInputStream(ssh)) { - XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(is); - while (reader.hasNext()) { - int next = reader.next(); - if (next == XMLStreamConstants.START_ELEMENT) { - if (reader.getLocalName().equals(MODEL_SUPERSEDES)) { - modelSupersedes = reader.getAttributeValue(CgmesNamespace.RDF_NAMESPACE, ATTR_RESOURCE); - reader.close(); - return modelSupersedes; - } - } - } - reader.close(); - } catch (XMLStreamException | IOException e) { - throw new RuntimeException(e); - } - return null; - } - @Test void microGridCgmesExportPreservingOriginalClassesOfLoads() throws IOException, XMLStreamException { ReadOnlyDataSource ds = Cgmes3ModifiedCatalog.microGridBaseCaseAllTypesOfLoads().dataSource(); @@ -357,14 +327,15 @@ void microGridCgmesExportPreservingOriginalClassesOfLoads() throws IOException, Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "microGridCgmesExportPreservingOriginalClassesOfLoads"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + Properties exportParams = new Properties(); + new CgmesExport().export(network, exportParams, new FileDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); InputStream expectedSsh = Repackager.newInputStream(ds, Repackager::ssh); - String actualSsh = exportSshAsString(actual, 5); + String actualSsh = exportSshAsString(actual); DifferenceEvaluator knownDiffsSsh = DifferenceEvaluators.chain( ExportXmlCompare::ignoringFullModelDependentOn, @@ -392,14 +363,15 @@ void miniGridCgmesExportPreservingOriginalClasses() throws IOException, XMLStrea Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "miniGridCgmesExportPreservingOriginalClasses"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + Properties exportParams = new Properties(); + new CgmesExport().export(network, exportParams, new FileDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), new Properties()); InputStream expectedSsh = Repackager.newInputStream(ds, Repackager::ssh); - String actualSsh = exportSshAsString(actual, 5); + String actualSsh = exportSshAsString(actual); DifferenceEvaluator knownDiffsSsh = DifferenceEvaluators.chain( ExportXmlCompare::ignoringFullModelDependentOn, diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ModelIdTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ModelIdTest.java index 691f27b4c07..06476d60cc8 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ModelIdTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/ModelIdTest.java @@ -66,7 +66,6 @@ void testModelIds() throws IOException { network.newExtension(CgmesMetadataModelsAdder.class) .newModel() .setSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS) - .setId("not-relevant") .setDescription("not-relevant") .setVersion(42) .setModelingAuthoritySet("not-relevant") diff --git a/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesMetadataModelsAdderImpl.java b/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesMetadataModelsAdderImpl.java index b88cb29a2b1..8cf6d008814 100644 --- a/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesMetadataModelsAdderImpl.java +++ b/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesMetadataModelsAdderImpl.java @@ -85,9 +85,6 @@ public CgmesMetadataModelsAdderImpl add() { if (subset == null) { throw new PowsyblException("Model subset is undefined"); } - if (id == null) { - throw new PowsyblException("Model id is undefined"); - } if (modelingAuthoritySet == null) { throw new PowsyblException("Model modelingAuthoritySet is undefined"); } diff --git a/docs/grid_exchange_formats/cgmes/export.md b/docs/grid_exchange_formats/cgmes/export.md index 461aaec2456..908702b272e 100644 --- a/docs/grid_exchange_formats/cgmes/export.md +++ b/docs/grid_exchange_formats/cgmes/export.md @@ -1,8 +1,155 @@ # Export -TODO +There are two main use-cases supported: + * Export IGM (Individual Grid Model) instance files. There is a single network and a unique CGMES modelling authority. + * Export CGM (Common Grid Model) instance files. A network composed of multiple subnetworks, where each subnetwork is an IGM. -Please note that PowSyBl only ever exports CIM-CGMES networks as CGMES Node/Breaker networks without consideration of the topology level of the PowSyBl network. +In both cases, the metadata model information in the exported files is built from metadata information read from the input files and stored in IIDM or received through parameters. +Information received through parameters takes precedence over information available from original metadata models. + +For a quick CGM export, the user may rely on the parameter **iidm.export.cgmes.cgm_export** to write in a single export multiple updated SSH files (one for each IGM) and a single SV for the whole common grid model. Specifics about this option are explained in the section [below](#cgm-common-grid-model-quick-export). +If you need complete control over the exported files in a CGM scenario, you may prefer to iterate through the subnetworks and make multiple calls to the export function. This is described in detail in the section [below](#cgm-common-grid-model-manual-export). + +Please note that when exporting equipment, PowSyBl always use the CGMES node/breaker level of detail, without considering the topology +level of the PowSyBl network. + +The user can specify the profiles to be exported using the parameter **iidm.export.cgmes.profiles**. The list of currently supported export instance files are: EQ, SSH, SV, TP. + +If the IIDM network has at least one voltage level with node/breaker topology level, and the SSH or SV is requested in the export, and the TP is not requested, an error will be logged, as there could be missing references in the SSH, SV files to Topological Nodes calculated automatically by IIDM that are not present in the output. + +If the dependencies have to be updated automatically (see parameter **iidm.export.cgmes.update-dependencies** below), the exported instance files will contain metadata models where: +* TP and SSH depend on EQ. +* SV depends on TP and SSH. +* EQ depends on EQ_BD (if present). EQ_BD is the profile for the boundary equipment definitions. +* SV depends on TP_BD (if present). TP_BD is the profile for the boundary topology. Only for CGMES 2.4. + +The output filenames will follow the pattern `_.xml`. The basename is determined from the parameters, or the basename of the export data source or the main network name. + +## CGM (Common Grid Model) quick export + +When exporting a CGM, we need an IIDM network (CGM) that contains multiple subnetworks (one for each IGM). +Only the CGMES instance files corresponding to SSH and SV profiles are exported: +an updated SSH file for every subnetwork (for every IGM) and a single SV file for the main network that represents the CGM. + +When exporting, it is verified that the main network and all subnetworks have the same scenario time (network case date). If they are different, an error is logged. + +If a version number is given as a parameter, it is used for the exported files. If not, the versions of the input CGM SV and IGM SSHs are obtained from their metadata, and their maximum value calculated. The output version is then set to 1 + this maximum value. + +The quick CGM export will always write updated SSH files for IGMs and a single SV for the CGM. The parameter for selecting which profiles to export is ignored in this kind of export. + +If the dependencies have to be updated automatically (see parameter **iidm.export.cgmes.update-dependencies** below), the exported instance files will contain metadata models where: +* Updated SSH for IGMs supersede the original ones. +* Updated SV for the CGM depends on the updated SSH from IGMs and on the original TP from IGMs. + +The filenames of the exported instance files will follow the pattern: +* For the CGM SV: `_SV.xml`. +* For the IGM SSHs: `__SSH.xml`. The IGM name is built from the country code of the first substation or the IIDM name if no country is present. + +The basename is determined from the parameters, or the basename of the export data source or the main network name. + +As an example, you can export one of the test configurations that has been provided by ENTSO-E. It is available in the cgmes-conformity module of the powsybl-core repository. If you run the following code: + +```java +Network cgmNetwork = Network.read(CgmesConformity1Catalog.microGridBaseCaseAssembled().dataSource()); + +Properties exportParams = new Properties(); +exportParams.put(CgmesExport.EXPORT_BOUNDARY_POWER_FLOWS, true); +exportParams.put(CgmesExport.NAMING_STRATEGY, "cgmes"); +exportParams.put(CgmesExport.CGM_EXPORT, true); +exportParams.put(CgmesExport.UPDATE_DEPENDENCIES, true); +exportParams.put(CgmesExport.MODELING_AUTHORITY_SET, "MAS1"); + +cgmNetwork.write("CGMES", exportParams, new FileDataSource(Path.of("/exampleFolder"), "exampleBase")); +``` + +You will obtain the following files in your `exampleFolder`: + +``` +exampleBase_BE_SSH.xml +exampleBase_NL_SSH.xml +exampleBase_SV.xml +``` + +where the updated SSH files will supersede the original ones, and the SV will contain the correct dependencies of new SSH and original TPs. + +## CGM (Common Grid Model) manual export + +If you want to intervene in how the updated IGM SSH files or the CGM SV are exported, you can make multiple calls to the CGMES export function. + +You can use following code for reference: + +```java +Network cgmNetwork = Network.read(CgmesConformity1Catalog.microGridBaseCaseAssembled().dataSource()); + +// We decide which version we want to export +int exportedVersion = 18; + +// Common export parameters +Properties exportParams = new Properties(); +exportParams.put(CgmesExport.EXPORT_BOUNDARY_POWER_FLOWS, true); +exportParams.put(CgmesExport.NAMING_STRATEGY, "cgmes"); +// We do not want a quick CGM export +exportParams.put(CgmesExport.CGM_EXPORT, false); +exportParams.put(CgmesExport.UPDATE_DEPENDENCIES, false); + +Path outputPath = Path.of("/manualExampleFolder"); +String basename = "manualExampleBasename"; + +// For each subnetwork, prepare the metadata for SSH and export it +for (Network n : cgmNetwork.getSubnetworks()) { + String country = n.getSubstations().iterator().next().getCountry().orElseThrow().toString(); + CgmesMetadataModel sshModel = n.getExtension(CgmesMetadataModels.class).getModelForSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS).orElseThrow(); + sshModel.clearDependencies() + .addDependentOn("myDependency") + .addSupersedes("mySupersede") + .setVersion(exportedVersion) + .setModelingAuthoritySet("myModellingAuthority"); + exportParams.put(CgmesExport.PROFILES, List.of("SSH")); + n.write("CGMES", exportParams, new FileDataSource(outputPath, basename + "_" + country)); +} + +// In the main network, CREATE the metadata for SV and export it +cgmNetwork.newExtension(CgmesMetadataModelsAdder.class) + .newModel() + .setSubset(CgmesSubset.STATE_VARIABLES) + .addProfile("http://entsoe.eu/CIM/StateVariables/4/1") + .setId("mySvId") + .setVersion(exportedVersion) + .setModelingAuthoritySet("myModellinAuthority") + .addDependentOn("mySvDependency1") + .addDependentOn("mySvDependency2") + .add() + .add(); +exportParams.put(CgmesExport.PROFILES, List.of("SV")); +cgmNetwork.write("CGMES", exportParams, new FileDataSource(outputPath, basename)); +``` + +The file `manualExampleBasename_BE_SSH.xml` inside `/manualExampleFolder` will have the following contents for the metadata: + +```xml +... +CGMES Conformity Assessment ... +18 + + +http://entsoe.eu/CIM/SteadyStateHypothesis/1/1 +myModellingAuthority +... +``` + +And the file `manualExampleBasename_SV.xml` will contain: + +```xml +... +18 + + +http://entsoe.eu/CIM/StateVariables/4/1 +myModellinAuthority +... +``` + +Remember that, in addition to setting the info for metadata models in the IIDM extensions, you could also rely on parameters passed to the export methods. ## Conversion from PowSyBl grid model to CGMES @@ -104,6 +251,8 @@ PowSyBl [`ControlAreas`](import.md#cgmes-control-areas) are exported as several These properties can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. +Note that if you are exporting a network that does not come from CGMES, you can use the [`iidm.import.cgmes.boundary-location`](#options) property to define the location of the boundary files to use as reference. + **iidm.export.cgmes.base-name** Optional property that defines the base name of the exported files. Exported CGMES files' names will look like this: ``` @@ -201,4 +350,9 @@ Its default value is 1. The business process in which the export takes place. This is used to generate unique UUIDs for the EQ, TP, SSH and SV file `FullModel`. Its default value is `1D`. -Note that if you are exporting a network that does not come from CGMES, you can use the [`iidm.import.cgmes.boundary-location`](#options) property to define the location of the boundary files to use as reference. +**iidm.export.cgmes.cgm_export** +Optional property to specify the export use-case: IGM (Individual Grid Model) or CGM (Common Grid Model). +To export instance files of a CGM, set the value to `True`. The default value is `False` to export network as an IGM. + +**iidm.export.cgmes.update-dependencies** +Optional property to determine if dependencies in the exported instance files should be managed automatically. The default value is `True`. From 9511a0df48d643370f15368d9005063fae9d7958 Mon Sep 17 00:00:00 2001 From: EtienneLt <32468651+EtienneLt@users.noreply.github.com> Date: Wed, 19 Jun 2024 14:32:24 +0200 Subject: [PATCH 06/57] Allow to use checkLimits methods with LimitReductions (#3056) * Move LimitViolationDetector interface in detectors dedicated package * Replace limitReductionValue by limitsComputer to detect violations * Restore old methods to prevent breaking changes * Optimize SimpleLimitsComputer + add a convenient method in Security Signed-off-by: Etienne LESOT --- .../com/powsybl/iidm/network/Overload.java | 4 + .../AbstractDistinctLimitsContainer.java | 4 + .../network/util/LimitViolationUtils.java | 107 +++++++++++++++--- .../iidm/network/util/OverloadImpl.java | 11 ++ .../util/PermanentLimitCheckResult.java | 17 +++ .../result/LimitsContainerTest.java | 12 ++ .../security/LimitViolationDetection.java | 98 ++++++++-------- .../java/com/powsybl/security/Security.java | 28 ++++- .../limitreduction/SimpleLimitsComputer.java | 40 +++++++ .../computation/AbstractLimitsReducer.java | 2 +- .../computation/SimpleLimitsReducer.java | 35 ++++++ .../security/LimitViolationDetectionTest.java | 48 +++++++- .../com/powsybl/security/SecurityTest.java | 37 +++++- .../impl/DefaultSecurityAnalysis.java | 7 +- 14 files changed, 368 insertions(+), 82 deletions(-) create mode 100644 iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/PermanentLimitCheckResult.java create mode 100644 security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/SimpleLimitsComputer.java create mode 100644 security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/computation/SimpleLimitsReducer.java diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Overload.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Overload.java index cd00ec22496..3d05546eb99 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Overload.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Overload.java @@ -27,4 +27,8 @@ public interface Overload { * The name of the current limit which has been overloaded. */ String getPreviousLimitName(); + + default double getLimitReductionCoefficient() { + return 1; + } } diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/limitmodification/result/AbstractDistinctLimitsContainer.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/limitmodification/result/AbstractDistinctLimitsContainer.java index 966604f9e97..20bbfaf41cc 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/limitmodification/result/AbstractDistinctLimitsContainer.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/limitmodification/result/AbstractDistinctLimitsContainer.java @@ -34,6 +34,10 @@ public boolean isDistinct() { return true; } + public abstract double getPermanentLimitReduction(); + + public abstract Double getTemporaryLimitReduction(int acceptableDuration); + public abstract double getOriginalPermanentLimit(); public abstract Double getOriginalTemporaryLimit(int acceptableDuration); diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/LimitViolationUtils.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/LimitViolationUtils.java index 0a8b3f8dbb3..d2e3ee71b64 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/LimitViolationUtils.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/LimitViolationUtils.java @@ -8,6 +8,9 @@ package com.powsybl.iidm.network.util; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.limitmodification.LimitsComputer; +import com.powsybl.iidm.network.limitmodification.result.AbstractDistinctLimitsContainer; +import com.powsybl.iidm.network.limitmodification.result.LimitsContainer; import java.util.Collection; import java.util.Objects; @@ -29,16 +32,32 @@ private LimitViolationUtils() { public static Overload checkTemporaryLimits(Branch branch, TwoSides side, double limitReductionValue, double i, LimitType type) { Objects.requireNonNull(branch); Objects.requireNonNull(side); - return getLimits(branch, side, type) - .map(limits -> getOverload(limits, i, limitReductionValue)) - .orElse(null); + return getLimits(branch, side.toThreeSides(), type, LimitsComputer.NO_MODIFICATIONS) + .map(limits -> getOverload(limits.getLimits(), i, limitReductionValue)) + .orElse(null); } public static Overload checkTemporaryLimits(ThreeWindingsTransformer transformer, ThreeSides side, double limitReductionValue, double i, LimitType type) { Objects.requireNonNull(transformer); Objects.requireNonNull(side); - return getLimits(transformer, side, type) - .map(limits -> getOverload(limits, i, limitReductionValue)) + return getLimits(transformer, side, type, LimitsComputer.NO_MODIFICATIONS) + .map(limits -> getOverload(limits.getLimits(), i, limitReductionValue)) + .orElse(null); + } + + public static Overload checkTemporaryLimits(Branch branch, TwoSides side, LimitsComputer, LoadingLimits> limitsComputer, double i, LimitType type) { + Objects.requireNonNull(branch); + Objects.requireNonNull(side); + return getLimits(branch, side.toThreeSides(), type, limitsComputer) + .map(limits -> getOverload(limits, i)) + .orElse(null); + } + + public static Overload checkTemporaryLimits(ThreeWindingsTransformer transformer, ThreeSides side, LimitsComputer, LoadingLimits> limitsComputer, double i, LimitType type) { + Objects.requireNonNull(transformer); + Objects.requireNonNull(side); + return getLimits(transformer, side, type, limitsComputer) + .map(limits -> getOverload(limits, i)) .orElse(null); } @@ -60,24 +79,62 @@ private static OverloadImpl getOverload(LoadingLimits limits, double i, double l return null; } - private static boolean checkPermanentLimitIfAny(LoadingLimits limits, double i, double limitReductionValue) { - double permanentLimit = limits.getPermanentLimit(); + private static Overload getOverload(LimitsContainer limitsContainer, double i) { + double permanentLimit = limitsContainer.getLimits().getPermanentLimit(); + if (Double.isNaN(i) || Double.isNaN(permanentLimit)) { + return null; + } + Collection temporaryLimits = limitsContainer.getLimits().getTemporaryLimits(); + String previousLimitName = PERMANENT_LIMIT_NAME; + double previousLimit = permanentLimit; + for (LoadingLimits.TemporaryLimit tl : temporaryLimits) { // iterate in ascending order + if (i >= previousLimit && i < tl.getValue()) { + return new OverloadImpl(tl, previousLimitName, previousLimit, + limitsContainer.isDistinct() ? ((AbstractDistinctLimitsContainer) limitsContainer).getTemporaryLimitReduction(tl.getAcceptableDuration()) : 1); + } + previousLimitName = tl.getName(); + previousLimit = tl.getValue(); + } + return null; + } + + private static PermanentLimitCheckResult checkPermanentLimitIfAny(LimitsContainer limitsContainer, double i) { + return checkPermanentLimitIfAny(limitsContainer, i, 1); + } + + private static PermanentLimitCheckResult checkPermanentLimitIfAny(LimitsContainer limitsContainer, double i, double limitReductionValue) { + double permanentLimit = limitsContainer.getLimits().getPermanentLimit(); if (Double.isNaN(i) || Double.isNaN(permanentLimit)) { - return false; + return new PermanentLimitCheckResult(false, limitReductionValue); + } + if (i >= permanentLimit * limitReductionValue) { + return new PermanentLimitCheckResult(true, limitsContainer.isDistinct() ? ((AbstractDistinctLimitsContainer) limitsContainer).getPermanentLimitReduction() : limitReductionValue); } - return i >= permanentLimit * limitReductionValue; + return new PermanentLimitCheckResult(false, limitReductionValue); } public static boolean checkPermanentLimit(Branch branch, TwoSides side, double limitReductionValue, double i, LimitType type) { - return getLimits(branch, side, type) - .map(l -> checkPermanentLimitIfAny(l, i, limitReductionValue)) - .orElse(false); + return getLimits(branch, side.toThreeSides(), type, LimitsComputer.NO_MODIFICATIONS) + .map(l -> checkPermanentLimitIfAny(l, i, limitReductionValue).isOverload()) + .orElse(false); + } + + public static PermanentLimitCheckResult checkPermanentLimit(Branch branch, TwoSides side, double i, LimitType type, LimitsComputer, LoadingLimits> computer) { + return getLimits(branch, side.toThreeSides(), type, computer) + .map(l -> checkPermanentLimitIfAny(l, i)) + .orElse(new PermanentLimitCheckResult(false, 1.0)); } public static boolean checkPermanentLimit(ThreeWindingsTransformer transformer, ThreeSides side, double limitReductionValue, double i, LimitType type) { - return getLimits(transformer, side, type) - .map(l -> checkPermanentLimitIfAny(l, i, limitReductionValue)) - .orElse(false); + return getLimits(transformer, side, type, LimitsComputer.NO_MODIFICATIONS) + .map(l -> checkPermanentLimitIfAny(l, i, limitReductionValue).isOverload()) + .orElse(false); + } + + public static PermanentLimitCheckResult checkPermanentLimit(ThreeWindingsTransformer transformer, ThreeSides side, LimitsComputer, LoadingLimits> computer, double i, LimitType type) { + return getLimits(transformer, side, type, computer) + .map(l -> checkPermanentLimitIfAny(l, i)) + .orElse(null); } public static double getValueForLimit(Terminal t, LimitType type) { @@ -94,14 +151,34 @@ public static boolean checkPermanentLimit(ThreeWindingsTransformer transformer, return checkPermanentLimit(transformer, side, limitReductionValue, getValueForLimit(transformer.getTerminal(side), type), type); } + public static PermanentLimitCheckResult checkPermanentLimit(ThreeWindingsTransformer transformer, ThreeSides side, LimitType type, LimitsComputer, LoadingLimits> computer) { + return checkPermanentLimit(transformer, side, computer, getValueForLimit(transformer.getTerminal(side), type), type); + } + public static Overload checkTemporaryLimits(ThreeWindingsTransformer transformer, ThreeSides side, double limitReductionValue, LimitType type) { return checkTemporaryLimits(transformer, side, limitReductionValue, getValueForLimit(transformer.getTerminal(side), type), type); } + public static Overload checkTemporaryLimits(ThreeWindingsTransformer transformer, ThreeSides side, LimitType type, LimitsComputer, LoadingLimits> computer) { + return checkTemporaryLimits(transformer, side, computer, getValueForLimit(transformer.getTerminal(side), type), type); + } + + public static Optional> getLimits(Identifiable transformer, ThreeSides side, LimitType type, LimitsComputer, LoadingLimits> computer) { + return computer.computeLimits(transformer, type, side, false); + } + + /** + * @deprecated should use {@link #getLoadingLimits(Identifiable, LimitType, ThreeSides)} instead + */ + @Deprecated(since = "6.4.0") public static Optional getLimits(Branch branch, TwoSides side, LimitType type) { return branch.getLimits(type, side); } + /** + * @deprecated should use {@link #getLoadingLimits(Identifiable, LimitType, ThreeSides)} instead + */ + @Deprecated(since = "6.4.0") public static Optional getLimits(ThreeWindingsTransformer transformer, ThreeSides side, LimitType type) { return transformer.getLeg(side).getLimits(type); } diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/OverloadImpl.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/OverloadImpl.java index 3141967b2a2..9559f864ce8 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/OverloadImpl.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/OverloadImpl.java @@ -24,11 +24,17 @@ public class OverloadImpl implements Overload { private final String previousLimitName; private final double previousLimit; + private final double limitReductionCoefficient; public OverloadImpl(LoadingLimits.TemporaryLimit temporaryLimit, String previousLimitName, double previousLimit) { + this(temporaryLimit, previousLimitName, previousLimit, 1); + } + + public OverloadImpl(LoadingLimits.TemporaryLimit temporaryLimit, String previousLimitName, double previousLimit, double limitReductionCoefficient) { this.temporaryLimit = Objects.requireNonNull(temporaryLimit); this.previousLimitName = previousLimitName; this.previousLimit = previousLimit; + this.limitReductionCoefficient = limitReductionCoefficient; } @Override @@ -45,4 +51,9 @@ public String getPreviousLimitName() { public double getPreviousLimit() { return previousLimit; } + + @Override + public double getLimitReductionCoefficient() { + return limitReductionCoefficient; + } } diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/PermanentLimitCheckResult.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/PermanentLimitCheckResult.java new file mode 100644 index 00000000000..d6cbe9076f0 --- /dev/null +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/PermanentLimitCheckResult.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2014, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.util; + +/** + * + * Class that collects data about an overload of a permanent limit + * + * @author Etienne Lesot {@literal } + */ +public record PermanentLimitCheckResult(boolean isOverload, double limitReductionValue) { +} diff --git a/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/limitmodification/result/LimitsContainerTest.java b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/limitmodification/result/LimitsContainerTest.java index 88cc00870f5..63dda7e4a78 100644 --- a/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/limitmodification/result/LimitsContainerTest.java +++ b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/limitmodification/result/LimitsContainerTest.java @@ -72,6 +72,16 @@ public double getOriginalPermanentLimit() { public Double getOriginalTemporaryLimit(int acceptableDuration) { return originalLimits.getTemporaryLimitValue(acceptableDuration); } + + @Override + public double getPermanentLimitReduction() { + return 0.5; + } + + @Override + public Double getTemporaryLimitReduction(int acceptableDuration) { + return acceptableDuration * 0.001; + } }; assertTrue(container.isDistinct()); assertEquals(originalLimits, container.getOriginalLimits()); @@ -80,5 +90,7 @@ public Double getOriginalTemporaryLimit(int acceptableDuration) { assertEquals(1050., container.getLimits().getTemporaryLimit(300).getValue(), 0.01); assertEquals(1000., container.getOriginalPermanentLimit(), 0.01); assertEquals(1400., container.getOriginalTemporaryLimit(300), 0.01); + assertEquals(0.5, container.getPermanentLimitReduction()); + assertEquals(0.005, container.getTemporaryLimitReduction(5)); } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/LimitViolationDetection.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/LimitViolationDetection.java index 4f634c49cb2..cf1a8ca201d 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/LimitViolationDetection.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/LimitViolationDetection.java @@ -9,7 +9,9 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.limitmodification.LimitsComputer; import com.powsybl.iidm.network.util.LimitViolationUtils; +import com.powsybl.iidm.network.util.PermanentLimitCheckResult; import com.powsybl.security.detectors.LoadingLimitType; import java.util.Set; @@ -30,13 +32,13 @@ private LimitViolationDetection() { * * @param network The network on which physical values must be checked. * @param currentLimitTypes The current limit type to consider. - * @param limitReductionValue The value of the limit reduction to apply. + * @param limitsComputer The computer of the limit reductions to apply. * @param consumer Will be fed with possibly created limit violations. */ public static void checkAll(Network network, Set currentLimitTypes, - double limitReductionValue, Consumer consumer) { - network.getBranchStream().forEach(b -> checkCurrent(b, currentLimitTypes, limitReductionValue, consumer)); - network.getThreeWindingsTransformerStream().forEach(t -> checkCurrent(t, currentLimitTypes, limitReductionValue, consumer)); + LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { + network.getBranchStream().forEach(b -> checkCurrent(b, currentLimitTypes, limitsComputer, consumer)); + network.getThreeWindingsTransformerStream().forEach(t -> checkCurrent(t, currentLimitTypes, limitsComputer, consumer)); network.getVoltageLevelStream() .flatMap(vl -> vl.getBusView().getBusStream()) .forEach(b -> checkVoltage(b, consumer)); @@ -52,38 +54,38 @@ public static void checkAll(Network network, Set currentLimitT * @param network The network on which physical values must be checked. * @param dcPowerFactor The DC power factor used to convert the active power into current. * @param currentLimitTypes The current limit type to consider. - * @param limitReductionValue The value of the limit reduction to apply. + * @param limitsComputer The computer of the limit reductions to apply. * @param consumer Will be fed with possibly created limit violations. */ public static void checkAllDc(Network network, double dcPowerFactor, Set currentLimitTypes, - double limitReductionValue, Consumer consumer) { - network.getBranchStream().forEach(b -> checkCurrentDc(b, dcPowerFactor, currentLimitTypes, limitReductionValue, consumer)); - network.getThreeWindingsTransformerStream().forEach(b -> checkCurrentDc(b, dcPowerFactor, currentLimitTypes, limitReductionValue, consumer)); + LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { + network.getBranchStream().forEach(b -> checkCurrentDc(b, dcPowerFactor, currentLimitTypes, limitsComputer, consumer)); + network.getThreeWindingsTransformerStream().forEach(b -> checkCurrentDc(b, dcPowerFactor, currentLimitTypes, limitsComputer, consumer)); network.getVoltageAngleLimitsStream().forEach(valOk -> checkVoltageAngle(valOk, consumer)); } private static void checkCurrent(Branch branch, Set currentLimitTypes, - double limitReductionValue, Consumer consumer) { - checkCurrent(branch, TwoSides.ONE, currentLimitTypes, limitReductionValue, consumer); - checkCurrent(branch, TwoSides.TWO, currentLimitTypes, limitReductionValue, consumer); + LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { + checkCurrent(branch, TwoSides.ONE, currentLimitTypes, limitsComputer, consumer); + checkCurrent(branch, TwoSides.TWO, currentLimitTypes, limitsComputer, consumer); } private static void checkCurrent(Branch branch, TwoSides side, Set currentLimitTypes, - double limitReductionValue, Consumer consumer) { - checkLimitViolation(branch, side, branch.getTerminal(side).getI(), LimitType.CURRENT, currentLimitTypes, limitReductionValue, consumer); + LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { + checkLimitViolation(branch, side, branch.getTerminal(side).getI(), LimitType.CURRENT, currentLimitTypes, limitsComputer, consumer); } private static void checkCurrentDc(Branch branch, double dcPowerFactor, Set currentLimitTypes, - double limitReductionValue, Consumer consumer) { - checkCurrentDc(branch, TwoSides.ONE, dcPowerFactor, currentLimitTypes, limitReductionValue, consumer); - checkCurrentDc(branch, TwoSides.TWO, dcPowerFactor, currentLimitTypes, limitReductionValue, consumer); + LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { + checkCurrentDc(branch, TwoSides.ONE, dcPowerFactor, currentLimitTypes, limitsComputer, consumer); + checkCurrentDc(branch, TwoSides.TWO, dcPowerFactor, currentLimitTypes, limitsComputer, consumer); } private static void checkCurrentDc(Branch branch, TwoSides side, double dcPowerFactor, - Set currentLimitTypes, double limitReductionValue, + Set currentLimitTypes, LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { double i = getTerminalIOrAnApproximation(branch.getTerminal(side), dcPowerFactor); - checkLimitViolation(branch, side, i, LimitType.CURRENT, currentLimitTypes, limitReductionValue, consumer); + checkLimitViolation(branch, side, i, LimitType.CURRENT, currentLimitTypes, limitsComputer, consumer); } public static double getTerminalIOrAnApproximation(Terminal terminal, double dcPowerFactor) { @@ -96,11 +98,11 @@ public static double getTerminalIOrAnApproximation(Terminal terminal, double dcP } static void checkLimitViolation(Branch branch, TwoSides side, double value, LimitType type, - Set currentLimitTypes, double limitReductionValue, + Set currentLimitTypes, LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { boolean overloadOnTemporary = false; if (currentLimitTypes.contains(LoadingLimitType.TATL)) { - Overload overload = LimitViolationUtils.checkTemporaryLimits(branch, side, limitReductionValue, value, type); + Overload overload = LimitViolationUtils.checkTemporaryLimits(branch, side, limitsComputer, value, type); if (overload != null) { consumer.accept(new LimitViolation(branch.getId(), branch.getOptionalName().orElse("null"), @@ -108,62 +110,64 @@ static void checkLimitViolation(Branch branch, TwoSides side, double value, L overload.getPreviousLimitName(), overload.getTemporaryLimit().getAcceptableDuration(), overload.getPreviousLimit(), - limitReductionValue, + overload.getLimitReductionCoefficient(), value, side)); overloadOnTemporary = true; } } - if (!overloadOnTemporary && currentLimitTypes.contains(LoadingLimitType.PATL) - && LimitViolationUtils.checkPermanentLimit(branch, side, limitReductionValue, value, type)) { - double limit = branch.getLimits(type, side).map(LoadingLimits::getPermanentLimit).orElseThrow(PowsyblException::new); - consumer.accept(new LimitViolation(branch.getId(), + if (!overloadOnTemporary && currentLimitTypes.contains(LoadingLimitType.PATL)) { + PermanentLimitCheckResult overload = LimitViolationUtils.checkPermanentLimit(branch, side, value, type, limitsComputer); + if (overload.isOverload()) { + double limit = branch.getLimits(type, side).map(LoadingLimits::getPermanentLimit).orElseThrow(PowsyblException::new); + consumer.accept(new LimitViolation(branch.getId(), branch.getOptionalName().orElse(null), toLimitViolationType(type), LimitViolationUtils.PERMANENT_LIMIT_NAME, Integer.MAX_VALUE, limit, - limitReductionValue, + overload.limitReductionValue(), value, side)); + } } } private static void checkCurrent(ThreeWindingsTransformer transformer, Set currentLimitTypes, - double limitReductionValue, Consumer consumer) { - checkCurrent(transformer, ThreeSides.ONE, currentLimitTypes, limitReductionValue, consumer); - checkCurrent(transformer, ThreeSides.TWO, currentLimitTypes, limitReductionValue, consumer); - checkCurrent(transformer, ThreeSides.THREE, currentLimitTypes, limitReductionValue, consumer); + LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { + checkCurrent(transformer, ThreeSides.ONE, currentLimitTypes, limitsComputer, consumer); + checkCurrent(transformer, ThreeSides.TWO, currentLimitTypes, limitsComputer, consumer); + checkCurrent(transformer, ThreeSides.THREE, currentLimitTypes, limitsComputer, consumer); } private static void checkCurrent(ThreeWindingsTransformer transformer, ThreeSides side, - Set currentLimitTypes, double limitReductionValue, + Set currentLimitTypes, LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { - checkLimitViolation(transformer, side, transformer.getTerminal(side).getI(), LimitType.CURRENT, currentLimitTypes, limitReductionValue, consumer + checkLimitViolation(transformer, side, transformer.getTerminal(side).getI(), LimitType.CURRENT, currentLimitTypes, limitsComputer, consumer ); } private static void checkCurrentDc(ThreeWindingsTransformer transformer, double dcPowerFactor, - Set currentLimitTypes, double limitReductionValue, + Set currentLimitTypes, LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { - checkCurrentDc(transformer, ThreeSides.ONE, dcPowerFactor, currentLimitTypes, limitReductionValue, consumer); - checkCurrentDc(transformer, ThreeSides.TWO, dcPowerFactor, currentLimitTypes, limitReductionValue, consumer); - checkCurrentDc(transformer, ThreeSides.THREE, dcPowerFactor, currentLimitTypes, limitReductionValue, consumer); + checkCurrentDc(transformer, ThreeSides.ONE, dcPowerFactor, currentLimitTypes, limitsComputer, consumer); + checkCurrentDc(transformer, ThreeSides.TWO, dcPowerFactor, currentLimitTypes, limitsComputer, consumer); + checkCurrentDc(transformer, ThreeSides.THREE, dcPowerFactor, currentLimitTypes, limitsComputer, consumer); } private static void checkCurrentDc(ThreeWindingsTransformer transformer, ThreeSides side, double dcPowerFactor, - Set currentLimitTypes, double limitReductionValue, + Set currentLimitTypes, LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { double i = getTerminalIOrAnApproximation(transformer.getTerminal(side), dcPowerFactor); - checkLimitViolation(transformer, side, i, LimitType.CURRENT, currentLimitTypes, limitReductionValue, consumer); + checkLimitViolation(transformer, side, i, LimitType.CURRENT, currentLimitTypes, limitsComputer, consumer); } static void checkLimitViolation(ThreeWindingsTransformer transformer, ThreeSides side, double value, - LimitType type, Set currentLimitTypes, double limitReductionValue, + LimitType type, Set currentLimitTypes, LimitsComputer, LoadingLimits> limitsComputer, Consumer consumer) { boolean overloadOnTemporary = false; if (currentLimitTypes.contains(LoadingLimitType.TATL)) { - Overload overload = LimitViolationUtils.checkTemporaryLimits(transformer, side, limitReductionValue, value, type); + Overload overload = LimitViolationUtils.checkTemporaryLimits(transformer, side, limitsComputer, value, type); if (overload != null) { consumer.accept(new LimitViolation(transformer.getId(), transformer.getOptionalName().orElse(null), @@ -171,24 +175,26 @@ static void checkLimitViolation(ThreeWindingsTransformer transformer, ThreeSides overload.getPreviousLimitName(), overload.getTemporaryLimit().getAcceptableDuration(), overload.getPreviousLimit(), - limitReductionValue, + overload.getLimitReductionCoefficient(), value, side)); overloadOnTemporary = true; } } - if (!overloadOnTemporary && currentLimitTypes.contains(LoadingLimitType.PATL) - && LimitViolationUtils.checkPermanentLimit(transformer, side, limitReductionValue, value, type)) { - double limit = transformer.getLeg(side).getLimits(type).map(LoadingLimits::getPermanentLimit).orElseThrow(PowsyblException::new); - consumer.accept(new LimitViolation(transformer.getId(), + if (!overloadOnTemporary && currentLimitTypes.contains(LoadingLimitType.PATL)) { + PermanentLimitCheckResult overload = LimitViolationUtils.checkPermanentLimit(transformer, side, limitsComputer, value, type); + if (overload.isOverload()) { + double limit = transformer.getLeg(side).getLimits(type).map(LoadingLimits::getPermanentLimit).orElseThrow(PowsyblException::new); + consumer.accept(new LimitViolation(transformer.getId(), transformer.getOptionalName().orElse(null), toLimitViolationType(type), LimitViolationUtils.PERMANENT_LIMIT_NAME, Integer.MAX_VALUE, limit, - limitReductionValue, + overload.limitReductionValue(), value, side)); + } } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/Security.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/Security.java index 9ffab351c4b..d520b44b301 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/Security.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/Security.java @@ -10,8 +10,12 @@ import com.powsybl.commons.io.table.*; import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.LoadingLimits; import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.limitmodification.LimitsComputer; import com.powsybl.security.detectors.LoadingLimitType; +import com.powsybl.security.limitreduction.SimpleLimitsComputer; import com.powsybl.security.results.PostContingencyResult; import java.io.IOException; @@ -48,7 +52,7 @@ private Security() { } public static List checkLimits(Network network) { - return checkLimits(network, EnumSet.allOf(LoadingLimitType.class), 1f); + return checkLimits(network, EnumSet.allOf(LoadingLimitType.class), LimitsComputer.NO_MODIFICATIONS); } public static List checkLimits(Network network, double limitReductionValue) { @@ -61,28 +65,40 @@ public static List checkLimits(Network network, LoadingLimitType } public static List checkLimits(Network network, Set currentLimitTypes, double limitReductionValue) { - Objects.requireNonNull(network); - Objects.requireNonNull(currentLimitTypes); // allow to increase the limits if (limitReductionValue <= 0) { throw new IllegalArgumentException("Bad limit reduction " + limitReductionValue); } + return checkLimits(network, currentLimitTypes, new SimpleLimitsComputer(limitReductionValue)); + } + + public static List checkLimits(Network network, LimitsComputer, LoadingLimits> limitsComputer) { + return checkLimits(network, EnumSet.allOf(LoadingLimitType.class), limitsComputer); + } + + public static List checkLimits(Network network, Set currentLimitTypes, LimitsComputer, LoadingLimits> limitsComputer) { + Objects.requireNonNull(network); + Objects.requireNonNull(currentLimitTypes); List violations = new ArrayList<>(); - LimitViolationDetection.checkAll(network, currentLimitTypes, limitReductionValue, violations::add); + LimitViolationDetection.checkAll(network, currentLimitTypes, limitsComputer, violations::add); return violations; } public static List checkLimitsDc(Network network, double limitReductionValue, double dcPowerFactor) { - Objects.requireNonNull(network); // allow to increase the limits if (limitReductionValue <= 0) { throw new IllegalArgumentException("Bad limit reduction " + limitReductionValue); } + return checkLimitsDc(network, new SimpleLimitsComputer(limitReductionValue), dcPowerFactor); + } + + public static List checkLimitsDc(Network network, LimitsComputer, LoadingLimits> limitsComputer, double dcPowerFactor) { + Objects.requireNonNull(network); if (dcPowerFactor <= 0 || dcPowerFactor > 1) { throw new IllegalArgumentException("Invalid DC power factor " + dcPowerFactor); } List violations = new ArrayList<>(); - LimitViolationDetection.checkAllDc(network, dcPowerFactor, EnumSet.allOf(LoadingLimitType.class), limitReductionValue, violations::add); + LimitViolationDetection.checkAllDc(network, dcPowerFactor, EnumSet.allOf(LoadingLimitType.class), limitsComputer, violations::add); return violations; } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/SimpleLimitsComputer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/SimpleLimitsComputer.java new file mode 100644 index 00000000000..38befb8d25a --- /dev/null +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/SimpleLimitsComputer.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.security.limitreduction; + +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.LimitType; +import com.powsybl.iidm.network.LoadingLimits; +import com.powsybl.iidm.network.ThreeSides; +import com.powsybl.iidm.network.limitmodification.AbstractLimitsComputerWithCache; +import com.powsybl.iidm.network.limitmodification.result.LimitsContainer; +import com.powsybl.iidm.network.util.LimitViolationUtils; +import com.powsybl.security.limitreduction.computation.SimpleLimitsReducer; + +import java.util.Optional; + +/** + *

A limits computer that takes a single limit reduction value for all the identifiers, and applies it on each permanent and temporary limit, for pre- and post-contingency states.

+ *

Computed values are stored in cache to avoid recomputing them.

+ * + * @author Etienne Lesot {@literal } + */ +public class SimpleLimitsComputer extends AbstractLimitsComputerWithCache, LoadingLimits> { + + private final double limitReduction; + + public SimpleLimitsComputer(double limitReduction) { + this.limitReduction = limitReduction; + } + + @Override + protected Optional> computeUncachedLimits(Identifiable identifiable, LimitType limitType, ThreeSides side, boolean monitoringOnly) { + Optional limits = LimitViolationUtils.getLoadingLimits(identifiable, limitType, side); + return limits.map(limits1 -> new SimpleLimitsReducer(limits1, limitReduction).getLimits()); + } +} diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/computation/AbstractLimitsReducer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/computation/AbstractLimitsReducer.java index d1eefcd9d02..73520c213ca 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/computation/AbstractLimitsReducer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/computation/AbstractLimitsReducer.java @@ -47,7 +47,7 @@ protected AbstractLimitsReducer(L originalLimits) { public abstract IntStream getTemporaryLimitsAcceptableDurationStream(); public LimitsContainer getLimits() { - if (permanentLimitReduction == 1.0 + if (getPermanentLimitReduction() == 1.0 && (temporaryLimitReductionByAcceptableDuration.isEmpty() || temporaryLimitReductionByAcceptableDuration.values().stream().allMatch(v -> v == 1.0))) { // No reductions are applicable diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/computation/SimpleLimitsReducer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/computation/SimpleLimitsReducer.java new file mode 100644 index 00000000000..a2e35bcf74a --- /dev/null +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/computation/SimpleLimitsReducer.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.security.limitreduction.computation; + +import com.powsybl.iidm.network.LoadingLimits; + +/** + * Limits reducer applying the same reduction value for the permanent and every temporary limits. + * + * @author Etienne Lesot {@literal } + */ +public class SimpleLimitsReducer extends DefaultLimitsReducer { + + private final double limitReduction; + + public SimpleLimitsReducer(LoadingLimits originalLimits, double limitReduction) { + super(originalLimits); + this.limitReduction = limitReduction; + } + + @Override + public double getPermanentLimitReduction() { + return limitReduction; + } + + @Override + public double getTemporaryLimitReduction(int acceptableDuration) { + return limitReduction; + } +} diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationDetectionTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationDetectionTest.java index b4640bca1e0..e4153090eaa 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationDetectionTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationDetectionTest.java @@ -7,13 +7,22 @@ */ package com.powsybl.security; +import com.powsybl.contingency.ContingencyContext; +import com.powsybl.iidm.criteria.NetworkElementIdListCriterion; +import com.powsybl.iidm.criteria.duration.PermanentDurationCriterion; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.limitmodification.LimitsComputer; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.security.detectors.AbstractLimitViolationDetectionTest; import com.powsybl.security.detectors.LoadingLimitType; +import com.powsybl.security.limitreduction.DefaultLimitReductionsApplier; +import com.powsybl.security.limitreduction.LimitReduction; +import com.powsybl.security.limitreduction.SimpleLimitsComputer; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import java.util.ArrayList; -import java.util.EnumSet; +import java.util.*; import java.util.function.Consumer; /** @@ -29,14 +38,22 @@ void setUp() { @Override protected void checkLimitViolation(Branch branch, TwoSides side, double currentValue, Consumer consumer, LimitType limitType, double limitReduction) { - LimitViolationDetection.checkLimitViolation(branch, side, currentValue, limitType, EnumSet.allOf(LoadingLimitType.class), limitReduction, consumer + LimitViolationDetection.checkLimitViolation(branch, side, currentValue, limitType, EnumSet.allOf(LoadingLimitType.class), new SimpleLimitsComputer(limitReduction), consumer ); } private void checkLimitViolation(ThreeWindingsTransformer transfo, ThreeSides side, double currentValue, Consumer consumer, LimitType limitType) { - LimitViolationDetection.checkLimitViolation(transfo, side, currentValue, limitType, EnumSet.allOf(LoadingLimitType.class), 1.0, consumer - ); + LimitViolationDetection.checkLimitViolation(transfo, side, currentValue, limitType, EnumSet.allOf(LoadingLimitType.class), new LimitsComputer.NoModificationsImpl(), consumer); + } + + private void checkLimitViolation(Branch branch, TwoSides side, double currentValue, Consumer consumer, + LimitType limitType, LimitsComputer, LoadingLimits> limitsComputer) { + LimitViolationDetection.checkLimitViolation(branch, side, currentValue, limitType, EnumSet.allOf(LoadingLimitType.class), limitsComputer, consumer); + } + + protected void checkCurrent(Branch branch, TwoSides side, double currentValue, Consumer consumer, LimitsComputer, LoadingLimits> limitsComputer) { + checkLimitViolation(branch, side, currentValue, consumer, LimitType.CURRENT, limitsComputer); } @Override @@ -78,4 +95,25 @@ protected void checkVoltage(Bus b, int voltageValue, Consumer co protected void checkVoltageAngle(VoltageAngleLimit voltageAngleLimit, double voltageAngleDifference, Consumer consumer) { LimitViolationDetection.checkVoltageAngle(voltageAngleLimit, voltageAngleDifference, consumer); } + + @Test + void testLimitsComputer() { + Network network = EurostagTutorialExample1Factory.createWithFixedCurrentLimits(); + List limitReductionList = new ArrayList<>(); + LimitReduction reduction1 = LimitReduction.builder(LimitType.CURRENT, 0.5) + .withMonitoringOnly(false) + .withContingencyContext(ContingencyContext.none()) + .withNetworkElementCriteria(new NetworkElementIdListCriterion(Set.of("NHV1_NHV2_1"))) + .withLimitDurationCriteria(new PermanentDurationCriterion()) + .build(); + limitReductionList.add(reduction1); + DefaultLimitReductionsApplier computer = new DefaultLimitReductionsApplier(limitReductionList); + checkCurrent(network.getLine("NHV1_NHV2_1"), TwoSides.ONE, 315, violationsCollector::add, computer); + Assertions.assertEquals(1, violationsCollector.size()); + Assertions.assertEquals(0.5, violationsCollector.get(0).getLimitReduction()); + Assertions.assertEquals(Integer.MAX_VALUE, violationsCollector.get(0).getAcceptableDuration()); + Assertions.assertEquals(315, violationsCollector.get(0).getValue(), 0.01); + Assertions.assertEquals(500., violationsCollector.get(0).getLimit(), 0.01); + Assertions.assertEquals(ThreeSides.ONE, violationsCollector.get(0).getSide()); + } } diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/SecurityTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/SecurityTest.java index ce668016ac3..ba4fa40d1f6 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/SecurityTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/SecurityTest.java @@ -10,21 +10,18 @@ import com.powsybl.commons.io.table.CsvTableFormatterFactory; import com.powsybl.commons.io.table.TableFormatterConfig; import com.powsybl.contingency.Contingency; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.TwoSides; +import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.iidm.network.test.ThreeWindingsTransformerNetworkFactory; import com.powsybl.loadflow.LoadFlowResult; +import com.powsybl.security.limitreduction.SimpleLimitsComputer; import com.powsybl.security.results.PostContingencyResult; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.io.StringWriter; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Locale; +import java.util.*; import static org.junit.jupiter.api.Assertions.*; @@ -132,6 +129,34 @@ void checkLimits() { assertViolations(violations); } + @Test + void checkLimits05() { + Line line = network.getLine(EurostagTutorialExample1Factory.NHV1_NHV2_1); + line.getCurrentLimits1().ifPresent(l -> l.setPermanentLimit(2000)); + + List violations = Security.checkLimits(network); + assertTrue(getCurrentLimitViolationOnLine1Side1(violations).isEmpty()); + + violations = Security.checkLimits(network, 0.5); + assertViolation05(violations); + + violations = Security.checkLimits(network, new SimpleLimitsComputer(0.5)); + assertViolation05(violations); + } + + private static Optional getCurrentLimitViolationOnLine1Side1(List violations) { + return violations.stream().filter(l -> EurostagTutorialExample1Factory.NHV1_NHV2_1.equals(l.getSubjectId()) && + l.getSide() == ThreeSides.ONE && l.getLimitType() == LimitViolationType.CURRENT).findFirst(); + } + + private static void assertViolation05(List violations) { + Optional violation = getCurrentLimitViolationOnLine1Side1(violations); + assertTrue(violation.isPresent()); + assertEquals(0.5, violation.get().getLimitReduction(), 0.001d); + assertEquals(2000, violation.get().getLimit(), 0.001d); + assertEquals(1192, violation.get().getValue(), 1d); + } + private static void assertViolationsForThreeWindingsTransformer(List violations) { assertEquals(3, violations.size()); violations.forEach(violation -> { diff --git a/security-analysis/security-analysis-default/src/main/java/com/powsybl/security/impl/DefaultSecurityAnalysis.java b/security-analysis/security-analysis-default/src/main/java/com/powsybl/security/impl/DefaultSecurityAnalysis.java index 714f704e745..55c6142b5ea 100644 --- a/security-analysis/security-analysis-default/src/main/java/com/powsybl/security/impl/DefaultSecurityAnalysis.java +++ b/security-analysis/security-analysis-default/src/main/java/com/powsybl/security/impl/DefaultSecurityAnalysis.java @@ -14,6 +14,7 @@ import com.powsybl.contingency.ContingenciesProvider; import com.powsybl.contingency.Contingency; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.limitmodification.LimitsComputer; import com.powsybl.loadflow.LoadFlow; import com.powsybl.loadflow.LoadFlowParameters; import com.powsybl.loadflow.LoadFlowResult; @@ -296,7 +297,7 @@ private void checkPreContingencyViolations(Network network, Consumer Date: Wed, 19 Jun 2024 15:59:33 +0200 Subject: [PATCH 07/57] Support of condensers (#3065) * Add isCondenser boolean parameter to generators * Add attribute to readthedoc table * Add unit test Signed-off-by: Florian Dupuy --- docs/grid_model/network_subnetwork.md | 28 +++++++++++-------- .../com/powsybl/iidm/network/Generator.java | 5 ++++ .../powsybl/iidm/network/GeneratorAdder.java | 5 ++++ .../iidm/network/impl/GeneratorAdderImpl.java | 10 ++++++- .../iidm/network/impl/GeneratorImpl.java | 12 ++++++-- .../powsybl/iidm/serde/GeneratorSerDe.java | 5 ++++ .../src/main/resources/xsd/iidm_V1_13.xsd | 1 + .../resources/xsd/iidm_equipment_V1_13.xsd | 1 + .../iidm/serde/AbstractIidmSerDeTest.java | 10 +++++++ .../iidm/serde/GeneratorSerDeTest.java | 23 +++++++++++++++ .../src/test/resources/V1_12/generator.xml | 20 +++++++++++++ .../src/test/resources/V1_13/generator.xml | 20 +++++++++++++ .../network/tck/AbstractGeneratorTest.java | 9 ++++-- 13 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/GeneratorSerDeTest.java create mode 100644 iidm/iidm-serde/src/test/resources/V1_12/generator.xml create mode 100644 iidm/iidm-serde/src/test/resources/V1_13/generator.xml diff --git a/docs/grid_model/network_subnetwork.md b/docs/grid_model/network_subnetwork.md index f8d3fcbd2af..1295082713a 100644 --- a/docs/grid_model/network_subnetwork.md +++ b/docs/grid_model/network_subnetwork.md @@ -159,18 +159,19 @@ A generator is an equipment that injects or consumes active power, and injects o **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- |-------------------------------------------------------------| -| $MinP$ | MW | Minimum generator active power output | -| $MaxP$ | MW | Maximum generator active power output | -| $ReactiveLimits$ | MVar | Operational limits of the generator (P/Q/V diagram) | -| $RatedS$ | MVA | The rated nominal power | -| $TargetP$ | MW | The active power target | -| $TargetQ$ | MVAr | The reactive power target at local terminal | -| $TargetV$ | kV | The voltage target at regulating terminal | -| $RegulatingTerminal$ | | Associated node or bus for which voltage is to be regulated | -| $VoltageRegulatorOn$ | | True if the generator regulates voltage | -| $EnergySource$ | | The energy source harnessed to turn the generator | +| Attribute | Unit | Description | +|----------------------|------|-------------------------------------------------------------| +| $MinP$ | MW | Minimum generator active power output | +| $MaxP$ | MW | Maximum generator active power output | +| $ReactiveLimits$ | MVar | Operational limits of the generator (P/Q/V diagram) | +| $RatedS$ | MVA | The rated nominal power | +| $TargetP$ | MW | The active power target | +| $TargetQ$ | MVAr | The reactive power target at local terminal | +| $TargetV$ | kV | The voltage target at regulating terminal | +| $RegulatingTerminal$ | | Associated node or bus for which voltage is to be regulated | +| $VoltageRegulatorOn$ | | True if the generator regulates voltage | +| $EnergySource$ | | The energy source harnessed to turn the generator | +| $IsCondenser$ | | True if the generator may behave as a condenser | **Specifications** @@ -180,6 +181,9 @@ The `VoltageRegulatorOn` attribute is required. It voltage regulation is enabled Target values for generators (`TargetP` and `TargetQ`) follow the generator sign convention: a positive value means an injection into the bus. Positive values for `TargetP` and `TargetQ` mean negative values at the flow observed at the generator `Terminal`, as `Terminal` flow always follows load sign convention. The following diagram shows the sign convention of these quantities with an example. +The `isCondenser` value corresponds for instance to generators which can control voltage even if their targetP is equal to zero. + + **Available extensions** - [Active Power Control](extensions.md#active-power-control) diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Generator.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Generator.java index 99020f55ab6..29d107ec9bf 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Generator.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Generator.java @@ -242,6 +242,11 @@ public interface Generator extends Injection, ReactiveLimitsHolder { Generator setRatedS(double ratedS); + /** + * Get whether the generator may behave as a condenser, for instance if it may control voltage even if its targetP is equal to zero. + */ + boolean isCondenser(); + @Override default IdentifiableType getType() { return IdentifiableType.GENERATOR; diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/GeneratorAdder.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/GeneratorAdder.java index 9131614c2fa..f84a65fff9f 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/GeneratorAdder.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/GeneratorAdder.java @@ -48,6 +48,11 @@ public interface GeneratorAdder extends InjectionAdderThese are the checks that are performed before creating the object : diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorAdderImpl.java index 15481d880e3..45104f441b1 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorAdderImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorAdderImpl.java @@ -33,6 +33,8 @@ class GeneratorAdderImpl extends AbstractInjectionAdder impl private double ratedS = Double.NaN; + private boolean isCondenser = false; + GeneratorAdderImpl(VoltageLevelExt voltageLevel) { this.voltageLevel = voltageLevel; } @@ -96,6 +98,12 @@ public GeneratorAdder setRatedS(double ratedS) { return this; } + @Override + public GeneratorAdder setCondenser(boolean isCondenser) { + this.isCondenser = isCondenser; + return this; + } + @Override public GeneratorImpl add() { NetworkImpl network = getNetwork(); @@ -121,7 +129,7 @@ id, getName(), isFictitious(), energySource, minP, maxP, voltageRegulatorOn, regulatingTerminal != null ? regulatingTerminal : terminal, targetP, targetQ, targetV, - ratedS); + ratedS, isCondenser); generator.addTerminal(terminal); voltageLevel.attach(terminal, false); network.getIndex().checkAndAdd(generator); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorImpl.java index 8f1ac3782f8..a5ba5df417f 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/GeneratorImpl.java @@ -7,8 +7,8 @@ */ package com.powsybl.iidm.network.impl; -import com.powsybl.iidm.network.*; import com.powsybl.commons.ref.Ref; +import com.powsybl.iidm.network.*; import gnu.trove.list.array.TDoubleArrayList; /** @@ -38,12 +38,14 @@ class GeneratorImpl extends AbstractConnectable implements Generator, private final TDoubleArrayList targetV; + private final boolean isCondenser; + GeneratorImpl(Ref network, String id, String name, boolean fictitious, EnergySource energySource, double minP, double maxP, boolean voltageRegulatorOn, TerminalExt regulatingTerminal, double targetP, double targetQ, double targetV, - double ratedS) { + double ratedS, boolean isCondenser) { super(network, id, name, fictitious); this.network = network; this.energySource = energySource; @@ -62,6 +64,7 @@ class GeneratorImpl extends AbstractConnectable implements Generator, this.targetQ.add(targetQ); this.targetV.add(targetV); } + this.isCondenser = isCondenser; } @Override @@ -214,6 +217,11 @@ public GeneratorImpl setRatedS(double ratedS) { return this; } + @Override + public boolean isCondenser() { + return isCondenser; + } + @Override public ReactiveCapabilityCurveAdderImpl newReactiveCapabilityCurve() { return new ReactiveCapabilityCurveAdderImpl(this); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java index 77a35ab0771..7b2b05ae679 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java @@ -8,6 +8,7 @@ package com.powsybl.iidm.serde; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.serde.util.IidmSerDeUtil; import static com.powsybl.iidm.serde.ConnectableSerDeUtil.*; @@ -36,6 +37,8 @@ protected void writeRootElementAttributes(Generator g, VoltageLevel vl, NetworkS context.getWriter().writeDoubleAttribute("targetP", g.getTargetP()); context.getWriter().writeDoubleAttribute("targetV", g.getTargetV()); context.getWriter().writeDoubleAttribute("targetQ", g.getTargetQ()); + IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_13, context, () -> + context.getWriter().writeBooleanAttribute("isCondenser", g.isCondenser(), false)); writeNodeOrBus(null, g.getTerminal(), context); writePQ(null, g.getTerminal(), context.getWriter()); } @@ -63,6 +66,8 @@ protected Generator readRootElementAttributes(GeneratorAdder adder, VoltageLevel double targetP = context.getReader().readDoubleAttribute("targetP"); double targetV = context.getReader().readDoubleAttribute("targetV"); double targetQ = context.getReader().readDoubleAttribute("targetQ"); + IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_13, context, () -> + adder.setCondenser(context.getReader().readBooleanAttribute("isCondenser", false))); readNodeOrBus(adder, context, voltageLevel.getTopologyKind()); adder.setEnergySource(energySource) .setMinP(minP) diff --git a/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd b/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd index 2b2c7fc657a..1fd74826fea 100644 --- a/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd +++ b/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd @@ -255,6 +255,7 @@ + diff --git a/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd b/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd index cb3d503842d..910e3cbee44 100644 --- a/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd +++ b/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd @@ -255,6 +255,7 @@ + diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java index bab7a07ad1d..4bbb856f3f9 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java @@ -103,6 +103,16 @@ protected void allFormatsRoundTripFromVersionedXmlFromMinToCurrentVersionTest(St .toArray(IidmVersion[]::new)); } + /** + * Execute a round trip test on the test resource IIDM-XML file with a given file name for all IIDM versions + * equals or more recent than a given minimum IIDM version. + */ + protected void allFormatsRoundTripFromVersionedXmlFromMinVersionTest(String file, IidmVersion minVersion) throws IOException { + allFormatsRoundTripFromVersionedXmlTest(file, Stream.of(IidmVersion.values()) + .filter(v -> v.compareTo(minVersion) >= 0) + .toArray(IidmVersion[]::new)); + } + /** * Execute a round trip test on the test resource IIDM-JSON file with a given file name for all IIDM versions * equals or more recent than a given minimum IIDM version and strictly older than the current IIDM version. diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/GeneratorSerDeTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/GeneratorSerDeTest.java new file mode 100644 index 00000000000..83a7803f3b6 --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/GeneratorSerDeTest.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +/** + * @author Florian Dupuy {@literal } + */ +class GeneratorSerDeTest extends AbstractIidmSerDeTest { + + @Test + void isCondenserTest() throws IOException { + allFormatsRoundTripFromVersionedXmlFromMinVersionTest("generator.xml", IidmVersion.V_1_12); + } +} diff --git a/iidm/iidm-serde/src/test/resources/V1_12/generator.xml b/iidm/iidm-serde/src/test/resources/V1_12/generator.xml new file mode 100644 index 00000000000..35356dd2c17 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_12/generator.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/resources/V1_13/generator.xml b/iidm/iidm-serde/src/test/resources/V1_13/generator.xml new file mode 100644 index 00000000000..b9f1a889d94 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_13/generator.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractGeneratorTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractGeneratorTest.java index 957d38b98fc..a6ffc76b4ac 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractGeneratorTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractGeneratorTest.java @@ -70,19 +70,20 @@ public void testSetterGetter() { assertFalse(generator.isVoltageRegulatorOn()); generator.setVoltageRegulatorOn(true); assertTrue(generator.isVoltageRegulatorOn()); + assertFalse(generator.isCondenser()); assertEquals(12, generator.getTerminal().getNodeBreakerView().getNode()); } @Test public void undefinedVoltageRegulatorOn() { - ValidationException e = assertThrows(ValidationException.class, () -> voltageLevel.newGenerator() + GeneratorAdder generatorAdder = voltageLevel.newGenerator() .setId("GEN") .setMaxP(Double.MAX_VALUE) .setMinP(-Double.MAX_VALUE) .setTargetP(30.0) - .setNode(1) - .add()); + .setNode(1); + ValidationException e = assertThrows(ValidationException.class, generatorAdder::add); assertEquals("Generator 'GEN': voltage regulator status is not set", e.getMessage()); } @@ -191,6 +192,7 @@ public void testAdder() { .setTargetQ(20.0) .setNode(1) .setTargetV(31.0) + .setCondenser(true) .add(); Generator generator = network.getGenerator(GEN_ID); assertNotNull(generator); @@ -203,6 +205,7 @@ public void testAdder() { assertEquals(30.0, generator.getTargetP(), 0.0); assertEquals(20.0, generator.getTargetQ(), 0.0); assertEquals(31.0, generator.getTargetV(), 0.0); + assertTrue(generator.isCondenser()); } @Test From 27f39d0907096457636f51ecae193fd64dcd9df2 Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 19 Jun 2024 17:13:29 +0200 Subject: [PATCH 08/57] Support ActivePowerControl extension v1.2 only since IIDM v1.13 (#3068) Signed-off-by: Olivier Perrin --- .../iidm/serde/extensions/ActivePowerControlSerDe.java | 2 +- .../iidm/serde/extensions/ActivePowerControlXmlTest.java | 4 ++-- .../V1_12/activePowerControlWithLimitRoundTripRef.xml | 6 +++--- .../resources/V1_12/eurostag-tutorial1-lf-extensions.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java index 01c38b76d90..6c318c9b59a 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java @@ -45,7 +45,7 @@ public ActivePowerControlSerDe() { .put(IidmVersion.V_1_9, ImmutableSortedSet.of("1.0", "1.1")) .put(IidmVersion.V_1_10, ImmutableSortedSet.of("1.0", "1.1")) .put(IidmVersion.V_1_11, ImmutableSortedSet.of("1.0", "1.1")) - .put(IidmVersion.V_1_12, ImmutableSortedSet.of("1.0", "1.1", "1.2")) + .put(IidmVersion.V_1_12, ImmutableSortedSet.of("1.0", "1.1")) .put(IidmVersion.V_1_13, ImmutableSortedSet.of("1.2")) .build(), new ImmutableMap.Builder() diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java index 613e0a6b5f9..fc8a63e5361 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java @@ -68,8 +68,8 @@ void testPLimitOverride() throws IOException { } @Test - void testIidmV11() throws IOException { - Network network2 = allFormatsRoundTripTest(network, "/activePowerControlRoundTripRef.xml", IidmVersion.V_1_11); + void testIidmV12() throws IOException { + Network network2 = allFormatsRoundTripTest(network, "/activePowerControlRoundTripRef.xml", IidmVersion.V_1_12); Battery bat2 = network2.getBattery("BAT"); assertNotNull(bat2); diff --git a/iidm/iidm-serde/src/test/resources/V1_12/activePowerControlWithLimitRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_12/activePowerControlWithLimitRoundTripRef.xml index 469356d8c44..27d9aac0b82 100644 --- a/iidm/iidm-serde/src/test/resources/V1_12/activePowerControlWithLimitRoundTripRef.xml +++ b/iidm/iidm-serde/src/test/resources/V1_12/activePowerControlWithLimitRoundTripRef.xml @@ -1,5 +1,5 @@ - + @@ -30,9 +30,9 @@ - + - + \ No newline at end of file diff --git a/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.json b/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.json index 7fa2484b7ec..e517fec6c07 100644 --- a/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.json +++ b/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.json @@ -5,7 +5,7 @@ "version" : "1.1" }, { "extensionName" : "activePowerControl", - "version" : "1.2" + "version" : "1.1" } ], "id" : "sim1", "caseDate" : "2013-01-15T18:45:00.000+01:00", From c24f8d1633da42af2c4ee7fd3e6d07566cb1b020 Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 19 Jun 2024 17:38:06 +0200 Subject: [PATCH 09/57] Bump to v6.4.0-RC1 Signed-off-by: Olivier Perrin --- action-api/pom.xml | 2 +- action-ial/action-ial-dsl-spi/pom.xml | 2 +- action-ial/action-ial-dsl/pom.xml | 2 +- action-ial/action-ial-simulator/pom.xml | 2 +- action-ial/action-ial-util/pom.xml | 2 +- action-ial/pom.xml | 2 +- ampl-converter/pom.xml | 2 +- ampl-executor/pom.xml | 2 +- cgmes/cgmes-completion/pom.xml | 2 +- cgmes/cgmes-conformity/pom.xml | 2 +- cgmes/cgmes-conversion/pom.xml | 2 +- cgmes/cgmes-extensions/pom.xml | 2 +- cgmes/cgmes-gl/pom.xml | 2 +- cgmes/cgmes-measurements/pom.xml | 2 +- cgmes/cgmes-model-alternatives/pom.xml | 2 +- cgmes/cgmes-model-test/pom.xml | 2 +- cgmes/cgmes-model/pom.xml | 2 +- cgmes/cgmes-shortcircuit/pom.xml | 2 +- cgmes/pom.xml | 2 +- cim-anonymiser/pom.xml | 2 +- commons-test/pom.xml | 2 +- commons/pom.xml | 2 +- computation-local-test/pom.xml | 2 +- computation-local/pom.xml | 2 +- computation/pom.xml | 2 +- config-classic/pom.xml | 2 +- config-test/pom.xml | 2 +- contingency/contingency-api/pom.xml | 2 +- contingency/contingency-dsl/pom.xml | 2 +- contingency/pom.xml | 2 +- distribution-core/pom.xml | 2 +- dsl/pom.xml | 2 +- dynamic-security-analysis/pom.xml | 2 +- dynamic-simulation/dynamic-simulation-api/pom.xml | 2 +- dynamic-simulation/dynamic-simulation-dsl/pom.xml | 2 +- dynamic-simulation/dynamic-simulation-tool/pom.xml | 2 +- dynamic-simulation/pom.xml | 2 +- entsoe-util/pom.xml | 2 +- ieee-cdf/ieee-cdf-converter/pom.xml | 2 +- ieee-cdf/ieee-cdf-model/pom.xml | 2 +- ieee-cdf/pom.xml | 2 +- iidm/iidm-api/pom.xml | 2 +- iidm/iidm-comparator/pom.xml | 2 +- iidm/iidm-criteria/pom.xml | 2 +- iidm/iidm-extensions/pom.xml | 2 +- iidm/iidm-geodata/pom.xml | 2 +- iidm/iidm-impl/pom.xml | 2 +- iidm/iidm-modification/pom.xml | 2 +- iidm/iidm-reducer/pom.xml | 2 +- iidm/iidm-scripting/pom.xml | 2 +- iidm/iidm-serde/pom.xml | 2 +- iidm/iidm-tck/pom.xml | 2 +- iidm/iidm-test/pom.xml | 2 +- iidm/pom.xml | 2 +- itools-packager/pom.xml | 2 +- loadflow/loadflow-api/pom.xml | 2 +- loadflow/loadflow-results-completion/pom.xml | 2 +- loadflow/loadflow-scripting/pom.xml | 2 +- loadflow/loadflow-validation/pom.xml | 2 +- loadflow/pom.xml | 2 +- math/pom.xml | 2 +- matpower/matpower-converter/pom.xml | 2 +- matpower/matpower-model/pom.xml | 2 +- matpower/pom.xml | 2 +- pom.xml | 2 +- powerfactory/pom.xml | 2 +- powerfactory/powerfactory-converter/pom.xml | 2 +- powerfactory/powerfactory-db/pom.xml | 2 +- powerfactory/powerfactory-dgs/pom.xml | 2 +- powerfactory/powerfactory-model/pom.xml | 2 +- psse/pom.xml | 2 +- psse/psse-converter/pom.xml | 2 +- psse/psse-model-test/pom.xml | 2 +- psse/psse-model/pom.xml | 2 +- scripting-test/pom.xml | 2 +- scripting/pom.xml | 2 +- security-analysis/pom.xml | 2 +- security-analysis/security-analysis-api/pom.xml | 2 +- security-analysis/security-analysis-default/pom.xml | 2 +- sensitivity-analysis-api/pom.xml | 2 +- shortcircuit-api/pom.xml | 2 +- time-series/pom.xml | 2 +- time-series/time-series-api/pom.xml | 2 +- time-series/time-series-dsl/pom.xml | 2 +- tools-test/pom.xml | 2 +- tools/pom.xml | 2 +- triple-store/pom.xml | 2 +- triple-store/triple-store-api/pom.xml | 2 +- triple-store/triple-store-impl-rdf4j/pom.xml | 2 +- triple-store/triple-store-test/pom.xml | 2 +- ucte/pom.xml | 2 +- ucte/ucte-converter/pom.xml | 2 +- ucte/ucte-network/pom.xml | 2 +- ucte/ucte-util/pom.xml | 2 +- 94 files changed, 94 insertions(+), 94 deletions(-) diff --git a/action-api/pom.xml b/action-api/pom.xml index 802f8771399..00902028f4f 100644 --- a/action-api/pom.xml +++ b/action-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-action-api diff --git a/action-ial/action-ial-dsl-spi/pom.xml b/action-ial/action-ial-dsl-spi/pom.xml index ec11ffeef35..3fa55757787 100644 --- a/action-ial/action-ial-dsl-spi/pom.xml +++ b/action-ial/action-ial-dsl-spi/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-action-ial - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-action-ial-dsl-spi diff --git a/action-ial/action-ial-dsl/pom.xml b/action-ial/action-ial-dsl/pom.xml index 07028cb7933..d690c50c202 100644 --- a/action-ial/action-ial-dsl/pom.xml +++ b/action-ial/action-ial-dsl/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-action-ial - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-action-ial-dsl diff --git a/action-ial/action-ial-simulator/pom.xml b/action-ial/action-ial-simulator/pom.xml index 52e1ce325e8..b5352a09f21 100644 --- a/action-ial/action-ial-simulator/pom.xml +++ b/action-ial/action-ial-simulator/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-action-ial - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-action-ial-simulator diff --git a/action-ial/action-ial-util/pom.xml b/action-ial/action-ial-util/pom.xml index 9415c93ad31..73f91424231 100644 --- a/action-ial/action-ial-util/pom.xml +++ b/action-ial/action-ial-util/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-action-ial - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-action-ial-util diff --git a/action-ial/pom.xml b/action-ial/pom.xml index b36754b36e9..31fec23834b 100644 --- a/action-ial/pom.xml +++ b/action-ial/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/ampl-converter/pom.xml b/ampl-converter/pom.xml index 2350676a03a..4faa6a686cf 100644 --- a/ampl-converter/pom.xml +++ b/ampl-converter/pom.xml @@ -14,7 +14,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-ampl-converter diff --git a/ampl-executor/pom.xml b/ampl-executor/pom.xml index b00ab84ac34..6c7ed151f1c 100644 --- a/ampl-executor/pom.xml +++ b/ampl-executor/pom.xml @@ -15,7 +15,7 @@ this com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-ampl-executor diff --git a/cgmes/cgmes-completion/pom.xml b/cgmes/cgmes-completion/pom.xml index 5b18d04b3cf..a4b4920578b 100644 --- a/cgmes/cgmes-completion/pom.xml +++ b/cgmes/cgmes-completion/pom.xml @@ -16,7 +16,7 @@ com.powsybl powsybl-cgmes - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes-completion diff --git a/cgmes/cgmes-conformity/pom.xml b/cgmes/cgmes-conformity/pom.xml index 2b5f215d758..6b6443ed063 100644 --- a/cgmes/cgmes-conformity/pom.xml +++ b/cgmes/cgmes-conformity/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes-conformity diff --git a/cgmes/cgmes-conversion/pom.xml b/cgmes/cgmes-conversion/pom.xml index 44e1a2f94b8..2c406f8e24c 100644 --- a/cgmes/cgmes-conversion/pom.xml +++ b/cgmes/cgmes-conversion/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes-conversion diff --git a/cgmes/cgmes-extensions/pom.xml b/cgmes/cgmes-extensions/pom.xml index 077258ce1d6..0614a009705 100644 --- a/cgmes/cgmes-extensions/pom.xml +++ b/cgmes/cgmes-extensions/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes-extensions diff --git a/cgmes/cgmes-gl/pom.xml b/cgmes/cgmes-gl/pom.xml index 355effae73b..2d31620b6e1 100644 --- a/cgmes/cgmes-gl/pom.xml +++ b/cgmes/cgmes-gl/pom.xml @@ -14,7 +14,7 @@ powsybl-cgmes com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes-gl diff --git a/cgmes/cgmes-measurements/pom.xml b/cgmes/cgmes-measurements/pom.xml index 3ec2792c8e3..3236dd6e076 100644 --- a/cgmes/cgmes-measurements/pom.xml +++ b/cgmes/cgmes-measurements/pom.xml @@ -13,7 +13,7 @@ powsybl-cgmes com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 4.0.0 diff --git a/cgmes/cgmes-model-alternatives/pom.xml b/cgmes/cgmes-model-alternatives/pom.xml index 41edce55dd2..8d772ea51fe 100644 --- a/cgmes/cgmes-model-alternatives/pom.xml +++ b/cgmes/cgmes-model-alternatives/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes-model-alternatives diff --git a/cgmes/cgmes-model-test/pom.xml b/cgmes/cgmes-model-test/pom.xml index c5fd1eef39f..652af421bf0 100644 --- a/cgmes/cgmes-model-test/pom.xml +++ b/cgmes/cgmes-model-test/pom.xml @@ -15,7 +15,7 @@ powsybl-cgmes com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes-model-test diff --git a/cgmes/cgmes-model/pom.xml b/cgmes/cgmes-model/pom.xml index 175e89a4636..58ccff81977 100644 --- a/cgmes/cgmes-model/pom.xml +++ b/cgmes/cgmes-model/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes-model diff --git a/cgmes/cgmes-shortcircuit/pom.xml b/cgmes/cgmes-shortcircuit/pom.xml index 91a7033cb9b..6f79c524317 100644 --- a/cgmes/cgmes-shortcircuit/pom.xml +++ b/cgmes/cgmes-shortcircuit/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes-shortcircuit diff --git a/cgmes/pom.xml b/cgmes/pom.xml index 5d7d2c1e5c1..8fbfc09f987 100644 --- a/cgmes/pom.xml +++ b/cgmes/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cgmes diff --git a/cim-anonymiser/pom.xml b/cim-anonymiser/pom.xml index 8ee1126258b..04b0d8ffffd 100644 --- a/cim-anonymiser/pom.xml +++ b/cim-anonymiser/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-cim-anonymiser diff --git a/commons-test/pom.xml b/commons-test/pom.xml index 9cd0343d983..c0ba3df9f52 100644 --- a/commons-test/pom.xml +++ b/commons-test/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-commons-test diff --git a/commons/pom.xml b/commons/pom.xml index 0627d408916..98a679916f0 100644 --- a/commons/pom.xml +++ b/commons/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-commons diff --git a/computation-local-test/pom.xml b/computation-local-test/pom.xml index 5183714146a..d97bc9bd772 100644 --- a/computation-local-test/pom.xml +++ b/computation-local-test/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-computation-local-test diff --git a/computation-local/pom.xml b/computation-local/pom.xml index 4608b4ebd24..60bbe96512a 100644 --- a/computation-local/pom.xml +++ b/computation-local/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-computation-local diff --git a/computation/pom.xml b/computation/pom.xml index cc48619c300..a401c2b5961 100644 --- a/computation/pom.xml +++ b/computation/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-computation diff --git a/config-classic/pom.xml b/config-classic/pom.xml index 4092251a08f..bc362eca2a1 100644 --- a/config-classic/pom.xml +++ b/config-classic/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-config-classic diff --git a/config-test/pom.xml b/config-test/pom.xml index f0ce9f27b4d..0cfe2b6c95e 100644 --- a/config-test/pom.xml +++ b/config-test/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-config-test diff --git a/contingency/contingency-api/pom.xml b/contingency/contingency-api/pom.xml index 501a9ad55fa..bf4370842be 100644 --- a/contingency/contingency-api/pom.xml +++ b/contingency/contingency-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-contingency - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-contingency-api diff --git a/contingency/contingency-dsl/pom.xml b/contingency/contingency-dsl/pom.xml index 0b8250a6d2a..5498ab16419 100644 --- a/contingency/contingency-dsl/pom.xml +++ b/contingency/contingency-dsl/pom.xml @@ -14,7 +14,7 @@ com.powsybl powsybl-contingency - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-contingency-dsl diff --git a/contingency/pom.xml b/contingency/pom.xml index c6250ec7b7f..1b775d2b6f9 100644 --- a/contingency/pom.xml +++ b/contingency/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/distribution-core/pom.xml b/distribution-core/pom.xml index 61601b8190a..60020bc3404 100644 --- a/distribution-core/pom.xml +++ b/distribution-core/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/dsl/pom.xml b/dsl/pom.xml index aa6b7df8055..dc3da9bf314 100644 --- a/dsl/pom.xml +++ b/dsl/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 4.0.0 diff --git a/dynamic-security-analysis/pom.xml b/dynamic-security-analysis/pom.xml index 5684b4f4fc5..950baa30ec1 100644 --- a/dynamic-security-analysis/pom.xml +++ b/dynamic-security-analysis/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-dynamic-security-analysis diff --git a/dynamic-simulation/dynamic-simulation-api/pom.xml b/dynamic-simulation/dynamic-simulation-api/pom.xml index ccd725415e7..db892ec4b88 100644 --- a/dynamic-simulation/dynamic-simulation-api/pom.xml +++ b/dynamic-simulation/dynamic-simulation-api/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-dynamic-simulation - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-dynamic-simulation-api diff --git a/dynamic-simulation/dynamic-simulation-dsl/pom.xml b/dynamic-simulation/dynamic-simulation-dsl/pom.xml index f8d3fd0e595..7dbb4d6e51d 100644 --- a/dynamic-simulation/dynamic-simulation-dsl/pom.xml +++ b/dynamic-simulation/dynamic-simulation-dsl/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-dynamic-simulation - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-dynamic-simulation-dsl diff --git a/dynamic-simulation/dynamic-simulation-tool/pom.xml b/dynamic-simulation/dynamic-simulation-tool/pom.xml index 831838ade7e..035fc8a9c51 100644 --- a/dynamic-simulation/dynamic-simulation-tool/pom.xml +++ b/dynamic-simulation/dynamic-simulation-tool/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-dynamic-simulation - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-dynamic-simulation-tool diff --git a/dynamic-simulation/pom.xml b/dynamic-simulation/pom.xml index 59cd89a5941..788edef43fa 100644 --- a/dynamic-simulation/pom.xml +++ b/dynamic-simulation/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/entsoe-util/pom.xml b/entsoe-util/pom.xml index c34b6a71197..159a2f75d0d 100644 --- a/entsoe-util/pom.xml +++ b/entsoe-util/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-entsoe-util diff --git a/ieee-cdf/ieee-cdf-converter/pom.xml b/ieee-cdf/ieee-cdf-converter/pom.xml index cfda88f4ca4..347140b45fb 100644 --- a/ieee-cdf/ieee-cdf-converter/pom.xml +++ b/ieee-cdf/ieee-cdf-converter/pom.xml @@ -13,7 +13,7 @@ powsybl-ieee-cdf com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-ieee-cdf-converter diff --git a/ieee-cdf/ieee-cdf-model/pom.xml b/ieee-cdf/ieee-cdf-model/pom.xml index 93cce5ad818..0ad873e92c0 100644 --- a/ieee-cdf/ieee-cdf-model/pom.xml +++ b/ieee-cdf/ieee-cdf-model/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-ieee-cdf - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-ieee-cdf-model diff --git a/ieee-cdf/pom.xml b/ieee-cdf/pom.xml index 1d00247b55e..c32654a342c 100644 --- a/ieee-cdf/pom.xml +++ b/ieee-cdf/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/iidm/iidm-api/pom.xml b/iidm/iidm-api/pom.xml index 5032500eecc..53ea910ae61 100644 --- a/iidm/iidm-api/pom.xml +++ b/iidm/iidm-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-api diff --git a/iidm/iidm-comparator/pom.xml b/iidm/iidm-comparator/pom.xml index 17239139110..8abf54fa875 100644 --- a/iidm/iidm-comparator/pom.xml +++ b/iidm/iidm-comparator/pom.xml @@ -15,7 +15,7 @@ powsybl-iidm com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-comparator diff --git a/iidm/iidm-criteria/pom.xml b/iidm/iidm-criteria/pom.xml index e1e52d700a2..d96370cb48b 100644 --- a/iidm/iidm-criteria/pom.xml +++ b/iidm/iidm-criteria/pom.xml @@ -16,7 +16,7 @@ powsybl-iidm com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-criteria diff --git a/iidm/iidm-extensions/pom.xml b/iidm/iidm-extensions/pom.xml index ddf092b3d92..a890fcb10cc 100644 --- a/iidm/iidm-extensions/pom.xml +++ b/iidm/iidm-extensions/pom.xml @@ -15,7 +15,7 @@ powsybl-iidm com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-extensions diff --git a/iidm/iidm-geodata/pom.xml b/iidm/iidm-geodata/pom.xml index 87f0ea7407b..6d42d987c15 100644 --- a/iidm/iidm-geodata/pom.xml +++ b/iidm/iidm-geodata/pom.xml @@ -14,7 +14,7 @@ powsybl-iidm com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-geodata diff --git a/iidm/iidm-impl/pom.xml b/iidm/iidm-impl/pom.xml index b87f52f857e..405bf884068 100644 --- a/iidm/iidm-impl/pom.xml +++ b/iidm/iidm-impl/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-impl diff --git a/iidm/iidm-modification/pom.xml b/iidm/iidm-modification/pom.xml index 7bb1c8e4dd1..fcf522829d3 100644 --- a/iidm/iidm-modification/pom.xml +++ b/iidm/iidm-modification/pom.xml @@ -13,7 +13,7 @@ powsybl-iidm com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-modification diff --git a/iidm/iidm-reducer/pom.xml b/iidm/iidm-reducer/pom.xml index e45b02df275..b128b813011 100644 --- a/iidm/iidm-reducer/pom.xml +++ b/iidm/iidm-reducer/pom.xml @@ -15,7 +15,7 @@ powsybl-iidm com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-reducer diff --git a/iidm/iidm-scripting/pom.xml b/iidm/iidm-scripting/pom.xml index 603cd26b09c..e5dfb997678 100644 --- a/iidm/iidm-scripting/pom.xml +++ b/iidm/iidm-scripting/pom.xml @@ -15,7 +15,7 @@ powsybl-iidm com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-scripting diff --git a/iidm/iidm-serde/pom.xml b/iidm/iidm-serde/pom.xml index e6346655f95..4ada28f788b 100644 --- a/iidm/iidm-serde/pom.xml +++ b/iidm/iidm-serde/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-serde diff --git a/iidm/iidm-tck/pom.xml b/iidm/iidm-tck/pom.xml index d84e137458f..646507563fb 100644 --- a/iidm/iidm-tck/pom.xml +++ b/iidm/iidm-tck/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-tck diff --git a/iidm/iidm-test/pom.xml b/iidm/iidm-test/pom.xml index 34be832182e..b9868708d32 100644 --- a/iidm/iidm-test/pom.xml +++ b/iidm/iidm-test/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-iidm-test diff --git a/iidm/pom.xml b/iidm/pom.xml index 15e69c2175a..e1eb39d0117 100644 --- a/iidm/pom.xml +++ b/iidm/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/itools-packager/pom.xml b/itools-packager/pom.xml index 4df50564b1e..3195c4a0417 100644 --- a/itools-packager/pom.xml +++ b/itools-packager/pom.xml @@ -14,7 +14,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-itools-packager-maven-plugin diff --git a/loadflow/loadflow-api/pom.xml b/loadflow/loadflow-api/pom.xml index e8b2af9e124..febe3f71944 100644 --- a/loadflow/loadflow-api/pom.xml +++ b/loadflow/loadflow-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-loadflow - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-loadflow-api diff --git a/loadflow/loadflow-results-completion/pom.xml b/loadflow/loadflow-results-completion/pom.xml index 7527505f2eb..3cc06abf21e 100644 --- a/loadflow/loadflow-results-completion/pom.xml +++ b/loadflow/loadflow-results-completion/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-loadflow - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-loadflow-results-completion diff --git a/loadflow/loadflow-scripting/pom.xml b/loadflow/loadflow-scripting/pom.xml index b0ee4890ba0..141de3395bd 100644 --- a/loadflow/loadflow-scripting/pom.xml +++ b/loadflow/loadflow-scripting/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-loadflow - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-loadflow-scripting diff --git a/loadflow/loadflow-validation/pom.xml b/loadflow/loadflow-validation/pom.xml index d329a6eec5a..f98d374f14b 100644 --- a/loadflow/loadflow-validation/pom.xml +++ b/loadflow/loadflow-validation/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-loadflow - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-loadflow-validation diff --git a/loadflow/pom.xml b/loadflow/pom.xml index db75017b0a7..edbc0e7ada8 100644 --- a/loadflow/pom.xml +++ b/loadflow/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/math/pom.xml b/math/pom.xml index aef1869cc32..ee6aea3cd22 100644 --- a/math/pom.xml +++ b/math/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-math diff --git a/matpower/matpower-converter/pom.xml b/matpower/matpower-converter/pom.xml index 124c781fbbd..4622523274b 100644 --- a/matpower/matpower-converter/pom.xml +++ b/matpower/matpower-converter/pom.xml @@ -13,7 +13,7 @@ powsybl-matpower com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-matpower-converter diff --git a/matpower/matpower-model/pom.xml b/matpower/matpower-model/pom.xml index 70bd0c09726..dbc76f346d9 100644 --- a/matpower/matpower-model/pom.xml +++ b/matpower/matpower-model/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-matpower - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-matpower-model diff --git a/matpower/pom.xml b/matpower/pom.xml index 27da617b2a4..ed44bbc4996 100644 --- a/matpower/pom.xml +++ b/matpower/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/pom.xml b/pom.xml index 970657b1839..9fa2720fa22 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom powsybl diff --git a/powerfactory/pom.xml b/powerfactory/pom.xml index a2a16ec08de..be0e89cf8c3 100644 --- a/powerfactory/pom.xml +++ b/powerfactory/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/powerfactory/powerfactory-converter/pom.xml b/powerfactory/powerfactory-converter/pom.xml index 7ee0d9abc33..d8335399f58 100644 --- a/powerfactory/powerfactory-converter/pom.xml +++ b/powerfactory/powerfactory-converter/pom.xml @@ -13,7 +13,7 @@ powsybl-powerfactory com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-powerfactory-converter diff --git a/powerfactory/powerfactory-db/pom.xml b/powerfactory/powerfactory-db/pom.xml index 8448104d936..17964e9cfd4 100644 --- a/powerfactory/powerfactory-db/pom.xml +++ b/powerfactory/powerfactory-db/pom.xml @@ -13,7 +13,7 @@ powsybl-powerfactory com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-powerfactory-db diff --git a/powerfactory/powerfactory-dgs/pom.xml b/powerfactory/powerfactory-dgs/pom.xml index ac9e5354953..6cd7bceb9b0 100644 --- a/powerfactory/powerfactory-dgs/pom.xml +++ b/powerfactory/powerfactory-dgs/pom.xml @@ -13,7 +13,7 @@ powsybl-powerfactory com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-powerfactory-dgs diff --git a/powerfactory/powerfactory-model/pom.xml b/powerfactory/powerfactory-model/pom.xml index b8da7347794..2abceb7090e 100644 --- a/powerfactory/powerfactory-model/pom.xml +++ b/powerfactory/powerfactory-model/pom.xml @@ -13,7 +13,7 @@ powsybl-powerfactory com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-powerfactory-model diff --git a/psse/pom.xml b/psse/pom.xml index 6ae42afb447..e84691f68f5 100644 --- a/psse/pom.xml +++ b/psse/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/psse/psse-converter/pom.xml b/psse/psse-converter/pom.xml index bd3d5220860..24a22651978 100644 --- a/psse/psse-converter/pom.xml +++ b/psse/psse-converter/pom.xml @@ -11,7 +11,7 @@ powsybl-psse com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 4.0.0 diff --git a/psse/psse-model-test/pom.xml b/psse/psse-model-test/pom.xml index 1c2c03fb68b..e23016a8c94 100644 --- a/psse/psse-model-test/pom.xml +++ b/psse/psse-model-test/pom.xml @@ -13,7 +13,7 @@ powsybl-psse com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-psse-model-test diff --git a/psse/psse-model/pom.xml b/psse/psse-model/pom.xml index c9567117589..faaeaad6946 100644 --- a/psse/psse-model/pom.xml +++ b/psse/psse-model/pom.xml @@ -13,7 +13,7 @@ powsybl-psse com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-psse-model diff --git a/scripting-test/pom.xml b/scripting-test/pom.xml index ab751207ab3..501e7ee8279 100644 --- a/scripting-test/pom.xml +++ b/scripting-test/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-scripting-test diff --git a/scripting/pom.xml b/scripting/pom.xml index c1d0c90c3a6..1e792794d5c 100644 --- a/scripting/pom.xml +++ b/scripting/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-scripting diff --git a/security-analysis/pom.xml b/security-analysis/pom.xml index 303cfa03b55..6516e3ece2e 100644 --- a/security-analysis/pom.xml +++ b/security-analysis/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/security-analysis/security-analysis-api/pom.xml b/security-analysis/security-analysis-api/pom.xml index 5d4e94fa4e5..3428bd47e8b 100644 --- a/security-analysis/security-analysis-api/pom.xml +++ b/security-analysis/security-analysis-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-security-analysis - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-security-analysis-api diff --git a/security-analysis/security-analysis-default/pom.xml b/security-analysis/security-analysis-default/pom.xml index ee9c73c77d3..173858d8868 100644 --- a/security-analysis/security-analysis-default/pom.xml +++ b/security-analysis/security-analysis-default/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-security-analysis - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-security-analysis-default diff --git a/sensitivity-analysis-api/pom.xml b/sensitivity-analysis-api/pom.xml index 4a3776cca12..8b11aabd7c3 100644 --- a/sensitivity-analysis-api/pom.xml +++ b/sensitivity-analysis-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-sensitivity-analysis-api diff --git a/shortcircuit-api/pom.xml b/shortcircuit-api/pom.xml index d3d250d7f52..8c91f0fe43a 100644 --- a/shortcircuit-api/pom.xml +++ b/shortcircuit-api/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-shortcircuit-api diff --git a/time-series/pom.xml b/time-series/pom.xml index d991c4f43f8..3847a85a901 100644 --- a/time-series/pom.xml +++ b/time-series/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/time-series/time-series-api/pom.xml b/time-series/time-series-api/pom.xml index ebadfc362ed..d40a0aad9c2 100644 --- a/time-series/time-series-api/pom.xml +++ b/time-series/time-series-api/pom.xml @@ -15,7 +15,7 @@ powsybl-time-series com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-time-series-api diff --git a/time-series/time-series-dsl/pom.xml b/time-series/time-series-dsl/pom.xml index ff962f05edf..a310dfaaf01 100644 --- a/time-series/time-series-dsl/pom.xml +++ b/time-series/time-series-dsl/pom.xml @@ -15,7 +15,7 @@ powsybl-time-series com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-time-series-dsl diff --git a/tools-test/pom.xml b/tools-test/pom.xml index a7a7b56e69a..649e451a999 100644 --- a/tools-test/pom.xml +++ b/tools-test/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-tools-test diff --git a/tools/pom.xml b/tools/pom.xml index 35be6ead595..ea808cc9f4d 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-tools diff --git a/triple-store/pom.xml b/triple-store/pom.xml index 62362fc2ef1..1751006f2e4 100644 --- a/triple-store/pom.xml +++ b/triple-store/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/triple-store/triple-store-api/pom.xml b/triple-store/triple-store-api/pom.xml index dbb0e36e175..ae178a93b6d 100644 --- a/triple-store/triple-store-api/pom.xml +++ b/triple-store/triple-store-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-triple-store - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-triple-store-api diff --git a/triple-store/triple-store-impl-rdf4j/pom.xml b/triple-store/triple-store-impl-rdf4j/pom.xml index 84346f7b1ea..321f9b8c8bf 100644 --- a/triple-store/triple-store-impl-rdf4j/pom.xml +++ b/triple-store/triple-store-impl-rdf4j/pom.xml @@ -11,7 +11,7 @@ com.powsybl powsybl-triple-store - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-triple-store-impl-rdf4j diff --git a/triple-store/triple-store-test/pom.xml b/triple-store/triple-store-test/pom.xml index 26493a89498..e9cbb73a3c0 100644 --- a/triple-store/triple-store-test/pom.xml +++ b/triple-store/triple-store-test/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-triple-store - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-triple-store-test diff --git a/ucte/pom.xml b/ucte/pom.xml index 420eece2a3d..cace33a42b2 100644 --- a/ucte/pom.xml +++ b/ucte/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-SNAPSHOT + 6.4.0-RC1 pom diff --git a/ucte/ucte-converter/pom.xml b/ucte/ucte-converter/pom.xml index 8c875f29621..7b925d9aedb 100644 --- a/ucte/ucte-converter/pom.xml +++ b/ucte/ucte-converter/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-ucte - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-ucte-converter diff --git a/ucte/ucte-network/pom.xml b/ucte/ucte-network/pom.xml index c2c421afc4a..4241bb55984 100644 --- a/ucte/ucte-network/pom.xml +++ b/ucte/ucte-network/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-ucte - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-ucte-network diff --git a/ucte/ucte-util/pom.xml b/ucte/ucte-util/pom.xml index 29938834a4b..74172939e92 100644 --- a/ucte/ucte-util/pom.xml +++ b/ucte/ucte-util/pom.xml @@ -7,7 +7,7 @@ com.powsybl powsybl-ucte - 6.4.0-SNAPSHOT + 6.4.0-RC1 powsybl-ucte-util From f79a54d702a540ad64317602e65cc47db531c4cb Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 19 Jun 2024 17:38:56 +0200 Subject: [PATCH 10/57] Bump to v6.5.0-SNAPSHOT Signed-off-by: Olivier Perrin --- action-api/pom.xml | 2 +- action-ial/action-ial-dsl-spi/pom.xml | 2 +- action-ial/action-ial-dsl/pom.xml | 2 +- action-ial/action-ial-simulator/pom.xml | 2 +- action-ial/action-ial-util/pom.xml | 2 +- action-ial/pom.xml | 2 +- ampl-converter/pom.xml | 2 +- ampl-executor/pom.xml | 2 +- cgmes/cgmes-completion/pom.xml | 2 +- cgmes/cgmes-conformity/pom.xml | 2 +- cgmes/cgmes-conversion/pom.xml | 2 +- cgmes/cgmes-extensions/pom.xml | 2 +- cgmes/cgmes-gl/pom.xml | 2 +- cgmes/cgmes-measurements/pom.xml | 2 +- cgmes/cgmes-model-alternatives/pom.xml | 2 +- cgmes/cgmes-model-test/pom.xml | 2 +- cgmes/cgmes-model/pom.xml | 2 +- cgmes/cgmes-shortcircuit/pom.xml | 2 +- cgmes/pom.xml | 2 +- cim-anonymiser/pom.xml | 2 +- commons-test/pom.xml | 2 +- commons/pom.xml | 2 +- computation-local-test/pom.xml | 2 +- computation-local/pom.xml | 2 +- computation/pom.xml | 2 +- config-classic/pom.xml | 2 +- config-test/pom.xml | 2 +- contingency/contingency-api/pom.xml | 2 +- contingency/contingency-dsl/pom.xml | 2 +- contingency/pom.xml | 2 +- distribution-core/pom.xml | 2 +- dsl/pom.xml | 2 +- dynamic-security-analysis/pom.xml | 2 +- dynamic-simulation/dynamic-simulation-api/pom.xml | 2 +- dynamic-simulation/dynamic-simulation-dsl/pom.xml | 2 +- dynamic-simulation/dynamic-simulation-tool/pom.xml | 2 +- dynamic-simulation/pom.xml | 2 +- entsoe-util/pom.xml | 2 +- ieee-cdf/ieee-cdf-converter/pom.xml | 2 +- ieee-cdf/ieee-cdf-model/pom.xml | 2 +- ieee-cdf/pom.xml | 2 +- iidm/iidm-api/pom.xml | 2 +- iidm/iidm-comparator/pom.xml | 2 +- iidm/iidm-criteria/pom.xml | 2 +- iidm/iidm-extensions/pom.xml | 2 +- iidm/iidm-geodata/pom.xml | 2 +- iidm/iidm-impl/pom.xml | 2 +- iidm/iidm-modification/pom.xml | 2 +- iidm/iidm-reducer/pom.xml | 2 +- iidm/iidm-scripting/pom.xml | 2 +- iidm/iidm-serde/pom.xml | 2 +- iidm/iidm-tck/pom.xml | 2 +- iidm/iidm-test/pom.xml | 2 +- iidm/pom.xml | 2 +- itools-packager/pom.xml | 2 +- loadflow/loadflow-api/pom.xml | 2 +- loadflow/loadflow-results-completion/pom.xml | 2 +- loadflow/loadflow-scripting/pom.xml | 2 +- loadflow/loadflow-validation/pom.xml | 2 +- loadflow/pom.xml | 2 +- math/pom.xml | 2 +- matpower/matpower-converter/pom.xml | 2 +- matpower/matpower-model/pom.xml | 2 +- matpower/pom.xml | 2 +- pom.xml | 2 +- powerfactory/pom.xml | 2 +- powerfactory/powerfactory-converter/pom.xml | 2 +- powerfactory/powerfactory-db/pom.xml | 2 +- powerfactory/powerfactory-dgs/pom.xml | 2 +- powerfactory/powerfactory-model/pom.xml | 2 +- psse/pom.xml | 2 +- psse/psse-converter/pom.xml | 2 +- psse/psse-model-test/pom.xml | 2 +- psse/psse-model/pom.xml | 2 +- scripting-test/pom.xml | 2 +- scripting/pom.xml | 2 +- security-analysis/pom.xml | 2 +- security-analysis/security-analysis-api/pom.xml | 2 +- security-analysis/security-analysis-default/pom.xml | 2 +- sensitivity-analysis-api/pom.xml | 2 +- shortcircuit-api/pom.xml | 2 +- time-series/pom.xml | 2 +- time-series/time-series-api/pom.xml | 2 +- time-series/time-series-dsl/pom.xml | 2 +- tools-test/pom.xml | 2 +- tools/pom.xml | 2 +- triple-store/pom.xml | 2 +- triple-store/triple-store-api/pom.xml | 2 +- triple-store/triple-store-impl-rdf4j/pom.xml | 2 +- triple-store/triple-store-test/pom.xml | 2 +- ucte/pom.xml | 2 +- ucte/ucte-converter/pom.xml | 2 +- ucte/ucte-network/pom.xml | 2 +- ucte/ucte-util/pom.xml | 2 +- 94 files changed, 94 insertions(+), 94 deletions(-) diff --git a/action-api/pom.xml b/action-api/pom.xml index 00902028f4f..d31076b5d21 100644 --- a/action-api/pom.xml +++ b/action-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-action-api diff --git a/action-ial/action-ial-dsl-spi/pom.xml b/action-ial/action-ial-dsl-spi/pom.xml index 3fa55757787..a21bf1fb75b 100644 --- a/action-ial/action-ial-dsl-spi/pom.xml +++ b/action-ial/action-ial-dsl-spi/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-action-ial - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-action-ial-dsl-spi diff --git a/action-ial/action-ial-dsl/pom.xml b/action-ial/action-ial-dsl/pom.xml index d690c50c202..163b2b49309 100644 --- a/action-ial/action-ial-dsl/pom.xml +++ b/action-ial/action-ial-dsl/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-action-ial - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-action-ial-dsl diff --git a/action-ial/action-ial-simulator/pom.xml b/action-ial/action-ial-simulator/pom.xml index b5352a09f21..3ebb03d8a19 100644 --- a/action-ial/action-ial-simulator/pom.xml +++ b/action-ial/action-ial-simulator/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-action-ial - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-action-ial-simulator diff --git a/action-ial/action-ial-util/pom.xml b/action-ial/action-ial-util/pom.xml index 73f91424231..35145aab174 100644 --- a/action-ial/action-ial-util/pom.xml +++ b/action-ial/action-ial-util/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-action-ial - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-action-ial-util diff --git a/action-ial/pom.xml b/action-ial/pom.xml index 31fec23834b..b1617bef202 100644 --- a/action-ial/pom.xml +++ b/action-ial/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/ampl-converter/pom.xml b/ampl-converter/pom.xml index 4faa6a686cf..d5b560d0821 100644 --- a/ampl-converter/pom.xml +++ b/ampl-converter/pom.xml @@ -14,7 +14,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-ampl-converter diff --git a/ampl-executor/pom.xml b/ampl-executor/pom.xml index 6c7ed151f1c..8b69404fbdd 100644 --- a/ampl-executor/pom.xml +++ b/ampl-executor/pom.xml @@ -15,7 +15,7 @@ this com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-ampl-executor diff --git a/cgmes/cgmes-completion/pom.xml b/cgmes/cgmes-completion/pom.xml index a4b4920578b..4e9e914cdc0 100644 --- a/cgmes/cgmes-completion/pom.xml +++ b/cgmes/cgmes-completion/pom.xml @@ -16,7 +16,7 @@ com.powsybl powsybl-cgmes - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes-completion diff --git a/cgmes/cgmes-conformity/pom.xml b/cgmes/cgmes-conformity/pom.xml index 6b6443ed063..98daae50a9d 100644 --- a/cgmes/cgmes-conformity/pom.xml +++ b/cgmes/cgmes-conformity/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes-conformity diff --git a/cgmes/cgmes-conversion/pom.xml b/cgmes/cgmes-conversion/pom.xml index 2c406f8e24c..2efdddb8029 100644 --- a/cgmes/cgmes-conversion/pom.xml +++ b/cgmes/cgmes-conversion/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes-conversion diff --git a/cgmes/cgmes-extensions/pom.xml b/cgmes/cgmes-extensions/pom.xml index 0614a009705..a0ad4b54e58 100644 --- a/cgmes/cgmes-extensions/pom.xml +++ b/cgmes/cgmes-extensions/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes-extensions diff --git a/cgmes/cgmes-gl/pom.xml b/cgmes/cgmes-gl/pom.xml index 2d31620b6e1..7280bc5aa36 100644 --- a/cgmes/cgmes-gl/pom.xml +++ b/cgmes/cgmes-gl/pom.xml @@ -14,7 +14,7 @@ powsybl-cgmes com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes-gl diff --git a/cgmes/cgmes-measurements/pom.xml b/cgmes/cgmes-measurements/pom.xml index 3236dd6e076..a8b86ddd520 100644 --- a/cgmes/cgmes-measurements/pom.xml +++ b/cgmes/cgmes-measurements/pom.xml @@ -13,7 +13,7 @@ powsybl-cgmes com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT 4.0.0 diff --git a/cgmes/cgmes-model-alternatives/pom.xml b/cgmes/cgmes-model-alternatives/pom.xml index 8d772ea51fe..abd05416cca 100644 --- a/cgmes/cgmes-model-alternatives/pom.xml +++ b/cgmes/cgmes-model-alternatives/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes-model-alternatives diff --git a/cgmes/cgmes-model-test/pom.xml b/cgmes/cgmes-model-test/pom.xml index 652af421bf0..995dcb17e45 100644 --- a/cgmes/cgmes-model-test/pom.xml +++ b/cgmes/cgmes-model-test/pom.xml @@ -15,7 +15,7 @@ powsybl-cgmes com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes-model-test diff --git a/cgmes/cgmes-model/pom.xml b/cgmes/cgmes-model/pom.xml index 58ccff81977..1d64c022297 100644 --- a/cgmes/cgmes-model/pom.xml +++ b/cgmes/cgmes-model/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes-model diff --git a/cgmes/cgmes-shortcircuit/pom.xml b/cgmes/cgmes-shortcircuit/pom.xml index 6f79c524317..04f4c578696 100644 --- a/cgmes/cgmes-shortcircuit/pom.xml +++ b/cgmes/cgmes-shortcircuit/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-cgmes - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes-shortcircuit diff --git a/cgmes/pom.xml b/cgmes/pom.xml index 8fbfc09f987..df3030b6883 100644 --- a/cgmes/pom.xml +++ b/cgmes/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cgmes diff --git a/cim-anonymiser/pom.xml b/cim-anonymiser/pom.xml index 04b0d8ffffd..131a6507e3b 100644 --- a/cim-anonymiser/pom.xml +++ b/cim-anonymiser/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-cim-anonymiser diff --git a/commons-test/pom.xml b/commons-test/pom.xml index c0ba3df9f52..85ce2d800dc 100644 --- a/commons-test/pom.xml +++ b/commons-test/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-commons-test diff --git a/commons/pom.xml b/commons/pom.xml index 98a679916f0..38f5e5b959a 100644 --- a/commons/pom.xml +++ b/commons/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-commons diff --git a/computation-local-test/pom.xml b/computation-local-test/pom.xml index d97bc9bd772..b09372b1fba 100644 --- a/computation-local-test/pom.xml +++ b/computation-local-test/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-computation-local-test diff --git a/computation-local/pom.xml b/computation-local/pom.xml index 60bbe96512a..513a0a668cf 100644 --- a/computation-local/pom.xml +++ b/computation-local/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-computation-local diff --git a/computation/pom.xml b/computation/pom.xml index a401c2b5961..02e2fc9c485 100644 --- a/computation/pom.xml +++ b/computation/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-computation diff --git a/config-classic/pom.xml b/config-classic/pom.xml index bc362eca2a1..4def6b2afe7 100644 --- a/config-classic/pom.xml +++ b/config-classic/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-config-classic diff --git a/config-test/pom.xml b/config-test/pom.xml index 0cfe2b6c95e..15a884ac9e4 100644 --- a/config-test/pom.xml +++ b/config-test/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-config-test diff --git a/contingency/contingency-api/pom.xml b/contingency/contingency-api/pom.xml index bf4370842be..6250113dd3d 100644 --- a/contingency/contingency-api/pom.xml +++ b/contingency/contingency-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-contingency - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-contingency-api diff --git a/contingency/contingency-dsl/pom.xml b/contingency/contingency-dsl/pom.xml index 5498ab16419..9c3827174ea 100644 --- a/contingency/contingency-dsl/pom.xml +++ b/contingency/contingency-dsl/pom.xml @@ -14,7 +14,7 @@ com.powsybl powsybl-contingency - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-contingency-dsl diff --git a/contingency/pom.xml b/contingency/pom.xml index 1b775d2b6f9..43617a24ee6 100644 --- a/contingency/pom.xml +++ b/contingency/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/distribution-core/pom.xml b/distribution-core/pom.xml index 60020bc3404..7959b416c39 100644 --- a/distribution-core/pom.xml +++ b/distribution-core/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/dsl/pom.xml b/dsl/pom.xml index dc3da9bf314..d622929bf2e 100644 --- a/dsl/pom.xml +++ b/dsl/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT 4.0.0 diff --git a/dynamic-security-analysis/pom.xml b/dynamic-security-analysis/pom.xml index 950baa30ec1..47112279e49 100644 --- a/dynamic-security-analysis/pom.xml +++ b/dynamic-security-analysis/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-dynamic-security-analysis diff --git a/dynamic-simulation/dynamic-simulation-api/pom.xml b/dynamic-simulation/dynamic-simulation-api/pom.xml index db892ec4b88..82980a10c3e 100644 --- a/dynamic-simulation/dynamic-simulation-api/pom.xml +++ b/dynamic-simulation/dynamic-simulation-api/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-dynamic-simulation - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-dynamic-simulation-api diff --git a/dynamic-simulation/dynamic-simulation-dsl/pom.xml b/dynamic-simulation/dynamic-simulation-dsl/pom.xml index 7dbb4d6e51d..5cbeeb3ad6d 100644 --- a/dynamic-simulation/dynamic-simulation-dsl/pom.xml +++ b/dynamic-simulation/dynamic-simulation-dsl/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-dynamic-simulation - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-dynamic-simulation-dsl diff --git a/dynamic-simulation/dynamic-simulation-tool/pom.xml b/dynamic-simulation/dynamic-simulation-tool/pom.xml index 035fc8a9c51..881d8959c1a 100644 --- a/dynamic-simulation/dynamic-simulation-tool/pom.xml +++ b/dynamic-simulation/dynamic-simulation-tool/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-dynamic-simulation - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-dynamic-simulation-tool diff --git a/dynamic-simulation/pom.xml b/dynamic-simulation/pom.xml index 788edef43fa..366aac51609 100644 --- a/dynamic-simulation/pom.xml +++ b/dynamic-simulation/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/entsoe-util/pom.xml b/entsoe-util/pom.xml index 159a2f75d0d..c5586bdcd66 100644 --- a/entsoe-util/pom.xml +++ b/entsoe-util/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-entsoe-util diff --git a/ieee-cdf/ieee-cdf-converter/pom.xml b/ieee-cdf/ieee-cdf-converter/pom.xml index 347140b45fb..551e39aab44 100644 --- a/ieee-cdf/ieee-cdf-converter/pom.xml +++ b/ieee-cdf/ieee-cdf-converter/pom.xml @@ -13,7 +13,7 @@ powsybl-ieee-cdf com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-ieee-cdf-converter diff --git a/ieee-cdf/ieee-cdf-model/pom.xml b/ieee-cdf/ieee-cdf-model/pom.xml index 0ad873e92c0..5851836d96d 100644 --- a/ieee-cdf/ieee-cdf-model/pom.xml +++ b/ieee-cdf/ieee-cdf-model/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-ieee-cdf - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-ieee-cdf-model diff --git a/ieee-cdf/pom.xml b/ieee-cdf/pom.xml index c32654a342c..77fd5b6aef0 100644 --- a/ieee-cdf/pom.xml +++ b/ieee-cdf/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/iidm/iidm-api/pom.xml b/iidm/iidm-api/pom.xml index 53ea910ae61..1e5ba6fa559 100644 --- a/iidm/iidm-api/pom.xml +++ b/iidm/iidm-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-api diff --git a/iidm/iidm-comparator/pom.xml b/iidm/iidm-comparator/pom.xml index 8abf54fa875..eb56d7d4ae9 100644 --- a/iidm/iidm-comparator/pom.xml +++ b/iidm/iidm-comparator/pom.xml @@ -15,7 +15,7 @@ powsybl-iidm com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-comparator diff --git a/iidm/iidm-criteria/pom.xml b/iidm/iidm-criteria/pom.xml index d96370cb48b..addd0e08536 100644 --- a/iidm/iidm-criteria/pom.xml +++ b/iidm/iidm-criteria/pom.xml @@ -16,7 +16,7 @@ powsybl-iidm com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-criteria diff --git a/iidm/iidm-extensions/pom.xml b/iidm/iidm-extensions/pom.xml index a890fcb10cc..4434163c4b0 100644 --- a/iidm/iidm-extensions/pom.xml +++ b/iidm/iidm-extensions/pom.xml @@ -15,7 +15,7 @@ powsybl-iidm com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-extensions diff --git a/iidm/iidm-geodata/pom.xml b/iidm/iidm-geodata/pom.xml index 6d42d987c15..d483ff3cacc 100644 --- a/iidm/iidm-geodata/pom.xml +++ b/iidm/iidm-geodata/pom.xml @@ -14,7 +14,7 @@ powsybl-iidm com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-geodata diff --git a/iidm/iidm-impl/pom.xml b/iidm/iidm-impl/pom.xml index 405bf884068..f5be61e5143 100644 --- a/iidm/iidm-impl/pom.xml +++ b/iidm/iidm-impl/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-impl diff --git a/iidm/iidm-modification/pom.xml b/iidm/iidm-modification/pom.xml index fcf522829d3..6c68f63b5b9 100644 --- a/iidm/iidm-modification/pom.xml +++ b/iidm/iidm-modification/pom.xml @@ -13,7 +13,7 @@ powsybl-iidm com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-modification diff --git a/iidm/iidm-reducer/pom.xml b/iidm/iidm-reducer/pom.xml index b128b813011..4554bd769cd 100644 --- a/iidm/iidm-reducer/pom.xml +++ b/iidm/iidm-reducer/pom.xml @@ -15,7 +15,7 @@ powsybl-iidm com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-reducer diff --git a/iidm/iidm-scripting/pom.xml b/iidm/iidm-scripting/pom.xml index e5dfb997678..d3f7e31cc87 100644 --- a/iidm/iidm-scripting/pom.xml +++ b/iidm/iidm-scripting/pom.xml @@ -15,7 +15,7 @@ powsybl-iidm com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-scripting diff --git a/iidm/iidm-serde/pom.xml b/iidm/iidm-serde/pom.xml index 4ada28f788b..0206f45cad9 100644 --- a/iidm/iidm-serde/pom.xml +++ b/iidm/iidm-serde/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-serde diff --git a/iidm/iidm-tck/pom.xml b/iidm/iidm-tck/pom.xml index 646507563fb..e81ded1a67b 100644 --- a/iidm/iidm-tck/pom.xml +++ b/iidm/iidm-tck/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-tck diff --git a/iidm/iidm-test/pom.xml b/iidm/iidm-test/pom.xml index b9868708d32..f27cc1337ce 100644 --- a/iidm/iidm-test/pom.xml +++ b/iidm/iidm-test/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-iidm - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-iidm-test diff --git a/iidm/pom.xml b/iidm/pom.xml index e1eb39d0117..ba1c5bddffd 100644 --- a/iidm/pom.xml +++ b/iidm/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/itools-packager/pom.xml b/itools-packager/pom.xml index 3195c4a0417..adaec919701 100644 --- a/itools-packager/pom.xml +++ b/itools-packager/pom.xml @@ -14,7 +14,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-itools-packager-maven-plugin diff --git a/loadflow/loadflow-api/pom.xml b/loadflow/loadflow-api/pom.xml index febe3f71944..147594ac095 100644 --- a/loadflow/loadflow-api/pom.xml +++ b/loadflow/loadflow-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-loadflow - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-loadflow-api diff --git a/loadflow/loadflow-results-completion/pom.xml b/loadflow/loadflow-results-completion/pom.xml index 3cc06abf21e..d8aaa47c3e0 100644 --- a/loadflow/loadflow-results-completion/pom.xml +++ b/loadflow/loadflow-results-completion/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-loadflow - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-loadflow-results-completion diff --git a/loadflow/loadflow-scripting/pom.xml b/loadflow/loadflow-scripting/pom.xml index 141de3395bd..03a4b82bd10 100644 --- a/loadflow/loadflow-scripting/pom.xml +++ b/loadflow/loadflow-scripting/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-loadflow - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-loadflow-scripting diff --git a/loadflow/loadflow-validation/pom.xml b/loadflow/loadflow-validation/pom.xml index f98d374f14b..dd462c36608 100644 --- a/loadflow/loadflow-validation/pom.xml +++ b/loadflow/loadflow-validation/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-loadflow - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-loadflow-validation diff --git a/loadflow/pom.xml b/loadflow/pom.xml index edbc0e7ada8..cb51219c35d 100644 --- a/loadflow/pom.xml +++ b/loadflow/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/math/pom.xml b/math/pom.xml index ee6aea3cd22..c652e0f9a38 100644 --- a/math/pom.xml +++ b/math/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-math diff --git a/matpower/matpower-converter/pom.xml b/matpower/matpower-converter/pom.xml index 4622523274b..53f7e1b24c9 100644 --- a/matpower/matpower-converter/pom.xml +++ b/matpower/matpower-converter/pom.xml @@ -13,7 +13,7 @@ powsybl-matpower com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-matpower-converter diff --git a/matpower/matpower-model/pom.xml b/matpower/matpower-model/pom.xml index dbc76f346d9..985bc1c556d 100644 --- a/matpower/matpower-model/pom.xml +++ b/matpower/matpower-model/pom.xml @@ -13,7 +13,7 @@ com.powsybl powsybl-matpower - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-matpower-model diff --git a/matpower/pom.xml b/matpower/pom.xml index ed44bbc4996..d374fd656c2 100644 --- a/matpower/pom.xml +++ b/matpower/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/pom.xml b/pom.xml index 9fa2720fa22..4b59514b4cb 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom powsybl diff --git a/powerfactory/pom.xml b/powerfactory/pom.xml index be0e89cf8c3..74f8d6588c5 100644 --- a/powerfactory/pom.xml +++ b/powerfactory/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/powerfactory/powerfactory-converter/pom.xml b/powerfactory/powerfactory-converter/pom.xml index d8335399f58..00ae1759be9 100644 --- a/powerfactory/powerfactory-converter/pom.xml +++ b/powerfactory/powerfactory-converter/pom.xml @@ -13,7 +13,7 @@ powsybl-powerfactory com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-powerfactory-converter diff --git a/powerfactory/powerfactory-db/pom.xml b/powerfactory/powerfactory-db/pom.xml index 17964e9cfd4..c76283d1584 100644 --- a/powerfactory/powerfactory-db/pom.xml +++ b/powerfactory/powerfactory-db/pom.xml @@ -13,7 +13,7 @@ powsybl-powerfactory com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-powerfactory-db diff --git a/powerfactory/powerfactory-dgs/pom.xml b/powerfactory/powerfactory-dgs/pom.xml index 6cd7bceb9b0..4a4230343ea 100644 --- a/powerfactory/powerfactory-dgs/pom.xml +++ b/powerfactory/powerfactory-dgs/pom.xml @@ -13,7 +13,7 @@ powsybl-powerfactory com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-powerfactory-dgs diff --git a/powerfactory/powerfactory-model/pom.xml b/powerfactory/powerfactory-model/pom.xml index 2abceb7090e..26f742f597c 100644 --- a/powerfactory/powerfactory-model/pom.xml +++ b/powerfactory/powerfactory-model/pom.xml @@ -13,7 +13,7 @@ powsybl-powerfactory com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-powerfactory-model diff --git a/psse/pom.xml b/psse/pom.xml index e84691f68f5..89b7de0734d 100644 --- a/psse/pom.xml +++ b/psse/pom.xml @@ -13,7 +13,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/psse/psse-converter/pom.xml b/psse/psse-converter/pom.xml index 24a22651978..ff3e9fd69eb 100644 --- a/psse/psse-converter/pom.xml +++ b/psse/psse-converter/pom.xml @@ -11,7 +11,7 @@ powsybl-psse com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT 4.0.0 diff --git a/psse/psse-model-test/pom.xml b/psse/psse-model-test/pom.xml index e23016a8c94..a87f73bc649 100644 --- a/psse/psse-model-test/pom.xml +++ b/psse/psse-model-test/pom.xml @@ -13,7 +13,7 @@ powsybl-psse com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-psse-model-test diff --git a/psse/psse-model/pom.xml b/psse/psse-model/pom.xml index faaeaad6946..5f80e44f65a 100644 --- a/psse/psse-model/pom.xml +++ b/psse/psse-model/pom.xml @@ -13,7 +13,7 @@ powsybl-psse com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-psse-model diff --git a/scripting-test/pom.xml b/scripting-test/pom.xml index 501e7ee8279..17575729959 100644 --- a/scripting-test/pom.xml +++ b/scripting-test/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-scripting-test diff --git a/scripting/pom.xml b/scripting/pom.xml index 1e792794d5c..1ad3564ae24 100644 --- a/scripting/pom.xml +++ b/scripting/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-scripting diff --git a/security-analysis/pom.xml b/security-analysis/pom.xml index 6516e3ece2e..158e8ee2548 100644 --- a/security-analysis/pom.xml +++ b/security-analysis/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/security-analysis/security-analysis-api/pom.xml b/security-analysis/security-analysis-api/pom.xml index 3428bd47e8b..be82baa951a 100644 --- a/security-analysis/security-analysis-api/pom.xml +++ b/security-analysis/security-analysis-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-security-analysis - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-security-analysis-api diff --git a/security-analysis/security-analysis-default/pom.xml b/security-analysis/security-analysis-default/pom.xml index 173858d8868..4ad9b3fa15e 100644 --- a/security-analysis/security-analysis-default/pom.xml +++ b/security-analysis/security-analysis-default/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-security-analysis - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-security-analysis-default diff --git a/sensitivity-analysis-api/pom.xml b/sensitivity-analysis-api/pom.xml index 8b11aabd7c3..01029d6b2f3 100644 --- a/sensitivity-analysis-api/pom.xml +++ b/sensitivity-analysis-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-sensitivity-analysis-api diff --git a/shortcircuit-api/pom.xml b/shortcircuit-api/pom.xml index 8c91f0fe43a..0db7007fe9e 100644 --- a/shortcircuit-api/pom.xml +++ b/shortcircuit-api/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-shortcircuit-api diff --git a/time-series/pom.xml b/time-series/pom.xml index 3847a85a901..e18b1ff7d5c 100644 --- a/time-series/pom.xml +++ b/time-series/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/time-series/time-series-api/pom.xml b/time-series/time-series-api/pom.xml index d40a0aad9c2..30bed7a3cda 100644 --- a/time-series/time-series-api/pom.xml +++ b/time-series/time-series-api/pom.xml @@ -15,7 +15,7 @@ powsybl-time-series com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-time-series-api diff --git a/time-series/time-series-dsl/pom.xml b/time-series/time-series-dsl/pom.xml index a310dfaaf01..8517412ede7 100644 --- a/time-series/time-series-dsl/pom.xml +++ b/time-series/time-series-dsl/pom.xml @@ -15,7 +15,7 @@ powsybl-time-series com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-time-series-dsl diff --git a/tools-test/pom.xml b/tools-test/pom.xml index 649e451a999..cc319fe8d07 100644 --- a/tools-test/pom.xml +++ b/tools-test/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-tools-test diff --git a/tools/pom.xml b/tools/pom.xml index ea808cc9f4d..2f9982137b4 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -15,7 +15,7 @@ powsybl-core com.powsybl - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-tools diff --git a/triple-store/pom.xml b/triple-store/pom.xml index 1751006f2e4..dc9dd5c4b28 100644 --- a/triple-store/pom.xml +++ b/triple-store/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/triple-store/triple-store-api/pom.xml b/triple-store/triple-store-api/pom.xml index ae178a93b6d..9a53b00a1d7 100644 --- a/triple-store/triple-store-api/pom.xml +++ b/triple-store/triple-store-api/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-triple-store - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-triple-store-api diff --git a/triple-store/triple-store-impl-rdf4j/pom.xml b/triple-store/triple-store-impl-rdf4j/pom.xml index 321f9b8c8bf..c2afc86e6c4 100644 --- a/triple-store/triple-store-impl-rdf4j/pom.xml +++ b/triple-store/triple-store-impl-rdf4j/pom.xml @@ -11,7 +11,7 @@ com.powsybl powsybl-triple-store - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-triple-store-impl-rdf4j diff --git a/triple-store/triple-store-test/pom.xml b/triple-store/triple-store-test/pom.xml index e9cbb73a3c0..50177d7fb0e 100644 --- a/triple-store/triple-store-test/pom.xml +++ b/triple-store/triple-store-test/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-triple-store - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-triple-store-test diff --git a/ucte/pom.xml b/ucte/pom.xml index cace33a42b2..1722a063af8 100644 --- a/ucte/pom.xml +++ b/ucte/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-core - 6.4.0-RC1 + 6.5.0-SNAPSHOT pom diff --git a/ucte/ucte-converter/pom.xml b/ucte/ucte-converter/pom.xml index 7b925d9aedb..3a659611c7d 100644 --- a/ucte/ucte-converter/pom.xml +++ b/ucte/ucte-converter/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-ucte - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-ucte-converter diff --git a/ucte/ucte-network/pom.xml b/ucte/ucte-network/pom.xml index 4241bb55984..da18f194c6e 100644 --- a/ucte/ucte-network/pom.xml +++ b/ucte/ucte-network/pom.xml @@ -15,7 +15,7 @@ com.powsybl powsybl-ucte - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-ucte-network diff --git a/ucte/ucte-util/pom.xml b/ucte/ucte-util/pom.xml index 74172939e92..9dd22757a09 100644 --- a/ucte/ucte-util/pom.xml +++ b/ucte/ucte-util/pom.xml @@ -7,7 +7,7 @@ com.powsybl powsybl-ucte - 6.4.0-RC1 + 6.5.0-SNAPSHOT powsybl-ucte-util From d15f6c4b025181814a080e134b39032e1a6512a7 Mon Sep 17 00:00:00 2001 From: Nicolas Rol Date: Thu, 20 Jun 2024 15:03:01 +0200 Subject: [PATCH 11/57] Fix exception for connectable not found in Network Modification connection and disconnection (#3069) Signed-off-by: Nicolas Rol --- .../action/ApplyActionToNetworkTest.java | 27 +++++++ .../modification/AbstractDisconnection.java | 6 +- .../modification/ConnectableConnection.java | 4 + .../modification/PlannedDisconnection.java | 2 +- .../modification/UnplannedDisconnection.java | 2 +- .../ConnectionAndDisconnectionsTest.java | 76 +++++++++++++++++++ 6 files changed, 114 insertions(+), 3 deletions(-) diff --git a/action-api/src/test/java/com/powsybl/action/ApplyActionToNetworkTest.java b/action-api/src/test/java/com/powsybl/action/ApplyActionToNetworkTest.java index e5949e32012..5abc717ebeb 100644 --- a/action-api/src/test/java/com/powsybl/action/ApplyActionToNetworkTest.java +++ b/action-api/src/test/java/com/powsybl/action/ApplyActionToNetworkTest.java @@ -9,7 +9,12 @@ package com.powsybl.action; import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.ComputationManager; +import com.powsybl.computation.local.LocalComputationManager; import com.powsybl.iidm.modification.NetworkModification; +import com.powsybl.iidm.modification.topology.DefaultNamingStrategy; +import com.powsybl.iidm.modification.topology.NamingStrategy; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControl; import com.powsybl.iidm.network.extensions.HvdcAngleDroopActivePowerControlAdder; @@ -58,6 +63,28 @@ void terminalConnectionAction() { assertFalse(branch.getTerminal(TwoSides.ONE).isConnected()); } + /** + * This action fails on tie-lines since they are not a connectable + */ + @Test + void terminalConnectionActionOnTieLineException() { + Network network = EurostagTutorialExample1Factory.createWithTieLine(); + + // Disconnection + TerminalsConnectionAction disconnectionAction = new TerminalsConnectionAction("id", "NHV1_NHV2_1", true); + NetworkModification disconnection = disconnectionAction.toModification(); + NamingStrategy namingStrategy = new DefaultNamingStrategy(); + ComputationManager computationManager = LocalComputationManager.getDefault(); + PowsyblException disconnectionException = assertThrows(PowsyblException.class, () -> disconnection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); + assertEquals("Connectable 'NHV1_NHV2_1' not found", disconnectionException.getMessage()); + + // Connection + TerminalsConnectionAction connectionAction = new TerminalsConnectionAction("id", "NHV1_NHV2_1", false); + NetworkModification connection = connectionAction.toModification(); + PowsyblException connectionException = assertThrows(PowsyblException.class, () -> connection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); + assertEquals("Connectable 'NHV1_NHV2_1' not found", connectionException.getMessage()); + } + @Test void dangingLineAction() { Network network = EurostagTutorialExample1Factory.createWithTieLine(); diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/AbstractDisconnection.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/AbstractDisconnection.java index 82d56b2877d..7a4a9363275 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/AbstractDisconnection.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/AbstractDisconnection.java @@ -35,12 +35,16 @@ public abstract class AbstractDisconnection extends AbstractNetworkModification this.side = side; } - public void applyModification(Network network, boolean isPlanned, ReportNode reportNode) { + public void applyModification(Network network, boolean isPlanned, boolean throwException, ReportNode reportNode) { // Add the reportNode to the network reportNode context network.getReportNodeContext().pushReportNode(reportNode); // Get the connectable Connectable connectable = network.getConnectable(connectableId); + if (connectable == null) { + logOrThrow(throwException, "Connectable '" + connectableId + "' not found"); + return; + } // Disconnect the connectable boolean hasBeenDisconnected; diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnection.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnection.java index 3f7d1ad3bb3..ef5e928f925 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnection.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnection.java @@ -59,6 +59,10 @@ public class ConnectableConnection extends AbstractNetworkModification { public void apply(Network network, NamingStrategy namingStrategy, boolean throwException, ComputationManager computationManager, ReportNode reportNode) { // Get the connectable Connectable connectable = network.getConnectable(connectableId); + if (connectable == null) { + logOrThrow(throwException, "Connectable '" + connectableId + "' not found"); + return; + } // Add the reportNode to the network reportNode context network.getReportNodeContext().pushReportNode(reportNode); diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnection.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnection.java index a94422a6574..646e69bf034 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnection.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnection.java @@ -28,6 +28,6 @@ public class PlannedDisconnection extends AbstractDisconnection { @Override public void apply(Network network, NamingStrategy namingStrategy, boolean throwException, ComputationManager computationManager, ReportNode reportNode) { - applyModification(network, true, reportNode); + applyModification(network, true, throwException, reportNode); } } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnection.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnection.java index a7d9ef64c66..eda8d1b732d 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnection.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnection.java @@ -28,6 +28,6 @@ public class UnplannedDisconnection extends AbstractDisconnection { @Override public void apply(Network network, NamingStrategy namingStrategy, boolean throwException, ComputationManager computationManager, ReportNode reportNode) { - applyModification(network, false, reportNode); + applyModification(network, false, throwException, reportNode); } } diff --git a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/ConnectionAndDisconnectionsTest.java b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/ConnectionAndDisconnectionsTest.java index 0ed92381a21..8c296a62014 100644 --- a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/ConnectionAndDisconnectionsTest.java +++ b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/ConnectionAndDisconnectionsTest.java @@ -7,8 +7,13 @@ */ package com.powsybl.iidm.modification; +import com.powsybl.commons.PowsyblException; import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.ComputationManager; +import com.powsybl.computation.local.LocalComputationManager; import com.powsybl.iidm.modification.topology.AbstractModificationTest; +import com.powsybl.iidm.modification.topology.DefaultNamingStrategy; +import com.powsybl.iidm.modification.topology.NamingStrategy; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.BusbarSectionPositionAdder; import org.junit.jupiter.api.Test; @@ -16,6 +21,8 @@ import java.io.IOException; import java.time.ZonedDateTime; +import static org.junit.jupiter.api.Assertions.*; + /** * @author Nicolas Rol {@literal } */ @@ -404,4 +411,73 @@ void testConnectionNoConnection() throws IOException { writeXmlTest(network, "/network-unplanned-disconnection-not-disconnected.xiidm"); testReportNode(reportNode, "/reportNode/connectable-not-connected.txt"); } + + + /** + * This network modification fails on tie-lines since they are not a connectable + */ + @Test + void testExceptions() { + Network network = createNetwork(); + + // Add tie line + DanglingLine nhv1xnode1 = network.getVoltageLevel("VL2").newDanglingLine() + .setId("NHV1_XNODE1") + .setP0(0.0) + .setQ0(0.0) + .setR(1.5) + .setX(20.0) + .setG(1E-6) + .setB(386E-6 / 2) + .setBus("bus2A") + .setPairingKey("XNODE1") + .add(); + DanglingLine xnode1nhv2 = network.getVoltageLevel("VL3").newDanglingLine() + .setId("XNODE1_NHV2") + .setP0(0.0) + .setQ0(0.0) + .setR(1.5) + .setX(13.0) + .setG(2E-6) + .setB(386E-6 / 2) + .setBus("bus3A") + .setPairingKey("XNODE1") + .add(); + TieLine tieLine = network.newTieLine() + .setId("NHV1_NHV2_1") + .setDanglingLine1(nhv1xnode1.getId()) + .setDanglingLine2(xnode1nhv2.getId()) + .add(); + + // Disconnection + assertTrue(tieLine.getDanglingLine1().getTerminal().isConnected()); + assertTrue(tieLine.getDanglingLine2().getTerminal().isConnected()); + UnplannedDisconnection disconnection = new UnplannedDisconnectionBuilder() + .withConnectableId("NHV1_NHV2_1") + .withFictitiousSwitchesOperable(false) + .build(); + NamingStrategy namingStrategy = new DefaultNamingStrategy(); + ComputationManager computationManager = LocalComputationManager.getDefault(); + PowsyblException disconnectionException = assertThrows(PowsyblException.class, () -> disconnection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); + assertEquals("Connectable 'NHV1_NHV2_1' not found", disconnectionException.getMessage()); + disconnection.apply(network); + assertTrue(tieLine.getDanglingLine1().getTerminal().isConnected()); + assertTrue(tieLine.getDanglingLine2().getTerminal().isConnected()); + + // Connection + tieLine.getDanglingLine1().disconnect(); + tieLine.getDanglingLine2().disconnect(); + assertFalse(tieLine.getDanglingLine1().getTerminal().isConnected()); + assertFalse(tieLine.getDanglingLine2().getTerminal().isConnected()); + ConnectableConnection connection = new ConnectableConnectionBuilder() + .withConnectableId("NHV1_NHV2_1") + .withFictitiousSwitchesOperable(false) + .withOnlyBreakersOperable(true) + .build(); + PowsyblException connectionException = assertThrows(PowsyblException.class, () -> connection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); + assertEquals("Connectable 'NHV1_NHV2_1' not found", connectionException.getMessage()); + connection.apply(network); + assertFalse(tieLine.getDanglingLine1().getTerminal().isConnected()); + assertFalse(tieLine.getDanglingLine2().getTerminal().isConnected()); + } } From d1097ee7cb5a5bed9733c6533a13b6de012909e2 Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Thu, 20 Jun 2024 17:01:19 +0200 Subject: [PATCH 12/57] Introduce OdreConfig to be more flexible with column name changes (#3077) Signed-off-by: Olivier Perrin --- .../iidm/geodata/odre/FileValidator.java | 67 ++++++----------- .../geodata/odre/GeographicDataParser.java | 25 ++++--- .../powsybl/iidm/geodata/odre/OdreConfig.java | 74 +++++++++++++++++++ .../iidm/geodata/odre/OdreGeoDataAdder.java | 8 +- .../odre/OdreGeoDataAdderPostProcessor.java | 18 +++-- .../geodata/odre/OdreGeoDataCsvLoader.java | 13 ++-- .../iidm/geodata/odre/AbstractOdreTest.java | 41 ++++++++++ .../iidm/geodata/odre/FileValidatorTest.java | 34 +++++---- .../OdreGeoDataAdderPostProcessorTest.java | 8 +- .../geodata/odre/OdreGeoDataAdderTest.java | 27 ++++--- .../odre/OdreGeoDataCsvLoaderTest.java | 2 +- .../default-headers/aerial-lines.csv | 3 + .../default-headers/substations-error.csv | 4 + .../default-headers/substations.csv | 3 + .../default-headers/underground-lines.csv | 2 + 15 files changed, 225 insertions(+), 104 deletions(-) create mode 100644 iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreConfig.java create mode 100644 iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/AbstractOdreTest.java create mode 100644 iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/aerial-lines.csv create mode 100644 iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/substations-error.csv create mode 100644 iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/substations.csv create mode 100644 iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/underground-lines.csv diff --git a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/FileValidator.java b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/FileValidator.java index 775b7ee7642..326fafcbcdc 100644 --- a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/FileValidator.java +++ b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/FileValidator.java @@ -43,37 +43,15 @@ private FileValidator() { public static final String SUBSTATIONS = "substations"; public static final String AERIAL_LINES = "aerial-lines"; public static final String UNDERGROUND_LINES = "underground-lines"; - static final String EQUIPMENT_TYPE = "type_ouvrage"; - static final String NULL_EQUIPMENT_TYPE = "NULL"; - static final String AERIAL_EQUIPMENT_TYPE = "AERIEN"; - static final String UNDERGROUND_EQUIPMENT_TYPE = "SOUTERRAIN"; - public static final String LINE_ID_1 = "code_ligne"; - public static final String LINE_ID_2 = "identification_2"; - public static final String LINE_ID_3 = "identification_3"; - public static final String LINE_ID_4 = "identification_4"; - public static final String LINE_ID_5 = "identification_5"; - public static final String LINE_ID_KEY_1 = "id1"; - public static final String LINE_ID_KEY_2 = "id2"; - public static final String LINE_ID_KEY_3 = "id3"; - public static final String LINE_ID_KEY_4 = "id4"; - public static final String LINE_ID_KEY_5 = "id5"; - static final Map IDS_COLUMNS_NAME = Map.of("id1", LINE_ID_1, "id2", LINE_ID_2, "id3", LINE_ID_3, "id4", LINE_ID_4, "id5", LINE_ID_5); - public static final String GEO_SHAPE = "geo_shape"; - static final String SUBSTATION_ID = "code_poste"; - static final String SUBSTATION_LONGITUDE = "longitude_poste"; - static final String SUBSTATION_LATITUDE = "latitude_poste"; - private static final List SUBSTATIONS_EXPECTED_HEADERS = List.of(SUBSTATION_ID, SUBSTATION_LONGITUDE, SUBSTATION_LATITUDE); - private static final List AERIAL_LINES_EXPECTED_HEADERS = List.of(LINE_ID_1, LINE_ID_2, LINE_ID_3, LINE_ID_4, LINE_ID_5, GEO_SHAPE); - private static final List UNDERGROUND_LINES_EXPECTED_HEADERS = List.of(LINE_ID_1, LINE_ID_2, LINE_ID_3, LINE_ID_4, LINE_ID_5, GEO_SHAPE); - public static boolean validateSubstations(Path path) { + public static boolean validateSubstations(Path path, OdreConfig odreConfig) { try (BufferedReader fileReader = new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8)); CsvMapReader mapReader = new CsvMapReader(fileReader, CSV_PREFERENCE)) { final List headers = List.of(mapReader.getHeader(true)); - if (new HashSet<>(headers).containsAll(SUBSTATIONS_EXPECTED_HEADERS)) { + if (new HashSet<>(headers).containsAll(odreConfig.substationsExpectedHeaders())) { return true; } else { - List notFoundHeaders = SUBSTATIONS_EXPECTED_HEADERS.stream().filter(isChangedHeaders(headers)).collect(Collectors.toList()); + List notFoundHeaders = odreConfig.substationsExpectedHeaders().stream().filter(isChangedHeaders(headers)).collect(Collectors.toList()); LOGGER.error(HEADERS_OF_FILE_HAS_CHANGED, path.getFileName(), notFoundHeaders); } } catch (IOException e) { @@ -83,26 +61,23 @@ public static boolean validateSubstations(Path path) { return false; } - public static Map validateLines(List paths) { + public static Map validateLines(List paths, OdreConfig odreConfig) { Map mapResult = new HashMap<>(); paths.forEach(path -> { try (CsvMapReader mapReader = new CsvMapReader(new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8)), CSV_PREFERENCE)) { final String[] headersString = mapReader.getHeader(true); final List headers = List.of(headersString); Map row = mapReader.read(headersString); - String equipmentType = row.get(EQUIPMENT_TYPE); - switch ((equipmentType != null) ? equipmentType : NULL_EQUIPMENT_TYPE) { - case NULL_EQUIPMENT_TYPE: - getIfSubstationsOrLogError(mapResult, path, headers, equipmentType); - break; - case AERIAL_EQUIPMENT_TYPE: - getResultOrLogError(headers, AERIAL_LINES_EXPECTED_HEADERS, mapResult, AERIAL_LINES, path); - break; - case UNDERGROUND_EQUIPMENT_TYPE: - getResultOrLogError(headers, UNDERGROUND_LINES_EXPECTED_HEADERS, mapResult, UNDERGROUND_LINES, path); - break; - default: - LOGGER.error("The file {} has no known equipment type : {}", path.getFileName(), equipmentType); + String equipmentType = row.get(odreConfig.equipmentTypeColumn()); + String type = equipmentType != null ? equipmentType : odreConfig.nullEquipmentType(); + if (type.equals(odreConfig.nullEquipmentType())) { + getIfSubstationsOrLogError(mapResult, path, headers, equipmentType, odreConfig); + } else if (type.equals(odreConfig.aerialEquipmentType())) { + getResultOrLogError(headers, odreConfig.aerialLinesExpectedHeaders(), mapResult, AERIAL_LINES, path); + } else if (type.equals(odreConfig.undergroundEquipmentType())) { + getResultOrLogError(headers, odreConfig.undergroundLinesExpectedHeaders(), mapResult, UNDERGROUND_LINES, path); + } else { + LOGGER.error("The file {} has no known equipment type : {}", path.getFileName(), equipmentType); } } catch (IOException e) { throw new UncheckedIOException(e); @@ -111,13 +86,13 @@ public static Map validateLines(List paths) { return mapResult; } - private static void getIfSubstationsOrLogError(Map mapResult, Path path, List headers, String typeOuvrage) throws IOException { - if (new HashSet<>(headers).containsAll(SUBSTATIONS_EXPECTED_HEADERS)) { + private static void getIfSubstationsOrLogError(Map mapResult, Path path, List headers, String typeOuvrage, OdreConfig odreConfig) throws IOException { + if (new HashSet<>(headers).containsAll(odreConfig.substationsExpectedHeaders())) { mapResult.putIfAbsent(SUBSTATIONS, new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))); - } else if (isAerialOrUnderground(headers)) { + } else if (isAerialOrUnderground(headers, odreConfig)) { LOGGER.error("The file {} has no equipment type : {}", path.getFileName(), typeOuvrage); } else { - List notFoundHeaders = SUBSTATIONS_EXPECTED_HEADERS.stream().filter(isChangedHeaders(headers)).collect(Collectors.toList()); + List notFoundHeaders = odreConfig.substationsExpectedHeaders().stream().filter(isChangedHeaders(headers)).collect(Collectors.toList()); LOGGER.error(HEADERS_OF_FILE_HAS_CHANGED, path.getFileName(), notFoundHeaders); } } @@ -126,9 +101,9 @@ private static Predicate isChangedHeaders(List headers) { return h -> !headers.contains(h); } - private static boolean isAerialOrUnderground(List headers) { - return new HashSet<>(headers).containsAll(AERIAL_LINES_EXPECTED_HEADERS) || - new HashSet<>(headers).containsAll(UNDERGROUND_LINES_EXPECTED_HEADERS); + private static boolean isAerialOrUnderground(List headers, OdreConfig odreConfig) { + return new HashSet<>(headers).containsAll(odreConfig.aerialLinesExpectedHeaders()) || + new HashSet<>(headers).containsAll(odreConfig.undergroundLinesExpectedHeaders()); } private static void getResultOrLogError(List headers, List expectedHeaders, Map mapResult, String fileType, Path path) throws IOException { diff --git a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/GeographicDataParser.java b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/GeographicDataParser.java index 0d9a0f93488..916d4d3a550 100644 --- a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/GeographicDataParser.java +++ b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/GeographicDataParser.java @@ -47,7 +47,7 @@ private GeographicDataParser() { private static final Logger LOGGER = LoggerFactory.getLogger(GeographicDataParser.class); private static final int THRESHOLD = 5; - public static Map parseSubstations(BufferedReader bufferedReader) { + public static Map parseSubstations(BufferedReader bufferedReader, OdreConfig odreConfig) { Map substations = new HashMap<>(); StopWatch stopWatch = new StopWatch(); stopWatch.start(); @@ -57,9 +57,9 @@ public static Map parseSubstations(BufferedReader buf final String[] headers = mapReader.getHeader(true); Map row; while ((row = mapReader.read(headers)) != null) { - String id = row.get(FileValidator.SUBSTATION_ID); - double lon = Double.parseDouble(row.get(FileValidator.SUBSTATION_LONGITUDE)); - double lat = Double.parseDouble(row.get(FileValidator.SUBSTATION_LATITUDE)); + String id = row.get(odreConfig.substationIdColumn()); + double lon = Double.parseDouble(row.get(odreConfig.substationLongitudeColumn())); + double lat = Double.parseDouble(row.get(odreConfig.substationLatitudeColumn())); SubstationGeoData substation = substations.get(id); if (substation == null) { SubstationGeoData substationGeoData = new SubstationGeoData(id, FileValidator.COUNTRY_FR, new Coordinate(lat, lon)); @@ -98,14 +98,14 @@ public static Pair substationOrder(Map parseLines(BufferedReader aerialLinesBr, BufferedReader undergroundLinesBr, - Map stringSubstationGeoDataMap) { + Map stringSubstationGeoDataMap, OdreConfig odreConfig) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); Map> graphByLine = new HashMap<>(); - parseLine(graphByLine, aerialLinesBr); - parseLine(graphByLine, undergroundLinesBr); + parseLine(graphByLine, aerialLinesBr, odreConfig); + parseLine(graphByLine, undergroundLinesBr, odreConfig); Map lines = new HashMap<>(); @@ -159,14 +159,19 @@ public static Map parseLines(BufferedReader aerialLinesBr, return lines; } - private static void parseLine(Map> graphByLine, BufferedReader br) { + private static void parseLine(Map> graphByLine, BufferedReader br, OdreConfig odreConfig) { try (CsvMapReader mapReader = new CsvMapReader(br, FileValidator.CSV_PREFERENCE)) { final String[] headers = mapReader.getHeader(true); Map row; while ((row = mapReader.read(headers)) != null) { - List ids = Stream.of(row.get(FileValidator.IDS_COLUMNS_NAME.get(FileValidator.LINE_ID_KEY_1)), row.get(FileValidator.IDS_COLUMNS_NAME.get(FileValidator.LINE_ID_KEY_2)), row.get(FileValidator.IDS_COLUMNS_NAME.get(FileValidator.LINE_ID_KEY_3)), row.get(FileValidator.IDS_COLUMNS_NAME.get(FileValidator.LINE_ID_KEY_4)), row.get(FileValidator.IDS_COLUMNS_NAME.get(FileValidator.LINE_ID_KEY_5))).filter(Objects::nonNull).collect(Collectors.toList()); - GeoShape geoShape = GeoShapeDeserializer.read(row.get(FileValidator.GEO_SHAPE)); + Map idsColumnNames = odreConfig.idsColumnNames(); + List ids = Stream.of(row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_1)), + row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_2)), + row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_3)), + row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_4)), + row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_5))).filter(Objects::nonNull).collect(Collectors.toList()); + GeoShape geoShape = GeoShapeDeserializer.read(row.get(odreConfig.geoShapeColumn())); if (ids.isEmpty() || geoShape.coordinates().isEmpty()) { continue; } diff --git a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreConfig.java b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreConfig.java new file mode 100644 index 00000000000..247b2518fce --- /dev/null +++ b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreConfig.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.geodata.odre; + +import java.util.List; +import java.util.Map; + +/** + * @author Olivier Perrin {@literal } + */ +public record OdreConfig( + String equipmentTypeColumn, + String nullEquipmentType, + String aerialEquipmentType, + String undergroundEquipmentType, + String lineId1Column, + String lineId2Column, + String lineId3Column, + String lineId4Column, + String lineId5Column, + String geoShapeColumn, + String substationIdColumn, + String substationLongitudeColumn, + String substationLatitudeColumn +) { + + public Map idsColumnNames() { + return Map.of(LINE_ID_KEY_1, lineId1Column, + LINE_ID_KEY_2, lineId2Column, + LINE_ID_KEY_3, lineId3Column, + LINE_ID_KEY_4, lineId4Column, + LINE_ID_KEY_5, lineId5Column); + } + + public List substationsExpectedHeaders() { + return List.of(substationIdColumn, substationLongitudeColumn, substationLatitudeColumn); + } + + public List aerialLinesExpectedHeaders() { + return List.of(lineId1Column, lineId2Column, lineId3Column, lineId4Column, lineId5Column, geoShapeColumn); + } + + public List undergroundLinesExpectedHeaders() { + return List.of(lineId1Column, lineId2Column, lineId3Column, lineId4Column, lineId5Column, geoShapeColumn); + } + + public static final String LINE_ID_KEY_1 = "id1"; + public static final String LINE_ID_KEY_2 = "id2"; + public static final String LINE_ID_KEY_3 = "id3"; + public static final String LINE_ID_KEY_4 = "id4"; + public static final String LINE_ID_KEY_5 = "id5"; + + public static OdreConfig getDefaultOdreConfig() { + return new OdreConfig( + "Type ouvrage", + "NULL", + "AERIEN", + "SOUTERRAIN", + "Code ligne 1", + "Code ligne 2", + "Code ligne 3", + "Code ligne 4", + "Code ligne 5", + "Geo Shape", + "Code poste", + "Longitude poste (DD)", + "Latitude poste (DD)"); + } +} diff --git a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdder.java b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdder.java index 4e17fa674d6..345c2ca9775 100644 --- a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdder.java +++ b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdder.java @@ -22,13 +22,13 @@ public class OdreGeoDataAdder { protected OdreGeoDataAdder() { } - public static void fillNetworkSubstationsGeoDataFromFile(Network network, Path path) { - fillNetworkSubstationsGeoData(network, OdreGeoDataCsvLoader.getSubstationsGeoData(path)); + public static void fillNetworkSubstationsGeoDataFromFile(Network network, Path path, OdreConfig odreConfig) { + fillNetworkSubstationsGeoData(network, OdreGeoDataCsvLoader.getSubstationsGeoData(path, odreConfig)); } public static void fillNetworkLinesGeoDataFromFiles(Network network, Path aerialLinesFilePath, - Path undergroundLinesFilePath, Path substationPath) { + Path undergroundLinesFilePath, Path substationPath, OdreConfig odreConfig) { fillNetworkLinesGeoData(network, - OdreGeoDataCsvLoader.getLinesGeoData(aerialLinesFilePath, undergroundLinesFilePath, substationPath)); + OdreGeoDataCsvLoader.getLinesGeoData(aerialLinesFilePath, undergroundLinesFilePath, substationPath, odreConfig)); } } diff --git a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderPostProcessor.java b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderPostProcessor.java index 5912f52a123..95b62fdd263 100644 --- a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderPostProcessor.java +++ b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderPostProcessor.java @@ -37,21 +37,29 @@ public class OdreGeoDataAdderPostProcessor implements ImportPostProcessor { private final Path aerialLinesFilePath; private final Path undergroundLinesFilePath; + private final OdreConfig odreConfig; + public OdreGeoDataAdderPostProcessor() { - this(PlatformConfig.defaultConfig()); + this(PlatformConfig.defaultConfig(), OdreConfig.getDefaultOdreConfig()); } public OdreGeoDataAdderPostProcessor(PlatformConfig config) { + this(config, OdreConfig.getDefaultOdreConfig()); + } + + public OdreGeoDataAdderPostProcessor(PlatformConfig config, OdreConfig odreConfig) { this(getEquipmentFileFromConfig(config, "substations"), getEquipmentFileFromConfig(config, "aerial-lines"), - getEquipmentFileFromConfig(config, "underground-lines")); + getEquipmentFileFromConfig(config, "underground-lines"), + odreConfig); } public OdreGeoDataAdderPostProcessor(Path substationsFilePath, Path aerialLinesFilePath, - Path undergroundLinesFilePath) { + Path undergroundLinesFilePath, OdreConfig odreConfig) { this.substationsFilePath = substationsFilePath; this.aerialLinesFilePath = aerialLinesFilePath; this.undergroundLinesFilePath = undergroundLinesFilePath; + this.odreConfig = odreConfig; } private static Path getEquipmentFileFromConfig(PlatformConfig platformConfig, String type) { @@ -70,12 +78,12 @@ public String getName() { @Override public void process(Network network, ComputationManager computationManager) { if (Files.exists(substationsFilePath)) { - OdreGeoDataAdder.fillNetworkSubstationsGeoDataFromFile(network, substationsFilePath); + OdreGeoDataAdder.fillNetworkSubstationsGeoDataFromFile(network, substationsFilePath, odreConfig); boolean aerialLinesPresent = Files.exists(aerialLinesFilePath); boolean undergroundLinesPresent = Files.exists(undergroundLinesFilePath); if (aerialLinesPresent && undergroundLinesPresent) { OdreGeoDataAdder.fillNetworkLinesGeoDataFromFiles(network, - aerialLinesFilePath, undergroundLinesFilePath, substationsFilePath); + aerialLinesFilePath, undergroundLinesFilePath, substationsFilePath, odreConfig); } else { String missingAerialFiles = aerialLinesPresent ? "" : aerialLinesFilePath + " "; String missingFiles = missingAerialFiles.concat(undergroundLinesPresent ? "" : undergroundLinesFilePath.toString()); diff --git a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoader.java b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoader.java index c7e87758d02..00558480a43 100644 --- a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoader.java +++ b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoader.java @@ -30,10 +30,10 @@ public class OdreGeoDataCsvLoader { protected OdreGeoDataCsvLoader() { } - public static List getSubstationsGeoData(Path path) { + public static List getSubstationsGeoData(Path path, OdreConfig odreConfig) { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path))))) { - if (FileValidator.validateSubstations(path)) { - return new ArrayList<>(GeographicDataParser.parseSubstations(bufferedReader).values()); + if (FileValidator.validateSubstations(path, odreConfig)) { + return new ArrayList<>(GeographicDataParser.parseSubstations(bufferedReader, odreConfig).values()); } else { return Collections.emptyList(); } @@ -42,13 +42,14 @@ public static List getSubstationsGeoData(Path path) { } } - public static List getLinesGeoData(Path aerialLinesFilePath, Path undergroundLinesFilePath, Path substationPath) { + public static List getLinesGeoData(Path aerialLinesFilePath, Path undergroundLinesFilePath, + Path substationPath, OdreConfig odreConfig) { Map mapValidation = FileValidator.validateLines(List.of(substationPath, - aerialLinesFilePath, undergroundLinesFilePath)); + aerialLinesFilePath, undergroundLinesFilePath), odreConfig); if (mapValidation.size() == 3) { return new ArrayList<>(GeographicDataParser.parseLines(mapValidation.get(FileValidator.AERIAL_LINES), mapValidation.get(FileValidator.UNDERGROUND_LINES), - GeographicDataParser.parseSubstations(mapValidation.get(FileValidator.SUBSTATIONS))).values()); + GeographicDataParser.parseSubstations(mapValidation.get(FileValidator.SUBSTATIONS), odreConfig), odreConfig).values()); } else { return Collections.emptyList(); } diff --git a/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/AbstractOdreTest.java b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/AbstractOdreTest.java new file mode 100644 index 00000000000..e609b57f0eb --- /dev/null +++ b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/AbstractOdreTest.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.geodata.odre; + +import org.junit.jupiter.params.provider.Arguments; + +import java.util.stream.Stream; + +/** + * @author Olivier Perrin {@literal } + */ +abstract class AbstractOdreTest { + + public static final OdreConfig ODRE_CONFIG1 = new OdreConfig( + "type_ouvrage", + "NULL", + "AERIEN", + "SOUTERRAIN", + "code_ligne", + "identification_2", + "identification_3", + "identification_4", + "identification_5", + "geo_shape", + "code_poste", + "longitude_poste", + "latitude_poste" + ); + + static Stream provideTestArguments() { + return Stream.of( + Arguments.of("default", "eurostag-test/default-headers/", OdreConfig.getDefaultOdreConfig()), + Arguments.of("alternate-config", "eurostag-test/", ODRE_CONFIG1) + ); + } +} diff --git a/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/FileValidatorTest.java b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/FileValidatorTest.java index d85ac8c8fa7..819833cbed5 100644 --- a/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/FileValidatorTest.java +++ b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/FileValidatorTest.java @@ -7,7 +7,8 @@ */ package com.powsybl.iidm.geodata.odre; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.net.URISyntaxException; import java.nio.file.Path; @@ -20,35 +21,36 @@ * @author Ahmed Bendaamer {@literal } * @author Hugo Kulesza {@literal } */ -class FileValidatorTest { +class FileValidatorTest extends AbstractOdreTest { - @Test - void whenCallingValidate() throws URISyntaxException { + @ParameterizedTest + @MethodSource("provideTestArguments") + void whenCallingValidate(String descr, String directory, OdreConfig config) throws URISyntaxException { Path file = Paths.get(getClass() - .getClassLoader().getResource("eurostag-test/substations.csv").toURI()); + .getClassLoader().getResource(directory + "substations.csv").toURI()); Path aerialLinesFile = Paths.get(getClass() - .getClassLoader().getResource("eurostag-test/aerial-lines.csv").toURI()); + .getClassLoader().getResource(directory + "aerial-lines.csv").toURI()); Path undergroundLinesFile = Paths.get(getClass() - .getClassLoader().getResource("eurostag-test/underground-lines.csv").toURI()); + .getClassLoader().getResource(directory + "underground-lines.csv").toURI()); Path substationsFile = Paths.get(getClass() - .getClassLoader().getResource("eurostag-test/substations.csv").toURI()); + .getClassLoader().getResource(directory + "substations.csv").toURI()); Path invalidFile = Paths.get(getClass() - .getClassLoader().getResource("eurostag-test/substations-error.csv").toURI()); + .getClassLoader().getResource(directory + "substations-error.csv").toURI()); // test substations file validator with valid file - assertTrue(FileValidator.validateSubstations(file)); + assertTrue(FileValidator.validateSubstations(file, config)); // test substations file validator with invalid file - assertFalse(FileValidator.validateSubstations(invalidFile)); + assertFalse(FileValidator.validateSubstations(invalidFile, config)); // test lines file validator with valid files - assertEquals(3, FileValidator.validateLines(List.of(substationsFile, aerialLinesFile, undergroundLinesFile)).size()); + assertEquals(3, FileValidator.validateLines(List.of(substationsFile, aerialLinesFile, undergroundLinesFile), config).size()); // test lines file validator with 1 invalid file - assertEquals(2, FileValidator.validateLines(List.of(substationsFile, invalidFile, undergroundLinesFile)).size()); + assertEquals(2, FileValidator.validateLines(List.of(substationsFile, invalidFile, undergroundLinesFile), config).size()); // test lines file validator with 2 invalid file - assertEquals(1, FileValidator.validateLines(List.of(invalidFile, invalidFile, undergroundLinesFile)).size()); + assertEquals(1, FileValidator.validateLines(List.of(invalidFile, invalidFile, undergroundLinesFile), config).size()); // test lines file validator with 3 invalid file - assertEquals(0, FileValidator.validateLines(List.of(invalidFile, invalidFile, invalidFile)).size()); + assertEquals(0, FileValidator.validateLines(List.of(invalidFile, invalidFile, invalidFile), config).size()); // test lines file validator with 4 invalid file - assertEquals(0, FileValidator.validateLines(List.of(invalidFile, invalidFile, invalidFile, invalidFile)).size()); + assertEquals(0, FileValidator.validateLines(List.of(invalidFile, invalidFile, invalidFile, invalidFile), config).size()); } } diff --git a/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderPostProcessorTest.java b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderPostProcessorTest.java index 387e318ce40..6e019ad633d 100644 --- a/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderPostProcessorTest.java +++ b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderPostProcessorTest.java @@ -64,7 +64,7 @@ void test() { Path path = platformConfig.getConfigDir().map(p -> p.resolve(fileName)).orElse(null); assertNotNull(path); try { - Files.copy(getClass().getResourceAsStream("/eurostag-test/" + fileName), path); + Files.copy(getClass().getResourceAsStream("/eurostag-test/default-headers/" + fileName), path); } catch (IOException e) { throw new RuntimeException(e); } @@ -111,20 +111,20 @@ void missingFiles() throws IOException { assertNull(network.getSubstation("P2").getExtension(SubstationPosition.class)); Path path = platformConfig.getConfigDir().map(p -> p.resolve("substations.csv")).orElse(null); - Files.copy(getClass().getResourceAsStream("/eurostag-test/substations.csv"), path); + Files.copy(getClass().getResourceAsStream("/eurostag-test/default-headers/substations.csv"), path); processor.process(network, computationManager); assertNotNull(network.getSubstation("P2").getExtension(SubstationPosition.class)); assertNull(network.getLine("NHV1_NHV2_1").getExtension(LinePosition.class)); Path aerialLinePath = platformConfig.getConfigDir().map(p -> p.resolve("aerial-lines.csv")).orElse(null); - Files.copy(getClass().getResourceAsStream("/eurostag-test/aerial-lines.csv"), aerialLinePath); + Files.copy(getClass().getResourceAsStream("/eurostag-test/default-headers/aerial-lines.csv"), aerialLinePath); processor.process(network, computationManager); assertNull(network.getLine("NHV1_NHV2_1").getExtension(LinePosition.class)); Path undergroundLinePath = platformConfig.getConfigDir().map(p -> p.resolve("underground-lines.csv")).orElse(null); - Files.copy(getClass().getResourceAsStream("/eurostag-test/underground-lines.csv"), undergroundLinePath); + Files.copy(getClass().getResourceAsStream("/eurostag-test/default-headers/underground-lines.csv"), undergroundLinePath); processor.process(network, computationManager); assertNotNull(network.getLine("NHV1_NHV2_1").getExtension(LinePosition.class)); diff --git a/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderTest.java b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderTest.java index a8fe37a696a..bee5ed12a33 100644 --- a/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderTest.java +++ b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataAdderTest.java @@ -15,7 +15,8 @@ import com.powsybl.iidm.network.extensions.SubstationPosition; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.net.URISyntaxException; import java.nio.file.Path; @@ -28,7 +29,7 @@ /** * @author Hugo Kulesza {@literal } */ -class OdreGeoDataAdderTest { +class OdreGeoDataAdderTest extends AbstractOdreTest { private Network network; @@ -37,12 +38,13 @@ void setUp() { network = EurostagTutorialExample1Factory.create(); } - @Test - void addSubstationsGeoDataFromFile() throws URISyntaxException { + @ParameterizedTest + @MethodSource("provideTestArguments") + void addSubstationsGeoDataFromFile(String descr, String directory, OdreConfig config) throws URISyntaxException { Path substationsPath = Paths.get(getClass() - .getClassLoader().getResource("eurostag-test/substations.csv").toURI()); + .getClassLoader().getResource(directory + "substations.csv").toURI()); - OdreGeoDataAdder.fillNetworkSubstationsGeoDataFromFile(network, substationsPath); + OdreGeoDataAdder.fillNetworkSubstationsGeoDataFromFile(network, substationsPath, config); Coordinate coord1 = new Coordinate(2, 1); Substation station1 = network.getSubstation("P1"); @@ -57,17 +59,18 @@ void addSubstationsGeoDataFromFile() throws URISyntaxException { assertEquals(coord2, position2.getCoordinate()); } - @Test - void addLinesGeoDataFromFile() throws URISyntaxException { + @ParameterizedTest + @MethodSource("provideTestArguments") + void addLinesGeoDataFromFile(String descr, String directory, OdreConfig config) throws URISyntaxException { Path substationsPath = Paths.get(getClass() - .getClassLoader().getResource("eurostag-test/substations.csv").toURI()); + .getClassLoader().getResource(directory + "substations.csv").toURI()); Path aerialLinesFile = Paths.get(getClass() - .getClassLoader().getResource("eurostag-test/aerial-lines.csv").toURI()); + .getClassLoader().getResource(directory + "aerial-lines.csv").toURI()); Path undergroundLinesFile = Paths.get(getClass() - .getClassLoader().getResource("eurostag-test/underground-lines.csv").toURI()); + .getClassLoader().getResource(directory + "underground-lines.csv").toURI()); OdreGeoDataAdder.fillNetworkLinesGeoDataFromFiles(network, aerialLinesFile, - undergroundLinesFile, substationsPath); + undergroundLinesFile, substationsPath, config); Line line = network.getLine("NHV1_NHV2_2"); LinePosition linePosition = line.getExtension(LinePosition.class); diff --git a/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoaderTest.java b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoaderTest.java index a40932af810..6768069c3f6 100644 --- a/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoaderTest.java +++ b/iidm/iidm-geodata/src/test/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoaderTest.java @@ -31,7 +31,7 @@ void validSubstationsLineParsing() throws URISyntaxException { Path linesPath = Paths.get(getClass() .getClassLoader().getResource("valid-line-name/aerial-lines.csv").toURI()); - List linesGeodata = OdreGeoDataCsvLoader.getLinesGeoData(linesPath, undergroundLinesPath, substationsPath); + List linesGeodata = OdreGeoDataCsvLoader.getLinesGeoData(linesPath, undergroundLinesPath, substationsPath, AbstractOdreTest.ODRE_CONFIG1); assertEquals(2, linesGeodata.size()); LineGeoData line1Position = linesGeodata.get(0); diff --git a/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/aerial-lines.csv b/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/aerial-lines.csv new file mode 100644 index 00000000000..96b9f24d178 --- /dev/null +++ b/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/aerial-lines.csv @@ -0,0 +1,3 @@ +Type ouvrage;Code ligne 1;Nom ligne 1;Propriétaire ligne 1;Etat;TENSION;Source donnée;Geo Shape;Nombre circuit;Code ligne 2;Nom ligne 2;Propriétaire ligne 2;Code ligne 3;Nom ligne 3;Propriétaire ligne 3;Code ligne 4;Nom ligne 4;Propriétaire ligne 4;Code ligne 5;Nom ligne 5;Proprietaire ligne 5;geo_point_2d +AERIEN;NHV1_NHV2_1;NHV1_NHV2_1;RTE;EN EXPLOITATION;400kV;RTE;"{""coordinates"": [[1, 1], [2, 2], [3, 3]], ""type"": ""LineString""}";1;;;;;;;;;;;;;2, 2 +AERIEN;NHV1_NHV2_1;NHV1_NHV2_1;RTE;EN EXPLOITATION;400kV;RTE;"{""coordinates"": [[4, 4], [5, 5]], ""type"": ""LineString""}";1;;;;;;;;;;;;;2, 2 diff --git a/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/substations-error.csv b/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/substations-error.csv new file mode 100644 index 00000000000..d2486c46e0f --- /dev/null +++ b/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/substations-error.csv @@ -0,0 +1,4 @@ +Code poste;Nom poste;FONCTION;Etat;Tension (kV);Longitude poste (DD);Géo-point poste (DD) +SUB.1;SUBSTATION 1;POSTE DE TRANSFORMATION;E;225kV;2.2;49.3,2.2 +SUB.2;SUBSTATION 2;POSTE DE TRANSFORMATION;E;225kV;5.3;43.2,5.3 +SUB.3;SUBSTATION 3;POSTE DE TRANSFORMATION;E;225kV;0.4;46.9,0.4 \ No newline at end of file diff --git a/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/substations.csv b/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/substations.csv new file mode 100644 index 00000000000..dd070151b3a --- /dev/null +++ b/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/substations.csv @@ -0,0 +1,3 @@ +Code poste;Nom poste;FONCTION;Etat;Tension (kV);Longitude poste (DD);Latitude poste (DD);Géo-point poste (DD) +P1;P1;POSTE DE TRANSFORMATION;E;380kV;1;2;2,1 +P2;P2;POSTE DE TRANSFORMATION;E;380kV;3;4;4,3 diff --git a/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/underground-lines.csv b/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/underground-lines.csv new file mode 100644 index 00000000000..d41220d62cb --- /dev/null +++ b/iidm/iidm-geodata/src/test/resources/eurostag-test/default-headers/underground-lines.csv @@ -0,0 +1,2 @@ +Type ouvrage;Code ligne 1;Nom ligne 1;Propriétaire ligne 1;Etat;TENSION;Nombre circuit;Source donnée;Code ligne 2;Nom ligne 2;Propriétaire ligne 2;Code ligne 3;Nom ligne 3;Propriétaire ligne 3;Code ligne 4;Nom ligne 4;Propriétaire ligne 4;Code ligne 5;Nom ligne 5;Propriétaire ligne 5;Geo Shape;Geo Point +SOUTERRAIN;NHV1_NHV2_2;NHV1_NHV2_2;RTE;EN EXPLOITATION;400kV;1;RTE;;;;;;;;;;;;;"{""coordinates"": [[4, 4], [5, 5]], ""type"": ""LineString""}";4.5, 4.5 From d73fa959d15ef1d173d54ee20eeaa775cde046ea Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Thu, 20 Jun 2024 22:05:50 +0200 Subject: [PATCH 13/57] ODRE data post-processing: Replace super-csv by apache commons-csv (#3075) * Replace super-csv by apache commons-csv * Cleaning Signed-off-by: Olivier Perrin --- iidm/iidm-geodata/pom.xml | 10 +- .../iidm/geodata/odre/FileValidator.java | 98 ++++++++++++++----- .../geodata/odre/GeographicDataParser.java | 38 ++++--- .../geodata/odre/OdreGeoDataCsvLoader.java | 28 +++--- .../eurostag-test/substations-error.csv | 13 +-- pom.xml | 1 + 6 files changed, 112 insertions(+), 76 deletions(-) diff --git a/iidm/iidm-geodata/pom.xml b/iidm/iidm-geodata/pom.xml index d483ff3cacc..31bbd5c1ee6 100644 --- a/iidm/iidm-geodata/pom.xml +++ b/iidm/iidm-geodata/pom.xml @@ -37,10 +37,6 @@ - - 2.4.0 - - @@ -61,9 +57,9 @@ commons-io - net.sf.supercsv - super-csv - ${supercsv.version} + org.apache.commons + commons-csv + ${commons-csv.version} diff --git a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/FileValidator.java b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/FileValidator.java index 326fafcbcdc..cc4e34e3a8d 100644 --- a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/FileValidator.java +++ b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/FileValidator.java @@ -8,22 +8,18 @@ package com.powsybl.iidm.geodata.odre; import com.powsybl.iidm.geodata.utils.InputUtils; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.supercsv.io.CsvMapReader; -import org.supercsv.prefs.CsvPreference; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; +import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -39,20 +35,41 @@ private FileValidator() { private static final String HEADERS_OF_FILE_HAS_CHANGED = "Invalid file, Headers of file {} has changed, header(s) not found: {}"; private static final Logger LOGGER = LoggerFactory.getLogger(FileValidator.class); static final String COUNTRY_FR = "FR"; - static final CsvPreference CSV_PREFERENCE = new CsvPreference.Builder('"', ';', System.lineSeparator()).build(); + + private static CSVFormat.Builder createCsvFormatBuilder() { + return CSVFormat.DEFAULT.builder() + .setQuote('"') + .setDelimiter(";") + .setRecordSeparator(System.lineSeparator()); + } + + static final CSVFormat CSV_FORMAT = createCsvFormatBuilder() + .setHeader() + .setSkipHeaderRecord(true) + .build(); + static final CSVFormat CSV_FORMAT_FOR_HEADER = createCsvFormatBuilder().build(); public static final String SUBSTATIONS = "substations"; public static final String AERIAL_LINES = "aerial-lines"; public static final String UNDERGROUND_LINES = "underground-lines"; public static boolean validateSubstations(Path path, OdreConfig odreConfig) { - try (BufferedReader fileReader = new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8)); - CsvMapReader mapReader = new CsvMapReader(fileReader, CSV_PREFERENCE)) { - final List headers = List.of(mapReader.getHeader(true)); + try (Reader reader = new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))) { + Iterator records = CSVParser.parse(reader, FileValidator.CSV_FORMAT_FOR_HEADER).iterator(); + + List headers; + if (records.hasNext()) { + CSVRecord headersRecord = records.next(); + headers = headersRecord.toList(); + } else { + LOGGER.error("The file {} is empty", path.getFileName()); + return false; + } + if (new HashSet<>(headers).containsAll(odreConfig.substationsExpectedHeaders())) { return true; } else { List notFoundHeaders = odreConfig.substationsExpectedHeaders().stream().filter(isChangedHeaders(headers)).collect(Collectors.toList()); - LOGGER.error(HEADERS_OF_FILE_HAS_CHANGED, path.getFileName(), notFoundHeaders); + logHeaderError(path, notFoundHeaders); } } catch (IOException e) { LOGGER.error(e.getMessage()); @@ -61,14 +78,29 @@ public static boolean validateSubstations(Path path, OdreConfig odreConfig) { return false; } - public static Map validateLines(List paths, OdreConfig odreConfig) { - Map mapResult = new HashMap<>(); + public static Map validateLines(List paths, OdreConfig odreConfig) { + Map mapResult = new HashMap<>(); paths.forEach(path -> { - try (CsvMapReader mapReader = new CsvMapReader(new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8)), CSV_PREFERENCE)) { - final String[] headersString = mapReader.getHeader(true); - final List headers = List.of(headersString); - Map row = mapReader.read(headersString); - String equipmentType = row.get(odreConfig.equipmentTypeColumn()); + try (Reader reader = new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))) { + Iterator records = CSVParser.parse(reader, FileValidator.CSV_FORMAT_FOR_HEADER).iterator(); + + final List headers; + if (records.hasNext()) { + CSVRecord headersRecord = records.next(); + headers = headersRecord.toList(); + } else { + headers = null; + LOGGER.error("The file {} is empty", path.getFileName()); + } + + String equipmentType = null; + if (headers != null && records.hasNext()) { + CSVRecord firstRow = records.next(); + equipmentType = readEquipmentType(firstRow, headers, odreConfig); + } else { + LOGGER.error("The file {} has no data", path.getFileName()); + } + String type = equipmentType != null ? equipmentType : odreConfig.nullEquipmentType(); if (type.equals(odreConfig.nullEquipmentType())) { getIfSubstationsOrLogError(mapResult, path, headers, equipmentType, odreConfig); @@ -80,20 +112,30 @@ public static Map validateLines(List paths, OdreCo LOGGER.error("The file {} has no known equipment type : {}", path.getFileName(), equipmentType); } } catch (IOException e) { + mapResult.values().forEach(IOUtils::closeQuietly); throw new UncheckedIOException(e); } }); return mapResult; } - private static void getIfSubstationsOrLogError(Map mapResult, Path path, List headers, String typeOuvrage, OdreConfig odreConfig) throws IOException { + private static String readEquipmentType(CSVRecord firstRow, List headers, OdreConfig odreConfig) { + String equipmentType = null; + int index = headers.indexOf(odreConfig.equipmentTypeColumn()); + if (index != -1) { + equipmentType = firstRow.get(index); + } + return equipmentType; + } + + private static void getIfSubstationsOrLogError(Map mapResult, Path path, List headers, String typeOuvrage, OdreConfig odreConfig) throws IOException { if (new HashSet<>(headers).containsAll(odreConfig.substationsExpectedHeaders())) { mapResult.putIfAbsent(SUBSTATIONS, new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))); } else if (isAerialOrUnderground(headers, odreConfig)) { LOGGER.error("The file {} has no equipment type : {}", path.getFileName(), typeOuvrage); } else { List notFoundHeaders = odreConfig.substationsExpectedHeaders().stream().filter(isChangedHeaders(headers)).collect(Collectors.toList()); - LOGGER.error(HEADERS_OF_FILE_HAS_CHANGED, path.getFileName(), notFoundHeaders); + logHeaderError(path, notFoundHeaders); } } @@ -106,13 +148,17 @@ private static boolean isAerialOrUnderground(List headers, OdreConfig od new HashSet<>(headers).containsAll(odreConfig.undergroundLinesExpectedHeaders()); } - private static void getResultOrLogError(List headers, List expectedHeaders, Map mapResult, String fileType, Path path) throws IOException { + private static void getResultOrLogError(List headers, List expectedHeaders, Map mapResult, String fileType, Path path) throws IOException { if (new HashSet<>(headers).containsAll(expectedHeaders)) { mapResult.putIfAbsent(fileType, new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))); } else { List notFoundHeaders = expectedHeaders.stream().filter(isChangedHeaders(headers)).collect(Collectors.toList()); - LOGGER.error(HEADERS_OF_FILE_HAS_CHANGED, path.getFileName(), notFoundHeaders); + logHeaderError(path, notFoundHeaders); } } + + private static void logHeaderError(Path path, List notFoundHeaders) { + LOGGER.error(HEADERS_OF_FILE_HAS_CHANGED, path.getFileName(), notFoundHeaders); + } } diff --git a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/GeographicDataParser.java b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/GeographicDataParser.java index 916d4d3a550..4046d177457 100644 --- a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/GeographicDataParser.java +++ b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/GeographicDataParser.java @@ -15,6 +15,8 @@ import com.powsybl.iidm.geodata.utils.GeoShapeDeserializer; import com.powsybl.iidm.geodata.utils.LineGraph; import com.powsybl.iidm.network.extensions.Coordinate; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; import org.apache.commons.lang3.time.StopWatch; import org.apache.commons.lang3.tuple.Pair; import org.jgrapht.Graph; @@ -23,13 +25,11 @@ import org.jgrapht.traverse.BreadthFirstIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.supercsv.io.CsvMapReader; -import java.io.BufferedReader; import java.io.IOException; +import java.io.Reader; import java.io.UncheckedIOException; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.Collections.min; @@ -47,16 +47,14 @@ private GeographicDataParser() { private static final Logger LOGGER = LoggerFactory.getLogger(GeographicDataParser.class); private static final int THRESHOLD = 5; - public static Map parseSubstations(BufferedReader bufferedReader, OdreConfig odreConfig) { + public static Map parseSubstations(Reader reader, OdreConfig odreConfig) { Map substations = new HashMap<>(); StopWatch stopWatch = new StopWatch(); stopWatch.start(); int substationCount = 0; - - try (CsvMapReader mapReader = new CsvMapReader(bufferedReader, FileValidator.CSV_PREFERENCE)) { - final String[] headers = mapReader.getHeader(true); - Map row; - while ((row = mapReader.read(headers)) != null) { + try { + Iterable records = CSVParser.parse(reader, FileValidator.CSV_FORMAT); + for (CSVRecord row : records) { String id = row.get(odreConfig.substationIdColumn()); double lon = Double.parseDouble(row.get(odreConfig.substationLongitudeColumn())); double lat = Double.parseDouble(row.get(odreConfig.substationLatitudeColumn())); @@ -70,6 +68,7 @@ public static Map parseSubstations(BufferedReader buf } catch (IOException e) { throw new UncheckedIOException(e); } + LOGGER.info("{} substations read in {} ms", substationCount, stopWatch.getTime()); return substations; } @@ -97,15 +96,15 @@ public static Pair substationOrder(Map parseLines(BufferedReader aerialLinesBr, BufferedReader undergroundLinesBr, + public static Map parseLines(Reader aerialLinesReader, Reader undergroundLinesReader, Map stringSubstationGeoDataMap, OdreConfig odreConfig) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); Map> graphByLine = new HashMap<>(); - parseLine(graphByLine, aerialLinesBr, odreConfig); - parseLine(graphByLine, undergroundLinesBr, odreConfig); + parseLine(graphByLine, aerialLinesReader, odreConfig); + parseLine(graphByLine, undergroundLinesReader, odreConfig); Map lines = new HashMap<>(); @@ -159,19 +158,18 @@ public static Map parseLines(BufferedReader aerialLinesBr, return lines; } - private static void parseLine(Map> graphByLine, BufferedReader br, OdreConfig odreConfig) { - - try (CsvMapReader mapReader = new CsvMapReader(br, FileValidator.CSV_PREFERENCE)) { - final String[] headers = mapReader.getHeader(true); - Map row; - while ((row = mapReader.read(headers)) != null) { - Map idsColumnNames = odreConfig.idsColumnNames(); + private static void parseLine(Map> graphByLine, Reader reader, OdreConfig odreConfig) { + try { + Iterable records = CSVParser.parse(reader, FileValidator.CSV_FORMAT); + Map idsColumnNames = odreConfig.idsColumnNames(); + for (CSVRecord row : records) { List ids = Stream.of(row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_1)), row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_2)), row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_3)), row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_4)), - row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_5))).filter(Objects::nonNull).collect(Collectors.toList()); + row.get(idsColumnNames.get(OdreConfig.LINE_ID_KEY_5))).filter(Objects::nonNull).toList(); GeoShape geoShape = GeoShapeDeserializer.read(row.get(odreConfig.geoShapeColumn())); + if (ids.isEmpty() || geoShape.coordinates().isEmpty()) { continue; } diff --git a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoader.java b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoader.java index 00558480a43..3fb654da37f 100644 --- a/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoader.java +++ b/iidm/iidm-geodata/src/main/java/com/powsybl/iidm/geodata/odre/OdreGeoDataCsvLoader.java @@ -10,11 +10,9 @@ import com.powsybl.iidm.geodata.elements.LineGeoData; import com.powsybl.iidm.geodata.elements.SubstationGeoData; import com.powsybl.iidm.geodata.utils.InputUtils; +import org.apache.commons.io.IOUtils; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; +import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -31,9 +29,9 @@ protected OdreGeoDataCsvLoader() { } public static List getSubstationsGeoData(Path path, OdreConfig odreConfig) { - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path))))) { + try (Reader reader = new BufferedReader(new InputStreamReader(InputUtils.toBomInputStream(Files.newInputStream(path))))) { if (FileValidator.validateSubstations(path, odreConfig)) { - return new ArrayList<>(GeographicDataParser.parseSubstations(bufferedReader, odreConfig).values()); + return new ArrayList<>(GeographicDataParser.parseSubstations(reader, odreConfig).values()); } else { return Collections.emptyList(); } @@ -44,14 +42,18 @@ public static List getSubstationsGeoData(Path path, OdreConfi public static List getLinesGeoData(Path aerialLinesFilePath, Path undergroundLinesFilePath, Path substationPath, OdreConfig odreConfig) { - Map mapValidation = FileValidator.validateLines(List.of(substationPath, + Map mapValidation = FileValidator.validateLines(List.of(substationPath, aerialLinesFilePath, undergroundLinesFilePath), odreConfig); - if (mapValidation.size() == 3) { - return new ArrayList<>(GeographicDataParser.parseLines(mapValidation.get(FileValidator.AERIAL_LINES), - mapValidation.get(FileValidator.UNDERGROUND_LINES), - GeographicDataParser.parseSubstations(mapValidation.get(FileValidator.SUBSTATIONS), odreConfig), odreConfig).values()); - } else { - return Collections.emptyList(); + List result = Collections.emptyList(); + try { + if (mapValidation.size() == 3) { + result = new ArrayList<>(GeographicDataParser.parseLines(mapValidation.get(FileValidator.AERIAL_LINES), + mapValidation.get(FileValidator.UNDERGROUND_LINES), + GeographicDataParser.parseSubstations(mapValidation.get(FileValidator.SUBSTATIONS), odreConfig), odreConfig).values()); + } + } finally { + mapValidation.values().forEach(IOUtils::closeQuietly); } + return result; } } diff --git a/iidm/iidm-geodata/src/test/resources/eurostag-test/substations-error.csv b/iidm/iidm-geodata/src/test/resources/eurostag-test/substations-error.csv index 7ed77a9c814..a31e7e7c0d3 100644 --- a/iidm/iidm-geodata/src/test/resources/eurostag-test/substations-error.csv +++ b/iidm/iidm-geodata/src/test/resources/eurostag-test/substations-error.csv @@ -1,11 +1,4 @@ code_poste;nom_poste;fonction;etat;tension;longitude_poste;geo_point_poste -V.SEP;VILLERS-SAINT-SEPULCRE;POSTE DE TRANSFORMATION;E;225kV;2.2014874715868253;49.35864164277698,2.2014874715868253 -V.POR;VIEUX-PORT;POSTE DE TRANSFORMATION;E;225kV;5.369362995699234;43.28964383589903,5.369362995699234 -MONDI;MONDION;POSTE DE TRANSFORMATION;E;225kV;0.45310103783373573;46.92751912107706,0.45310103783373573 -B.THO;BARRE-THOMAS (LA);POSTE DE TRANSFORMATION;E;90kV;-1.72574736112968;48.11054982058195,-1.72574736112968 -ZV.BE;PIQUAGE A VILLIERS LE BEL;POINT DE PIQUAGE;E;225kV;2.408444606752255;49.010164497815914,2.408444606752255 -1AVAL;AVALATS(LES);POSTE DE TRANSFORMATION;E;<45kV;2.239481110152893;43.94243916390505,2.239481110152893 -1LART;LARTIGE /1ZVLE 30KV;POSTE DE TRANSFORMATION;E;<45kV;1.5316152230911364;45.80749288269255,1.5316152230911364 -1ONER;ONERA /AVRIEUX 10KV;POSTE DE TRANSFORMATION;E;<45kV;6.712988386882246;45.21385121248377,6.712988386882246 -1SSFO;SAINT-FONS /BELLE-ETOILE;POSTE DE TRANSFORMATION;E;<45kV;4.8510704590363405;45.697346813171535,4.8510704590363405 -A.ADO;AIRE-SUR-ADOUR;POSTE DE TRANSFORMATION;E;63kV;-0.2621029649557296;43.70687313781712,-0.2621029649557296 +SUB.1;SUBSTATION 1;POSTE DE TRANSFORMATION;E;225kV;2.2;49.3,2.2 +SUB.2;SUBSTATION 2;POSTE DE TRANSFORMATION;E;225kV;5.3;43.2,5.3 +SUB.3;SUBSTATION 3;POSTE DE TRANSFORMATION;E;225kV;0.4;46.9,0.4 diff --git a/pom.xml b/pom.xml index 4b59514b4cb..cba5fc7fa2b 100644 --- a/pom.xml +++ b/pom.xml @@ -102,6 +102,7 @@ 1.8.0 1.26.2 2.10.1 + 1.11.0 2.16.1 3.14.0 3.6.1 From 309d05e82f0750a577cf1f7d6fe133fcc5645d2c Mon Sep 17 00:00:00 2001 From: jeandemanged Date: Fri, 21 Jun 2024 08:07:54 +0200 Subject: [PATCH 14/57] Fix ReferenceTerminals and SlackTerminal iIDM extension with merged networks and subnetworks (#3076) * Fix ReferenceTerminals and SlackTerminal iIDM extension with merged networks and subnetworks Signed-off-by: Damien Jeandemange --- .../extensions/ReferenceTerminals.java | 12 ++- .../network/extensions/SlackTerminal.java | 7 +- .../extensions/ReferenceTerminalsImpl.java | 31 ++++++-- .../AbstractReferenceTerminalsTest.java | 77 ++++++++++++++++++- .../extensions/AbstractSlackTerminalTest.java | 32 +++++++- 5 files changed, 141 insertions(+), 18 deletions(-) diff --git a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ReferenceTerminals.java b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ReferenceTerminals.java index fdc4cb65174..0ec10d7cc32 100644 --- a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ReferenceTerminals.java +++ b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ReferenceTerminals.java @@ -39,7 +39,7 @@ default String getName() { ReferenceTerminals addReferenceTerminal(Terminal terminal); /** - * Deletes all defined reference terminals in the network for the current variant + * Deletes all defined reference terminals in the network and all its subnetworks for the current variant * @param network network whose reference terminals should be deleted */ static void reset(Network network) { @@ -51,10 +51,13 @@ static void reset(Network network) { .add(); } ext.reset(); + // reset also all subnetworks + network.getSubnetworks().forEach(ReferenceTerminals::reset); } /** - * Defines/add a terminal as reference in the network for the current variant + * Defines/add a terminal as reference in the network for the current variant. + * In case of a merged network with subnetwork, the extension is placed on the root/merged network. * @param terminal terminal to be added as reference terminal */ static void addTerminal(Terminal terminal) { @@ -70,7 +73,10 @@ static void addTerminal(Terminal terminal) { } /** - * Gets the reference terminals in the network for the current variant + * Gets the reference terminals in the network for the current variant. + *

Note: This method returns only the terminal from the extension attached to the provided network. + * In case of a merged network with subnetworks, be careful whether you want the extension + * of the merged network or of a subnetwork.

* @param network network to get reference terminals from */ static Set getTerminals(Network network) { diff --git a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/SlackTerminal.java b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/SlackTerminal.java index 1ece3fc5859..21688ab458b 100644 --- a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/SlackTerminal.java +++ b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/SlackTerminal.java @@ -25,13 +25,16 @@ public interface SlackTerminal extends Extension { String NAME = "slackTerminal"; /** - * Set the terminal of all SlackTerminal extensions from the given network to null. If the extension is empty, - * meaning that for each variant the terminal is null, this method automatically remove the extension. + * Set the terminal of all SlackTerminal extensions from the given network and all its subnetworks to null. + * If the extension is empty, meaning that for each variant the terminal is null, this method automatically + * remove the extension. * * @param network A network to cleanup */ static void reset(Network network) { network.getVoltageLevels().forEach(vl -> reset(vl, null)); + // reset also all subnetworks + network.getSubnetworks().forEach(sn -> sn.getVoltageLevels().forEach(vl -> reset(vl, null))); } /** diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ReferenceTerminalsImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ReferenceTerminalsImpl.java index eb58d49ad17..cf3ebb43a0a 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ReferenceTerminalsImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ReferenceTerminalsImpl.java @@ -22,7 +22,7 @@ class ReferenceTerminalsImpl extends AbstractMultiVariantIdentifiableExtension identifiable) { if (identifiable instanceof Connectable connectable) { // if connectable removed from network, remove its terminals from this extension terminalsPerVariant.forEach(referenceTerminals -> connectable.getTerminals().forEach(referenceTerminals::remove)); @@ -39,12 +39,21 @@ public ReferenceTerminalsImpl(Network network, Set terminals) { Collections.nCopies(getVariantManagerHolder().getVariantManager().getVariantArraySize(), new LinkedHashSet<>())); setReferenceTerminals(terminals); this.referenceTerminalsListener = new ReferenceTerminalsListener(); - network.addListener(this.referenceTerminalsListener); + } + + @Override + public void setExtendable(Network extendable) { + super.setExtendable(extendable); + if (extendable != null) { + // Add the listener, this will be done both extension creation, but also on extension transfer when merging and detaching. + extendable.getNetwork().addListener(this.referenceTerminalsListener); + } } @Override protected void cleanup() { - getExtendable().removeListener(this.referenceTerminalsListener); + // when extension removed from extendable, remove the listener. This will happen when merging and detaching. + getExtendable().getNetwork().removeListener(this.referenceTerminalsListener); } @Override @@ -103,9 +112,19 @@ public void allocateVariantArrayElement(int[] indexes, int sourceIndex) { } private static void checkTerminalInNetwork(Terminal terminal, Network network) { - if (!terminal.getVoltageLevel().getNetwork().equals(network)) { - throw new PowsyblException("Terminal given is not in the right Network (" - + terminal.getVoltageLevel().getNetwork().getId() + " instead of " + network.getId() + ")"); + final boolean extendableIsRootNetwork = network.getNetwork().equals(network); + if (extendableIsRootNetwork) { + // it is all fine as long as the terminal belongs to the merged network + if (!terminal.getVoltageLevel().getNetwork().equals(network)) { + throw new PowsyblException("Terminal given is not in the right Network (" + + terminal.getVoltageLevel().getNetwork().getId() + " instead of " + network.getId() + ")"); + } + } else { + // subnetwork: the terminal must be in the subnetwork + if (!terminal.getVoltageLevel().getParentNetwork().equals(network)) { + throw new PowsyblException("Terminal given is not in the right Network (" + + terminal.getVoltageLevel().getParentNetwork().getId() + " instead of " + network.getId() + ")"); + } } } } diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractReferenceTerminalsTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractReferenceTerminalsTest.java index ab1cba33140..9e414ef43e4 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractReferenceTerminalsTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractReferenceTerminalsTest.java @@ -184,9 +184,9 @@ public void testVariantsCloning() { public void testWrongNetwork() { Network other = EurostagTutorialExample1Factory.create(); Terminal terminal = other.getBusBreakerView().getBus("NHV1").getConnectedTerminals().iterator().next(); - PowsyblException ex1 = assertThrows(PowsyblException.class, () -> network.newExtension(ReferenceTerminalsAdder.class) - .withTerminals(Set.of(terminal)) - .add()); + ReferenceTerminalsAdder referenceTerminalsAdder = network.newExtension(ReferenceTerminalsAdder.class) + .withTerminals(Set.of(terminal)); + PowsyblException ex1 = assertThrows(PowsyblException.class, referenceTerminalsAdder::add); assertEquals("Terminal given is not in the right Network (sim1 instead of fourSubstations)", ex1.getMessage()); network.newExtension(ReferenceTerminalsAdder.class) .withTerminals(Set.of()) @@ -196,6 +196,77 @@ public void testWrongNetwork() { assertEquals("Terminal given is not in the right Network (sim1 instead of fourSubstations)", ex2.getMessage()); } + @Test + public void testWithSubnetwork() { + Network merged = Network.merge(network, EurostagTutorialExample1Factory.create()); + Network subnetwork = merged.getSubnetwork("fourSubstations"); + Network sim1subnetwork = merged.getSubnetwork("sim1"); + gh1 = merged.getGenerator("GH1"); + gh2 = merged.getGenerator("GH2"); + Terminal gh1Terminal = gh1.getTerminal(); + Terminal gh2Terminal = gh2.getTerminal(); + + // gh1 is in subnetwork and can be added to subnetwork extension + subnetwork.newExtension(ReferenceTerminalsAdder.class) + .withTerminals(Set.of(gh1Terminal)) + .add(); + ReferenceTerminals extSubnetwork = subnetwork.getExtension(ReferenceTerminals.class); + assertEquals(1, extSubnetwork.getReferenceTerminals().size()); + assertTrue(extSubnetwork.getReferenceTerminals().contains(gh1Terminal)); + + // gh2 is in subnetwork and can be added to root network extension + merged.newExtension(ReferenceTerminalsAdder.class) + .withTerminals(Set.of(gh2Terminal)) + .add(); + ReferenceTerminals extMergedNetwork = merged.getExtension(ReferenceTerminals.class); + assertEquals(1, extMergedNetwork.getReferenceTerminals().size()); + assertTrue(extMergedNetwork.getReferenceTerminals().contains(gh2Terminal)); + + // we can reset everything via this method + ReferenceTerminals.reset(merged); + assertTrue(ReferenceTerminals.getTerminals(merged).isEmpty()); + + // we can add easily to merged/root network via this method + ReferenceTerminals.addTerminal(gh1Terminal); + extMergedNetwork = merged.getExtension(ReferenceTerminals.class); + extSubnetwork = subnetwork.getExtension(ReferenceTerminals.class); + assertEquals(1, extMergedNetwork.getReferenceTerminals().size()); + assertEquals(0, extSubnetwork.getReferenceTerminals().size()); // not added to subnetwork + // same as above, but using the more user-friendly static methods from ReferenceTerminals. + assertEquals(1, ReferenceTerminals.getTerminals(merged).size()); + assertEquals(0, ReferenceTerminals.getTerminals(subnetwork).size()); + + // we can't add gh1 to sim1 + sim1subnetwork.newExtension(ReferenceTerminalsAdder.class) + .withTerminals(Set.of()) + .add(); + ReferenceTerminals extSim1 = sim1subnetwork.getExtension(ReferenceTerminals.class); + PowsyblException ex = assertThrows(PowsyblException.class, () -> extSim1.addReferenceTerminal(gh1Terminal)); + assertEquals("Terminal given is not in the right Network (fourSubstations instead of sim1)", ex.getMessage()); + } + + @Test + public void testListenersTransferOnMergeAndDetach() { + Network network1 = FourSubstationsNodeBreakerFactory.create(); + Network network2 = EurostagTutorialExample1Factory.create(); + ReferenceTerminals.addTerminal(network1.getGenerator("GH1").getTerminal()); + ReferenceTerminals.addTerminal(network1.getGenerator("GH2").getTerminal()); + + Network merged = Network.merge(network1, network2); + network1 = merged.getSubnetwork("fourSubstations"); + + // check listener is now effective on merged network + assertEquals(2, ReferenceTerminals.getTerminals(network1).size()); + merged.getGenerator("GH1").remove(); + assertEquals(1, ReferenceTerminals.getTerminals(network1).size()); + + Network network1detached = network1.detach(); + // check listener is now effective on detached network + assertEquals(1, ReferenceTerminals.getTerminals(network1detached).size()); + network1detached.getGenerator("GH2").remove(); + assertEquals(0, ReferenceTerminals.getTerminals(network1detached).size()); + } + @Test public void testRemoveEquipment() { network.newExtension(ReferenceTerminalsAdder.class) diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractSlackTerminalTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractSlackTerminalTest.java index 63f2ae41ddc..8d665ba4e3b 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractSlackTerminalTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractSlackTerminalTest.java @@ -51,10 +51,6 @@ static Network createBusBreakerNetwork() { .setQ0(50) .add(); - Substation s1 = network.newSubstation() - .setId("S1") - .setCountry(Country.FR) - .add(); VoltageLevel vl1 = s.newVoltageLevel() .setId("VL1") .setNominalV(400) @@ -260,4 +256,32 @@ public void variantsResetTest() { assertNull(vlhv1.getExtension(SlackTerminal.class)); } + + @Test + public void testWithSubnetwork() { + Network network1 = createBusBreakerNetwork(); + SlackTerminal.attach(network1.getBusBreakerView().getBus("B")); + Network network2 = EurostagTutorialExample1Factory.create(); + SlackTerminal.attach(network2.getBusBreakerView().getBus("NHV1")); + + Network merged = Network.merge(network1, network2); + network1 = merged.getSubnetwork("test"); + network2 = merged.getSubnetwork("sim1"); + + // still there after merge + assertNotNull(merged.getVoltageLevel("VL").getExtension(SlackTerminal.class)); + assertNotNull(merged.getVoltageLevel("VLHV1").getExtension(SlackTerminal.class)); + + // we can reset everything (including subnetworks) + SlackTerminal.reset(merged); + assertNull(merged.getVoltageLevel("VL").getExtension(SlackTerminal.class)); + assertNull(merged.getVoltageLevel("VLHV1").getExtension(SlackTerminal.class)); + + // we can reset only a subnetwork + SlackTerminal.attach(network1.getBusBreakerView().getBus("B")); + SlackTerminal.attach(network2.getBusBreakerView().getBus("NHV1")); + SlackTerminal.reset(network1); + assertNull(merged.getVoltageLevel("VL").getExtension(SlackTerminal.class)); // reset + assertNotNull(merged.getVoltageLevel("VLHV1").getExtension(SlackTerminal.class)); // untouched + } } From 573a27cdf73f59770a321e6f277ab720881f250c Mon Sep 17 00:00:00 2001 From: Anne Tilloy <48123713+annetill@users.noreply.github.com> Date: Mon, 24 Jun 2024 09:45:04 +0200 Subject: [PATCH 15/57] TerminalsConnectionAction: fix javadoc (#3079) Signed-off-by: Anne Tilloy --- .../powsybl/action/TerminalsConnectionAction.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/action-api/src/main/java/com/powsybl/action/TerminalsConnectionAction.java b/action-api/src/main/java/com/powsybl/action/TerminalsConnectionAction.java index 04cca036eda..7135ffd8ea9 100644 --- a/action-api/src/main/java/com/powsybl/action/TerminalsConnectionAction.java +++ b/action-api/src/main/java/com/powsybl/action/TerminalsConnectionAction.java @@ -27,9 +27,10 @@ public class TerminalsConnectionAction extends AbstractAction { /** * @param id the id of the action. - * @param elementId the id of the element which terminals are operated. - * The element can be any connectable, including a tie line by referring the terminal of - * an underlying dangling line. + * @param elementId the id of the connectable which terminals are operated. + * As a tie line (respectively an hvdc line) is not a connectable, opening or closing a tie line + * (respectively an hvdc line) on both sides is done through two {@link TerminalsConnectionAction}, + * each one referring to one of the underlying dangling lines (respectively converter stations). * @param side the side of the element to operate in the action. * @param open the status for the terminal to operate. {@code true} means terminal opening. */ @@ -42,9 +43,10 @@ public TerminalsConnectionAction(String id, String elementId, ThreeSides side, b /** * @param id the id of the action. - * @param elementId the id of the element which terminals are operated. - * The element can be any connectable, including a tie line by referring the terminal of - * an underlying dangling line. + * @param elementId the id of the connectable which terminals are operated. + * As a tie line (respectively an hvdc line) is not a connectable, opening or closing a tie line + * (respectively an hvdc line) on both sides is done through two {@link TerminalsConnectionAction}, + * each one referring to one of the underlying dangling lines (respectively converter stations). * @param open the status for all the terminals of the element to operate. {@code true} means all terminals opening. */ public TerminalsConnectionAction(String id, String elementId, boolean open) { From 7e4fb45c6f045b54238625ae3507e55fe5e15c40 Mon Sep 17 00:00:00 2001 From: jeandemanged Date: Mon, 24 Jun 2024 15:50:37 +0200 Subject: [PATCH 16/57] Fix area listeners when merging and detaching Networks (#3081) Signed-off-by: Damien Jeandemange --- .../powsybl/iidm/network/impl/AreaImpl.java | 12 ++++++- .../iidm/network/impl/NetworkImpl.java | 5 +++ .../iidm/network/impl/SubnetworkImpl.java | 7 +++- .../iidm/network/tck/AbstractAreaTest.java | 36 +++++++++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java index 1bc2eac8173..fd3edc96eaa 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java @@ -48,9 +48,19 @@ public void beforeRemoval(Identifiable identifiable) { } } + /** + * Must be called whenever the Area is moved to a different root Network when merging and detaching. + * @param fromNetwork previous root network + * @param toNetwork new root network + */ + void moveListener(NetworkImpl fromNetwork, NetworkImpl toNetwork) { + fromNetwork.removeListener(this.areaListener); + toNetwork.addListener(this.areaListener); + } + private final NetworkListener areaListener; - public AreaImpl(Ref ref, Ref subnetworkRef, String id, String name, boolean fictitious, String areaType, + AreaImpl(Ref ref, Ref subnetworkRef, String id, String name, boolean fictitious, String areaType, double acInterchangeTarget) { super(id, name, fictitious); this.networkRef = Objects.requireNonNull(ref); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java index 45845e53fa6..c1c00967747 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java @@ -1018,6 +1018,11 @@ private void merge(Network other) { checkMergeability(otherNetwork); + otherNetwork.getAreaStream().forEach(a -> { + AreaImpl area = (AreaImpl) a; + area.moveListener(otherNetwork, this); + }); + // try to find dangling lines couples List lines = new ArrayList<>(); Map> dl1byPairingKey = new HashMap<>(); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubnetworkImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubnetworkImpl.java index a19d931c1c9..38f438af3e0 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubnetworkImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/SubnetworkImpl.java @@ -805,7 +805,7 @@ public Network detach() { transferExtensions(this, detachedNetwork); transferProperties(this, detachedNetwork); - // Memorize the network identifiables/voltageAngleLimits before moving references (to use them latter) + // Memorize the network identifiables/voltageAngleLimits before moving references (to use them later) Collection> identifiables = getIdentifiables(); Iterable vals = getVoltageAngleLimits(); @@ -831,6 +831,11 @@ public Network detach() { detachedNetwork.getVoltageAngleLimitsIndex().put(val.getId(), val); } + detachedNetwork.getAreaStream().forEach(a -> { + AreaImpl area = (AreaImpl) a; + area.moveListener(previousRootNetwork, detachedNetwork); + }); + // We don't control that regulating terminals and phase/ratio regulation terminals are in the same subnetwork // as their network elements (generators, PSTs, ...). It is unlikely that those terminals and their elements // are in different subnetworks but nothing prevents it. For now, we ignore this case, but it may be necessary diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java index 012a29725cb..59cbec5f610 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java @@ -11,6 +11,7 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.iidm.network.test.FourSubstationsNodeBreakerFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -424,6 +425,41 @@ public void removeEquipmentRemovesAreaBoundary() { assertEquals(2, controlAreaB.getAreaBoundaryStream().count()); } + @Test + public void removeEquipmentRemovesAreaBoundaryMergeAndDetach() { + // do a merge + Network fourBus = FourSubstationsNodeBreakerFactory.create(); + Network merged = Network.merge(network, fourBus); + network = merged.getSubnetwork("sim1"); + controlAreaA = network.getArea("ControlArea_A"); + controlAreaB = network.getArea("ControlArea_B"); + dlXnode1A = network.getDanglingLine("NHV1_XNODE1"); + tieLine1 = network.getTieLine("NHV1_NHV2_1"); + assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); + assertEquals(2, controlAreaB.getAreaBoundaryStream().count()); + + // Verify the cleanup listener is now effective on the merged network + tieLine1.remove(); + dlXnode1A.remove(); + + assertEquals(1, controlAreaA.getAreaBoundaryStream().count()); + assertEquals(2, controlAreaB.getAreaBoundaryStream().count()); + + // now detach + network = network.detach(); + controlAreaA = network.getArea("ControlArea_A"); + controlAreaB = network.getArea("ControlArea_B"); + dlXnode2A = network.getDanglingLine("NVH1_XNODE2"); + tieLine2 = network.getTieLine("NHV1_NHV2_2"); + + // Verify the cleanup listener is now effective on the detached network + tieLine2.remove(); + dlXnode2A.remove(); + + assertEquals(0, controlAreaA.getAreaBoundaryStream().count()); + assertEquals(2, controlAreaB.getAreaBoundaryStream().count()); + } + @Test public void throwBoundaryOtherNetwork() { Network subnetwork = network.createSubnetwork("subnetwork_id", "Subnetwork", "json"); From 67018049c6dae69548e47f73ada881a03e4d9c74 Mon Sep 17 00:00:00 2001 From: Sophie Frasnedo <93923177+So-Fras@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:09:06 +0200 Subject: [PATCH 17/57] Fix omission of styles.css (#3064) Signed-off-by: Sophie Frasnedo --- docs/_static/styles/styles.css | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/_static/styles/styles.css diff --git a/docs/_static/styles/styles.css b/docs/_static/styles/styles.css new file mode 100644 index 00000000000..f3340e112c6 --- /dev/null +++ b/docs/_static/styles/styles.css @@ -0,0 +1,6 @@ +/* The following code ensures font is not too big (Furo theme can make font too big on large screens) */ +@media(min-width:97em) { + html { + font-size: 100% + } +} \ No newline at end of file From cb5b396938dbe3d3d891fd0e2c2020d59b98e2e7 Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Thu, 27 Jun 2024 17:04:01 +0200 Subject: [PATCH 18/57] Fix VoltageRegulation extension XSDs for compatibility (#3083) Signed-off-by: Olivier Perrin --- .../extensions/VoltageRegulationSerDe.java | 56 ++++++++++++++----- .../voltageRegulation_V1_10.xsd | 26 +++++++++ .../voltageRegulation_V1_11.xsd | 26 +++++++++ .../voltageRegulation_V1_12.xsd | 26 +++++++++ .../voltageRegulation_V1_2.xsd | 26 +++++++++ .../voltageRegulation_V1_3.xsd | 26 +++++++++ .../voltageRegulation_V1_4.xsd | 26 +++++++++ .../voltageRegulation_V1_5.xsd | 26 +++++++++ .../voltageRegulation_V1_6.xsd | 26 +++++++++ .../voltageRegulation_V1_7.xsd | 26 +++++++++ .../voltageRegulation_V1_8.xsd | 26 +++++++++ .../voltageRegulation_V1_9.xsd | 26 +++++++++ .../test/VoltageRegulationSerDeTest.java | 45 +++++++++++---- .../voltageRegulationCompatibilityVersion.xml | 40 +++++++++++++ 14 files changed, 402 insertions(+), 25 deletions(-) create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_10.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_11.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_12.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_2.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_3.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_4.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_5.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_6.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_7.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_8.xsd create mode 100644 iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_9.xsd create mode 100644 iidm/iidm-serde/src/test/resources/V1_12/voltageRegulationCompatibilityVersion.xml diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/VoltageRegulationSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/VoltageRegulationSerDe.java index b7c577250cb..e2377edb335 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/VoltageRegulationSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/VoltageRegulationSerDe.java @@ -32,30 +32,45 @@ public class VoltageRegulationSerDe extends AbstractVersionableNetworkExtensionSerDe { public VoltageRegulationSerDe() { - super("voltageRegulation", VoltageRegulation.class, "vr", + super(VoltageRegulation.NAME, VoltageRegulation.class, "vr", ImmutableMap.>builder() .put(IidmVersion.V_1_0, ImmutableSortedSet.of("1.0", "1.0-legacy")) .put(IidmVersion.V_1_1, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_2, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_3, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_4, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_5, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_6, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_7, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_8, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_9, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_10, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_11, ImmutableSortedSet.of("1.1")) - .put(IidmVersion.V_1_12, ImmutableSortedSet.of("1.1")) + .put(IidmVersion.V_1_2, reversedNaturalOrderOf("1.1", "1.2")) + .put(IidmVersion.V_1_3, reversedNaturalOrderOf("1.1", "1.3")) + .put(IidmVersion.V_1_4, reversedNaturalOrderOf("1.1", "1.4")) + .put(IidmVersion.V_1_5, reversedNaturalOrderOf("1.1", "1.5")) + .put(IidmVersion.V_1_6, reversedNaturalOrderOf("1.1", "1.6")) + .put(IidmVersion.V_1_7, reversedNaturalOrderOf("1.1", "1.7")) + .put(IidmVersion.V_1_8, reversedNaturalOrderOf("1.1", "1.8")) + .put(IidmVersion.V_1_9, reversedNaturalOrderOf("1.1", "1.9")) + .put(IidmVersion.V_1_10, reversedNaturalOrderOf("1.1", "1.10")) + .put(IidmVersion.V_1_11, reversedNaturalOrderOf("1.1", "1.11")) + .put(IidmVersion.V_1_12, reversedNaturalOrderOf("1.1", "1.12")) .put(IidmVersion.V_1_13, ImmutableSortedSet.of("1.1")) .build(), ImmutableMap.builder() .put("1.0", "http://www.itesla_project.eu/schema/iidm/ext/voltage_regulation/1_0") .put("1.0-legacy", "http://www.itesla_project.eu/schema/iidm/ext/voltageregulation/1_0") .put("1.1", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_1") + .put("1.2", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_2") + .put("1.3", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_3") + .put("1.4", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_4") + .put("1.5", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_5") + .put("1.6", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_6") + .put("1.7", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_7") + .put("1.8", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_8") + .put("1.9", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_9") + .put("1.10", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_10") + .put("1.11", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_11") + .put("1.12", "http://www.powsybl.org/schema/iidm/ext/voltage_regulation/1_12") .build()); } + private static ImmutableSortedSet reversedNaturalOrderOf(String... versions) { + return ImmutableSortedSet.reverseOrder().add(versions).build(); + } + @Override public InputStream getXsdAsStream() { return getClass().getResourceAsStream("/xsd/voltageRegulation_V1_1.xsd"); @@ -63,9 +78,20 @@ public InputStream getXsdAsStream() { @Override public List getXsdAsStreamList() { - return List.of(getClass().getResourceAsStream("/xsd/voltageRegulation_V1_0.xsd"), - getClass().getResourceAsStream("/xsd/voltageRegulation_V1_0_legacy.xsd"), - getClass().getResourceAsStream("/xsd/voltageRegulation_V1_1.xsd")); + return List.of(Objects.requireNonNull(getClass().getResourceAsStream("/xsd/voltageRegulation_V1_0.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/voltageRegulation_V1_0_legacy.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/voltageRegulation_V1_1.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_2.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_3.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_4.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_5.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_6.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_7.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_8.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_9.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_10.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_11.xsd")), + Objects.requireNonNull(getClass().getResourceAsStream("/xsd/compatibility/voltage_regulation/voltageRegulation_V1_12.xsd"))); } @Override diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_10.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_10.xsd new file mode 100644 index 00000000000..a7bdbb44960 --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_10.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_11.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_11.xsd new file mode 100644 index 00000000000..3d33a968dee --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_11.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_12.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_12.xsd new file mode 100644 index 00000000000..669adf80ec4 --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_12.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_2.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_2.xsd new file mode 100644 index 00000000000..47b48da63f9 --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_2.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_3.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_3.xsd new file mode 100644 index 00000000000..bfe166fb6ee --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_3.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_4.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_4.xsd new file mode 100644 index 00000000000..191ada07bd8 --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_4.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_5.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_5.xsd new file mode 100644 index 00000000000..7e5c6dbf83b --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_5.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_6.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_6.xsd new file mode 100644 index 00000000000..69d3ff6b0c9 --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_6.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_7.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_7.xsd new file mode 100644 index 00000000000..959ec29e340 --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_7.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_8.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_8.xsd new file mode 100644 index 00000000000..7ad9450433b --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_8.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_9.xsd b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_9.xsd new file mode 100644 index 00000000000..8963243a8ac --- /dev/null +++ b/iidm/iidm-serde/src/main/resources/xsd/compatibility/voltage_regulation/voltageRegulation_V1_9.xsd @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/test/VoltageRegulationSerDeTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/test/VoltageRegulationSerDeTest.java index da3cc1b7189..ab8ff1d5d85 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/test/VoltageRegulationSerDeTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/test/VoltageRegulationSerDeTest.java @@ -14,7 +14,10 @@ import com.powsybl.iidm.network.extensions.VoltageRegulationAdder; import com.powsybl.iidm.network.test.BatteryNetworkFactory; import com.powsybl.iidm.serde.AbstractIidmSerDeTest; +import com.powsybl.iidm.serde.ExportOptions; import com.powsybl.iidm.serde.IidmSerDeConstants; +import com.powsybl.iidm.serde.IidmVersion; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -26,28 +29,37 @@ * @author Coline Piloquet {@literal } */ class VoltageRegulationSerDeTest extends AbstractIidmSerDeTest { - @Test - void test() throws IOException { - Network network = BatteryNetworkFactory.create(); + + private static Network network; + + @BeforeAll + static void init() { + network = BatteryNetworkFactory.create(); Generator gen = network.getGenerator("GEN"); Battery bat = network.getBattery("BAT"); assertNotNull(bat); bat.newExtension(VoltageRegulationAdder.class) - .withVoltageRegulatorOn(true) - .withTargetV(100.0) - .add(); + .withVoltageRegulatorOn(true) + .withTargetV(100.0) + .add(); Battery bat2 = network.getBattery("BAT2"); assertNotNull(bat2); bat2.newExtension(VoltageRegulationAdder.class) - .withRegulatingTerminal(gen.getTerminal()) - .withVoltageRegulatorOn(true) - .withTargetV(100) - .add(); + .withRegulatingTerminal(gen.getTerminal()) + .withVoltageRegulatorOn(true) + .withTargetV(100) + .add(); + } + @Test + void test() throws IOException { Network network2 = allFormatsRoundTripTest(network, "voltageRegulationRoundTripRef.xml", IidmSerDeConstants.CURRENT_IIDM_VERSION); + assertExtension(network2); + } + private static void assertExtension(Network network2) { VoltageRegulation voltageRegulationXml = network2.getBattery("BAT").getExtension(VoltageRegulation.class); assertNotNull(voltageRegulationXml); assertEquals(100.0, voltageRegulationXml.getTargetV(), 0); @@ -58,4 +70,17 @@ void test() throws IOException { assertNotNull(voltageRegulationXml); assertSame(network2.getGenerator("GEN").getTerminal(), voltageRegulationXml2.getRegulatingTerminal()); } + + @Test + void testOlderVersion() throws IOException { + // Round trip with default extension version (v1_1) + Network network2 = allFormatsRoundTripTest(network, "voltageRegulationRoundTripRef.xml", IidmVersion.V_1_12); + assertExtension(network2); + + // Import then export with version v1_12 + ExportOptions exportOptions = new ExportOptions(); + exportOptions.addExtensionVersion(VoltageRegulation.NAME, "1.12"); + Network network3 = allFormatsRoundTripTest(network, "voltageRegulationCompatibilityVersion.xml", IidmVersion.V_1_12, exportOptions); + assertExtension(network3); + } } diff --git a/iidm/iidm-serde/src/test/resources/V1_12/voltageRegulationCompatibilityVersion.xml b/iidm/iidm-serde/src/test/resources/V1_12/voltageRegulationCompatibilityVersion.xml new file mode 100644 index 00000000000..ce64a3bad16 --- /dev/null +++ b/iidm/iidm-serde/src/test/resources/V1_12/voltageRegulationCompatibilityVersion.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 20478c3a57f2db752cc619ac41137aad9db97d3a Mon Sep 17 00:00:00 2001 From: Naledi <151443525+nao1345678@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:27:00 +0200 Subject: [PATCH 19/57] Allow zero value for limits in loading limits (#3061) * allow zero value in loading limit Signed-off-by: Naledi EL CHEIKH --- .../powsybl/iidm/network/ValidationUtil.java | 9 +++++++-- .../impl/AbstractLoadingLimitsAdder.java | 12 +++++++++--- .../tck/AbstractCurrentLimitsTest.java | 19 +++++++++++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java index c7ce27c067e..a3824c1283d 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java @@ -562,10 +562,15 @@ private static ValidationLevel checkPermanentLimit(Validable validable, double p throwExceptionOrLogError(validable, "permanent limit must be defined if temporary limits are present", throwException, reportNode); validationLevel = ValidationLevel.min(validationLevel, ValidationLevel.EQUIPMENT); } - if (!Double.isNaN(permanentLimit) && permanentLimit <= 0) { + if (permanentLimit < 0) { // because it is forbidden for SSH and EQ validation levels. - throw new ValidationException(validable, "permanent limit must be > 0"); + throw new ValidationException(validable, "permanent limit must be >= 0"); } + if (permanentLimit == 0) { + // log if zero + LOGGER.info("{}permanent limit is set to 0", validable.getMessageHeader()); + } + return validationLevel; } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java index d7719490aed..e8ea74fda2b 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java @@ -9,15 +9,18 @@ import com.powsybl.commons.report.ReportNode; import com.powsybl.iidm.network.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.*; - import static java.lang.Integer.MAX_VALUE; /** * @author Miora Ralambotiana {@literal } */ abstract class AbstractLoadingLimitsAdder> implements LoadingLimitsAdder { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLoadingLimitsAdder.class); + private static final Comparator ACCEPTABLE_DURATION_COMPARATOR = (acceptableDuration1, acceptableDuration2) -> acceptableDuration2 - acceptableDuration1; protected final Validable validable; @@ -74,8 +77,11 @@ public B endTemporaryLimit() { if (Double.isNaN(value)) { throw new ValidationException(validable, "temporary limit value is not set"); } - if (value <= 0) { - throw new ValidationException(validable, "temporary limit value must be > 0"); + if (value < 0) { + throw new ValidationException(validable, "temporary limit value must be >= 0"); + } + if (value == 0) { + LOGGER.info("{}temporary limit value is set to 0", validable.getMessageHeader()); } if (acceptableDuration == null) { throw new ValidationException(validable, "acceptable duration is not set"); diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java index 9c3b13d5713..f1412ec5a6e 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test; import java.util.Collection; +import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; @@ -678,4 +679,22 @@ public void testAdderSetPermanentLimitWithInfiniteDurationValue() { assertEquals(1, names.size()); assertTrue(names.contains("TL1")); } + + @Test + public void testAdderWithValueZero() { + Line line = createNetwork().getLine("L"); + CurrentLimitsAdder adder = line.newCurrentLimits1() + .setPermanentLimit(0) + .beginTemporaryLimit() + .setName("TEST") + .setAcceptableDuration(Integer.MAX_VALUE) + .setValue(0) + .endTemporaryLimit(); + adder.add(); + Optional optionalLimits = line.getCurrentLimits(TwoSides.ONE); + assertTrue(optionalLimits.isPresent()); + CurrentLimits limits = optionalLimits.get(); + assertEquals(0, limits.getPermanentLimit()); + assertEquals(0, limits.getTemporaryLimit(Integer.MAX_VALUE).getValue()); + } } From 09042bfe248def59bb128b69dcef8f860b526581 Mon Sep 17 00:00:00 2001 From: vidaldid-rte <156446663+vidaldid-rte@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:14:09 +0200 Subject: [PATCH 20/57] `ActivePowerControl` extension: Rename optional fields min/maxPOverride -> min/maxTargetP (#3084) * rename optional fields min/maxPOverride -> min/maxTargetP * review feedback Signed-off-by: Didier Vidal --- docs/grid_model/extensions.md | 8 +- .../extensions/ActivePowerControl.java | 18 ++-- .../extensions/ActivePowerControlAdder.java | 4 +- .../ActivePowerControlAdderImpl.java | 14 +-- .../extensions/ActivePowerControlImpl.java | 95 +++++++++++++++---- .../extensions/ActivePowerControlSerDe.java | 16 ++-- .../resources/xsd/activePowerControl_V1_2.xsd | 4 +- .../extensions/ActivePowerControlXmlTest.java | 19 ++-- ...ctivePowerControlWithLimitRoundTripRef.xml | 4 +- .../AbstractActivePowerControlTest.java | 53 +++++++---- 10 files changed, 158 insertions(+), 77 deletions(-) diff --git a/docs/grid_model/extensions.md b/docs/grid_model/extensions.md index 35b7490639a..54519106dce 100644 --- a/docs/grid_model/extensions.md +++ b/docs/grid_model/extensions.md @@ -20,8 +20,8 @@ This extension is used to configure the participation factor of the generator, t | participate | boolean | - | yes | - | The participation status | | droop | double | None (repartition key) | no | - | The participation factor equals maxP / droop | | participation factor | double | None (repartition key) | no | - | Defines the participation factor explicitly | -| maxP override | double | MW | no | - | If defined, this limit is used for slack distribution instead of the generator's maxP | -| minP override | double | MW | no | - | if defined, this limit is used for slack distribution instead of the generator's minP | +| max targetP | double | MW | no | - | If defined, this limit is used for slack distribution instead of the generator's maxP | +| min targetP | double | MW | no | - | if defined, this limit is used for slack distribution instead of the generator's minP | Here is how to add an active power control extension to a generator: ```java @@ -31,8 +31,8 @@ generator.newExtension(ActivePowerControlAdder.class) .withParticipationFactor(1.5) .add(); ``` - -The participation status, the participation factor, the maxP override and the minP override are multi-variants: they can vary from one variant to another. +If defined, min targetP and max targetP must be in the [pMin, pMax] interval of the Generator or Battery. +The participation status, the participation factor, the max targetP and the min targetP are multi-variants: they can vary from one variant to another. This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. diff --git a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControl.java b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControl.java index af9dc407473..e6b503132a7 100644 --- a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControl.java +++ b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControl.java @@ -59,22 +59,24 @@ default String getName() { /** * @return if present, provides the overridden value of minP to be used for active power control operations. */ - OptionalDouble getMinPOverride(); + OptionalDouble getMinTargetP(); /** - * Sets the overridden minimal active power. - * @param pMinOverride The overridden value of minP. A Nan value removes the override. + * Sets the minimum value for targetP. The value must be in the [pmin,pmax] interval of the extended + * generator or battery. + * @param minTargetP The overridden value of minP. A Nan value removes the override. */ - void setMinPOverride(double pMinOverride); + void setMinTargetP(double minTargetP); /** * @return if present, provides the overridden value of maxP to be used for active power control operations. */ - OptionalDouble getMaxPOverride(); + OptionalDouble getMaxTargetP(); /** - * Sets the overridden maximal active power. - * @param pMaxOverride The overridden value of maxP. A Nan value removes the override. + * Sets the maximum value for targetP. The value must be in the [pmin,pmax] interval of the extended + * generator or battery. + * @param maxTargetP The overridden value of maxP. A Nan value removes the override. */ - void setMaxPOverride(double pMaxOverride); + void setMaxTargetP(double maxTargetP); } diff --git a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControlAdder.java b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControlAdder.java index b4d6e500b2f..8d4b86a797e 100644 --- a/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControlAdder.java +++ b/iidm/iidm-extensions/src/main/java/com/powsybl/iidm/network/extensions/ActivePowerControlAdder.java @@ -24,7 +24,7 @@ default Class getExtensionClass() { ActivePowerControlAdder withParticipationFactor(double participationFactor); - ActivePowerControlAdder withMinPOverride(double minPOverride); + ActivePowerControlAdder withMinTargetP(double minTargetP); - ActivePowerControlAdder withMaxPOverride(double maxPOverride); + ActivePowerControlAdder withMaxTargetP(double maxTargetP); } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlAdderImpl.java index 69235972c9e..59fdb4e1a21 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlAdderImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlAdderImpl.java @@ -20,8 +20,8 @@ public class ActivePowerControlAdderImpl> private double droop = Double.NaN; private double participationFactor = Double.NaN; - private double minPOverride = Double.NaN; - private double maxPOverride = Double.NaN; + private double minTargetP = Double.NaN; + private double maxTargetP = Double.NaN; protected ActivePowerControlAdderImpl(I extendable) { super(extendable); @@ -29,7 +29,7 @@ protected ActivePowerControlAdderImpl(I extendable) { @Override protected ActivePowerControlImpl createExtension(I extendable) { - return new ActivePowerControlImpl<>(extendable, participate, droop, participationFactor, minPOverride, maxPOverride); + return new ActivePowerControlImpl<>(extendable, participate, droop, participationFactor, minTargetP, maxTargetP); } @Override @@ -51,14 +51,14 @@ public ActivePowerControlAdder withParticipationFactor(double participationFa } @Override - public ActivePowerControlAdder withMinPOverride(double minPOverride) { - this.minPOverride = minPOverride; + public ActivePowerControlAdder withMinTargetP(double minTargetP) { + this.minTargetP = minTargetP; return this; } @Override - public ActivePowerControlAdder withMaxPOverride(double maxPOverride) { - this.maxPOverride = maxPOverride; + public ActivePowerControlAdder withMaxTargetP(double maxTargetP) { + this.maxTargetP = maxTargetP; return this; } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlImpl.java index c890aabe86e..22339d7ba18 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/extensions/ActivePowerControlImpl.java @@ -7,11 +7,16 @@ */ package com.powsybl.iidm.network.impl.extensions; +import com.powsybl.commons.PowsyblException; import com.powsybl.commons.util.trove.TBooleanArrayList; +import com.powsybl.iidm.network.Battery; +import com.powsybl.iidm.network.Generator; import com.powsybl.iidm.network.Injection; import com.powsybl.iidm.network.extensions.ActivePowerControl; import com.powsybl.iidm.network.impl.AbstractMultiVariantIdentifiableExtension; import gnu.trove.list.array.TDoubleArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.List; import java.util.OptionalDouble; @@ -22,13 +27,14 @@ public class ActivePowerControlImpl> extends AbstractMultiVariantIdentifiableExtension implements ActivePowerControl { + private static final Logger LOGGER = LoggerFactory.getLogger(ActivePowerControlImpl.class); private final TBooleanArrayList participate; private final TDoubleArrayList droop; private final TDoubleArrayList participationFactor; - private final TDoubleArrayList minPOverride; - private final TDoubleArrayList maxPOverride; + private final TDoubleArrayList minTargetP; + private final TDoubleArrayList maxTargetP; private final List allTDoubleArrayLists; @@ -43,22 +49,71 @@ public ActivePowerControlImpl(T component, boolean participate, double droop, double participationFactor, - double minPOverride, - double maxPOverride) { + double minTargetP, + double maxTargetP) { super(component); int variantArraySize = getVariantManagerHolder().getVariantManager().getVariantArraySize(); this.participate = new TBooleanArrayList(variantArraySize); this.droop = new TDoubleArrayList(variantArraySize); this.participationFactor = new TDoubleArrayList(variantArraySize); - this.minPOverride = new TDoubleArrayList(variantArraySize); - this.maxPOverride = new TDoubleArrayList(variantArraySize); - this.allTDoubleArrayLists = List.of(this.droop, this.participationFactor, this.minPOverride, this.maxPOverride); + this.minTargetP = new TDoubleArrayList(variantArraySize); + this.maxTargetP = new TDoubleArrayList(variantArraySize); + this.allTDoubleArrayLists = List.of(this.droop, this.participationFactor, this.minTargetP, this.maxTargetP); + double checkedMinTargetP = checkTargetPLimit(minTargetP, "minTargetP", component); + double checkedMaxTargetP = checkTargetPLimit(maxTargetP, "maxTargetP", component); for (int i = 0; i < variantArraySize; i++) { this.participate.add(participate); this.droop.add(droop); this.participationFactor.add(participationFactor); - this.minPOverride.add(minPOverride); - this.maxPOverride.add(maxPOverride); + this.minTargetP.add(checkedMinTargetP); + this.maxTargetP.add(checkedMaxTargetP); + } + checkLimitOrder(minTargetP, maxTargetP); + } + + record PLimits(double minP, double maxP) { } + + private PLimits getPLimits(T injection) { + double maxP = Double.MAX_VALUE; + double minP = -Double.MAX_VALUE; + if (injection instanceof Generator generator) { + maxP = generator.getMaxP(); + minP = generator.getMinP(); + } else if (injection instanceof Battery battery) { + maxP = battery.getMaxP(); + minP = battery.getMinP(); + } + return new PLimits(minP, maxP); + } + + private double withinPMinMax(double value, T injection) { + PLimits pLimits = getPLimits(injection); + + if (!Double.isNaN(value) && (value < pLimits.minP || value > pLimits.maxP)) { + LOGGER.warn("targetP limit is now outside of pMin,pMax for component {}. Returning closest value in [pmin,pMax].", + injection.getId()); + return value < pLimits.minP ? pLimits.minP : pLimits.maxP; + } + return value; + } + + private double checkTargetPLimit(double targetPLimit, String name, T injection) { + PLimits pLimits = getPLimits(injection); + + if (!Double.isNaN(targetPLimit) && (targetPLimit < pLimits.minP || targetPLimit > pLimits.maxP)) { + throw new PowsyblException(String.format("%s value (%s) is not between minP and maxP for component %s", + name, + targetPLimit, + injection.getId())); + } + + return targetPLimit; + } + + private void checkLimitOrder(double minTargetP, double maxTargetP) { + if (!Double.isNaN(minTargetP) && !Double.isNaN(maxTargetP) + && minTargetP > maxTargetP) { + throw new PowsyblException("invalid targetP limits [" + minTargetP + ", " + maxTargetP + "]"); } } @@ -116,24 +171,26 @@ public void allocateVariantArrayElement(int[] indexes, int sourceIndex) { } @Override - public OptionalDouble getMinPOverride() { - double result = minPOverride.get(getVariantIndex()); - return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(result); + public OptionalDouble getMinTargetP() { + double result = minTargetP.get(getVariantIndex()); + return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(withinPMinMax(result, getExtendable())); } @Override - public void setMinPOverride(double minPOverride) { - this.minPOverride.set(getVariantIndex(), minPOverride); + public void setMinTargetP(double minTargetP) { + checkLimitOrder(minTargetP, maxTargetP.get(getVariantIndex())); + this.minTargetP.set(getVariantIndex(), checkTargetPLimit(minTargetP, "minTargetP", getExtendable())); } @Override - public OptionalDouble getMaxPOverride() { - double result = maxPOverride.get(getVariantIndex()); - return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(result); + public OptionalDouble getMaxTargetP() { + double result = maxTargetP.get(getVariantIndex()); + return Double.isNaN(result) ? OptionalDouble.empty() : OptionalDouble.of(withinPMinMax(result, getExtendable())); } @Override - public void setMaxPOverride(double maxPOverride) { - this.maxPOverride.set(getVariantIndex(), maxPOverride); + public void setMaxTargetP(double maxTargetP) { + checkLimitOrder(minTargetP.get(getVariantIndex()), maxTargetP); + this.maxTargetP.set(getVariantIndex(), checkTargetPLimit(maxTargetP, "maxTargetP", getExtendable())); } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java index 6c318c9b59a..7ca6d7cfcbb 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java @@ -67,8 +67,8 @@ public void write(ActivePowerControl activePowerControl, SerializerContext co } if ("1.2".compareTo(extVersionStr) <= 0) { // not using writeOptionalDouble and trusting implementation convention: : writeDoubleAttribute does not write NaN values in human-readable formats JSON/XML - context.getWriter().writeDoubleAttribute("maxPOverride", activePowerControl.getMaxPOverride().orElse(Double.NaN)); - context.getWriter().writeDoubleAttribute("minPOverride", activePowerControl.getMinPOverride().orElse(Double.NaN)); + context.getWriter().writeDoubleAttribute("maxTargetP", activePowerControl.getMaxTargetP().orElse(Double.NaN)); + context.getWriter().writeDoubleAttribute("minTargetP", activePowerControl.getMinTargetP().orElse(Double.NaN)); } } @@ -89,8 +89,8 @@ public ActivePowerControl read(T identifiable, DeserializerContext context) { boolean participate = context.getReader().readBooleanAttribute("participate"); double droop = context.getReader().readDoubleAttribute("droop"); double participationFactor = Double.NaN; - double minPOverride = Double.NaN; - double maxPOverride = Double.NaN; + double minTargetP = Double.NaN; + double maxTargetP = Double.NaN; NetworkDeserializerContext networkContext = (NetworkDeserializerContext) context; String extVersionStr = networkContext.getExtensionVersion(this).orElseThrow(IllegalStateException::new); if ("1.1".compareTo(extVersionStr) <= 0) { @@ -98,16 +98,16 @@ public ActivePowerControl read(T identifiable, DeserializerContext context) { } if ("1.2".compareTo(extVersionStr) <= 0) { // not using readOptionalDouble and trusting implementation convention: readDoubleAttribute returns Nan if attribute is absent in human-readable formats (JSON / XML) - maxPOverride = context.getReader().readDoubleAttribute("maxPOverride"); - minPOverride = context.getReader().readDoubleAttribute("minPOverride"); + maxTargetP = context.getReader().readDoubleAttribute("maxTargetP"); + minTargetP = context.getReader().readDoubleAttribute("minTargetP"); } context.getReader().readEndNode(); ActivePowerControlAdder activePowerControlAdder = identifiable.newExtension(ActivePowerControlAdder.class); return activePowerControlAdder.withParticipate(participate) .withDroop(droop) .withParticipationFactor(participationFactor) - .withMinPOverride(minPOverride) - .withMaxPOverride(maxPOverride) + .withMinTargetP(minTargetP) + .withMaxTargetP(maxTargetP) .add(); } } diff --git a/iidm/iidm-serde/src/main/resources/xsd/activePowerControl_V1_2.xsd b/iidm/iidm-serde/src/main/resources/xsd/activePowerControl_V1_2.xsd index d53fa4a9ca9..db43a8b8d25 100644 --- a/iidm/iidm-serde/src/main/resources/xsd/activePowerControl_V1_2.xsd +++ b/iidm/iidm-serde/src/main/resources/xsd/activePowerControl_V1_2.xsd @@ -17,8 +17,8 @@ - - + +
diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java index fc8a63e5361..e740ccce8a5 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/ActivePowerControlXmlTest.java @@ -33,6 +33,7 @@ class ActivePowerControlXmlTest extends AbstractIidmSerDeTest { private Network network; + @Override @BeforeEach public void setUp() throws IOException { super.setUp(); @@ -47,24 +48,24 @@ public void setUp() throws IOException { } @Test - void testPLimitOverride() throws IOException { - network.getGenerator("GEN").getExtension(ActivePowerControl.class).setMaxPOverride(100.); - network.getBattery("BAT").getExtension(ActivePowerControl.class).setMinPOverride(10.); + void testTargetPLimits() throws IOException { + network.getGenerator("GEN").getExtension(ActivePowerControl.class).setMaxTargetP(800.); + network.getBattery("BAT").getExtension(ActivePowerControl.class).setMinTargetP(10.); Network network2 = allFormatsRoundTripTest(network, "/activePowerControlWithLimitRoundTripRef.xml", CURRENT_IIDM_VERSION); Generator gen2 = network2.getGenerator("GEN"); assertNotNull(gen2); ActivePowerControl activePowerControl1 = gen2.getExtension(ActivePowerControl.class); assertNotNull(activePowerControl1); - assertEquals(OptionalDouble.of(100), activePowerControl1.getMaxPOverride()); - assertTrue(activePowerControl1.getMinPOverride().isEmpty()); + assertEquals(OptionalDouble.of(800), activePowerControl1.getMaxTargetP()); + assertTrue(activePowerControl1.getMinTargetP().isEmpty()); Battery bat2 = network2.getBattery("BAT"); assertNotNull(bat2); ActivePowerControl activePowerControl2 = bat2.getExtension(ActivePowerControl.class); assertNotNull(activePowerControl2); - assertTrue(activePowerControl2.getMaxPOverride().isEmpty()); - assertEquals(OptionalDouble.of(10), activePowerControl2.getMinPOverride()); + assertTrue(activePowerControl2.getMaxTargetP().isEmpty()); + assertEquals(OptionalDouble.of(10), activePowerControl2.getMinTargetP()); } @Test @@ -75,8 +76,8 @@ void testIidmV12() throws IOException { assertNotNull(bat2); ActivePowerControl activePowerControl2 = bat2.getExtension(ActivePowerControl.class); assertNotNull(activePowerControl2); - assertTrue(activePowerControl2.getMaxPOverride().isEmpty()); - assertTrue(activePowerControl2.getMinPOverride().isEmpty()); + assertTrue(activePowerControl2.getMaxTargetP().isEmpty()); + assertTrue(activePowerControl2.getMinTargetP().isEmpty()); } @Test diff --git a/iidm/iidm-serde/src/test/resources/V1_13/activePowerControlWithLimitRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_13/activePowerControlWithLimitRoundTripRef.xml index 567b5e0782d..1e5ae4221ed 100644 --- a/iidm/iidm-serde/src/test/resources/V1_13/activePowerControlWithLimitRoundTripRef.xml +++ b/iidm/iidm-serde/src/test/resources/V1_13/activePowerControlWithLimitRoundTripRef.xml @@ -30,9 +30,9 @@ - + - + \ No newline at end of file diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractActivePowerControlTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractActivePowerControlTest.java index 32f2b1e213f..bb1ed844e2c 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractActivePowerControlTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/extensions/AbstractActivePowerControlTest.java @@ -19,6 +19,7 @@ import java.util.Arrays; import java.util.List; +import java.util.OptionalDouble; import static com.powsybl.iidm.network.VariantManagerConstants.INITIAL_VARIANT_ID; import static org.junit.jupiter.api.Assertions.*; @@ -90,6 +91,25 @@ public void variantsCloneTest() { variantManager.setWorkingVariant(variant3); checkValues1(activePowerControl); + // test limitControl + double minP = bat.getMinP(); + double maxP = bat.getMaxP(); + assertThrows(PowsyblException.class, () -> activePowerControl.setMaxTargetP(maxP + 1)); + assertThrows(PowsyblException.class, () -> activePowerControl.setMaxTargetP(minP - 1)); + assertThrows(PowsyblException.class, () -> activePowerControl.setMinTargetP(maxP + 1)); + assertThrows(PowsyblException.class, () -> activePowerControl.setMinTargetP(minP - 1)); + + activePowerControl.setMaxTargetP(200); + activePowerControl.setMinTargetP(100); + assertThrows(PowsyblException.class, () -> activePowerControl.setMinTargetP(201)); + assertThrows(PowsyblException.class, () -> activePowerControl.setMaxTargetP(99)); + + // try to fool the extension + bat.setMaxP(190); + bat.setMinP(110); + assertEquals(OptionalDouble.of(190), activePowerControl.getMaxTargetP()); + assertEquals(OptionalDouble.of(110), activePowerControl.getMinTargetP()); + // Test removing current variant variantManager.removeVariant(variant3); try { @@ -113,8 +133,8 @@ public void variantsCloneTestWithOverride() { .withDroop(4.0) .withParticipate(true) .withParticipationFactor(1.2) - .withMinPOverride(10) - .withMaxPOverride(100) + .withMinTargetP(10) + .withMaxTargetP(100) .add(); ActivePowerControl activePowerControl = bat.getExtension(ActivePowerControl.class); assertNotNull(activePowerControl); @@ -130,12 +150,12 @@ public void variantsCloneTestWithOverride() { activePowerControl.setDroop(6.0); activePowerControl.setParticipate(false); activePowerControl.setParticipationFactor(3.0); - activePowerControl.setMaxPOverride(110.); - activePowerControl.setMinPOverride(Double.NaN); + activePowerControl.setMaxTargetP(110.); + activePowerControl.setMinTargetP(Double.NaN); checkValues4(activePowerControl); - activePowerControl.setMinPOverride(11.); - activePowerControl.setMaxPOverride(Double.NaN); + activePowerControl.setMinTargetP(11.); + activePowerControl.setMaxTargetP(Double.NaN); checkValues5(activePowerControl); variantManager.setWorkingVariant(INITIAL_VARIANT_ID); @@ -164,39 +184,40 @@ private static void checkValues1(ActivePowerControl activePowerControl) assertTrue(activePowerControl.isParticipate()); assertEquals(4.0, activePowerControl.getDroop(), 0.0); assertEquals(1.2, activePowerControl.getParticipationFactor(), 0.0); - assertTrue(activePowerControl.getMaxPOverride().isEmpty()); - assertTrue(activePowerControl.getMinPOverride().isEmpty()); + assertTrue(activePowerControl.getMaxTargetP().isEmpty()); + assertTrue(activePowerControl.getMinTargetP().isEmpty()); } private static void checkValues2(ActivePowerControl activePowerControl) { assertFalse(activePowerControl.isParticipate()); assertEquals(6.0, activePowerControl.getDroop(), 0.0); assertEquals(3.0, activePowerControl.getParticipationFactor(), 0.0); - assertTrue(activePowerControl.getMaxPOverride().isEmpty()); - assertTrue(activePowerControl.getMinPOverride().isEmpty()); + assertTrue(activePowerControl.getMaxTargetP().isEmpty()); + assertTrue(activePowerControl.getMinTargetP().isEmpty()); } private static void checkValues3(ActivePowerControl activePowerControl) { assertTrue(activePowerControl.isParticipate()); assertEquals(4.0, activePowerControl.getDroop(), 0.0); assertEquals(1.2, activePowerControl.getParticipationFactor(), 0.0); - assertEquals(10, activePowerControl.getMinPOverride().getAsDouble()); - assertEquals(100, activePowerControl.getMaxPOverride().getAsDouble()); + assertEquals(10, activePowerControl.getMinTargetP().getAsDouble()); + assertEquals(100, activePowerControl.getMaxTargetP().getAsDouble()); } private static void checkValues4(ActivePowerControl activePowerControl) { assertFalse(activePowerControl.isParticipate()); assertEquals(6.0, activePowerControl.getDroop(), 0.0); assertEquals(3.0, activePowerControl.getParticipationFactor(), 0.0); - assertTrue(activePowerControl.getMinPOverride().isEmpty()); - assertEquals(110, activePowerControl.getMaxPOverride().getAsDouble()); + assertTrue(activePowerControl.getMinTargetP().isEmpty()); + assertEquals(110, activePowerControl.getMaxTargetP().getAsDouble()); } private static void checkValues5(ActivePowerControl activePowerControl) { assertFalse(activePowerControl.isParticipate()); assertEquals(6.0, activePowerControl.getDroop(), 0.0); assertEquals(3.0, activePowerControl.getParticipationFactor(), 0.0); - assertTrue(activePowerControl.getMaxPOverride().isEmpty()); - assertEquals(11, activePowerControl.getMinPOverride().getAsDouble()); + assertTrue(activePowerControl.getMaxTargetP().isEmpty()); + assertEquals(11, activePowerControl.getMinTargetP().getAsDouble()); } + } From e1fac997097b0ebf731428b9429ade2d33130552 Mon Sep 17 00:00:00 2001 From: Nicolas Rol Date: Fri, 28 Jun 2024 08:38:13 +0200 Subject: [PATCH 21/57] Add connect/disconnect on Tie Lines and HVDC Lines (#3072) * add connection and disconnection to HVDC lines and Tie Lines * extract connect/disconnect methods to util class * modify javadoc and parameters name Signed-off-by: Nicolas Rol --- .../action/TerminalsConnectionAction.java | 20 +- .../action/ApplyActionToNetworkTest.java | 18 +- .../com/powsybl/iidm/network/HvdcLine.java | 14 ++ .../com/powsybl/iidm/network/TieLine.java | 14 ++ .../network/impl/AbstractConnectable.java | 120 ++--------- .../network/impl/ConnectDisconnectUtil.java | 155 +++++++++++++++ .../iidm/network/impl/HvdcLineImpl.java | 51 ++++- .../network/impl/NodeBreakerVoltageLevel.java | 6 +- .../iidm/network/impl/TieLineImpl.java | 50 ++++- .../modification/AbstractDisconnection.java | 59 ++++-- .../modification/ConnectableConnection.java | 44 +++-- .../ConnectableConnectionBuilder.java | 28 ++- .../modification/PlannedDisconnection.java | 15 +- .../PlannedDisconnectionBuilder.java | 30 ++- .../modification/UnplannedDisconnection.java | 15 +- .../UnplannedDisconnectionBuilder.java | 30 ++- .../util/ModificationReports.java | 20 +- .../ConnectionAndDisconnectionsTest.java | 186 +++++++++++++++--- .../reportNode/connectable-connected.txt | 2 +- .../connectable-disconnected-planned.txt | 2 +- .../connectable-disconnected-unplanned.txt | 2 +- .../reportNode/connectable-not-connected.txt | 1 + .../connectable-not-disconnected-planned.txt | 3 +- ...connectable-not-disconnected-unplanned.txt | 3 +- .../network/tck/AbstractHvdcLineTest.java | 41 ++++ .../iidm/network/tck/AbstractTieLineTest.java | 115 +++++++++++ 26 files changed, 829 insertions(+), 215 deletions(-) create mode 100644 iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ConnectDisconnectUtil.java diff --git a/action-api/src/main/java/com/powsybl/action/TerminalsConnectionAction.java b/action-api/src/main/java/com/powsybl/action/TerminalsConnectionAction.java index 7135ffd8ea9..21ce6b1ef52 100644 --- a/action-api/src/main/java/com/powsybl/action/TerminalsConnectionAction.java +++ b/action-api/src/main/java/com/powsybl/action/TerminalsConnectionAction.java @@ -27,10 +27,10 @@ public class TerminalsConnectionAction extends AbstractAction { /** * @param id the id of the action. - * @param elementId the id of the connectable which terminals are operated. - * As a tie line (respectively an hvdc line) is not a connectable, opening or closing a tie line - * (respectively an hvdc line) on both sides is done through two {@link TerminalsConnectionAction}, - * each one referring to one of the underlying dangling lines (respectively converter stations). + * @param elementId the id of the element which terminals are operated. + * The element can be a connectable, a tie line or an HVDC line. + * For a tie line (respectively an HVDC line), it will operate the terminals of the underlying + * dangling lines (respectively converter stations). * @param side the side of the element to operate in the action. * @param open the status for the terminal to operate. {@code true} means terminal opening. */ @@ -43,10 +43,10 @@ public TerminalsConnectionAction(String id, String elementId, ThreeSides side, b /** * @param id the id of the action. - * @param elementId the id of the connectable which terminals are operated. - * As a tie line (respectively an hvdc line) is not a connectable, opening or closing a tie line - * (respectively an hvdc line) on both sides is done through two {@link TerminalsConnectionAction}, - * each one referring to one of the underlying dangling lines (respectively converter stations). + * @param elementId the id of the element which terminals are operated. + * The element can be a connectable, a tie line or an HVDC line. + * For a tie line (respectively an HVDC line), it will operate the terminals of the underlying + * dangling lines (respectively converter stations). * @param open the status for all the terminals of the element to operate. {@code true} means all terminals opening. */ public TerminalsConnectionAction(String id, String elementId, boolean open) { @@ -90,12 +90,12 @@ public boolean isOpen() { public NetworkModification toModification() { if (isOpen()) { PlannedDisconnectionBuilder builder = new PlannedDisconnectionBuilder() - .withConnectableId(elementId) + .withIdentifiableId(elementId) .withSide(side); return builder.build(); } else { ConnectableConnectionBuilder builder = new ConnectableConnectionBuilder() - .withConnectableId(elementId) + .withIdentifiableId(elementId) .withOnlyBreakersOperable(true) .withSide(side); return builder.build(); diff --git a/action-api/src/test/java/com/powsybl/action/ApplyActionToNetworkTest.java b/action-api/src/test/java/com/powsybl/action/ApplyActionToNetworkTest.java index 5abc717ebeb..08a26805e3f 100644 --- a/action-api/src/test/java/com/powsybl/action/ApplyActionToNetworkTest.java +++ b/action-api/src/test/java/com/powsybl/action/ApplyActionToNetworkTest.java @@ -63,26 +63,28 @@ void terminalConnectionAction() { assertFalse(branch.getTerminal(TwoSides.ONE).isConnected()); } - /** - * This action fails on tie-lines since they are not a connectable - */ @Test - void terminalConnectionActionOnTieLineException() { + void terminalConnectionActionOnTieLine() { Network network = EurostagTutorialExample1Factory.createWithTieLine(); + TieLine tieLine = network.getTieLine("NHV1_NHV2_1"); // Disconnection TerminalsConnectionAction disconnectionAction = new TerminalsConnectionAction("id", "NHV1_NHV2_1", true); NetworkModification disconnection = disconnectionAction.toModification(); NamingStrategy namingStrategy = new DefaultNamingStrategy(); ComputationManager computationManager = LocalComputationManager.getDefault(); - PowsyblException disconnectionException = assertThrows(PowsyblException.class, () -> disconnection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); - assertEquals("Connectable 'NHV1_NHV2_1' not found", disconnectionException.getMessage()); + assertTrue(tieLine.getDanglingLine1().getTerminal().isConnected()); + assertTrue(tieLine.getDanglingLine2().getTerminal().isConnected()); + disconnection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP); + assertFalse(tieLine.getDanglingLine1().getTerminal().isConnected()); + assertFalse(tieLine.getDanglingLine2().getTerminal().isConnected()); // Connection TerminalsConnectionAction connectionAction = new TerminalsConnectionAction("id", "NHV1_NHV2_1", false); NetworkModification connection = connectionAction.toModification(); - PowsyblException connectionException = assertThrows(PowsyblException.class, () -> connection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); - assertEquals("Connectable 'NHV1_NHV2_1' not found", connectionException.getMessage()); + connection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP); + assertTrue(tieLine.getDanglingLine1().getTerminal().isConnected()); + assertTrue(tieLine.getDanglingLine2().getTerminal().isConnected()); } @Test diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/HvdcLine.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/HvdcLine.java index 10a646544e0..602a6dee1eb 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/HvdcLine.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/HvdcLine.java @@ -7,6 +7,8 @@ */ package com.powsybl.iidm.network; +import java.util.function.Predicate; + /** * A HVDC line connected to two HVDC converters on DC side. * It has to be connected to the same {@link HvdcConverterStation} subclass. @@ -204,6 +206,18 @@ default HvdcConverterStation getConverterStation(TwoSides side) { */ void remove(); + boolean connectConverterStations(); + + boolean connectConverterStations(Predicate isTypeSwitchToOperate); + + boolean connectConverterStations(Predicate isTypeSwitchToOperate, TwoSides side); + + boolean disconnectConverterStations(); + + boolean disconnectConverterStations(Predicate isSwitchOpenable); + + boolean disconnectConverterStations(Predicate isSwitchOpenable, TwoSides side); + @Override default IdentifiableType getType() { return IdentifiableType.HVDC_LINE; diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/TieLine.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/TieLine.java index e538c56192f..b2355ee3367 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/TieLine.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/TieLine.java @@ -7,6 +7,8 @@ */ package com.powsybl.iidm.network; +import java.util.function.Predicate; + /** * A tie line is an AC line sharing power between two neighbouring regional grids. It is constituted of two {@link DanglingLine} *

@@ -129,5 +131,17 @@ default IdentifiableType getType() { */ void remove(boolean updateDanglingLines); + boolean connectDanglingLines(); + + boolean connectDanglingLines(Predicate isTypeSwitchToOperate); + + boolean connectDanglingLines(Predicate isTypeSwitchToOperate, TwoSides side); + + boolean disconnectDanglingLines(); + + boolean disconnectDanglingLines(Predicate isSwitchOpenable); + + boolean disconnectDanglingLines(Predicate isSwitchOpenable, TwoSides side); + Network getNetwork(); } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractConnectable.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractConnectable.java index 6f8f88f25a3..7025a5084bf 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractConnectable.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractConnectable.java @@ -8,17 +8,16 @@ package com.powsybl.iidm.network.impl; import com.powsybl.commons.PowsyblException; -import com.powsybl.commons.report.ReportNode; -import com.powsybl.commons.report.TypedValue; -import com.powsybl.iidm.network.*; import com.powsybl.commons.ref.Ref; +import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.util.SwitchPredicates; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import java.util.function.Predicate; import java.util.function.Supplier; -import static com.powsybl.iidm.network.TopologyKind.BUS_BREAKER; import static com.powsybl.iidm.network.TopologyKind.NODE_BREAKER; /** @@ -206,59 +205,12 @@ public boolean connect(Predicate isTypeSwitchToOperate) { @Override public boolean connect(Predicate isTypeSwitchToOperate, ThreeSides side) { - // ReportNode - ReportNode reportNode = this.getNetwork().getReportNodeContext().getReportNode(); - - // Booleans - boolean isAlreadyConnected = true; - boolean isNowConnected = true; - - // Initialisation of a list to open in case some terminals are in node-breaker view - Set switchForDisconnection = new HashSet<>(); - - // We try to connect each terminal - for (TerminalExt terminal : getTerminals(side)) { - // Check if the terminal is already connected - if (terminal.isConnected()) { - reportNode.newReportNode() - .withMessageTemplate("alreadyConnectedTerminal", "A terminal of connectable ${connectable} is already connected.") - .withUntypedValue("connectable", this.getId()) - .withSeverity(TypedValue.WARN_SEVERITY) - .add(); - continue; - } else { - isAlreadyConnected = false; - } - - // If it's a node-breaker terminal, the switches to connect are added to a set - if (terminal.getVoltageLevel() instanceof NodeBreakerVoltageLevel nodeBreakerVoltageLevel) { - isNowConnected = nodeBreakerVoltageLevel.getConnectingSwitches(terminal, isTypeSwitchToOperate, switchForDisconnection); - } - // If it's a bus-breaker terminal, there is nothing to do - - // Exit if the terminal cannot be connected - if (!isNowConnected) { - return false; - } - } - - // Exit if the connectable is already fully connected - if (isAlreadyConnected) { - return false; - } - - // Connect all bus-breaker terminals - for (TerminalExt terminal : getTerminals(side)) { - if (!terminal.isConnected() - && terminal.getVoltageLevel().getTopologyKind() == BUS_BREAKER) { - // At this point, isNowConnected should always stay true but let's be careful - isNowConnected = isNowConnected && terminal.connect(isTypeSwitchToOperate); - } - } - // Disconnect all switches on node-breaker terminals - switchForDisconnection.forEach(sw -> sw.setOpen(false)); - return isNowConnected; + return ConnectDisconnectUtil.connectAllTerminals( + this, + getTerminals(side), + isTypeSwitchToOperate, + getNetwork().getReportNodeContext().getReportNode()); } @Override @@ -273,55 +225,11 @@ public boolean disconnect(Predicate isSwitchOpenable) { @Override public boolean disconnect(Predicate isSwitchOpenable, ThreeSides side) { - // ReportNode - ReportNode reportNode = this.getNetwork().getReportNodeContext().getReportNode(); - - // Booleans - boolean isAlreadyDisconnected = true; - boolean isNowDisconnected = true; - - // Initialisation of a list to open in case some terminals are in node-breaker view - Set switchForDisconnection = new HashSet<>(); - - // We try to disconnect each terminal - for (TerminalExt terminal : getTerminals(side)) { - // Check if the terminal is already disconnected - if (!terminal.isConnected()) { - reportNode.newReportNode() - .withMessageTemplate("alreadyDisconnectedTerminal", "A terminal of connectable ${connectable} is already disconnected.") - .withUntypedValue("connectable", this.getId()) - .withSeverity(TypedValue.WARN_SEVERITY) - .add(); - continue; - } - // The terminal is connected - isAlreadyDisconnected = false; - - // If it's a node-breaker terminal, the switches to disconnect are added to a set - if (terminal.getVoltageLevel() instanceof NodeBreakerVoltageLevel nodeBreakerVoltageLevel - && !nodeBreakerVoltageLevel.getDisconnectingSwitches(terminal, isSwitchOpenable, switchForDisconnection)) { - // Exit if the terminal cannot be disconnected - return false; - } - // If it's a bus-breaker terminal, there is nothing to do - } - - // Exit if the connectable is already fully disconnected - if (isAlreadyDisconnected) { - return false; - } - - // Disconnect all bus-breaker terminals - for (TerminalExt terminal : getTerminals(side)) { - if (terminal.isConnected() - && terminal.getVoltageLevel().getTopologyKind() == BUS_BREAKER) { - // At this point, isNowDisconnected should always stay true but let's be careful - isNowDisconnected = isNowDisconnected && terminal.disconnect(isSwitchOpenable); - } - } - // Disconnect all switches on node-breaker terminals - switchForDisconnection.forEach(sw -> sw.setOpen(true)); - return isNowDisconnected; + return ConnectDisconnectUtil.disconnectAllTerminals( + this, + getTerminals(side), + isSwitchOpenable, + getNetwork().getReportNodeContext().getReportNode()); } public List getTerminals(ThreeSides side) { diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ConnectDisconnectUtil.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ConnectDisconnectUtil.java new file mode 100644 index 00000000000..e178a176e2e --- /dev/null +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ConnectDisconnectUtil.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.impl; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.report.TypedValue; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.Switch; +import com.powsybl.iidm.network.Terminal; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +import static com.powsybl.iidm.network.TopologyKind.BUS_BREAKER; + +/** + * @author Nicolas Rol {@literal } + */ +public final class ConnectDisconnectUtil { + + private ConnectDisconnectUtil() { + } + + /** + * Connect the specified terminals. It will connect all the specified terminals or none if at least one cannot be + * connected. + * @param identifiable network element to connect. It can be a connectable, a tie line or an HVDC line + * @param terminals list of the terminals that should be connected. For a connectable, it should be its own + * terminals, while for a tie line (respectively an HVDC line) it should be the terminals of the + * underlying dangling lines (respectively converter stations) + * @param isTypeSwitchToOperate type of switches that can be operated + * @param reportNode report node + * @return {@code true} if all the specified terminals have been connected, else {@code false}. + */ + static boolean connectAllTerminals(Identifiable identifiable, List terminals, Predicate isTypeSwitchToOperate, ReportNode reportNode) { + + // Booleans + boolean isAlreadyConnected = true; + boolean isNowConnected = true; + + // Initialisation of a list to open in case some terminals are in node-breaker view + Set switchForDisconnection = new HashSet<>(); + + // We try to connect each terminal + for (Terminal terminal : terminals) { + // Check if the terminal is already connected + if (terminal.isConnected()) { + reportNode.newReportNode() + .withMessageTemplate("alreadyConnectedTerminal", "A terminal of identifiable ${identifiable} is already connected.") + .withUntypedValue("identifiable", identifiable.getId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + continue; + } else { + isAlreadyConnected = false; + } + + // If it's a node-breaker terminal, the switches to connect are added to a set + if (terminal.getVoltageLevel() instanceof NodeBreakerVoltageLevel nodeBreakerVoltageLevel) { + isNowConnected = nodeBreakerVoltageLevel.getConnectingSwitches(terminal, isTypeSwitchToOperate, switchForDisconnection); + } + // If it's a bus-breaker terminal, there is nothing to do + + // Exit if the terminal cannot be connected + if (!isNowConnected) { + return false; + } + } + + // Exit if the connectable is already fully connected + if (isAlreadyConnected) { + return false; + } + + // Connect all bus-breaker terminals + for (Terminal terminal : terminals) { + if (!terminal.isConnected() + && terminal.getVoltageLevel().getTopologyKind() == BUS_BREAKER) { + // At this point, isNowConnected should always stay true but let's be careful + isNowConnected = isNowConnected && terminal.connect(isTypeSwitchToOperate); + } + } + + // Disconnect all switches on node-breaker terminals + switchForDisconnection.forEach(sw -> sw.setOpen(false)); + return isNowConnected; + } + + /** + * Disconnect the specified terminals. It will disconnect all the specified terminals or none if at least one cannot + * be disconnected. + * @param identifiable network element to disconnect. It can be a connectable, a tie line or an HVDC line + * @param terminals list of the terminals that should be connected. For a connectable, it should be its own + * terminals, while for a tie line (respectively an HVDC line) it should be the terminals of the + * underlying dangling lines (respectively converter stations) + * @param isSwitchOpenable type of switches that can be operated + * @param reportNode report node + * @return {@code true} if all the specified terminals have been disconnected, else {@code false}. + */ + static boolean disconnectAllTerminals(Identifiable identifiable, List terminals, Predicate isSwitchOpenable, ReportNode reportNode) { + // Booleans + boolean isAlreadyDisconnected = true; + boolean isNowDisconnected = true; + + // Initialisation of a list to open in case some terminals are in node-breaker view + Set switchForDisconnection = new HashSet<>(); + + // We try to disconnect each terminal + for (Terminal terminal : terminals) { + // Check if the terminal is already disconnected + if (!terminal.isConnected()) { + reportNode.newReportNode() + .withMessageTemplate("alreadyDisconnectedTerminal", "A terminal of identifiable ${identifiable} is already disconnected.") + .withUntypedValue("identifiable", identifiable.getId()) + .withSeverity(TypedValue.WARN_SEVERITY) + .add(); + continue; + } + // The terminal is connected + isAlreadyDisconnected = false; + + // If it's a node-breaker terminal, the switches to disconnect are added to a set + if (terminal.getVoltageLevel() instanceof NodeBreakerVoltageLevel nodeBreakerVoltageLevel + && !nodeBreakerVoltageLevel.getDisconnectingSwitches(terminal, isSwitchOpenable, switchForDisconnection)) { + // Exit if the terminal cannot be disconnected + return false; + } + // If it's a bus-breaker terminal, there is nothing to do + } + + // Exit if the connectable is already fully disconnected + if (isAlreadyDisconnected) { + return false; + } + + // Disconnect all bus-breaker terminals + for (Terminal terminal : terminals) { + if (terminal.isConnected() + && terminal.getVoltageLevel().getTopologyKind() == BUS_BREAKER) { + // At this point, isNowDisconnected should always stay true but let's be careful + isNowDisconnected = isNowDisconnected && terminal.disconnect(isSwitchOpenable); + } + } + // Disconnect all switches on node-breaker terminals + switchForDisconnection.forEach(sw -> sw.setOpen(true)); + return isNowDisconnected; + } +} diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/HvdcLineImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/HvdcLineImpl.java index ea31deb2cff..31821594abc 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/HvdcLineImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/HvdcLineImpl.java @@ -7,11 +7,15 @@ */ package com.powsybl.iidm.network.impl; -import com.powsybl.iidm.network.*; import com.powsybl.commons.ref.Ref; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.util.SwitchPredicates; import gnu.trove.list.array.TDoubleArrayList; import gnu.trove.list.array.TIntArrayList; +import java.util.List; +import java.util.function.Predicate; + /** * @author Geoffroy Jamgotchian {@literal } * @author Mathieu Bague {@literal } @@ -231,6 +235,51 @@ public void remove() { network.getListeners().notifyAfterRemoval(id); } + @Override + public boolean connectConverterStations() { + return connectConverterStations(SwitchPredicates.IS_NONFICTIONAL_BREAKER, null); + } + + @Override + public boolean connectConverterStations(Predicate isTypeSwitchToOperate) { + return connectConverterStations(isTypeSwitchToOperate, null); + } + + @Override + public boolean connectConverterStations(Predicate isTypeSwitchToOperate, TwoSides side) { + return ConnectDisconnectUtil.connectAllTerminals( + this, + getTerminalsOfConverterStations(side), + isTypeSwitchToOperate, + getNetwork().getReportNodeContext().getReportNode()); + } + + @Override + public boolean disconnectConverterStations() { + return disconnectConverterStations(SwitchPredicates.IS_CLOSED_BREAKER, null); + } + + @Override + public boolean disconnectConverterStations(Predicate isSwitchOpenable) { + return disconnectConverterStations(isSwitchOpenable, null); + } + + @Override + public boolean disconnectConverterStations(Predicate isSwitchOpenable, TwoSides side) { + return ConnectDisconnectUtil.disconnectAllTerminals( + this, + getTerminalsOfConverterStations(side), + isSwitchOpenable, + getNetwork().getReportNodeContext().getReportNode()); + } + + private List getTerminalsOfConverterStations(TwoSides side) { + return side == null ? List.of(getConverterStation1().getTerminal(), getConverterStation2().getTerminal()) : switch (side) { + case ONE -> List.of(getConverterStation1().getTerminal()); + case TWO -> List.of(getConverterStation2().getTerminal()); + }; + } + @Override protected String getTypeDescription() { return TYPE_DESCRIPTION; diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java index d2c5b629008..eab64debcd9 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java @@ -1169,7 +1169,7 @@ private boolean checkNonClosableSwitch(SwitchImpl sw, Predicate isSwi } } - boolean getConnectingSwitches(TerminalExt terminal, Predicate isSwitchOperable, Set switchForConnection) { + boolean getConnectingSwitches(Terminal terminal, Predicate isSwitchOperable, Set switchForConnection) { // Check the topology kind checkTopologyKind(terminal); @@ -1274,7 +1274,7 @@ public boolean disconnect(TerminalExt terminal, Predicate is } } - boolean getDisconnectingSwitches(TerminalExt terminal, Predicate isSwitchOpenable, Set switchForDisconnection) { + boolean getDisconnectingSwitches(Terminal terminal, Predicate isSwitchOpenable, Set switchForDisconnection) { // Check the topology kind checkTopologyKind(terminal); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java index 7b9b4f57286..7f2472a7fd1 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/TieLineImpl.java @@ -8,13 +8,16 @@ package com.powsybl.iidm.network.impl; import com.powsybl.commons.PowsyblException; -import com.powsybl.iidm.network.*; import com.powsybl.commons.ref.Ref; +import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.util.LimitViolationUtils; +import com.powsybl.iidm.network.util.SwitchPredicates; import com.powsybl.iidm.network.util.TieLineUtil; import java.util.Collection; +import java.util.List; import java.util.Optional; +import java.util.function.Predicate; /** * @author Geoffroy Jamgotchian {@literal } @@ -162,6 +165,51 @@ public void remove(boolean updateDanglingLines) { removed = true; } + @Override + public boolean connectDanglingLines() { + return connectDanglingLines(SwitchPredicates.IS_NONFICTIONAL_BREAKER, null); + } + + @Override + public boolean connectDanglingLines(Predicate isTypeSwitchToOperate) { + return connectDanglingLines(isTypeSwitchToOperate, null); + } + + @Override + public boolean connectDanglingLines(Predicate isTypeSwitchToOperate, TwoSides side) { + return ConnectDisconnectUtil.connectAllTerminals( + this, + getTerminalsOfDanglingLines(side), + isTypeSwitchToOperate, + getNetwork().getReportNodeContext().getReportNode()); + } + + @Override + public boolean disconnectDanglingLines() { + return disconnectDanglingLines(SwitchPredicates.IS_CLOSED_BREAKER, null); + } + + @Override + public boolean disconnectDanglingLines(Predicate isSwitchOpenable) { + return disconnectDanglingLines(isSwitchOpenable, null); + } + + @Override + public boolean disconnectDanglingLines(Predicate isSwitchOpenable, TwoSides side) { + return ConnectDisconnectUtil.disconnectAllTerminals( + this, + getTerminalsOfDanglingLines(side), + isSwitchOpenable, + getNetwork().getReportNodeContext().getReportNode()); + } + + private List getTerminalsOfDanglingLines(TwoSides side) { + return side == null ? List.of(getTerminal1(), getTerminal2()) : switch (side) { + case ONE -> List.of(getTerminal1()); + case TWO -> List.of(getTerminal2()); + }; + } + @Override public TerminalExt getTerminal1() { return danglingLine1.getTerminal(); diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/AbstractDisconnection.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/AbstractDisconnection.java index 7a4a9363275..cb0531fa984 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/AbstractDisconnection.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/AbstractDisconnection.java @@ -8,29 +8,36 @@ package com.powsybl.iidm.modification; import com.powsybl.commons.report.ReportNode; -import com.powsybl.iidm.network.Connectable; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.Switch; -import com.powsybl.iidm.network.ThreeSides; +import com.powsybl.iidm.network.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Objects; import java.util.function.Predicate; -import static com.powsybl.iidm.modification.util.ModificationReports.connectableDisconnectionReport; +import static com.powsybl.iidm.modification.util.ModificationReports.identifiableDisconnectionReport; /** + *

This network modification is used to disconnect a network element from the bus or bus bar section to which it is + * currently connected.

+ *

It works on:

+ *
    + *
  • Connectables
  • + *
  • HVDC lines by disconnecting their converter stations
  • + *
  • Tie lines by disconnecting their underlying dangling lines
  • + *
+ *

The user can specify a side of the element to disconnect. If no side is specified, the network modification will + * try to disconnect every side.

* @author Nicolas Rol {@literal } */ public abstract class AbstractDisconnection extends AbstractNetworkModification { private static final Logger LOG = LoggerFactory.getLogger(AbstractDisconnection.class); - final String connectableId; + final String identifiableId; final Predicate openableSwitches; final ThreeSides side; - AbstractDisconnection(String connectableId, Predicate openableSwitches, ThreeSides side) { - this.connectableId = Objects.requireNonNull(connectableId); + AbstractDisconnection(String identifiableId, Predicate openableSwitches, ThreeSides side) { + this.identifiableId = Objects.requireNonNull(identifiableId); this.openableSwitches = openableSwitches; this.side = side; } @@ -40,25 +47,43 @@ public void applyModification(Network network, boolean isPlanned, boolean throwE network.getReportNodeContext().pushReportNode(reportNode); // Get the connectable - Connectable connectable = network.getConnectable(connectableId); - if (connectable == null) { - logOrThrow(throwException, "Connectable '" + connectableId + "' not found"); - return; + Identifiable identifiable = network.getIdentifiable(identifiableId); + + // Disconnect the identifiable if it exists + if (identifiable == null) { + logOrThrow(throwException, "Identifiable '" + identifiableId + "' not found"); + } else { + disconnectIdentifiable(identifiable, network, isPlanned, throwException, reportNode); } + } - // Disconnect the connectable + private void disconnectIdentifiable(Identifiable identifiable, Network network, boolean isPlanned, boolean throwException, ReportNode reportNode) { boolean hasBeenDisconnected; try { - hasBeenDisconnected = connectable.disconnect(openableSwitches, side); + hasBeenDisconnected = disconnect(identifiable, throwException); } finally { network.getReportNodeContext().popReportNode(); } if (hasBeenDisconnected) { - LOG.info("Connectable {} has been disconnected ({} disconnection) {}.", connectableId, isPlanned ? "planned" : "unplanned", side == null ? "on each side" : "on side " + side.getNum()); + LOG.info("Identifiable {} has been disconnected ({} disconnection) {}.", identifiableId, isPlanned ? "planned" : "unplanned", side == null ? "on each side" : "on side " + side.getNum()); + } else { + LOG.info("Identifiable {} has NOT been disconnected ({} disconnection) {}.", identifiableId, isPlanned ? "planned" : "unplanned", side == null ? "on each side" : "on side " + side.getNum()); + } + identifiableDisconnectionReport(reportNode, identifiable, hasBeenDisconnected, isPlanned, side); + } + + private boolean disconnect(Identifiable identifiable, boolean throwException) { + boolean hasBeenDisconnected = false; + if (identifiable instanceof Connectable connectable) { + hasBeenDisconnected = connectable.disconnect(openableSwitches, side); + } else if (identifiable instanceof TieLine tieLine) { + hasBeenDisconnected = tieLine.disconnectDanglingLines(openableSwitches, side == null ? null : side.toTwoSides()); + } else if (identifiable instanceof HvdcLine hvdcLine) { + hasBeenDisconnected = hvdcLine.disconnectConverterStations(openableSwitches, side == null ? null : side.toTwoSides()); } else { - LOG.info("Connectable {} has NOT been disconnected ({} disconnection) {}.", connectableId, isPlanned ? "planned" : "unplanned", side == null ? "on each side" : "on side " + side.getNum()); + logOrThrow(throwException, String.format("Disconnection not implemented for identifiable '%s'", identifiableId)); } - connectableDisconnectionReport(reportNode, connectable, hasBeenDisconnected, isPlanned, side); + return hasBeenDisconnected; } } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnection.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnection.java index ef5e928f925..4a3d56e752b 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnection.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnection.java @@ -10,10 +10,7 @@ import com.powsybl.commons.report.ReportNode; import com.powsybl.computation.ComputationManager; import com.powsybl.iidm.modification.topology.NamingStrategy; -import com.powsybl.iidm.network.Connectable; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.Switch; -import com.powsybl.iidm.network.ThreeSides; +import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.util.SwitchPredicates; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +21,15 @@ import static com.powsybl.iidm.modification.util.ModificationReports.connectableConnectionReport; /** + *

This network modification is used to connect a network element to the closest bus or bus bar section.

+ *

It works on:

+ *
    + *
  • Connectables by connecting their terminals
  • + *
  • HVDC lines by connecting the terminals of their converter stations
  • + *
  • Tie lines by connecting the terminals of their underlying dangling lines
  • + *
+ *

The user can specify a side of the element to connect. If no side is specified, the network modification will + * try to connect every side.

* @author Nicolas Rol {@literal } */ public class ConnectableConnection extends AbstractNetworkModification { @@ -58,19 +64,31 @@ public class ConnectableConnection extends AbstractNetworkModification { @Override public void apply(Network network, NamingStrategy namingStrategy, boolean throwException, ComputationManager computationManager, ReportNode reportNode) { // Get the connectable - Connectable connectable = network.getConnectable(connectableId); - if (connectable == null) { - logOrThrow(throwException, "Connectable '" + connectableId + "' not found"); - return; - } + Identifiable identifiable = network.getIdentifiable(connectableId); // Add the reportNode to the network reportNode context network.getReportNodeContext().pushReportNode(reportNode); - // Disconnect the connectable - boolean hasBeenConnected; + // Connect the element if it exists + if (identifiable == null) { + logOrThrow(throwException, "Identifiable '" + connectableId + "' not found"); + } else { + connectIdentifiable(identifiable, network, throwException, reportNode); + } + } + + private void connectIdentifiable(Identifiable identifiable, Network network, boolean throwException, ReportNode reportNode) { + boolean hasBeenConnected = false; try { - hasBeenConnected = connectable.connect(isTypeSwitchToOperate, side); + if (identifiable instanceof Connectable connectable) { + hasBeenConnected = connectable.connect(isTypeSwitchToOperate, side); + } else if (identifiable instanceof TieLine tieLine) { + hasBeenConnected = tieLine.connectDanglingLines(isTypeSwitchToOperate, side == null ? null : side.toTwoSides()); + } else if (identifiable instanceof HvdcLine hvdcLine) { + hasBeenConnected = hvdcLine.connectConverterStations(isTypeSwitchToOperate, side == null ? null : side.toTwoSides()); + } else { + logOrThrow(throwException, String.format("Connection not implemented for identifiable '%s'", connectableId)); + } } finally { network.getReportNodeContext().popReportNode(); } @@ -80,6 +98,6 @@ public void apply(Network network, NamingStrategy namingStrategy, boolean throwE } else { LOG.info("Connectable {} has NOT been connected {}.", connectableId, side == null ? "on each side" : "on side " + side.getNum()); } - connectableConnectionReport(reportNode, connectable, hasBeenConnected, side); + connectableConnectionReport(reportNode, identifiable, hasBeenConnected, side); } } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnectionBuilder.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnectionBuilder.java index 39479104655..11d2047df3b 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnectionBuilder.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/ConnectableConnectionBuilder.java @@ -10,16 +10,38 @@ import com.powsybl.iidm.network.ThreeSides; /** + *

This builder help creating the network modification used to connect a network element to the closest bus or bus + * bar section. It works on:

+ *
    + *
  • Connectables by connecting their terminals
  • + *
  • HVDC lines by connecting the terminals of their converter stations
  • + *
  • Tie lines by connecting the terminals of their underlying dangling lines
  • + *
+ *

The user can specify a side of the element to connect. If no side is specified, the network modification will + * try to connect every side.

* @author Nicolas Rol {@literal } */ public class ConnectableConnectionBuilder { - String connectableId = null; + String identifiableId = null; boolean operateFictitiousSwitches = false; boolean operateOnlyBreakers = false; ThreeSides side; + /** + * Specify the network element to connect. It can be either a connectable, an HVDC line or a tie line. + * @param identifiableId id of the network element to connect + */ + public ConnectableConnectionBuilder withIdentifiableId(String identifiableId) { + this.identifiableId = identifiableId; + return this; + } + + /** + * @deprecated Use {@link ConnectableConnectionBuilder#withIdentifiableId(String)} instead + */ + @Deprecated(since = "6.4.0") public ConnectableConnectionBuilder withConnectableId(String connectableId) { - this.connectableId = connectableId; + this.identifiableId = connectableId; return this; } @@ -39,6 +61,6 @@ public ConnectableConnectionBuilder withSide(ThreeSides side) { } public ConnectableConnection build() { - return new ConnectableConnection(connectableId, operateFictitiousSwitches, operateOnlyBreakers, side); + return new ConnectableConnection(identifiableId, operateFictitiousSwitches, operateOnlyBreakers, side); } } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnection.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnection.java index 646e69bf034..bf9e6c1d2e0 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnection.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnection.java @@ -15,12 +15,23 @@ import com.powsybl.iidm.network.util.SwitchPredicates; /** + *

This network modification is used to disconnect a network element from the bus or bus bar section to which it is + * currently connected. This network modification should be used if the disconnection is planned. If it is not, + * {@link UnplannedDisconnection} should be used instead.

+ *

It works on:

+ *
    + *
  • Connectables
  • + *
  • HVDC lines by disconnecting their converter stations
  • + *
  • Tie lines by disconnecting their underlying dangling lines
  • + *
+ *

The user can specify a side of the element to disconnect. If no side is specified, the network modification will + * try to disconnect every side.

* @author Nicolas Rol {@literal } */ public class PlannedDisconnection extends AbstractDisconnection { - PlannedDisconnection(String connectableId, boolean openFictitiousSwitches, ThreeSides side) { - super(connectableId, openFictitiousSwitches ? + PlannedDisconnection(String identifiableId, boolean openFictitiousSwitches, ThreeSides side) { + super(identifiableId, openFictitiousSwitches ? SwitchPredicates.IS_OPEN.negate() : SwitchPredicates.IS_OPEN.negate().and(SwitchPredicates.IS_NONFICTIONAL), side); } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnectionBuilder.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnectionBuilder.java index ecda406f197..7244f681b2d 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnectionBuilder.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/PlannedDisconnectionBuilder.java @@ -10,15 +10,39 @@ import com.powsybl.iidm.network.ThreeSides; /** + *

This builder help creating the network modification used to disconnect a network element from the bus or bus bar + * section to which it is currently connected. This builder should be used if the disconnection is planned. + * If it is not, {@link UnplannedDisconnectionBuilder} should be used instead.

+ *

It works on:

+ *
    + *
  • Connectables
  • + *
  • HVDC lines by disconnecting their converter stations
  • + *
  • Tie lines by disconnecting their underlying dangling lines
  • + *
+ *

The user can specify a side of the element to disconnect. If no side is specified, the network modification will + * try to disconnect every side.

* @author Nicolas Rol {@literal } */ public class PlannedDisconnectionBuilder { - String connectableId = null; + String identifiableId = null; boolean openFictitiousSwitches = false; ThreeSides side; + /** + * Specify the network element to disconnect. It can be either a connectable, an HVDC line or a tie line. + * @param identifiableId id of the network element to disconnect + */ + public PlannedDisconnectionBuilder withIdentifiableId(String identifiableId) { + this.identifiableId = identifiableId; + return this; + } + + /** + * @deprecated Use {@link PlannedDisconnectionBuilder#withIdentifiableId(String)} instead + */ + @Deprecated(since = "6.4.0") public PlannedDisconnectionBuilder withConnectableId(String connectableId) { - this.connectableId = connectableId; + this.identifiableId = connectableId; return this; } @@ -33,6 +57,6 @@ public PlannedDisconnectionBuilder withSide(ThreeSides side) { } public PlannedDisconnection build() { - return new PlannedDisconnection(connectableId, openFictitiousSwitches, side); + return new PlannedDisconnection(identifiableId, openFictitiousSwitches, side); } } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnection.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnection.java index eda8d1b732d..1390cc8053a 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnection.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnection.java @@ -15,12 +15,23 @@ import com.powsybl.iidm.network.util.SwitchPredicates; /** + *

This network modification is used to disconnect a network element from the bus or bus bar section to which it is + * currently connected. This network modification should be used if the disconnection is not planned. If it is planned, + * {@link PlannedDisconnection} should be used instead.

+ *

It works on:

+ *
    + *
  • Connectables
  • + *
  • HVDC lines by disconnecting their converter stations
  • + *
  • Tie lines by disconnecting their underlying dangling lines
  • + *
+ *

The user can specify a side of the element to disconnect. If no side is specified, the network modification will + * try to disconnect every side.

* @author Nicolas Rol {@literal } */ public class UnplannedDisconnection extends AbstractDisconnection { - UnplannedDisconnection(String connectableId, boolean openFictitiousSwitches, ThreeSides side) { - super(connectableId, openFictitiousSwitches ? + UnplannedDisconnection(String identifiableId, boolean openFictitiousSwitches, ThreeSides side) { + super(identifiableId, openFictitiousSwitches ? SwitchPredicates.IS_OPEN.negate().and(SwitchPredicates.IS_BREAKER) : SwitchPredicates.IS_OPEN.negate().and(SwitchPredicates.IS_BREAKER).and(SwitchPredicates.IS_NONFICTIONAL), side); } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnectionBuilder.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnectionBuilder.java index 217ad99e783..9c11ad53f73 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnectionBuilder.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/UnplannedDisconnectionBuilder.java @@ -10,15 +10,39 @@ import com.powsybl.iidm.network.ThreeSides; /** + *

This builder help creating the network modification used to disconnect a network element from the bus or bus bar + * section to which it is currently connected. This builder should be used if the disconnection is not planned. If it + * is planned, {@link PlannedDisconnectionBuilder} should be used instead.

+ *

It works on:

+ *
    + *
  • Connectables
  • + *
  • HVDC lines by disconnecting their converter stations
  • + *
  • Tie lines by disconnecting their underlying dangling lines
  • + *
+ *

The user can specify a side of the element to disconnect. If no side is specified, the network modification will + * try to disconnect every side.

* @author Nicolas Rol {@literal } */ public class UnplannedDisconnectionBuilder { - String connectableId = null; + String identifiableId = null; boolean openFictitiousSwitches = false; ThreeSides side; + /** + * Specify the network element to disconnect. It can be either a connectable, an HVDC line or a tie line. + * @param identifiableId id of the network element to disconnect + */ + public UnplannedDisconnectionBuilder withIdentifiableId(String identifiableId) { + this.identifiableId = identifiableId; + return this; + } + + /** + * @deprecated Use {@link UnplannedDisconnectionBuilder#withIdentifiableId(String)} instead + */ + @Deprecated(since = "6.4.0") public UnplannedDisconnectionBuilder withConnectableId(String connectableId) { - this.connectableId = connectableId; + this.identifiableId = connectableId; return this; } @@ -33,6 +57,6 @@ public UnplannedDisconnectionBuilder withSide(ThreeSides side) { } public UnplannedDisconnection build() { - return new UnplannedDisconnection(connectableId, openFictitiousSwitches, side); + return new UnplannedDisconnection(identifiableId, openFictitiousSwitches, side); } } diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/util/ModificationReports.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/util/ModificationReports.java index 85ce59aa435..46659a297cf 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/util/ModificationReports.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/util/ModificationReports.java @@ -583,30 +583,32 @@ public static void scalingReport(ReportNode reportNode, String type, ScalingType .add(); } - public static void connectableConnectionReport(ReportNode reportNode, Connectable connectable, boolean connectionSuccessful, ThreeSides side) { + public static void connectableConnectionReport(ReportNode reportNode, Identifiable identifiable, boolean connectionSuccessful, ThreeSides side) { String defaultMessage = connectionSuccessful ? - "Connectable ${connectable} has been connected" : - "Connectable ${connectable} has NOT been connected"; + "Connectable ${identifiable} has been connected" : + "Connectable ${identifiable} has NOT been connected"; defaultMessage += side == null ? " on each side." : " on side " + side.getNum() + "."; String key = connectionSuccessful ? "connectableConnected" : "connectableNotConnected"; + key += side == null ? "" : "Side" + side.getNum(); reportNode.newReportNode() .withMessageTemplate(key, defaultMessage) - .withUntypedValue("connectable", connectable.getId()) + .withUntypedValue("identifiable", identifiable.getId()) .withSeverity(TypedValue.INFO_SEVERITY) .add(); } - public static void connectableDisconnectionReport(ReportNode reportNode, Connectable connectable, boolean disconnectionSuccessful, boolean isPlanned, ThreeSides side) { + public static void identifiableDisconnectionReport(ReportNode reportNode, Identifiable identifiable, boolean disconnectionSuccessful, boolean isPlanned, ThreeSides side) { String defaultMessage = disconnectionSuccessful ? - "Connectable ${connectable} has been disconnected" : - "Connectable ${connectable} has NOT been disconnected"; + "Identifiable ${identifiable} has been disconnected" : + "Identifiable ${identifiable} has NOT been disconnected"; defaultMessage += isPlanned ? " (planned disconnection)" : " (unplanned disconnection)"; defaultMessage += side == null ? " on each side." : " on side " + side.getNum() + "."; String key = isPlanned ? "planned" : "unplanned"; - key += disconnectionSuccessful ? "ConnectableDisconnected" : "ConnectableNotDisconnected"; + key += disconnectionSuccessful ? "IdentifiableDisconnected" : "IdentifiableNotDisconnected"; + key += side == null ? "" : "Side" + side.getNum(); reportNode.newReportNode() .withMessageTemplate(key, defaultMessage) - .withUntypedValue("connectable", connectable.getId()) + .withUntypedValue("identifiable", identifiable.getId()) .withSeverity(TypedValue.INFO_SEVERITY) .add(); } diff --git a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/ConnectionAndDisconnectionsTest.java b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/ConnectionAndDisconnectionsTest.java index 8c296a62014..9e42d4ae7af 100644 --- a/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/ConnectionAndDisconnectionsTest.java +++ b/iidm/iidm-modification/src/test/java/com/powsybl/iidm/modification/ConnectionAndDisconnectionsTest.java @@ -16,6 +16,7 @@ import com.powsybl.iidm.modification.topology.NamingStrategy; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.BusbarSectionPositionAdder; +import com.powsybl.iidm.network.test.HvdcTestNetwork; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -305,7 +306,7 @@ void testPlannedDisconnection() throws IOException { // Network modification PlannedDisconnection modification = new PlannedDisconnectionBuilder() - .withConnectableId("L1") + .withIdentifiableId("L1") .build(); modification.apply(network); writeXmlTest(network, "/network-planned-disconnection.xiidm"); @@ -319,7 +320,7 @@ void testPlannedDisconnectionComplete() throws IOException { // Network modification ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("reportPlannedDisconnectionComplete", "Testing reportNode for connectable disconnection").build(); PlannedDisconnection modification = new PlannedDisconnectionBuilder() - .withConnectableId("L1") + .withIdentifiableId("L1") .withFictitiousSwitchesOperable(true) .build(); modification.apply(network, reportNode); @@ -338,11 +339,20 @@ void testPlannedDisconnectionNoDisconnection() throws IOException { // Network modification ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("reportTestConnectionNoDisconnection", "Testing reportNode for connectable disconnection").build(); PlannedDisconnection modification = new PlannedDisconnectionBuilder() - .withConnectableId("L1") + .withIdentifiableId("L1") .withFictitiousSwitchesOperable(false) .build(); modification.apply(network, reportNode); writeXmlTest(network, "/network-planned-disconnection-not-disconnected.xiidm"); + + // Network modification + PlannedDisconnection modificationSide1 = new PlannedDisconnectionBuilder() + .withIdentifiableId("L1") + .withFictitiousSwitchesOperable(false) + .withSide(ThreeSides.ONE) + .build(); + modificationSide1.apply(network, reportNode); + writeXmlTest(network, "/network-planned-disconnection-not-disconnected.xiidm"); testReportNode(reportNode, "/reportNode/connectable-not-disconnected-planned.txt"); } @@ -354,7 +364,7 @@ void testUnplannedDisconnection() throws IOException { // Network modification ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("reportTestConnectionDisconnection", "Testing reportNode for connectable disconnection").build(); UnplannedDisconnection modification = new UnplannedDisconnectionBuilder() - .withConnectableId("L1") + .withIdentifiableId("L1") .withFictitiousSwitchesOperable(true) .build(); modification.apply(network, reportNode); @@ -370,11 +380,20 @@ void testUnplannedDisconnectionNoDisconnection() throws IOException { // Network modification ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("reportTestConnectionNoDisconnection", "Testing reportNode for connectable disconnection").build(); UnplannedDisconnection modification = new UnplannedDisconnectionBuilder() - .withConnectableId("L1") + .withIdentifiableId("L1") .withFictitiousSwitchesOperable(false) .build(); modification.apply(network, reportNode); writeXmlTest(network, "/network-unplanned-disconnection-not-disconnected.xiidm"); + + // Network modification + UnplannedDisconnection modificationSide1 = new UnplannedDisconnectionBuilder() + .withIdentifiableId("L1") + .withFictitiousSwitchesOperable(false) + .withSide(ThreeSides.ONE) + .build(); + modificationSide1.apply(network, reportNode); + writeXmlTest(network, "/network-unplanned-disconnection-not-disconnected.xiidm"); testReportNode(reportNode, "/reportNode/connectable-not-disconnected-unplanned.txt"); } @@ -386,7 +405,7 @@ void testConnection() throws IOException { // Network modification ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("reportTestConnection", "Testing reportNode for connectable connection").build(); ConnectableConnection modification = new ConnectableConnectionBuilder() - .withConnectableId("L2") + .withIdentifiableId("L2") .withFictitiousSwitchesOperable(true) .withOnlyBreakersOperable(false) .build(); @@ -403,21 +422,27 @@ void testConnectionNoConnection() throws IOException { // Network modification ReportNode reportNode = ReportNode.newRootReportNode().withMessageTemplate("reportTestConnectionNoConnection", "Testing reportNode for connectable connection").build(); ConnectableConnection modification = new ConnectableConnectionBuilder() - .withConnectableId("L2") + .withIdentifiableId("L2") .withFictitiousSwitchesOperable(false) .withOnlyBreakersOperable(true) .build(); modification.apply(network, reportNode); writeXmlTest(network, "/network-unplanned-disconnection-not-disconnected.xiidm"); + + // Connection on one side + ConnectableConnection modificationSide1 = new ConnectableConnectionBuilder() + .withIdentifiableId("L2") + .withFictitiousSwitchesOperable(false) + .withOnlyBreakersOperable(true) + .withSide(ThreeSides.ONE) + .build(); + modificationSide1.apply(network, reportNode); + writeXmlTest(network, "/network-unplanned-disconnection-not-disconnected.xiidm"); testReportNode(reportNode, "/reportNode/connectable-not-connected.txt"); } - - /** - * This network modification fails on tie-lines since they are not a connectable - */ @Test - void testExceptions() { + void testTieLine() { Network network = createNetwork(); // Add tie line @@ -450,34 +475,137 @@ void testExceptions() { .add(); // Disconnection - assertTrue(tieLine.getDanglingLine1().getTerminal().isConnected()); - assertTrue(tieLine.getDanglingLine2().getTerminal().isConnected()); + assertTieLineConnection(tieLine, true, true); + UnplannedDisconnection disconnection = new UnplannedDisconnectionBuilder() + .withIdentifiableId("NHV1_NHV2_1") + .withFictitiousSwitchesOperable(false) + .build(); + disconnection.apply(network); + assertTieLineConnection(tieLine, false, false); + + // Connection + ConnectableConnection connection = new ConnectableConnectionBuilder() + .withIdentifiableId("NHV1_NHV2_1") + .withFictitiousSwitchesOperable(false) + .withOnlyBreakersOperable(true) + .build(); + connection.apply(network); + assertTieLineConnection(tieLine, true, true); + + // Disconnection on one side + UnplannedDisconnection disconnectionSide1 = new UnplannedDisconnectionBuilder() + .withIdentifiableId("NHV1_NHV2_1") + .withFictitiousSwitchesOperable(false) + .withSide(ThreeSides.ONE) + .build(); + disconnectionSide1.apply(network); + assertTieLineConnection(tieLine, false, true); + + // Connection on the same side + ConnectableConnection connectionSide1 = new ConnectableConnectionBuilder() + .withIdentifiableId("NHV1_NHV2_1") + .withFictitiousSwitchesOperable(false) + .withOnlyBreakersOperable(true) + .withSide(ThreeSides.ONE) + .build(); + connectionSide1.apply(network); + assertTieLineConnection(tieLine, true, true); + } + + @Test + void testHvdcLine() { + Network network = HvdcTestNetwork.createLcc(); + HvdcLine hvdcLine = network.getHvdcLine("L"); + + // Disconnection + assertHvdcLineConnection(hvdcLine, true, true); + UnplannedDisconnection disconnection = new UnplannedDisconnectionBuilder() + .withIdentifiableId("L") + .withFictitiousSwitchesOperable(false) + .build(); + disconnection.apply(network); + assertHvdcLineConnection(hvdcLine, false, false); + + // Connection + ConnectableConnection connection = new ConnectableConnectionBuilder() + .withIdentifiableId("L") + .withFictitiousSwitchesOperable(false) + .withOnlyBreakersOperable(true) + .build(); + connection.apply(network); + assertHvdcLineConnection(hvdcLine, true, true); + + // Disconnection on one side + UnplannedDisconnection disconnectionSide2 = new UnplannedDisconnectionBuilder() + .withIdentifiableId("L") + .withFictitiousSwitchesOperable(false) + .withSide(ThreeSides.TWO) + .build(); + disconnectionSide2.apply(network); + assertHvdcLineConnection(hvdcLine, true, false); + + // Connection on the same side + ConnectableConnection connectionSide2 = new ConnectableConnectionBuilder() + .withIdentifiableId("L") + .withFictitiousSwitchesOperable(false) + .withOnlyBreakersOperable(true) + .withSide(ThreeSides.TWO) + .build(); + connectionSide2.apply(network); + assertHvdcLineConnection(hvdcLine, true, true); + } + + private void assertTieLineConnection(TieLine tieLine, boolean expectedConnectionOnSide1, boolean expectedConnectionOnSide2) { + assertEquals(expectedConnectionOnSide1, tieLine.getDanglingLine1().getTerminal().isConnected()); + assertEquals(expectedConnectionOnSide2, tieLine.getDanglingLine2().getTerminal().isConnected()); + } + + private void assertHvdcLineConnection(HvdcLine hvdcLine, boolean expectedConnectionOnSide1, boolean expectedConnectionOnSide2) { + assertEquals(expectedConnectionOnSide1, hvdcLine.getConverterStation1().getTerminal().isConnected()); + assertEquals(expectedConnectionOnSide2, hvdcLine.getConverterStation2().getTerminal().isConnected()); + } + + @Test + void testIdentifiableNotFoundException() { + Network network = createNetwork(); UnplannedDisconnection disconnection = new UnplannedDisconnectionBuilder() - .withConnectableId("NHV1_NHV2_1") + .withIdentifiableId("ELEMENT_NOT_PRESENT") .withFictitiousSwitchesOperable(false) .build(); + NamingStrategy namingStrategy = new DefaultNamingStrategy(); ComputationManager computationManager = LocalComputationManager.getDefault(); PowsyblException disconnectionException = assertThrows(PowsyblException.class, () -> disconnection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); - assertEquals("Connectable 'NHV1_NHV2_1' not found", disconnectionException.getMessage()); - disconnection.apply(network); - assertTrue(tieLine.getDanglingLine1().getTerminal().isConnected()); - assertTrue(tieLine.getDanglingLine2().getTerminal().isConnected()); + assertEquals("Identifiable 'ELEMENT_NOT_PRESENT' not found", disconnectionException.getMessage()); - // Connection - tieLine.getDanglingLine1().disconnect(); - tieLine.getDanglingLine2().disconnect(); - assertFalse(tieLine.getDanglingLine1().getTerminal().isConnected()); - assertFalse(tieLine.getDanglingLine2().getTerminal().isConnected()); ConnectableConnection connection = new ConnectableConnectionBuilder() - .withConnectableId("NHV1_NHV2_1") + .withIdentifiableId("ELEMENT_NOT_PRESENT") .withFictitiousSwitchesOperable(false) .withOnlyBreakersOperable(true) .build(); PowsyblException connectionException = assertThrows(PowsyblException.class, () -> connection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); - assertEquals("Connectable 'NHV1_NHV2_1' not found", connectionException.getMessage()); - connection.apply(network); - assertFalse(tieLine.getDanglingLine1().getTerminal().isConnected()); - assertFalse(tieLine.getDanglingLine2().getTerminal().isConnected()); + assertEquals("Identifiable 'ELEMENT_NOT_PRESENT' not found", connectionException.getMessage()); + } + + @Test + void testMethodNotImplemented() { + Network network = createNetwork(); + UnplannedDisconnection disconnection = new UnplannedDisconnectionBuilder() + .withIdentifiableId("S1") + .withFictitiousSwitchesOperable(false) + .build(); + + NamingStrategy namingStrategy = new DefaultNamingStrategy(); + ComputationManager computationManager = LocalComputationManager.getDefault(); + PowsyblException disconnectionException = assertThrows(PowsyblException.class, () -> disconnection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); + assertEquals("Disconnection not implemented for identifiable 'S1'", disconnectionException.getMessage()); + + ConnectableConnection connection = new ConnectableConnectionBuilder() + .withIdentifiableId("S1") + .withFictitiousSwitchesOperable(false) + .withOnlyBreakersOperable(true) + .build(); + PowsyblException connectionException = assertThrows(PowsyblException.class, () -> connection.apply(network, namingStrategy, true, computationManager, ReportNode.NO_OP)); + assertEquals("Connection not implemented for identifiable 'S1'", connectionException.getMessage()); } } diff --git a/iidm/iidm-modification/src/test/resources/reportNode/connectable-connected.txt b/iidm/iidm-modification/src/test/resources/reportNode/connectable-connected.txt index 49095a7a918..572b11b9a58 100644 --- a/iidm/iidm-modification/src/test/resources/reportNode/connectable-connected.txt +++ b/iidm/iidm-modification/src/test/resources/reportNode/connectable-connected.txt @@ -1,3 +1,3 @@ + Testing reportNode for connectable connection - A terminal of connectable L2 is already connected. + A terminal of identifiable L2 is already connected. Connectable L2 has been connected on each side. diff --git a/iidm/iidm-modification/src/test/resources/reportNode/connectable-disconnected-planned.txt b/iidm/iidm-modification/src/test/resources/reportNode/connectable-disconnected-planned.txt index 240298ee8b5..de5a21ee6ef 100644 --- a/iidm/iidm-modification/src/test/resources/reportNode/connectable-disconnected-planned.txt +++ b/iidm/iidm-modification/src/test/resources/reportNode/connectable-disconnected-planned.txt @@ -1,2 +1,2 @@ + Testing reportNode for connectable disconnection - Connectable L1 has been disconnected (planned disconnection) on each side. + Identifiable L1 has been disconnected (planned disconnection) on each side. diff --git a/iidm/iidm-modification/src/test/resources/reportNode/connectable-disconnected-unplanned.txt b/iidm/iidm-modification/src/test/resources/reportNode/connectable-disconnected-unplanned.txt index 76d42d2dcab..b1d9c3bcaba 100644 --- a/iidm/iidm-modification/src/test/resources/reportNode/connectable-disconnected-unplanned.txt +++ b/iidm/iidm-modification/src/test/resources/reportNode/connectable-disconnected-unplanned.txt @@ -1,2 +1,2 @@ + Testing reportNode for connectable disconnection - Connectable L1 has been disconnected (unplanned disconnection) on each side. + Identifiable L1 has been disconnected (unplanned disconnection) on each side. diff --git a/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-connected.txt b/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-connected.txt index 91e981f2803..20a437bd383 100644 --- a/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-connected.txt +++ b/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-connected.txt @@ -1,2 +1,3 @@ + Testing reportNode for connectable connection Connectable L2 has NOT been connected on each side. + Connectable L2 has NOT been connected on side 1. diff --git a/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-disconnected-planned.txt b/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-disconnected-planned.txt index 1e98560aceb..7241d56cf54 100644 --- a/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-disconnected-planned.txt +++ b/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-disconnected-planned.txt @@ -1,2 +1,3 @@ + Testing reportNode for connectable disconnection - Connectable L1 has NOT been disconnected (planned disconnection) on each side. + Identifiable L1 has NOT been disconnected (planned disconnection) on each side. + Identifiable L1 has NOT been disconnected (planned disconnection) on side 1. diff --git a/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-disconnected-unplanned.txt b/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-disconnected-unplanned.txt index 05d1aa05bc1..f5f622ccb97 100644 --- a/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-disconnected-unplanned.txt +++ b/iidm/iidm-modification/src/test/resources/reportNode/connectable-not-disconnected-unplanned.txt @@ -1,2 +1,3 @@ + Testing reportNode for connectable disconnection - Connectable L1 has NOT been disconnected (unplanned disconnection) on each side. + Identifiable L1 has NOT been disconnected (unplanned disconnection) on each side. + Identifiable L1 has NOT been disconnected (unplanned disconnection) on side 1. diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractHvdcLineTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractHvdcLineTest.java index cdf6ca02c4e..c703475efb4 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractHvdcLineTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractHvdcLineTest.java @@ -10,6 +10,7 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.HvdcTestNetwork; +import com.powsybl.iidm.network.util.SwitchPredicates; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -216,4 +217,44 @@ private void createHvdcLine(String id, String name, double r, HvdcLine.Converter .setConverterStationId2(converterStationId2) .add(); } + + @Test + void testConnectDisconnect() { + HvdcLine hvdcLine = network.getHvdcLine("L"); + + // Check that the HVDC line is connected + assertHvdcLineConnection(hvdcLine, true, true); + + // Connection fails since it's already connected + assertFalse(hvdcLine.connectConverterStations()); + + // Disconnection fails if switches cannot be opened (here, only fictional switches could be opened) + assertFalse(hvdcLine.disconnectConverterStations(SwitchPredicates.IS_NONFICTIONAL.negate().and(SwitchPredicates.IS_OPEN.negate()))); + + // Disconnection + assertTrue(hvdcLine.disconnectConverterStations()); + assertHvdcLineConnection(hvdcLine, false, false); + + // Disconnection fails since it's already disconnected + assertFalse(hvdcLine.disconnectConverterStations()); + + // Connection fails if switches cannot be opened (here, only fictional switches could be closed) + assertFalse(hvdcLine.connectConverterStations(SwitchPredicates.IS_NONFICTIONAL.negate())); + + // Connection + assertTrue(hvdcLine.connectConverterStations()); + assertHvdcLineConnection(hvdcLine, true, true); + + // Disconnect one side + assertTrue(hvdcLine.disconnectConverterStations(SwitchPredicates.IS_CLOSED_BREAKER, TwoSides.ONE)); + assertHvdcLineConnection(hvdcLine, false, true); + + // Connection on the other side fails since it's still connected + assertFalse(hvdcLine.connectConverterStations(SwitchPredicates.IS_NONFICTIONAL_BREAKER, TwoSides.TWO)); + } + + private void assertHvdcLineConnection(HvdcLine hvdcLine, boolean expectedConnectionOnSide1, boolean expectedConnectionOnSide2) { + assertEquals(expectedConnectionOnSide1, hvdcLine.getConverterStation1().getTerminal().isConnected()); + assertEquals(expectedConnectionOnSide2, hvdcLine.getConverterStation2().getTerminal().isConnected()); + } } diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTieLineTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTieLineTest.java index fb632825af8..fc3055009ee 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTieLineTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTieLineTest.java @@ -10,8 +10,10 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.iidm.network.test.FourSubstationsNodeBreakerFactory; import com.powsybl.iidm.network.test.NoEquipmentNetworkFactory; import com.powsybl.iidm.network.util.SV; +import com.powsybl.iidm.network.util.SwitchPredicates; import com.powsybl.iidm.network.util.TieLineUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -432,4 +434,117 @@ private void createTieLineWithDanglingline2ByDefault(String id, String name, Str .setDanglingLine2(dl2.getId()) .add(); } + + @Test + void testConnectDisconnect() { + Network networkWithTieLine = createNetworkWithTieLine(); + TieLine tieLine = networkWithTieLine.getTieLine("TL"); + + // Check that the tie line is connected + assertTieLineConnection(tieLine, true, true); + + // Connection fails since it's already connected + assertFalse(tieLine.connectDanglingLines()); + + // Disconnection fails if switches cannot be opened (here, only fictional switches could be opened) + assertFalse(tieLine.disconnectDanglingLines(SwitchPredicates.IS_NONFICTIONAL.negate().and(SwitchPredicates.IS_OPEN.negate()))); + + // Disconnection + assertTrue(tieLine.disconnectDanglingLines()); + assertTieLineConnection(tieLine, false, false); + + // Disconnection fails since it's already disconnected + assertFalse(tieLine.disconnectDanglingLines()); + + // Connection fails if switches cannot be opened (here, only fictional switches could be closed) + assertFalse(tieLine.connectDanglingLines(SwitchPredicates.IS_NONFICTIONAL.negate())); + + // Connection + assertTrue(tieLine.connectDanglingLines()); + assertTieLineConnection(tieLine, true, true); + + // Disconnect one side + assertTrue(tieLine.disconnectDanglingLines(SwitchPredicates.IS_CLOSED_BREAKER, TwoSides.ONE)); + assertTieLineConnection(tieLine, false, true); + + // Connection on the other side fails since it's still connected + assertFalse(tieLine.connectDanglingLines(SwitchPredicates.IS_NONFICTIONAL_BREAKER, TwoSides.TWO)); + } + + private void assertTieLineConnection(TieLine tieLine, boolean expectedConnectionOnSide1, boolean expectedConnectionOnSide2) { + assertEquals(expectedConnectionOnSide1, tieLine.getDanglingLine1().getTerminal().isConnected()); + assertEquals(expectedConnectionOnSide2, tieLine.getDanglingLine2().getTerminal().isConnected()); + } + + private Network createNetworkWithTieLine() { + // Initialize the network + Network networkWithTieLine = FourSubstationsNodeBreakerFactory.create(); + + // Existing voltage levels in Node-breaker view + VoltageLevel s1vl1 = networkWithTieLine.getVoltageLevel("S1VL1"); + + // New voltage levels in bus-breaker view + VoltageLevel s2vl2 = networkWithTieLine.getSubstation("S2").newVoltageLevel() + .setId("S2VL2") + .setNominalV(1.0) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + + // New buses + s2vl2.getBusBreakerView() + .newBus() + .setId("bus22") + .add(); + + /* + * First Tie line on node-breaker + */ + // Add a dangling line in the first Voltage level + createSwitch(s1vl1, "S1VL1_DL_DISCONNECTOR", SwitchKind.DISCONNECTOR, false, 0, 20); + createSwitch(s1vl1, "S1VL1_DL_BREAKER", SwitchKind.BREAKER, false, 20, 21); + DanglingLine danglingLine1 = s1vl1.newDanglingLine() + .setId("NHV1_XNODE1") + .setP0(0.0) + .setQ0(0.0) + .setR(1.5) + .setX(20.0) + .setG(1E-6) + .setB(386E-6 / 2) + .setNode(21) + .setPairingKey("XNODE1") + .add(); + + // Add a dangling line in the second Voltage level + DanglingLine danglingLine2 = s2vl2.newDanglingLine() + .setId("S2VL2_DL") + .setP0(0.0) + .setQ0(0.0) + .setR(1.5) + .setX(13.0) + .setG(2E-6) + .setB(386E-6 / 2) + .setBus("bus22") + .setPairingKey("XNODE1") + .add(); + networkWithTieLine.newTieLine() + .setId("TL") + .setDanglingLine1(danglingLine1.getId()) + .setDanglingLine2(danglingLine2.getId()) + .add(); + + return networkWithTieLine; + } + + private static void createSwitch(VoltageLevel vl, String id, SwitchKind kind, boolean open, int node1, int node2) { + vl.getNodeBreakerView().newSwitch() + .setId(id) + .setName(id) + .setKind(kind) + .setRetained(kind.equals(SwitchKind.BREAKER)) + .setOpen(open) + .setFictitious(false) + .setNode1(node1) + .setNode2(node2) + .add(); + } } From 70ae502b1f156c1fe2c6a1dd93f6dfe440203b14 Mon Sep 17 00:00:00 2001 From: Nicolas Rol Date: Fri, 28 Jun 2024 11:53:03 +0200 Subject: [PATCH 22/57] Fix regression on assert command for tool tests (#3088) * revert deletion of assertCommand method to avoid regression * deprecate method Signed-off-by: Nicolas Rol --- .../powsybl/tools/test/AbstractToolTest.java | 17 +++++ .../tools/test/CommandLineToolsTest.java | 75 ++++++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/tools-test/src/main/java/com/powsybl/tools/test/AbstractToolTest.java b/tools-test/src/main/java/com/powsybl/tools/test/AbstractToolTest.java index 680651c5519..6ff033accef 100644 --- a/tools-test/src/main/java/com/powsybl/tools/test/AbstractToolTest.java +++ b/tools-test/src/main/java/com/powsybl/tools/test/AbstractToolTest.java @@ -33,6 +33,7 @@ import java.nio.file.Files; import java.util.Objects; import java.util.function.BiConsumer; +import java.util.regex.Pattern; import static org.junit.jupiter.api.Assertions.*; @@ -93,6 +94,10 @@ public static void containsTxt(String expected, String actual) { assertTrue(actual.contains(expected), () -> ASSERT_MATCH_TEXT_BLOCK.formatted(expected, actual)); } + public void matchTextOrRegex(String expected, String actual) { + assertTrue(actual.equals(expected) || Pattern.compile(expected).matcher(actual).find()); + } + protected void assertCommandSuccessful(String[] args) { assertCommand(args, CommandLineTools.COMMAND_OK_STATUS, null, "", ComparisonUtils::assertTxtEquals); } @@ -117,6 +122,18 @@ protected void assertCommandErrorMatch(String[] args, String expectedErr) { assertCommand(args, CommandLineTools.EXECUTION_ERROR_STATUS, null, expectedErr, AbstractToolTest::containsTxt); } + /** + * @deprecated use {@link AbstractToolTest#assertCommandMatchTextOrRegex} instead + */ + @Deprecated(since = "6.4.0") + protected void assertCommand(String[] args, int expectedStatus, String expectedOut, String expectedErr) { + assertCommandMatchTextOrRegex(args, expectedStatus, expectedOut, expectedErr); + } + + protected void assertCommandMatchTextOrRegex(String[] args, int expectedStatus, String expectedOut, String expectedErr) { + assertCommand(args, expectedStatus, expectedOut, expectedErr, this::matchTextOrRegex); + } + private void assertCommand(String[] args, int expectedStatus, String expectedOut, String expectedErr, BiConsumer comparisonFunction) { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ByteArrayOutputStream berr = new ByteArrayOutputStream(); diff --git a/tools-test/src/test/java/com/powsybl/tools/test/CommandLineToolsTest.java b/tools-test/src/test/java/com/powsybl/tools/test/CommandLineToolsTest.java index a1cca0d839a..6b95c5da39f 100644 --- a/tools-test/src/test/java/com/powsybl/tools/test/CommandLineToolsTest.java +++ b/tools-test/src/test/java/com/powsybl/tools/test/CommandLineToolsTest.java @@ -16,8 +16,12 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; import java.util.Arrays; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @author Geoffroy Jamgotchian {@literal } @@ -114,9 +118,54 @@ public void run(CommandLine line, ToolRunningContext context) { } } + private static class Tool3 implements Tool { + + @Override + public Command getCommand() { + return new Command() { + @Override + public String getName() { + return "tool3"; + } + + @Override + public String getTheme() { + return "theme3"; + } + + @Override + public String getDescription() { + return "test tool3"; + } + + @Override + public Options getOptions() { + Options options = new Options(); + options.addOption(Option.builder() + .longOpt("option1") + .desc("this is option 1") + .hasArg() + .argName("FILE") + .build()); + return options; + } + + @Override + public String getUsageFooter() { + return "footer1"; + } + }; + } + + @Override + public void run(CommandLine line, ToolRunningContext context) { + context.getOutputStream().print(UUID.randomUUID()); + } + } + @Override protected Iterable getTools() { - return Arrays.asList(new Tool1(), new Tool2()); + return Arrays.asList(new Tool1(), new Tool2(), new Tool3()); } @Override @@ -133,6 +182,18 @@ public void assertCommand() { assertOption(command.getOptions(), "option2", false, false); } + @Test + void testRegexOutput() { + // Matches a regex + assertCommandMatchTextOrRegex(new String[] {"tool3"}, 0, "^[a-z0-9-]+$", ""); + + // Matches a regex - deprecated method + assertCommand(new String[] {"tool3"}, 0, "^[a-z0-9-]+$", ""); + + // Matches a string + assertCommandMatchTextOrRegex(new String[] {"tool1", "--option1", "file.txt"}, 0, "result1", ""); + } + @Test void test() { String scriptOptions = "Available options are:" + System.lineSeparator() + @@ -150,12 +211,15 @@ void test() { System.lineSeparator() + "theme2:" + System.lineSeparator() + " tool2 test tool2" + System.lineSeparator() + + System.lineSeparator() + + "theme3:" + System.lineSeparator() + + " tool3 test tool3" + System.lineSeparator() + System.lineSeparator(); assertCommandError(new String[] {}, CommandLineTools.COMMAND_NOT_FOUND_STATUS, usage); // usage when command does not exist - assertCommandError(new String[] {"tool3"}, CommandLineTools.COMMAND_NOT_FOUND_STATUS, usage); + assertCommandError(new String[] {"tool4"}, CommandLineTools.COMMAND_NOT_FOUND_STATUS, usage); // command success assertCommandSuccessful(new String[] {"tool1", "--option1", "file.txt"}, "result1"); @@ -202,4 +266,11 @@ void test() { "footer1" + System.lineSeparator()); } + + @Test + void testComparisonMethods() { + matchTextOrRegex("works", "works"); + matchTextOrRegex("^[a-z0-9-]+$", UUID.randomUUID().toString()); + assertThrows(AssertionFailedError.class, () -> matchTextOrRegex("^[a-z0-9-]+$", "fail test")); + } } From a59d652af227b5f9f2a1a3c1d18b3e8bb4c99981 Mon Sep 17 00:00:00 2001 From: Geoffroy Jamgotchian Date: Fri, 28 Jun 2024 13:36:38 +0200 Subject: [PATCH 23/57] Upgrade math native to 1.4.0 (#3089) Signed-off-by: Geoffroy Jamgotchian --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cba5fc7fa2b..69e086aa000 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ 1.9 1.5.6-3 - 1.3.1 + 1.4.0 From 3b70ef0a293a9df472b9a7ee30b046902098fa2a Mon Sep 17 00:00:00 2001 From: Luma Date: Fri, 28 Jun 2024 13:50:30 +0200 Subject: [PATCH 24/57] CGMES: Fix equivalent injection missing regulation target (#3087) * fix equivalent injection missing regulation target Signed-off-by: Luma --- .../EquivalentInjectionConversion.java | 7 +- .../test/EquivalentInjectionImportTest.java | 64 ++++++++++++++ .../resources/issues/ei_regulation_EQ.xml | 83 +++++++++++++++++++ .../resources/issues/ei_regulation_EQ_BD.xml | 31 +++++++ .../ei_regulation_missing_target_SSH.xml | 18 ++++ .../issues/ei_regulation_with_target_SSH.xml | 19 +++++ .../ei_regulation_with_target_zero_SSH.xml | 19 +++++ 7 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/EquivalentInjectionImportTest.java create mode 100644 cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_EQ.xml create mode 100644 cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_EQ_BD.xml create mode 100644 cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_missing_target_SSH.xml create mode 100644 cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_with_target_SSH.xml create mode 100644 cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_with_target_zero_SSH.xml diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/EquivalentInjectionConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/EquivalentInjectionConversion.java index 72c9485c5df..1b23aeec791 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/EquivalentInjectionConversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/EquivalentInjectionConversion.java @@ -143,10 +143,9 @@ private Regulation getRegulation() { regulation.targetV = Double.NaN; if (regulation.status) { regulation.targetV = p.asDouble(REGULATION_TARGET); - if (regulation.targetV == 0) { - fixed(REGULATION_TARGET, "Target voltage value can not be zero", regulation.targetV, - voltageLevel().getNominalV()); - regulation.targetV = voltageLevel().getNominalV(); + if (Double.isNaN(regulation.targetV) || regulation.targetV == 0) { + missing("Valid target voltage value (voltage regulation is considered as off)"); + regulation.status = false; } } diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/EquivalentInjectionImportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/EquivalentInjectionImportTest.java new file mode 100644 index 00000000000..40d556fe84c --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/EquivalentInjectionImportTest.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ + +package com.powsybl.cgmes.conversion.test; + +import com.powsybl.commons.datasource.ResourceDataSource; +import com.powsybl.commons.datasource.ResourceSet; +import com.powsybl.commons.test.AbstractSerDeTest; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.Network; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Luma Zamarreño {@literal } + */ + +class EquivalentInjectionImportTest extends AbstractSerDeTest { + + @Test + void equivalentInjectionWithRegulationTargetTest() { + String basename = "ei_regulation"; + Network network = Network.read(new ResourceDataSource(basename, + new ResourceSet("/issues", + "ei_regulation_EQ.xml", + "ei_regulation_with_target_SSH.xml", + "ei_regulation_EQ_BD.xml"))); + DanglingLine dl = network.getDanglingLine("ACLS1"); + assertEquals(401, dl.getGeneration().getTargetV()); + assertTrue(dl.getGeneration().isVoltageRegulationOn()); + } + + @Test + void equivalentInjectionMissingRegulationTargetTest() { + String basename = "ei_regulation"; + Network network = Network.read(new ResourceDataSource(basename, + new ResourceSet("/issues", + "ei_regulation_EQ.xml", + "ei_regulation_missing_target_SSH.xml", + "ei_regulation_EQ_BD.xml"))); + DanglingLine dl = network.getDanglingLine("ACLS1"); + // No generation data has been created for the dangling line + assertNull(dl.getGeneration()); + } + + @Test + void equivalentInjectionWithRegulationTargetZeroTest() { + String basename = "ei_regulation"; + Network network = Network.read(new ResourceDataSource(basename, + new ResourceSet("/issues", + "ei_regulation_EQ.xml", + "ei_regulation_with_target_zero_SSH.xml", + "ei_regulation_EQ_BD.xml"))); + DanglingLine dl = network.getDanglingLine("ACLS1"); + // Zero is an invalid value, no generation data has been created for the dangling line + assertNull(dl.getGeneration()); + } +} diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_EQ.xml b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_EQ.xml new file mode 100644 index 00000000000..d1fffde719f --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_EQ.xml @@ -0,0 +1,83 @@ + + + 2024-06-28T11:00:00Z + 2024-06-28T11:00:00Z + 1 + Equivalent injection missing regulation target + https://www.powsybl.org/ + http://iec.ch/TC57/ns/CIM/CoreEquipment-EU/3.0 + + + + GeographicalRegionTest + GeographicalRegionTest + + + SubGeographicalRegionTest + SubGeographicalRegionTest + + + + Substation1 + Substation1 + + + + VoltageLevel1 + VoltageLevel1 + 420 + 380 + + + + + ConnectivityNode1 + Connectivity Node 1 + + + + + ACLS1 + + ACLS1 + 0.12 + 0 + 0.3 + 0.34 + + + ACLS1_T1 + ACLS1 Terminal 1 + 1 + + + + + + ACLS1_T2 + ACLS1 Terminal 2 + 2 + + + + + + + EI1 + EI1 + + + 0 + 0 + true + + + EI1_T + EI1 Terminal + 1 + + + + + + diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_EQ_BD.xml b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_EQ_BD.xml new file mode 100644 index 00000000000..43e90eddca7 --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_EQ_BD.xml @@ -0,0 +1,31 @@ + + + + 2024-06-28T11:00:00Z + 2024-06-28T11:00:00Z + 1 + Equivalent injection missing regulation target + http://iec.ch/TC57/ns/CIM/EquipmentBoundary-EU/3.0 + http://powsybl.org + + + BaseVoltage400 + Base Voltage Level 400kV + 400.0 kV + 400.0 + + + LineContainerAtBoundary1 + LineContainerAtBoundary1 + + + ConnectivityNodeAtBoundary1 + ConnectivityNodeAtBoundary1 + + + + BoundaryPoint1 + BoundaryPoint1 + + + diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_missing_target_SSH.xml b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_missing_target_SSH.xml new file mode 100644 index 00000000000..62e70216462 --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_missing_target_SSH.xml @@ -0,0 +1,18 @@ + + + 2024-06-28T11:00:00Z + 2024-06-28T11:00:00Z + 1 + Equivalent injection missing regulation target + https://www.powsybl.org/ + http://iec.ch/TC57/ns/CIM/SteadyStateHypothesis-EU/3.0 + + + + true + -100 + 10 + true + + + diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_with_target_SSH.xml b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_with_target_SSH.xml new file mode 100644 index 00000000000..8020f178e9d --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_with_target_SSH.xml @@ -0,0 +1,19 @@ + + + 2024-06-28T11:00:00Z + 2024-06-28T11:00:00Z + 1 + Equivalent injection missing regulation target + https://www.powsybl.org/ + http://iec.ch/TC57/ns/CIM/SteadyStateHypothesis-EU/3.0 + + + + true + -100 + 10 + true + + 401 + + diff --git a/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_with_target_zero_SSH.xml b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_with_target_zero_SSH.xml new file mode 100644 index 00000000000..a65a9907701 --- /dev/null +++ b/cgmes/cgmes-conversion/src/test/resources/issues/ei_regulation_with_target_zero_SSH.xml @@ -0,0 +1,19 @@ + + + 2024-06-28T11:00:00Z + 2024-06-28T11:00:00Z + 1 + Equivalent injection missing regulation target + https://www.powsybl.org/ + http://iec.ch/TC57/ns/CIM/SteadyStateHypothesis-EU/3.0 + + + + true + -100 + 10 + true + + 0 + + From 3bfc53e45ec7f133fc69440caf98703a74ab2d41 Mon Sep 17 00:00:00 2001 From: marqueslanauja <51124986+marqueslanauja@users.noreply.github.com> Date: Mon, 1 Jul 2024 08:37:52 +0200 Subject: [PATCH 25/57] Matpower: fix pv buses (#3003) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Matpower: export dcLines * Fix unit test after macOS small diffs * Use US locale * Create a dcline if regulation is on at both converters. Define a slack for each synchronousComponent. Select as slack the bus with the highest generation Signed-off-by: José Antonio Marqués Co-authored-by: Luma --- .../matpower/converter/MatpowerExporter.java | 166 +++++++++++------- .../resources/dangling-line-generation.json | 4 +- .../test/resources/fourSubstationFactory.json | 46 ++--- 3 files changed, 124 insertions(+), 92 deletions(-) diff --git a/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerExporter.java b/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerExporter.java index 6999562b27c..8dec4edeba0 100644 --- a/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerExporter.java +++ b/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerExporter.java @@ -117,11 +117,7 @@ private static MBus.Type getType(Bus bus, Context context) { if (context.refBusId.contains(bus.getId()) || hasSlackExtension(bus)) { return MBus.Type.REF; } - for (Generator g : bus.getGenerators()) { - if (g.isVoltageRegulatorOn()) { - return MBus.Type.PV; - } - } + // PV buses will be defined at the end of the export process return MBus.Type.PQ; } @@ -139,6 +135,11 @@ static class Context { final List generatorIdsConvertedToLoad = new ArrayList<>(); final Set synchronousComponentsToBeExported = new HashSet<>(); + final Map> generatorsToBeExported = new HashMap<>(); + + private record GenRc(String id, double targetVpu, double targetP, double minP, double maxP, double targetQ, double minQ, double maxQ, + boolean isValidVoltageRegulation, boolean isRemoteRegulation, double ratedS) { + } public Context(double maxGeneratorActivePowerLimit, double maxGeneratorReactivePowerLimit) { this.maxGeneratorActivePowerLimit = maxGeneratorActivePowerLimit; @@ -735,35 +736,35 @@ private void createBranches(Network network, MatpowerModel model, Context contex createTransformerLegs(network, model, context); } - private void createDanglingLineGenerators(Network network, MatpowerModel model, Context context) { + private void findDanglingLineGenerators(Network network, Context context) { for (DanglingLine dl : network.getDanglingLines(DanglingLineFilter.UNPAIRED)) { Terminal t = dl.getTerminal(); Bus bus = t.getBusView().getBus(); if (isExported(bus, context)) { var g = dl.getGeneration(); if (g != null) { + int busNumber = context.mBusesNumbersByIds.get(dl.getId()); VoltageLevel vl = t.getVoltageLevel(); - MGen mGen = new MGen(); - mGen.setNumber(context.mBusesNumbersByIds.get(dl.getId())); - mGen.setStatus(CONNECTED_STATUS); - mGen.setRealPowerOutput(g.getTargetP()); - mGen.setReactivePowerOutput(g.getTargetQ()); - mGen.setVoltageMagnitudeSetpoint(g.isVoltageRegulationOn() ? g.getTargetV() / vl.getNominalV() : 0); - mGen.setMinimumRealPowerOutput(Math.max(g.getMinP(), -context.maxGeneratorActivePowerLimit)); - mGen.setMaximumRealPowerOutput(Math.min(g.getMaxP(), context.maxGeneratorActivePowerLimit)); - mGen.setMinimumReactivePowerOutput(Math.max(g.getReactiveLimits().getMinQ(g.getTargetP()), -context.maxGeneratorReactivePowerLimit)); - mGen.setMaximumReactivePowerOutput(Math.min(g.getReactiveLimits().getMaxQ(g.getTargetP()), context.maxGeneratorReactivePowerLimit)); - model.addGenerator(mGen); + addMgen(context, busNumber, dl.getId(), + checkAndFixTargetVpu(g.getTargetV() / vl.getNominalV()), + g.getTargetP(), + Math.max(g.getMinP(), -context.maxGeneratorActivePowerLimit), + Math.min(g.getMaxP(), context.maxGeneratorActivePowerLimit), + g.getTargetQ(), + Math.max(g.getReactiveLimits().getMinQ(g.getTargetP()), -context.maxGeneratorReactivePowerLimit), + Math.min(g.getReactiveLimits().getMaxQ(g.getTargetP()), context.maxGeneratorReactivePowerLimit), + g.isVoltageRegulationOn(), false, Double.NaN); } } } } - private void createGenerators(Network network, MatpowerModel model, Context context) { + private void findGenerators(Network network, Context context) { for (Generator g : network.getGenerators()) { Terminal t = g.getTerminal(); Bus bus = t.getBusView().getBus(); if (isExported(bus, context)) { + int busNumber = context.mBusesNumbersByIds.get(bus.getId()); String id = g.getId(); double targetP = g.getTargetP(); double targetQ = g.getTargetQ(); @@ -773,10 +774,10 @@ private void createGenerators(Network network, MatpowerModel model, Context cont double maxQ = g.getReactiveLimits().getMaxQ(g.getTargetP()); double minQ = g.getReactiveLimits().getMinQ(g.getTargetP()); Bus regulatedBus = g.getRegulatingTerminal().getBusView().getBus(); - boolean voltageRegulation = g.isVoltageRegulatorOn(); + boolean isValidVoltageRegulation = isValidVoltageRegulation(g.isVoltageRegulatorOn(), regulatedBus); + boolean isRemoteRegulation = isRemoteRegulation(bus, regulatedBus); double ratedS = g.getRatedS(); - addMgen(model, context, bus, id, targetVpu, targetP, minP, maxP, targetQ, Math.min(minQ, maxQ), Math.max(minQ, maxQ), regulatedBus, - voltageRegulation, ratedS); + addMgen(context, busNumber, id, targetVpu, targetP, minP, maxP, targetQ, Math.min(minQ, maxQ), Math.max(minQ, maxQ), isValidVoltageRegulation, isRemoteRegulation, ratedS); } } } @@ -786,11 +787,12 @@ private static double findTargetVpu(Generator generator) { return generator.getTargetV() / generator.getRegulatingTerminal().getVoltageLevel().getNominalV(); } - private void createStaticVarCompensators(Network network, MatpowerModel model, Context context) { + private void findStaticVarCompensatorGenerators(Network network, Context context) { for (StaticVarCompensator svc : network.getStaticVarCompensators()) { Terminal t = svc.getTerminal(); Bus bus = t.getBusView().getBus(); if (isExported(bus, context)) { + int busNumber = context.mBusesNumbersByIds.get(bus.getId()); String id = svc.getId(); double targetQ; if (StaticVarCompensator.RegulationMode.REACTIVE_POWER.equals(svc.getRegulationMode())) { @@ -803,9 +805,9 @@ private void createStaticVarCompensators(Network network, MatpowerModel model, C double maxQ = svc.getBmax() * vSquared; double targetVpu = checkAndFixTargetVpu(findTargetVpu(svc)); Bus regulatedBus = svc.getRegulatingTerminal().getBusView().getBus(); - boolean voltageRegulation = StaticVarCompensator.RegulationMode.VOLTAGE.equals(svc.getRegulationMode()); - addMgen(model, context, bus, id, targetVpu, 0, 0, 0, targetQ, minQ, - maxQ, regulatedBus, voltageRegulation, Double.NaN); + boolean isValidVoltageRegulation = isValidVoltageRegulation(StaticVarCompensator.RegulationMode.VOLTAGE.equals(svc.getRegulationMode()), regulatedBus); + boolean isRemoteRegulation = isRemoteRegulation(bus, regulatedBus); + addMgen(context, busNumber, id, targetVpu, 0, 0, 0, targetQ, minQ, maxQ, isValidVoltageRegulation, isRemoteRegulation, Double.NaN); } } } @@ -840,8 +842,8 @@ private static void exportVscHvdcLine(VscConverterStation rectifierVscConverterS if (isExportedAsDcLine(rectifierVscConverterStation, inverterVscConverterStation)) { createDcLine(rectifierVscConverterStation, inverterVscConverterStation, hvdcLine, model, context); } else { - createGeneratorOrLoadFromVscConverter(rectifierVscConverterStation, model, context); - createGeneratorOrLoadFromVscConverter(inverterVscConverterStation, model, context); + createGeneratorOrLoadFromVscConverter(rectifierVscConverterStation, context); + createGeneratorOrLoadFromVscConverter(inverterVscConverterStation, context); } } @@ -935,11 +937,12 @@ private static double calculateL1(double l0, double losses, double rectifierTarg return rectifierTargetP != 0.0 ? (losses - l0) / rectifierTargetP : 0.0; } - private static void createGeneratorOrLoadFromVscConverter(VscConverterStation vscConverterStation, MatpowerModel model, Context context) { + private static void createGeneratorOrLoadFromVscConverter(VscConverterStation vscConverterStation, Context context) { Terminal terminal = vscConverterStation.getTerminal(); Bus bus = findBus(terminal); if (isExported(bus, context)) { + int busNumber = context.mBusesNumbersByIds.get(bus.getId()); String id = vscConverterStation.getId(); double targetQ = checkAndFixTargetQ(vscConverterStation.getReactivePowerSetpoint()); double targetVpu = checkAndFixTargetVpu(findTargetVpu(vscConverterStation)); @@ -947,49 +950,76 @@ private static void createGeneratorOrLoadFromVscConverter(VscConverterStation vs double targetP = HvdcUtils.getConverterStationTargetP(vscConverterStation); double minQ = checkAndFixMinQ(vscConverterStation.getReactiveLimits().getMinQ(targetP)); // approximation double maxQ = checkAndFixMaxQ(vscConverterStation.getReactiveLimits().getMaxQ(targetP)); // approximation - boolean voltageRegulation = vscConverterStation.isVoltageRegulatorOn(); + boolean isValidVoltageRegulation = isValidVoltageRegulation(vscConverterStation.isVoltageRegulatorOn(), regulatedBus); double maxP = vscConverterStation.getHvdcLine().getMaxP(); - addMgen(model, context, bus, id, targetVpu, targetP, -maxP, maxP, targetQ, minQ, - maxQ, regulatedBus, voltageRegulation, Double.NaN); + boolean isRemoteRegulation = isRemoteRegulation(bus, regulatedBus); + addMgen(context, busNumber, id, targetVpu, targetP, -maxP, maxP, targetQ, minQ, maxQ, isValidVoltageRegulation, isRemoteRegulation, Double.NaN); } } - private static void addMgen(MatpowerModel model, Context context, Bus bus, - String id, double targetVpu, double targetP, double minP, double maxP, double targetQ, - double minQ, double maxQ, Bus regulatedBus, boolean voltageRegulation, double ratedS) { - int busNum = context.mBusesNumbersByIds.get(bus.getId()); - MBus mBus = model.getBusByNum(busNum); - boolean validVoltageRegulation = voltageRegulation && regulatedBus != null; - // Matpower power flow does not support bus with multiple generators that do not have the same voltage regulation - // status. if the bus has PV type, all of its generator must have a valid voltage set point. - if (!validVoltageRegulation && mBus.getType() == MBus.Type.PV) { - // convert to load - mBus.setRealPowerDemand(mBus.getRealPowerDemand() - targetP); - mBus.setReactivePowerDemand(mBus.getReactivePowerDemand() - targetQ); - context.generatorIdsConvertedToLoad.add(id); - } else { - MGen mGen = new MGen(); - mGen.setNumber(busNum); - mGen.setStatus(CONNECTED_STATUS); - mGen.setRealPowerOutput(targetP); - mGen.setReactivePowerOutput(Double.isNaN(targetQ) ? 0 : targetQ); - if (validVoltageRegulation) { - if (!regulatedBus.getId().equals(bus.getId())) { - LOGGER.warn("Generator remote voltage control not supported in Matpower model, control has been localized {}", id); - } - mGen.setVoltageMagnitudeSetpoint(targetVpu); + private static void addMgen(Context context, int busNum, String id, double targetVpu, double targetP, double minP, double maxP, + double targetQ, double minQ, double maxQ, boolean isValidVoltageRegulation, boolean isRemoteRegulation, double ratedS) { + Context.GenRc genRc = new Context.GenRc(id, targetVpu, targetP, minP, maxP, targetQ, minQ, maxQ, isValidVoltageRegulation, isRemoteRegulation, ratedS); + context.generatorsToBeExported.computeIfAbsent(busNum, k -> new ArrayList<>()).add(genRc); + } + + // Matpower power flow does not support bus with multiple generators that do not have the same voltage regulation + // status. if the bus has PV type, all of its generator must have a valid voltage set point. + private static void createGeneratorsAndDefinePVBuses(MatpowerModel model, Context context) { + context.generatorsToBeExported.keySet().stream().sorted().forEach(busNumber -> { + List genRcs = context.generatorsToBeExported.get(busNumber); + MBus mBus = model.getBusByNum(busNumber); + List genRcsWithRegulationOn = genRcs.stream().filter(genRc -> genRc.isValidVoltageRegulation).toList(); + List genRcsWithRegulationOff = genRcs.stream().filter(genRc -> !genRc.isValidVoltageRegulation).toList(); + if (genRcsWithRegulationOn.isEmpty()) { + genRcsWithRegulationOff.forEach(genRc -> { + MGen mGen = createMGen(model, busNumber, genRc, context); + // we can safely set voltage setpoint to zero, because a PQ bus never go back to PV even if reactive limits + // are activated in Matpower power flow + mGen.setVoltageMagnitudeSetpoint(0); + }); } else { - // we can safely set voltage setpoint to zero, because a PQ bus never go back to PV even if reactive limits - // are activated in Matpower power flow - mGen.setVoltageMagnitudeSetpoint(0); + if (mBus.getType().equals(MBus.Type.PQ)) { + mBus.setType(MBus.Type.PV); + } + genRcsWithRegulationOn.forEach(genRc -> createMGen(model, busNumber, genRc, context)); + + genRcsWithRegulationOff.forEach(genRc -> { + mBus.setRealPowerDemand(mBus.getRealPowerDemand() - genRc.targetP); + mBus.setReactivePowerDemand(mBus.getReactivePowerDemand() - genRc.targetQ); + context.generatorIdsConvertedToLoad.add(genRc.id); + }); } - mGen.setMinimumRealPowerOutput(Math.max(minP, -context.maxGeneratorActivePowerLimit)); - mGen.setMaximumRealPowerOutput(Math.min(maxP, context.maxGeneratorActivePowerLimit)); - mGen.setMinimumReactivePowerOutput(Math.max(minQ, -context.maxGeneratorReactivePowerLimit)); - mGen.setMaximumReactivePowerOutput(Math.min(maxQ, context.maxGeneratorReactivePowerLimit)); - mGen.setTotalMbase(Double.isNaN(ratedS) ? 0 : ratedS); - model.addGenerator(mGen); + }); + } + + private static MGen createMGen(MatpowerModel model, int busNumber, Context.GenRc genRc, Context context) { + MGen mGen = new MGen(); + mGen.setNumber(busNumber); + mGen.setStatus(CONNECTED_STATUS); + mGen.setRealPowerOutput(genRc.targetP); + mGen.setReactivePowerOutput(Double.isNaN(genRc.targetQ) ? 0 : genRc.targetQ); + mGen.setVoltageMagnitudeSetpoint(genRc.targetVpu); + + mGen.setMinimumRealPowerOutput(Math.max(genRc.minP, -context.maxGeneratorActivePowerLimit)); + mGen.setMaximumRealPowerOutput(Math.min(genRc.maxP, context.maxGeneratorActivePowerLimit)); + mGen.setMinimumReactivePowerOutput(Math.max(genRc.minQ, -context.maxGeneratorReactivePowerLimit)); + mGen.setMaximumReactivePowerOutput(Math.min(genRc.maxQ, context.maxGeneratorReactivePowerLimit)); + mGen.setTotalMbase(Double.isNaN(genRc.ratedS) ? 0 : genRc.ratedS); + model.addGenerator(mGen); + + if (genRc.isRemoteRegulation) { + LOGGER.warn("Generator remote voltage control not supported in Matpower model, control has been localized {}", genRc.id); } + return mGen; + } + + private static boolean isValidVoltageRegulation(boolean voltageRegulation, Bus regulatedBus) { + return voltageRegulation && regulatedBus != null; + } + + private static boolean isRemoteRegulation(Bus bus, Bus regulatedBus) { + return !(bus != null && regulatedBus != null && bus.getId().equals(regulatedBus.getId())); } private static double checkAndFixVoltageMagnitude(double voltageMagnitude) { @@ -1071,11 +1101,13 @@ public void export(Network network, Properties parameters, DataSource dataSource context.num = preserveBusIds(network, context); createBuses(network, model, context); createBranches(network, model, context); - createGenerators(network, model, context); - createStaticVarCompensators(network, model, context); - createDanglingLineGenerators(network, model, context); + findGenerators(network, context); + findStaticVarCompensatorGenerators(network, context); + findDanglingLineGenerators(network, context); createDcLines(network, model, context); + createGeneratorsAndDefinePVBuses(model, context); + if (!context.generatorIdsConvertedToLoad.isEmpty()) { LOGGER.debug("{} generators have been converted to a load: {}", context.generatorIdsConvertedToLoad.size(), context.generatorIdsConvertedToLoad); } diff --git a/matpower/matpower-converter/src/test/resources/dangling-line-generation.json b/matpower/matpower-converter/src/test/resources/dangling-line-generation.json index 1b3a455a95d..689745cfeda 100644 --- a/matpower/matpower-converter/src/test/resources/dangling-line-generation.json +++ b/matpower/matpower-converter/src/test/resources/dangling-line-generation.json @@ -19,7 +19,7 @@ "minimumVoltageMagnitude" : 0.8 }, { "number" : 2, - "type" : "PQ", + "type" : "PV", "name" : "DL", "realPowerDemand" : 50.0, "reactivePowerDemand" : 30.0, @@ -58,7 +58,7 @@ }, { "number" : 2, "realPowerOutput" : 440.0, - "reactivePowerOutput" : "NaN", + "reactivePowerOutput" : 0.0, "maximumReactivePowerOutput" : 46.25, "minimumReactivePowerOutput" : -54.55, "voltageMagnitudeSetpoint" : 1.01, diff --git a/matpower/matpower-converter/src/test/resources/fourSubstationFactory.json b/matpower/matpower-converter/src/test/resources/fourSubstationFactory.json index d84c7b1e8ca..d61f1db9379 100644 --- a/matpower/matpower-converter/src/test/resources/fourSubstationFactory.json +++ b/matpower/matpower-converter/src/test/resources/fourSubstationFactory.json @@ -34,7 +34,7 @@ "minimumVoltageMagnitude" : 0.975 }, { "number" : 3, - "type" : "PQ", + "type" : "PV", "name" : "S4VL1_0", "realPowerDemand" : 240.0, "reactivePowerDemand" : 10.0, @@ -71,16 +71,16 @@ "rampQ" : 0.0, "apf" : 0.0 }, { - "number" : 2, - "realPowerOutput" : 250.9944, - "reactivePowerOutput" : 71.8487, - "maximumReactivePowerOutput" : 185.0972075, - "minimumReactivePowerOutput" : -172.5943015, - "voltageMagnitudeSetpoint" : 1.0, + "number" : 1, + "realPowerOutput" : 9.780605394241391, + "reactivePowerOutput" : 120.0, + "maximumReactivePowerOutput" : 500.0, + "minimumReactivePowerOutput" : -400.0, + "voltageMagnitudeSetpoint" : 0.0, "totalMbase" : 0.0, "status" : 1, - "maximumRealPowerOutput" : 400.0, - "minimumRealPowerOutput" : 0.0, + "maximumRealPowerOutput" : 300.0, + "minimumRealPowerOutput" : -300.0, "pc1" : 0.0, "pc2" : 0.0, "qc1Min" : 0.0, @@ -93,15 +93,15 @@ "rampQ" : 0.0, "apf" : 0.0 }, { - "number" : 3, - "realPowerOutput" : 0.0, - "reactivePowerOutput" : 0.0, - "maximumReactivePowerOutput" : 8000.0, - "minimumReactivePowerOutput" : -8000.0, + "number" : 2, + "realPowerOutput" : 250.9944, + "reactivePowerOutput" : 71.8487, + "maximumReactivePowerOutput" : 185.0972075, + "minimumReactivePowerOutput" : -172.5943015, "voltageMagnitudeSetpoint" : 1.0, "totalMbase" : 0.0, "status" : 1, - "maximumRealPowerOutput" : 0.0, + "maximumRealPowerOutput" : 400.0, "minimumRealPowerOutput" : 0.0, "pc1" : 0.0, "pc2" : 0.0, @@ -115,16 +115,16 @@ "rampQ" : 0.0, "apf" : 0.0 }, { - "number" : 1, - "realPowerOutput" : 9.780605394241391, - "reactivePowerOutput" : 120.0, - "maximumReactivePowerOutput" : 500.0, - "minimumReactivePowerOutput" : -400.0, - "voltageMagnitudeSetpoint" : 0.0, + "number" : 3, + "realPowerOutput" : 0.0, + "reactivePowerOutput" : 0.0, + "maximumReactivePowerOutput" : 8000.0, + "minimumReactivePowerOutput" : -8000.0, + "voltageMagnitudeSetpoint" : 1.0, "totalMbase" : 0.0, "status" : 1, - "maximumRealPowerOutput" : 300.0, - "minimumRealPowerOutput" : -300.0, + "maximumRealPowerOutput" : 0.0, + "minimumRealPowerOutput" : 0.0, "pc1" : 0.0, "pc2" : 0.0, "qc1Min" : 0.0, From da44759eff41246daea8c5cb8420c444abc079c0 Mon Sep 17 00:00:00 2001 From: "R. Yao" <105953673+rygx@users.noreply.github.com> Date: Thu, 4 Jul 2024 06:18:44 +0000 Subject: [PATCH 26/57] Open setters for all properties in PsseTransformerWinding class (#3091) Signed-off-by: Rui Yao --- .../psse/model/pf/PsseTransformerWinding.java | 68 ++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/psse/psse-model/src/main/java/com/powsybl/psse/model/pf/PsseTransformerWinding.java b/psse/psse-model/src/main/java/com/powsybl/psse/model/pf/PsseTransformerWinding.java index b5d92a38862..f4ba479fd04 100644 --- a/psse/psse-model/src/main/java/com/powsybl/psse/model/pf/PsseTransformerWinding.java +++ b/psse/psse-model/src/main/java/com/powsybl/psse/model/pf/PsseTransformerWinding.java @@ -82,10 +82,6 @@ public double getWindv() { return windv; } - public void setWindv(double windv) { - this.windv = windv; - } - public double getNomv() { return nomv; } @@ -94,10 +90,6 @@ public double getAng() { return ang; } - public void setAng(double ang) { - this.ang = ang; - } - public int getCod() { return cod; } @@ -147,6 +139,66 @@ public double getCnxa() { return cnxa; } + public void setWindv(double windv) { + this.windv = windv; + } + + public void setNomv(double nomv) { + this.nomv = nomv; + } + + public void setAng(double ang) { + this.ang = ang; + } + + public void setCod(int cod) { + this.cod = cod; + } + + public void setCont(int cont) { + this.cont = cont; + } + + public void setNode(int node) { + this.node = node; + } + + public void setRma(double rma) { + this.rma = rma; + } + + public void setRmi(double rmi) { + this.rmi = rmi; + } + + public void setVma(double vma) { + this.vma = vma; + } + + public void setVmi(double vmi) { + this.vmi = vmi; + } + + public void setNtp(int ntp) { + this.ntp = ntp; + } + + public void setTab(int tab) { + this.tab = tab; + } + + public void setCr(double cr) { + this.cr = cr; + } + + public void setCx(double cx) { + this.cx = cx; + } + + public void setCnxa(double cnxa) { + this.cnxa = cnxa; + } + public PsseTransformerWinding copy() { PsseTransformerWinding copy = new PsseTransformerWinding(); copy.windv = this.windv; From a0a2e9a2f766d97fa4a4f3ec291c71cb8543eabc Mon Sep 17 00:00:00 2001 From: Abdelsalem <46495975+AbdelHedhili@users.noreply.github.com> Date: Tue, 9 Jul 2024 14:12:00 +0200 Subject: [PATCH 27/57] TCK: remove reference comparison for TapChangers step (#3090) Signed-off-by: Abdelsalem --- .../powsybl/iidm/network/tck/AbstractTapChangerTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTapChangerTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTapChangerTest.java index 24954126d3b..5fa05f14ca5 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTapChangerTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTapChangerTest.java @@ -97,7 +97,12 @@ public void baseTestsPhaseTapChanger() { assertEquals(1.0, neutralStep.getRho(), 0.0); phaseTapChanger.setTapPosition(0); assertEquals(0, phaseTapChanger.getTapPosition()); - assertSame(phaseTapChanger.getCurrentStep(), phaseTapChanger.getStep(0)); + assertEquals(phaseTapChanger.getCurrentStep().getR(), phaseTapChanger.getStep(0).getR(), 0.0); + assertEquals(phaseTapChanger.getCurrentStep().getX(), phaseTapChanger.getStep(0).getX(), 0.0); + assertEquals(phaseTapChanger.getCurrentStep().getG(), phaseTapChanger.getStep(0).getG(), 0.0); + assertEquals(phaseTapChanger.getCurrentStep().getB(), phaseTapChanger.getStep(0).getB(), 0.0); + assertEquals(phaseTapChanger.getCurrentStep().getAlpha(), phaseTapChanger.getStep(0).getAlpha(), 0.0); + assertEquals(phaseTapChanger.getCurrentStep().getRho(), phaseTapChanger.getStep(0).getRho(), 0.0); phaseTapChanger.setRegulationValue(5.0); assertEquals(5.0, phaseTapChanger.getRegulationValue(), 0.0); phaseTapChanger.setTargetDeadband(0.5); From 1f079a6c7eeaf29822574ca999468ae6a21bbcf1 Mon Sep 17 00:00:00 2001 From: Abdelsalem <46495975+AbdelHedhili@users.noreply.github.com> Date: Tue, 9 Jul 2024 14:49:37 +0200 Subject: [PATCH 28/57] Fix: Update TapChanger neutralPosition after using a stepsReplacer (#3094) Fix: Update TapChanger neutralPosition after using a stepsReplacer Signed-off-by: Abdelsalem --- .../iidm/network/impl/AbstractTapChanger.java | 3 +- .../network/tck/AbstractTapChangerTest.java | 165 ++++++++++++++++++ 2 files changed, 167 insertions(+), 1 deletion(-) diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChanger.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChanger.java index 00c1b65a1e8..d46a9b1a830 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChanger.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractTapChanger.java @@ -29,7 +29,7 @@ abstract class AbstractTapChanger steps; @@ -160,6 +160,7 @@ protected C setSteps(List steps) { } this.steps = steps; + this.relativeNeutralPosition = getRelativeNeutralPosition(); return (C) this; } diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTapChangerTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTapChangerTest.java index 5fa05f14ca5..29beeffd7a4 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTapChangerTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTapChangerTest.java @@ -204,6 +204,92 @@ public void testDefaultPhaseTapChangerStep() { assertEquals(1.0, step.getRho(), 0.0); } + @Test + public void testPhaseTapChangerStepsReplacer() { + PhaseTapChanger phaseTapChanger = twt.newPhaseTapChanger() + .setTapPosition(1) + .setLowTapPosition(0) + .setRegulating(true) + .setTargetDeadband(1.0) + .setRegulationMode(PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL) + .setRegulationValue(10.0) + .setRegulationTerminal(terminal) + .beginStep() + .setR(1.0) + .setX(2.0) + .setG(3.0) + .setB(4.0) + .setAlpha(0.0) + .setRho(1.0) + .endStep() + .beginStep() + .setR(1.0) + .setX(2.0) + .setG(3.0) + .setB(4.0) + .setAlpha(5.0) + .setRho(6.0) + .endStep() + .add(); + assertEquals(2, phaseTapChanger.getStepCount()); + assertEquals(2, phaseTapChanger.getAllSteps().size()); + assertEquals(0, phaseTapChanger.getLowTapPosition()); + assertEquals(1, phaseTapChanger.getHighTapPosition()); + assertEquals(0, phaseTapChanger.getNeutralPosition().orElseThrow()); + + //check neutral step attributes + PhaseTapChangerStep neutralStep = phaseTapChanger.getNeutralStep().orElseThrow(); + assertEquals(0, neutralStep.getAlpha(), 0.0); + assertEquals(1, neutralStep.getRho(), 0.0); + assertEquals(1, neutralStep.getR(), 0.0); + assertEquals(2, neutralStep.getX(), 0.0); + assertEquals(3, neutralStep.getG(), 0.0); + assertEquals(4, neutralStep.getB(), 0.0); + + //replace steps + phaseTapChanger.stepsReplacer() + .beginStep() + .setR(1.0) + .setX(2.0) + .setG(3.0) + .setB(4.0) + .setAlpha(5.0) + .setRho(6.0) + .endStep() + .beginStep() + .setR(5.0) + .setX(6.0) + .setG(7.0) + .setB(8.0) + .setAlpha(6.0) + .setRho(7.0) + .endStep() + .beginStep() + .setR(9.0) + .setX(10.0) + .setG(11.0) + .setB(12.0) + .setAlpha(0.0) + .setRho(1.0) + .endStep() + .replaceSteps(); + + assertEquals(3, phaseTapChanger.getStepCount()); + assertEquals(3, phaseTapChanger.getAllSteps().size()); + assertEquals(0, phaseTapChanger.getLowTapPosition()); + assertEquals(2, phaseTapChanger.getHighTapPosition()); + assertEquals(2, phaseTapChanger.getNeutralPosition().orElseThrow()); + + //check neutral step attributes + neutralStep = phaseTapChanger.getNeutralStep().orElseThrow(); + assertEquals(0, neutralStep.getAlpha(), 0.0); + assertEquals(1, neutralStep.getRho(), 0.0); + assertEquals(9, neutralStep.getR(), 0.0); + assertEquals(10, neutralStep.getX(), 0.0); + assertEquals(11, neutralStep.getG(), 0.0); + assertEquals(12, neutralStep.getB(), 0.0); + } + @Test public void invalidTapPositionPhase() { ValidationException e = assertThrows(ValidationException.class, () -> createPhaseTapChangerWith2Steps(3, 0, false, @@ -521,6 +607,85 @@ public void testDefaultRatioTapChangerStep() { assertEquals(0.0, step.getB(), 0.0); } + @Test + public void testRatioTapChangerStepsReplacer() { + RatioTapChanger ratioTapChanger = twt.newRatioTapChanger() + .setTapPosition(1) + .setLowTapPosition(0) + .setRegulating(true) + .setTargetDeadband(1.0) + .setRegulationMode(RatioTapChanger.RegulationMode.REACTIVE_POWER) + .setRegulationValue(10.0) + .setRegulationTerminal(terminal) + .beginStep() + .setR(1.0) + .setX(2.0) + .setG(3.0) + .setB(4.0) + .setRho(1.0) + .endStep() + .beginStep() + .setR(1.0) + .setX(2.0) + .setG(3.0) + .setB(4.0) + .setRho(6.0) + .endStep() + .add(); + assertEquals(2, ratioTapChanger.getStepCount()); + assertEquals(2, ratioTapChanger.getAllSteps().size()); + assertEquals(0, ratioTapChanger.getLowTapPosition()); + assertEquals(1, ratioTapChanger.getHighTapPosition()); + assertEquals(0, ratioTapChanger.getNeutralPosition().orElseThrow()); + + //check neutral step attributes + RatioTapChangerStep neutralStep = ratioTapChanger.getNeutralStep().orElseThrow(); + assertEquals(1, neutralStep.getRho()); + assertEquals(1, neutralStep.getR()); + assertEquals(2, neutralStep.getX()); + assertEquals(3, neutralStep.getG()); + assertEquals(4, neutralStep.getB()); + + //replace steps + ratioTapChanger.stepsReplacer() + .beginStep() + .setR(1.0) + .setX(2.0) + .setG(3.0) + .setB(4.0) + .setRho(6.0) + .endStep() + .beginStep() + .setR(5.0) + .setX(6.0) + .setG(7.0) + .setB(8.0) + .setRho(7.0) + .endStep() + .beginStep() + .setR(9.0) + .setX(10.0) + .setG(11.0) + .setB(12.0) + .setRho(1.0) + .endStep() + .replaceSteps(); + + assertEquals(3, ratioTapChanger.getStepCount()); + assertEquals(3, ratioTapChanger.getAllSteps().size()); + assertEquals(0, ratioTapChanger.getLowTapPosition()); + assertEquals(2, ratioTapChanger.getHighTapPosition()); + assertEquals(2, ratioTapChanger.getNeutralPosition().orElseThrow()); + + //check neutral step attributes + neutralStep = ratioTapChanger.getNeutralStep().orElseThrow(); + assertEquals(1, neutralStep.getRho()); + assertEquals(9, neutralStep.getR()); + assertEquals(10, neutralStep.getX()); + assertEquals(11, neutralStep.getG()); + assertEquals(12, neutralStep.getB()); + } + @Test public void invalidRatioTapChangerWithoutSteps() { ValidationException e = assertThrows(ValidationException.class, () -> twt.newRatioTapChanger() From d1b926955833542391bac4bd2cf28aa67faa8745 Mon Sep 17 00:00:00 2001 From: Anne Tilloy <48123713+annetill@users.noreply.github.com> Date: Wed, 10 Jul 2024 15:40:02 +0200 Subject: [PATCH 29/57] Make method to compute hvdc line losses public (#3098) * Fix access to hvdc line losses. Signed-off-by: Anne Tilloy --- .../src/main/java/com/powsybl/iidm/network/util/HvdcUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/HvdcUtils.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/HvdcUtils.java index 0bc5677b583..4b464295f0c 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/HvdcUtils.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/HvdcUtils.java @@ -77,7 +77,7 @@ private static double getAbsoluteValuePAc(HvdcConverterStation station) { } } - private static double getHvdcLineLosses(double rectifierPDc, double nominalV, double r) { + public static double getHvdcLineLosses(double rectifierPDc, double nominalV, double r) { // This method computes the losses due to the HVDC line. // The active power value on rectifier DC side is known as the HVDC active power set point minus the losses related // to AC/DC conversion (rectifier conversion), the voltage is approximated to the nominal voltage as attribute of the HVDC line. From 3075deac3dab9d651ae38bf2d02827ea3ab0a96d Mon Sep 17 00:00:00 2001 From: Luma Date: Thu, 11 Jul 2024 15:56:46 +0200 Subject: [PATCH 30/57] CGMES export: report multiple model identifiers (#3093) * CGMES export report model identifiers * value of constant in message for easy reading * add cgmes subset and network id to the report nodes with exported cgmes identifiers Signed-off-by: Luma --- .../conversion/export/CgmesExportUtil.java | 14 +++- .../export/CommonGridModelExportTest.java | 67 ++++++++++++++++++- .../powsybl/commons/report/TypedValue.java | 3 + 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java index 7507fc001f5..351444b7672 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java @@ -18,6 +18,7 @@ import com.powsybl.cgmes.model.CgmesNames; import com.powsybl.cgmes.model.CgmesSubset; import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.report.TypedValue; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.LoadDetail; import org.slf4j.Logger; @@ -47,6 +48,11 @@ public final class CgmesExportUtil { private CgmesExportUtil() { } + public static final String REPORT_NODE_KEY_EXPORTED_CGMES_ID = "ExportedCgmesId"; + public static final String REPORT_VALUE_EXPORTED_CGMES_ID = "cgmesId"; + public static final String REPORT_VALUE_EXPORTED_CGMES_SUBSET = "cgmesSubset"; + public static final String REPORT_VALUE_EXPORTED_CGMES_NETWORK_ID = "networkId"; + // Avoid trailing zeros and format always using US locale private static final DecimalFormatSymbols DOUBLE_FORMAT_SYMBOLS = new DecimalFormatSymbols(Locale.US); @@ -137,7 +143,13 @@ public static void writeModelDescription(Network network, CgmesSubset subset, XM } writer.writeStartElement(MD_NAMESPACE, "FullModel"); writer.writeAttribute(RDF_NAMESPACE, CgmesNames.ABOUT, modelDescription.getId()); - context.getReportNode().newReportNode().withMessageTemplate("CgmesId", modelDescription.getId()).add(); + // Report the exported CGMES model identifiers + context.getReportNode().newReportNode() + .withMessageTemplate(REPORT_NODE_KEY_EXPORTED_CGMES_ID, "CGMES exported model identifier: ${cgmesId} for subset ${cgmesSubset} of network ${networkId}") + .withTypedValue(REPORT_VALUE_EXPORTED_CGMES_ID, modelDescription.getId(), TypedValue.URN_UUID) + .withTypedValue(REPORT_VALUE_EXPORTED_CGMES_SUBSET, subset.getIdentifier(), TypedValue.CGMES_SUBSET) + .withTypedValue(REPORT_VALUE_EXPORTED_CGMES_NETWORK_ID, network.getId(), TypedValue.ID) + .add(); writer.writeStartElement(MD_NAMESPACE, CgmesNames.SCENARIO_TIME); writer.writeCharacters(DATE_TIME_FORMATTER.format(context.getScenarioTime())); writer.writeEndElement(); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java index f1c577d282f..d0899ac7db5 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java @@ -9,15 +9,18 @@ import com.powsybl.cgmes.conformity.CgmesConformity1Catalog; import com.powsybl.cgmes.conversion.CgmesExport; +import com.powsybl.cgmes.conversion.export.CgmesExportUtil; import com.powsybl.cgmes.extensions.CgmesMetadataModels; import com.powsybl.cgmes.extensions.CgmesMetadataModelsAdder; import com.powsybl.cgmes.model.CgmesMetadataModel; +import com.powsybl.cgmes.model.CgmesNamespace; import com.powsybl.cgmes.model.CgmesSubset; import com.powsybl.commons.PowsyblException; import com.powsybl.commons.datasource.DataSource; import com.powsybl.commons.datasource.FileDataSource; import com.powsybl.commons.datasource.MemDataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; +import com.powsybl.commons.report.ReportNode; import com.powsybl.commons.test.AbstractSerDeTest; import com.powsybl.iidm.network.*; import org.junit.jupiter.api.Test; @@ -36,6 +39,7 @@ import java.util.regex.Pattern; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * Summary from CGM Building Process Implementation Guide: @@ -420,8 +424,20 @@ void testFaraoUseCase() { exportParams.put(CgmesExport.UPDATE_DEPENDENCIES, true); exportParams.put(CgmesExport.MODELING_AUTHORITY_SET, "MAS1"); + // Export using a reporter to gather the exported model identifiers + ReportNode report = ReportNode + .newRootReportNode() + .withMessageTemplate("rootKey", "") + .build(); + MemDataSource memDataSource = new MemDataSource(); - cgmNetwork.write("CGMES", exportParams, memDataSource); + cgmNetwork.write(new ExportersServiceLoader(), "CGMES", exportParams, memDataSource, report); + + // Check the output + Set exportedModelIdsFromFiles = new HashSet<>(); + Map exportedModelId2Subset = new HashMap<>(); + Map exportedModelId2NetworkId = new HashMap<>(); + String cgmesId; for (Map.Entry entry : TSO_BY_COUNTRY.entrySet()) { Country country = entry.getKey(); // For this unit test we do not need the TSO from the entry value @@ -439,11 +455,34 @@ void testFaraoUseCase() { // To know the exported version we should read what has been written in the output files int sshVersionInOutput = readVersion(memDataSource, filenameFromCgmesExport).orElseThrow(); assertEquals(expectedOutputVersion, sshVersionInOutput); + cgmesId = readModelId(memDataSource, filenameFromCgmesExport); + exportedModelIdsFromFiles.add(cgmesId); + exportedModelId2Subset.put(cgmesId, CgmesSubset.STEADY_STATE_HYPOTHESIS.getIdentifier()); + exportedModelId2NetworkId.put(cgmesId, nc.getId()); } String filenameFromCgmesExport = cgmNetwork.getNameOrId() + "_SV.xml"; // We have to read it from inside the SV file ... int svVersionInOutput = readVersion(memDataSource, filenameFromCgmesExport).orElseThrow(); assertEquals(expectedOutputVersion, svVersionInOutput); + cgmesId = readModelId(memDataSource, filenameFromCgmesExport); + exportedModelIdsFromFiles.add(cgmesId); + exportedModelId2Subset.put(cgmesId, CgmesSubset.STATE_VARIABLES.getIdentifier()); + exportedModelId2NetworkId.put(cgmesId, cgmNetwork.getId()); + + // Obtain exported model identifiers from reporter + Set exportedModelIdsFromReporter = new HashSet<>(); + for (ReportNode n : report.getChildren()) { + if (CgmesExportUtil.REPORT_NODE_KEY_EXPORTED_CGMES_ID.equals(n.getMessageKey())) { + cgmesId = n.getValue(CgmesExportUtil.REPORT_VALUE_EXPORTED_CGMES_ID).orElseThrow().toString(); + exportedModelIdsFromReporter.add(cgmesId); + String subset = n.getValue(CgmesExportUtil.REPORT_VALUE_EXPORTED_CGMES_SUBSET).orElseThrow().toString(); + String networkId = n.getValue(CgmesExportUtil.REPORT_VALUE_EXPORTED_CGMES_NETWORK_ID).orElseThrow().toString(); + assertEquals(exportedModelId2Subset.get(cgmesId), subset); + assertEquals(exportedModelId2NetworkId.get(cgmesId), networkId); + } + } + assertFalse(exportedModelIdsFromReporter.isEmpty()); + assertEquals(exportedModelIdsFromReporter, exportedModelIdsFromFiles); } @Test @@ -552,6 +591,32 @@ private static Optional readVersion(InputStream is) { return Optional.empty(); } + private static String readModelId(ReadOnlyDataSource ds, String filename) { + try { + return readId(ds.newInputStream(filename)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static String readId(InputStream is) { + try { + XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(is); + while (reader.hasNext()) { + int next = reader.next(); + if (next == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("FullModel")) { + String id = reader.getAttributeValue(CgmesNamespace.RDF_NAMESPACE, "about"); + reader.close(); + return id; + } + } + reader.close(); + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } + return null; + } + private Network bareNetwork2Subnetworks() { Network network1 = Network .create("Network_BE", "test") diff --git a/commons/src/main/java/com/powsybl/commons/report/TypedValue.java b/commons/src/main/java/com/powsybl/commons/report/TypedValue.java index 65131ba97fb..ca170dc8b82 100644 --- a/commons/src/main/java/com/powsybl/commons/report/TypedValue.java +++ b/commons/src/main/java/com/powsybl/commons/report/TypedValue.java @@ -33,6 +33,9 @@ public class TypedValue { public static final String VOLTAGE_LEVEL = "VOLTAGE_LEVEL"; public static final String FILENAME = "FILENAME"; public static final String TIMESTAMP = "TIMESTAMP"; + public static final String URN_UUID = "URN_UUID"; + public static final String CGMES_SUBSET = "CGMES_SUBSET"; + public static final String ID = "ID"; public static final TypedValue TRACE_SEVERITY = new TypedValue("TRACE", TypedValue.SEVERITY); public static final TypedValue DEBUG_SEVERITY = new TypedValue("DEBUG", TypedValue.SEVERITY); From e7439880396db4fa6a139aed4c425d9758cad823 Mon Sep 17 00:00:00 2001 From: Luma Date: Thu, 11 Jul 2024 16:11:08 +0200 Subject: [PATCH 31/57] CGMES metadata models: allow clear of supersedes (#3099) * cgmes metadata models, allow clear of supersedes Signed-off-by: Luma --- .../main/java/com/powsybl/cgmes/conversion/CgmesExport.java | 2 ++ .../conversion/test/export/CommonGridModelExportTest.java | 3 +++ .../java/com/powsybl/cgmes/model/CgmesMetadataModel.java | 6 ++++++ .../com/powsybl/cgmes/model/CgmesMetadataModelImpl.java | 6 ++++++ 4 files changed, 17 insertions(+) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java index 99280462817..d64e9d0650f 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java @@ -235,7 +235,9 @@ public static CgmesMetadataModel initializeModelForExport( */ private void updateDependenciesCGM(Collection igmModels, CgmesMetadataModel updatedCgmSvModel) { // Each updated SSH model supersedes the original one + // Clear previous dependencies igmModels.forEach(m -> m.updatedSsh.clearDependencies()); + igmModels.forEach(m -> m.updatedSsh.clearSupersedes()); igmModels.forEach(m -> m.updatedSsh.addSupersedes(m.originalSsh.getId())); // Updated SV model depends on updated SSH models and original TP models diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java index d0899ac7db5..38dbae74623 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java @@ -509,6 +509,7 @@ void testFaraoUseCaseManualExport() throws IOException { String country = n.getSubstations().iterator().next().getCountry().orElseThrow().toString(); CgmesMetadataModel sshModel = n.getExtension(CgmesMetadataModels.class).getModelForSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS).orElseThrow(); sshModel.clearDependencies() + .clearSupersedes() .addDependentOn("myDependency") .addSupersedes("mySupersede") .setVersion(exportedVersion) @@ -643,6 +644,7 @@ private void addModelsForSubnetworks(Network network, int version) { .setVersion(version) .setModelingAuthoritySet("http://elia.be/CGMES/2.4.15") .addDependentOn("BE EQ model ID") + .addSupersedes("BE SSH previous ID") .addProfile("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1") .add() .add(); @@ -654,6 +656,7 @@ private void addModelsForSubnetworks(Network network, int version) { .setVersion(version) .setModelingAuthoritySet("http://tennet.nl/CGMES/2.4.15") .addDependentOn("NL EQ model ID") + .addSupersedes("NL SSH previous ID") .addProfile("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1") .add() .add(); diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesMetadataModel.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesMetadataModel.java index bc944a25b7f..e59f03b110e 100644 --- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesMetadataModel.java +++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesMetadataModel.java @@ -166,4 +166,10 @@ public interface CgmesMetadataModel { * @return The model with an empty set of values this model depends on. */ CgmesMetadataModelImpl clearDependencies(); + + /** + * Remove all the model ids this model supersedes. + * @return The model with an empty set of values this model supersedes. + */ + CgmesMetadataModelImpl clearSupersedes(); } diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesMetadataModelImpl.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesMetadataModelImpl.java index f260b25cc9b..51db13015eb 100644 --- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesMetadataModelImpl.java +++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesMetadataModelImpl.java @@ -141,6 +141,12 @@ public CgmesMetadataModelImpl clearDependencies() { return this; } + @Override + public CgmesMetadataModelImpl clearSupersedes() { + this.supersedes.clear(); + return this; + } + private static void addIfNonEmpty(String id, Collection ids) { if (id != null && !id.isEmpty()) { ids.add(id); From cc4cfa99ad96db3a32aafd90bddddf1dee06d5d4 Mon Sep 17 00:00:00 2001 From: jeandemanged Date: Thu, 11 Jul 2024 16:44:38 +0200 Subject: [PATCH 32/57] Rename Area acInterchangeTarget to interchangeTarget (#3095) Signed-off-by: Damien Jeandemange --- docs/grid_model/network_subnetwork.md | 13 ++-- .../java/com/powsybl/iidm/network/Area.java | 18 ++--- .../com/powsybl/iidm/network/AreaAdder.java | 4 +- .../iidm/network/impl/AreaAdderImpl.java | 10 +-- .../powsybl/iidm/network/impl/AreaImpl.java | 30 ++++---- .../com/powsybl/iidm/serde/AreaSerDe.java | 12 ++-- .../src/main/resources/xsd/iidm_V1_13.xsd | 2 +- .../resources/xsd/iidm_equipment_V1_13.xsd | 2 +- .../com/powsybl/iidm/serde/AreaSerDeTest.java | 2 +- .../test/resources/V1_13/areaRoundTripRef.xml | 2 +- .../iidm/network/tck/AbstractAreaTest.java | 70 +++++++++---------- .../network/tck/AbstractMergeNetworkTest.java | 10 +-- .../test/EurostagTutorialExample1Factory.java | 4 +- 13 files changed, 89 insertions(+), 90 deletions(-) diff --git a/docs/grid_model/network_subnetwork.md b/docs/grid_model/network_subnetwork.md index 1295082713a..c28a691b299 100644 --- a/docs/grid_model/network_subnetwork.md +++ b/docs/grid_model/network_subnetwork.md @@ -118,18 +118,17 @@ for DC part only (considering only DC boundaries) and in total (AC+DC). Note that if the Area has no boundary explicitly defined, the interchange is considered 0 MW. For area types that are meant to be used for area interchange control, e.g. in Load Flow simulations, the interchange target of the area can be specified as an input for the simulation. -Note that this target interchange is for only the AC part of the interchange. All area interchange values use the load sign convention: positive values indicate that the area is importing, negative values that the area is exporting. **Characteristics of an Area** -| Attribute | Unit | Description | -|-----------------------|------|----------------------------------------------------------------| -| $AreaType$ | | To specify the type of Area (eg. ControlArea, BiddingZone ...) | -| $AcInterchangeTarget$ | MW | Target AC active power interchange | -| $VoltageLevels$ | | List of voltage levels of the area | -| $AreaBoundaries$ | | List of area boundaries of the area | +| Attribute | Unit | Description | +|---------------------|------|----------------------------------------------------------------| +| $AreaType$ | | To specify the type of Area (eg. ControlArea, BiddingZone ...) | +| $interchangeTarget$ | MW | Target active power interchange | +| $VoltageLevels$ | | List of voltage levels of the area | +| $AreaBoundaries$ | | List of area boundaries of the area | **Characteristics of an AreaBoundary** diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Area.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Area.java index b08d9e6e363..ababf54f7c4 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Area.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Area.java @@ -58,12 +58,12 @@ * The type of Area. For instance: "ControlArea", "BiddingZone", "Country"... * * - * AcInterchangeTarget + * interchangeTarget * Double * - * no * - - * The optional target AC Interchange of this area in MW, using load sign convention (negative is export, positive is import) + * The optional target interchange of this area in MW, using load sign convention (negative is export, positive is import) * * * @@ -153,20 +153,20 @@ default IdentifiableType getType() { } /** - * Get the optional target AC Interchange of this area in MW, in load sign convention (negative is export, positive is import). + * Get the optional target interchange of this area in MW, in load sign convention (negative is export, positive is import). *

Depends on the working variant.

* - * @return the AC Interchange target (MW) + * @return the interchange target (MW) */ - OptionalDouble getAcInterchangeTarget(); + OptionalDouble getInterchangeTarget(); /** - * Set the target AC Interchange of this area in MW, in load sign convention (negative is export, positive is import). + * Set the target interchange of this area in MW, in load sign convention (negative is export, positive is import). * Providing Double.NaN removes the target. *

Depends on the working variant.

- * @param acInterchangeTarget new AC interchange target (MW) + * @param interchangeTarget new interchange target (MW) */ - Area setAcInterchangeTarget(double acInterchangeTarget); + Area setInterchangeTarget(double interchangeTarget); /** * Get the current AC Interchange of this area in MW, in load sign convention (negative is export, positive is import) @@ -184,7 +184,7 @@ default IdentifiableType getType() { * Get the current total (AC+DC) Interchange of this area in MW, in load sign convention (negative is export, positive is import) * @return the total position (MW, 0 MW if no boundary) */ - double getTotalInterchange(); + double getInterchange(); void remove(); diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaAdder.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaAdder.java index 316355c72fa..26cfb9d32f1 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaAdder.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/AreaAdder.java @@ -33,9 +33,9 @@ public interface AreaAdder extends IdentifiableAdder { AreaAdder setAreaType(String areaType); /** - * Set the target AC Interchange of this area in MW, in load sign convention (negative is export, positive is import). + * Set the target interchange of this area in MW, in load sign convention (negative is export, positive is import). */ - AreaAdder setAcInterchangeTarget(double acInterchangeTarget); + AreaAdder setInterchangeTarget(double interchangeTarget); /** * add a VoltageLevel to the Area diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaAdderImpl.java index b2df4429e1b..df9fae512d8 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaAdderImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaAdderImpl.java @@ -24,7 +24,7 @@ public class AreaAdderImpl extends AbstractIdentifiableAdder impl private String areaType; - private double acInterchangeTarget; + private double interchangeTarget; private final Set voltageLevels; @@ -38,7 +38,7 @@ public class AreaAdderImpl extends AbstractIdentifiableAdder impl this.terminalAreaBoundaries = new HashMap<>(); this.boundaryAreaBoundaries = new HashMap<>(); this.voltageLevels = new HashSet<>(); - this.acInterchangeTarget = Double.NaN; + this.interchangeTarget = Double.NaN; } public NetworkImpl getNetwork() { @@ -50,8 +50,8 @@ public AreaAdder setAreaType(String areaType) { return this; } - public AreaAdder setAcInterchangeTarget(double acInterchangeTarget) { - this.acInterchangeTarget = acInterchangeTarget; + public AreaAdder setInterchangeTarget(double interchangeTarget) { + this.interchangeTarget = interchangeTarget; return this; } @@ -77,7 +77,7 @@ protected Set getVoltageLevels() { @Override public Area add() { String id = checkAndGetUniqueId(); - AreaImpl area = new AreaImpl(networkRef, subnetworkRef, id, getName(), isFictitious(), areaType, acInterchangeTarget); + AreaImpl area = new AreaImpl(networkRef, subnetworkRef, id, getName(), isFictitious(), areaType, interchangeTarget); terminalAreaBoundaries.forEach((terminal, ac) -> area.newAreaBoundary().setTerminal(terminal).setAc(ac).add()); boundaryAreaBoundaries.forEach((boundary, ac) -> area.newAreaBoundary().setBoundary(boundary).setAc(ac).add()); voltageLevels.forEach(area::addVoltageLevel); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java index fd3edc96eaa..89d18793155 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AreaImpl.java @@ -32,7 +32,7 @@ public class AreaImpl extends AbstractIdentifiable implements Area { // attributes depending on the variant - private final TDoubleArrayList acInterchangeTarget; + private final TDoubleArrayList interchangeTarget; private final class AreaListener extends DefaultNetworkListener { @Override @@ -61,7 +61,7 @@ void moveListener(NetworkImpl fromNetwork, NetworkImpl toNetwork) { private final NetworkListener areaListener; AreaImpl(Ref ref, Ref subnetworkRef, String id, String name, boolean fictitious, String areaType, - double acInterchangeTarget) { + double interchangeTarget) { super(id, name, fictitious); this.networkRef = Objects.requireNonNull(ref); this.subnetworkRef = subnetworkRef; @@ -70,9 +70,9 @@ void moveListener(NetworkImpl fromNetwork, NetworkImpl toNetwork) { this.areaBoundaries = new ArrayList<>(); int variantArraySize = networkRef.get().getVariantManager().getVariantArraySize(); - this.acInterchangeTarget = new TDoubleArrayList(variantArraySize); + this.interchangeTarget = new TDoubleArrayList(variantArraySize); for (int i = 0; i < variantArraySize; i++) { - this.acInterchangeTarget.add(acInterchangeTarget); + this.interchangeTarget.add(interchangeTarget); } this.areaListener = new AreaListener(); getNetwork().addListener(this.areaListener); @@ -114,9 +114,9 @@ public Stream getVoltageLevelStream() { } @Override - public OptionalDouble getAcInterchangeTarget() { - throwIfRemoved("AC interchange target"); - double target = acInterchangeTarget.get(getNetwork().getVariantIndex()); + public OptionalDouble getInterchangeTarget() { + throwIfRemoved("interchange target"); + double target = interchangeTarget.get(getNetwork().getVariantIndex()); if (Double.isNaN(target)) { return OptionalDouble.empty(); } @@ -124,12 +124,12 @@ public OptionalDouble getAcInterchangeTarget() { } @Override - public Area setAcInterchangeTarget(double acInterchangeTarget) { + public Area setInterchangeTarget(double interchangeTarget) { NetworkImpl n = getNetwork(); int variantIndex = n.getVariantIndex(); - double oldValue = this.acInterchangeTarget.set(variantIndex, acInterchangeTarget); + double oldValue = this.interchangeTarget.set(variantIndex, interchangeTarget); String variantId = n.getVariantManager().getVariantId(variantIndex); - notifyUpdate("acInterchangeTarget", variantId, oldValue, acInterchangeTarget); + notifyUpdate("interchangeTarget", variantId, oldValue, interchangeTarget); return this; } @@ -146,7 +146,7 @@ public double getDcInterchange() { } @Override - public double getTotalInterchange() { + public double getInterchange() { throwIfRemoved("total interchange"); return getInterchange(areaBoundary -> true); } @@ -266,16 +266,16 @@ protected void notifyUpdate(String attribute, String variantId, Object oldValue, @Override public void extendVariantArraySize(int initVariantArraySize, int number, int sourceIndex) { super.extendVariantArraySize(initVariantArraySize, number, sourceIndex); - acInterchangeTarget.ensureCapacity(acInterchangeTarget.size() + number); + interchangeTarget.ensureCapacity(interchangeTarget.size() + number); for (int i = 0; i < number; i++) { - acInterchangeTarget.add(acInterchangeTarget.get(sourceIndex)); + interchangeTarget.add(interchangeTarget.get(sourceIndex)); } } @Override public void reduceVariantArraySize(int number) { super.reduceVariantArraySize(number); - acInterchangeTarget.remove(acInterchangeTarget.size() - number, number); + interchangeTarget.remove(interchangeTarget.size() - number, number); } @Override @@ -288,7 +288,7 @@ public void deleteVariantArrayElement(int index) { public void allocateVariantArrayElement(int[] indexes, int sourceIndex) { super.allocateVariantArrayElement(indexes, sourceIndex); for (int index : indexes) { - acInterchangeTarget.set(index, acInterchangeTarget.get(sourceIndex)); + interchangeTarget.set(index, interchangeTarget.get(sourceIndex)); } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaSerDe.java index 092a09bd8f9..03ff128cc4d 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AreaSerDe.java @@ -30,11 +30,11 @@ protected String getRootElementName() { @Override protected void writeRootElementAttributes(final Area area, final Network parent, final NetworkSerializerContext context) { context.getWriter().writeStringAttribute("areaType", context.getAnonymizer().anonymizeString(area.getAreaType())); - Double acInterchangeTarget = null; - if (area.getAcInterchangeTarget().isPresent()) { - acInterchangeTarget = area.getAcInterchangeTarget().getAsDouble(); + Double interchangeTarget = null; + if (area.getInterchangeTarget().isPresent()) { + interchangeTarget = area.getInterchangeTarget().getAsDouble(); } - context.getWriter().writeOptionalDoubleAttribute("acInterchangeTarget", acInterchangeTarget); + context.getWriter().writeOptionalDoubleAttribute("interchangeTarget", interchangeTarget); } @Override @@ -58,8 +58,8 @@ protected AreaAdder createAdder(final Network network) { protected Area readRootElementAttributes(final AreaAdder adder, final Network parent, final NetworkDeserializerContext context) { String areaType = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("areaType")); adder.setAreaType(areaType); - OptionalDouble acInterchangeTarget = context.getReader().readOptionalDoubleAttribute("acInterchangeTarget"); - acInterchangeTarget.ifPresent(adder::setAcInterchangeTarget); + OptionalDouble interchangeTarget = context.getReader().readOptionalDoubleAttribute("interchangeTarget"); + interchangeTarget.ifPresent(adder::setInterchangeTarget); return adder.add(); } diff --git a/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd b/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd index 1fd74826fea..7f05e3d027d 100644 --- a/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd +++ b/iidm/iidm-serde/src/main/resources/xsd/iidm_V1_13.xsd @@ -722,7 +722,7 @@ - + diff --git a/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd b/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd index 910e3cbee44..f6c25b626ab 100644 --- a/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd +++ b/iidm/iidm-serde/src/main/resources/xsd/iidm_equipment_V1_13.xsd @@ -722,7 +722,7 @@ - + diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AreaSerDeTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AreaSerDeTest.java index 100bd2515d9..10226c9db80 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AreaSerDeTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AreaSerDeTest.java @@ -56,7 +56,7 @@ private static Network createBaseNetworkWithAreas() { network.newArea().setAreaType(biddingZoneType).setId("BidZoneId1").setName("BidZoneName1").addAreaBoundary(load1.getTerminal(), true) .addAreaBoundary(danglingLine.getBoundary(), false).add(); network.newArea().setAreaType(biddingZoneType).setId("BidZoneId2").setName("BidZoneName2").addAreaBoundary(line.getTerminal1(), true) - .setAcInterchangeTarget(100.).add(); + .setInterchangeTarget(100.).add(); network.newArea().setAreaType(controlAreaType).setId("ControlAreaId1").setName("ControlAreaName1").add(); vl1.addArea(network.getArea("BidZoneId1")); vl1.addArea(network.getArea("ControlAreaId1")); diff --git a/iidm/iidm-serde/src/test/resources/V1_13/areaRoundTripRef.xml b/iidm/iidm-serde/src/test/resources/V1_13/areaRoundTripRef.xml index 43513dcc01a..a3ab220c837 100644 --- a/iidm/iidm-serde/src/test/resources/V1_13/areaRoundTripRef.xml +++ b/iidm/iidm-serde/src/test/resources/V1_13/areaRoundTripRef.xml @@ -29,7 +29,7 @@ - + diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java index 59cbec5f610..a7e49645f72 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractAreaTest.java @@ -69,32 +69,32 @@ public void areaAttributes() { assertEquals("ControlArea_A", controlAreaA.getId()); assertEquals("Control Area A", controlAreaA.getOptionalName().orElseThrow()); assertEquals(CONTROL_AREA_TYPE, controlAreaA.getAreaType()); - assertEquals(-602.6, controlAreaA.getAcInterchangeTarget().orElseThrow()); + assertEquals(-602.6, controlAreaA.getInterchangeTarget().orElseThrow()); assertEquals(Set.of(vlgen, vlhv1), controlAreaA.getVoltageLevels()); assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); assertEquals("ControlArea_B", controlAreaB.getId()); assertEquals("Control Area B", controlAreaB.getOptionalName().orElseThrow()); assertEquals(CONTROL_AREA_TYPE, controlAreaB.getAreaType()); - assertEquals(+602.6, controlAreaB.getAcInterchangeTarget().orElseThrow()); + assertEquals(+602.6, controlAreaB.getInterchangeTarget().orElseThrow()); assertEquals(Set.of(vlhv2, vlload), controlAreaB.getVoltageLevels()); assertEquals(2, controlAreaB.getAreaBoundaryStream().count()); assertEquals("Region_AB", regionAB.getId()); assertEquals("Region AB", regionAB.getOptionalName().orElseThrow()); assertEquals(REGION_AREA_TYPE, regionAB.getAreaType()); - assertFalse(regionAB.getAcInterchangeTarget().isPresent()); + assertFalse(regionAB.getInterchangeTarget().isPresent()); assertEquals(Set.of(vlgen, vlhv1, vlhv2, vlload), regionAB.getVoltageLevels()); assertEquals(0, regionAB.getAreaBoundaryStream().count()); } @Test public void testSetterGetter() { - controlAreaA.setAcInterchangeTarget(123.0); - assertTrue(controlAreaA.getAcInterchangeTarget().isPresent()); - assertEquals(123.0, controlAreaA.getAcInterchangeTarget().getAsDouble()); - controlAreaA.setAcInterchangeTarget(Double.NaN); - assertTrue(controlAreaA.getAcInterchangeTarget().isEmpty()); + controlAreaA.setInterchangeTarget(123.0); + assertTrue(controlAreaA.getInterchangeTarget().isPresent()); + assertEquals(123.0, controlAreaA.getInterchangeTarget().getAsDouble()); + controlAreaA.setInterchangeTarget(Double.NaN); + assertTrue(controlAreaA.getInterchangeTarget().isEmpty()); } @Test @@ -105,32 +105,32 @@ public void testChangesNotification() { network.addListener(mockedListener); // Get initial values - double oldValue = controlAreaA.getAcInterchangeTarget().orElseThrow(); + double oldValue = controlAreaA.getInterchangeTarget().orElseThrow(); // Change values - controlAreaA.setAcInterchangeTarget(Double.NaN); + controlAreaA.setInterchangeTarget(Double.NaN); // Check update notification Mockito.verify(mockedListener, Mockito.times(1)) - .onUpdate(controlAreaA, "acInterchangeTarget", VariantManagerConstants.INITIAL_VARIANT_ID, oldValue, Double.NaN); + .onUpdate(controlAreaA, "interchangeTarget", VariantManagerConstants.INITIAL_VARIANT_ID, oldValue, Double.NaN); // Change values - controlAreaA.setAcInterchangeTarget(123.4); + controlAreaA.setInterchangeTarget(123.4); // Check update notification Mockito.verify(mockedListener, Mockito.times(1)) - .onUpdate(controlAreaA, "acInterchangeTarget", VariantManagerConstants.INITIAL_VARIANT_ID, Double.NaN, 123.4); + .onUpdate(controlAreaA, "interchangeTarget", VariantManagerConstants.INITIAL_VARIANT_ID, Double.NaN, 123.4); // After this point, no more changes are taken into account. // Simulate exception for onUpdate calls Mockito.doThrow(new PowsyblException()).when(mockedListener) - .onUpdate(controlAreaA, "acInterchangeTarget", VariantManagerConstants.INITIAL_VARIANT_ID, oldValue, 123.4); + .onUpdate(controlAreaA, "interchangeTarget", VariantManagerConstants.INITIAL_VARIANT_ID, oldValue, 123.4); // Case when same value is set - controlAreaA.setAcInterchangeTarget(123.4); + controlAreaA.setInterchangeTarget(123.4); // Case when no listener is registered network.removeListener(mockedListener); - controlAreaA.setAcInterchangeTarget(456.7); + controlAreaA.setInterchangeTarget(456.7); // Check no notification Mockito.verifyNoMoreInteractions(mockedListener); @@ -150,11 +150,11 @@ public void testSetterGetterInMultiVariants() { variantManager.setWorkingVariant("s4"); // check values cloned by extend - assertEquals(-602.6, controlAreaA.getAcInterchangeTarget().orElseThrow(), 0.0); + assertEquals(-602.6, controlAreaA.getInterchangeTarget().orElseThrow(), 0.0); // change values in s4 - controlAreaA.setAcInterchangeTarget(123.0); + controlAreaA.setInterchangeTarget(123.0); // Check P0 & Q0 update notification - Mockito.verify(mockedListener, Mockito.times(1)).onUpdate(controlAreaA, "acInterchangeTarget", "s4", -602.6, 123.0); + Mockito.verify(mockedListener, Mockito.times(1)).onUpdate(controlAreaA, "interchangeTarget", "s4", -602.6, 123.0); // remove s2 variantManager.removeVariant("s2"); @@ -162,16 +162,16 @@ public void testSetterGetterInMultiVariants() { variantManager.cloneVariant("s4", "s2b"); variantManager.setWorkingVariant("s2b"); // check values cloned by allocate - assertEquals(123.0, controlAreaA.getAcInterchangeTarget().orElseThrow(), 0.0); + assertEquals(123.0, controlAreaA.getInterchangeTarget().orElseThrow(), 0.0); // recheck initial variant value variantManager.setWorkingVariant(VariantManagerConstants.INITIAL_VARIANT_ID); - assertEquals(-602.6, controlAreaA.getAcInterchangeTarget().orElseThrow(), 0.0); + assertEquals(-602.6, controlAreaA.getInterchangeTarget().orElseThrow(), 0.0); // remove working variant s4 variantManager.setWorkingVariant("s4"); variantManager.removeVariant("s4"); try { - controlAreaA.getAcInterchangeTarget(); + controlAreaA.getInterchangeTarget(); fail(); } catch (Exception ignored) { // ignore @@ -198,22 +198,22 @@ public void testGetAreaBoundary() { public void areaInterchangeComputation() { assertEquals(-602.94, controlAreaA.getAcInterchange(), DELTA); assertEquals(0.0, controlAreaA.getDcInterchange()); - assertEquals(-602.94, controlAreaA.getTotalInterchange(), DELTA); + assertEquals(-602.94, controlAreaA.getInterchange(), DELTA); assertEquals(+602.94, controlAreaB.getAcInterchange(), DELTA); assertEquals(0.0, controlAreaB.getDcInterchange()); - assertEquals(+602.94, controlAreaB.getTotalInterchange(), DELTA); + assertEquals(+602.94, controlAreaB.getInterchange(), DELTA); // no boundaries defined assertEquals(0.0, regionAB.getAcInterchange()); assertEquals(0.0, regionAB.getDcInterchange()); - assertEquals(0.0, regionAB.getTotalInterchange()); + assertEquals(0.0, regionAB.getInterchange()); // verify NaN do not mess up the calculation dlXnode1A.getTerminal().setP(Double.NaN); assertEquals(-301.47, controlAreaA.getAcInterchange(), DELTA); assertEquals(0.0, controlAreaA.getDcInterchange()); - assertEquals(-301.47, controlAreaA.getTotalInterchange(), DELTA); + assertEquals(-301.47, controlAreaA.getInterchange(), DELTA); } @Test @@ -287,17 +287,17 @@ public void testWithTerminals() { assertEquals(-604.89, controlAreaA.getAcInterchange(), DELTA); assertEquals(0.0, controlAreaA.getDcInterchange()); - assertEquals(-604.89, controlAreaA.getTotalInterchange(), DELTA); + assertEquals(-604.89, controlAreaA.getInterchange(), DELTA); assertEquals(+604.89, controlAreaB.getAcInterchange(), DELTA); assertEquals(0.0, controlAreaB.getDcInterchange()); - assertEquals(+604.89, controlAreaB.getTotalInterchange(), DELTA); + assertEquals(+604.89, controlAreaB.getInterchange(), DELTA); // verify NaN do not mess up the calculation ngenNhv1.getTerminal2().setP(Double.NaN); assertEquals(0.0, controlAreaA.getAcInterchange()); assertEquals(0.0, controlAreaA.getDcInterchange()); - assertEquals(0.0, controlAreaA.getTotalInterchange()); + assertEquals(0.0, controlAreaA.getInterchange()); // test removing Terminal boundaries controlAreaB @@ -317,7 +317,7 @@ public void testAddSameBoundary() { assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); assertEquals(-602.94, controlAreaA.getAcInterchange(), DELTA); assertEquals(0.0, controlAreaA.getDcInterchange()); - assertEquals(-602.94, controlAreaA.getTotalInterchange(), DELTA); + assertEquals(-602.94, controlAreaA.getInterchange(), DELTA); // change them to DC controlAreaA @@ -326,7 +326,7 @@ public void testAddSameBoundary() { assertEquals(2, controlAreaA.getAreaBoundaryStream().count()); assertEquals(0.0, controlAreaA.getAcInterchange()); assertEquals(-602.94, controlAreaA.getDcInterchange(), DELTA); - assertEquals(-602.94, controlAreaA.getTotalInterchange(), DELTA); + assertEquals(-602.94, controlAreaA.getInterchange(), DELTA); } @Test @@ -342,7 +342,7 @@ public void testWithDc() { dlXnode2A.setP0(310.0); assertEquals(-290.0, controlAreaA.getAcInterchange(), DELTA); assertEquals(-310.0, controlAreaA.getDcInterchange()); - assertEquals(-600.0, controlAreaA.getTotalInterchange(), DELTA); + assertEquals(-600.0, controlAreaA.getInterchange(), DELTA); } @Test @@ -505,13 +505,13 @@ public void removeArea() { Throwable e1 = assertThrows(PowsyblException.class, controlAreaA::getAreaType); assertEquals("Cannot access area type of removed area ControlArea_A", e1.getMessage()); - Throwable e2 = assertThrows(PowsyblException.class, controlAreaA::getAcInterchangeTarget); - assertEquals("Cannot access AC interchange target of removed area ControlArea_A", e2.getMessage()); + Throwable e2 = assertThrows(PowsyblException.class, controlAreaA::getInterchangeTarget); + assertEquals("Cannot access interchange target of removed area ControlArea_A", e2.getMessage()); Throwable e3 = assertThrows(PowsyblException.class, controlAreaA::getAcInterchange); assertEquals("Cannot access AC interchange of removed area ControlArea_A", e3.getMessage()); Throwable e4 = assertThrows(PowsyblException.class, controlAreaA::getDcInterchange); assertEquals("Cannot access DC interchange of removed area ControlArea_A", e4.getMessage()); - Throwable e5 = assertThrows(PowsyblException.class, controlAreaA::getTotalInterchange); + Throwable e5 = assertThrows(PowsyblException.class, controlAreaA::getInterchange); assertEquals("Cannot access total interchange of removed area ControlArea_A", e5.getMessage()); Throwable e6 = assertThrows(PowsyblException.class, controlAreaA::getVoltageLevels); assertEquals("Cannot access voltage levels of removed area ControlArea_A", e6.getMessage()); diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMergeNetworkTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMergeNetworkTest.java index c590f3297dc..45602bcee92 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMergeNetworkTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMergeNetworkTest.java @@ -417,7 +417,7 @@ public void failMergeContainingSubnetworks() { public void testMergeAndDetachWithDistinctAreas() { addCommonSubstationsAndVoltageLevels(); Area n1TsoA = addArea(n1, "tsoA", "tso"); - Area n2BzB = n2.newArea().setId("bzB").setName("bzB_Name").setAreaType("bz").setFictitious(true).setAcInterchangeTarget(100.).add(); + Area n2BzB = n2.newArea().setId("bzB").setName("bzB_Name").setAreaType("bz").setFictitious(true).setInterchangeTarget(100.).add(); n1TsoA.addVoltageLevel(n1.getVoltageLevel("vl1")); n2BzB.addVoltageLevel(n2.getVoltageLevel("vl2")); @@ -428,8 +428,8 @@ public void testMergeAndDetachWithDistinctAreas() { final Area mergedBzB = merged.getArea("bzB"); assertNotNull(mergedBzB); assertTrue(mergedBzB.isFictitious()); - assertTrue(mergedBzB.getAcInterchangeTarget().isPresent()); - assertEquals(100., mergedBzB.getAcInterchangeTarget().getAsDouble()); + assertTrue(mergedBzB.getInterchangeTarget().isPresent()); + assertEquals(100., mergedBzB.getInterchangeTarget().getAsDouble()); // Detach n1, and check its content Network n1Detached = merged.getSubnetwork(n1.getId()).detach(); @@ -446,8 +446,8 @@ public void testMergeAndDetachWithDistinctAreas() { final Area detachedBzB = n2Detached.getArea("bzB"); assertNotNull(detachedBzB); assertTrue(detachedBzB.isFictitious()); - assertTrue(detachedBzB.getAcInterchangeTarget().isPresent()); - assertEquals(100., detachedBzB.getAcInterchangeTarget().getAsDouble()); + assertTrue(detachedBzB.getInterchangeTarget().isPresent()); + assertEquals(100., detachedBzB.getInterchangeTarget().getAsDouble()); } @Test diff --git a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java index b10ca53ebfa..0bef269be0b 100644 --- a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java +++ b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java @@ -898,7 +898,7 @@ public static Network createWithTieLinesAndAreas(NetworkFactory networkFactory) .setId("ControlArea_A") .setName("Control Area A") .setAreaType("ControlArea") - .setAcInterchangeTarget(-602.6) + .setInterchangeTarget(-602.6) .addVoltageLevel(network.getVoltageLevel(VLGEN)) .addVoltageLevel(network.getVoltageLevel(VLHV1)) .addAreaBoundary(network.getDanglingLine(DANGLING_LINE_XNODE1_1).getBoundary(), true) @@ -908,7 +908,7 @@ public static Network createWithTieLinesAndAreas(NetworkFactory networkFactory) .setId("ControlArea_B") .setName("Control Area B") .setAreaType("ControlArea") - .setAcInterchangeTarget(+602.6) + .setInterchangeTarget(+602.6) .addVoltageLevel(network.getVoltageLevel(VLHV2)) .addVoltageLevel(network.getVoltageLevel(VLLOAD)) .addAreaBoundary(network.getDanglingLine(DANGLING_LINE_XNODE1_2).getBoundary(), true) From 18ca45536ca5cfe61334596c2bb1a3095cb8a65e Mon Sep 17 00:00:00 2001 From: Coline Piloquet <55250145+colinepiloquet@users.noreply.github.com> Date: Thu, 18 Jul 2024 13:11:11 +0200 Subject: [PATCH 33/57] Documentation: add special SVGs for dark mode (#3104) Signed-off-by: Coline PILOQUET --- .../three-winding-transformer-model.svg | 3 + .../two-winding-transformer-model.svg | 3 + ...vg => three-winding-transformer-model.svg} | 0 ....svg => two-winding-transformer-model.svg} | 0 docs/grid_exchange_formats/psse/import.md | 6 +- docs/grid_model/additional.md | 9 +- ...ample1.svg => current-limits-example1.svg} | 0 ...ample2.svg => current-limits-example2.svg} | 0 .../{currentLimits.svg => current-limits.svg} | 0 .../{danglingLine.svg => dangling-line.svg} | 0 .../img/dark_mode/current-limits-example1.svg | 73 ++ .../img/dark_mode/current-limits-example2.svg | 78 +++ .../img/dark_mode/current-limits.svg | 75 +++ .../img/dark_mode/dangling-line.svg | 306 +++++++++ .../dark_mode/generator-sign-convention.svg | 99 +++ docs/grid_model/img/dark_mode/line-model.svg | 150 +++++ .../three-winding-transformer-model.svg | 635 ++++++++++++++++++ .../two-winding-transformer-model.svg | 183 +++++ .../img/dark_mode/voltage-level.svg | 613 +++++++++++++++++ ...vg => three-winding-transformer-model.svg} | 0 ....svg => two-winding-transformer-model.svg} | 0 docs/grid_model/network_subnetwork.md | 24 +- 22 files changed, 2243 insertions(+), 14 deletions(-) create mode 100644 docs/grid_exchange_formats/psse/img/dark_mode/three-winding-transformer-model.svg create mode 100644 docs/grid_exchange_formats/psse/img/dark_mode/two-winding-transformer-model.svg rename docs/grid_exchange_formats/psse/img/{three-windings-transformer-model.svg => three-winding-transformer-model.svg} (100%) rename docs/grid_exchange_formats/psse/img/{two-windings-transformer-model.svg => two-winding-transformer-model.svg} (100%) rename docs/grid_model/img/{currentLimitsExample1.svg => current-limits-example1.svg} (100%) rename docs/grid_model/img/{currentLimitsExample2.svg => current-limits-example2.svg} (100%) rename docs/grid_model/img/{currentLimits.svg => current-limits.svg} (100%) rename docs/grid_model/img/{danglingLine.svg => dangling-line.svg} (100%) create mode 100644 docs/grid_model/img/dark_mode/current-limits-example1.svg create mode 100644 docs/grid_model/img/dark_mode/current-limits-example2.svg create mode 100644 docs/grid_model/img/dark_mode/current-limits.svg create mode 100644 docs/grid_model/img/dark_mode/dangling-line.svg create mode 100644 docs/grid_model/img/dark_mode/generator-sign-convention.svg create mode 100644 docs/grid_model/img/dark_mode/line-model.svg create mode 100644 docs/grid_model/img/dark_mode/three-winding-transformer-model.svg create mode 100644 docs/grid_model/img/dark_mode/two-winding-transformer-model.svg create mode 100644 docs/grid_model/img/dark_mode/voltage-level.svg rename docs/grid_model/img/{three-windings-transformer-model.svg => three-winding-transformer-model.svg} (100%) rename docs/grid_model/img/{two-windings-transformer-model.svg => two-winding-transformer-model.svg} (100%) diff --git a/docs/grid_exchange_formats/psse/img/dark_mode/three-winding-transformer-model.svg b/docs/grid_exchange_formats/psse/img/dark_mode/three-winding-transformer-model.svg new file mode 100644 index 00000000000..dc10fb93710 --- /dev/null +++ b/docs/grid_exchange_formats/psse/img/dark_mode/three-winding-transformer-model.svg @@ -0,0 +1,3 @@ + + +
PSSE Three winding transformer model
PSSE Three winding transformer model
PowSyBl Three winding transformer model
PowSyBl Three winding transformer model
Star Bus
Star Bus
Star Bus
Star Bus
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/grid_exchange_formats/psse/img/dark_mode/two-winding-transformer-model.svg b/docs/grid_exchange_formats/psse/img/dark_mode/two-winding-transformer-model.svg new file mode 100644 index 00000000000..fef1631b4ec --- /dev/null +++ b/docs/grid_exchange_formats/psse/img/dark_mode/two-winding-transformer-model.svg @@ -0,0 +1,3 @@ + + +
PSSE Two winding transformer model
PSSE Two winding transformer model
PowSyBl Two winding transformer model
PowSyBl Two winding transformer model
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/grid_exchange_formats/psse/img/three-windings-transformer-model.svg b/docs/grid_exchange_formats/psse/img/three-winding-transformer-model.svg similarity index 100% rename from docs/grid_exchange_formats/psse/img/three-windings-transformer-model.svg rename to docs/grid_exchange_formats/psse/img/three-winding-transformer-model.svg diff --git a/docs/grid_exchange_formats/psse/img/two-windings-transformer-model.svg b/docs/grid_exchange_formats/psse/img/two-winding-transformer-model.svg similarity index 100% rename from docs/grid_exchange_formats/psse/img/two-windings-transformer-model.svg rename to docs/grid_exchange_formats/psse/img/two-winding-transformer-model.svg diff --git a/docs/grid_exchange_formats/psse/import.md b/docs/grid_exchange_formats/psse/import.md index 7ec3d13a963..aaa8ca1c675 100644 --- a/docs/grid_exchange_formats/psse/import.md +++ b/docs/grid_exchange_formats/psse/import.md @@ -165,7 +165,8 @@ The transformer is connected at both ends if the branch status (field `STAT` in In PSS®E the transformer model allows to define a ratio and angle at the end `1` and only a fixed ratio at the end `2`. The transformer magnetizing admittance is modeled between the bus and the ratio of the end `1`. The PowSyBl grid model supports a ratioTapChanger and a phaseTapChanger at the end `1` and the magnetizing admittance is between the ratio and the transmission impedance. -![TwoWindingsTransformerModels](img/two-windings-transformer-model.svg){width="100%" align=center} +![TwoWindingsTransformerModels](img/two-winding-transformer-model.svg){width="100%" align=center class="only-light"} +![TwoWindingsTransformerModels](img/dark_mode/two-winding-transformer-model.svg){width="100%" align=center class="only-dark"} To express the PSS®E electric attributes of the transformer in the PowSyBl grid model the following conversions are performed: @@ -218,7 +219,8 @@ When a three windings transformer is modeled the two windings transformer steps - Each winding can have a complex ratio and a `ratioTapChanger` or `phaseTapChanger` with its corresponding control, always at end `1`. The current PowSyBl version only supports one enabled control by three windings transformer so if there is more than one enabled only the first (winding `1`, winding `2`, winding `3`) is kept enabled, the rest are automatically disabled. - In three windings transformers the status attribute (field `STAT` in the _Transformer Data_ record) could be `0` that means all the windings disconnected, `1` for all windings connected, `2` for only the second winding disconnected, `3` for the third winding disconnected and `4` for the first winding disconnected. -![ThreeWindingsTransformerModels](img/three-windings-transformer-model.svg){width="100%" align=center} +![ThreeWindingsTransformerModels](img/three-winding-transformer-model.svg){width="100%" align=center class="only-light"} +![ThreeWindingsTransformerModels](img/dark_mode/three-winding-transformer-model.svg){width="100%" align=center class="only-dark"} ### Slack bus diff --git a/docs/grid_model/additional.md b/docs/grid_model/additional.md index 1b06270a317..448c3dd24b7 100644 --- a/docs/grid_model/additional.md +++ b/docs/grid_model/additional.md @@ -85,7 +85,8 @@ The component on which the current limits are applied can safely remain between the preceding limit (it could be another temporary limit or a permanent limit) and this limit for a duration up to the acceptable duration. Please look at this scheme to fully understand the modelling (the following example shows current limits but this modelling is valid for all loading limits): -![Loading limits model](img/currentLimits.svg){width="50%" align=center} +![Loading limits model](img/current-limits.svg){width="50%" align=center class="only-light"} +![Loading limits model](img/dark_mode/current-limits.svg){width="50%" align=center class="only-dark"} Note that, following this modelling, in general the last temporary limit (the higher one in value) should be infinite with an acceptable duration different from zero, except for tripping current modeling where the last temporary limit is infinite with an acceptable duration equal to zero. If temporary limits are modeled, the permanent limit becomes mandatory. @@ -118,7 +119,8 @@ CurrentLimits currentLimits = network.getDanglingLine("DL").newCurrentLimits() .endTemporaryLimit() .add(); ``` -![Current limits scheme_example1](img/currentLimitsExample1.svg) +![Current limits scheme_example1](img/current-limits-example1.svg){align=center class="only-light"} +![Current limits scheme_example1](img/dark_mode/current-limits-example1.svg){align=center class="only-dark"} ##### Second example This second example creates a `CurrentLimits` instance containing one permanent limit and three temporary limits, one of them having an infinite limit value. @@ -142,7 +144,8 @@ CurrentLimits currentLimits = network.getDanglingLine("DL").newCurrentLimits() .endTemporaryLimit() .add(); ``` -![Current limits scheme_example2](img/currentLimitsExample2.svg) +![Current limits scheme_example2](img/current-limits-example2.svg){align=center class="only-light"} +![Current limits scheme_example2](img/dark_mode/current-limits-example2.svg){align=center class="only-dark"} (phase-tap-changer)= ## Phase tap changer diff --git a/docs/grid_model/img/currentLimitsExample1.svg b/docs/grid_model/img/current-limits-example1.svg similarity index 100% rename from docs/grid_model/img/currentLimitsExample1.svg rename to docs/grid_model/img/current-limits-example1.svg diff --git a/docs/grid_model/img/currentLimitsExample2.svg b/docs/grid_model/img/current-limits-example2.svg similarity index 100% rename from docs/grid_model/img/currentLimitsExample2.svg rename to docs/grid_model/img/current-limits-example2.svg diff --git a/docs/grid_model/img/currentLimits.svg b/docs/grid_model/img/current-limits.svg similarity index 100% rename from docs/grid_model/img/currentLimits.svg rename to docs/grid_model/img/current-limits.svg diff --git a/docs/grid_model/img/danglingLine.svg b/docs/grid_model/img/dangling-line.svg similarity index 100% rename from docs/grid_model/img/danglingLine.svg rename to docs/grid_model/img/dangling-line.svg diff --git a/docs/grid_model/img/dark_mode/current-limits-example1.svg b/docs/grid_model/img/dark_mode/current-limits-example1.svg new file mode 100644 index 00000000000..f3f35506bab --- /dev/null +++ b/docs/grid_model/img/dark_mode/current-limits-example1.svg @@ -0,0 +1,73 @@ + + + + + + branch current (A) + + + + permanent limit + acceptable permanently + 100 + + temporary limit "TL1" + 120 + acceptable 1200 s + + temporary limit "TL2" + 140 + acceptable 600 s + + + diff --git a/docs/grid_model/img/dark_mode/current-limits-example2.svg b/docs/grid_model/img/dark_mode/current-limits-example2.svg new file mode 100644 index 00000000000..2d9927f2ec1 --- /dev/null +++ b/docs/grid_model/img/dark_mode/current-limits-example2.svg @@ -0,0 +1,78 @@ + + + + + + + + branch current (A) + + + + permanent limit + acceptable permanently + 700 + + temporary limit "IT20" + acceptable 1200 s + 800 + + temporary limit "IT10" + acceptable 600 s + 900 + + acceptable 60 s + + diff --git a/docs/grid_model/img/dark_mode/current-limits.svg b/docs/grid_model/img/dark_mode/current-limits.svg new file mode 100644 index 00000000000..00887e8ba24 --- /dev/null +++ b/docs/grid_model/img/dark_mode/current-limits.svg @@ -0,0 +1,75 @@ + + + + + + + + branch current (A) + + + + permanent limit + acceptable permanently + + temporary limit x s + acceptable x s + + temporary limit y s + acceptable y s + + temporary limit z s + acceptable z s + + + diff --git a/docs/grid_model/img/dark_mode/dangling-line.svg b/docs/grid_model/img/dark_mode/dangling-line.svg new file mode 100644 index 00000000000..38ba482d497 --- /dev/null +++ b/docs/grid_model/img/dark_mode/dangling-line.svg @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/grid_model/img/dark_mode/generator-sign-convention.svg b/docs/grid_model/img/dark_mode/generator-sign-convention.svg new file mode 100644 index 00000000000..9394839a430 --- /dev/null +++ b/docs/grid_model/img/dark_mode/generator-sign-convention.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + CB-1 + + + + + T-1 + + + + + + + + 45.0 Mw + + + + -15.0 Mvar + + + + + B-1 + + + + + + G-1 + + + + + + + + + + CB-2 + + + + +  -45.0  Mw       + + + +   15.0 Mvar   + + + + + + + Terminal active/reactive power flow + + + + + + +   45.0  Mw       Target + P + + + + +  -15.0 Mvar    Target Q + + + + + + Terminal active/reactive power flow + + + + \ No newline at end of file diff --git a/docs/grid_model/img/dark_mode/line-model.svg b/docs/grid_model/img/dark_mode/line-model.svg new file mode 100644 index 00000000000..a18e576da2f --- /dev/null +++ b/docs/grid_model/img/dark_mode/line-model.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/grid_model/img/dark_mode/three-winding-transformer-model.svg b/docs/grid_model/img/dark_mode/three-winding-transformer-model.svg new file mode 100644 index 00000000000..89ffcd6b056 --- /dev/null +++ b/docs/grid_model/img/dark_mode/three-winding-transformer-model.svg @@ -0,0 +1,635 @@ + +image/svg+xml3ρ3ejα3r3+jx32r2+jx2g1+jb1ρ2ejα2g2+jb2g3+jb31ρ1ejα1r1+jx1 diff --git a/docs/grid_model/img/dark_mode/two-winding-transformer-model.svg b/docs/grid_model/img/dark_mode/two-winding-transformer-model.svg new file mode 100644 index 00000000000..2033c12be94 --- /dev/null +++ b/docs/grid_model/img/dark_mode/two-winding-transformer-model.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/grid_model/img/dark_mode/voltage-level.svg b/docs/grid_model/img/dark_mode/voltage-level.svg new file mode 100644 index 00000000000..904f506db7e --- /dev/null +++ b/docs/grid_model/img/dark_mode/voltage-level.svg @@ -0,0 +1,613 @@ + + + + + + + + + + + G-1 + + + + + + + G-2 + + + + + + + + + CB-4 + + + + + + + + + + + G-3 + + + + + + + + + CB-5 + + + + + + Node/Breaker + + + + + + + + + G-1 + + + + + + + G-2 + + + + + + + + + CB-4 + + + + + + + + + + + G-3 + + + + + + + + + CB-5 + + + + + + Physical Network + + + + + + + + D-1 + + + + + + + + D-2 + + + + + + + + + + + + + CB-1 + + + + + + + + + + + D-5 + + + + + + + + D-6 + + + + + + + + + CB-2 + + + + + + B-1 + + + + + + B-2 + + + + + + + + + + + CB-3 + + + + + + + + D-3 + + + + + + + + + + + + D-4 + + + + + + + + D-7 + + + + + + + + D-8 + + + + + + + + D-1 + + + + + + + + D-2 + + + + + + + + + + + + + CB-1 + + + + + + + + + + + D-5 + + + + + + + + D-6 + + + + + + + + + CB-2 + + + + + + B-1 + + + + + + B-2 + + + + + + + + + + + CB-3 + + + + + + + + D-3 + + + + + + + + + + + + D-4 + + + + + + + + D-7 + + + + + + + + D-8 + + + + + + + + + + + + + + + + + + + + + + + + R + + + + R + + + + R + + + + R + + + + + + + G-1 + + + + + + G-2 + + + + + + G-3 + + + + + Bus/Breaker + + + + + + + + + + + + + CB-2 + + + + + B-1 + + + + + B-2 + + + + + + + + + + + + CB-1 + + + + + + + + + + + CB-3 + + + + + + + + + + + + + CB-4 + + + + + + + + + + + + + + + + + G-1 + + + + + + G-2 + + + + + + G-3 + + + + + Bus/Branch + + + + + + + + + B-1 + + + + + + + + + + + + + + + + + B-2 + + + + + + Buses + + + + + + Buses + + + + + + Nodes + + + + + R Retained Switches + + + + T-1 + + + + + T-1 + + + + + T-1 + + + + + T-1 + + + + \ No newline at end of file diff --git a/docs/grid_model/img/three-windings-transformer-model.svg b/docs/grid_model/img/three-winding-transformer-model.svg similarity index 100% rename from docs/grid_model/img/three-windings-transformer-model.svg rename to docs/grid_model/img/three-winding-transformer-model.svg diff --git a/docs/grid_model/img/two-windings-transformer-model.svg b/docs/grid_model/img/two-winding-transformer-model.svg similarity index 100% rename from docs/grid_model/img/two-windings-transformer-model.svg rename to docs/grid_model/img/two-winding-transformer-model.svg diff --git a/docs/grid_model/network_subnetwork.md b/docs/grid_model/network_subnetwork.md index c28a691b299..44ae4455340 100644 --- a/docs/grid_model/network_subnetwork.md +++ b/docs/grid_model/network_subnetwork.md @@ -89,7 +89,8 @@ PowSyBl provides an integrated topology processor that allows to automatically o The following diagram represents an example voltage level with two busbars separated by a circuit breaker, a transformer connected to one of them and three generators that can connect to any of the two busbars. The three topology levels are shown. -![VoltageLevel](img/voltage-level.svg){width="100%" align=center} +![VoltageLevel](img/voltage-level.svg){width="100%" align=center class="only-light"} +![VoltageLevel](img/dark_mode/voltage-level.svg){width="100%" align=center class="only-dark"} When defining the model, the user has to specify how the different equipment connect to the network. If the voltage level is built at node/breaker level, the user has to specify a `Node` when adding equipment to the model. If the user is building using bus/breaker level, the `Bus` of the equipment must be specified. Using this information, the model creates a `Terminal` that will be used to manage the point of connection of the equipment to the network. @@ -154,7 +155,8 @@ which are then separated for AC and DC parts. A generator is an equipment that injects or consumes active power, and injects or consumes reactive power. It may be used as a controller to hold a voltage or reactive target somewhere in the network, not necessarily directly where it is connected. In that specific case, the voltage or reactive power control is remote. -![GeneratorSignConvention](img/generator-sign-convention.svg){width="40%" align=center} +![GeneratorSignConvention](img/generator-sign-convention.svg){width="50%" align=center class="only-light"} +![GeneratorSignConvention](img/dark_mode/generator-sign-convention.svg){width="50%" align=center class="only-dark"} **Characteristics** @@ -278,7 +280,8 @@ A battery on the electric grid is an energy storage device that is either capabl A network may be connected to other networks for which a full description is not available or unwanted. In this case, a boundary line exists between the two networks. In the network of interest, that connection could be represented through a dangling line, which represents the part of that boundary line which is located in it. A dangling line is thus a passive or active component that aggregates a line chunk and a constant power injection, in passive-sign convention. The active and reactive power set points are fixed: the injection represents the power flow that would occur through the connection, were the other network fully described. -![Dangling line model](img/danglingLine.svg){width="50%" align=center} +![Dangling line model](img/dangling-line.svg){width="50%" align=center class="only-light"} +![Dangling line model](img/dark_mode/dangling-line.svg){width="50%" align=center class="only-dark"} A generation part, at boundary side can also be modeled, with a constant active power injection and a constant reactive power injection if the generation part of the dangling line is out of voltage regulation or a voltage target if the regulation is enabled. This fictitious generator can only regulate voltage locally: the regulating terminal can not be set, it is necessary the boundary side of the dangling line. Limits are modeled through $MinP$ and $MaxP$ for active power limits and through [reactive limits](./additional.md#reactive-limits). This generation part is optional. The generation part of the dangling line follows the classical generator sign convention. @@ -462,7 +465,8 @@ In IIDM the static VAR compensator also comprises some metadata: AC transmission lines are modeled using a standard $\pi$ model with distributed parameters. A `Line` is a `Branch`, that models equipment with two terminals (or two sides). For the time being, a branch is an AC equipment. -![Line model](img/line-model.svg){width="50%" align=center} +![Line model](img/line-model.svg){width="50%" align=center class="only-light"} +![Line model](img/dark_mode/line-model.svg){width="50%" align=center class="only-dark"} With series impedance $z$ and the shunt admittance on each side $y_1$ and $y_2$: @@ -550,7 +554,8 @@ A tie line is not a connectable. It is just a container of two underlying dangli A two windings power transformer is connected to two voltage levels (side 1 and side 2) that belong to a same substation. Two windings transformers are modeled with the following equivalent $\Pi$ model: -![Power line model](img/two-windings-transformer-model.svg){width="50%" align=center} +![Power line model](img/two-winding-transformer-model.svg){width="50%" align=center class="only-light"} +![Power line model](img/dark_mode/two-winding-transformer-model.svg){width="50%" align=center class="only-dark"} With the series impedance $z$ and the shunt admittance $y$ and the voltage ratio $\rho$ and the angle difference $\alpha$ and potentially parameters from the current step of a [ratio tap changer](./additional.md#ratio-tap-changer) and/or a [phase tap changer](./additional.md#phase-tap-changer), we have: @@ -625,13 +630,14 @@ same substation. We usually have: A three windings transformer is modeled with three legs, where every leg model is electrically equivalent to a two windings transformer. For each leg, the network bus is at side 1 and the star bus is at side 2. -![Line model](img/three-windings-transformer-model.svg){width="50%" align=center} +![Line model](img/three-winding-transformer-model.svg){width="50%" align=center class="only-light"} +![Line model](img/dark_mode/three-winding-transformer-model.svg){width="50%" align=center class="only-dark"} **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $RatedU0$ | kV | The rated voltage at the star bus | +| Attribute | Unit | Description | +|-----------|------|-----------------------------------| +| $RatedU0$ | kV | The rated voltage at the star bus | **Specifications** From 9111c59237c330a1da35c47ba903aeeda54cc659 Mon Sep 17 00:00:00 2001 From: Nicolas Rol Date: Fri, 19 Jul 2024 15:55:26 +0200 Subject: [PATCH 34/57] Datasources: Rework datasource classes (#3101) * reorganize datasource classes * add tests Signed-off-by: Nicolas Rol --- .../executor/AmplModelExecutionHandler.java | 6 +- .../CreateMissingContainersPreProcessor.java | 4 +- .../conversion/test/ConversionTester.java | 12 +- .../test/ImportExportPerformanceTest.java | 4 +- .../test/export/CgmesExportTest.java | 14 +- .../test/export/CgmesNamingStrategyTest.java | 2 +- .../export/CommonGridModelExportTest.java | 6 +- .../test/export/EquipmentExportTest.java | 50 +++--- .../test/export/ExportToCimVersionTest.java | 4 +- .../SteadyStateHypothesisExportTest.java | 10 +- .../export/TopologyExportCornerCasesTest.java | 4 +- .../test/export/issues/SwitchExportTest.java | 4 +- .../datasource/AbstractArchiveDataSource.java | 29 +++ .../AbstractFileSystemDataSource.java | 59 ++++++ .../commons/datasource/ArchiveFormat.java | 35 ++++ ...rce.java => Bzip2DirectoryDataSource.java} | 15 +- .../commons/datasource/DataSourceUtil.java | 24 +-- ...taSource.java => DirectoryDataSource.java} | 58 +++--- .../datasource/GenericReadOnlyDataSource.java | 14 +- ...Source.java => GzDirectoryDataSource.java} | 15 +- .../datasource/ReadOnlyDataSource.java | 18 ++ ...Source.java => XZDirectoryDataSource.java} | 15 +- ...aSource.java => ZipArchiveDataSource.java} | 48 ++--- ...urce.java => ZstdDirectoryDataSource.java} | 15 +- .../AbstractArchiveDataSourceTest.java | 84 +++++++++ .../AbstractFileSystemDataSourceTest.java | 163 +++++++++++++++++ .../commons/datasource/ArchiveFormatTest.java | 29 +++ .../Bzip2DirectoryDataSourceTest.java | 92 ++++++++++ .../datasource/Bzip2FileDataSourceTest.java | 24 --- .../datasource/DataSourceObserverTest.java | 2 +- .../commons/datasource/DataSourceTest.java | 74 ++++++++ .../datasource/DataSourceUtilTest.java | 36 ++++ .../datasource/DirectoryDataSourceTest.java | 134 ++++++++++++++ .../datasource/FileDataSourceTest.java | 91 ---------- .../datasource/GzDirectoryDataSourceTest.java | 86 +++++++++ .../datasource/GzFileDataSourceTest.java | 19 -- ...SourceTest.java => MemDataSourceTest.java} | 39 ++-- .../datasource/MemFileDataSourceTest.java | 24 --- .../MultipleReadOnlyDataSourceTest.java | 4 +- .../datasource/XZDirectoryDataSourceTest.java | 92 ++++++++++ .../datasource/XZFileDataSourceTest.java | 24 --- .../datasource/ZipArchiveDataSourceTest.java | 170 ++++++++++++++++++ .../datasource/ZipFileDataSourceTest.java | 86 --------- .../ZstdDirectoryDataSourceTest.java | 86 +++++++++ .../datasource/ZstdFileDataSourceTest.java | 19 -- commons/src/test/resources/foo.iidm.zip | Bin 0 -> 654 bytes .../converter/IeeeCdfImporterTest.java | 4 +- .../com/powsybl/iidm/network/Importers.java | 2 +- .../com/powsybl/iidm/network/Network.java | 2 +- .../powsybl/iidm/serde/XMLImporterTest.java | 36 ++-- .../converter/MatpowerExporterTest.java | 6 +- .../converter/MatpowerImporterTest.java | 16 +- .../converter/MatpowerRoundTripTest.java | 12 +- .../converter/PowerFactoryImporterTest.java | 10 +- .../psse/converter/PsseExporterTest.java | 4 +- .../powsybl/psse/model/PsseRawDataTest.java | 28 +-- .../powsybl/triplestore/test/ExportTest.java | 4 +- 57 files changed, 1406 insertions(+), 561 deletions(-) create mode 100644 commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java create mode 100644 commons/src/main/java/com/powsybl/commons/datasource/AbstractFileSystemDataSource.java create mode 100644 commons/src/main/java/com/powsybl/commons/datasource/ArchiveFormat.java rename commons/src/main/java/com/powsybl/commons/datasource/{Bzip2FileDataSource.java => Bzip2DirectoryDataSource.java} (72%) rename commons/src/main/java/com/powsybl/commons/datasource/{FileDataSource.java => DirectoryDataSource.java} (67%) rename commons/src/main/java/com/powsybl/commons/datasource/{GzFileDataSource.java => GzDirectoryDataSource.java} (72%) rename commons/src/main/java/com/powsybl/commons/datasource/{XZFileDataSource.java => XZDirectoryDataSource.java} (72%) rename commons/src/main/java/com/powsybl/commons/datasource/{ZipFileDataSource.java => ZipArchiveDataSource.java} (83%) rename commons/src/main/java/com/powsybl/commons/datasource/{ZstdFileDataSource.java => ZstdDirectoryDataSource.java} (72%) create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/AbstractArchiveDataSourceTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/AbstractFileSystemDataSourceTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/ArchiveFormatTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSourceTest.java delete mode 100644 commons/src/test/java/com/powsybl/commons/datasource/Bzip2FileDataSourceTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/DataSourceTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/DirectoryDataSourceTest.java delete mode 100644 commons/src/test/java/com/powsybl/commons/datasource/FileDataSourceTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/GzDirectoryDataSourceTest.java delete mode 100644 commons/src/test/java/com/powsybl/commons/datasource/GzFileDataSourceTest.java rename commons/src/test/java/com/powsybl/commons/datasource/{AbstractDataSourceTest.java => MemDataSourceTest.java} (87%) delete mode 100644 commons/src/test/java/com/powsybl/commons/datasource/MemFileDataSourceTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/XZDirectoryDataSourceTest.java delete mode 100644 commons/src/test/java/com/powsybl/commons/datasource/XZFileDataSourceTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java delete mode 100644 commons/src/test/java/com/powsybl/commons/datasource/ZipFileDataSourceTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/ZstdDirectoryDataSourceTest.java delete mode 100644 commons/src/test/java/com/powsybl/commons/datasource/ZstdFileDataSourceTest.java create mode 100644 commons/src/test/resources/foo.iidm.zip diff --git a/ampl-executor/src/main/java/com/powsybl/ampl/executor/AmplModelExecutionHandler.java b/ampl-executor/src/main/java/com/powsybl/ampl/executor/AmplModelExecutionHandler.java index 624d78dc32b..a59279a0c6e 100644 --- a/ampl-executor/src/main/java/com/powsybl/ampl/executor/AmplModelExecutionHandler.java +++ b/ampl-executor/src/main/java/com/powsybl/ampl/executor/AmplModelExecutionHandler.java @@ -10,7 +10,7 @@ import com.powsybl.ampl.converter.*; import com.powsybl.commons.PowsyblException; import com.powsybl.commons.datasource.DataSource; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.util.StringToIntMapper; import com.powsybl.computation.*; import com.powsybl.iidm.network.Network; @@ -97,7 +97,7 @@ private void exportAmplParameters(Path workingDir) throws IOException { } private void exportNetworkAsAmpl(Path workingDir) { - DataSource networkExportDataSource = new FileDataSource(workingDir, this.model.getNetworkDataPrefix()); + DataSource networkExportDataSource = new DirectoryDataSource(workingDir, this.model.getNetworkDataPrefix()); if (parameters.getAmplExportConfig() != null) { new AmplExporter().export(network, parameters.getAmplExportConfig(), networkExportDataSource); } else { @@ -180,7 +180,7 @@ public List before(Path workingDir) throws IOException { @Override public AmplResults after(Path workingDir, ExecutionReport report) throws IOException { super.after(workingDir.toAbsolutePath(), report); - DataSource networkAmplResults = new FileDataSource(workingDir, this.model.getOutputFilePrefix()); + DataSource networkAmplResults = new DirectoryDataSource(workingDir, this.model.getOutputFilePrefix()); AmplNetworkReader reader = new AmplNetworkReader(networkAmplResults, this.network, this.model.getVariant(), mapper, this.model.getNetworkUpdaterFactory(), this.model.getOutputFormat()); Map indicators = readIndicators(reader); diff --git a/cgmes/cgmes-completion/src/main/java/com/powsybl/cgmes/completion/CreateMissingContainersPreProcessor.java b/cgmes/cgmes-completion/src/main/java/com/powsybl/cgmes/completion/CreateMissingContainersPreProcessor.java index c813fe1c027..530969a183d 100644 --- a/cgmes/cgmes-completion/src/main/java/com/powsybl/cgmes/completion/CreateMissingContainersPreProcessor.java +++ b/cgmes/cgmes-completion/src/main/java/com/powsybl/cgmes/completion/CreateMissingContainersPreProcessor.java @@ -22,7 +22,7 @@ import com.powsybl.cgmes.model.triplestore.CgmesModelTripleStore; import com.powsybl.commons.PowsyblException; import com.powsybl.commons.config.PlatformConfig; -import com.powsybl.commons.datasource.ZipFileDataSource; +import com.powsybl.commons.datasource.ZipArchiveDataSource; import com.powsybl.commons.parameters.Parameter; import com.powsybl.commons.parameters.ParameterDefaultValueConfig; import com.powsybl.commons.parameters.ParameterType; @@ -132,7 +132,7 @@ private static void prepareAndReadFixesUsingZipFile(CgmesModel cgmes, String bas LOG.info("Missing voltage levels: {}", missingVoltageLevels); if (!missingVoltageLevels.isEmpty()) { buildZipFileWithFixes(cgmes, missingVoltageLevels, fixesFile, basename); - cgmes.read(new ZipFileDataSource(fixesFile), ReportNode.NO_OP); + cgmes.read(new ZipArchiveDataSource(fixesFile), ReportNode.NO_OP); } Set missingVoltageLevelsAfterFix = findMissingVoltageLevels(cgmes); if (!missingVoltageLevelsAfterFix.isEmpty()) { diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ConversionTester.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ConversionTester.java index ee61186c11c..10b5a7d1188 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ConversionTester.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ConversionTester.java @@ -19,9 +19,9 @@ import com.powsybl.cgmes.conversion.test.network.compare.ComparisonConfig; import com.powsybl.cgmes.model.*; import com.powsybl.commons.datasource.DataSource; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; -import com.powsybl.commons.datasource.ZipFileDataSource; +import com.powsybl.commons.datasource.ZipArchiveDataSource; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.impl.NetworkFactoryImpl; import com.powsybl.iidm.serde.XMLExporter; @@ -165,17 +165,17 @@ private static void exportXiidm(String name, String impl, Network expected, Netw XMLExporter xmlExporter = new XMLExporter(); // Last component of the path is the name for the exported XML if (expected != null) { - xmlExporter.export(expected, null, new FileDataSource(path, "expected")); + xmlExporter.export(expected, null, new DirectoryDataSource(path, "expected")); } if (actual != null) { - xmlExporter.export(actual, null, new FileDataSource(path, "actual")); + xmlExporter.export(actual, null, new DirectoryDataSource(path, "actual")); } } private static void exportCgmes(String name, String impl, Network network) throws IOException { String name1 = name.replace('/', '-'); Path path = Files.createTempDirectory("temp-export-cgmes-" + name1 + "-" + impl + "-"); - new CgmesExport().export(network, null, new FileDataSource(path, "foo")); + new CgmesExport().export(network, null, new DirectoryDataSource(path, "foo")); } private static String subsetFromName(String name) { @@ -197,7 +197,7 @@ private void testExportImportCgmes(Network network, ReadOnlyDataSource originalD Path path = fs.getPath("temp-export-cgmes"); Files.createDirectories(path); String baseName = originalDs.getBaseName(); - DataSource ds = new ZipFileDataSource(path, baseName); + DataSource ds = new ZipArchiveDataSource(path, baseName); // Copy the original files to the temporary destination, ensuring a normalized name for (String name : new CgmesOnDataSource(originalDs).names()) { diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ImportExportPerformanceTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ImportExportPerformanceTest.java index 06b07853e09..854d6529e61 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ImportExportPerformanceTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/ImportExportPerformanceTest.java @@ -17,7 +17,7 @@ import com.powsybl.cgmes.model.GridModelReference; import com.powsybl.cgmes.model.test.Cim14SmallCasesCatalog; import com.powsybl.commons.datasource.DataSource; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.NetworkFactory; @@ -81,7 +81,7 @@ private void importExport(String ts, ReadOnlyDataSource ds, FileSystem fs) throw CgmesExport e = new CgmesExport(); Path exportFolder = fs.getPath("impl-" + ts); Files.createDirectories(exportFolder); - DataSource exportDataSource = new FileDataSource(exportFolder, ""); + DataSource exportDataSource = new DirectoryDataSource(exportFolder, ""); Properties exportParameters = new Properties(); exportParameters.put(CgmesExport.CIM_VERSION, "16"); e.export(n, exportParameters, exportDataSource); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java index be488373e0d..e66a700efa0 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java @@ -477,7 +477,7 @@ void testModelDescription() throws IOException { try (FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix())) { Path tmpDir = Files.createDirectory(fileSystem.getPath("tmp")); - ZipFileDataSource zip = new ZipFileDataSource(tmpDir.resolve("."), "output"); + ZipArchiveDataSource zip = new ZipArchiveDataSource(tmpDir.resolve("."), "output"); new CgmesExport().export(network, params, zip); Network network2 = Network.read(new GenericReadOnlyDataSource(tmpDir.resolve("output.zip")), importParams); CgmesMetadataModel sshMetadata = network2 @@ -497,7 +497,7 @@ void testModelVersion() throws IOException { try (FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix())) { Path tmpDir = Files.createDirectory(fileSystem.getPath("tmp")); - ZipFileDataSource zip = new ZipFileDataSource(tmpDir.resolve("."), "output"); + ZipArchiveDataSource zip = new ZipArchiveDataSource(tmpDir.resolve("."), "output"); new CgmesExport().export(network, params, zip); Network network2 = Network.read(new GenericReadOnlyDataSource(tmpDir.resolve("output.zip")), importParams); CgmesMetadataModel sshMetadata = network2.getExtension(CgmesMetadataModels.class).getModelForSubset(CgmesSubset.STEADY_STATE_HYPOTHESIS).orElseThrow(); @@ -519,7 +519,7 @@ void testModelDescriptionClosingXML() throws IOException { try (FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix())) { Path tmpDir = Files.createDirectory(fileSystem.getPath("tmp")); - ZipFileDataSource zip = new ZipFileDataSource(tmpDir.resolve("."), "output"); + ZipArchiveDataSource zip = new ZipArchiveDataSource(tmpDir.resolve("."), "output"); new CgmesExport().export(network, params, zip); // check network can be reimported and that ModelDescription still includes end-tag @@ -561,7 +561,7 @@ void testExportWithModelingAuthorityFromReferenceData() throws IOException { Properties exportParams = new Properties(); // It is enough to check that the MAS has been set correctly in the EQ instance file exportParams.put(CgmesExport.PROFILES, "EQ"); - new CgmesExport(platformConfig).export(network, exportParams, new FileDataSource(tmpDir, network.getNameOrId())); + new CgmesExport(platformConfig).export(network, exportParams, new DirectoryDataSource(tmpDir, network.getNameOrId())); String eq = Files.readString(tmpDir.resolve(network.getNameOrId() + "_EQ.xml")); assertTrue(eq.contains("modelingAuthoritySet>http://www.elia.be/OperationalPlanning")); @@ -625,7 +625,7 @@ void testCanGeneratorControl() throws IOException { Properties exportParams = new Properties(); exportParams.put(CgmesExport.PROFILES, "EQ"); // network.write("CGMES", null, tmpDir.resolve(baseName)); - new CgmesExport().export(network, exportParams, new FileDataSource(tmpDir, baseName)); + new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName)); String eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml")); // Check that RC are exported properly @@ -644,7 +644,7 @@ void testCanGeneratorControl() throws IOException { generatorNoRcc.setTargetV(Double.NaN); //network.write("CGMES", null, tmpDir.resolve(baseName)); - new CgmesExport().export(network, exportParams, new FileDataSource(tmpDir, baseName)); + new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName)); eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml")); assertFalse(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC")); assertFalse(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC")); @@ -665,7 +665,7 @@ void testCanGeneratorControl() throws IOException { mmrlAdder.setMaxQ(mmrl.getMinQ()); mmrlAdder.add(); - new CgmesExport().export(network, exportParams, new FileDataSource(tmpDir, baseName)); + new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName)); eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml")); assertFalse(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC")); assertFalse(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC")); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesNamingStrategyTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesNamingStrategyTest.java index f2bf6289145..70bf4ed75b1 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesNamingStrategyTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesNamingStrategyTest.java @@ -223,7 +223,7 @@ private DataSource tmpDataSource(String folder, String baseName) throws IOExcept FileUtils.cleanDirectory(exportFolder.toFile()); } Files.createDirectories(exportFolder); - return new FileDataSource(exportFolder, baseName); + return new DirectoryDataSource(exportFolder, baseName); } private void copyBoundary(String outputFolderName, String baseName, ReadOnlyDataSource originalDataSource) throws IOException { diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java index 38dbae74623..47ae912b2ae 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java @@ -17,7 +17,7 @@ import com.powsybl.cgmes.model.CgmesSubset; import com.powsybl.commons.PowsyblException; import com.powsybl.commons.datasource.DataSource; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.datasource.MemDataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.report.ReportNode; @@ -515,7 +515,7 @@ void testFaraoUseCaseManualExport() throws IOException { .setVersion(exportedVersion) .setModelingAuthoritySet("myModellingAuthority"); exportParams.put(CgmesExport.PROFILES, List.of("SSH")); - n.write("CGMES", exportParams, new FileDataSource(tmpFolder, basename + "_" + country)); + n.write("CGMES", exportParams, new DirectoryDataSource(tmpFolder, basename + "_" + country)); } // In the main network, CREATE the metadata for SV and export it @@ -531,7 +531,7 @@ void testFaraoUseCaseManualExport() throws IOException { .add() .add(); exportParams.put(CgmesExport.PROFILES, List.of("SV")); - DataSource dataSource = new FileDataSource(tmpFolder, basename); + DataSource dataSource = new DirectoryDataSource(tmpFolder, basename); cgmNetwork.write("CGMES", exportParams, dataSource); int expectedOutputVersion = exportedVersion; diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java index 8e8e0db7c4e..4281631f520 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java @@ -21,7 +21,7 @@ import com.powsybl.cgmes.extensions.*; import com.powsybl.cgmes.model.CgmesNames; import com.powsybl.commons.test.AbstractSerDeTest; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.datasource.ResourceDataSource; import com.powsybl.commons.datasource.ResourceSet; @@ -207,7 +207,7 @@ void nordic32() throws IOException, XMLStreamException { exportToCgmesEQ(network); exportToCgmesTP(network); // Import EQ & TP file, no additional information (boundaries) are required - Network actual = new CgmesImport().importData(new FileDataSource(tmpDir, "exported"), NetworkFactory.findDefault(), null); + Network actual = new CgmesImport().importData(new DirectoryDataSource(tmpDir, "exported"), NetworkFactory.findDefault(), null); // The xiidm file does not contain ratedS values, but during the cgmes export process default values // are exported for each transformer that are reading in the import process. @@ -225,7 +225,7 @@ void nordic32SortTransformerEnds() throws IOException, XMLStreamException { exportToCgmesEQ(network, true); exportToCgmesTP(network); // Import EQ & TP file, no additional information (boundaries) are required - Network actual = new CgmesImport().importData(new FileDataSource(tmpDir, "exported"), NetworkFactory.findDefault(), null); + Network actual = new CgmesImport().importData(new DirectoryDataSource(tmpDir, "exported"), NetworkFactory.findDefault(), null); // Before comparing, interchange ends in twoWindingsTransformers that do not follow the high voltage at end1 rule prepareNetworkForSortedTransformerEndsComparison(network); @@ -352,7 +352,7 @@ void threeWindingsTransformerTest() throws IOException, XMLStreamException { ThreeWindingsTransformer expected = network.getThreeWindingsTransformer(t3id); // The 3-winding transformer has a ratio and phase tap changer at every end - Network network1 = new CgmesImport().importData(new FileDataSource(tmpDir, "exportedEq"), NetworkFactory.findDefault(), null); + Network network1 = new CgmesImport().importData(new DirectoryDataSource(tmpDir, "exportedEq"), NetworkFactory.findDefault(), null); ThreeWindingsTransformer actual1 = network1.getThreeWindingsTransformer(t3id); for (int k = 1; k <= 3; k++) { String aliasType; @@ -369,7 +369,7 @@ void threeWindingsTransformerTest() throws IOException, XMLStreamException { // Export an IIDM Network that has been imported from CGMES, // identifiers for tap changers must be preserved exportToCgmesEQ(network1); - Network network2 = new CgmesImport().importData(new FileDataSource(tmpDir, "exportedEq"), NetworkFactory.findDefault(), null); + Network network2 = new CgmesImport().importData(new DirectoryDataSource(tmpDir, "exportedEq"), NetworkFactory.findDefault(), null); ThreeWindingsTransformer actual2 = network2.getThreeWindingsTransformer(t3id); for (int k = 1; k <= 3; k++) { String aliasType; @@ -683,11 +683,11 @@ void equivalentShuntTest() throws IOException { Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "microGridEquivalentShunt"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, new Properties(), new DirectoryDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); ShuntCompensator expectedEquivalentShunt = network.getShuntCompensator("d771118f-36e9-4115-a128-cc3d9ce3e3da"); ShuntCompensator actualEquivalentShunt = actual.getShuntCompensator("d771118f-36e9-4115-a128-cc3d9ce3e3da"); @@ -705,11 +705,11 @@ void equivalentShuntWithZeroSectionCountTest() throws IOException { Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "microGridEquivalentShunt"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, new Properties(), new DirectoryDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); ShuntCompensator actualEquivalentShunt = actual.getShuntCompensator("d771118f-36e9-4115-a128-cc3d9ce3e3da"); assertTrue(equivalentShuntsAreEqual(equivalentShunt, actualEquivalentShunt)); @@ -767,11 +767,11 @@ void tapChangerControlDefineControlTest() throws IOException { Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "microGridTapChangerDefineControl"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, new Properties(), new DirectoryDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); TwoWindingsTransformer twtActual = actual.getTwoWindingsTransformer("e8a7eaec-51d6-4571-b3d9-c36d52073c33"); assertEquals(twtNetwork.getPhaseTapChanger().getRegulationMode().name(), twtActual.getPhaseTapChanger().getRegulationMode().name()); @@ -812,11 +812,11 @@ void tapChangerControlDefineRatioTapChangerAndPhaseTapChangerTest() throws IOExc Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "microGridTapChangerDefineRatioTapChangerAndPhaseTapChanger"; - network.write("CGMES", null, new FileDataSource(outputPath, baseName)); + network.write("CGMES", null, new DirectoryDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); TwoWindingsTransformer twtActual = actual.getTwoWindingsTransformer("ceb5d06a-a7ff-4102-a620-7f3ea5fb4a51"); assertEquals(twtNetwork.getRatioTapChanger().getTargetV(), twtActual.getRatioTapChanger().getTargetV()); @@ -839,11 +839,11 @@ void tapChangerControlDefineRatioTapChangerAndPhaseTapChangerT3wLeg1Test() throw Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "microGridTapChangerDefineRatioTapChangerAndPhaseTapChangerLeg1"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, new Properties(), new DirectoryDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); ThreeWindingsTransformer twtActual = actual.getThreeWindingsTransformer("84ed55f4-61f5-4d9d-8755-bba7b877a246"); checkLeg(twtNetwork.getLeg1(), twtActual.getLeg1()); @@ -860,11 +860,11 @@ void tapChangerControlDefineRatioTapChangerAndPhaseTapChangerT3wLeg2Test() throw Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "microGridTapChangerDefineRatioTapChangerAndPhaseTapChangerLeg2"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, new Properties(), new DirectoryDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); ThreeWindingsTransformer twtActual = actual.getThreeWindingsTransformer("411b5401-0a43-404a-acb4-05c3d7d0c95c"); checkLeg(twtNetwork.getLeg2(), twtActual.getLeg2()); @@ -881,11 +881,11 @@ void tapChangerControlDefineRatioTapChangerAndPhaseTapChangerT3wLeg3Test() throw Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "microGridTapChangerDefineRatioTapChangerAndPhaseTapChangerLeg3"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, new Properties(), new DirectoryDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); ThreeWindingsTransformer twtActual = actual.getThreeWindingsTransformer("411b5401-0a43-404a-acb4-05c3d7d0c95c"); checkLeg(twtNetwork.getLeg3(), twtActual.getLeg3()); @@ -939,10 +939,10 @@ void fossilFuelExportAndImportTest() throws IOException { Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "oneGeneratorFossilFuel"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, new Properties(), new DirectoryDataSource(outputPath, baseName)); // re-import - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), new Properties()); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), new Properties()); Generator actualGenerator = actual.getGenerator("generator1"); // check the fuelType property @@ -964,10 +964,10 @@ void hydroPowerPlantExportAndImportTest() throws IOException { Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "oneGeneratorFossilHydroPowerPlant"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, new Properties(), new DirectoryDataSource(outputPath, baseName)); // re-import - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), new Properties()); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), new Properties()); Generator actualGenerator = actual.getGenerator("generator1"); // check the storage kind property @@ -991,10 +991,10 @@ void synchronousMachineKindExportAndImportTest() throws IOException { Path outputPath = tmpDir.resolve("temp.cgmesExport"); Files.createDirectories(outputPath); String baseName = "oneGeneratorSynchronousMachineKind"; - new CgmesExport().export(network, new Properties(), new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, new Properties(), new DirectoryDataSource(outputPath, baseName)); // re-import - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), new Properties()); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), new Properties()); Generator actualGenerator = actual.getGenerator("generator1"); // check the synchronous machine kind diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportToCimVersionTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportToCimVersionTest.java index ddd30e57db0..e0d1caf512b 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportToCimVersionTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/ExportToCimVersionTest.java @@ -74,7 +74,7 @@ void testExportIEEE14ToCim100CheckIsNodeBreaker() { String cimZipFilename = "ieee14_CIM100"; Properties params = new Properties(); params.put(CgmesExport.CIM_VERSION, "100"); - ZipFileDataSource zip = new ZipFileDataSource(tmpDir.resolve("."), cimZipFilename); + ZipArchiveDataSource zip = new ZipArchiveDataSource(tmpDir.resolve("."), cimZipFilename); new CgmesExport().export(network, params, zip); Properties importParams = new Properties(); @@ -127,7 +127,7 @@ private void testExportToCim(Network network, String name, int cimVersion) { String cimZipFilename = name + "_CIM" + cimVersion; Properties params = new Properties(); params.put(CgmesExport.CIM_VERSION, Integer.toString(cimVersion)); - ZipFileDataSource zip = new ZipFileDataSource(tmpDir.resolve("."), cimZipFilename); + ZipArchiveDataSource zip = new ZipArchiveDataSource(tmpDir.resolve("."), cimZipFilename); new CgmesExport().export(network, params, zip); // Reimport and verify contents of Network diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java index 43aaa9eefbc..9c69ed7d45f 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java @@ -15,7 +15,7 @@ import com.powsybl.cgmes.extensions.CgmesControlArea; import com.powsybl.cgmes.extensions.CgmesControlAreas; import com.powsybl.cgmes.model.CgmesNamespace; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.test.AbstractSerDeTest; import com.powsybl.commons.xml.XmlUtil; @@ -328,11 +328,11 @@ void microGridCgmesExportPreservingOriginalClassesOfLoads() throws IOException, Files.createDirectories(outputPath); String baseName = "microGridCgmesExportPreservingOriginalClassesOfLoads"; Properties exportParams = new Properties(); - new CgmesExport().export(network, exportParams, new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, exportParams, new DirectoryDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), importParams); InputStream expectedSsh = Repackager.newInputStream(ds, Repackager::ssh); String actualSsh = exportSshAsString(actual); @@ -364,11 +364,11 @@ void miniGridCgmesExportPreservingOriginalClasses() throws IOException, XMLStrea Files.createDirectories(outputPath); String baseName = "miniGridCgmesExportPreservingOriginalClasses"; Properties exportParams = new Properties(); - new CgmesExport().export(network, exportParams, new FileDataSource(outputPath, baseName)); + new CgmesExport().export(network, exportParams, new DirectoryDataSource(outputPath, baseName)); // re-import after adding the original boundary files copyBoundary(outputPath, baseName, ds); - Network actual = new CgmesImport().importData(new FileDataSource(outputPath, baseName), NetworkFactory.findDefault(), new Properties()); + Network actual = new CgmesImport().importData(new DirectoryDataSource(outputPath, baseName), NetworkFactory.findDefault(), new Properties()); InputStream expectedSsh = Repackager.newInputStream(ds, Repackager::ssh); String actualSsh = exportSshAsString(actual); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/TopologyExportCornerCasesTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/TopologyExportCornerCasesTest.java index a9bb1aa51b9..4bb61a53619 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/TopologyExportCornerCasesTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/TopologyExportCornerCasesTest.java @@ -3,7 +3,7 @@ import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.CgmesImport; import com.powsybl.commons.test.AbstractSerDeTest; -import com.powsybl.commons.datasource.ZipFileDataSource; +import com.powsybl.commons.datasource.ZipArchiveDataSource; import com.powsybl.computation.local.LocalComputationManager; import com.powsybl.iidm.network.*; import org.junit.jupiter.api.Disabled; @@ -78,7 +78,7 @@ private void test(Network network, // Export as CGMES 3 Properties params = new Properties(); params.put(CgmesExport.CIM_VERSION, "100"); - ZipFileDataSource zip = new ZipFileDataSource(tmpDir.resolve("."), name); + ZipArchiveDataSource zip = new ZipArchiveDataSource(tmpDir.resolve("."), name); new CgmesExport().export(network, params, zip); Properties importParams = new Properties(); importParams.put(CgmesImport.IMPORT_CGM_WITH_SUBNETWORKS, "false"); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/SwitchExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/SwitchExportTest.java index f221f0bab70..5d538228907 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/SwitchExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/issues/SwitchExportTest.java @@ -10,7 +10,7 @@ import com.powsybl.cgmes.conformity.CgmesConformity1ModifiedCatalog; import com.powsybl.cgmes.conversion.CgmesImport; import com.powsybl.cgmes.model.CgmesNamespace; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.test.AbstractSerDeTest; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.SwitchKind; @@ -104,7 +104,7 @@ void testExportRetainedSwitchWithSameBusBreakerBusAtBothEnds() { // Check that the coupler is preserved when exported to CGMES String basename = n.getNameOrId(); n.write("CGMES", null, tmpDir.resolve(basename)); - Network n1 = Network.read(new FileDataSource(tmpDir, basename)); + Network n1 = Network.read(new DirectoryDataSource(tmpDir, basename)); assertNotNull(n1.getSwitch("coupler")); } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java new file mode 100644 index 00000000000..a1439a91845 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import java.nio.file.Path; + +/** + * @author Nicolas Rol {@literal } + */ +public abstract class AbstractArchiveDataSource extends AbstractFileSystemDataSource { + + private final String archiveFileName; + final ArchiveFormat archiveFormat; + + AbstractArchiveDataSource(Path directory, String archiveFileName, String baseName, CompressionFormat compressionFormat, ArchiveFormat archiveFormat, DataSourceObserver observer) { + super(directory, baseName, compressionFormat, observer); + this.archiveFileName = archiveFileName; + this.archiveFormat = archiveFormat; + } + + protected Path getArchiveFilePath() { + return directory.resolve(archiveFileName); + } +} diff --git a/commons/src/main/java/com/powsybl/commons/datasource/AbstractFileSystemDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/AbstractFileSystemDataSource.java new file mode 100644 index 00000000000..3b208ae0b09 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/datasource/AbstractFileSystemDataSource.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Objects; + +/** + * @author Nicolas Rol {@literal } + */ +abstract class AbstractFileSystemDataSource implements DataSource { + final Path directory; + final String baseName; + final CompressionFormat compressionFormat; + final DataSourceObserver observer; + + /** + * + * @param directory Directory in which is located the data + * @param baseName Base of the filenames that will be used + * @param observer Data source observer + */ + AbstractFileSystemDataSource(Path directory, String baseName, + CompressionFormat compressionFormat, + DataSourceObserver observer) { + this.directory = Objects.requireNonNull(directory); + this.baseName = Objects.requireNonNull(baseName); + this.compressionFormat = compressionFormat; + this.observer = observer; + } + + public Path getDirectory() { + return this.directory; + } + + @Override + public String getBaseName() { + return baseName; + } + + public CompressionFormat getCompressionFormat() { + return compressionFormat; + } + + public DataSourceObserver getObserver() { + return observer; + } + + @Override + public boolean exists(String suffix, String ext) throws IOException { + return exists(DataSourceUtil.getFileName(baseName, suffix, ext)); + } +} diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ArchiveFormat.java b/commons/src/main/java/com/powsybl/commons/datasource/ArchiveFormat.java new file mode 100644 index 00000000000..8de2889c47a --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/datasource/ArchiveFormat.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Objects; + +/** + * @author Nicolas Rol {@literal } + */ +public enum ArchiveFormat { + ZIP("zip"); + + ArchiveFormat(String extension) { + this.extension = Objects.requireNonNull(extension); + } + + public String getExtension() { + return extension; + } + + public static Collection getFormats() { + return Arrays.stream(ArchiveFormat.values()) + .map(ArchiveFormat::name) + .toList(); + } + + private final String extension; +} diff --git a/commons/src/main/java/com/powsybl/commons/datasource/Bzip2FileDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSource.java similarity index 72% rename from commons/src/main/java/com/powsybl/commons/datasource/Bzip2FileDataSource.java rename to commons/src/main/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSource.java index a6433a5fa29..f2a4ae78ad4 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/Bzip2FileDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSource.java @@ -16,19 +16,14 @@ /** * @author Christian Biasuzzi {@literal } */ -public class Bzip2FileDataSource extends FileDataSource { +public class Bzip2DirectoryDataSource extends DirectoryDataSource { - public Bzip2FileDataSource(Path directory, String baseName, DataSourceObserver observer) { - super(directory, baseName, observer); + public Bzip2DirectoryDataSource(Path directory, String baseName, DataSourceObserver observer) { + super(directory, baseName, CompressionFormat.BZIP2, observer); } - public Bzip2FileDataSource(Path directory, String baseName) { - super(directory, baseName); - } - - @Override - protected String getCompressionExt() { - return ".bz2"; + public Bzip2DirectoryDataSource(Path directory, String baseName) { + super(directory, baseName, CompressionFormat.BZIP2, null); } @Override diff --git a/commons/src/main/java/com/powsybl/commons/datasource/DataSourceUtil.java b/commons/src/main/java/com/powsybl/commons/datasource/DataSourceUtil.java index b49896c896a..23a5cc25011 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/DataSourceUtil.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/DataSourceUtil.java @@ -44,19 +44,19 @@ static DataSource createDataSource(Path directory, String basename, CompressionF Objects.requireNonNull(basename); if (compressionExtension == null) { - return new FileDataSource(directory, basename, observer); + return new DirectoryDataSource(directory, basename, observer); } else { switch (compressionExtension) { case BZIP2: - return new Bzip2FileDataSource(directory, basename, observer); + return new Bzip2DirectoryDataSource(directory, basename, observer); case GZIP: - return new GzFileDataSource(directory, basename, observer); + return new GzDirectoryDataSource(directory, basename, observer); case XZ: - return new XZFileDataSource(directory, basename, observer); + return new XZDirectoryDataSource(directory, basename, observer); case ZIP: - return new ZipFileDataSource(directory, basename, observer); + return new ZipArchiveDataSource(directory, basename, observer); case ZSTD: - return new ZstdFileDataSource(directory, basename, observer); + return new ZstdDirectoryDataSource(directory, basename, observer); default: throw new IllegalStateException("Unexpected CompressionFormat value: " + compressionExtension); } @@ -68,17 +68,17 @@ static DataSource createDataSource(Path directory, String fileNameOrBaseName, Da Objects.requireNonNull(fileNameOrBaseName); if (fileNameOrBaseName.endsWith(".zst")) { - return new ZstdFileDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 4)), observer); + return new ZstdDirectoryDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 4)), observer); } else if (fileNameOrBaseName.endsWith(".zip")) { - return new ZipFileDataSource(directory, fileNameOrBaseName, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 4)), observer); + return new ZipArchiveDataSource(directory, fileNameOrBaseName, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 4)), observer); } else if (fileNameOrBaseName.endsWith(".xz")) { - return new XZFileDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 3)), observer); + return new XZDirectoryDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 3)), observer); } else if (fileNameOrBaseName.endsWith(".gz")) { - return new GzFileDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 3)), observer); + return new GzDirectoryDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 3)), observer); } else if (fileNameOrBaseName.endsWith(".bz2")) { - return new Bzip2FileDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 4)), observer); + return new Bzip2DirectoryDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 4)), observer); } else { - return new FileDataSource(directory, getBaseName(fileNameOrBaseName), observer); + return new DirectoryDataSource(directory, getBaseName(fileNameOrBaseName), observer); } } } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/FileDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/DirectoryDataSource.java similarity index 67% rename from commons/src/main/java/com/powsybl/commons/datasource/FileDataSource.java rename to commons/src/main/java/com/powsybl/commons/datasource/DirectoryDataSource.java index 2e30202a2f3..24fca2af1d4 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/FileDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/DirectoryDataSource.java @@ -20,40 +20,39 @@ /** * @author Geoffroy Jamgotchian {@literal } + * @author Nicolas Rol {@literal } */ -public class FileDataSource implements DataSource { +public class DirectoryDataSource extends AbstractFileSystemDataSource { - private static final String COMPRESSION_EXT = ""; - - private final Path directory; - - private final String baseName; - - private final DataSourceObserver observer; - - public FileDataSource(Path directory, String baseName) { - this(directory, baseName, null); + public DirectoryDataSource(Path directory, String baseName) { + this(directory, baseName, null, null); } - public FileDataSource(Path directory, String baseName, DataSourceObserver observer) { - this.directory = Objects.requireNonNull(directory); - this.baseName = Objects.requireNonNull(baseName); - this.observer = observer; + public DirectoryDataSource(Path directory, String baseName, + DataSourceObserver observer) { + this(directory, baseName, null, observer); } - @Override - public String getBaseName() { - return baseName; + DirectoryDataSource(Path directory, String baseName, + CompressionFormat compressionFormat, + DataSourceObserver observer) { + super(directory, baseName, compressionFormat, observer); } protected String getCompressionExt() { - return COMPRESSION_EXT; + return compressionFormat == null ? "" : "." + compressionFormat.getExtension(); } + /** + * @throws IOException Overriding classes may throw this exception + */ protected InputStream getCompressedInputStream(InputStream is) throws IOException { return is; } + /** + * @throws IOException Overriding classes may throw this exception + */ protected OutputStream getCompressedOutputStream(OutputStream os) throws IOException { return os; } @@ -75,11 +74,6 @@ public OutputStream newOutputStream(String fileName, boolean append) throws IOEx return observer != null ? new ObservableOutputStream(os, path.toString(), observer) : os; } - @Override - public boolean exists(String suffix, String ext) throws IOException { - return exists(DataSourceUtil.getFileName(baseName, suffix, ext)); - } - @Override public boolean exists(String fileName) throws IOException { Path path = getPath(fileName); @@ -105,14 +99,14 @@ public Set listNames(String regex) throws IOException { int maxDepth = 1; try (Stream paths = Files.walk(directory, maxDepth)) { return paths - .filter(Files::isRegularFile) - .map(Path::getFileName) - .map(Path::toString) - .filter(name -> name.startsWith(baseName)) - // Return names after removing the compression extension - .map(name -> name.replace(getCompressionExt(), "")) - .filter(s -> p.matcher(s).matches()) - .collect(Collectors.toSet()); + .filter(Files::isRegularFile) + .map(Path::getFileName) + .map(Path::toString) + .filter(name -> name.startsWith(baseName)) + // Return names after removing the compression extension + .map(name -> name.replace(getCompressionExt(), "")) + .filter(s -> p.matcher(s).matches()) + .collect(Collectors.toSet()); } } } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/GenericReadOnlyDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/GenericReadOnlyDataSource.java index 88813dc7a15..c5643233a2b 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/GenericReadOnlyDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/GenericReadOnlyDataSource.java @@ -23,13 +23,13 @@ public class GenericReadOnlyDataSource implements ReadOnlyDataSource { public GenericReadOnlyDataSource(Path directory, String baseName, DataSourceObserver observer) { dataSources = new DataSource[] { - new FileDataSource(directory, baseName, observer), - new ZstdFileDataSource(directory, baseName, observer), - new ZipFileDataSource(directory), - new ZipFileDataSource(directory, baseName + ".zip", baseName, observer), - new XZFileDataSource(directory, baseName, observer), - new GzFileDataSource(directory, baseName, observer), - new Bzip2FileDataSource(directory, baseName, observer) + new DirectoryDataSource(directory, baseName, observer), + new ZstdDirectoryDataSource(directory, baseName, observer), + new ZipArchiveDataSource(directory), + new ZipArchiveDataSource(directory, baseName + ".zip", baseName, observer), + new XZDirectoryDataSource(directory, baseName, observer), + new GzDirectoryDataSource(directory, baseName, observer), + new Bzip2DirectoryDataSource(directory, baseName, observer) }; } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/GzFileDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/GzDirectoryDataSource.java similarity index 72% rename from commons/src/main/java/com/powsybl/commons/datasource/GzFileDataSource.java rename to commons/src/main/java/com/powsybl/commons/datasource/GzDirectoryDataSource.java index d05c71390eb..6381c570d3b 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/GzFileDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/GzDirectoryDataSource.java @@ -17,19 +17,14 @@ /** * @author Geoffroy Jamgotchian {@literal } */ -public class GzFileDataSource extends FileDataSource { +public class GzDirectoryDataSource extends DirectoryDataSource { - public GzFileDataSource(Path directory, String baseName, DataSourceObserver observer) { - super(directory, baseName, observer); + public GzDirectoryDataSource(Path directory, String baseName, DataSourceObserver observer) { + super(directory, baseName, CompressionFormat.GZIP, observer); } - public GzFileDataSource(Path directory, String baseName) { - super(directory, baseName); - } - - @Override - protected String getCompressionExt() { - return ".gz"; + public GzDirectoryDataSource(Path directory, String baseName) { + super(directory, baseName, CompressionFormat.GZIP, null); } @Override diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyDataSource.java index 2e21b00c0b8..c4ee48e42ac 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyDataSource.java @@ -18,13 +18,31 @@ public interface ReadOnlyDataSource { String getBaseName(); + /** + * Check if a file exists in the datasource. The file name will be constructed as: + * {@code .}

+ * @param suffix Suffix to add to the basename of the datasource + * @param ext Extension of the file (for example: .iidm, .xml, .txt, etc.) + * @return true if the file exists, else false + */ boolean exists(String suffix, String ext) throws IOException; + /** + * Check if a file exists in the datasource. + * @param fileName Name of the file (excluding the compression extension) + * @return true if the file exists, else false + */ boolean exists(String fileName) throws IOException; InputStream newInputStream(String suffix, String ext) throws IOException; InputStream newInputStream(String fileName) throws IOException; + /** + * Returns a set of Strings corresponding to the name of the different files in the datasource. + * @param regex regex used to identify files in the datasource + * @return a set of filenames + * @throws IOException exception thrown during file opening + */ Set listNames(String regex) throws IOException; } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/XZFileDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/XZDirectoryDataSource.java similarity index 72% rename from commons/src/main/java/com/powsybl/commons/datasource/XZFileDataSource.java rename to commons/src/main/java/com/powsybl/commons/datasource/XZDirectoryDataSource.java index 1ce00b2e593..a5eb4014e79 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/XZFileDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/XZDirectoryDataSource.java @@ -16,19 +16,14 @@ /** * @author Olivier Bretteville {@literal } */ -public class XZFileDataSource extends FileDataSource { +public class XZDirectoryDataSource extends DirectoryDataSource { - public XZFileDataSource(Path directory, String baseName, DataSourceObserver observer) { - super(directory, baseName, observer); + public XZDirectoryDataSource(Path directory, String baseName, DataSourceObserver observer) { + super(directory, baseName, CompressionFormat.XZ, observer); } - public XZFileDataSource(Path directory, String baseName) { - super(directory, baseName); - } - - @Override - protected String getCompressionExt() { - return ".xz"; + public XZDirectoryDataSource(Path directory, String baseName) { + super(directory, baseName, CompressionFormat.XZ, null); } @Override diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ZipFileDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java similarity index 83% rename from commons/src/main/java/com/powsybl/commons/datasource/ZipFileDataSource.java rename to commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java index a5e01f9b2e7..7cdff8ab992 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ZipFileDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java @@ -29,54 +29,30 @@ /** * @author Geoffroy Jamgotchian {@literal } + * @author Nicolas Rol {@literal } */ -public class ZipFileDataSource implements DataSource { +public class ZipArchiveDataSource extends AbstractArchiveDataSource { - private final Path directory; - - private final String zipFileName; - - private final String baseName; - - private final DataSourceObserver observer; - - public ZipFileDataSource(Path directory, String zipFileName, String baseName, DataSourceObserver observer) { - this.directory = Objects.requireNonNull(directory); - this.zipFileName = Objects.requireNonNull(zipFileName); - this.baseName = Objects.requireNonNull(baseName); - this.observer = observer; + public ZipArchiveDataSource(Path directory, String zipFileName, String baseName, DataSourceObserver observer) { + super(directory, zipFileName, baseName, CompressionFormat.ZIP, ArchiveFormat.ZIP, observer); } - public ZipFileDataSource(Path directory, String zipFileName, String baseName) { + public ZipArchiveDataSource(Path directory, String zipFileName, String baseName) { this(directory, zipFileName, baseName, null); } - public ZipFileDataSource(Path directory, String baseName) { + public ZipArchiveDataSource(Path directory, String baseName) { this(directory, baseName + ".zip", baseName, null); } - public ZipFileDataSource(Path directory, String baseName, DataSourceObserver observer) { + public ZipArchiveDataSource(Path directory, String baseName, DataSourceObserver observer) { this(directory, baseName + ".zip", baseName, observer); } - public ZipFileDataSource(Path zipFile) { + public ZipArchiveDataSource(Path zipFile) { this(zipFile.getParent(), com.google.common.io.Files.getNameWithoutExtension(zipFile.getFileName().toString())); } - @Override - public String getBaseName() { - return baseName; - } - - private Path getZipFilePath() { - return directory.resolve(zipFileName); - } - - @Override - public boolean exists(String suffix, String ext) throws IOException { - return exists(DataSourceUtil.getFileName(baseName, suffix, ext)); - } - private static boolean entryExists(Path zipFilePath, String fileName) { if (Files.exists(zipFilePath)) { try (ZipFile zipFile = ZipFile.builder() @@ -93,7 +69,7 @@ private static boolean entryExists(Path zipFilePath, String fileName) { @Override public boolean exists(String fileName) { Objects.requireNonNull(fileName); - Path zipFilePath = getZipFilePath(); + Path zipFilePath = getArchiveFilePath(); return entryExists(zipFilePath, fileName); } @@ -122,7 +98,7 @@ public void close() throws IOException { @Override public InputStream newInputStream(String fileName) throws IOException { Objects.requireNonNull(fileName); - Path zipFilePath = getZipFilePath(); + Path zipFilePath = getArchiveFilePath(); if (entryExists(zipFilePath, fileName)) { InputStream is = new ZipEntryInputStream(ZipFile.builder() .setSeekableByteChannel(Files.newByteChannel(zipFilePath)) @@ -197,7 +173,7 @@ public OutputStream newOutputStream(String fileName, boolean append) throws IOEx if (append) { throw new UnsupportedOperationException("append not supported in zip file data source"); } - Path zipFilePath = getZipFilePath(); + Path zipFilePath = getArchiveFilePath(); OutputStream os = new ZipEntryOutputStream(zipFilePath, fileName); return observer != null ? new ObservableOutputStream(os, zipFilePath + ":" + fileName, observer) : os; } @@ -212,7 +188,7 @@ public Set listNames(String regex) throws IOException { // Consider only files in the given folder, do not go into folders Pattern p = Pattern.compile(regex); Set names = new HashSet<>(); - Path zipFilePath = getZipFilePath(); + Path zipFilePath = getArchiveFilePath(); try (ZipFile zipFile = ZipFile.builder() .setSeekableByteChannel(Files.newByteChannel(zipFilePath)) .get()) { diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ZstdFileDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/ZstdDirectoryDataSource.java similarity index 72% rename from commons/src/main/java/com/powsybl/commons/datasource/ZstdFileDataSource.java rename to commons/src/main/java/com/powsybl/commons/datasource/ZstdDirectoryDataSource.java index 416d6275402..f91fe3d36bd 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ZstdFileDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ZstdDirectoryDataSource.java @@ -16,19 +16,14 @@ /** * @author Olivier Bretteville {@literal } */ -public class ZstdFileDataSource extends FileDataSource { +public class ZstdDirectoryDataSource extends DirectoryDataSource { - public ZstdFileDataSource(Path directory, String baseName, DataSourceObserver observer) { - super(directory, baseName, observer); + public ZstdDirectoryDataSource(Path directory, String baseName, DataSourceObserver observer) { + super(directory, baseName, CompressionFormat.ZSTD, observer); } - public ZstdFileDataSource(Path directory, String baseName) { - super(directory, baseName); - } - - @Override - protected String getCompressionExt() { - return ".zst"; + public ZstdDirectoryDataSource(Path directory, String baseName) { + super(directory, baseName, CompressionFormat.ZSTD, null); } @Override diff --git a/commons/src/test/java/com/powsybl/commons/datasource/AbstractArchiveDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/AbstractArchiveDataSourceTest.java new file mode 100644 index 00000000000..90c27ac425b --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/AbstractArchiveDataSourceTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Nicolas Rol {@literal } + */ +abstract class AbstractArchiveDataSourceTest extends AbstractFileSystemDataSourceTest { + protected final Set filesInArchive = Set.of( + "foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", "bar.iidm", "bar"); + protected String archiveWithSubfolders; + protected String appendException; + protected ArchiveFormat archiveFormat; + + @BeforeEach + void setUp() throws Exception { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + testDir = fileSystem.getPath("/tmp"); + Files.createDirectories(testDir); + existingFiles = Set.of( + "foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", "bar.iidm", "bar"); + } + + @AfterEach + void tearDown() throws Exception { + fileSystem.close(); + } + + @Test + void testFileInSubfolder() throws IOException { + // File + File file = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(archiveWithSubfolders)).getFile()); + Path path = file.toPath(); + + // Create the datasource + DataSource dataSource = DataSource.fromPath(path); + + // All the files are listed, no filter is applied + Set files = dataSource.listNames(".*"); + assertEquals(3, files.size()); + assertTrue(files.contains("foo.iidm")); + assertTrue(files.contains("foo_bar.iidm")); + assertFalse(files.contains("foo_baz.iidm")); + assertTrue(files.contains("subfolder/foo_baz.iidm")); + } + + @Test + void testErrorOnAppend() throws IOException { + // File + Path path = testDir.resolve(archiveWithSubfolders); + Files.createFile(path); + + // Create the datasource + DataSource dataSource = DataSource.fromPath(path); + + UnsupportedOperationException exception = assertThrows(UnsupportedOperationException.class, () -> { + try (OutputStream ignored = dataSource.newOutputStream("foo.bar", true)) { + fail(); + } + }); + assertEquals(appendException, exception.getMessage()); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/AbstractFileSystemDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/AbstractFileSystemDataSourceTest.java new file mode 100644 index 00000000000..8a285694a99 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/AbstractFileSystemDataSourceTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.google.common.io.ByteStreams; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Nicolas Rol {@literal } + */ +abstract class AbstractFileSystemDataSourceTest { + protected FileSystem fileSystem; + protected Path testDir; + protected Set unlistedFiles; + protected Set existingFiles; + protected CompressionFormat compressionFormat; + + @Test + abstract void testConstructors(); + + protected boolean appendTest() { + return true; + } + + protected abstract DataSource createDataSource(); + + protected abstract DataSource createDataSource(DataSourceObserver observer); + + protected String getFileName(String baseName, CompressionFormat compressionFormat) { + return testDir + "/" + baseName + + (compressionFormat == null ? "" : "." + compressionFormat.getExtension()); + } + + protected abstract void createFiles(String fileName) throws IOException; + + @ParameterizedTest + @MethodSource("provideArgumentsForWriteThenReadTest") + void writeThenReadTest(String baseName, CompressionFormat compressionFormat) throws IOException { + // Compute the full filename + String fileName = getFileName(baseName, compressionFormat); + + // Create the files + createFiles(fileName); + + // Create the datasource + DataSource dataSource = DataSource.fromPath(fileSystem.getPath(fileName)); + + writeThenReadTest(dataSource); + } + + void writeThenReadTest(DataSource dataSource) throws IOException { + writeThenReadTest(dataSource, null, "bar"); + writeThenReadTest(dataSource, "_baz", "bar"); + writeThenReadTest(dataSource, "_baz", null); + } + + private void writeThenReadTest(DataSource dataSource, String suffix, String ext) throws IOException { + // check file does not exist + assertFalse(dataSource.exists(suffix, ext)); + + // write file + try (OutputStream os = dataSource.newOutputStream(suffix, ext, false)) { + os.write("line1".getBytes(StandardCharsets.UTF_8)); + } + if (appendTest()) { + // write file in append mode + try (OutputStream os = dataSource.newOutputStream(suffix, ext, true)) { + os.write((System.lineSeparator() + "line2").getBytes(StandardCharsets.UTF_8)); + } + } + + // write another file + try (OutputStream os = dataSource.newOutputStream("dummy.txt", false)) { + os.write("otherline1".getBytes(StandardCharsets.UTF_8)); + } + + // check files exists + assertTrue(dataSource.exists(suffix, ext)); + assertTrue(dataSource.exists("dummy.txt")); + + // check content exists and is ok + try (InputStream is = dataSource.newInputStream(suffix, ext)) { + assertEquals("line1" + (appendTest() ? System.lineSeparator() + "line2" : ""), + new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8)); + } catch (IOException x) { + fail(); + } + try (InputStream is = dataSource.newInputStream("dummy.txt")) { + assertEquals("otherline1", new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8)); + } catch (IOException x) { + fail(); + } + } + + @ParameterizedTest + @MethodSource("provideArgumentsForClassAndListingTest") + void testClassAndListing(String baseName, + CompressionFormat compressionFormat, Class dataSourceClass, + Set listedFiles, Set listedBarFiles) throws IOException { + // Compute the full filename + String fileName = getFileName(baseName, compressionFormat); + + // Update the list of unlisted files + unlistedFiles = existingFiles.stream().filter(name -> !listedFiles.contains(name)).collect(Collectors.toSet()); + + // Create the files + createFiles(fileName); + + // Create the datasource + DataSource dataSource = DataSource.fromPath(fileSystem.getPath(fileName)); + + // Check the class + assertInstanceOf(dataSourceClass, dataSource); + + // List all the files in the datasource + assertEquals(listedFiles, dataSource.listNames(".*")); + assertEquals(listedBarFiles, dataSource.listNames(".*bar.*")); + } + + @Test + void testGetters() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Create the datasource + DataSource dataSourceWithObserver = createDataSource(observer); + + // Checks + assertInstanceOf(AbstractFileSystemDataSource.class, dataSourceWithObserver); + assertEquals("foo", dataSourceWithObserver.getBaseName()); + assertEquals(testDir, ((AbstractFileSystemDataSource) dataSourceWithObserver).getDirectory()); + assertEquals(compressionFormat, ((AbstractFileSystemDataSource) dataSourceWithObserver).getCompressionFormat()); + assertEquals(observer, ((AbstractFileSystemDataSource) dataSourceWithObserver).getObserver()); + + // Create the datasource + DataSource dataSourceWithoutObserver = createDataSource(); + + // Checks + assertInstanceOf(AbstractFileSystemDataSource.class, dataSourceWithoutObserver); + assertEquals("foo", dataSourceWithoutObserver.getBaseName()); + assertEquals(testDir, ((AbstractFileSystemDataSource) dataSourceWithoutObserver).getDirectory()); + assertEquals(compressionFormat, ((AbstractFileSystemDataSource) dataSourceWithoutObserver).getCompressionFormat()); + assertNull(((AbstractFileSystemDataSource) dataSourceWithoutObserver).getObserver()); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ArchiveFormatTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ArchiveFormatTest.java new file mode 100644 index 00000000000..0af78199448 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/ArchiveFormatTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Nicolas Rol {@literal } + */ +class ArchiveFormatTest { + @Test + void test() { + assertEquals(1, ArchiveFormat.values().length); + assertEquals("zip", ArchiveFormat.ZIP.getExtension()); + + List formats = List.of( + ArchiveFormat.ZIP.name()); + assertEquals(formats, ArchiveFormat.getFormats()); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSourceTest.java new file mode 100644 index 00000000000..9e19f4874d1 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSourceTest.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2016, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Set; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Geoffroy Jamgotchian {@literal } + * @author Nicolas Rol {@literal } + */ +class Bzip2DirectoryDataSourceTest extends DirectoryDataSourceTest { + + @Override + @BeforeEach + void setUp() throws Exception { + super.setUp(); + compressionFormat = CompressionFormat.BZIP2; + } + + @Test + @Override + void testConstructors() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Check constructors + checkDataSource(new Bzip2DirectoryDataSource(testDir, "foo_bar", observer), observer); + checkDataSource(new Bzip2DirectoryDataSource(testDir, "foo_bar"), null); + } + + private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver observer) { + assertEquals(testDir, dataSource.getDirectory()); + assertEquals(compressionFormat, dataSource.getCompressionFormat()); + assertEquals("foo_bar", dataSource.getBaseName()); + assertEquals(observer, dataSource.getObserver()); + } + + @Override + protected boolean appendTest() { + // Append does not work with bzip2 compression + return false; + } + + @Override + protected DataSource createDataSource() { + return new Bzip2DirectoryDataSource(testDir, "foo"); + } + + @Override + protected DataSource createDataSource(DataSourceObserver observer) { + return new Bzip2DirectoryDataSource(testDir, "foo", observer); + } + + static Stream provideArgumentsForWriteThenReadTest() { + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.BZIP2), + Arguments.of("foo", CompressionFormat.BZIP2), + Arguments.of("foo.v3", CompressionFormat.BZIP2) + ); + } + + static Stream provideArgumentsForClassAndListingTest() { + Set listedFiles = Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", + "foo.xz", "foo.txt.xz", "foo.iidm.xz", "foo.xiidm.xz", "foo.v3.iidm.xz", "foo.v3.xz", "foo_bar.iidm.xz", "foo_bar.xz", + "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst", + "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz"); + Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.xz", "foo_bar.xz", "foo_bar.iidm.zst", "foo_bar.zst", "foo_bar.iidm.gz", "foo_bar.gz"); + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.BZIP2, Bzip2DirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo", CompressionFormat.BZIP2, Bzip2DirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo.v3", CompressionFormat.BZIP2, Bzip2DirectoryDataSource.class, + listedFiles, + listedBarFiles) + ); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/Bzip2FileDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/Bzip2FileDataSourceTest.java deleted file mode 100644 index 2ed3dc43417..00000000000 --- a/commons/src/test/java/com/powsybl/commons/datasource/Bzip2FileDataSourceTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2016, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.commons.datasource; - -/** - * @author Geoffroy Jamgotchian {@literal } - */ -class Bzip2FileDataSourceTest extends AbstractDataSourceTest { - - @Override - protected boolean appendTest() { - return false; // FIXME append does not work woth bzip2 compression - } - - @Override - protected DataSource createDataSource() { - return new Bzip2FileDataSource(testDir, getBaseName()); - } -} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/DataSourceObserverTest.java b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceObserverTest.java index 0eca0fd17b6..f384099c824 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/DataSourceObserverTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceObserverTest.java @@ -59,7 +59,7 @@ public void closed(String streamName) { } }; - DataSource dataSource = new FileDataSource(testDir, "test", observer); + DataSource dataSource = new DirectoryDataSource(testDir, "test", observer); try (OutputStream os = dataSource.newOutputStream(null, "txt", false)) { } diff --git a/commons/src/test/java/com/powsybl/commons/datasource/DataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceTest.java new file mode 100644 index 00000000000..bbed33dccea --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.commons.PowsyblException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Nicolas Rol {@literal } + */ +class DataSourceTest { + + private FileSystem fileSystem; + private Path directory; + + @BeforeEach + void setUp() throws IOException { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + directory = fileSystem.getPath("/test/"); + Files.createDirectories(directory); + } + + @AfterEach + void tearDown() throws IOException { + fileSystem.close(); + } + + private static Stream provideArgumentsForTestClass() { + return Stream.of( + Arguments.of("foo.xml", DirectoryDataSource.class), + Arguments.of("foo.xml.gz", GzDirectoryDataSource.class), + Arguments.of("foo.iidm.bz2", Bzip2DirectoryDataSource.class) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForTestClass") + void testClass(String fileName, Class dataSourceClass) throws IOException { + // File path + Path filePath = directory.resolve(fileName); + + // Assert exception when the file does not exist + PowsyblException exception = assertThrows(PowsyblException.class, () -> DataSource.fromPath(filePath)); + assertEquals("File " + filePath + " does not exist or is not a regular file", exception.getMessage()); + + // Create the fake file + Files.createFile(filePath); + + // Create the datasource based on the file + DataSource dataSource = DataSource.fromPath(filePath); + + // Assert the class of the datasource + assertInstanceOf(dataSourceClass, dataSource); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/DataSourceUtilTest.java b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceUtilTest.java index f22aa097132..8ad5ce3dfd5 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/DataSourceUtilTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceUtilTest.java @@ -7,14 +7,40 @@ */ package com.powsybl.commons.datasource; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static com.powsybl.commons.datasource.DataSourceUtil.createDataSource; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; /** * @author Mathieu Bague {@literal } + * @author Nicolas Rol {@literal } */ class DataSourceUtilTest { + protected FileSystem fileSystem; + protected Path testDir; + + @BeforeEach + void setUp() throws IOException { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + testDir = fileSystem.getPath("/tmp"); + Files.createDirectories(testDir); + } + + @AfterEach + void tearDown() throws Exception { + fileSystem.close(); + } @Test void testGetBaseName() { @@ -22,4 +48,14 @@ void testGetBaseName() { assertEquals("dummy", DataSourceUtil.getBaseName("dummy.gz")); assertEquals("dummy", DataSourceUtil.getBaseName("dummy")); } + + @Test + void testCreateDataSource() { + assertInstanceOf(DirectoryDataSource.class, createDataSource(testDir, "dummy", null, null)); + assertInstanceOf(Bzip2DirectoryDataSource.class, createDataSource(testDir, "dummy", CompressionFormat.BZIP2, null)); + assertInstanceOf(GzDirectoryDataSource.class, createDataSource(testDir, "dummy", CompressionFormat.GZIP, null)); + assertInstanceOf(XZDirectoryDataSource.class, createDataSource(testDir, "dummy", CompressionFormat.XZ, null)); + assertInstanceOf(ZipArchiveDataSource.class, createDataSource(testDir, "dummy", CompressionFormat.ZIP, null)); + assertInstanceOf(ZstdDirectoryDataSource.class, createDataSource(testDir, "dummy", CompressionFormat.ZSTD, null)); + } } diff --git a/commons/src/test/java/com/powsybl/commons/datasource/DirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/DirectoryDataSourceTest.java new file mode 100644 index 00000000000..2641571df76 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/DirectoryDataSourceTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.Set; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * @author Nicolas Rol {@literal } + */ +class DirectoryDataSourceTest extends AbstractFileSystemDataSourceTest { + + @BeforeEach + void setUp() throws Exception { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + testDir = fileSystem.getPath("/tmp"); + Files.createDirectories(testDir); + + // Files + existingFiles = Set.of( + "foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", "bar.iidm", "bar", + "foo.bz2", "foo.txt.bz2", "foo.iidm.bz2", "foo.xiidm.bz2", "foo.v3.iidm.bz2", "foo.v3.bz2", "foo_bar.iidm.bz2", "foo_bar.bz2", "bar.iidm.bz2", "bar.bz2", + "foo.xz", "foo.txt.xz", "foo.iidm.xz", "foo.xiidm.xz", "foo.v3.iidm.xz", "foo.v3.xz", "foo_bar.iidm.xz", "foo_bar.xz", "bar.iidm.xz", "bar.xz", + "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst", "bar.iidm.zst", "bar.zst", + "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz", "bar.iidm.gz", "bar.gz" + ); + } + + @AfterEach + void tearDown() throws Exception { + fileSystem.close(); + } + + @Override + protected void createFiles(String fileName) { + createFiles(); + } + + private void createFiles() { + // Create the test files + existingFiles.forEach(fileName -> { + try { + Files.createFile(fileSystem.getPath(testDir + "/" + fileName)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + @Test + @Override + void testConstructors() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Check constructors + checkDataSource(new DirectoryDataSource(testDir, "foo_bar", CompressionFormat.GZIP, observer), CompressionFormat.GZIP, observer); + checkDataSource(new DirectoryDataSource(testDir, "foo_bar", observer), null, observer); + checkDataSource(new DirectoryDataSource(testDir, "foo_bar"), null, null); + } + + private void checkDataSource(DirectoryDataSource dataSource, CompressionFormat compressionFormat, DataSourceObserver observer) { + assertEquals(testDir, dataSource.getDirectory()); + assertEquals(compressionFormat, dataSource.getCompressionFormat()); + assertEquals("foo_bar", dataSource.getBaseName()); + assertEquals(observer, dataSource.getObserver()); + } + + @Override + protected DataSource createDataSource() { + return new DirectoryDataSource(testDir, "foo"); + } + + @Override + protected DataSource createDataSource(DataSourceObserver observer) { + return new DirectoryDataSource(testDir, "foo", observer); + } + + static Stream provideArgumentsForWriteThenReadTest() { + return Stream.of( + Arguments.of("foo.iidm", null), + Arguments.of("foo", null), + Arguments.of("foo.v3", null) + ); + } + + static Stream provideArgumentsForClassAndListingTest() { + Set listedFiles = Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", + "foo.bz2", "foo.txt.bz2", "foo.iidm.bz2", "foo.xiidm.bz2", "foo.v3.iidm.bz2", "foo.v3.bz2", "foo_bar.iidm.bz2", "foo_bar.bz2", + "foo.xz", "foo.txt.xz", "foo.iidm.xz", "foo.xiidm.xz", "foo.v3.iidm.xz", "foo.v3.xz", "foo_bar.iidm.xz", "foo_bar.xz", + "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst", + "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz"); + Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.bz2", "foo_bar.bz2", "foo_bar.iidm.xz", "foo_bar.xz", + "foo_bar.iidm.zst", "foo_bar.zst", "foo_bar.iidm.gz", "foo_bar.gz"); + return Stream.of( + Arguments.of("foo.iidm", null, DirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo", null, DirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo.v3", null, DirectoryDataSource.class, + listedFiles, + listedBarFiles) + ); + } + + @Test + void testExceptionListNames() { + + // Create the datasource + DataSource dataSource = new DirectoryDataSource(fileSystem.getPath("/foo"), "baz"); + + // An exception is thrown because the directory does not exist + assertThrows(IOException.class, () -> dataSource.listNames(".*")); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/FileDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/FileDataSourceTest.java deleted file mode 100644 index 99e6a7d0e72..00000000000 --- a/commons/src/test/java/com/powsybl/commons/datasource/FileDataSourceTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) 2016, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.commons.datasource; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Geoffroy Jamgotchian {@literal } - */ -class FileDataSourceTest extends AbstractDataSourceTest { - - @Override - protected DataSource createDataSource() { - return new FileDataSource(testDir, getBaseName()); - } - - @Test - void listNamesTest() throws IOException { - // Create a couple of files in the test folder - // One using the basename - String validFilename = getBaseName() + ".txt"; - try (OutputStream os = Files.newOutputStream(testDir.resolve(validFilename))) { - os.write("basename_line".getBytes(StandardCharsets.UTF_8)); - } - // Other that has a different name - String otherFilename = "other.txt"; - try (OutputStream os = Files.newOutputStream(testDir.resolve(otherFilename))) { - os.write("other_line".getBytes(StandardCharsets.UTF_8)); - } - - // A file data source created using the complete filename does not return other filenames - Set names = new FileDataSource(testDir, getBaseName() + ".txt").listNames(".*"); - assertEquals(1, names.size()); - assertTrue(names.contains(validFilename)); - assertFalse(names.contains(otherFilename)); - - // A file data source created using the test folder and the basename sees only the right file - names = new FileDataSource(testDir, getBaseName()).listNames(".*"); - assertEquals(1, names.size()); - assertTrue(names.contains(validFilename)); - assertFalse(names.contains(otherFilename)); - } - - @Test - void createNewFilesTest() throws IOException { - DataSource ds = createDataSource(); - - // use the data source to write a file that contains basename - String suffix = "suffix"; - String ext = "ext"; - try (OutputStream os = ds.newOutputStream(suffix, ext, false)) { - os.write("line".getBytes(StandardCharsets.UTF_8)); - } - - // it is allowed to use the data source to write a file that does not contain the basename - try (OutputStream os = ds.newOutputStream("dummy.txt", false)) { - os.write("dummy_line".getBytes(StandardCharsets.UTF_8)); - } - - // write another file in the same directory of data source that does not contain the basename - // do not use the data source, just write in the same directory - try (OutputStream os = Files.newOutputStream(testDir.resolve("dummy2.txt"))) { - os.write("dummy2_line".getBytes(StandardCharsets.UTF_8)); - } - - // check the three files exists when checked through the data source - assertTrue(ds.exists(suffix, ext)); - assertTrue(ds.exists("dummy.txt")); - assertTrue(ds.exists("dummy2.txt")); - - // but only the files that contain the basename can be accessed through list names - Set names = ds.listNames(".*"); - assertEquals(1, names.size()); - assertTrue(names.contains(getBaseName() + suffix + "." + ext)); - assertFalse(names.contains("dummy.txt")); - assertFalse(names.contains("dummy2.txt")); - } -} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/GzDirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/GzDirectoryDataSourceTest.java new file mode 100644 index 00000000000..b49391e9189 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/GzDirectoryDataSourceTest.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2016, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Set; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Geoffroy Jamgotchian {@literal } + * @author Nicolas Rol {@literal } + */ +class GzDirectoryDataSourceTest extends DirectoryDataSourceTest { + + @Override + @BeforeEach + void setUp() throws Exception { + super.setUp(); + compressionFormat = CompressionFormat.GZIP; + } + + @Test + @Override + void testConstructors() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Check constructors + checkDataSource(new GzDirectoryDataSource(testDir, "foo_bar", observer), observer); + checkDataSource(new GzDirectoryDataSource(testDir, "foo_bar"), null); + } + + private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver observer) { + assertEquals(testDir, dataSource.getDirectory()); + assertEquals(compressionFormat, dataSource.getCompressionFormat()); + assertEquals("foo_bar", dataSource.getBaseName()); + assertEquals(observer, dataSource.getObserver()); + } + + @Override + protected DataSource createDataSource() { + return new GzDirectoryDataSource(testDir, "foo"); + } + + @Override + protected DataSource createDataSource(DataSourceObserver observer) { + return new GzDirectoryDataSource(testDir, "foo", observer); + } + + static Stream provideArgumentsForWriteThenReadTest() { + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.GZIP), + Arguments.of("foo", CompressionFormat.GZIP), + Arguments.of("foo.v3", CompressionFormat.GZIP) + ); + } + + static Stream provideArgumentsForClassAndListingTest() { + Set listedFiles = Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", + "foo.bz2", "foo.txt.bz2", "foo.iidm.bz2", "foo.xiidm.bz2", "foo.v3.iidm.bz2", "foo.v3.bz2", "foo_bar.iidm.bz2", "foo_bar.bz2", + "foo.xz", "foo.txt.xz", "foo.iidm.xz", "foo.xiidm.xz", "foo.v3.iidm.xz", "foo.v3.xz", "foo_bar.iidm.xz", "foo_bar.xz", + "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst"); + Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.bz2", "foo_bar.bz2", "foo_bar.iidm.xz", "foo_bar.xz", "foo_bar.iidm.zst", "foo_bar.zst"); + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.GZIP, GzDirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo", CompressionFormat.GZIP, GzDirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo.v3", CompressionFormat.GZIP, GzDirectoryDataSource.class, + listedFiles, + listedBarFiles) + ); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/GzFileDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/GzFileDataSourceTest.java deleted file mode 100644 index d343bbd4fc1..00000000000 --- a/commons/src/test/java/com/powsybl/commons/datasource/GzFileDataSourceTest.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2016, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.commons.datasource; - -/** - * @author Geoffroy Jamgotchian {@literal } - */ -class GzFileDataSourceTest extends AbstractDataSourceTest { - - @Override - protected DataSource createDataSource() { - return new GzFileDataSource(testDir, getBaseName()); - } -} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/AbstractDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/MemDataSourceTest.java similarity index 87% rename from commons/src/test/java/com/powsybl/commons/datasource/AbstractDataSourceTest.java rename to commons/src/test/java/com/powsybl/commons/datasource/MemDataSourceTest.java index ae642a78cf9..419cd822b02 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/AbstractDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/MemDataSourceTest.java @@ -7,10 +7,12 @@ */ package com.powsybl.commons.datasource; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import com.google.common.io.ByteStreams; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; @@ -20,23 +22,15 @@ import java.nio.file.Files; import java.nio.file.Path; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.google.common.io.ByteStreams; -import com.google.common.jimfs.Configuration; -import com.google.common.jimfs.Jimfs; +import static org.junit.jupiter.api.Assertions.*; /** * @author Geoffroy Jamgotchian {@literal } */ -abstract class AbstractDataSourceTest { +class MemDataSourceTest { protected FileSystem fileSystem; - protected Path testDir; - private DataSource dataSource; @BeforeEach @@ -52,15 +46,17 @@ void tearDown() throws Exception { fileSystem.close(); } - protected String getBaseName() { - return "foo"; - } - protected boolean appendTest() { return true; } - protected abstract DataSource createDataSource(); + protected String getBaseName() { + return ""; // because undefined in a memory impl datasource + } + + protected DataSource createDataSource() { + return new MemDataSource(); + } @Test void baseNameTest() { @@ -95,7 +91,7 @@ private void writeThenReadTest(String suffix, String ext) throws IOException { for (String name : dataSource.listNames(".*")) { assertTrue(dataSource.exists(name)); try (InputStream is = dataSource.newInputStream(name)) { - // Ok, some content is available + assertNotNull(is); } catch (IOException x) { fail(name); } @@ -104,7 +100,7 @@ private void writeThenReadTest(String suffix, String ext) throws IOException { // check content is ok try (InputStream is = dataSource.newInputStream(suffix, ext)) { assertEquals("line1" + (appendTest() ? System.lineSeparator() + "line2" : ""), - new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8)); + new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8)); } try (InputStream is = dataSource.newInputStream("dummy.txt")) { assertEquals("otherline1", new String(ByteStreams.toByteArray(is), StandardCharsets.UTF_8)); @@ -117,5 +113,4 @@ void writeThenReadTest() throws IOException { writeThenReadTest("_baz", "bar"); writeThenReadTest("_baz", null); } - } diff --git a/commons/src/test/java/com/powsybl/commons/datasource/MemFileDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/MemFileDataSourceTest.java deleted file mode 100644 index afd4505e089..00000000000 --- a/commons/src/test/java/com/powsybl/commons/datasource/MemFileDataSourceTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2016, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.commons.datasource; - -/** - * @author Geoffroy Jamgotchian {@literal } - */ -class MemFileDataSourceTest extends AbstractDataSourceTest { - - @Override - protected String getBaseName() { - return ""; // because undefined in a memory impl datasource - } - - @Override - protected DataSource createDataSource() { - return new MemDataSource(); - } -} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSourceTest.java index 67c50ba1cab..3d79eabc81d 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSourceTest.java @@ -47,8 +47,8 @@ void tearDown() throws Exception { @Test void test() throws IOException { - ReadOnlyDataSource dataSource = new MultipleReadOnlyDataSource(new FileDataSource(testDir, "a"), - new FileDataSource(testDir, "b")); + ReadOnlyDataSource dataSource = new MultipleReadOnlyDataSource(new DirectoryDataSource(testDir, "a"), + new DirectoryDataSource(testDir, "b")); assertEquals("a", dataSource.getBaseName()); assertTrue(dataSource.exists(null, "txt")); assertFalse(dataSource.exists(null, "json")); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/XZDirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/XZDirectoryDataSourceTest.java new file mode 100644 index 00000000000..a670c4c19bd --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/XZDirectoryDataSourceTest.java @@ -0,0 +1,92 @@ +/** + * 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/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Set; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Olivier Bretteville {@literal } + * @author Nicolas Rol {@literal } + */ +class XZDirectoryDataSourceTest extends DirectoryDataSourceTest { + + @Override + @BeforeEach + void setUp() throws Exception { + super.setUp(); + compressionFormat = CompressionFormat.XZ; + } + + @Test + @Override + void testConstructors() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Check constructors + checkDataSource(new XZDirectoryDataSource(testDir, "foo_bar", observer), observer); + checkDataSource(new XZDirectoryDataSource(testDir, "foo_bar"), null); + } + + private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver observer) { + assertEquals(testDir, dataSource.getDirectory()); + assertEquals(compressionFormat, dataSource.getCompressionFormat()); + assertEquals("foo_bar", dataSource.getBaseName()); + assertEquals(observer, dataSource.getObserver()); + } + + @Override + protected boolean appendTest() { + // Append does not work with xz compression + return false; + } + + @Override + protected DataSource createDataSource() { + return new XZDirectoryDataSource(testDir, "foo"); + } + + @Override + protected DataSource createDataSource(DataSourceObserver observer) { + return new XZDirectoryDataSource(testDir, "foo", observer); + } + + static Stream provideArgumentsForWriteThenReadTest() { + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.XZ), + Arguments.of("foo", CompressionFormat.XZ), + Arguments.of("foo.v3", CompressionFormat.XZ) + ); + } + + static Stream provideArgumentsForClassAndListingTest() { + Set listedFiles = Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", + "foo.bz2", "foo.txt.bz2", "foo.iidm.bz2", "foo.xiidm.bz2", "foo.v3.iidm.bz2", "foo.v3.bz2", "foo_bar.iidm.bz2", "foo_bar.bz2", + "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst", + "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz"); + Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.bz2", "foo_bar.bz2", "foo_bar.iidm.zst", "foo_bar.zst", "foo_bar.iidm.gz", "foo_bar.gz"); + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.XZ, XZDirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo", CompressionFormat.XZ, XZDirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo.v3", CompressionFormat.XZ, XZDirectoryDataSource.class, + listedFiles, + listedBarFiles) + ); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/XZFileDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/XZFileDataSourceTest.java deleted file mode 100644 index 0bf55fb63b9..00000000000 --- a/commons/src/test/java/com/powsybl/commons/datasource/XZFileDataSourceTest.java +++ /dev/null @@ -1,24 +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/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.commons.datasource; - -/** - * @author Olivier Bretteville {@literal } - */ -class XZFileDataSourceTest extends AbstractDataSourceTest { - - @Override - protected boolean appendTest() { - return false; // FIXME append does not work with xz compression - } - - @Override - protected DataSource createDataSource() { - return new XZFileDataSource(testDir, getBaseName()); - } -} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java new file mode 100644 index 00000000000..5e72fafdb9b --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java @@ -0,0 +1,170 @@ +/** + * Copyright (c) 2016, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.Set; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Geoffroy Jamgotchian {@literal } + * @author Nicolas Rol {@literal } + */ +class ZipArchiveDataSourceTest extends AbstractArchiveDataSourceTest { + + private static final String WORK_DIR = "/work/"; + private static final String MAIN_EXT = "xml"; + private static final String BASENAME = "network"; + private static final String MAIN_FILE = BASENAME + "." + MAIN_EXT; + private static final String ZIP_FILENAME = MAIN_FILE + ".zip"; + private static final String ZIP_PATH = WORK_DIR + ZIP_FILENAME; + private static final String ADDITIONAL_SUFFIX = "_mapping"; + private static final String ADDITIONAL_EXT = "csv"; + private static final String ADDITIONAL_FILE = BASENAME + ADDITIONAL_SUFFIX + "." + ADDITIONAL_EXT; + private static final String UNRELATED_FILE = "other.de"; + + @Override + @BeforeEach + void setUp() throws Exception { + super.setUp(); + archiveWithSubfolders = "foo.iidm.zip"; + appendException = "append not supported in zip file data source"; + archiveFormat = ArchiveFormat.ZIP; + compressionFormat = CompressionFormat.ZIP; + } + + @Test + @Override + void testConstructors() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Check constructors + checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo", observer), "foo_bar.zip", "foo", observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo"), "foo_bar.zip", "foo", null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", observer), "foo.zip", "foo", observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo"), "foo.zip", "foo", null); + checkDataSource(new ZipArchiveDataSource(testDir.resolve("foo_bar.zip")), "foo_bar.zip", "foo_bar", null); + } + + private void checkDataSource(ZipArchiveDataSource dataSource, String zipFileName, String baseName, DataSourceObserver observer) { + assertEquals(testDir, dataSource.getDirectory()); + assertEquals(zipFileName, dataSource.getArchiveFilePath().getFileName().toString()); + assertEquals(baseName, dataSource.getBaseName()); + assertEquals(observer, dataSource.getObserver()); + } + + @Override + protected boolean appendTest() { + return false; + } + + @Override + protected DataSource createDataSource() { + return new ZipArchiveDataSource(testDir, "foo.zip", "foo"); + } + + @Override + protected DataSource createDataSource(DataSourceObserver observer) { + return new ZipArchiveDataSource(testDir, "foo", observer); + } + + static Stream provideArgumentsForWriteThenReadTest() { + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.ZIP), + Arguments.of("foo", CompressionFormat.ZIP), + Arguments.of("foo.v3", CompressionFormat.ZIP) + ); + } + + // Currently, the files are not filtered in the zip archive + static Stream provideArgumentsForClassAndListingTest() { + Set listedFiles = Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", "bar.iidm", "bar"); + Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "bar.iidm", "bar"); + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.ZIP, ZipArchiveDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo", CompressionFormat.ZIP, ZipArchiveDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo.v3", CompressionFormat.ZIP, ZipArchiveDataSource.class, + listedFiles, + listedBarFiles) + ); + } + + @Override + protected void createFiles(String fileName) throws IOException { + // Create the Zip archive and add the files + try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(fileSystem.getPath(fileName)))) { + filesInArchive.forEach(fileInArchive -> { + try { + ZipEntry e = new ZipEntry(fileInArchive); + out.putNextEntry(e); + byte[] data = "Test String".getBytes(); + out.write(data, 0, data.length); + out.closeEntry(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + }); + } + } + + @Test + void fakeZipTest() throws IOException { + Files.createFile(testDir.resolve("fake.zip")); + assertFalse(new ZipArchiveDataSource(testDir, "fake").exists("e")); + } + + @Test + void createZipDataSourceWithMoreThanOneDot() throws IOException { + try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(fileSystem.getPath(ZIP_PATH)))) { + // create an entry + ZipEntry e = new ZipEntry(UNRELATED_FILE); + out.putNextEntry(e); + byte[] data = "Test String".getBytes(); + out.write(data, 0, data.length); + + e = new ZipEntry(MAIN_FILE); + out.putNextEntry(e); + data = "Test String 2".getBytes(); + out.write(data, 0, data.length); + out.closeEntry(); + + e = new ZipEntry(ADDITIONAL_FILE); + out.putNextEntry(e); + data = "Test String 2".getBytes(); + out.write(data, 0, data.length); + out.closeEntry(); + + } + var workdirPath = fileSystem.getPath(WORK_DIR); + DataSource dataSource = DataSourceUtil.createDataSource(workdirPath, ZIP_FILENAME, null); + assertTrue(dataSource.exists(UNRELATED_FILE)); + assertFalse(dataSource.exists("not.zip")); + assertTrue(dataSource.exists(null, MAIN_EXT)); + assertTrue(dataSource.exists(ADDITIONAL_SUFFIX, ADDITIONAL_EXT)); + assertFalse(dataSource.exists("-not", "there")); + try (InputStream is = dataSource.newInputStream(UNRELATED_FILE)) { + assertEquals("Test String", new String(is.readAllBytes())); + } + } + +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ZipFileDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ZipFileDataSourceTest.java deleted file mode 100644 index 0b6f8c58874..00000000000 --- a/commons/src/test/java/com/powsybl/commons/datasource/ZipFileDataSourceTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2016, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.commons.datasource; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Geoffroy Jamgotchian {@literal } - */ -class ZipFileDataSourceTest extends AbstractDataSourceTest { - - private static final String WORK_DIR = "/work/"; - private static final String MAIN_EXT = "xml"; - private static final String BASENAME = "network"; - private static final String MAIN_FILE = BASENAME + "." + MAIN_EXT; - private static final String ZIP_FILENAME = MAIN_FILE + ".zip"; - private static final String ZIP_PATH = WORK_DIR + ZIP_FILENAME; - private static final String ADDITIONAL_SUFFIX = "_mapping"; - private static final String ADDITIONAL_EXT = "csv"; - private static final String ADDITIONAL_FILE = BASENAME + ADDITIONAL_SUFFIX + "." + ADDITIONAL_EXT; - private static final String UNRELATED_FILE = "other.de"; - - @Override - protected boolean appendTest() { - return false; - } - - @Override - protected DataSource createDataSource() { - return new ZipFileDataSource(testDir, getBaseName()); - } - - @Test - void fakeZipTest() throws IOException { - Files.createFile(testDir.resolve("fake.zip")); - assertFalse(new ZipFileDataSource(testDir, "fake").exists("e")); - } - - @Test - void createZipDataSourceWithMoreThanOneDot() throws IOException { - try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(fileSystem.getPath(ZIP_PATH)));) { - // create an entry - ZipEntry e = new ZipEntry(UNRELATED_FILE); - out.putNextEntry(e); - byte[] data = "Test String".getBytes(); - out.write(data, 0, data.length); - - e = new ZipEntry(MAIN_FILE); - out.putNextEntry(e); - data = "Test String 2".getBytes(); - out.write(data, 0, data.length); - out.closeEntry(); - - e = new ZipEntry(ADDITIONAL_FILE); - out.putNextEntry(e); - data = "Test String 2".getBytes(); - out.write(data, 0, data.length); - out.closeEntry(); - - } - var workdirPath = fileSystem.getPath(WORK_DIR); - DataSource dataSource = DataSourceUtil.createDataSource(workdirPath, ZIP_FILENAME, null); - assertTrue(dataSource.exists(UNRELATED_FILE)); - assertFalse(dataSource.exists("not.zip")); - assertTrue(dataSource.exists(null, MAIN_EXT)); - assertTrue(dataSource.exists(ADDITIONAL_SUFFIX, ADDITIONAL_EXT)); - assertFalse(dataSource.exists("-not", "there")); - try (InputStream is = dataSource.newInputStream(UNRELATED_FILE)) { - assertEquals("Test String", new String(is.readAllBytes())); - } - } - -} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ZstdDirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ZstdDirectoryDataSourceTest.java new file mode 100644 index 00000000000..5d8cfbc67b5 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/ZstdDirectoryDataSourceTest.java @@ -0,0 +1,86 @@ +/** + * 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/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Set; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Olivier Bretteville {@literal } + * @author Nicolas Rol {@literal } + */ +class ZstdDirectoryDataSourceTest extends DirectoryDataSourceTest { + + @Override + @BeforeEach + void setUp() throws Exception { + super.setUp(); + compressionFormat = CompressionFormat.ZSTD; + } + + @Test + @Override + void testConstructors() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Check constructors + checkDataSource(new ZstdDirectoryDataSource(testDir, "foo_bar", observer), observer); + checkDataSource(new ZstdDirectoryDataSource(testDir, "foo_bar"), null); + } + + private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver observer) { + assertEquals(testDir, dataSource.getDirectory()); + assertEquals(compressionFormat, dataSource.getCompressionFormat()); + assertEquals("foo_bar", dataSource.getBaseName()); + assertEquals(observer, dataSource.getObserver()); + } + + @Override + protected DataSource createDataSource() { + return new ZstdDirectoryDataSource(testDir, "foo"); + } + + @Override + protected DataSource createDataSource(DataSourceObserver observer) { + return new ZstdDirectoryDataSource(testDir, "foo", observer); + } + + static Stream provideArgumentsForWriteThenReadTest() { + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.ZSTD), + Arguments.of("foo", CompressionFormat.ZSTD), + Arguments.of("foo.v3", CompressionFormat.ZSTD) + ); + } + + static Stream provideArgumentsForClassAndListingTest() { + Set listedFiles = Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", + "foo.bz2", "foo.txt.bz2", "foo.iidm.bz2", "foo.xiidm.bz2", "foo.v3.iidm.bz2", "foo.v3.bz2", "foo_bar.iidm.bz2", "foo_bar.bz2", + "foo.xz", "foo.txt.xz", "foo.iidm.xz", "foo.xiidm.xz", "foo.v3.iidm.xz", "foo.v3.xz", "foo_bar.iidm.xz", "foo_bar.xz", + "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz"); + Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.bz2", "foo_bar.bz2", "foo_bar.iidm.xz", "foo_bar.xz", "foo_bar.iidm.gz", "foo_bar.gz"); + return Stream.of( + Arguments.of("foo.iidm", CompressionFormat.ZSTD, ZstdDirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo", CompressionFormat.ZSTD, ZstdDirectoryDataSource.class, + listedFiles, + listedBarFiles), + Arguments.of("foo.v3", CompressionFormat.ZSTD, ZstdDirectoryDataSource.class, + listedFiles, + listedBarFiles) + ); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ZstdFileDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ZstdFileDataSourceTest.java deleted file mode 100644 index 016cf483c2c..00000000000 --- a/commons/src/test/java/com/powsybl/commons/datasource/ZstdFileDataSourceTest.java +++ /dev/null @@ -1,19 +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/. - * SPDX-License-Identifier: MPL-2.0 - */ -package com.powsybl.commons.datasource; - -/** - * @author Olivier Bretteville {@literal } - */ -class ZstdFileDataSourceTest extends AbstractDataSourceTest { - - @Override - protected DataSource createDataSource() { - return new ZstdFileDataSource(testDir, getBaseName()); - } -} diff --git a/commons/src/test/resources/foo.iidm.zip b/commons/src/test/resources/foo.iidm.zip new file mode 100644 index 0000000000000000000000000000000000000000..37c5709db71d0266eb19fff06e8bbb197e0c96fa GIT binary patch literal 654 zcmWIWW@h1H00E}z+6XWMN^mjAFcg<2rRC?Oq!#IihHx@4GoNQmV*%pQ3T_5Q7K0^6 zGQmUu+>G{$+K6MOJFQuPW`M95iWzD7`SD4KReG73DY;Y(RH`Fb~8Spo@wSM%Cdq$_VHrMkYCC zT%jcaG!p~_82&nfXymYCg@hfNKatJD3^|B-j6jpNG`gUehZ>GRv$2FBve}pch-~&u vpxKDv1eyj4PCPEf3`b consumer, Consumer listener, NetworkFactory networkFactory, ReportNode reportNode) { diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Network.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Network.java index 8bae3870328..6a7d5eb1d05 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Network.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Network.java @@ -1524,7 +1524,7 @@ default void write(String format, Properties parameters, Path file) { * @param reportNode the reportNode used for functional logs */ default void write(ExportersLoader loader, String format, Properties parameters, String directory, String baseName, ReportNode reportNode) { - write(loader, format, parameters, new FileDataSource(Paths.get(directory), baseName), reportNode); + write(loader, format, parameters, new DirectoryDataSource(Paths.get(directory), baseName), reportNode); } default void write(ExportersLoader loader, String format, Properties parameters, String directory, String basename) { diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java index 34b41271586..1962f41b130 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java @@ -122,7 +122,7 @@ public void setUp() throws IOException { void backwardCompatibilityTest() throws IOException { // create network and datasource writeNetwork("/v_1_0.xiidm", IidmVersion.V_1_0, false); - DataSource dataSource = new FileDataSource(fileSystem.getPath("/"), "v_1_0"); + DataSource dataSource = new DirectoryDataSource(fileSystem.getPath("/"), "v_1_0"); // exists assertTrue(importer.exists(dataSource)); @@ -144,23 +144,23 @@ void testMetaInfos() { @Test void exists() { - assertTrue(importer.exists(new FileDataSource(fileSystem.getPath("/"), "test0"))); - assertTrue(importer.exists(new FileDataSource(fileSystem.getPath("/"), "test1"))); - assertTrue(importer.exists(new FileDataSource(fileSystem.getPath("/"), "test2"))); - assertFalse(importer.exists(new FileDataSource(fileSystem.getPath("/"), "test3"))); // wrong extension - assertFalse(importer.exists(new FileDataSource(fileSystem.getPath("/"), "test4"))); // does not exist - assertFalse(importer.exists(new FileDataSource(fileSystem.getPath("/"), "testDummy"))); // namespace URI is not defined + assertTrue(importer.exists(new DirectoryDataSource(fileSystem.getPath("/"), "test0"))); + assertTrue(importer.exists(new DirectoryDataSource(fileSystem.getPath("/"), "test1"))); + assertTrue(importer.exists(new DirectoryDataSource(fileSystem.getPath("/"), "test2"))); + assertFalse(importer.exists(new DirectoryDataSource(fileSystem.getPath("/"), "test3"))); // wrong extension + assertFalse(importer.exists(new DirectoryDataSource(fileSystem.getPath("/"), "test4"))); // does not exist + assertFalse(importer.exists(new DirectoryDataSource(fileSystem.getPath("/"), "testDummy"))); // namespace URI is not defined } @Test void copy() throws Exception { - importer.copy(new FileDataSource(fileSystem.getPath("/"), "test0"), new FileDataSource(fileSystem.getPath("/"), "test0_copy")); + importer.copy(new DirectoryDataSource(fileSystem.getPath("/"), "test0"), new DirectoryDataSource(fileSystem.getPath("/"), "test0_copy")); assertTrue(Files.exists(fileSystem.getPath("/test0_copy.xiidm"))); assertEquals(Files.readAllLines(fileSystem.getPath("/test0.xiidm"), StandardCharsets.UTF_8), Files.readAllLines(fileSystem.getPath("/test0_copy.xiidm"), StandardCharsets.UTF_8)); // test copy with id mapping file - importer.copy(new FileDataSource(fileSystem.getPath("/"), "test6"), new FileDataSource(fileSystem.getPath("/"), "test6_copy")); + importer.copy(new DirectoryDataSource(fileSystem.getPath("/"), "test6"), new DirectoryDataSource(fileSystem.getPath("/"), "test6_copy")); assertTrue(Files.exists(fileSystem.getPath("/test6_copy.xiidm"))); assertTrue(Files.exists(fileSystem.getPath("/test6_copy_mapping.csv"))); assertEquals(Files.readAllLines(fileSystem.getPath("/test6.xiidm"), StandardCharsets.UTF_8), @@ -172,24 +172,24 @@ void copy() throws Exception { @Test void importData() { // should be ok - assertNotNull(importer.importData(new FileDataSource(fileSystem.getPath("/"), "test0"), NetworkFactory.findDefault(), null)); + assertNotNull(importer.importData(new DirectoryDataSource(fileSystem.getPath("/"), "test0"), NetworkFactory.findDefault(), null)); // should fail because file that does not exist try { - importer.importData(new FileDataSource(fileSystem.getPath("/"), "test4"), NetworkFactory.findDefault(), null); + importer.importData(new DirectoryDataSource(fileSystem.getPath("/"), "test4"), NetworkFactory.findDefault(), null); fail(); } catch (RuntimeException ignored) { } // extension plugin will be not found but default option just warn - assertNotNull(importer.importData(new FileDataSource(fileSystem.getPath("/"), "test5"), NetworkFactory.findDefault(), null)); + assertNotNull(importer.importData(new DirectoryDataSource(fileSystem.getPath("/"), "test5"), NetworkFactory.findDefault(), null)); // extension plugin will be not found but option is set to throw an exception // (deprecated parameter name) Properties params = new Properties(); params.put("throwExceptionIfExtensionNotFound", "true"); try { - importer.importData(new FileDataSource(fileSystem.getPath("/"), "test5"), NetworkFactory.findDefault(), params); + importer.importData(new DirectoryDataSource(fileSystem.getPath("/"), "test5"), NetworkFactory.findDefault(), params); fail(); } catch (RuntimeException ignored) { } @@ -199,28 +199,28 @@ void importData() { Properties params2 = new Properties(); params2.put("iidm.import.xml.throw-exception-if-extension-not-found", "true"); try { - importer.importData(new FileDataSource(fileSystem.getPath("/"), "test5"), NetworkFactory.findDefault(), params2); + importer.importData(new DirectoryDataSource(fileSystem.getPath("/"), "test5"), NetworkFactory.findDefault(), params2); fail(); } catch (RuntimeException ignored) { } // read file with id mapping - Network network = importer.importData(new FileDataSource(fileSystem.getPath("/"), "test6"), NetworkFactory.findDefault(), params); + Network network = importer.importData(new DirectoryDataSource(fileSystem.getPath("/"), "test6"), NetworkFactory.findDefault(), params); assertNotNull(network.getSubstation("X1")); // and not P1 !!!!! - Network network2 = importer.importData(new FileDataSource(fileSystem.getPath("/"), "test7"), NetworkFactory.findDefault(), null); + Network network2 = importer.importData(new DirectoryDataSource(fileSystem.getPath("/"), "test7"), NetworkFactory.findDefault(), null); assertNotNull(network2.getSubstation("P1")); } @Test void importDataReportNodeTest() throws IOException { - FileDataSource dataSource = new FileDataSource(fileSystem.getPath("/"), "test8"); + DirectoryDataSource dataSource = new DirectoryDataSource(fileSystem.getPath("/"), "test8"); importDataAndTestReportNode("/importXmlReport.txt", dataSource); } @Test void importDataReportNodeExtensionNotFoundTest() throws IOException { - FileDataSource dataSource = new FileDataSource(fileSystem.getPath("/"), "test5"); + DirectoryDataSource dataSource = new DirectoryDataSource(fileSystem.getPath("/"), "test5"); importDataAndTestReportNode("/importXmlReportExtensionsNotFound.txt", dataSource); } diff --git a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerExporterTest.java b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerExporterTest.java index ca46b701844..b1c5b3e2829 100644 --- a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerExporterTest.java +++ b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerExporterTest.java @@ -15,7 +15,7 @@ import com.powsybl.cgmes.conformity.CgmesConformity1ModifiedCatalog; import com.powsybl.commons.config.InMemoryPlatformConfig; import com.powsybl.commons.config.PlatformConfig; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.datasource.MemDataSource; import com.powsybl.commons.test.AbstractSerDeTest; import com.powsybl.commons.test.ComparisonUtils; @@ -142,7 +142,7 @@ void testCase30ConsideringBaseVoltage() throws IOException { Properties properties = new Properties(); properties.put("matpower.import.ignore-base-voltage", false); - Network network = new MatpowerImporter().importData(new FileDataSource(tmpDir, caseId), NetworkFactory.findDefault(), properties); + Network network = new MatpowerImporter().importData(new DirectoryDataSource(tmpDir, caseId), NetworkFactory.findDefault(), properties); exportToMatAndCompareTo(network, "/ieee30-considering-base-voltage.json"); } @@ -276,7 +276,7 @@ void testExportCase9DcLine() throws IOException { Path matFile = tmpDir.resolve(caseId + ".mat"); MatpowerWriter.write(matpowerModel, matFile, true); - var network = new MatpowerImporter().importData(new FileDataSource(tmpDir, caseId), NetworkFactory.findDefault(), null); + var network = new MatpowerImporter().importData(new DirectoryDataSource(tmpDir, caseId), NetworkFactory.findDefault(), null); exportToMatAndCompareTo(network, "/t_case9_dcline_exported.json"); } diff --git a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerImporterTest.java b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerImporterTest.java index 84b593c55bb..7218ca727cb 100644 --- a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerImporterTest.java +++ b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerImporterTest.java @@ -7,7 +7,7 @@ */ package com.powsybl.matpower.converter; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.test.AbstractSerDeTest; import com.powsybl.iidm.network.Importer; import com.powsybl.iidm.network.Network; @@ -61,8 +61,8 @@ void copyTest() throws IOException { MatpowerModel model = MatpowerModelFactory.create9(); Path matpowerBinCase = tmpDir.resolve(model.getCaseName() + ".mat"); MatpowerWriter.write(model, matpowerBinCase, true); - new MatpowerImporter().copy(new FileDataSource(tmpDir, model.getCaseName()), - new FileDataSource(tmpDir, "copy")); + new MatpowerImporter().copy(new DirectoryDataSource(tmpDir, model.getCaseName()), + new DirectoryDataSource(tmpDir, "copy")); assertTrue(Files.exists(tmpDir.resolve("copy.mat"))); } @@ -71,8 +71,8 @@ void existsTest() throws IOException { MatpowerModel model = MatpowerModelFactory.create118(); Path matpowerBinCase = tmpDir.resolve(model.getCaseName() + ".mat"); MatpowerWriter.write(model, matpowerBinCase, true); - assertTrue(new MatpowerImporter().exists(new FileDataSource(tmpDir, model.getCaseName()))); - assertFalse(new MatpowerImporter().exists(new FileDataSource(tmpDir, "doesnotexist"))); + assertTrue(new MatpowerImporter().exists(new DirectoryDataSource(tmpDir, model.getCaseName()))); + assertFalse(new MatpowerImporter().exists(new DirectoryDataSource(tmpDir, "doesnotexist"))); } @Test @@ -152,7 +152,7 @@ void testCase9DcLine() throws IOException { @Test void testNonexistentCase() { - assertThrows(UncheckedIOException.class, () -> testNetwork(new MatpowerImporter().importData(new FileDataSource(tmpDir, "unknown"), NetworkFactory.findDefault(), null))); + assertThrows(UncheckedIOException.class, () -> testNetwork(new MatpowerImporter().importData(new DirectoryDataSource(tmpDir, "unknown"), NetworkFactory.findDefault(), null))); } private void testCase(MatpowerModel model) throws IOException { @@ -164,7 +164,7 @@ private void testCase(MatpowerModel model, Properties properties) throws IOExcep Path matFile = tmpDir.resolve(caseId + ".mat"); MatpowerWriter.write(model, matFile, true); - Network network = new MatpowerImporter().importData(new FileDataSource(tmpDir, caseId), NetworkFactory.findDefault(), properties); + Network network = new MatpowerImporter().importData(new DirectoryDataSource(tmpDir, caseId), NetworkFactory.findDefault(), properties); testNetwork(network, caseId); } @@ -190,7 +190,7 @@ private void testCaseSolved(MatpowerModel model) throws IOException { Path matFile = tmpDir.resolve(caseId + ".mat"); MatpowerWriter.write(model, matFile, true); - Network network = new MatpowerImporter().importData(new FileDataSource(tmpDir, caseId), NetworkFactory.findDefault(), null); + Network network = new MatpowerImporter().importData(new DirectoryDataSource(tmpDir, caseId), NetworkFactory.findDefault(), null); testSolved(network); } diff --git a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerRoundTripTest.java b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerRoundTripTest.java index 4cef99bbef4..898d305cf9f 100644 --- a/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerRoundTripTest.java +++ b/matpower/matpower-converter/src/test/java/com/powsybl/matpower/converter/MatpowerRoundTripTest.java @@ -9,7 +9,7 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.matpower.model.MatpowerModel; @@ -64,8 +64,8 @@ void test() { Network network = EurostagTutorialExample1Factory.create(); Properties parameters = new Properties(); parameters.setProperty("matpower.import.ignore-base-voltage", "false"); - new MatpowerExporter().export(network, parameters, new FileDataSource(dir, "test")); - Network network2 = new MatpowerImporter().importData(new FileDataSource(dir, "test"), NetworkFactory.findDefault(), parameters); + new MatpowerExporter().export(network, parameters, new DirectoryDataSource(dir, "test")); + Network network2 = new MatpowerImporter().importData(new DirectoryDataSource(dir, "test"), NetworkFactory.findDefault(), parameters); assertEquals(calculateRatio(network, "NGEN_NHV1"), calculateRatio(network2, "TWT-1-2"), 1e-16); assertEquals(calculateRatio(network, "NHV2_NLOAD"), calculateRatio(network2, "TWT-3-4"), 1e-16); } @@ -77,10 +77,10 @@ void testRoundTripDcLines() throws IOException { Path matFile = dir.resolve(caseId + ".mat"); MatpowerWriter.write(matpowerModel, matFile, true); - var network = new MatpowerImporter().importData(new FileDataSource(dir, caseId), NetworkFactory.findDefault(), null); + var network = new MatpowerImporter().importData(new DirectoryDataSource(dir, caseId), NetworkFactory.findDefault(), null); - new MatpowerExporter().export(network, null, new FileDataSource(dir, "test")); - Network network1 = new MatpowerImporter().importData(new FileDataSource(dir, "test"), NetworkFactory.findDefault(), null); + new MatpowerExporter().export(network, null, new DirectoryDataSource(dir, "test")); + Network network1 = new MatpowerImporter().importData(new DirectoryDataSource(dir, "test"), NetworkFactory.findDefault(), null); assertEquals(network.getHvdcLineCount(), network1.getHvdcLineCount()); assertTrue(network.getHvdcLineStream().allMatch(hvdcLine -> existHvdcLineInTheOtherNetworkAndIsEqual(network1, hvdcLine))); diff --git a/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java b/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java index edab8f77716..6c2cf66ac18 100644 --- a/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java +++ b/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java @@ -7,7 +7,7 @@ */ package com.powsybl.powerfactory.converter; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.datasource.ResourceDataSource; import com.powsybl.commons.datasource.ResourceSet; import com.powsybl.commons.test.AbstractSerDeTest; @@ -58,11 +58,11 @@ void testExistsAndCopy() throws IOException { assertTrue(studyCase.isPresent()); PowerFactoryImporter importer = new PowerFactoryImporter(); - assertTrue(importer.exists(new FileDataSource(fileSystem.getPath("/work"), "ieee14"))); - assertFalse(importer.exists(new FileDataSource(fileSystem.getPath("/work"), "error"))); + assertTrue(importer.exists(new DirectoryDataSource(fileSystem.getPath("/work"), "ieee14"))); + assertFalse(importer.exists(new DirectoryDataSource(fileSystem.getPath("/work"), "error"))); - importer.copy(new FileDataSource(fileSystem.getPath("/work"), "ieee14"), - new FileDataSource(fileSystem.getPath("/work"), "ieee14-copy")); + importer.copy(new DirectoryDataSource(fileSystem.getPath("/work"), "ieee14"), + new DirectoryDataSource(fileSystem.getPath("/work"), "ieee14-copy")); assertTrue(Files.exists(fileSystem.getPath("/work/ieee14-copy.dgs"))); } diff --git a/psse/psse-converter/src/test/java/com/powsybl/psse/converter/PsseExporterTest.java b/psse/psse-converter/src/test/java/com/powsybl/psse/converter/PsseExporterTest.java index 919c66ce9d6..0e2cfd17d79 100644 --- a/psse/psse-converter/src/test/java/com/powsybl/psse/converter/PsseExporterTest.java +++ b/psse/psse-converter/src/test/java/com/powsybl/psse/converter/PsseExporterTest.java @@ -17,7 +17,7 @@ import com.google.common.io.ByteStreams; import com.powsybl.commons.test.AbstractSerDeTest; import com.powsybl.commons.datasource.DataSource; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.datasource.ResourceDataSource; import com.powsybl.commons.datasource.ResourceSet; @@ -74,7 +74,7 @@ private void exportTest(Network network, String baseName, String fileName) throw Path file = fileSystem.getPath(pathName + fileName); Properties properties = null; - DataSource dataSource = new FileDataSource(path, baseName); + DataSource dataSource = new DirectoryDataSource(path, baseName); new PsseExporter().export(network, properties, dataSource); try (InputStream is = Files.newInputStream(file)) { diff --git a/psse/psse-model/src/test/java/com/powsybl/psse/model/PsseRawDataTest.java b/psse/psse-model/src/test/java/com/powsybl/psse/model/PsseRawDataTest.java index 5ce3be2021c..5c4122458bb 100644 --- a/psse/psse-model/src/test/java/com/powsybl/psse/model/PsseRawDataTest.java +++ b/psse/psse-model/src/test/java/com/powsybl/psse/model/PsseRawDataTest.java @@ -16,7 +16,7 @@ import com.google.common.io.ByteStreams; import com.powsybl.commons.test.AbstractSerDeTest; import com.powsybl.commons.test.TestUtil; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.datasource.ResourceDataSource; import com.powsybl.commons.datasource.ResourceSet; @@ -504,7 +504,7 @@ void ieee14BusWriteTest() throws IOException { PowerFlowRawData33 rawData33 = new PowerFlowRawData33(); PssePowerFlowModel rawData = rawData33.read(ieee14Raw(), "raw", context); assertNotNull(rawData); - rawData33.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_exported")); + rawData33.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_14_bus_exported.raw"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_14_bus_exported.raw"), is); } @@ -517,7 +517,7 @@ void ieee14BusRev35WriteTest() throws IOException { PssePowerFlowModel rawData = rawData35.read(ieee14Raw35(), "raw", context); assertNotNull(rawData); - rawData35.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_rev35_exported")); + rawData35.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_rev35_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_14_bus_rev35_exported.raw"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_14_bus_rev35_exported.raw"), is); } @@ -530,7 +530,7 @@ void minimalExampleRawxWriteTest() throws IOException { PssePowerFlowModel rawData = rawXData35.read(minimalRawx(), "rawx", context); assertNotNull(rawData); - rawXData35.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "MinimalExample_exported")); + rawXData35.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "MinimalExample_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "MinimalExample_exported.rawx"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "MinimalExample_exported.rawx"), is); } @@ -543,7 +543,7 @@ void ieee14BusRev35RawxWriteTest() throws IOException { PssePowerFlowModel rawData = rawXData35.read(ieee14Rawx35(), "rawx", context); assertNotNull(rawData); - rawXData35.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_rev35_exported")); + rawXData35.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_rev35_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_14_bus_rev35_exported.rawx"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_14_bus_rev35_exported.rawx"), is); } @@ -556,7 +556,7 @@ void ieee14WhitespaceAsDelimiterWriteTest() throws IOException { PowerFlowRawData33 rawData33 = new PowerFlowRawData33(); PssePowerFlowModel rawData = rawData33.read(ieee14WhitespaceAsDelimiterRaw(), "raw", context); assertNotNull(rawData); - rawData33.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_whitespaceAsDelimiter_exported")); + rawData33.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_whitespaceAsDelimiter_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_14_bus_whitespaceAsDelimiter_exported.raw"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_14_bus_whitespaceAsDelimiter_exported.raw"), is); } @@ -569,7 +569,7 @@ void ieee14IsolatedBusesWriteTest() throws IOException { PowerFlowRawData33 rawData33 = new PowerFlowRawData33(); PssePowerFlowModel rawData = rawData33.read(ieee14IsolatedBusesRaw(), "raw", context); assertNotNull(rawData); - rawData33.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_14_isolated_buses_exported")); + rawData33.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_14_isolated_buses_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_14_isolated_buses_exported.raw"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_14_isolated_buses_exported.raw"), is); } @@ -639,7 +639,7 @@ void ieee24BusWriteTest() throws IOException { PssePowerFlowModel rawData = rawData33.read(ieee24Raw(), "raw", context); assertNotNull(rawData); - rawData33.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_24_bus_exported")); + rawData33.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_24_bus_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_24_bus_exported.raw"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_24_bus_exported.raw"), is); } @@ -652,7 +652,7 @@ void ieee24BusRev35WriteTest() throws IOException { PssePowerFlowModel rawData = rawData35.read(ieee24Raw35(), "raw", context); assertNotNull(rawData); - rawData35.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_24_bus_rev35_exported")); + rawData35.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_24_bus_rev35_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_24_bus_rev35_exported.raw"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_24_bus_rev35_exported.raw"), is); } @@ -665,7 +665,7 @@ void ieee24BusRev35RawxWriteTest() throws IOException { PssePowerFlowModel rawData = rawxData35.read(ieee24Rawx35(), "rawx", context); assertNotNull(rawData); - rawxData35.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_24_bus_rev35_exported")); + rawxData35.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_24_bus_rev35_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_24_bus_rev35_exported.rawx"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_24_bus_rev35_exported.rawx"), is); } @@ -726,7 +726,7 @@ void ieee14BusCompletedWriteTest() throws IOException { PssePowerFlowModel rawData = rawData33.read(ieee14CompletedRaw(), "raw", context); assertNotNull(rawData); - rawData33.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_completed_exported")); + rawData33.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_completed_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_14_bus_completed_exported.raw"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_14_bus_completed_exported.raw"), is); } @@ -739,7 +739,7 @@ void ieee14BusCompletedRev35WriteTest() throws IOException { PssePowerFlowModel rawData = rawData35.read(ieee14CompletedRaw35(), "raw", context); assertNotNull(rawData); - rawData35.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_completed_rev35_exported")); + rawData35.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_completed_rev35_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_14_bus_completed_rev35_exported.raw"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_14_bus_completed_rev35_exported.raw"), is); } @@ -752,7 +752,7 @@ void ieee14BusCompletedRev35RawxWriteTest() throws IOException { PssePowerFlowModel rawData = rawxData35.read(ieee14CompletedRawx35(), "rawx", context); assertNotNull(rawData); - rawxData35.write(rawData, context, new FileDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_completed_rev35_exported")); + rawxData35.write(rawData, context, new DirectoryDataSource(fileSystem.getPath("/work/"), "IEEE_14_bus_completed_rev35_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "IEEE_14_bus_completed_rev35_exported.rawx"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "IEEE_14_bus_completed_rev35_exported.rawx"), is); } @@ -784,7 +784,7 @@ void exampleVersion32WriteTest() throws IOException { assertNotNull(rawData); rawData32.write(rawData, context, - new FileDataSource(fileSystem.getPath("/work/"), "ExampleVersion32_exported")); + new DirectoryDataSource(fileSystem.getPath("/work/"), "ExampleVersion32_exported")); try (InputStream is = Files.newInputStream(fileSystem.getPath("/work/", "ExampleVersion32_exported.raw"))) { assertTxtEquals(getClass().getResourceAsStream("/" + "ExampleVersion32_exported.raw"), is); } diff --git a/triple-store/triple-store-test/src/test/java/com/powsybl/triplestore/test/ExportTest.java b/triple-store/triple-store-test/src/test/java/com/powsybl/triplestore/test/ExportTest.java index db2bcd815a1..6f5023e8888 100644 --- a/triple-store/triple-store-test/src/test/java/com/powsybl/triplestore/test/ExportTest.java +++ b/triple-store/triple-store-test/src/test/java/com/powsybl/triplestore/test/ExportTest.java @@ -10,7 +10,7 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import com.powsybl.commons.datasource.DataSource; -import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.commons.datasource.DirectoryDataSource; import com.powsybl.triplestore.api.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -109,7 +109,7 @@ void test() throws IOException { checkRepository(exportTripleStore, baseVoltageMasterResourceId); // export triple store - DataSource dataSource = new FileDataSource(exportFolder, networkId + "_" + implementation); + DataSource dataSource = new DirectoryDataSource(exportFolder, networkId + "_" + implementation); exportTripleStore.write(dataSource); // create import triple store From 6612eea7b047bf770146260af3d3e4b70646a861 Mon Sep 17 00:00:00 2001 From: Coline Piloquet <55250145+colinepiloquet@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:09:10 +0200 Subject: [PATCH 35/57] [Short-circuit API] Add some javadoc (#3096) Signed-off-by: Coline PILOQUET --- .../com/powsybl/shortcircuit/BranchFault.java | 12 +++-- .../com/powsybl/shortcircuit/BusFault.java | 3 +- .../shortcircuit/FailedFaultResult.java | 2 + .../java/com/powsybl/shortcircuit/Fault.java | 47 ++++++++++++++++--- .../powsybl/shortcircuit/FaultParameters.java | 15 ++++++ .../com/powsybl/shortcircuit/FaultResult.java | 7 ++- .../powsybl/shortcircuit/FeederResult.java | 2 + .../shortcircuit/FortescueFaultResult.java | 2 +- .../shortcircuit/FortescueFeederResult.java | 2 + .../FortescueShortCircuitBusResults.java | 2 + .../powsybl/shortcircuit/FortescueValue.java | 40 ++++++++++++++++ .../InitialVoltageProfileMode.java | 17 ++++++- .../shortcircuit/MagnitudeFaultResult.java | 2 +- .../shortcircuit/MagnitudeFeederResult.java | 2 + .../MagnitudeShortCircuitBusResults.java | 2 + .../shortcircuit/ShortCircuitBusResults.java | 2 + .../shortcircuit/ShortCircuitConstants.java | 2 + .../shortcircuit/ShortCircuitParameters.java | 11 ++++- .../com/powsybl/shortcircuit/StudyType.java | 15 ++++++ 19 files changed, 166 insertions(+), 21 deletions(-) diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/BranchFault.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/BranchFault.java index b6fb7456102..7d15a9c1fbd 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/BranchFault.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/BranchFault.java @@ -8,14 +8,12 @@ package com.powsybl.shortcircuit; /** - * Class to describe the characteristics of the fault to be simulated. - * Used for elementary short-circuit analysis only. + * Class to describe the characteristics of a fault that occurs on a branch and that is to be simulated. * * @author Anne Tilloy {@literal } */ public class BranchFault extends AbstractFault { - // Location of the fault in % of the branch length (with side ONE as reference). private final double proportionalLocation; public BranchFault(String id, String elementId, double r, double x, ConnectionType connection, FaultType faultType, double proportionalLocation) { @@ -25,12 +23,12 @@ public BranchFault(String id, String elementId, double r, double x, ConnectionTy } public BranchFault(String id, String elementId, double r, double x, double proportionalLocation) { - // Here the elementId is the id of a bus from the bus view. + // Here the elementId is the id of a branch. this(id, elementId, r, x, ConnectionType.SERIES, FaultType.THREE_PHASE, proportionalLocation); } public BranchFault(String id, String elementId, double proportionalLocation) { - // Here the elementId is the id of a bus from the bus view. + // Here the elementId is the id of a branch. this(id, elementId, 0.0, 0.0, ConnectionType.SERIES, FaultType.THREE_PHASE, proportionalLocation); } @@ -39,6 +37,10 @@ public Type getType() { return Type.BRANCH; } + /** + * Get the location of the fault on the branch + * @return the location of the fault in % of the branch length (with side ONE as reference). + */ public double getProportionalLocation() { return this.proportionalLocation; } diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/BusFault.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/BusFault.java index fefecf88c36..13880b30988 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/BusFault.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/BusFault.java @@ -8,8 +8,7 @@ package com.powsybl.shortcircuit; /** - * Class to describe the characteristics of the fault to be simulated. - * Used for elementary short-circuit analysis only. + * Class to describe the characteristics of a fault that occurs on a bus and that is to be simulated. * * @author Anne Tilloy {@literal } */ diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FailedFaultResult.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FailedFaultResult.java index f7eed04af6e..6ca4bf13eb5 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FailedFaultResult.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FailedFaultResult.java @@ -8,6 +8,8 @@ package com.powsybl.shortcircuit; /** + * A class to represent the result if the analysis has failed. + * * @author Coline Piloquet {@literal } */ public class FailedFaultResult extends AbstractFaultResult { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/Fault.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/Fault.java index 3cd1407aad2..7c09ba6fbe3 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/Fault.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/Fault.java @@ -21,25 +21,30 @@ /** * Interface to describe the characteristics of the fault to be simulated. - * Used for elementary short-circuit analysis only. * * @author Anne Tilloy {@literal } */ public interface Fault { - // Type of fault (use for downcast & serialize/deserialize) + /** + * Type of fault (use for downcast & serialize/deserialize). + */ enum Type { BUS, BRANCH } - // How the fault impedance and resistance are associated. + /** + * How the fault impedance and resistance are connected to the ground. + */ enum ConnectionType { SERIES, PARALLEL, } - // What kind of fault is simulated + /** + * The type of fault being simulated. + */ enum FaultType { THREE_PHASE, SINGLE_PHASE, @@ -47,27 +52,50 @@ enum FaultType { //TODO : add the numbers of the phase for two and single phase - // The fault id. + /** + * The ID of the fault. + */ String getId(); - // The equipment or bus id where the fault is simulated. + /** + * The ID of the equipment or bus associated to this fault. + */ String getElementId(); - // Characteristics of the short circuit to ground. + /** + * The resistance of the fault to the ground. The default is zero Ohms. + */ double getRToGround(); + /** + * The reactance of the fault to the ground. The default is zero Ohms. + */ double getXToGround(); + /** + * The type of the element associated to the fault: can be BUS or BRANCH. + */ Type getType(); + /** + * How the fault resistance and reactance are connected to the ground. Can be SERIES or PARALLEL. + */ ConnectionType getConnectionType(); + /** + * The type of fault occurring on the network element: can be THREE-PHASE or SINGLE-PHASE. + */ FaultType getFaultType(); private static ObjectMapper createObjectMapper() { return JsonUtil.createObjectMapper().registerModule(new ShortCircuitAnalysisJsonModule()); } + /** + * Writes a list of faults to a JSON file + * @param faults the list of faults + * @param jsonFile the path to the JSON file + */ static void write(List faults, Path jsonFile) { try (OutputStream out = Files.newOutputStream(jsonFile)) { createObjectMapper().writerWithDefaultPrettyPrinter().writeValue(out, faults); @@ -76,6 +104,11 @@ static void write(List faults, Path jsonFile) { } } + /** + * Reads a JSON file and creates the associated list of faults + * @param jsonFile the path to the existing JSON file + * @return a list of faults + */ static List read(Path jsonFile) { try (InputStream is = Files.newInputStream(jsonFile)) { return createObjectMapper().readerForListOf(Fault.class).readValue(is); diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FaultParameters.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FaultParameters.java index 79d3735ddf4..bb39bd6be56 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FaultParameters.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FaultParameters.java @@ -27,6 +27,8 @@ import static com.powsybl.shortcircuit.VoltageRange.checkVoltageRange; /** + * Class to override general short-circuit analysis parameters and make them specific to a particular fault. + * * @author Thomas Adam {@literal } */ public class FaultParameters { @@ -241,6 +243,11 @@ private static ObjectMapper createObjectMapper() { return JsonUtil.createObjectMapper().registerModule(new ShortCircuitAnalysisJsonModule()); } + /** + * Writes a list of FaultParameters to a JSON file + * @param parameters the list of FaultParameters + * @param jsonFile the path to the JSON file + */ public static void write(List parameters, Path jsonFile) { try (OutputStream out = Files.newOutputStream(jsonFile)) { createObjectMapper().writerWithDefaultPrettyPrinter().writeValue(out, parameters); @@ -249,6 +256,11 @@ public static void write(List parameters, Path jsonFile) { } } + /** + * Reads a JSON file and creates the associated list of FaultParameters + * @param jsonFile the path to the JSON file + * @return a list of FaultParameters + */ public static List read(Path jsonFile) { try (InputStream is = Files.newInputStream(jsonFile)) { return createObjectMapper().readerForListOf(FaultParameters.class).readValue(is); @@ -257,6 +269,9 @@ public static List read(Path jsonFile) { } } + /** + * Method used to validate FaultParameters. The voltage ranges should be defined if the parameter initialVoltageProfileMode is set to CONFIGURED. + */ public void validate() { if (initialVoltageProfileMode == InitialVoltageProfileMode.CONFIGURED && (voltageRanges == null || voltageRanges.isEmpty())) { throw new PowsyblException("Configured initial voltage profile but nominal voltage ranges with associated coefficients are missing."); diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FaultResult.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FaultResult.java index f9c57c56432..a13dafed7ef 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FaultResult.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FaultResult.java @@ -14,13 +14,18 @@ import java.util.List; /** + * Interface to describe the result of the short-circuit analysis for a given fault. + * * @author Coline Piloquet {@literal } */ public interface FaultResult extends Extendable { + /** + * The status of the computation. + */ enum Status { /** - * The computation went ok and no error were returned + * The computation went ok and no error was returned */ SUCCESS, /** diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FeederResult.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FeederResult.java index a3ef82cd7b7..84f8588190b 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FeederResult.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FeederResult.java @@ -13,6 +13,8 @@ import javax.annotation.Nullable; /** + * Interface to describe the contribution of a feeder to the short-circuit current after the analysis. + * * @author Coline Piloquet {@literal } */ public interface FeederResult { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueFaultResult.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueFaultResult.java index b2b3039319d..67bbe1bc1c5 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueFaultResult.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueFaultResult.java @@ -14,7 +14,7 @@ import java.util.List; /** - * Results for one fault computation with currents and voltage on the three phases. + * Results of the short-circuit calculation with the voltage and currents detailed on the three phases. * * @author Coline Piloquet {@literal } */ diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueFeederResult.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueFeederResult.java index 2d0da7f18e6..f060d66f9e9 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueFeederResult.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueFeederResult.java @@ -10,6 +10,8 @@ import com.powsybl.iidm.network.ThreeSides; /** + * Result detailed on the three phases for a feeder contributing to the short-circuit current. + * * @author Coline Piloquet {@literal } */ public class FortescueFeederResult extends AbstractFeederResult { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueShortCircuitBusResults.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueShortCircuitBusResults.java index 4b8ed0e6a7b..1dd32987f89 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueShortCircuitBusResults.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueShortCircuitBusResults.java @@ -8,6 +8,8 @@ package com.powsybl.shortcircuit; /** + * Results detailed on the three phases of the voltages on a bus. + * * @author Coline Piloquet {@literal } */ public class FortescueShortCircuitBusResults extends AbstractShortCircuitBusResults { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueValue.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueValue.java index 686384dd8a1..ed8697503f8 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueValue.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/FortescueValue.java @@ -39,26 +39,44 @@ public ThreePhaseValue(double magnitudeA, double magnitudeB, double magnitudeC, this.angleC = angleC; } + /** + * The magnitude on phase A + */ public double getMagnitudeA() { return magnitudeA; } + /** + * The magnitude on phase B + */ public double getMagnitudeB() { return magnitudeB; } + /** + * The magnitude on phase C + */ public double getMagnitudeC() { return magnitudeC; } + /** + * The angle on phase A + */ public double getAngleA() { return angleA; } + /** + * The angle on phase B + */ public double getAngleB() { return angleB; } + /** + * The angle on phase C + */ public double getAngleC() { return angleC; } @@ -89,30 +107,52 @@ public FortescueValue(double positiveMagnitude) { this(positiveMagnitude, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN); } + /** + * The magnitude on the positive sequence + */ public double getPositiveMagnitude() { return positiveMagnitude; } + /** + * The magnitude on the zero sequence + */ public double getZeroMagnitude() { return zeroMagnitude; } + /** + * The magnitude on the negative sequence + */ public double getNegativeMagnitude() { return negativeMagnitude; } + /** + * The angle on the positive sequence + */ public double getPositiveAngle() { return positiveAngle; } + /** + * The angle on the zero sequence + */ public double getZeroAngle() { return zeroAngle; } + /** + * The angle on the negative sequence + */ public double getNegativeAngle() { return negativeAngle; } + /** + * Convert the value from the positive, zero and negative sequence to the A, B and C phase components. + * @return the three phase components. + */ public ThreePhaseValue toThreePhaseValue() { // [G1] [ 1 1 1 ] [Gh] diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/InitialVoltageProfileMode.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/InitialVoltageProfileMode.java index cca84f64fd2..5e2511a592f 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/InitialVoltageProfileMode.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/InitialVoltageProfileMode.java @@ -8,11 +8,24 @@ package com.powsybl.shortcircuit; /** + * The initial voltage profile to consider for the computation. + * * @author Coline Piloquet {@literal } */ public enum InitialVoltageProfileMode { + /** + * The nominal values of the voltage are used. + */ NOMINAL, - CONFIGURED, // Voltage profile given by the user - PREVIOUS_VALUE // Voltage profile from the loadflow + + /** + * The user gives the voltage profile. + */ + CONFIGURED, + + /** + * The voltage profile used is the one calculated by the load flow calculation. + */ + PREVIOUS_VALUE } diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeFaultResult.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeFaultResult.java index ce4af0ebc71..981df0864ec 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeFaultResult.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeFaultResult.java @@ -14,7 +14,7 @@ import java.util.List; /** - * Results for one fault computation with current magnitude. + * Results for one fault with three-phase current magnitude. * * @author Coline Piloquet {@literal } */ diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeFeederResult.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeFeederResult.java index 9b3d1012fb6..3bc3000c8a7 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeFeederResult.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeFeederResult.java @@ -10,6 +10,8 @@ import com.powsybl.iidm.network.ThreeSides; /** + * Three-phase current of a feeder contributing to the short-circuit current. + * * @author Coline Piloquet {@literal } */ public class MagnitudeFeederResult extends AbstractFeederResult { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeShortCircuitBusResults.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeShortCircuitBusResults.java index be9f4ea801e..27f161a9392 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeShortCircuitBusResults.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/MagnitudeShortCircuitBusResults.java @@ -8,6 +8,8 @@ package com.powsybl.shortcircuit; /** + * Three-phase voltage results on a bus after the short-circuit computation. + * * @author Coline Piloquet {@literal } */ public class MagnitudeShortCircuitBusResults extends AbstractShortCircuitBusResults { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitBusResults.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitBusResults.java index 9c6efacb33c..2ebf22da5d6 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitBusResults.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitBusResults.java @@ -8,6 +8,8 @@ package com.powsybl.shortcircuit; /** + * An interface describing the voltage results on a bus. + * * @author Coline Piloquet {@literal } */ public interface ShortCircuitBusResults { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitConstants.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitConstants.java index 79b5de910fc..c487ef37cc7 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitConstants.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitConstants.java @@ -8,6 +8,8 @@ package com.powsybl.shortcircuit; /** + * Some constants related to the short-circuit API. Includes the default values of parameters. + * * @author Coline Piloquet {@literal } */ public final class ShortCircuitConstants { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitParameters.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitParameters.java index 015d13ea5a2..4523bbf7f35 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitParameters.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/ShortCircuitParameters.java @@ -113,6 +113,10 @@ private void readExtensions(PlatformConfig platformConfig) { } } + /** + * Whether limit violations should be returned after the calculation. + * They indicate whether the maximum or minimum allowable current has been reached. + */ public boolean isWithLimitViolations() { return withLimitViolations; } @@ -196,7 +200,7 @@ public ShortCircuitParameters setMinVoltageDropProportionalThreshold(double minV return this; } - /** In case of a sub-transient study, a multiplicative coefficient to obtain the sub-transient reactance of the generators + /** In the case of a sub-transient study, a multiplicative coefficient to obtain the sub-transient reactance of the generators * from the transient reactance. By default, X''d = 0.7 * X'd. */ public double getSubTransientCoefficient() { @@ -267,7 +271,7 @@ public ShortCircuitParameters setWithNeutralPosition(boolean withNeutralPosition * The initial voltage profile mode, it can be either: * - nominal: nominal voltages will be used * - configured: the voltage profile is given by the user, voltage ranges and associated coefficients should be given using {@link VoltageRange} - * - previous value: the voltage profile computed from the loadflow will be used + * - previous value: the voltage profile computed from the load flow will be used */ public InitialVoltageProfileMode getInitialVoltageProfileMode() { return initialVoltageProfileMode; @@ -304,6 +308,9 @@ public ShortCircuitParameters setDetailedReport(boolean detailedReport) { return this; } + /** + * Validates the ShortCircuitParameters. If the initial voltage profile mode is set to CONFIGURED, then the voltage ranges should not be empty. + */ public void validate() { if (initialVoltageProfileMode == InitialVoltageProfileMode.CONFIGURED && (voltageRanges == null || voltageRanges.isEmpty())) { throw new PowsyblException("Configured initial voltage profile but nominal voltage ranges with associated coefficients are missing."); diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/StudyType.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/StudyType.java index c4cbffb7fb1..710b8337864 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/StudyType.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/StudyType.java @@ -7,8 +7,23 @@ */ package com.powsybl.shortcircuit; +/** + * The type of short-circuit calculation, transient, sub-transient or steady-state. + */ public enum StudyType { + + /** + * The first stage of the short circuit, right when the fault occurs. + */ SUB_TRANSIENT, + + /** + * The second stage of the short circuit, before the system stabilizes. + */ TRANSIENT, + + /** + * The final stage of the short circuit, when all transient effects have disappeared. + */ STEADY_STATE } From 906d5fa5640c43e833e6e3c196d5ee76d6117c4b Mon Sep 17 00:00:00 2001 From: Coline Piloquet <55250145+colinepiloquet@users.noreply.github.com> Date: Mon, 22 Jul 2024 17:05:25 +0200 Subject: [PATCH 36/57] [Network modification] Modify log when no extension is created. (#3100) * Modify the log when no extension is created when a feeder is created with its bay. * Use already defined constant. Signed-off-by: Coline PILOQUET --- .../topology/AbstractCreateConnectableFeederBays.java | 4 ++-- .../powsybl/iidm/modification/util/ModificationReports.java | 5 +++-- .../reportNode/create-line-NB-without-extensions-report.txt | 4 ++-- .../reportNode/create-load-NB-without-extensions-report.txt | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/topology/AbstractCreateConnectableFeederBays.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/topology/AbstractCreateConnectableFeederBays.java index 9255f6eba96..d23801968dc 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/topology/AbstractCreateConnectableFeederBays.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/topology/AbstractCreateConnectableFeederBays.java @@ -224,8 +224,8 @@ private void createExtensionAndTopology(Connectable connectable, Network netw createConnectablePosition = true; } } else { - LOGGER.warn("No order positions found on voltageLevel {}. The extension is not created.", voltageLevel.getId()); - noConnectablePositionExtension(reportNode, voltageLevel); + LOGGER.warn("No ConnectablePosition extension found on voltageLevel {}. The ConnectablePosition extension is not created for new feeder {}.", voltageLevel.getId(), connectableId); + noConnectablePositionExtension(reportNode, voltageLevel, connectableId); } // create switches and a breaker linking the connectable to the busbar sections. createTopology(side, network, voltageLevel, connectable, namingStrategy, reportNode); diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/util/ModificationReports.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/util/ModificationReports.java index 46659a297cf..35cae543424 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/util/ModificationReports.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/util/ModificationReports.java @@ -296,10 +296,11 @@ public static void positionOrderTooHighReport(ReportNode reportNode, int maxValu .add(); } - public static void noConnectablePositionExtension(ReportNode reportNode, VoltageLevel voltageLevel) { + public static void noConnectablePositionExtension(ReportNode reportNode, VoltageLevel voltageLevel, String connectableId) { reportNode.newReportNode() - .withMessageTemplate("noConnectablePositionExtensions", "No extensions found on voltageLevel ${voltageLevel}. The extension on the connectable is not created.") + .withMessageTemplate("noConnectablePositionExtensions", "No ConnectablePosition extension found on voltageLevel ${voltageLevel}. The ConnectablePosition extension is not created for new feeder ${connectableId}.") .withUntypedValue("voltageLevel", voltageLevel.getId()) + .withUntypedValue(CONNECTABLE_ID, connectableId) .withSeverity(TypedValue.WARN_SEVERITY) .add(); } diff --git a/iidm/iidm-modification/src/test/resources/reportNode/create-line-NB-without-extensions-report.txt b/iidm/iidm-modification/src/test/resources/reportNode/create-line-NB-without-extensions-report.txt index 1ac8c95f743..c736a130e3c 100644 --- a/iidm/iidm-modification/src/test/resources/reportNode/create-line-NB-without-extensions-report.txt +++ b/iidm/iidm-modification/src/test/resources/reportNode/create-line-NB-without-extensions-report.txt @@ -1,8 +1,8 @@ + Testing creating line reportNode without extensions New connectable lineTest of type LINE created. - No extensions found on voltageLevel vl2. The extension on the connectable is not created. + No ConnectablePosition extension found on voltageLevel vl2. The ConnectablePosition extension is not created for new feeder lineTest. No busbar section position extension found on bbs5, only one disconnector is created. New feeder bay associated to lineTest of type LINE was created and connected to voltage level vl2 on busbar section bbs5 with a closed disconnector and on 0 parallel busbar sections with an open disconnector. - No extensions found on voltageLevel vl1. The extension on the connectable is not created. + No ConnectablePosition extension found on voltageLevel vl1. The ConnectablePosition extension is not created for new feeder lineTest. No busbar section position extension found on bbs1, only one disconnector is created. New feeder bay associated to lineTest of type LINE was created and connected to voltage level vl1 on busbar section bbs1 with a closed disconnector and on 0 parallel busbar sections with an open disconnector. diff --git a/iidm/iidm-modification/src/test/resources/reportNode/create-load-NB-without-extensions-report.txt b/iidm/iidm-modification/src/test/resources/reportNode/create-load-NB-without-extensions-report.txt index c5a1850f02b..1929976e26e 100644 --- a/iidm/iidm-modification/src/test/resources/reportNode/create-load-NB-without-extensions-report.txt +++ b/iidm/iidm-modification/src/test/resources/reportNode/create-load-NB-without-extensions-report.txt @@ -1,5 +1,5 @@ + Testing reportNode for a load creation in a network without extensions New connectable newLoad of type LOAD created. - No extensions found on voltageLevel vl1. The extension on the connectable is not created. + No ConnectablePosition extension found on voltageLevel vl1. The ConnectablePosition extension is not created for new feeder newLoad. No busbar section position extension found on bbs4, only one disconnector is created. New feeder bay associated to newLoad of type LOAD was created and connected to voltage level vl1 on busbar section bbs4 with a closed disconnector and on 0 parallel busbar sections with an open disconnector. From 5bbc51120399dbe93e1a5e9b06fa3632aa03d4e2 Mon Sep 17 00:00:00 2001 From: nemanja-st <79513586+nemanja-st@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:12:21 +0200 Subject: [PATCH 37/57] CGMES export of regulating controls (#2995) Signed-off-by: stojkovicn Co-authored-by: Anne Tilloy Co-authored-by: Luma --- .../conversion/export/CgmesExportContext.java | 11 +- .../conversion/export/CgmesExportUtil.java | 66 ++- .../conversion/export/EquipmentExport.java | 36 +- .../export/SteadyStateHypothesisExport.java | 54 ++- .../export/elements/RegulatingControlEq.java | 19 +- .../export/elements/TapChangerEq.java | 2 +- .../test/export/CgmesExportTest.java | 14 +- .../test/export/EquipmentExportTest.java | 400 ++++++++++++++++- .../SteadyStateHypothesisExportTest.java | 402 ++++++++++++++++++ docs/grid_exchange_formats/cgmes/export.md | 53 ++- .../test/EurostagTutorialExample1Factory.java | 296 ++++++++++++- .../test/PhaseShifterTestCaseFactory.java | 31 ++ .../network/test/ShuntTestCaseFactory.java | 59 +++ .../iidm/network/test/SvcTestCaseFactory.java | 121 ++++++ 14 files changed, 1477 insertions(+), 87 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java index 2af33a231dc..a40a2338ad1 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportContext.java @@ -388,7 +388,7 @@ private void addIidmMappingsGenerators(Network network) { } private static boolean hasVoltageControlCapability(Generator generator) { - if (Double.isNaN(generator.getTargetV()) || generator.getReactiveLimits() == null) { + if (generator.getReactiveLimits() == null) { return false; } @@ -432,7 +432,8 @@ private void addIidmMappingsShuntCompensators(Network network) { continue; } String regulatingControlId = shuntCompensator.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL); - if (regulatingControlId == null && (shuntCompensator.isVoltageRegulatorOn() || !Objects.equals(shuntCompensator, shuntCompensator.getRegulatingTerminal().getConnectable()))) { + if (regulatingControlId == null && (CgmesExportUtil.isValidVoltageSetpoint(shuntCompensator.getTargetV()) + || !Objects.equals(shuntCompensator, shuntCompensator.getRegulatingTerminal().getConnectable()))) { regulatingControlId = namingStrategy.getCgmesId(ref(shuntCompensator), Part.REGULATING_CONTROL); shuntCompensator.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL, regulatingControlId); } @@ -442,7 +443,11 @@ private void addIidmMappingsShuntCompensators(Network network) { private void addIidmMappingsStaticVarCompensators(Network network) { for (StaticVarCompensator svc : network.getStaticVarCompensators()) { String regulatingControlId = svc.getProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL); - if (regulatingControlId == null && (StaticVarCompensator.RegulationMode.VOLTAGE.equals(svc.getRegulationMode()) || !Objects.equals(svc, svc.getRegulatingTerminal().getConnectable()))) { + boolean validVoltageSetpoint = CgmesExportUtil.isValidVoltageSetpoint(svc.getVoltageSetpoint()); + boolean validReactiveSetpoint = CgmesExportUtil.isValidReactivePowerSetpoint(svc.getReactivePowerSetpoint()); + if (regulatingControlId == null && (validReactiveSetpoint + || validVoltageSetpoint + || !Objects.equals(svc, svc.getRegulatingTerminal().getConnectable()))) { regulatingControlId = namingStrategy.getCgmesId(ref(svc), Part.REGULATING_CONTROL); svc.setProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + REGULATING_CONTROL, regulatingControlId); } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java index 351444b7672..e4f532b3e8c 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java @@ -8,6 +8,7 @@ package com.powsybl.cgmes.conversion.export; import com.powsybl.cgmes.conversion.Conversion; +import com.powsybl.cgmes.conversion.export.elements.RegulatingControlEq; import com.powsybl.cgmes.conversion.naming.CgmesObjectReference; import com.powsybl.cgmes.conversion.naming.CgmesObjectReference.Part; import com.powsybl.cgmes.extensions.CgmesTapChanger; @@ -21,6 +22,7 @@ import com.powsybl.commons.report.TypedValue; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.LoadDetail; +import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -404,7 +406,7 @@ private static > String getTapChangerId(C twt, String c } static boolean regulatingControlIsDefined(RatioTapChanger rtc) { - return !Double.isNaN(rtc.getTargetV()) + return !Double.isNaN(rtc.getRegulationValue()) && !Double.isNaN(rtc.getTargetDeadband()) && rtc.getRegulationTerminal() != null; } @@ -513,6 +515,68 @@ static String obtainCalculatedSynchronousMachineKind(double minP, double maxP, R return kind; } + public static boolean isValidVoltageSetpoint(double v) { + return Double.isFinite(v) && v > 0; + } + + public static boolean isValidReactivePowerSetpoint(double q) { + return Double.isFinite(q); + } + + public static String getGeneratorRegulatingControlMode(Generator generator, RemoteReactivePowerControl rrpc) { + if (rrpc == null) { + return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE; + } + boolean enabledVoltageControl = generator.isVoltageRegulatorOn(); + boolean enabledReactivePowerControl = rrpc.isEnabled(); + + if (enabledVoltageControl) { + return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE; + } else if (enabledReactivePowerControl) { + return RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER; + } else { + boolean validVoltageSetpoint = isValidVoltageSetpoint(generator.getTargetV()); + boolean validReactiveSetpoint = isValidReactivePowerSetpoint(rrpc.getTargetQ()); + if (validReactiveSetpoint && !validVoltageSetpoint) { + return RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER; + } + return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE; + } + } + + public static String getSvcMode(StaticVarCompensator svc) { + if (svc.getRegulationMode().equals(StaticVarCompensator.RegulationMode.VOLTAGE)) { + return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE; + } else if (svc.getRegulationMode().equals(StaticVarCompensator.RegulationMode.REACTIVE_POWER)) { + return RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER; + } else { + boolean validVoltageSetpoint = isValidVoltageSetpoint(svc.getVoltageSetpoint()); + boolean validReactiveSetpoint = isValidReactivePowerSetpoint(svc.getReactivePowerSetpoint()); + if (validReactiveSetpoint && !validVoltageSetpoint) { + return RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER; + } + return RegulatingControlEq.REGULATING_CONTROL_VOLTAGE; + } + } + + public static String getTcMode(RatioTapChanger rtc) { + if (rtc.getRegulationMode() == null) { + throw new PowsyblException("Regulation mode not defined for RTC."); + } + return switch (rtc.getRegulationMode()) { + case VOLTAGE -> RegulatingControlEq.REGULATING_CONTROL_VOLTAGE; + case REACTIVE_POWER -> RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER; + }; + } + + public static String getPhaseTapChangerRegulationMode(PhaseTapChanger ptc) { + return switch (ptc.getRegulationMode()) { + case CURRENT_LIMITER -> RegulatingControlEq.REGULATING_CONTROL_CURRENT_FLOW; + case ACTIVE_POWER_CONTROL -> RegulatingControlEq.REGULATING_CONTROL_ACTIVE_POWER; + default -> throw new PowsyblException("Unexpected regulation mode: " + ptc.getRegulationMode()); + }; + } + public static boolean isMinusOrMaxValue(double value) { return value == -Double.MAX_VALUE || value == Double.MAX_VALUE; } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java index 2355ac9b16c..a69c4f5fccc 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/EquipmentExport.java @@ -20,6 +20,7 @@ import com.powsybl.commons.exceptions.UncheckedXmlStreamException; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl; import com.powsybl.iidm.network.extensions.VoltagePerReactivePowerControl; import org.apache.commons.lang3.tuple.Pair; @@ -45,9 +46,6 @@ public final class EquipmentExport { private static final String AC_DC_CONVERTER_DC_TERMINAL = "ACDCConverterDCTerminal"; - private static final String PHASE_TAP_CHANGER_REGULATION_MODE_ACTIVE_POWER = "activePower"; - private static final String PHASE_TAP_CHANGER_REGULATION_MODE_CURRENT_FLOW = "currentFlow"; - private static final String RATIO_TAP_CHANGER_REGULATION_MODE_VOLTAGE = "voltage"; private static final String TERMINAL_BOUNDARY = "Terminal_Boundary"; private static final Logger LOG = LoggerFactory.getLogger(EquipmentExport.class); @@ -382,7 +380,9 @@ private static void writeGenerators(Network network, Map mapTe Set generatingUnitsWritten = new HashSet<>(); for (Generator generator : network.getGenerators()) { String cgmesOriginalClass = generator.getProperty(Conversion.PROPERTY_CGMES_ORIGINAL_CLASS, CgmesNames.SYNCHRONOUS_MACHINE); - + RemoteReactivePowerControl rrpc = generator.getExtension(RemoteReactivePowerControl.class); + String mode = CgmesExportUtil.getGeneratorRegulatingControlMode(generator, rrpc); + Terminal regulatingTerminal = mode.equals(RegulatingControlEq.REGULATING_CONTROL_VOLTAGE) ? generator.getRegulatingTerminal() : rrpc.getRegulatingTerminal(); switch (cgmesOriginalClass) { case CgmesNames.EQUIVALENT_INJECTION: String reactiveCapabilityCurveId = writeReactiveCapabilityCurve(generator, cimNamespace, writer, context); @@ -393,14 +393,14 @@ private static void writeGenerators(Network network, Map mapTe cimNamespace, writer, context); break; case CgmesNames.EXTERNAL_NETWORK_INJECTION: - String regulatingControlId = RegulatingControlEq.writeKindVoltage(generator, exportedTerminalId(mapTerminal2Id, generator.getRegulatingTerminal()), regulatingControlsWritten, cimNamespace, writer, context); + String regulatingControlId = RegulatingControlEq.writeRegulatingControlEq(generator, exportedTerminalId(mapTerminal2Id, regulatingTerminal), regulatingControlsWritten, mode, cimNamespace, writer, context); ExternalNetworkInjectionEq.write(context.getNamingStrategy().getCgmesId(generator), generator.getNameOrId(), context.getNamingStrategy().getCgmesId(generator.getTerminal().getVoltageLevel()), obtainGeneratorGovernorScd(generator), generator.getMaxP(), obtainMaxQ(generator), generator.getMinP(), obtainMinQ(generator), regulatingControlId, cimNamespace, writer, context); break; case CgmesNames.SYNCHRONOUS_MACHINE: - regulatingControlId = RegulatingControlEq.writeKindVoltage(generator, exportedTerminalId(mapTerminal2Id, generator.getRegulatingTerminal()), regulatingControlsWritten, cimNamespace, writer, context); + regulatingControlId = RegulatingControlEq.writeRegulatingControlEq(generator, exportedTerminalId(mapTerminal2Id, regulatingTerminal), regulatingControlsWritten, mode, cimNamespace, writer, context); writeSynchronousMachine(generator, cimNamespace, writeInitialP, generator.getMinP(), generator.getMaxP(), generator.getTargetP(), generator.getRatedS(), generator.getEnergySource(), regulatingControlId, writer, context, generatingUnitsWritten); @@ -574,13 +574,15 @@ private static void writeShuntCompensators(Network network, Map mapTerminal2Id, Set regulatingControlsWritten, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { for (StaticVarCompensator svc : network.getStaticVarCompensators()) { - String regulatingControlId = RegulatingControlEq.writeKindVoltage(svc, exportedTerminalId(mapTerminal2Id, svc.getRegulatingTerminal()), regulatingControlsWritten, cimNamespace, writer, context); + String mode = CgmesExportUtil.getSvcMode(svc); + String regulatingControlId = RegulatingControlEq.writeRegulatingControlEq(svc, exportedTerminalId(mapTerminal2Id, svc.getRegulatingTerminal()), regulatingControlsWritten, mode, cimNamespace, writer, context); double inductiveRating = svc.getBmin() != 0 ? 1 / svc.getBmin() : 0; double capacitiveRating = svc.getBmax() != 0 ? 1 / svc.getBmax() : 0; StaticVarCompensatorEq.write(context.getNamingStrategy().getCgmesId(svc), svc.getNameOrId(), context.getNamingStrategy().getCgmesId(svc.getTerminal().getVoltageLevel()), regulatingControlId, inductiveRating, capacitiveRating, svc.getExtension(VoltagePerReactivePowerControl.class), svc.getRegulationMode(), svc.getVoltageSetpoint(), cimNamespace, writer, context); @@ -838,7 +841,7 @@ private static > void writePhaseTapChanger(C eq, PhaseT Optional regulatingControlId = getTapChangerControlId(eq, tapChangerId); String cgmesRegulatingControlId = null; if (regulatingControlId.isPresent() && CgmesExportUtil.regulatingControlIsDefined(ptc)) { - String mode = getPhaseTapChangerRegulationMode(ptc); + String mode = CgmesExportUtil.getPhaseTapChangerRegulationMode(ptc); String controlName = twtName + "_PTC_RC"; String terminalId = CgmesExportUtil.getTerminalId(ptc.getRegulationTerminal(), context); cgmesRegulatingControlId = context.getNamingStrategy().getCgmesId(regulatingControlId.get()); @@ -879,14 +882,6 @@ private static > Optional getTapChangerControlI return Optional.empty(); } - private static String getPhaseTapChangerRegulationMode(PhaseTapChanger ptc) { - return switch (ptc.getRegulationMode()) { - case CURRENT_LIMITER -> PHASE_TAP_CHANGER_REGULATION_MODE_CURRENT_FLOW; - case ACTIVE_POWER_CONTROL -> PHASE_TAP_CHANGER_REGULATION_MODE_ACTIVE_POWER; - default -> throw new PowsyblException("Unexpected regulation mode: " + ptc.getRegulationMode()); - }; - } - private static int getPhaseTapChangerNeutralStep(PhaseTapChanger ptc) { int neutralStep = ptc.getLowTapPosition(); double minAlpha = Math.abs(ptc.getStep(neutralStep).getAlpha()); @@ -922,8 +917,11 @@ private static > void writeRatioTapChanger(C eq, RatioT String terminalId = CgmesExportUtil.getTerminalId(rtc.getRegulationTerminal(), context); cgmesRegulatingControlId = context.getNamingStrategy().getCgmesId(regulatingControlId.get()); if (!regulatingControlsWritten.contains(cgmesRegulatingControlId)) { - // Regulating control mode is always "voltage" - TapChangerEq.writeControl(cgmesRegulatingControlId, controlName, RATIO_TAP_CHANGER_REGULATION_MODE_VOLTAGE, terminalId, cimNamespace, writer, context); + String tccMode = CgmesExportUtil.getTcMode(rtc); + if (tccMode.equals(RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER)) { + controlMode = "reactive"; + } + TapChangerEq.writeControl(cgmesRegulatingControlId, controlName, tccMode, terminalId, cimNamespace, writer, context); regulatingControlsWritten.add(cgmesRegulatingControlId); } } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java index b91825b82f6..1a47768509a 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java @@ -9,6 +9,7 @@ import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.Conversion; +import com.powsybl.cgmes.conversion.export.elements.RegulatingControlEq; import com.powsybl.cgmes.extensions.CgmesControlArea; import com.powsybl.cgmes.extensions.CgmesControlAreas; import com.powsybl.cgmes.extensions.CgmesTapChanger; @@ -21,6 +22,7 @@ import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.ActivePowerControl; import com.powsybl.iidm.network.extensions.ReferencePriority; +import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -192,6 +194,7 @@ private static String cgmesTapChangerId(TwoWindingsTransformer twt, String tapCh private static void writeTapChangers(Network network, String cimNamespace, Map> regulatingControlViews, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { for (TwoWindingsTransformer twt : network.getTwoWindingsTransformers()) { + CgmesExportUtil.addUpdateCgmesTapChangerExtension(twt, context); if (twt.hasPhaseTapChanger()) { String ptcId = cgmesTapChangerId(twt, CgmesNames.PHASE_TAP_CHANGER, context); writeTapChanger(twt, ptcId, twt.getPhaseTapChanger(), CgmesNames.PHASE_TAP_CHANGER_TABULAR, regulatingControlViews, cimNamespace, writer, context); @@ -204,6 +207,7 @@ private static void writeTapChangers(Network network, String cimNamespace, Map new ArrayList<>()).add(rcv); } } - private static boolean isValidSvcVolatgeSetpoint(double v) { - return Double.isFinite(v) && v > 0; - } - - private static boolean isValidSvcReactivePowerSetpoint(double q) { - return Double.isFinite(q); - } - private static void writeStaticVarCompensators(Network network, String cimNamespace, Map> regulatingControlViews, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { for (StaticVarCompensator svc : network.getStaticVarCompensators()) { @@ -409,12 +421,11 @@ private static void writeStaticVarCompensators(Network network, String cimNamesp // Regulating control could be reactive power or voltage double targetValue; String multiplier; - if (regulationMode == StaticVarCompensator.RegulationMode.VOLTAGE - || regulationMode == StaticVarCompensator.RegulationMode.OFF && isValidSvcVolatgeSetpoint(svc.getVoltageSetpoint())) { + String svcMode = CgmesExportUtil.getSvcMode(svc); + if (svcMode.equals(RegulatingControlEq.REGULATING_CONTROL_VOLTAGE)) { targetValue = svc.getVoltageSetpoint(); multiplier = "k"; - } else if (regulationMode == StaticVarCompensator.RegulationMode.REACTIVE_POWER - || regulationMode == StaticVarCompensator.RegulationMode.OFF && isValidSvcReactivePowerSetpoint(svc.getReactivePowerSetpoint())) { + } else if (svcMode.equals(RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER)) { targetValue = svc.getReactivePowerSetpoint(); multiplier = "M"; } else { @@ -451,24 +462,29 @@ private static void addRegulatingControlView(TapChanger tc, CgmesTap RegulatingControlView rcv = null; if (tc instanceof RatioTapChanger ratioTapChanger && CgmesExportUtil.regulatingControlIsDefined(ratioTapChanger)) { + String controlMode = CgmesExportUtil.getTcMode(ratioTapChanger); + String unitMultiplier = switch (controlMode) { + case RegulatingControlEq.REGULATING_CONTROL_VOLTAGE -> "k"; + case RegulatingControlEq.REGULATING_CONTROL_REACTIVE_POWER -> "M"; + default -> "none"; + }; rcv = new RegulatingControlView(controlId, RegulatingControlType.TAP_CHANGER_CONTROL, true, ratioTapChanger.isRegulating(), ratioTapChanger.getTargetDeadband(), - ratioTapChanger.getTargetV(), - // Unit multiplier is k for ratio tap changers (regulation value is a voltage in kV) - "k"); + ratioTapChanger.getRegulationValue(), + unitMultiplier); } else if (tc instanceof PhaseTapChanger phaseTapChanger && CgmesExportUtil.regulatingControlIsDefined(phaseTapChanger)) { boolean valid; - String unitMultiplier = switch (phaseTapChanger.getRegulationMode()) { - case CURRENT_LIMITER -> { + String unitMultiplier = switch (CgmesExportUtil.getPhaseTapChangerRegulationMode(phaseTapChanger)) { + case RegulatingControlEq.REGULATING_CONTROL_CURRENT_FLOW -> { // Unit multiplier is none (multiply by 1), regulation value is a current in Amperes valid = true; yield "none"; } - case ACTIVE_POWER_CONTROL -> { + case RegulatingControlEq.REGULATING_CONTROL_ACTIVE_POWER -> { // Unit multiplier is M, regulation value is an active power flow in MW valid = true; yield "M"; diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/RegulatingControlEq.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/RegulatingControlEq.java index eac5db96020..68b03f05e2a 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/RegulatingControlEq.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/RegulatingControlEq.java @@ -25,25 +25,24 @@ public final class RegulatingControlEq { public static final String REGULATING_CONTROL_VOLTAGE = "RegulatingControlModeKind.voltage"; + public static final String REGULATING_CONTROL_REACTIVE_POWER = "RegulatingControlModeKind.reactivePower"; + public static final String REGULATING_CONTROL_ACTIVE_POWER = "RegulatingControlModeKind.activePower"; + public static final String REGULATING_CONTROL_CURRENT_FLOW = "RegulatingControlModeKind.currentFlow"; - public static String writeKindVoltage(Connectable c, String terminalId, Set regulatingControlsWritten, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { + public static String writeRegulatingControlEq(Connectable c, String terminalId, Set regulatingControlsWritten, String mode, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { String regulatingControlId = context.getNamingStrategy().getCgmesIdFromProperty(c, Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl"); if (regulatingControlId != null && !regulatingControlsWritten.contains(regulatingControlId)) { String regulatingControlName = "RC_" + c.getNameOrId(); - RegulatingControlEq.writeKindVoltage(regulatingControlId, regulatingControlName, terminalId, cimNamespace, writer, context); + CgmesExportUtil.writeStartIdName("RegulatingControl", regulatingControlId, regulatingControlName, cimNamespace, writer, context); + CgmesExportUtil.writeReference("RegulatingControl.Terminal", terminalId, cimNamespace, writer, context); + writer.writeEmptyElement(cimNamespace, "RegulatingControl.mode"); + writer.writeAttribute(RDF_NAMESPACE, CgmesNames.RESOURCE, cimNamespace + mode); + writer.writeEndElement(); regulatingControlsWritten.add(regulatingControlId); } return regulatingControlId; } - private static void writeKindVoltage(String id, String regulatingControlName, String terminalId, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { - CgmesExportUtil.writeStartIdName("RegulatingControl", id, regulatingControlName, cimNamespace, writer, context); - CgmesExportUtil.writeReference("RegulatingControl.Terminal", terminalId, cimNamespace, writer, context); - writer.writeEmptyElement(cimNamespace, "RegulatingControl.mode"); - writer.writeAttribute(RDF_NAMESPACE, CgmesNames.RESOURCE, cimNamespace + REGULATING_CONTROL_VOLTAGE); - writer.writeEndElement(); - } - private RegulatingControlEq() { } } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/TapChangerEq.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/TapChangerEq.java index 3d0125423dc..a35419d45fb 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/TapChangerEq.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/elements/TapChangerEq.java @@ -99,7 +99,7 @@ public static void writeRatio(String id, String tapChangerName, String transform public static void writeControl(String id, String name, String mode, String terminalId, String cimNamespace, XMLStreamWriter writer, CgmesExportContext context) throws XMLStreamException { CgmesExportUtil.writeStartIdName("TapChangerControl", id, name, cimNamespace, writer, context); writer.writeEmptyElement(cimNamespace, "RegulatingControl.mode"); - writer.writeAttribute(RDF_NAMESPACE, CgmesNames.RESOURCE, String.format("%s%s.%s", cimNamespace, "RegulatingControlModeKind", mode)); + writer.writeAttribute(RDF_NAMESPACE, CgmesNames.RESOURCE, String.format("%s%s", cimNamespace, mode)); CgmesExportUtil.writeReference("RegulatingControl.Terminal", terminalId, cimNamespace, writer, context); writer.writeEndElement(); } diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java index e66a700efa0..f5e9f4c5977 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CgmesExportTest.java @@ -634,7 +634,7 @@ void testCanGeneratorControl() throws IOException { generatorRcc.removeProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl"); generatorNoRcc.removeProperty(Conversion.CGMES_PREFIX_ALIAS_PROPERTIES + "RegulatingControl"); - // RC shouldn't be exported without targetV + // RC is exported without targetV, if reactive capability is, or it was imported double rccTargetV = generatorRcc.getTargetV(); generatorRcc.setVoltageRegulatorOn(false); generatorRcc.setTargetV(Double.NaN); @@ -642,19 +642,17 @@ void testCanGeneratorControl() throws IOException { double noRccTargetV = generatorNoRcc.getTargetV(); generatorNoRcc.setVoltageRegulatorOn(false); generatorNoRcc.setTargetV(Double.NaN); - - //network.write("CGMES", null, tmpDir.resolve(baseName)); new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName)); eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml")); - assertFalse(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC")); - assertFalse(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC")); + assertTrue(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC")); + assertTrue(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC")); generatorRcc.setTargetV(rccTargetV); generatorRcc.setVoltageRegulatorOn(true); generatorNoRcc.setTargetV(noRccTargetV); generatorNoRcc.setVoltageRegulatorOn(true); - // RC shouldn't be exported when Qmin and Qmax are the same + // RC shouldn't be exported when Qmin and Qmax are the same, but it exists when it was already imported ReactiveCapabilityCurveAdder rccAdder = generatorRcc.newReactiveCapabilityCurve(); ReactiveCapabilityCurve rcc = (ReactiveCapabilityCurve) generatorRcc.getReactiveLimits(); rcc.getPoints().forEach(point -> rccAdder.beginPoint().setP(point.getP()).setMaxQ(point.getMaxQ()).setMinQ(point.getMaxQ()).endPoint()); @@ -667,8 +665,8 @@ void testCanGeneratorControl() throws IOException { new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName)); eq = Files.readString(tmpDir.resolve(baseName + "_EQ.xml")); - assertFalse(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC")); - assertFalse(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC")); + assertTrue(eq.contains("3a3b27be-b18b-4385-b557-6735d733baf0_RC")); + assertTrue(eq.contains("550ebe0d-f2b2-48c1-991f-cebea43a21aa_RC")); } } diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java index 4281631f520..1c74cb09b75 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/EquipmentExportTest.java @@ -29,11 +29,11 @@ import com.powsybl.computation.local.LocalComputationManager; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.ThreeSides; -import com.powsybl.iidm.network.test.ThreeWindingsTransformerNetworkFactory; +import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl; +import com.powsybl.iidm.network.test.*; import com.powsybl.iidm.serde.ExportOptions; import com.powsybl.iidm.serde.NetworkSerDe; import com.powsybl.iidm.serde.XMLImporter; -import com.powsybl.iidm.network.test.FourSubstationsNodeBreakerFactory; import com.powsybl.iidm.network.util.BranchData; import com.powsybl.iidm.network.util.TwtData; @@ -51,9 +51,12 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.FileSystem; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; import static org.junit.jupiter.api.Assertions.*; @@ -1004,6 +1007,399 @@ void synchronousMachineKindExportAndImportTest() throws IOException { assertEquals(expectedOperatingMode, actualOperatingMode); } + @Test + void phaseTapChangerTapChangerControlEQTest() throws IOException { + String exportFolder = "/test-pst-tcc"; + String baseName = "testPstTcc"; + Network network; + String eq; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "EQ"); + + // PST with FIXED_TAP + network = PhaseShifterTestCaseFactory.createWithTargetDeadband(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithoutAttribute(eq, "_PS1_PTC_RC", "_PS1_PT_T_2", "activePower"); + + // PST local with ACTIVE_POWER_CONTROL + network = PhaseShifterTestCaseFactory.createLocalActivePowerWithTargetDeadband(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_PS1_PTC_RC", "_PS1_PT_T_2", "activePower"); + network.getTwoWindingsTransformer("PS1").getPhaseTapChanger().setRegulating(true); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_PS1_PTC_RC", "_PS1_PT_T_2", "activePower"); + + // PST local with CURRENT_LIMITER + network = PhaseShifterTestCaseFactory.createLocalCurrentLimiterWithTargetDeadband(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_PS1_PTC_RC", "_PS1_PT_T_2", "currentFlow"); + network.getTwoWindingsTransformer("PS1").getPhaseTapChanger().setRegulating(true); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_PS1_PTC_RC", "_PS1_PT_T_2", "currentFlow"); + + // PST remote with CURRENT_LIMITER + network = PhaseShifterTestCaseFactory.createRemoteCurrentLimiterWithTargetDeadband(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_PS1_PTC_RC", "_LD2_EC_T_1", "currentFlow"); + network.getTwoWindingsTransformer("PS1").getPhaseTapChanger().setRegulating(true); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_PS1_PTC_RC", "_LD2_EC_T_1", "currentFlow"); + + // PST remote with ACTIVE_POWER_CONTROL + network = PhaseShifterTestCaseFactory.createRemoteActivePowerWithTargetDeadband(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_PS1_PTC_RC", "_LD2_EC_T_1", "activePower"); + network.getTwoWindingsTransformer("PS1").getPhaseTapChanger().setRegulating(true); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_PS1_PTC_RC", "_LD2_EC_T_1", "activePower"); + } + } + + @Test + void ratioTapChangerTapChangerControlEQTest() throws IOException { + String exportFolder = "/test-rtc-tcc"; + String baseName = "testRtcTcc"; + Network network; + String eq; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "EQ"); + + // RTC without control + network = EurostagTutorialExample1Factory.createWithoutRtcControl(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithoutAttribute(eq, "_NHV2_NLOAD_RTC_RC", "", "dummy"); + + // RTC local with VOLTAGE + network = EurostagTutorialExample1Factory.create(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NHV2_NLOAD_RTC_RC", "_NHV2_NLOAD_PT_T_2", "voltage"); + network.getTwoWindingsTransformer("NHV2_NLOAD").getRatioTapChanger().setRegulating(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NHV2_NLOAD_RTC_RC", "_NHV2_NLOAD_PT_T_2", "voltage"); + + // RTC local with REACTIVE_POWER + network = EurostagTutorialExample1Factory.createWithReactiveTcc(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NHV2_NLOAD_RTC_RC", "_NHV2_NLOAD_PT_T_2", "reactivePower"); + network.getTwoWindingsTransformer("NHV2_NLOAD").getRatioTapChanger().setRegulating(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NHV2_NLOAD_RTC_RC", "_NHV2_NLOAD_PT_T_2", "reactivePower"); + + // RTC remote with VOLTAGE + network = EurostagTutorialExample1Factory.createRemoteVoltageTcc(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NHV2_NLOAD_RTC_RC", "_GEN_SM_T_1", "voltage"); + network.getTwoWindingsTransformer("NHV2_NLOAD").getRatioTapChanger().setRegulating(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NHV2_NLOAD_RTC_RC", "_GEN_SM_T_1", "voltage"); + + // RTC remote with REACTIVE_POWER + network = EurostagTutorialExample1Factory.createRemoteReactiveTcc(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NHV2_NLOAD_RTC_RC", "_GEN_SM_T_1", "reactivePower"); + network.getTwoWindingsTransformer("NHV2_NLOAD").getRatioTapChanger().setRegulating(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NHV2_NLOAD_RTC_RC", "_GEN_SM_T_1", "reactivePower"); + + // 3w without control + network = EurostagTutorialExample1Factory.createWith3wWithoutControl(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithoutAttribute(eq, "_NGEN_V2_NHV1_RTC_RC", "", "dummy"); + + // 3w with local voltage control + network = EurostagTutorialExample1Factory.createWith3wWithVoltageControl(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NGEN_V2_NHV1_RTC_RC", "_NGEN_V2_NHV1_PT_T_1", "voltage"); + + // 3w with local reactive control + network = EurostagTutorialExample1Factory.create3wWithReactiveTcc(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NGEN_V2_NHV1_RTC_RC", "_NGEN_V2_NHV1_PT_T_1", "reactivePower"); + network.getThreeWindingsTransformer("NGEN_V2_NHV1").getLeg1().getRatioTapChanger().setRegulating(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NGEN_V2_NHV1_RTC_RC", "_NGEN_V2_NHV1_PT_T_1", "reactivePower"); + + // 3w with remote voltage + network = EurostagTutorialExample1Factory.create3wRemoteVoltageTcc(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NGEN_V2_NHV1_RTC_RC", "_GEN_SM_T_1", "voltage"); + network.getThreeWindingsTransformer("NGEN_V2_NHV1").getLeg1().getRatioTapChanger().setRegulating(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NGEN_V2_NHV1_RTC_RC", "_GEN_SM_T_1", "voltage"); + + // 3w with remote reactive + network = EurostagTutorialExample1Factory.create3wRemoteReactiveTcc(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NGEN_V2_NHV1_RTC_RC", "_GEN_SM_T_1", "reactivePower"); + network.getThreeWindingsTransformer("NGEN_V2_NHV1").getLeg1().getRatioTapChanger().setRegulating(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(eq, "_NGEN_V2_NHV1_RTC_RC", "_GEN_SM_T_1", "reactivePower"); + } + } + + @Test + void staticVarCompensatorRegulatingControlEQTest() throws IOException { + String exportFolder = "/test-svc-rc"; + String baseName = "testSvcRc"; + Network network; + String eq; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "EQ"); + + // SVC VOLTAGE + // Local + network = SvcTestCaseFactory.createLocalVoltageControl(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_SVC2_SVC_T_1", "voltage"); + + // Remote + network = SvcTestCaseFactory.createRemoteVoltageControl(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_L2_EC_T_1", "voltage"); + + // SVC REACTIVE_POWER + // Local + network = SvcTestCaseFactory.createLocalReactiveControl(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_SVC2_SVC_T_1", "reactivePower"); + + // Remote + network = SvcTestCaseFactory.createRemoteReactiveControl(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_L2_EC_T_1", "reactivePower"); + + // SVC OFF + // Local + network = SvcTestCaseFactory.createLocalOffNoTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRCWithoutAttribute(eq, "_SVC2_RC", "_SVC2_SVC_T_1", "dummy"); + network = SvcTestCaseFactory.createLocalOffReactiveTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_SVC2_SVC_T_1", "reactivePower"); + network = SvcTestCaseFactory.createLocalOffVoltageTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_SVC2_SVC_T_1", "voltage"); + network = SvcTestCaseFactory.createLocalOffBothTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_SVC2_SVC_T_1", "voltage"); + + // Remote + network = SvcTestCaseFactory.createRemoteOffNoTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_L2_EC_T_1", "voltage"); + network = SvcTestCaseFactory.createRemoteOffReactiveTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_L2_EC_T_1", "reactivePower"); + network = SvcTestCaseFactory.createRemoteOffVoltageTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_L2_EC_T_1", "voltage"); + network = SvcTestCaseFactory.createRemoteOffBothTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SVC2_RC", "_L2_EC_T_1", "voltage"); + } + } + + @Test + void shuntCompensatorRegulatingControlEQTest() throws IOException { + String exportFolder = "/test-sc-rc"; + String baseName = "testScRc"; + Network network; + String eq; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "EQ"); + + // SC linear + network = ShuntTestCaseFactory.create(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_LOAD_EC_T_1", "voltage"); + testRcEqRCWithoutAttribute(eq, "", "", "reactivePower"); + + network = ShuntTestCaseFactory.createLocalLinear(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_SHUNT_SC_T_1", "voltage"); + testRcEqRCWithoutAttribute(eq, "", "", "reactivePower"); + + network = ShuntTestCaseFactory.createDisabledRemoteLinear(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_LOAD_EC_T_1", "voltage"); + testRcEqRCWithoutAttribute(eq, "", "", "reactivePower"); + + network = ShuntTestCaseFactory.createDisabledLocalLinear(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_SHUNT_SC_T_1", "voltage"); + testRcEqRCWithoutAttribute(eq, "", "", "reactivePower"); + + network = ShuntTestCaseFactory.createLocalLinearNoTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRCWithoutAttribute(eq, "_SHUNT_RC", "", ""); + + network = ShuntTestCaseFactory.createRemoteLinearNoTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_LOAD_EC_T_1", "voltage"); + testRcEqRCWithoutAttribute(eq, "", "", "reactivePower"); + + // SC nonlinear + network = ShuntTestCaseFactory.createNonLinear(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_LOAD_EC_T_1", "voltage"); + testRcEqRCWithoutAttribute(eq, "", "", "reactivePower"); + + network = ShuntTestCaseFactory.createLocalNonLinear(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_SHUNT_SC_T_1", "voltage"); + testRcEqRCWithoutAttribute(eq, "", "", "reactivePower"); + + network = ShuntTestCaseFactory.createDisabledRemoteNonLinear(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_LOAD_EC_T_1", "voltage"); + testRcEqRCWithoutAttribute(eq, "", "", "reactivePower"); + + network = ShuntTestCaseFactory.createDisabledLocalNonLinear(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_SHUNT_SC_T_1", "voltage"); + + network = ShuntTestCaseFactory.createLocalNonLinearNoTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRCWithoutAttribute(eq, "_SHUNT_RC", "", ""); + + network = ShuntTestCaseFactory.createRemoteNonLinearNoTarget(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_SHUNT_RC", "_LOAD_EC_T_1", "voltage"); + testRcEqRCWithoutAttribute(eq, "", "", "reactivePower"); + } + } + + @Test + void generatorRegulatingControlEQTest() throws IOException { + String exportFolder = "/test-gen-rc"; + String baseName = "testGenRc"; + Network network; + String eq; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "EQ"); + + // Generator local voltage + network = EurostagTutorialExample1Factory.create(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "voltage"); + network.getGenerator("GEN").setVoltageRegulatorOn(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "voltage"); + + // Generator remote voltage + network = EurostagTutorialExample1Factory.createWithRemoteVoltageGenerator(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "voltage"); + network.getGenerator("GEN").setVoltageRegulatorOn(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "voltage"); + + // Generator with local reactive + network = EurostagTutorialExample1Factory.createWithLocalReactiveGenerator(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "reactivePower"); + network.getGenerator("GEN").getExtension(RemoteReactivePowerControl.class).setEnabled(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "reactivePower"); + + // Generator with remote reactive + network = EurostagTutorialExample1Factory.createWithRemoteReactiveGenerator(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "reactivePower"); + network.getGenerator("GEN").getExtension(RemoteReactivePowerControl.class).setEnabled(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "reactivePower"); + + // Generator with local reactive and voltage + network = EurostagTutorialExample1Factory.createWithLocalReactiveAndVoltageGenerator(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "voltage"); + network.getGenerator("GEN").setVoltageRegulatorOn(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "reactivePower"); + network.getGenerator("GEN").getExtension(RemoteReactivePowerControl.class).setEnabled(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "voltage"); + network.getGenerator("GEN").setVoltageRegulatorOn(true); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "voltage"); + + // Generator with remote reactive and voltage + network = EurostagTutorialExample1Factory.createWithRemoteReactiveAndVoltageGenerators(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "voltage"); + network.getGenerator("GEN").setVoltageRegulatorOn(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "reactivePower"); + network.getGenerator("GEN").getExtension(RemoteReactivePowerControl.class).setEnabled(false); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "voltage"); + network.getGenerator("GEN").setVoltageRegulatorOn(true); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "voltage"); + + // Generator without control + network = EurostagTutorialExample1Factory.createWithoutControl(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_GEN_SM_T_1", "voltage"); + + // Generator with remote terminal without control + network = EurostagTutorialExample1Factory.createRemoteWithoutControl(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(eq, "_GEN_RC", "_NHV2_NLOAD_PT_T_1", "voltage"); + + // Generator without control capability + network = EurostagTutorialExample1Factory.create(); + network.getGenerator("GEN").newMinMaxReactiveLimits().setMaxQ(0).setMinQ(0).add(); + eq = getEQ(network, baseName, tmpDir, exportParams); + testRcEqRCWithoutAttribute(eq, "_GEN_RC", "", "dummy"); + } + } + + private void testTcTccWithoutAttribute(String eq, String rcID, String terID, String rcMode) { + assertFalse(eq.contains("cim:TapChangerControl rdf:ID=\"" + rcID + "\"")); + assertFalse(eq.contains("cim:TapChanger.TapChangerControl rdf:resource=\"#" + rcID + "\"")); + assertFalse(eq.contains("RegulatingControlModeKind." + rcMode)); + assertFalse(eq.contains("cim:RegulatingControl.Terminal rdf:resource=\"#" + terID + "\"")); + } + + private void testTcTccWithAttribute(String eq, String rcID, String terID, String rcMode) { + assertTrue(eq.contains("cim:TapChangerControl rdf:ID=\"" + rcID + "\"")); + assertTrue(eq.contains("cim:TapChanger.TapChangerControl rdf:resource=\"#" + rcID + "\"")); + assertTrue(eq.contains("RegulatingControlModeKind." + rcMode)); + assertTrue(eq.contains("cim:RegulatingControl.Terminal rdf:resource=\"#" + terID + "\"")); + } + + private void testRcEqRCWithoutAttribute(String eq, String rcID, String terID, String rcMode) { + assertFalse(eq.contains("cim:RegulatingControl rdf:ID=\"" + rcID + "\"")); + assertFalse(eq.contains("cim:RegulatingCondEq.RegulatingControl rdf:resource=\"#" + rcID + "\"")); + // dummy kind is used when false assertion would trigger because other RCs are present from other equipment + assertFalse(eq.contains("RegulatingControlModeKind." + rcMode)); + assertFalse(eq.contains("cim:RegulatingControl.Terminal rdf:resource=\"#" + terID + "\"")); + } + + private void testRcEqRcWithAttribute(String eq, String rcID, String terID, String rcMode) { + assertTrue(eq.contains("cim:RegulatingControl rdf:ID=\"" + rcID + "\"")); + assertTrue(eq.contains("cim:RegulatingCondEq.RegulatingControl rdf:resource=\"#" + rcID + "\"")); + assertTrue(eq.contains("RegulatingControlModeKind." + rcMode)); + assertTrue(eq.contains("cim:RegulatingControl.Terminal rdf:resource=\"#" + terID + "\"")); + } + + private String getEQ(Network network, String baseName, Path tmpDir, Properties exportParams) throws IOException { + new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName)); + return Files.readString(tmpDir.resolve(baseName + "_EQ.xml")); + } + private Network createOneGeneratorNetwork() { Network network = NetworkFactory.findDefault().createNetwork("network", "test"); Substation substation1 = network.newSubstation() diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java index 9c69ed7d45f..bf5b4c2c7dd 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/SteadyStateHypothesisExportTest.java @@ -7,6 +7,8 @@ */ package com.powsybl.cgmes.conversion.test.export; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; import com.powsybl.cgmes.conformity.*; import com.powsybl.cgmes.conversion.CgmesExport; import com.powsybl.cgmes.conversion.CgmesImport; @@ -23,6 +25,11 @@ import com.powsybl.iidm.network.ImportConfig; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.NetworkFactory; +import com.powsybl.iidm.network.extensions.RemoteReactivePowerControl; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import com.powsybl.iidm.network.test.PhaseShifterTestCaseFactory; +import com.powsybl.iidm.network.test.ShuntTestCaseFactory; +import com.powsybl.iidm.network.test.SvcTestCaseFactory; import com.powsybl.iidm.serde.NetworkSerDe; import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.BeforeEach; @@ -33,6 +40,7 @@ import javax.xml.stream.*; import java.io.*; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -382,6 +390,400 @@ void miniGridCgmesExportPreservingOriginalClasses() throws IOException, XMLStrea assertTrue(ExportXmlCompare.compareSSH(expectedSsh, new ByteArrayInputStream(actualSsh.getBytes(StandardCharsets.UTF_8)), knownDiffsSsh)); } + @Test + void phaseTapChangerTapChangerControlSSHTest() throws IOException { + String exportFolder = "/test-pst-tcc"; + String baseName = "testPstTcc"; + Network network; + String ssh; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "SSH"); + + // PST with FIXED_TAP + network = PhaseShifterTestCaseFactory.createWithTargetDeadband(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithoutAttribute(ssh, "_PS1_PTC_RC", "true", "false", "10", "200", "M"); + + // PST local with ACTIVE_POWER_CONTROL + network = PhaseShifterTestCaseFactory.createLocalActivePowerWithTargetDeadband(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_PS1_PTC_RC", "true", "false", "10", "200", "M"); + network.getTwoWindingsTransformer("PS1").getPhaseTapChanger().setRegulating(true); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_PS1_PTC_RC", "true", "true", "10", "200", "M"); + + // PST local with CURRENT_LIMITER + network = PhaseShifterTestCaseFactory.createLocalCurrentLimiterWithTargetDeadband(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_PS1_PTC_RC", "true", "false", "10", "200", "none"); + network.getTwoWindingsTransformer("PS1").getPhaseTapChanger().setRegulating(true); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_PS1_PTC_RC", "true", "true", "10", "200", "none"); + + // PST remote with CURRENT_LIMITER + network = PhaseShifterTestCaseFactory.createRemoteCurrentLimiterWithTargetDeadband(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_PS1_PTC_RC", "true", "false", "10", "200", "none"); + network.getTwoWindingsTransformer("PS1").getPhaseTapChanger().setRegulating(true); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_PS1_PTC_RC", "true", "true", "10", "200", "none"); + + // PST remote with ACTIVE_POWER_CONTROL + network = PhaseShifterTestCaseFactory.createRemoteActivePowerWithTargetDeadband(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_PS1_PTC_RC", "true", "false", "10", "200", "M"); + network.getTwoWindingsTransformer("PS1").getPhaseTapChanger().setRegulating(true); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_PS1_PTC_RC", "true", "true", "10", "200", "M"); + } + } + + @Test + void ratioTapChangerTapChangerControlSSHTest() throws IOException { + String exportFolder = "/test-rtc-tcc"; + String baseName = "testRtcTcc"; + Network network; + String ssh; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "SSH"); + + // RTC without control + network = EurostagTutorialExample1Factory.createWithoutRtcControl(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithoutAttribute(ssh, "_NHV2_NLOAD_RTC_RC", "", "", "", "", ""); + + // RTC local with VOLTAGE + network = EurostagTutorialExample1Factory.create(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NHV2_NLOAD_RTC_RC", "true", "true", "0", "158", "k"); + network.getTwoWindingsTransformer("NHV2_NLOAD").getRatioTapChanger().setRegulating(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NHV2_NLOAD_RTC_RC", "true", "false", "0", "158", "k"); + + // RTC local with REACTIVE_POWER + network = EurostagTutorialExample1Factory.createWithReactiveTcc(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NHV2_NLOAD_RTC_RC", "true", "true", "0", "100", "M"); + network.getTwoWindingsTransformer("NHV2_NLOAD").getRatioTapChanger().setRegulating(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NHV2_NLOAD_RTC_RC", "true", "false", "0", "100", "M"); + + // RTC remote with VOLTAGE + network = EurostagTutorialExample1Factory.createRemoteVoltageTcc(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NHV2_NLOAD_RTC_RC", "true", "true", "0", "158", "k"); + network.getTwoWindingsTransformer("NHV2_NLOAD").getRatioTapChanger().setRegulating(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NHV2_NLOAD_RTC_RC", "true", "false", "0", "158", "k"); + + // RTC remote with REACTIVE_POWER + network = EurostagTutorialExample1Factory.createRemoteReactiveTcc(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NHV2_NLOAD_RTC_RC", "true", "true", "0", "100", "M"); + network.getTwoWindingsTransformer("NHV2_NLOAD").getRatioTapChanger().setRegulating(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NHV2_NLOAD_RTC_RC", "true", "false", "0", "100", "M"); + + // 3w without control + network = EurostagTutorialExample1Factory.createWith3wWithoutControl(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithoutAttribute(ssh, "_NGEN_V2_NHV1_RTC_RC", "", "", "", "", ""); + + // 3w with local voltage control + network = EurostagTutorialExample1Factory.createWith3wWithVoltageControl(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NGEN_V2_NHV1_RTC_RC", "true", "true", "0", "158", "k"); + + // 3w with local reactive control + network = EurostagTutorialExample1Factory.create3wWithReactiveTcc(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NGEN_V2_NHV1_RTC_RC", "true", "true", "0", "100", "M"); + network.getThreeWindingsTransformer("NGEN_V2_NHV1").getLeg1().getRatioTapChanger().setRegulating(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NGEN_V2_NHV1_RTC_RC", "true", "false", "0", "100", "M"); + + // 3w with remote voltage + network = EurostagTutorialExample1Factory.create3wRemoteVoltageTcc(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NGEN_V2_NHV1_RTC_RC", "true", "true", "0", "158", "k"); + network.getThreeWindingsTransformer("NGEN_V2_NHV1").getLeg1().getRatioTapChanger().setRegulating(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NGEN_V2_NHV1_RTC_RC", "true", "false", "0", "158", "k"); + + // 3w with remote reactive + network = EurostagTutorialExample1Factory.create3wRemoteReactiveTcc(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NGEN_V2_NHV1_RTC_RC", "true", "true", "0", "100", "M"); + network.getThreeWindingsTransformer("NGEN_V2_NHV1").getLeg1().getRatioTapChanger().setRegulating(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithAttribute(ssh, "_NGEN_V2_NHV1_RTC_RC", "true", "false", "0", "100", "M"); + } + } + + @Test + void staticVarCompensatorRegulatingControlSSHTest() throws IOException { + String exportFolder = "/test-svc-rc"; + String baseName = "testSvcRc"; + Network network; + String ssh; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "SSH"); + + // SVC VOLTAGE + // Local + network = SvcTestCaseFactory.createLocalVoltageControl(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "true", "0", "390", "k"); + + // Remote + network = SvcTestCaseFactory.createRemoteVoltageControl(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "true", "0", "390", "k"); + + // SVC REACTIVE_POWER + // Local + network = SvcTestCaseFactory.createLocalReactiveControl(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "true", "0", "350", "M"); + + // Remote + network = SvcTestCaseFactory.createRemoteReactiveControl(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "true", "0", "350", "M"); + + // SVC OFF + // Local + network = SvcTestCaseFactory.createLocalOffNoTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRCWithoutAttribute(ssh, "_SVC2_RC"); + network = SvcTestCaseFactory.createLocalOffReactiveTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "false", "0", "350", "M"); + network = SvcTestCaseFactory.createLocalOffVoltageTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "false", "0", "390", "k"); + network = SvcTestCaseFactory.createLocalOffBothTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "false", "0", "390", "k"); + + // Remote + network = SvcTestCaseFactory.createRemoteOffNoTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "false", "0", "0", "k"); + network = SvcTestCaseFactory.createRemoteOffReactiveTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "false", "0", "350", "M"); + network = SvcTestCaseFactory.createRemoteOffVoltageTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "false", "0", "390", "k"); + network = SvcTestCaseFactory.createRemoteOffBothTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SVC2_RC", "false", "false", "0", "390", "k"); + } + } + + @Test + void shuntCompensatorRegulatingControlSSHTest() throws IOException { + String exportFolder = "/test-sc-rc"; + String baseName = "testScRc"; + Network network; + String ssh; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "SSH"); + + // SC linear + network = ShuntTestCaseFactory.create(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "true", "5", "200", "k"); + testTcTccWithoutAttribute(ssh, "", "", "", "", "", "M"); + + network = ShuntTestCaseFactory.createLocalLinear(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "true", "5", "200", "k"); + testTcTccWithoutAttribute(ssh, "", "", "", "", "", "M"); + + network = ShuntTestCaseFactory.createDisabledRemoteLinear(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "false", "5", "200", "k"); + testTcTccWithoutAttribute(ssh, "", "", "", "", "", "M"); + + network = ShuntTestCaseFactory.createDisabledLocalLinear(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "false", "5", "200", "k"); + testTcTccWithoutAttribute(ssh, "", "", "", "", "", "M"); + + network = ShuntTestCaseFactory.createLocalLinearNoTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithoutAttribute(ssh, "_SHUNT_RC", "", "", "", "", ""); + + network = ShuntTestCaseFactory.createRemoteLinearNoTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "false", "5", "0", "k"); + + // SC nonlinear + network = ShuntTestCaseFactory.createNonLinear(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "true", "5", "200", "k"); + testTcTccWithoutAttribute(ssh, "", "", "", "", "", "M"); + + network = ShuntTestCaseFactory.createLocalNonLinear(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "true", "5", "200", "k"); + testTcTccWithoutAttribute(ssh, "", "", "", "", "", "M"); + + network = ShuntTestCaseFactory.createDisabledRemoteNonLinear(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "false", "5", "200", "k"); + testTcTccWithoutAttribute(ssh, "", "", "", "", "", "M"); + + network = ShuntTestCaseFactory.createDisabledLocalNonLinear(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "false", "5", "200", "k"); + testTcTccWithoutAttribute(ssh, "", "", "", "", "", "M"); + + network = ShuntTestCaseFactory.createLocalNonLinearNoTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testTcTccWithoutAttribute(ssh, "_SHUNT_RC", "", "", "", "", ""); + + network = ShuntTestCaseFactory.createRemoteNonLinearNoTarget(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_SHUNT_RC", "true", "false", "5", "0", "k"); + } + } + + @Test + void generatorRegulatingControlSSHTest() throws IOException { + String exportFolder = "/test-gen-rc"; + String baseName = "testGenRc"; + Network network; + String ssh; + try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) { + Path tmpDir = Files.createDirectory(fs.getPath(exportFolder)); + Properties exportParams = new Properties(); + exportParams.put(CgmesExport.PROFILES, "SSH"); + + // Generator local voltage + network = EurostagTutorialExample1Factory.create(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "24.5", "k"); + network.getGenerator("GEN").setVoltageRegulatorOn(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "24.5", "k"); + + // Generator remote voltage + network = EurostagTutorialExample1Factory.createWithRemoteVoltageGenerator(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "24.5", "k"); + network.getGenerator("GEN").setVoltageRegulatorOn(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "24.5", "k"); + + // Generator with local reactive + network = EurostagTutorialExample1Factory.createWithLocalReactiveGenerator(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "200", "M"); + network.getGenerator("GEN").getExtension(RemoteReactivePowerControl.class).setEnabled(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "200", "M"); + + // Generator with remote reactive + network = EurostagTutorialExample1Factory.createWithRemoteReactiveGenerator(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "200", "M"); + network.getGenerator("GEN").getExtension(RemoteReactivePowerControl.class).setEnabled(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "200", "M"); + + // Generator with local reactive and voltage + network = EurostagTutorialExample1Factory.createWithLocalReactiveAndVoltageGenerator(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "24.5", "k"); + network.getGenerator("GEN").setVoltageRegulatorOn(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "200", "M"); + network.getGenerator("GEN").getExtension(RemoteReactivePowerControl.class).setEnabled(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "24.5", "k"); + network.getGenerator("GEN").setVoltageRegulatorOn(true); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "24.5", "k"); + + // Generator with remote reactive and voltage + network = EurostagTutorialExample1Factory.createWithRemoteReactiveAndVoltageGenerators(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "24.5", "k"); + network.getGenerator("GEN").setVoltageRegulatorOn(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "200", "M"); + network.getGenerator("GEN").getExtension(RemoteReactivePowerControl.class).setEnabled(false); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "24.5", "k"); + network.getGenerator("GEN").setVoltageRegulatorOn(true); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "true", "0", "24.5", "k"); + + // Generator without control + network = EurostagTutorialExample1Factory.createWithoutControl(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "0", "k"); + + // Generator with remote terminal without control + network = EurostagTutorialExample1Factory.createRemoteWithoutControl(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRcWithAttribute(ssh, "_GEN_RC", "false", "false", "0", "0", "k"); + + // Generator without control capability + network = EurostagTutorialExample1Factory.create(); + network.getGenerator("GEN").newMinMaxReactiveLimits().setMaxQ(0).setMinQ(0).add(); + ssh = getSSH(network, baseName, tmpDir, exportParams); + testRcEqRCWithoutAttribute(ssh, "_GEN_RC"); + } + } + + private void testTcTccWithoutAttribute(String ssh, String rcID, String discrete, String enabled, String deadband, String target, String multiplier) { + assertFalse(ssh.contains("cim:TapChangerControl rdf:about=\"#" + rcID + "\"")); + assertFalse(ssh.contains("" + discrete + "")); + assertFalse(ssh.contains("" + enabled + "")); + assertFalse(ssh.contains("" + deadband + "")); + assertFalse(ssh.contains("" + target + "")); + assertFalse(ssh.contains("UnitMultiplier." + multiplier + "\"")); + } + + private void testTcTccWithAttribute(String ssh, String rcID, String discrete, String enabled, String deadband, String target, String multiplier) { + assertTrue(ssh.contains("cim:TapChangerControl rdf:about=\"#" + rcID + "\"")); + assertTrue(ssh.contains("" + discrete + "")); + assertTrue(ssh.contains("" + enabled + "")); + assertTrue(ssh.contains("" + deadband + "")); + assertTrue(ssh.contains("" + target + "")); + assertTrue(ssh.contains("UnitMultiplier." + multiplier + "\"")); + } + + private void testRcEqRCWithoutAttribute(String ssh, String rcID) { + assertFalse(ssh.contains("cim:RegulatingControl rdf:about=\"#" + rcID + "\"")); + } + + private void testRcEqRcWithAttribute(String ssh, String rcID, String discrete, String enabled, String deadband, String target, String multiplier) { + assertTrue(ssh.contains("cim:RegulatingControl rdf:about=\"#" + rcID + "\"")); + assertTrue(ssh.contains("" + discrete + "")); + assertTrue(ssh.contains("" + enabled + "")); + assertTrue(ssh.contains("" + deadband + "")); + assertTrue(ssh.contains("" + target + "")); + assertTrue(ssh.contains("UnitMultiplier." + multiplier + "\"")); + } + + private String getSSH(Network network, String baseName, Path tmpDir, Properties exportParams) throws IOException { + new CgmesExport().export(network, exportParams, new DirectoryDataSource(tmpDir, baseName)); + return Files.readString(tmpDir.resolve(baseName + "_SSH.xml")); + } + private static void copyBoundary(Path outputFolder, String baseName, ReadOnlyDataSource originalDataSource) throws IOException { String eqbd = originalDataSource.listNames(".*EQ_BD.*").stream().findFirst().orElse(null); if (eqbd != null) { diff --git a/docs/grid_exchange_formats/cgmes/export.md b/docs/grid_exchange_formats/cgmes/export.md index 908702b272e..a5ca33a8773 100644 --- a/docs/grid_exchange_formats/cgmes/export.md +++ b/docs/grid_exchange_formats/cgmes/export.md @@ -178,7 +178,16 @@ Each dangling line will be exported as one `EquivalentInjection` and one `ACLine PowSyBl [`Generator`](../../grid_model/network_subnetwork.md#generator) is exported as CGMES `SynchronousMachine`. -TODO details +#### Regulating control + +If the network comes from a CIM-CGMES model and a generator has initially a `RegulatingControl`, it always has at export +too. Otherwise, a `RegulatingControl` is always exported for generators, except if it has no regulating capabilities because +$minQ = maxQ$. + +A `RegulatingControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.reactivePower` when a +generator has the extension [`RemoteReactivePowerControl`](../../grid_model/extensions.md#remote-reactive-power-control) +with the `enabled` activated and the generator attribute `voltageRegulatorOn` set to `false`. In all other cases, a +`RegulatingControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.voltage`. ### HVDC line and HVDC converter stations @@ -202,13 +211,32 @@ PowSyBl [`Load`](../../grid_model/network_subnetwork.md#load) is exported as `Co PowSyBl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) is exported as `LinearShuntCompensator` or `NonlinearShuntCompensator` depending on their models. -TODO details +#### Regulating control + +If the network comes from a CIM-CGMES model and a shunt compensator has initially a `RegulatingControl`, it always +has at export too. + +A shunt compensator with local voltage control (i.e. the regulating terminal is the same of the terminal of connection) +and no valid voltage target will not have any exported regulating control. In all other cases, a `RegulatingControl` +is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.voltage`. ### StaticVarCompensator PowSyBl [`StaticVarCompensator`](../../grid_model/network_subnetwork.md#static-var-compensator) is exported as `StaticVarCompensator`. -TODO details +#### Regulating control + +If the network comes from a CIM-CGMES model and a static VAR compensator has initially a `RegulatingControl`, it always +has at export too. + +A static VAR compensator which voltage control is local (i.e. the regulating terminal is the same of the terminal of +connection) and no valid voltage or reactive power target will not have any exported regulating control. + +A `RegulatingControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.reactivePower` when +the static VAR compensator mode is `REACTIVE_POWER`. A `RegulatingControl` is exported with `RegulatingControl.mode` set +to `RegulatingControlModeKind.voltage` when the static VAR compensator mode is `VOLTAGE`. When the static VAR compensator +is `OFF`, the exported regulating control mode will be reactive power only if the voltage target is not valid but the +reactive power target is. Otherwise, the exported mode will be voltage. ### Substation @@ -225,13 +253,28 @@ PowSyBl [`Switch`](../../grid_model/network_subnetwork.md#breakerswitch) is expo ### ThreeWindingsTransformer PowSyBl [`ThreeWindingsTransformer`](../../grid_model/network_subnetwork.md#three-windings-transformer) is exported as `PowerTransformer` with three `PowerTransformerEnds`. -TODO details + +#### Tap changer control + +If the network comes from a CIM-CGMES model and the tap changer has initially a `TapChangerControl`, it always has at export +too. Otherwise, a `TapChangerControl` is exported for the tap changer if it is considered as defined. A `RatioTapChanger` +is considered as defined if it has a valid regulation value, a valid target deadband and a non-null regulating terminal. +A `PhaseTapChanger` is considered as defined if it has a regulation mode different of `FIXED_TAP`, a valid regulation value, +a valid target deadband, and a non-null regulating terminal. + +In a `RatioTapChanger`, the `TapChangerControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.reactivePower` when +`RatioTapChanger` `regulationMode` is set to `REACTIVE_POWER`, and with `RegulatingControl.mode` set to `RegulatingControlModeKind.voltage` when +`RatioTapChanger` `regulationMode` is set to `VOLTAGE`. + +In a `PhaseTapChanger`, the `TapChangerControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.activePower` when +`PhaseTapChanger` `regulationMode` is set to `ACTIVE_POWER_CONTROL`, and with `RegulatingControl.mode` set to `RegulatingControlModeKind.currentFlow` +when `PhaseTapChanger` `regulationMode` is set to `CURRENT_LIMITER`. ### TwoWindingsTransformer PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-windings-transformer) is exported as `PowerTransformer` with two `PowerTransformerEnds`. -TODO details +Tap changer controls for two windings transformers are exported following the same rules explained in the previous section about three windings transformers. See [tap changer control](#tap-changer-control). ### Voltage level diff --git a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java index 0bef269be0b..690b27e1a52 100644 --- a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java +++ b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/EurostagTutorialExample1Factory.java @@ -8,6 +8,7 @@ package com.powsybl.iidm.network.test; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.RemoteReactivePowerControlAdder; import java.time.ZonedDateTime; /** @@ -31,6 +32,9 @@ public final class EurostagTutorialExample1Factory { public static final String NGEN_NHV1 = "NGEN_NHV1"; public static final String NHV2_NLOAD = "NHV2_NLOAD"; public static final String XNODE_1 = "XNODE1"; + public static final String NGEN_V2_NHV1 = "NGEN_V2_NHV1"; + public static final String NGEN = "NGEN"; + public static final String NHV1 = "NHV1"; private EurostagTutorialExample1Factory() { } @@ -74,10 +78,10 @@ public static Network create(NetworkFactory networkFactory) { .setTopologyKind(TopologyKind.BUS_BREAKER) .add(); Bus ngen = vlgen.getBusBreakerView().newBus() - .setId("NGEN") + .setId(NGEN) .add(); Bus nhv1 = vlhv1.getBusBreakerView().newBus() - .setId("NHV1") + .setId(NHV1) .add(); Bus nhv2 = vlhv2.getBusBreakerView().newBus() .setId("NHV2") @@ -220,8 +224,8 @@ public static Network createWithTieLines(NetworkFactory networkFactory) { .setX(20.0) .setG(1E-6) .setB(386E-6 / 2) - .setBus("NHV1") .setPairingKey(XNODE_1) + .setBus(NHV1) .add(); DanglingLine xnode1nhv2 = network.getVoltageLevel(VLHV2).newDanglingLine() .setId(DANGLING_LINE_XNODE1_2) @@ -247,7 +251,7 @@ public static Network createWithTieLines(NetworkFactory networkFactory) { .setX(20.0) .setG(1E-6) .setB(386E-6 / 2) - .setBus("NHV1") + .setBus(NHV1) .setPairingKey("XNODE2") .add(); DanglingLine xnode2nhv2 = network.getVoltageLevel(VLHV2).newDanglingLine() @@ -290,10 +294,10 @@ public static Network createWithLFResults(NetworkFactory factory) { Network network = create(factory); network.setCaseDate(ZonedDateTime.parse("2013-01-15T18:45:00.000+01:00")); - network.getBusBreakerView().getBus("NGEN") + network.getBusBreakerView().getBus(NGEN) .setV(24.500000610351563) .setAngle(2.3259763717651367); - network.getBusBreakerView().getBus("NHV1") + network.getBusBreakerView().getBus(NHV1) .setV(402.1428451538086) .setAngle(0.0); network.getBusBreakerView().getBus("NHV2") @@ -345,7 +349,7 @@ public static Network createWithMoreGenerators(NetworkFactory networkFactory) { Network network = create(networkFactory); VoltageLevel vlgen = network.getVoltageLevel(VLGEN); - Bus ngen = vlgen.getBusBreakerView().getBus("NGEN"); + Bus ngen = vlgen.getBusBreakerView().getBus(NGEN); Generator generator2 = vlgen.newGenerator() .setId("GEN2") @@ -429,8 +433,8 @@ public static Network createWithFixedCurrentLimits(NetworkFactory networkFactory network.getVoltageLevel(VLGEN).newGenerator() .setId("GEN2") - .setBus("NGEN") - .setConnectableBus("NGEN") + .setBus(NGEN) + .setConnectableBus(NGEN) .setMinP(-9999.99) .setMaxP(9999.99) .setVoltageRegulatorOn(true) @@ -439,7 +443,7 @@ public static Network createWithFixedCurrentLimits(NetworkFactory networkFactory .setTargetQ(301.0) .add(); - ((Bus) network.getIdentifiable("NHV1")).setV(380).getVoltageLevel().setLowVoltageLimit(400).setHighVoltageLimit(500); + ((Bus) network.getIdentifiable(NHV1)).setV(380).getVoltageLevel().setLowVoltageLimit(400).setHighVoltageLimit(500); ((Bus) network.getIdentifiable("NHV2")).setV(380).getVoltageLevel().setLowVoltageLimit(300).setHighVoltageLimit(500); Line line = network.getLine(NHV1_NHV2_1); @@ -499,8 +503,8 @@ public static Network createWithFixedLimits(NetworkFactory networkFactory) { network.getVoltageLevel(VLGEN).newGenerator() .setId("GEN2") - .setBus("NGEN") - .setConnectableBus("NGEN") + .setBus(NGEN) + .setConnectableBus(NGEN) .setMinP(-9999.99) .setMaxP(9999.99) .setVoltageRegulatorOn(true) @@ -509,7 +513,7 @@ public static Network createWithFixedLimits(NetworkFactory networkFactory) { .setTargetQ(301.0) .add(); - ((Bus) network.getIdentifiable("NHV1")).setV(380).getVoltageLevel().setLowVoltageLimit(400).setHighVoltageLimit(500); + ((Bus) network.getIdentifiable(NHV1)).setV(380).getVoltageLevel().setLowVoltageLimit(400).setHighVoltageLimit(500); ((Bus) network.getIdentifiable("NHV2")).setV(380).getVoltageLevel().setLowVoltageLimit(300).setHighVoltageLimit(500); Line line = network.getLine(NHV1_NHV2_1); @@ -604,8 +608,8 @@ public static Network createWithFixedCurrentLimitsOnDanglingLines(NetworkFactory network.getVoltageLevel(VLGEN).newGenerator() .setId("GEN2") - .setBus("NGEN") - .setConnectableBus("NGEN") + .setBus(NGEN) + .setConnectableBus(NGEN) .setMinP(-9999.99) .setMaxP(9999.99) .setVoltageRegulatorOn(true) @@ -614,7 +618,7 @@ public static Network createWithFixedCurrentLimitsOnDanglingLines(NetworkFactory .setTargetQ(301.0) .add(); - ((Bus) network.getIdentifiable("NHV1")).setV(380).getVoltageLevel().setLowVoltageLimit(400).setHighVoltageLimit(500); + ((Bus) network.getIdentifiable(NHV1)).setV(380).getVoltageLevel().setLowVoltageLimit(400).setHighVoltageLimit(500); ((Bus) network.getIdentifiable("NHV2")).setV(380).getVoltageLevel().setLowVoltageLimit(300).setHighVoltageLimit(500); DanglingLine danglingLine1 = network.getDanglingLine(DANGLING_LINE_XNODE1_1); @@ -676,8 +680,8 @@ public static Network createWithFixedLimitsOnDanglingLines(NetworkFactory networ network.getVoltageLevel(VLGEN).newGenerator() .setId("GEN2") - .setBus("NGEN") - .setConnectableBus("NGEN") + .setBus(NGEN) + .setConnectableBus(NGEN) .setMinP(-9999.99) .setMaxP(9999.99) .setVoltageRegulatorOn(true) @@ -686,7 +690,7 @@ public static Network createWithFixedLimitsOnDanglingLines(NetworkFactory networ .setTargetQ(301.0) .add(); - ((Bus) network.getIdentifiable("NHV1")).setV(380).getVoltageLevel().setLowVoltageLimit(400).setHighVoltageLimit(500); + ((Bus) network.getIdentifiable(NHV1)).setV(380).getVoltageLevel().setLowVoltageLimit(400).setHighVoltageLimit(500); ((Bus) network.getIdentifiable("NHV2")).setV(380).getVoltageLevel().setLowVoltageLimit(300).setHighVoltageLimit(500); DanglingLine danglingLine1 = network.getDanglingLine(DANGLING_LINE_XNODE1_1); @@ -923,7 +927,261 @@ public static Network createWithTieLinesAndAreas(NetworkFactory networkFactory) .addVoltageLevel(network.getVoltageLevel(VLHV2)) .addVoltageLevel(network.getVoltageLevel(VLLOAD)) .add(); + return network; + } + + public static Network createWithReactiveTcc() { + Network network = create(); + network.getTwoWindingsTransformer(NHV2_NLOAD) + .getRatioTapChanger() + .setRegulationMode(RatioTapChanger.RegulationMode.REACTIVE_POWER) + .setRegulationValue(100); + return network; + } + + public static Network createRemoteReactiveTcc() { + return createRemoteTcc(createWithReactiveTcc()); + } + + public static Network createRemoteVoltageTcc() { + return createRemoteTcc(create()); + } + + private static Network createRemoteTcc(Network network) { + network.getTwoWindingsTransformer(NHV2_NLOAD) + .getRatioTapChanger() + .setRegulationTerminal(network.getGenerator("GEN").getTerminal()); + + return network; + } + + public static Network createWithoutRtcControl() { + Network network = create(); + TwoWindingsTransformer nhv2Nload = network.getTwoWindingsTransformer(NHV2_NLOAD); + RatioTapChanger rtc = nhv2Nload.getRatioTapChanger(); + rtc.remove(); + nhv2Nload.newRatioTapChanger() + .beginStep() + .setRho(0.85f) + .setB(0.0) + .endStep() + .beginStep() + .setRho(1) + .setR(0.0) + .setX(0.0) + .setG(0.0) + .setB(0.0) + .endStep() + .beginStep() + .setRho(1.15f) + .setR(0.0) + .setX(0.0) + .setG(0.0) + .setB(0.0) + .endStep() + .setTapPosition(1) + .setLoadTapChangingCapabilities(false) + .add(); + return network; + } + + public static Network createWith3wTransformer() { + Network network = create(); + Substation p1 = network.getSubstation("P1"); + VoltageLevel v2 = p1.newVoltageLevel() + .setId("V2") + .setNominalV(150.0) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + v2.getBusBreakerView().newBus() + .setId("N2") + .add(); + network.getTwoWindingsTransformer(NHV2_NLOAD).remove(); + ThreeWindingsTransformerAdder threeWindingsTransformerAdder1 = p1.newThreeWindingsTransformer() + .setId(NGEN_V2_NHV1) + .setRatedU0(400); + threeWindingsTransformerAdder1.newLeg1() + .setBus(NHV1) + .setR(0.001) + .setX(0.000001) + .setB(0) + .setG(0) + .setRatedU(400) + .setVoltageLevel(VLHV1) + .add(); + threeWindingsTransformerAdder1.newLeg2() + .setBus("N2") + .setR(0.1) + .setX(0.00001) + .setB(0) + .setG(0) + .setRatedU(150.0) + .setVoltageLevel("V2") + .add(); + threeWindingsTransformerAdder1.newLeg3() + .setBus(NGEN) + .setR(0.01) + .setX(0.0001) + .setB(0) + .setG(0) + .setRatedU(24) + .setVoltageLevel(VLGEN) + .add(); + threeWindingsTransformerAdder1.add(); + return network; + } + + public static Network createWith3wWithVoltageControl() { + Network network = createWith3wTransformer(); + add3wRtcWithVoltageControl(network); + return network; + } + + public static Network createWith3wWithoutControl() { + Network network = createWith3wTransformer(); + add3wRtcWithoutControl(network); + return network; + } + + private static void add3wRtcWithVoltageControl(Network network) { + network.getThreeWindingsTransformer(NGEN_V2_NHV1).getLeg1().newRatioTapChanger() + .beginStep() + .setRho(0.85f) + .setR(0.0) + .setX(0.0) + .setG(0.0) + .setB(0.0) + .endStep() + .beginStep() + .setRho(1.0) + .setR(0.0) + .setX(0.0) + .setG(0.0) + .setB(0.0) + .endStep() + .beginStep() + .setRho(1.15f) + .setR(0.0) + .setX(0.0) + .setG(0.0) + .setB(0.0) + .endStep() + .setTapPosition(1) + .setLoadTapChangingCapabilities(true) + .setRegulating(true) + .setRegulationMode(RatioTapChanger.RegulationMode.VOLTAGE) + .setRegulationValue(158.0) + .setTargetDeadband(0) + .setRegulationTerminal(network.getThreeWindingsTransformer(NGEN_V2_NHV1).getLeg1().getTerminal()) + .add(); + } + + private static void add3wRtcWithoutControl(Network network) { + network.getThreeWindingsTransformer(NGEN_V2_NHV1).getLeg1().newRatioTapChanger() + .beginStep() + .setRho(0.85f) + .setR(0.0) + .setX(0.0) + .setG(0.0) + .setB(0.0) + .endStep() + .beginStep() + .setRho(1.0) + .setR(0.0) + .setX(0.0) + .setG(0.0) + .setB(0.0) + .endStep() + .beginStep() + .setRho(1.15f) + .setR(0.0) + .setX(0.0) + .setG(0.0) + .setB(0.0) + .endStep() + .setTapPosition(1) + .setLoadTapChangingCapabilities(false) + .add(); + } + + public static Network create3wWithReactiveTcc() { + Network network = createWith3wWithVoltageControl(); + network.getThreeWindingsTransformer(NGEN_V2_NHV1).getLeg1() + .getRatioTapChanger() + .setRegulationMode(RatioTapChanger.RegulationMode.REACTIVE_POWER) + .setRegulationValue(100); + return network; + } + + private static Network create3wRemoteTcc(Network network) { + network.getThreeWindingsTransformer(NGEN_V2_NHV1).getLeg1() + .getRatioTapChanger() + .setRegulationTerminal(network.getGenerator("GEN").getTerminal()); + return network; + } + + public static Network create3wRemoteReactiveTcc() { + return create3wRemoteTcc(create3wWithReactiveTcc()); + } + + public static Network create3wRemoteVoltageTcc() { + return create3wRemoteTcc(createWith3wWithVoltageControl()); + } + + public static Network createWithRemoteVoltageGenerator() { + return addRemoteVoltageGenerator(create()); + } + + public static Network createWithRemoteReactiveGenerator() { + return removeVoltageControlForGenerator(addRemoteReactiveGenerator(create())); + } + + public static Network createWithLocalReactiveGenerator() { + return removeVoltageControlForGenerator(addLocalReactiveGenerator(create())); + } + + public static Network createWithRemoteReactiveAndVoltageGenerators() { + return addRemoteVoltageGenerator(addRemoteReactiveGenerator(create())); + } + + public static Network createWithLocalReactiveAndVoltageGenerator() { + return addLocalReactiveGenerator(create()); + } + + public static Network createWithoutControl() { + return removeVoltageControlForGenerator(create()); + } + + public static Network createRemoteWithoutControl() { + return removeVoltageControlForGenerator(createWithRemoteVoltageGenerator()); + } + + private static Network addLocalReactiveGenerator(Network network) { + return addReactiveGenerator(network, network.getGenerator("GEN").getRegulatingTerminal()); + } + + private static Network addRemoteReactiveGenerator(Network network) { + return addReactiveGenerator(network, network.getTwoWindingsTransformer(NHV2_NLOAD).getTerminal1()); + } + + private static Network addReactiveGenerator(Network network, Terminal terminal) { + network.getGenerator("GEN").newExtension(RemoteReactivePowerControlAdder.class) + .withRegulatingTerminal(terminal) + .withTargetQ(200) + .withEnabled(true).add(); + return network; + } + + private static Network addRemoteVoltageGenerator(Network network) { + network.getGenerator("GEN") + .setRegulatingTerminal(network.getTwoWindingsTransformer(NHV2_NLOAD).getTerminal1()); + return network; + } + private static Network removeVoltageControlForGenerator(Network network) { + Generator gen = network.getGenerator("GEN"); + gen.setVoltageRegulatorOn(false); + gen.setTargetV(Double.NaN); return network; } } diff --git a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/PhaseShifterTestCaseFactory.java b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/PhaseShifterTestCaseFactory.java index c35769e0752..f51872ef3a7 100644 --- a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/PhaseShifterTestCaseFactory.java +++ b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/PhaseShifterTestCaseFactory.java @@ -183,4 +183,35 @@ public static Network createWithTargetDeadband() { .setTargetDeadband(10.0); return network; } + + public static Network createLocalActivePowerWithTargetDeadband() { + Network network = createWithTargetDeadband(); + network.getTwoWindingsTransformer("PS1") + .getPhaseTapChanger() + .setRegulationMode(PhaseTapChanger.RegulationMode.ACTIVE_POWER_CONTROL); + return network; + } + + public static Network createLocalCurrentLimiterWithTargetDeadband() { + Network network = createWithTargetDeadband(); + network.getTwoWindingsTransformer("PS1") + .getPhaseTapChanger() + .setRegulationMode(PhaseTapChanger.RegulationMode.CURRENT_LIMITER); + return network; + } + + public static Network createRemoteActivePowerWithTargetDeadband() { + return createRemote(createLocalActivePowerWithTargetDeadband()); + } + + public static Network createRemoteCurrentLimiterWithTargetDeadband() { + return createRemote(createLocalCurrentLimiterWithTargetDeadband()); + } + + private static Network createRemote(Network network) { + network.getTwoWindingsTransformer("PS1") + .getPhaseTapChanger() + .setRegulationTerminal(network.getLoad("LD2").getTerminal()); + return network; + } } diff --git a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/ShuntTestCaseFactory.java b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/ShuntTestCaseFactory.java index dbde19a85f9..5793c6e837d 100644 --- a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/ShuntTestCaseFactory.java +++ b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/ShuntTestCaseFactory.java @@ -141,4 +141,63 @@ private static Network createBase(NetworkFactory networkFactory) { .add(); return network; } + + public static Network createLocalLinear() { + return createLocalShunt(create()); + } + + public static Network createLocalShunt(Network network) { + network.getShuntCompensator(SHUNT) + .setRegulatingTerminal(network.getShuntCompensator(SHUNT).getTerminal()); + return network; + } + + public static Network createDisabledRemoteLinear() { + return createDisabledShunt(create()); + } + + public static Network createDisabledLocalLinear() { + return createDisabledShunt(createLocalLinear()); + } + + public static Network createDisabledRemoteNonLinear() { + return createDisabledShunt(createNonLinear()); + } + + public static Network createDisabledLocalNonLinear() { + return createDisabledShunt(createLocalNonLinear()); + } + + public static Network createDisabledShunt(Network network) { + network.getShuntCompensator(SHUNT) + .setVoltageRegulatorOn(false); + return network; + } + + private static Network createShuntWithNoTarget(Network network) { + network.getShuntCompensator(SHUNT) + .setVoltageRegulatorOn(false) + .setTargetV(Double.NaN); + return network; + } + + public static Network createRemoteLinearNoTarget() { + return createShuntWithNoTarget(create()); + } + + public static Network createRemoteNonLinearNoTarget() { + return createShuntWithNoTarget(createNonLinear()); + } + + public static Network createLocalLinearNoTarget() { + return createShuntWithNoTarget(createLocalLinear()); + } + + public static Network createLocalNonLinearNoTarget() { + return createShuntWithNoTarget(createLocalNonLinear()); + } + + public static Network createLocalNonLinear() { + return createLocalShunt(createNonLinear()); + } } diff --git a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/SvcTestCaseFactory.java b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/SvcTestCaseFactory.java index 23c29b1d070..7e1edab8857 100644 --- a/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/SvcTestCaseFactory.java +++ b/iidm/iidm-test/src/main/java/com/powsybl/iidm/network/test/SvcTestCaseFactory.java @@ -140,4 +140,125 @@ public static Network createWithRemoteRegulatingTerminal(NetworkFactory networkF return network; } + + private static Network addReactiveTarget(Network network) { + network.getStaticVarCompensator("SVC2") + .setReactivePowerSetpoint(350); + return network; + } + + private static Network addVoltageTarget(Network network) { + network.getStaticVarCompensator("SVC2") + .setVoltageSetpoint(390); + return network; + } + + private static Network addBothTarget(Network network) { + network.getStaticVarCompensator("SVC2") + .setReactivePowerSetpoint(350) + .setVoltageSetpoint(390); + return network; + } + + private static Network createLocalVoltageControl(Network network) { + return addVoltageControl(network); + } + + public static Network createLocalVoltageControl() { + return createLocalVoltageControl(create()); + } + + public static Network createRemoteVoltageControl() { + return createLocalVoltageControl(createWithRemoteRegulatingTerminal()); + } + + private static Network addVoltageControl(Network network) { + addVoltageTarget(network); + network.getStaticVarCompensator("SVC2") + .setRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE); + return network; + } + + private static Network createReactiveControl(Network network) { + return addReactiveControl(network); + } + + public static Network createLocalReactiveControl() { + return createReactiveControl(create()); + } + + public static Network createRemoteReactiveControl() { + return createReactiveControl(createWithRemoteRegulatingTerminal()); + } + + private static Network addReactiveControl(Network network) { + addReactiveTarget(network); + network.getStaticVarCompensator("SVC2") + .setRegulationMode(StaticVarCompensator.RegulationMode.REACTIVE_POWER); + return network; + } + + public static Network createLocalOffReactiveTarget() { + return createOffReactiveTarget(create()); + } + + public static Network createRemoteOffReactiveTarget() { + return createOffReactiveTarget(createWithRemoteRegulatingTerminal()); + } + + private static Network createOffReactiveTarget(Network network) { + return addOffReactiveTarget(network); + } + + private static Network addOffReactiveTarget(Network network) { + return addReactiveTarget(addOffNoTarget(network)); + } + + public static Network createLocalOffVoltageTarget() { + return createOffVoltageTarget(create()); + } + + public static Network createRemoteOffVoltageTarget() { + return createOffVoltageTarget(createWithRemoteRegulatingTerminal()); + } + + private static Network createOffVoltageTarget(Network network) { + return addOffVoltageTarget(network); + } + + private static Network addOffVoltageTarget(Network network) { + return addVoltageTarget(addOffNoTarget(network)); + } + + public static Network createLocalOffBothTarget() { + return createOffBothTarget(create()); + } + + public static Network createRemoteOffBothTarget() { + return createOffBothTarget(createWithRemoteRegulatingTerminal()); + } + + private static Network createOffBothTarget(Network network) { + return addOffBothTarget(network); + } + + private static Network addOffBothTarget(Network network) { + return addBothTarget(addOffNoTarget(network)); + } + + public static Network createLocalOffNoTarget() { + return addOffNoTarget(create()); + } + + public static Network createRemoteOffNoTarget() { + return addOffNoTarget(createWithRemoteRegulatingTerminal()); + } + + private static Network addOffNoTarget(Network network) { + network.getStaticVarCompensator("SVC2") + .setRegulationMode(StaticVarCompensator.RegulationMode.OFF) + .setVoltageSetpoint(Double.NaN) + .setReactivePowerSetpoint(Double.NaN); + return network; + } } From ccda94fa26fdddb5eca77b8e0f6b9ead4fb37607 Mon Sep 17 00:00:00 2001 From: Nicolas Rol Date: Thu, 1 Aug 2024 16:39:40 +0200 Subject: [PATCH 38/57] Datasources - Part 2: data extension and builder (#3102) * reorganize datasource classes * add tests * add dataExtension parameter to datasource * add builder and file information * add a conversion test * move boolean exists(String suffix, String ext) to AbstractFileSystemDataSource Signed-off-by: Nicolas Rol --- .../powsybl/cgmes/conversion/CgmesImport.java | 5 + .../cgmes/model/CgmesOnDataSource.java | 42 +++- .../datasource/AbstractArchiveDataSource.java | 4 +- .../AbstractFileSystemDataSource.java | 17 ++ .../datasource/Bzip2DirectoryDataSource.java | 8 +- .../commons/datasource/DataSourceBuilder.java | 108 ++++++++++ .../commons/datasource/DataSourceUtil.java | 59 +++--- .../datasource/DirectoryDataSource.java | 27 ++- .../commons/datasource/FileInformation.java | 115 +++++++++++ .../datasource/GenericReadOnlyDataSource.java | 35 +++- .../datasource/GzDirectoryDataSource.java | 8 +- .../MultipleReadOnlyDataSource.java | 5 + .../datasource/ReadOnlyDataSource.java | 13 +- .../datasource/ReadOnlyMemDataSource.java | 10 + .../datasource/ResourceDataSource.java | 19 +- .../datasource/XZDirectoryDataSource.java | 8 +- .../datasource/ZipArchiveDataSource.java | 22 +- .../datasource/ZstdDirectoryDataSource.java | 8 +- .../AbstractFileSystemDataSourceTest.java | 36 +++- .../Bzip2DirectoryDataSourceTest.java | 20 +- .../datasource/DataSourceBuilderTest.java | 104 ++++++++++ .../datasource/DirectoryDataSourceTest.java | 24 ++- .../datasource/FileInformationTest.java | 71 +++++++ .../GenericReadOnlyDataSourceTest.java | 189 ++++++++++++++++++ .../datasource/GzDirectoryDataSourceTest.java | 20 +- .../commons/datasource/MemDataSourceTest.java | 5 + .../MultipleReadOnlyDataSourceTest.java | 7 +- .../datasource/ResourcesDataSourceTest.java | 26 +++ .../datasource/XZDirectoryDataSourceTest.java | 20 +- .../datasource/ZipArchiveDataSourceTest.java | 34 ++-- .../ZstdDirectoryDataSourceTest.java | 20 +- commons/src/test/resources/test/foo.bar | 1 + .../ieeecdf/converter/IeeeCdfImporter.java | 2 +- .../network/MultipleImporterIssueTest.java | 103 ++++++++++ .../powsybl/iidm/network/TestImporter.java | 2 +- iidm/iidm-serde/pom.xml | 6 + .../iidm/serde/AbstractTreeDataImporter.java | 2 +- .../powsybl/iidm/serde/BinaryImporter.java | 2 +- .../com/powsybl/iidm/serde/JsonImporter.java | 2 +- .../com/powsybl/iidm/serde/XMLImporter.java | 2 +- .../powsybl/iidm/serde/ConversionTest.java | 96 +++++++++ .../matpower/converter/MatpowerImporter.java | 2 +- .../converter/PowerFactoryImporter.java | 2 +- .../powsybl/psse/converter/PsseImporter.java | 2 +- .../powsybl/tools/test/AbstractToolTest.java | 19 +- .../powsybl/ucte/converter/UcteImporter.java | 2 +- 46 files changed, 1154 insertions(+), 180 deletions(-) create mode 100644 commons/src/main/java/com/powsybl/commons/datasource/DataSourceBuilder.java create mode 100644 commons/src/main/java/com/powsybl/commons/datasource/FileInformation.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/DataSourceBuilderTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/FileInformationTest.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/GenericReadOnlyDataSourceTest.java create mode 100644 commons/src/test/resources/test/foo.bar create mode 100644 iidm/iidm-api/src/test/java/com/powsybl/iidm/network/MultipleImporterIssueTest.java create mode 100644 iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ConversionTest.java diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java index d2c5d68985e..807ea189556 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java @@ -194,6 +194,11 @@ public boolean exists(String suffix, String ext) throws IOException { return ds.exists(suffix, ext) && filter.test(DataSourceUtil.getFileName(getBaseName(), suffix, ext)); } + @Override + public boolean isDataExtension(String ext) { + return ds.isDataExtension(ext); + } + @Override public boolean exists(String fileName) throws IOException { return ds.exists(fileName) && filter.test(fileName); diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesOnDataSource.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesOnDataSource.java index 15ac210058a..4654c1a7490 100644 --- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesOnDataSource.java +++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesOnDataSource.java @@ -24,6 +24,8 @@ * @author Luma Zamarreño {@literal } */ public class CgmesOnDataSource { + private static final String EXTENSIONS = "xml"; + public CgmesOnDataSource(ReadOnlyDataSource ds) { this.dataSource = ds; } @@ -32,29 +34,55 @@ public ReadOnlyDataSource dataSource() { return dataSource; } + private boolean checkIfMainFileNotWithCgmesData(boolean isCim14) { + if (dataSource.getDataExtension() == null || dataSource.getDataExtension().isEmpty()) { + return false; + } else if (EXTENSIONS.equals(dataSource.getDataExtension())) { + try (InputStream is = dataSource.newInputStream(null, EXTENSIONS)) { + return isCim14 ? !existsNamespacesCim14(NamespaceReader.namespaces(is)) : !existsNamespaces(NamespaceReader.namespaces(is)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return true; + } + public boolean exists() { + // Check that the main file is a CGMES file + if (checkIfMainFileNotWithCgmesData(false)) { + return false; + } // check that RDF and CIM16 are defined as namespaces in the data source - Set foundNamespaces = namespaces(); - if (!foundNamespaces.contains(RDF_NAMESPACE)) { + return existsNamespaces(namespaces()); + } + + private boolean existsNamespaces(Set namespaces) { + if (!namespaces.contains(RDF_NAMESPACE)) { return false; } // FIXME(Luma) This is legacy behaviour, we do not consider CIM14 valid in this check // But I think we do not need to support 14 separately? - return foundNamespaces.contains(CIM_16_NAMESPACE) || foundNamespaces.contains(CIM_100_NAMESPACE); + return namespaces.contains(CIM_16_NAMESPACE) || namespaces.contains(CIM_100_NAMESPACE); } public boolean existsCim14() { + // Check that the main file is a CGMES file + if (checkIfMainFileNotWithCgmesData(true)) { + return false; + } // check that RDF and CIM16 are defined as namespaces in the data source - Set foundNamespaces = namespaces(); - if (!foundNamespaces.contains(RDF_NAMESPACE)) { + return existsNamespacesCim14(namespaces()); + } + + private boolean existsNamespacesCim14(Set namespaces) { + if (!namespaces.contains(RDF_NAMESPACE)) { return false; } // FIXME(Luma) This is legacy behaviour, we do not consider CIM14 valid in this check // But I think we do not need to support 14 separately? - if (!foundNamespaces.contains(CIM_14_NAMESPACE)) { + if (!namespaces.contains(CIM_14_NAMESPACE)) { return false; } - return names().stream().anyMatch(CgmesSubset.EQUIPMENT::isValidName); } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java index a1439a91845..6871f2b1149 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java @@ -17,8 +17,8 @@ public abstract class AbstractArchiveDataSource extends AbstractFileSystemDataSo private final String archiveFileName; final ArchiveFormat archiveFormat; - AbstractArchiveDataSource(Path directory, String archiveFileName, String baseName, CompressionFormat compressionFormat, ArchiveFormat archiveFormat, DataSourceObserver observer) { - super(directory, baseName, compressionFormat, observer); + AbstractArchiveDataSource(Path directory, String archiveFileName, String baseName, String dataExtension, CompressionFormat compressionFormat, ArchiveFormat archiveFormat, DataSourceObserver observer) { + super(directory, baseName, dataExtension, compressionFormat, observer); this.archiveFileName = archiveFileName; this.archiveFormat = archiveFormat; } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/AbstractFileSystemDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/AbstractFileSystemDataSource.java index 3b208ae0b09..335b0295b53 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/AbstractFileSystemDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/AbstractFileSystemDataSource.java @@ -17,6 +17,7 @@ abstract class AbstractFileSystemDataSource implements DataSource { final Path directory; final String baseName; + final String dataExtension; final CompressionFormat compressionFormat; final DataSourceObserver observer; @@ -27,14 +28,21 @@ abstract class AbstractFileSystemDataSource implements DataSource { * @param observer Data source observer */ AbstractFileSystemDataSource(Path directory, String baseName, + String dataExtension, CompressionFormat compressionFormat, DataSourceObserver observer) { this.directory = Objects.requireNonNull(directory); this.baseName = Objects.requireNonNull(baseName); + this.dataExtension = dataExtension; this.compressionFormat = compressionFormat; this.observer = observer; } + @Override + public boolean isDataExtension(String ext) { + return dataExtension == null || dataExtension.isEmpty() || dataExtension.equals(ext); + } + public Path getDirectory() { return this.directory; } @@ -44,10 +52,19 @@ public String getBaseName() { return baseName; } + @Override + public String getDataExtension() { + return dataExtension; + } + public CompressionFormat getCompressionFormat() { return compressionFormat; } + String getCompressionExtension() { + return compressionFormat == null ? "" : "." + compressionFormat.getExtension(); + } + public DataSourceObserver getObserver() { return observer; } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSource.java index f2a4ae78ad4..145b69d82ce 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSource.java @@ -18,12 +18,8 @@ */ public class Bzip2DirectoryDataSource extends DirectoryDataSource { - public Bzip2DirectoryDataSource(Path directory, String baseName, DataSourceObserver observer) { - super(directory, baseName, CompressionFormat.BZIP2, observer); - } - - public Bzip2DirectoryDataSource(Path directory, String baseName) { - super(directory, baseName, CompressionFormat.BZIP2, null); + public Bzip2DirectoryDataSource(Path directory, String baseName, String dataExtension, DataSourceObserver observer) { + super(directory, baseName, dataExtension, CompressionFormat.BZIP2, observer); } @Override diff --git a/commons/src/main/java/com/powsybl/commons/datasource/DataSourceBuilder.java b/commons/src/main/java/com/powsybl/commons/datasource/DataSourceBuilder.java new file mode 100644 index 00000000000..9736c8d3cbe --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/datasource/DataSourceBuilder.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.powsybl.commons.PowsyblException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * @author Nicolas Rol {@literal } + */ +class DataSourceBuilder { + private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceBuilder.class); + private Path directory; + private String baseName; + private String archiveFileName; + private CompressionFormat compressionFormat; + private ArchiveFormat archiveFormat; + private String dataExtension = ""; + private DataSourceObserver observer; + + DataSourceBuilder withDirectory(Path directory) { + this.directory = directory; + return this; + } + + DataSourceBuilder withBaseName(String baseName) { + this.baseName = baseName; + return this; + } + + DataSourceBuilder withArchiveFileName(String archiveFileName) { + this.archiveFileName = archiveFileName; + return this; + } + + DataSourceBuilder withCompressionFormat(CompressionFormat compressionFormat) { + this.compressionFormat = compressionFormat; + return this; + } + + DataSourceBuilder withArchiveFormat(ArchiveFormat archiveFormat) { + this.archiveFormat = archiveFormat; + return this; + } + + DataSourceBuilder withDataExtension(String dataExtension) { + this.dataExtension = dataExtension; + return this; + } + + DataSourceBuilder withObserver(DataSourceObserver observer) { + this.observer = observer; + return this; + } + + DataSource build() { + // Check the mandatory parameters + buildChecks(); + + // Create the datasource + if (compressionFormat == CompressionFormat.ZIP || archiveFormat == ArchiveFormat.ZIP) { + return buildZip(); + } else if (compressionFormat == null) { + return new DirectoryDataSource(directory, baseName, dataExtension, observer); + } else { + return switch (compressionFormat) { + case BZIP2 -> new Bzip2DirectoryDataSource(directory, baseName, dataExtension, observer); + case GZIP -> new GzDirectoryDataSource(directory, baseName, dataExtension, observer); + case XZ -> new XZDirectoryDataSource(directory, baseName, dataExtension, observer); + case ZSTD -> new ZstdDirectoryDataSource(directory, baseName, dataExtension, observer); + default -> { + LOGGER.warn("Unsupported compression format {}", compressionFormat); + yield new DirectoryDataSource(directory, baseName, dataExtension, observer); + } + }; + } + } + + private void buildChecks() { + if (directory == null) { + throw new PowsyblException("Datasource directory cannot be null"); + } + if (!Files.isDirectory(directory)) { + throw new PowsyblException("Datasource directory has to be a directory"); + } + if (baseName == null) { + throw new PowsyblException("Datasource baseName cannot be null"); + } + } + + private DataSource buildZip() { + if (compressionFormat != null && archiveFormat != null && !(compressionFormat == CompressionFormat.ZIP && archiveFormat == ArchiveFormat.ZIP)) { + throw new PowsyblException(String.format("Incoherence between compression format %s and archive format %s", compressionFormat, archiveFormat)); + } + return archiveFileName == null ? + new ZipArchiveDataSource(directory, baseName, dataExtension, observer) : + new ZipArchiveDataSource(directory, archiveFileName, baseName, dataExtension, observer); + } +} diff --git a/commons/src/main/java/com/powsybl/commons/datasource/DataSourceUtil.java b/commons/src/main/java/com/powsybl/commons/datasource/DataSourceUtil.java index 23a5cc25011..2a4f7aa7939 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/DataSourceUtil.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/DataSourceUtil.java @@ -40,45 +40,44 @@ static String getBaseName(String fileName) { } static DataSource createDataSource(Path directory, String basename, CompressionFormat compressionExtension, DataSourceObserver observer) { + return createDataSource(directory, basename, null, compressionExtension, observer); + } + + static DataSource createDataSource(Path directory, String basename, String dataExtension, CompressionFormat compressionFormat, DataSourceObserver observer) { Objects.requireNonNull(directory); Objects.requireNonNull(basename); - if (compressionExtension == null) { - return new DirectoryDataSource(directory, basename, observer); - } else { - switch (compressionExtension) { - case BZIP2: - return new Bzip2DirectoryDataSource(directory, basename, observer); - case GZIP: - return new GzDirectoryDataSource(directory, basename, observer); - case XZ: - return new XZDirectoryDataSource(directory, basename, observer); - case ZIP: - return new ZipArchiveDataSource(directory, basename, observer); - case ZSTD: - return new ZstdDirectoryDataSource(directory, basename, observer); - default: - throw new IllegalStateException("Unexpected CompressionFormat value: " + compressionExtension); - } + DataSourceBuilder dataSourceBuilder = new DataSourceBuilder() + .withDirectory(directory) + .withBaseName(basename) + .withDataExtension(dataExtension) + .withCompressionFormat(compressionFormat) + .withObserver(observer); + + // If a zip compression is asked + if (compressionFormat == CompressionFormat.ZIP) { + dataSourceBuilder.withArchiveFormat(ArchiveFormat.ZIP); } + + return dataSourceBuilder.build(); } static DataSource createDataSource(Path directory, String fileNameOrBaseName, DataSourceObserver observer) { Objects.requireNonNull(directory); Objects.requireNonNull(fileNameOrBaseName); - if (fileNameOrBaseName.endsWith(".zst")) { - return new ZstdDirectoryDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 4)), observer); - } else if (fileNameOrBaseName.endsWith(".zip")) { - return new ZipArchiveDataSource(directory, fileNameOrBaseName, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 4)), observer); - } else if (fileNameOrBaseName.endsWith(".xz")) { - return new XZDirectoryDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 3)), observer); - } else if (fileNameOrBaseName.endsWith(".gz")) { - return new GzDirectoryDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 3)), observer); - } else if (fileNameOrBaseName.endsWith(".bz2")) { - return new Bzip2DirectoryDataSource(directory, getBaseName(fileNameOrBaseName.substring(0, fileNameOrBaseName.length() - 4)), observer); - } else { - return new DirectoryDataSource(directory, getBaseName(fileNameOrBaseName), observer); - } + // Get the file information + FileInformation fileInformation = new FileInformation(fileNameOrBaseName); + + DataSourceBuilder dataSourceBuilder = new DataSourceBuilder() + .withDirectory(directory) + .withArchiveFileName(fileNameOrBaseName) + .withBaseName(fileInformation.getBaseName()) + .withDataExtension(fileInformation.getDataExtension()) + .withCompressionFormat(fileInformation.getCompressionFormat()) + .withArchiveFormat(fileInformation.getArchiveFormat()) + .withObserver(observer); + + return dataSourceBuilder.build(); } } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/DirectoryDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/DirectoryDataSource.java index 24fca2af1d4..3a92a4b84ff 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/DirectoryDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/DirectoryDataSource.java @@ -25,22 +25,25 @@ public class DirectoryDataSource extends AbstractFileSystemDataSource { public DirectoryDataSource(Path directory, String baseName) { - this(directory, baseName, null, null); + this(directory, baseName, null, null, null); } public DirectoryDataSource(Path directory, String baseName, DataSourceObserver observer) { - this(directory, baseName, null, observer); + this(directory, baseName, null, null, observer); + } + + public DirectoryDataSource(Path directory, String baseName, + String dataExtension, + DataSourceObserver observer) { + this(directory, baseName, dataExtension, null, observer); } DirectoryDataSource(Path directory, String baseName, + String dataExtension, CompressionFormat compressionFormat, DataSourceObserver observer) { - super(directory, baseName, compressionFormat, observer); - } - - protected String getCompressionExt() { - return compressionFormat == null ? "" : "." + compressionFormat.getExtension(); + super(directory, baseName, dataExtension, compressionFormat, observer); } /** @@ -59,7 +62,8 @@ protected OutputStream getCompressedOutputStream(OutputStream os) throws IOExcep private Path getPath(String fileName) { Objects.requireNonNull(fileName); - return directory.resolve(fileName + getCompressionExt()); + FileInformation fileInformation = new FileInformation(fileName, false); + return directory.resolve(fileInformation.getCompressionFormat() == null ? fileName + getCompressionExtension() : fileName); } @Override @@ -74,6 +78,11 @@ public OutputStream newOutputStream(String fileName, boolean append) throws IOEx return observer != null ? new ObservableOutputStream(os, path.toString(), observer) : os; } + /** + * Check if a file exists in the datasource. + * @param fileName Name of the file (excluding the compression extension) + * @return true if the file exists, else false + */ @Override public boolean exists(String fileName) throws IOException { Path path = getPath(fileName); @@ -104,7 +113,7 @@ public Set listNames(String regex) throws IOException { .map(Path::toString) .filter(name -> name.startsWith(baseName)) // Return names after removing the compression extension - .map(name -> name.replace(getCompressionExt(), "")) + .map(name -> name.replace(getCompressionExtension(), "")) .filter(s -> p.matcher(s).matches()) .collect(Collectors.toSet()); } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/FileInformation.java b/commons/src/main/java/com/powsybl/commons/datasource/FileInformation.java new file mode 100644 index 00000000000..2cb17ece723 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/datasource/FileInformation.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.powsybl.commons.PowsyblException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; + +/** + * @author Nicolas Rol {@literal } + */ +public class FileInformation { + + private static final Logger LOGGER = LoggerFactory.getLogger(FileInformation.class); + private String baseName; + private CompressionFormat compressionFormat; + private ArchiveFormat archiveFormat; + private String dataExtension; + + FileInformation(String fileName) { + Objects.requireNonNull(fileName); + computeInformation(fileName, true); + } + + FileInformation(String fileName, boolean dataSourceInitialization) { + Objects.requireNonNull(fileName); + computeInformation(fileName, dataSourceInitialization); + } + + private void computeInformation(String fileName, boolean dataSourceInitialization) { + // Check if filename is empty or only a dot + if (fileName.isEmpty() || fileName.equals(".")) { + throw new PowsyblException("File name cannot be empty nor just a dot"); + } + + // Last dot index + int currentDotIndex = fileName.lastIndexOf('.'); + + // Compression extension + compressionFormat = switch (fileName.substring(currentDotIndex + 1)) { + case "bz2" -> CompressionFormat.BZIP2; + case "gz" -> CompressionFormat.GZIP; + case "xz" -> CompressionFormat.XZ; + case "zip" -> CompressionFormat.ZIP; + case "zst" -> CompressionFormat.ZSTD; + default -> null; + }; + + // File name without the compression extension + String fileNameWithoutCompressionExtension = compressionFormat == null ? fileName : fileName.substring(0, currentDotIndex); + + // Archive extension + String fileNameWithoutCompressionNorArchive; + archiveFormat = compressionFormat == CompressionFormat.ZIP ? ArchiveFormat.ZIP : null; + fileNameWithoutCompressionNorArchive = fileNameWithoutCompressionExtension; + + // Last dot index + currentDotIndex = fileNameWithoutCompressionNorArchive.lastIndexOf('.'); + + /* Data datasource extension + * Four cases are possible: + * - case 1 ("dummy"): currentDotIndex < 0 -> no source format is given + * - case 2 (".dummy"): currentDotIndex == 0 -> considered as a hidden file so no source format is given + * - case 3 ("dummy.foo"): ".foo" is the source format + */ + dataExtension = currentDotIndex < 1 ? "" : fileNameWithoutCompressionNorArchive.substring(currentDotIndex + 1); + logDataExtension(fileName, dataExtension, dataSourceInitialization); + + // Base name + baseName = dataExtension.isEmpty() ? + fileNameWithoutCompressionNorArchive : + fileNameWithoutCompressionNorArchive.substring(0, currentDotIndex); + if (baseName.isEmpty()) { + LOGGER.warn("Base name is empty in file {}", fileName); + } + } + + private void logDataExtension(String fileName, String dataExtension, boolean dataSourceInitialization) { + if (dataSourceInitialization && dataExtension.isEmpty()) { + LOGGER.warn("Data extension is empty in file {}", fileName); + } + } + + public String getBaseName() { + return baseName; + } + + public CompressionFormat getCompressionFormat() { + return compressionFormat; + } + + public ArchiveFormat getArchiveFormat() { + return archiveFormat; + } + + public String getDataExtension() { + return dataExtension; + } + + public String toString() { + return "FileInformation[" + + "baseName=" + baseName + ", " + + "dataExtension=" + dataExtension + ", " + + "archiveFormat=" + archiveFormat + ", " + + "compressionFormat=" + compressionFormat + + "]"; + } +} diff --git a/commons/src/main/java/com/powsybl/commons/datasource/GenericReadOnlyDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/GenericReadOnlyDataSource.java index c5643233a2b..51a576a21a7 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/GenericReadOnlyDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/GenericReadOnlyDataSource.java @@ -21,15 +21,15 @@ public class GenericReadOnlyDataSource implements ReadOnlyDataSource { private final ReadOnlyDataSource[] dataSources; - public GenericReadOnlyDataSource(Path directory, String baseName, DataSourceObserver observer) { + public GenericReadOnlyDataSource(Path directory, String baseName, String dataExtension, DataSourceObserver observer) { dataSources = new DataSource[] { - new DirectoryDataSource(directory, baseName, observer), - new ZstdDirectoryDataSource(directory, baseName, observer), + new DirectoryDataSource(directory, baseName, dataExtension, observer), + new ZstdDirectoryDataSource(directory, baseName, dataExtension, observer), new ZipArchiveDataSource(directory), - new ZipArchiveDataSource(directory, baseName + ".zip", baseName, observer), - new XZDirectoryDataSource(directory, baseName, observer), - new GzDirectoryDataSource(directory, baseName, observer), - new Bzip2DirectoryDataSource(directory, baseName, observer) + new ZipArchiveDataSource(directory, baseName, dataExtension, observer), + new XZDirectoryDataSource(directory, baseName, dataExtension, observer), + new GzDirectoryDataSource(directory, baseName, dataExtension, observer), + new Bzip2DirectoryDataSource(directory, baseName, dataExtension, observer) }; } @@ -37,18 +37,27 @@ public GenericReadOnlyDataSource(Path directory, String baseName, DataSourceObse * The data source contains all files inside the given directory. */ public GenericReadOnlyDataSource(Path directory) { - this(directory, ""); + this(directory, "", null); } public GenericReadOnlyDataSource(Path directory, String baseName) { this(directory, baseName, null); } + public GenericReadOnlyDataSource(Path directory, String baseName, String dataExtension) { + this(directory, baseName, dataExtension, null); + } + @Override public String getBaseName() { return dataSources[0].getBaseName(); } + @Override + public String getDataExtension() { + return dataSources[0].getDataExtension(); + } + @Override public boolean exists(String suffix, String ext) throws IOException { for (ReadOnlyDataSource dataSource : dataSources) { @@ -59,6 +68,16 @@ public boolean exists(String suffix, String ext) throws IOException { return false; } + @Override + public boolean isDataExtension(String ext) { + for (ReadOnlyDataSource dataSource : dataSources) { + if (dataSource.isDataExtension(ext)) { + return true; + } + } + return false; + } + @Override public boolean exists(String fileName) throws IOException { for (ReadOnlyDataSource dataSource : dataSources) { diff --git a/commons/src/main/java/com/powsybl/commons/datasource/GzDirectoryDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/GzDirectoryDataSource.java index 6381c570d3b..5b6609be310 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/GzDirectoryDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/GzDirectoryDataSource.java @@ -19,12 +19,8 @@ */ public class GzDirectoryDataSource extends DirectoryDataSource { - public GzDirectoryDataSource(Path directory, String baseName, DataSourceObserver observer) { - super(directory, baseName, CompressionFormat.GZIP, observer); - } - - public GzDirectoryDataSource(Path directory, String baseName) { - super(directory, baseName, CompressionFormat.GZIP, null); + public GzDirectoryDataSource(Path directory, String baseName, String dataExtension, DataSourceObserver observer) { + super(directory, baseName, dataExtension, CompressionFormat.GZIP, observer); } @Override diff --git a/commons/src/main/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSource.java index e09592615c4..8db6959c269 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSource.java @@ -51,6 +51,11 @@ public boolean exists(String suffix, String ext) throws IOException { }); } + @Override + public boolean isDataExtension(String ext) { + return dataSources.stream().anyMatch(dataSource -> dataSource.isDataExtension(ext)); + } + @Override public boolean exists(String fileName) throws IOException { return dataSources.stream().anyMatch(dataSource -> { diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyDataSource.java index c4ee48e42ac..91f29c53f2a 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyDataSource.java @@ -18,6 +18,10 @@ public interface ReadOnlyDataSource { String getBaseName(); + default String getDataExtension() { + return null; + } + /** * Check if a file exists in the datasource. The file name will be constructed as: * {@code .}

@@ -29,11 +33,18 @@ public interface ReadOnlyDataSource { /** * Check if a file exists in the datasource. - * @param fileName Name of the file (excluding the compression extension) + * @param fileName Name of the file * @return true if the file exists, else false */ boolean exists(String fileName) throws IOException; + /** + * Check if the provided extension is a data extension for the datasource + * @param ext Extension to compare to the data extension of the datasource + * @return true if the datasource data extension is null, empty or equal to {@code ext}, else false + */ + boolean isDataExtension(String ext); + InputStream newInputStream(String suffix, String ext) throws IOException; InputStream newInputStream(String fileName) throws IOException; diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyMemDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyMemDataSource.java index 0d2c8a2b115..b776622de69 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyMemDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ReadOnlyMemDataSource.java @@ -67,6 +67,16 @@ public boolean exists(String suffix, String ext) throws IOException { return exists(DataSourceUtil.getFileName(baseName, suffix, ext)); } + /** + * {@inheritDoc} + * As a ReadOnlyMemDataSource does not have a main extension, this method always returns true + * @return true + */ + @Override + public boolean isDataExtension(String ext) { + return true; + } + @Override public boolean exists(String fileName) throws IOException { Objects.requireNonNull(fileName); diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ResourceDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/ResourceDataSource.java index b3ec498b802..ac69880802e 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ResourceDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ResourceDataSource.java @@ -22,14 +22,21 @@ public class ResourceDataSource implements ReadOnlyDataSource { private final String baseName; + private final String dataExtension; + private final List resourceSets; public ResourceDataSource(String baseName, ResourceSet... resourceSets) { - this(baseName, Arrays.asList(resourceSets)); + this(baseName, null, Arrays.asList(resourceSets)); } public ResourceDataSource(String baseName, List resourceSets) { + this(baseName, null, resourceSets); + } + + public ResourceDataSource(String baseName, String dataExtension, List resourceSets) { this.baseName = Objects.requireNonNull(baseName); + this.dataExtension = dataExtension; this.resourceSets = Objects.requireNonNull(resourceSets); } @@ -38,11 +45,21 @@ public String getBaseName() { return baseName; } + @Override + public String getDataExtension() { + return dataExtension; + } + @Override public boolean exists(String suffix, String ext) { return exists(DataSourceUtil.getFileName(baseName, suffix, ext)); } + @Override + public boolean isDataExtension(String ext) { + return dataExtension == null || dataExtension.isEmpty() || dataExtension.equals(ext); + } + @Override public boolean exists(String fileName) { Objects.requireNonNull(fileName); diff --git a/commons/src/main/java/com/powsybl/commons/datasource/XZDirectoryDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/XZDirectoryDataSource.java index a5eb4014e79..74a8b68e62e 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/XZDirectoryDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/XZDirectoryDataSource.java @@ -18,12 +18,8 @@ */ public class XZDirectoryDataSource extends DirectoryDataSource { - public XZDirectoryDataSource(Path directory, String baseName, DataSourceObserver observer) { - super(directory, baseName, CompressionFormat.XZ, observer); - } - - public XZDirectoryDataSource(Path directory, String baseName) { - super(directory, baseName, CompressionFormat.XZ, null); + public XZDirectoryDataSource(Path directory, String baseName, String dataExtension, DataSourceObserver observer) { + super(directory, baseName, dataExtension, CompressionFormat.XZ, observer); } @Override diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java index 7cdff8ab992..b6c90f26c19 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java @@ -33,20 +33,28 @@ */ public class ZipArchiveDataSource extends AbstractArchiveDataSource { - public ZipArchiveDataSource(Path directory, String zipFileName, String baseName, DataSourceObserver observer) { - super(directory, zipFileName, baseName, CompressionFormat.ZIP, ArchiveFormat.ZIP, observer); + public ZipArchiveDataSource(Path directory, String zipFileName, String baseName, String dataExtension, DataSourceObserver observer) { + super(directory, zipFileName, baseName, dataExtension, CompressionFormat.ZIP, ArchiveFormat.ZIP, observer); } - public ZipArchiveDataSource(Path directory, String zipFileName, String baseName) { - this(directory, zipFileName, baseName, null); + public ZipArchiveDataSource(Path directory, String zipFileName, String baseName, String dataExtension) { + this(directory, zipFileName, baseName, dataExtension, null); } - public ZipArchiveDataSource(Path directory, String baseName) { - this(directory, baseName + ".zip", baseName, null); + public ZipArchiveDataSource(Path directory, String baseName, String dataExtension, DataSourceObserver observer) { + this(directory, baseName + ((dataExtension == null || dataExtension.isEmpty()) ? "" : "." + dataExtension) + ".zip", baseName, dataExtension, observer); + } + + public ZipArchiveDataSource(Path directory, String baseName, String dataExtension) { + this(directory, baseName + ((dataExtension == null || dataExtension.isEmpty()) ? "" : "." + dataExtension) + ".zip", baseName, dataExtension, null); } public ZipArchiveDataSource(Path directory, String baseName, DataSourceObserver observer) { - this(directory, baseName + ".zip", baseName, observer); + this(directory, baseName + ".zip", baseName, null, observer); + } + + public ZipArchiveDataSource(Path directory, String baseName) { + this(directory, baseName + ".zip", baseName, null, null); } public ZipArchiveDataSource(Path zipFile) { diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ZstdDirectoryDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/ZstdDirectoryDataSource.java index f91fe3d36bd..b5b06a5f18c 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ZstdDirectoryDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ZstdDirectoryDataSource.java @@ -18,12 +18,8 @@ */ public class ZstdDirectoryDataSource extends DirectoryDataSource { - public ZstdDirectoryDataSource(Path directory, String baseName, DataSourceObserver observer) { - super(directory, baseName, CompressionFormat.ZSTD, observer); - } - - public ZstdDirectoryDataSource(Path directory, String baseName) { - super(directory, baseName, CompressionFormat.ZSTD, null); + public ZstdDirectoryDataSource(Path directory, String baseName, String dataExtension, DataSourceObserver observer) { + super(directory, baseName, dataExtension, CompressionFormat.ZSTD, observer); } @Override diff --git a/commons/src/test/java/com/powsybl/commons/datasource/AbstractFileSystemDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/AbstractFileSystemDataSourceTest.java index 8a285694a99..c615887e620 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/AbstractFileSystemDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/AbstractFileSystemDataSourceTest.java @@ -44,8 +44,8 @@ protected boolean appendTest() { protected abstract DataSource createDataSource(DataSourceObserver observer); - protected String getFileName(String baseName, CompressionFormat compressionFormat) { - return testDir + "/" + baseName + protected String getFileName(String baseName, String dataExtension, CompressionFormat compressionFormat) { + return testDir + "/" + baseName + (dataExtension == null || dataExtension.isEmpty() ? "" : "." + dataExtension) + (compressionFormat == null ? "" : "." + compressionFormat.getExtension()); } @@ -53,9 +53,9 @@ protected String getFileName(String baseName, CompressionFormat compressionForma @ParameterizedTest @MethodSource("provideArgumentsForWriteThenReadTest") - void writeThenReadTest(String baseName, CompressionFormat compressionFormat) throws IOException { + void writeThenReadTest(String baseName, String dataExtension, CompressionFormat compressionFormat) throws IOException { // Compute the full filename - String fileName = getFileName(baseName, compressionFormat); + String fileName = getFileName(baseName, dataExtension, compressionFormat); // Create the files createFiles(fileName); @@ -112,11 +112,11 @@ private void writeThenReadTest(DataSource dataSource, String suffix, String ext) @ParameterizedTest @MethodSource("provideArgumentsForClassAndListingTest") - void testClassAndListing(String baseName, + void testClassAndListing(String baseName, String dataExtension, CompressionFormat compressionFormat, Class dataSourceClass, Set listedFiles, Set listedBarFiles) throws IOException { // Compute the full filename - String fileName = getFileName(baseName, compressionFormat); + String fileName = getFileName(baseName, dataExtension, compressionFormat); // Update the list of unlisted files unlistedFiles = existingFiles.stream().filter(name -> !listedFiles.contains(name)).collect(Collectors.toSet()); @@ -145,8 +145,9 @@ void testGetters() { // Checks assertInstanceOf(AbstractFileSystemDataSource.class, dataSourceWithObserver); - assertEquals("foo", dataSourceWithObserver.getBaseName()); assertEquals(testDir, ((AbstractFileSystemDataSource) dataSourceWithObserver).getDirectory()); + assertEquals("foo", dataSourceWithObserver.getBaseName()); + assertEquals("iidm", dataSourceWithObserver.getDataExtension()); assertEquals(compressionFormat, ((AbstractFileSystemDataSource) dataSourceWithObserver).getCompressionFormat()); assertEquals(observer, ((AbstractFileSystemDataSource) dataSourceWithObserver).getObserver()); @@ -157,7 +158,28 @@ void testGetters() { assertInstanceOf(AbstractFileSystemDataSource.class, dataSourceWithoutObserver); assertEquals("foo", dataSourceWithoutObserver.getBaseName()); assertEquals(testDir, ((AbstractFileSystemDataSource) dataSourceWithoutObserver).getDirectory()); + assertEquals("foo", dataSourceWithoutObserver.getBaseName()); + assertNull(dataSourceWithoutObserver.getDataExtension()); assertEquals(compressionFormat, ((AbstractFileSystemDataSource) dataSourceWithoutObserver).getCompressionFormat()); assertNull(((AbstractFileSystemDataSource) dataSourceWithoutObserver).getObserver()); } + + @Test + void testExists() throws IOException { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Create the files + createFiles(getFileName("foo", "iidm", compressionFormat)); + + // Create the datasource + DataSource dataSourceWithObserver = createDataSource(observer); + + // Checks + assertTrue(dataSourceWithObserver.exists(null, "iidm")); + assertTrue(dataSourceWithObserver.exists(null, "txt")); + assertTrue(dataSourceWithObserver.isDataExtension("iidm")); + assertFalse(dataSourceWithObserver.isDataExtension("txt")); + + } } diff --git a/commons/src/test/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSourceTest.java index 9e19f4874d1..3d6ccae925f 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/Bzip2DirectoryDataSourceTest.java @@ -36,12 +36,12 @@ void testConstructors() { DataSourceObserver observer = new DefaultDataSourceObserver(); // Check constructors - checkDataSource(new Bzip2DirectoryDataSource(testDir, "foo_bar", observer), observer); - checkDataSource(new Bzip2DirectoryDataSource(testDir, "foo_bar"), null); + checkDataSource(new Bzip2DirectoryDataSource(testDir, "foo_bar", "iidm", observer), observer); } private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver observer) { assertEquals(testDir, dataSource.getDirectory()); + assertEquals("iidm", dataSource.getDataExtension()); assertEquals(compressionFormat, dataSource.getCompressionFormat()); assertEquals("foo_bar", dataSource.getBaseName()); assertEquals(observer, dataSource.getObserver()); @@ -55,19 +55,19 @@ protected boolean appendTest() { @Override protected DataSource createDataSource() { - return new Bzip2DirectoryDataSource(testDir, "foo"); + return new Bzip2DirectoryDataSource(testDir, "foo", null, null); } @Override protected DataSource createDataSource(DataSourceObserver observer) { - return new Bzip2DirectoryDataSource(testDir, "foo", observer); + return new Bzip2DirectoryDataSource(testDir, "foo", "iidm", observer); } static Stream provideArgumentsForWriteThenReadTest() { return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.BZIP2), - Arguments.of("foo", CompressionFormat.BZIP2), - Arguments.of("foo.v3", CompressionFormat.BZIP2) + Arguments.of("foo", "iidm", CompressionFormat.BZIP2), + Arguments.of("foo", "", CompressionFormat.BZIP2), + Arguments.of("foo", "v3", CompressionFormat.BZIP2) ); } @@ -78,13 +78,13 @@ static Stream provideArgumentsForClassAndListingTest() { "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz"); Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.xz", "foo_bar.xz", "foo_bar.iidm.zst", "foo_bar.zst", "foo_bar.iidm.gz", "foo_bar.gz"); return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.BZIP2, Bzip2DirectoryDataSource.class, + Arguments.of("foo", "iidm", CompressionFormat.BZIP2, Bzip2DirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo", CompressionFormat.BZIP2, Bzip2DirectoryDataSource.class, + Arguments.of("foo", "", CompressionFormat.BZIP2, Bzip2DirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo.v3", CompressionFormat.BZIP2, Bzip2DirectoryDataSource.class, + Arguments.of("foo", "v3", CompressionFormat.BZIP2, Bzip2DirectoryDataSource.class, listedFiles, listedBarFiles) ); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/DataSourceBuilderTest.java b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceBuilderTest.java new file mode 100644 index 00000000000..1ad9ecfd6c3 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceBuilderTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.commons.PowsyblException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Nicolas Rol {@literal } + */ +class DataSourceBuilderTest { + protected FileSystem fileSystem; + protected Path testDir; + + @BeforeEach + void setUp() throws IOException { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + testDir = fileSystem.getPath("/tmp"); + Files.createDirectories(testDir); + } + + @Test + void testBuilder() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Constant parameters + DataSourceBuilder builder = new DataSourceBuilder() + .withDirectory(testDir) + .withBaseName("foo") + .withDataExtension(".baz") + .withObserver(observer); + + // Directory datasource + assertInstanceOf(DirectoryDataSource.class, builder.build()); + assertInstanceOf(GzDirectoryDataSource.class, builder.withCompressionFormat(CompressionFormat.GZIP).build()); + assertInstanceOf(ZstdDirectoryDataSource.class, builder.withCompressionFormat(CompressionFormat.ZSTD).build()); + assertInstanceOf(XZDirectoryDataSource.class, builder.withCompressionFormat(CompressionFormat.XZ).build()); + assertInstanceOf(Bzip2DirectoryDataSource.class, builder.withCompressionFormat(CompressionFormat.BZIP2).build()); + + // Archive datasources + assertInstanceOf(ZipArchiveDataSource.class, builder.withArchiveFormat(ArchiveFormat.ZIP).withCompressionFormat(null).build()); + assertInstanceOf(ZipArchiveDataSource.class, builder.withCompressionFormat(CompressionFormat.ZIP).build()); + assertInstanceOf(ZipArchiveDataSource.class, builder.withArchiveFormat(null).build()); + } + + @Test + void testBuilderErrors() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Builder + DataSourceBuilder builder = new DataSourceBuilder() + .withCompressionFormat(CompressionFormat.ZIP) + .withArchiveFileName("bar.zip") + .withDataExtension(".baz") + .withArchiveFormat(ArchiveFormat.ZIP) + .withObserver(observer); + + // Directory missing + PowsyblException exception = assertThrows(PowsyblException.class, builder::build); + assertEquals("Datasource directory cannot be null", exception.getMessage()); + + // Directory parameter is not a directory but a file + Path file = testDir.resolve("fake.zip"); + builder.withDirectory(file); + exception = assertThrows(PowsyblException.class, builder::build); + assertEquals("Datasource directory has to be a directory", exception.getMessage()); + + // Base name missing + builder.withDirectory(testDir); + exception = assertThrows(PowsyblException.class, builder::build); + assertEquals("Datasource baseName cannot be null", exception.getMessage()); + } + + @Test + void testBuilderErrorsZip() { + DataSourceBuilder builder = new DataSourceBuilder() + .withDirectory(testDir) + .withBaseName("foo") + .withArchiveFileName("bar.zip") + .withDataExtension(".baz"); + + // Wrong compression format + builder.withCompressionFormat(CompressionFormat.GZIP).withArchiveFormat(ArchiveFormat.ZIP); + PowsyblException exception = assertThrows(PowsyblException.class, builder::build); + assertEquals("Incoherence between compression format GZIP and archive format ZIP", exception.getMessage()); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/DirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/DirectoryDataSourceTest.java index 2641571df76..5eb13218e6c 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/DirectoryDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/DirectoryDataSourceTest.java @@ -71,13 +71,15 @@ void testConstructors() { DataSourceObserver observer = new DefaultDataSourceObserver(); // Check constructors - checkDataSource(new DirectoryDataSource(testDir, "foo_bar", CompressionFormat.GZIP, observer), CompressionFormat.GZIP, observer); - checkDataSource(new DirectoryDataSource(testDir, "foo_bar", observer), null, observer); - checkDataSource(new DirectoryDataSource(testDir, "foo_bar"), null, null); + checkDataSource(new DirectoryDataSource(testDir, "foo_bar"), null, null, null); + checkDataSource(new DirectoryDataSource(testDir, "foo_bar", observer), null, null, observer); + checkDataSource(new DirectoryDataSource(testDir, "foo_bar", "iidm", observer), "iidm", null, observer); + checkDataSource(new DirectoryDataSource(testDir, "foo_bar", "iidm", CompressionFormat.GZIP, observer), "iidm", CompressionFormat.GZIP, observer); } - private void checkDataSource(DirectoryDataSource dataSource, CompressionFormat compressionFormat, DataSourceObserver observer) { + private void checkDataSource(DirectoryDataSource dataSource, String dataExtension, CompressionFormat compressionFormat, DataSourceObserver observer) { assertEquals(testDir, dataSource.getDirectory()); + assertEquals(dataExtension, dataSource.getDataExtension()); assertEquals(compressionFormat, dataSource.getCompressionFormat()); assertEquals("foo_bar", dataSource.getBaseName()); assertEquals(observer, dataSource.getObserver()); @@ -90,14 +92,14 @@ protected DataSource createDataSource() { @Override protected DataSource createDataSource(DataSourceObserver observer) { - return new DirectoryDataSource(testDir, "foo", observer); + return new DirectoryDataSource(testDir, "foo", "iidm", observer); } static Stream provideArgumentsForWriteThenReadTest() { return Stream.of( - Arguments.of("foo.iidm", null), - Arguments.of("foo", null), - Arguments.of("foo.v3", null) + Arguments.of("foo", "iidm", null), + Arguments.of("foo", "", null), + Arguments.of("foo", "v3", null) ); } @@ -110,13 +112,13 @@ static Stream provideArgumentsForClassAndListingTest() { Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.bz2", "foo_bar.bz2", "foo_bar.iidm.xz", "foo_bar.xz", "foo_bar.iidm.zst", "foo_bar.zst", "foo_bar.iidm.gz", "foo_bar.gz"); return Stream.of( - Arguments.of("foo.iidm", null, DirectoryDataSource.class, + Arguments.of("foo", "iidm", null, DirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo", null, DirectoryDataSource.class, + Arguments.of("foo", "", null, DirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo.v3", null, DirectoryDataSource.class, + Arguments.of("foo", "v3", null, DirectoryDataSource.class, listedFiles, listedBarFiles) ); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/FileInformationTest.java b/commons/src/test/java/com/powsybl/commons/datasource/FileInformationTest.java new file mode 100644 index 00000000000..4396afc5586 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/FileInformationTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.powsybl.commons.PowsyblException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * @author Nicolas Rol {@literal } + */ +class FileInformationTest { + + @Test + void tests() { + unitTest("dummy", "dummy", null, null, ""); + unitTest("dummy.iidm", "dummy", null, null, "iidm"); + unitTest("dummy.xml.xz", "dummy", CompressionFormat.XZ, null, "xml"); + + // A zip file is a compressed archive + unitTest("dummy.zip", "dummy", CompressionFormat.ZIP, ArchiveFormat.ZIP, ""); + + // It can still specify the source format + unitTest("dummy.jiidm.zip", "dummy", CompressionFormat.ZIP, ArchiveFormat.ZIP, "jiidm"); + + // If there is a usual format and additional extensions, those extensions go in the baseName + unitTest("dummy.v3.xml.zst", "dummy.v3", CompressionFormat.ZSTD, null, "xml"); + + // If there are additional extensions but no usual format, the last extension becomes the source format + unitTest("dummy.v3.bz2", "dummy", CompressionFormat.BZIP2, null, "v3"); + + // Hidden files + unitTest(".dummy", ".dummy", null, null, ""); + unitTest(".iidm", ".iidm", null, null, ""); + unitTest(".zip", "", CompressionFormat.ZIP, ArchiveFormat.ZIP, ""); + unitTest(".dummy.jiidm.zip", ".dummy", CompressionFormat.ZIP, ArchiveFormat.ZIP, "jiidm"); + + PowsyblException exception = assertThrows(PowsyblException.class, () -> new FileInformation("")); + assertEquals("File name cannot be empty nor just a dot", exception.getMessage()); + + exception = assertThrows(PowsyblException.class, () -> new FileInformation(".")); + assertEquals("File name cannot be empty nor just a dot", exception.getMessage()); + } + + private void unitTest(String filename, String baseName, + CompressionFormat compressionFormat, ArchiveFormat archiveFormat, String dataExtension) { + // Create the file information object + FileInformation fileInformation = new FileInformation(filename); + + // Check the information + assertEquals(baseName, fileInformation.getBaseName()); + assertEquals(compressionFormat, fileInformation.getCompressionFormat()); + assertEquals(archiveFormat, fileInformation.getArchiveFormat()); + assertEquals(dataExtension, fileInformation.getDataExtension()); + } + + @Test + void testToString() { + // Create the file information object + FileInformation fileInformation = new FileInformation("foo.bar.zip"); + + assertEquals("FileInformation[baseName=foo, dataExtension=bar, archiveFormat=ZIP, compressionFormat=ZIP]", fileInformation.toString()); + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/GenericReadOnlyDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/GenericReadOnlyDataSourceTest.java new file mode 100644 index 00000000000..0f3a28f82a8 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/GenericReadOnlyDataSourceTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Nicolas Rol {@literal } + */ +class GenericReadOnlyDataSourceTest { + + protected FileSystem fileSystem; + protected Path testDir; + protected Set unlistedFiles; + protected Set existingFiles; + protected Set filesInArchive; + + @BeforeEach + void setUp() throws Exception { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + testDir = fileSystem.getPath("/tmp"); + Files.createDirectories(testDir); + + // Files + existingFiles = Set.of( + "foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", "bar.iidm", "bar", + "foo.bz2", "foo.txt.bz2", "foo.iidm.bz2", "foo.xiidm.bz2", "foo.v3.iidm.bz2", "foo.v3.bz2", "foo_bar.iidm.bz2", "foo_bar.bz2", "bar.iidm.bz2", "bar.bz2", + "foo.xz", "foo.txt.xz", "foo.iidm.xz", "foo.xiidm.xz", "foo.v3.iidm.xz", "foo.v3.xz", "foo_bar.iidm.xz", "foo_bar.xz", "bar.iidm.xz", "bar.xz", + "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst", "bar.iidm.zst", "bar.zst", + "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz", "bar.iidm.gz", "bar.gz" + ); + filesInArchive = Set.of( + "foo_in_archive", "foo_in_archive.txt", "foo_in_archive.iidm", "foo_in_archive.xiidm", + "foo_in_archive.v3.iidm", "foo_in_archive.v3", "foo_bar_in_archive.iidm", "foo_bar_in_archive", + "bar_in_archive.iidm", "bar_in_archive"); + } + + @AfterEach + void tearDown() throws Exception { + fileSystem.close(); + } + + @Test + void testConstructors() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Check constructors + checkDataSource(new GenericReadOnlyDataSource(testDir), "", null); + checkDataSource(new GenericReadOnlyDataSource(testDir, "foo_bar"), "foo_bar", null); + checkDataSource(new GenericReadOnlyDataSource(testDir, "foo_bar", "iidm"), "foo_bar", "iidm"); + checkDataSource(new GenericReadOnlyDataSource(testDir, "foo_bar", "iidm", observer), "foo_bar", "iidm"); + } + + private void checkDataSource(GenericReadOnlyDataSource dataSource, String baseName, String dataExtension) { + assertEquals(baseName, dataSource.getBaseName()); + assertEquals(dataExtension, dataSource.getDataExtension()); + } + + private String getFileName(String baseName, String dataExtension) { + return testDir + "/" + baseName + (dataExtension == null || dataExtension.isEmpty() ? "" : "." + dataExtension); + } + + private void createFiles(String archiveName) throws IOException { + // Create the test files in the directory + existingFiles.forEach(fileName -> { + try { + Files.createFile(fileSystem.getPath(testDir + "/" + fileName)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + // Create the Zip archive and add the files + try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(fileSystem.getPath(archiveName + ".zip")))) { + filesInArchive.forEach(fileInArchive -> { + try { + ZipEntry e = new ZipEntry(fileInArchive); + out.putNextEntry(e); + byte[] data = "Test String".getBytes(); + out.write(data, 0, data.length); + out.closeEntry(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + }); + } + } + + // Currently, the files are not filtered in the zip archive + static Stream provideArgumentsForClassAndListingTest() { + Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.bz2", "foo_bar.bz2", "foo_bar.iidm.xz", "foo_bar.xz", + "foo_bar.iidm.zst", "foo_bar.zst", "foo_bar.iidm.gz", "foo_bar.gz", "foo_bar_in_archive.iidm", "foo_bar_in_archive", + "bar_in_archive.iidm", "bar_in_archive"); + return Stream.of( + Arguments.of("foo", "iidm", GenericReadOnlyDataSource.class, + Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", + "foo.bz2", "foo.txt.bz2", "foo.iidm.bz2", "foo.xiidm.bz2", "foo.v3.iidm.bz2", "foo.v3.bz2", "foo_bar.iidm.bz2", "foo_bar.bz2", + "foo.xz", "foo.txt.xz", "foo.iidm.xz", "foo.xiidm.xz", "foo.v3.iidm.xz", "foo.v3.xz", "foo_bar.iidm.xz", "foo_bar.xz", + "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst", + "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz", + "foo_in_archive", "foo_in_archive.txt", "foo_in_archive.iidm", "foo_in_archive.xiidm", + "foo_in_archive.v3.iidm", "foo_in_archive.v3", "foo_bar_in_archive.iidm", "foo_bar_in_archive", + "bar_in_archive.iidm", "bar_in_archive", "foo.iidm.zip"), + listedBarFiles), + Arguments.of("foo", "", GenericReadOnlyDataSource.class, + Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", + "foo.bz2", "foo.txt.bz2", "foo.iidm.bz2", "foo.xiidm.bz2", "foo.v3.iidm.bz2", "foo.v3.bz2", "foo_bar.iidm.bz2", "foo_bar.bz2", + "foo.xz", "foo.txt.xz", "foo.iidm.xz", "foo.xiidm.xz", "foo.v3.iidm.xz", "foo.v3.xz", "foo_bar.iidm.xz", "foo_bar.xz", + "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst", + "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz", + "foo_in_archive", "foo_in_archive.txt", "foo_in_archive.iidm", "foo_in_archive.xiidm", + "foo_in_archive.v3.iidm", "foo_in_archive.v3", "foo_bar_in_archive.iidm", "foo_bar_in_archive", + "bar_in_archive.iidm", "bar_in_archive", "foo.zip"), + listedBarFiles), + Arguments.of("foo", "v3", GenericReadOnlyDataSource.class, + Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", + "foo.bz2", "foo.txt.bz2", "foo.iidm.bz2", "foo.xiidm.bz2", "foo.v3.iidm.bz2", "foo.v3.bz2", "foo_bar.iidm.bz2", "foo_bar.bz2", + "foo.xz", "foo.txt.xz", "foo.iidm.xz", "foo.xiidm.xz", "foo.v3.iidm.xz", "foo.v3.xz", "foo_bar.iidm.xz", "foo_bar.xz", + "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst", + "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz", + "foo_in_archive", "foo_in_archive.txt", "foo_in_archive.iidm", "foo_in_archive.xiidm", + "foo_in_archive.v3.iidm", "foo_in_archive.v3", "foo_bar_in_archive.iidm", "foo_bar_in_archive", + "bar_in_archive.iidm", "bar_in_archive", "foo.v3.zip"), + listedBarFiles) + ); + } + + @ParameterizedTest + @MethodSource("provideArgumentsForClassAndListingTest") + void testClassAndListing(String baseName, String dataExtension, + Class dataSourceClass, + Set listedFiles, Set listedBarFiles) throws IOException { + // Compute the full filename + String fileName = getFileName(baseName, dataExtension); + + // Update the list of unlisted files + unlistedFiles = existingFiles.stream().filter(name -> !listedFiles.contains(name)).collect(Collectors.toSet()); + + // Create the files + createFiles(fileName); + + // Create the datasource + GenericReadOnlyDataSource dataSource = new GenericReadOnlyDataSource(testDir, baseName, dataExtension); + + // Check the class + assertInstanceOf(dataSourceClass, dataSource); + + // List all the files in the datasource + assertEquals(listedFiles, dataSource.listNames(".*")); + assertEquals(listedBarFiles, dataSource.listNames(".*bar.*")); + } + + @Test + void testIsDataExtension() { + GenericReadOnlyDataSource dataSource = new GenericReadOnlyDataSource(testDir, "foo_bar"); + assertTrue(dataSource.isDataExtension("test")); + assertTrue(dataSource.isDataExtension("iidm")); + + dataSource = new GenericReadOnlyDataSource(testDir, "foo_bar", "iidm"); + assertTrue(dataSource.isDataExtension("test")); + assertTrue(dataSource.isDataExtension("iidm")); + + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/GzDirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/GzDirectoryDataSourceTest.java index b49391e9189..793082c13ef 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/GzDirectoryDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/GzDirectoryDataSourceTest.java @@ -36,12 +36,12 @@ void testConstructors() { DataSourceObserver observer = new DefaultDataSourceObserver(); // Check constructors - checkDataSource(new GzDirectoryDataSource(testDir, "foo_bar", observer), observer); - checkDataSource(new GzDirectoryDataSource(testDir, "foo_bar"), null); + checkDataSource(new GzDirectoryDataSource(testDir, "foo_bar", "iidm", observer), observer); } private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver observer) { assertEquals(testDir, dataSource.getDirectory()); + assertEquals("iidm", dataSource.getDataExtension()); assertEquals(compressionFormat, dataSource.getCompressionFormat()); assertEquals("foo_bar", dataSource.getBaseName()); assertEquals(observer, dataSource.getObserver()); @@ -49,19 +49,19 @@ private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver @Override protected DataSource createDataSource() { - return new GzDirectoryDataSource(testDir, "foo"); + return new GzDirectoryDataSource(testDir, "foo", null, null); } @Override protected DataSource createDataSource(DataSourceObserver observer) { - return new GzDirectoryDataSource(testDir, "foo", observer); + return new GzDirectoryDataSource(testDir, "foo", "iidm", observer); } static Stream provideArgumentsForWriteThenReadTest() { return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.GZIP), - Arguments.of("foo", CompressionFormat.GZIP), - Arguments.of("foo.v3", CompressionFormat.GZIP) + Arguments.of("foo", "iidm", CompressionFormat.GZIP), + Arguments.of("foo", "", CompressionFormat.GZIP), + Arguments.of("foo", "v3", CompressionFormat.GZIP) ); } @@ -72,13 +72,13 @@ static Stream provideArgumentsForClassAndListingTest() { "foo.zst", "foo.txt.zst", "foo.iidm.zst", "foo.xiidm.zst", "foo.v3.iidm.zst", "foo.v3.zst", "foo_bar.iidm.zst", "foo_bar.zst"); Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.bz2", "foo_bar.bz2", "foo_bar.iidm.xz", "foo_bar.xz", "foo_bar.iidm.zst", "foo_bar.zst"); return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.GZIP, GzDirectoryDataSource.class, + Arguments.of("foo", "iidm", CompressionFormat.GZIP, GzDirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo", CompressionFormat.GZIP, GzDirectoryDataSource.class, + Arguments.of("foo", "", CompressionFormat.GZIP, GzDirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo.v3", CompressionFormat.GZIP, GzDirectoryDataSource.class, + Arguments.of("foo", "v3", CompressionFormat.GZIP, GzDirectoryDataSource.class, listedFiles, listedBarFiles) ); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/MemDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/MemDataSourceTest.java index 419cd822b02..282f5947b1c 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/MemDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/MemDataSourceTest.java @@ -63,6 +63,11 @@ void baseNameTest() { assertEquals(dataSource.getBaseName(), getBaseName()); } + @Test + void dataExtensionTest() { + assertNull(dataSource.getDataExtension()); + } + private void writeThenReadTest(String suffix, String ext) throws IOException { // check file does not exist assertFalse(dataSource.exists(suffix, ext)); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSourceTest.java index 3d79eabc81d..8ac50671499 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/MultipleReadOnlyDataSourceTest.java @@ -38,6 +38,7 @@ void setUp() throws Exception { Files.createDirectories(testDir); Files.createFile(testDir.resolve("a.txt")); Files.createFile(testDir.resolve("b.txt")); + Files.createFile(testDir.resolve("b.foo")); } @AfterEach @@ -48,14 +49,16 @@ void tearDown() throws Exception { @Test void test() throws IOException { ReadOnlyDataSource dataSource = new MultipleReadOnlyDataSource(new DirectoryDataSource(testDir, "a"), - new DirectoryDataSource(testDir, "b")); + new DirectoryDataSource(testDir, "b", "txt", null)); assertEquals("a", dataSource.getBaseName()); assertTrue(dataSource.exists(null, "txt")); assertFalse(dataSource.exists(null, "json")); + assertTrue(dataSource.exists(null, "foo")); + assertTrue(dataSource.isDataExtension("foo")); assertTrue(dataSource.exists("a.txt")); assertTrue(dataSource.exists("b.txt")); assertFalse(dataSource.exists("c.txt")); - assertEquals(Set.of("a.txt", "b.txt"), dataSource.listNames(".*")); + assertEquals(Set.of("a.txt", "b.txt", "b.foo"), dataSource.listNames(".*")); try (var is = dataSource.newInputStream("a.txt")) { assertNotNull(is); } diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ResourcesDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ResourcesDataSourceTest.java index 079159a3905..c41dbce5f82 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/ResourcesDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/ResourcesDataSourceTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test; import java.util.Collections; +import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -18,10 +19,35 @@ */ class ResourcesDataSourceTest { + @Test + void testExists() { + ResourceDataSource dataSourceWithNoDataExtension = new ResourceDataSource("foo", List.of(new ResourceSet("/test/", "foo.txt", "foo.bar"))); + assertTrue(dataSourceWithNoDataExtension.exists("foo.txt")); + assertTrue(dataSourceWithNoDataExtension.exists(null, "bar")); + assertTrue(dataSourceWithNoDataExtension.exists(null, "txt")); + assertTrue(dataSourceWithNoDataExtension.isDataExtension("bar")); + assertTrue(dataSourceWithNoDataExtension.isDataExtension("txt")); + + ResourceDataSource dataSourceWithEmptyDataExtension = new ResourceDataSource("foo", "", List.of(new ResourceSet("/test/", "foo.txt", "foo.bar"))); + assertTrue(dataSourceWithEmptyDataExtension.exists("foo.txt")); + assertTrue(dataSourceWithEmptyDataExtension.exists(null, "bar")); + assertTrue(dataSourceWithEmptyDataExtension.exists(null, "txt")); + assertTrue(dataSourceWithEmptyDataExtension.isDataExtension("bar")); + assertTrue(dataSourceWithEmptyDataExtension.isDataExtension("txt")); + + ResourceDataSource dataSourceWithDataExtension = new ResourceDataSource("foo", "txt", List.of(new ResourceSet("/test/", "foo.txt", "foo.bar"))); + assertTrue(dataSourceWithDataExtension.exists("foo.txt")); + assertTrue(dataSourceWithDataExtension.exists(null, "bar")); + assertTrue(dataSourceWithDataExtension.exists(null, "txt")); + assertFalse(dataSourceWithDataExtension.isDataExtension("bar")); + assertTrue(dataSourceWithDataExtension.isDataExtension("txt")); + } + @Test void test() { ResourceDataSource dataSource = new ResourceDataSource("foo", new ResourceSet("/test/", "foo.txt")); assertEquals("foo", dataSource.getBaseName()); + assertNull(dataSource.getDataExtension()); assertTrue(dataSource.exists("foo.txt")); assertTrue(dataSource.exists(null, "txt")); assertFalse(dataSource.exists("foo.doc")); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/XZDirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/XZDirectoryDataSourceTest.java index a670c4c19bd..197c3b5f0e8 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/XZDirectoryDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/XZDirectoryDataSourceTest.java @@ -36,12 +36,12 @@ void testConstructors() { DataSourceObserver observer = new DefaultDataSourceObserver(); // Check constructors - checkDataSource(new XZDirectoryDataSource(testDir, "foo_bar", observer), observer); - checkDataSource(new XZDirectoryDataSource(testDir, "foo_bar"), null); + checkDataSource(new XZDirectoryDataSource(testDir, "foo_bar", "iidm", observer), observer); } private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver observer) { assertEquals(testDir, dataSource.getDirectory()); + assertEquals("iidm", dataSource.getDataExtension()); assertEquals(compressionFormat, dataSource.getCompressionFormat()); assertEquals("foo_bar", dataSource.getBaseName()); assertEquals(observer, dataSource.getObserver()); @@ -55,19 +55,19 @@ protected boolean appendTest() { @Override protected DataSource createDataSource() { - return new XZDirectoryDataSource(testDir, "foo"); + return new XZDirectoryDataSource(testDir, "foo", null, null); } @Override protected DataSource createDataSource(DataSourceObserver observer) { - return new XZDirectoryDataSource(testDir, "foo", observer); + return new XZDirectoryDataSource(testDir, "foo", "iidm", observer); } static Stream provideArgumentsForWriteThenReadTest() { return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.XZ), - Arguments.of("foo", CompressionFormat.XZ), - Arguments.of("foo.v3", CompressionFormat.XZ) + Arguments.of("foo", "iidm", CompressionFormat.XZ), + Arguments.of("foo", "", CompressionFormat.XZ), + Arguments.of("foo", "v3", CompressionFormat.XZ) ); } @@ -78,13 +78,13 @@ static Stream provideArgumentsForClassAndListingTest() { "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz"); Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.bz2", "foo_bar.bz2", "foo_bar.iidm.zst", "foo_bar.zst", "foo_bar.iidm.gz", "foo_bar.gz"); return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.XZ, XZDirectoryDataSource.class, + Arguments.of("foo", "iidm", CompressionFormat.XZ, XZDirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo", CompressionFormat.XZ, XZDirectoryDataSource.class, + Arguments.of("foo", "", CompressionFormat.XZ, XZDirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo.v3", CompressionFormat.XZ, XZDirectoryDataSource.class, + Arguments.of("foo", "v3", CompressionFormat.XZ, XZDirectoryDataSource.class, listedFiles, listedBarFiles) ); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java index 5e72fafdb9b..f715be6551b 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java @@ -55,15 +55,21 @@ void testConstructors() { DataSourceObserver observer = new DefaultDataSourceObserver(); // Check constructors - checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo", observer), "foo_bar.zip", "foo", observer); - checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo"), "foo_bar.zip", "foo", null); - checkDataSource(new ZipArchiveDataSource(testDir, "foo", observer), "foo.zip", "foo", observer); - checkDataSource(new ZipArchiveDataSource(testDir, "foo"), "foo.zip", "foo", null); - checkDataSource(new ZipArchiveDataSource(testDir.resolve("foo_bar.zip")), "foo_bar.zip", "foo_bar", null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo", "iidm", observer), "foo_bar.zip", "foo", "iidm", observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo", "iidm"), "foo_bar.zip", "foo", "iidm", null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", "iidm", observer), "foo.iidm.zip", "foo", "iidm", observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", "", observer), "foo.zip", "foo", "", observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", "iidm"), "foo.iidm.zip", "foo", "iidm", null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", (String) null), "foo.zip", "foo", null, null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", ""), "foo.zip", "foo", "", null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", observer), "foo.zip", "foo", null, observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo"), "foo.zip", "foo", null, null); + checkDataSource(new ZipArchiveDataSource(testDir.resolve("foo_bar.zip")), "foo_bar.zip", "foo_bar", null, null); } - private void checkDataSource(ZipArchiveDataSource dataSource, String zipFileName, String baseName, DataSourceObserver observer) { + private void checkDataSource(ZipArchiveDataSource dataSource, String zipFileName, String baseName, String dataExtension, DataSourceObserver observer) { assertEquals(testDir, dataSource.getDirectory()); + assertEquals(dataExtension, dataSource.getDataExtension()); assertEquals(zipFileName, dataSource.getArchiveFilePath().getFileName().toString()); assertEquals(baseName, dataSource.getBaseName()); assertEquals(observer, dataSource.getObserver()); @@ -76,19 +82,19 @@ protected boolean appendTest() { @Override protected DataSource createDataSource() { - return new ZipArchiveDataSource(testDir, "foo.zip", "foo"); + return new ZipArchiveDataSource(testDir, "foo.zip", "foo", null, null); } @Override protected DataSource createDataSource(DataSourceObserver observer) { - return new ZipArchiveDataSource(testDir, "foo", observer); + return new ZipArchiveDataSource(testDir, "foo", "iidm", observer); } static Stream provideArgumentsForWriteThenReadTest() { return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.ZIP), - Arguments.of("foo", CompressionFormat.ZIP), - Arguments.of("foo.v3", CompressionFormat.ZIP) + Arguments.of("foo", "iidm", CompressionFormat.ZIP), + Arguments.of("foo", "", CompressionFormat.ZIP), + Arguments.of("foo", "v3", CompressionFormat.ZIP) ); } @@ -97,13 +103,13 @@ static Stream provideArgumentsForClassAndListingTest() { Set listedFiles = Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", "bar.iidm", "bar"); Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "bar.iidm", "bar"); return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.ZIP, ZipArchiveDataSource.class, + Arguments.of("foo", "iidm", CompressionFormat.ZIP, ZipArchiveDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo", CompressionFormat.ZIP, ZipArchiveDataSource.class, + Arguments.of("foo", "", CompressionFormat.ZIP, ZipArchiveDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo.v3", CompressionFormat.ZIP, ZipArchiveDataSource.class, + Arguments.of("foo", "v3", CompressionFormat.ZIP, ZipArchiveDataSource.class, listedFiles, listedBarFiles) ); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ZstdDirectoryDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ZstdDirectoryDataSourceTest.java index 5d8cfbc67b5..c94f790e76f 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/ZstdDirectoryDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/ZstdDirectoryDataSourceTest.java @@ -36,12 +36,12 @@ void testConstructors() { DataSourceObserver observer = new DefaultDataSourceObserver(); // Check constructors - checkDataSource(new ZstdDirectoryDataSource(testDir, "foo_bar", observer), observer); - checkDataSource(new ZstdDirectoryDataSource(testDir, "foo_bar"), null); + checkDataSource(new ZstdDirectoryDataSource(testDir, "foo_bar", "iidm", observer), observer); } private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver observer) { assertEquals(testDir, dataSource.getDirectory()); + assertEquals("iidm", dataSource.getDataExtension()); assertEquals(compressionFormat, dataSource.getCompressionFormat()); assertEquals("foo_bar", dataSource.getBaseName()); assertEquals(observer, dataSource.getObserver()); @@ -49,19 +49,19 @@ private void checkDataSource(DirectoryDataSource dataSource, DataSourceObserver @Override protected DataSource createDataSource() { - return new ZstdDirectoryDataSource(testDir, "foo"); + return new ZstdDirectoryDataSource(testDir, "foo", null, null); } @Override protected DataSource createDataSource(DataSourceObserver observer) { - return new ZstdDirectoryDataSource(testDir, "foo", observer); + return new ZstdDirectoryDataSource(testDir, "foo", "iidm", observer); } static Stream provideArgumentsForWriteThenReadTest() { return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.ZSTD), - Arguments.of("foo", CompressionFormat.ZSTD), - Arguments.of("foo.v3", CompressionFormat.ZSTD) + Arguments.of("foo", "iidm", CompressionFormat.ZSTD), + Arguments.of("foo", "", CompressionFormat.ZSTD), + Arguments.of("foo", "v3", CompressionFormat.ZSTD) ); } @@ -72,13 +72,13 @@ static Stream provideArgumentsForClassAndListingTest() { "foo.gz", "foo.txt.gz", "foo.iidm.gz", "foo.xiidm.gz", "foo.v3.iidm.gz", "foo.v3.gz", "foo_bar.iidm.gz", "foo_bar.gz"); Set listedBarFiles = Set.of("foo_bar.iidm", "foo_bar", "foo_bar.iidm.bz2", "foo_bar.bz2", "foo_bar.iidm.xz", "foo_bar.xz", "foo_bar.iidm.gz", "foo_bar.gz"); return Stream.of( - Arguments.of("foo.iidm", CompressionFormat.ZSTD, ZstdDirectoryDataSource.class, + Arguments.of("foo", "iidm", CompressionFormat.ZSTD, ZstdDirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo", CompressionFormat.ZSTD, ZstdDirectoryDataSource.class, + Arguments.of("foo", "", CompressionFormat.ZSTD, ZstdDirectoryDataSource.class, listedFiles, listedBarFiles), - Arguments.of("foo.v3", CompressionFormat.ZSTD, ZstdDirectoryDataSource.class, + Arguments.of("foo", "v3", CompressionFormat.ZSTD, ZstdDirectoryDataSource.class, listedFiles, listedBarFiles) ); diff --git a/commons/src/test/resources/test/foo.bar b/commons/src/test/resources/test/foo.bar new file mode 100644 index 00000000000..19102815663 --- /dev/null +++ b/commons/src/test/resources/test/foo.bar @@ -0,0 +1 @@ +foo \ No newline at end of file diff --git a/ieee-cdf/ieee-cdf-converter/src/main/java/com/powsybl/ieeecdf/converter/IeeeCdfImporter.java b/ieee-cdf/ieee-cdf-converter/src/main/java/com/powsybl/ieeecdf/converter/IeeeCdfImporter.java index 6671dd47de3..dbff332835e 100644 --- a/ieee-cdf/ieee-cdf-converter/src/main/java/com/powsybl/ieeecdf/converter/IeeeCdfImporter.java +++ b/ieee-cdf/ieee-cdf-converter/src/main/java/com/powsybl/ieeecdf/converter/IeeeCdfImporter.java @@ -85,7 +85,7 @@ public String getComment() { @Override public boolean exists(ReadOnlyDataSource dataSource) { try { - if (dataSource.exists(null, EXT)) { + if (dataSource.isDataExtension(EXT) && dataSource.exists(null, EXT)) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(dataSource.newInputStream(null, EXT)))) { String titleLine = reader.readLine(); if (titleLine != null) { diff --git a/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/MultipleImporterIssueTest.java b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/MultipleImporterIssueTest.java new file mode 100644 index 00000000000..5b546d9bf1f --- /dev/null +++ b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/MultipleImporterIssueTest.java @@ -0,0 +1,103 @@ +/** + * 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.iidm.network; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.commons.datasource.DataSourceUtil; +import com.powsybl.commons.datasource.ReadOnlyDataSource; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.ComputationManager; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * @author Geoffroy Jamgotchian + */ +class MultipleImporterIssueTest { + + static class FooImporter implements Importer { + + @Override + public String getFormat() { + return "foo"; + } + + @Override + public String getComment() { + return ""; + } + + @Override + public boolean exists(ReadOnlyDataSource dataSource) { + try { + return dataSource.isDataExtension("foo") && dataSource.exists(null, "foo"); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public Network importData(ReadOnlyDataSource dataSource, NetworkFactory networkFactory, Properties parameters, ReportNode reportNode) { + throw new UnsupportedOperationException(); + } + } + + static class BarImporter implements Importer { + + @Override + public String getFormat() { + return "bar"; + } + + @Override + public String getComment() { + return ""; + } + + @Override + public boolean exists(ReadOnlyDataSource dataSource) { + try { + return dataSource.isDataExtension("bar") && dataSource.exists(null, "bar"); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public Network importData(ReadOnlyDataSource dataSource, NetworkFactory networkFactory, Properties parameters, ReportNode reportNode) { + throw new UnsupportedOperationException(); + } + } + + @Test + void test() throws IOException { + try (FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix())) { + Path workingDir = fileSystem.getPath("/work"); + Path fooFile = workingDir.resolve("test.foo"); + Path barFile = workingDir.resolve("test.bar"); + Files.createFile(fooFile); + Files.createFile(barFile); + + var dataSource = DataSourceUtil.createDataSource(workingDir, "test.bar", null); + ComputationManager computationManager = Mockito.mock(ComputationManager.class); + var importer = Importer.find(dataSource, new ImportersLoaderList(new FooImporter(), new BarImporter()), computationManager, new ImportConfig()); + assertNotNull(importer); + assertInstanceOf(BarImporter.class, importer); + } + } +} diff --git a/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/TestImporter.java b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/TestImporter.java index a81dd8f84ab..76f2c0b55a5 100644 --- a/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/TestImporter.java +++ b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/TestImporter.java @@ -40,7 +40,7 @@ public String getComment() { @Override public boolean exists(ReadOnlyDataSource dataSource) { try { - return dataSource == null || dataSource.exists(null, "tst"); + return dataSource == null || dataSource.isDataExtension("tst") && dataSource.exists(null, "tst"); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/iidm/iidm-serde/pom.xml b/iidm/iidm-serde/pom.xml index 0206f45cad9..0c29d3e1ec8 100644 --- a/iidm/iidm-serde/pom.xml +++ b/iidm/iidm-serde/pom.xml @@ -118,6 +118,12 @@ ${project.version} test + + ${project.groupId} + powsybl-tools-test + ${project.version} + test + diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java index e4eaf0241ba..a56e9d0e57f 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTreeDataImporter.java @@ -94,7 +94,7 @@ public List getParameters() { private String findExtension(ReadOnlyDataSource dataSource) throws IOException { for (String ext : getExtensions()) { - if (dataSource.exists(null, ext)) { + if (dataSource.isDataExtension(ext) && dataSource.exists(null, ext)) { return ext; } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java index 27460f369d1..deb44113a06 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java @@ -27,7 +27,7 @@ @AutoService(Importer.class) public class BinaryImporter extends AbstractTreeDataImporter { - private static final String[] EXTENSIONS = {"biidm", "bin", "iidm.bin"}; + private static final String[] EXTENSIONS = {"biidm", "bin"}; @Override protected String[] getExtensions() { diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java index 92eda22af8a..25100ed9ca4 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/JsonImporter.java @@ -28,7 +28,7 @@ @AutoService(Importer.class) public class JsonImporter extends AbstractTreeDataImporter { - private static final String[] EXTENSIONS = {"jiidm", "json", "iidm.json"}; + private static final String[] EXTENSIONS = {"jiidm", "json"}; @Override protected String[] getExtensions() { diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java index 0d50956f6f0..f1910f38716 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/XMLImporter.java @@ -34,7 +34,7 @@ public class XMLImporter extends AbstractTreeDataImporter { private static final Logger LOGGER = LoggerFactory.getLogger(XMLImporter.class); - private static final String[] EXTENSIONS = {"xiidm", "iidm", "xml", "iidm.xml"}; + private static final String[] EXTENSIONS = {"xiidm", "iidm", "xml"}; private static final Supplier XML_INPUT_FACTORY_SUPPLIER = Suppliers.memoize(XMLInputFactory::newInstance); diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ConversionTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ConversionTest.java new file mode 100644 index 00000000000..d381a90e247 --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/ConversionTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.commons.config.InMemoryPlatformConfig; +import com.powsybl.iidm.network.ImportConfig; +import com.powsybl.iidm.network.tools.ConversionTool; +import com.powsybl.tools.CommandLineTools; +import com.powsybl.tools.test.AbstractToolTest; +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Nicolas Rol {@literal } + */ +class ConversionTest { + + protected FileSystem fileSystem; + + protected InMemoryPlatformConfig platformConfig; + + private CommandLineTools tools; + + @BeforeEach + public void setUp() { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + platformConfig = new InMemoryPlatformConfig(fileSystem); + tools = new CommandLineTools(Collections.singletonList(createConversionTool())); + } + + private ConversionTool createConversionTool() { + return new ConversionTool() { + + @Override + protected ImportConfig createImportConfig() { + return ImportConfig.load(platformConfig); + } + }; + } + + @Test + void testConversionZip() throws IOException { + // Prepare the firectory and file + Files.createDirectory(fileSystem.getPath("/tmp")); + Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/slackTerminal.xml")), fileSystem.getPath("/tmp/foo.xiidm")); + + // Prepare the command + String[] commandLine = new String[] { + "convert-network", + "--input-file", "/tmp/foo.xiidm", + "--output-format", "XIIDM", + "--output-file", "/tmp/bar.zip" + }; + + // Assert the command is successful + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ByteArrayOutputStream berr = new ByteArrayOutputStream(); + int status = AbstractToolTest.runCommand(commandLine, bout, berr, tools, fileSystem); + + // Assert the command worked + assertEquals(0, status); + assertEquals("Generating file /tmp/bar.zip:bar.xiidm..." + System.lineSeparator(), + bout.toString(StandardCharsets.UTF_8)); + assertEquals("", berr.toString(StandardCharsets.UTF_8)); + + // Assert the right files are created + Path zipFilePath = fileSystem.getPath("/tmp/bar.zip"); + assertTrue(Files.isRegularFile(zipFilePath)); + try (ZipFile zipFile = ZipFile.builder() + .setSeekableByteChannel(Files.newByteChannel(zipFilePath)) + .get()) { + assertNotNull(zipFile.getEntry("bar.xiidm")); + } catch (IOException e) { + fail(); + } + } +} diff --git a/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerImporter.java b/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerImporter.java index 237422896e2..40aa7e0d316 100644 --- a/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerImporter.java +++ b/matpower/matpower-converter/src/main/java/com/powsybl/matpower/converter/MatpowerImporter.java @@ -516,7 +516,7 @@ private static boolean reactiveLimitsAreOk(double minQ, double maxQ) { @Override public boolean exists(ReadOnlyDataSource dataSource) { try { - return dataSource.exists(null, MatpowerConstants.EXT); + return dataSource.isDataExtension(MatpowerConstants.EXT) && dataSource.exists(null, MatpowerConstants.EXT); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java index 672f7747c9b..bc11235be9d 100644 --- a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java +++ b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java @@ -69,7 +69,7 @@ public String getComment() { private Optional> findProjectLoader(ReadOnlyDataSource dataSource) { for (PowerFactoryDataLoader studyCaseLoader : PowerFactoryDataLoader.find(StudyCase.class)) { try { - if (dataSource.exists(null, studyCaseLoader.getExtension())) { + if (dataSource.isDataExtension(studyCaseLoader.getExtension()) && dataSource.exists(null, studyCaseLoader.getExtension())) { return Optional.of(studyCaseLoader); } } catch (IOException e) { diff --git a/psse/psse-converter/src/main/java/com/powsybl/psse/converter/PsseImporter.java b/psse/psse-converter/src/main/java/com/powsybl/psse/converter/PsseImporter.java index bb611714df8..5e7ad5f3a6d 100644 --- a/psse/psse-converter/src/main/java/com/powsybl/psse/converter/PsseImporter.java +++ b/psse/psse-converter/src/main/java/com/powsybl/psse/converter/PsseImporter.java @@ -88,7 +88,7 @@ public boolean exists(ReadOnlyDataSource dataSource) { private String findExtension(ReadOnlyDataSource dataSource) throws IOException { for (String ext : EXTENSIONS) { - if (dataSource.exists(null, ext)) { + if (dataSource.isDataExtension(ext) && dataSource.exists(null, ext)) { return ext; } } diff --git a/tools-test/src/main/java/com/powsybl/tools/test/AbstractToolTest.java b/tools-test/src/main/java/com/powsybl/tools/test/AbstractToolTest.java index 6ff033accef..5b3a37d8cf6 100644 --- a/tools-test/src/main/java/com/powsybl/tools/test/AbstractToolTest.java +++ b/tools-test/src/main/java/com/powsybl/tools/test/AbstractToolTest.java @@ -137,6 +137,17 @@ protected void assertCommandMatchTextOrRegex(String[] args, int expectedStatus, private void assertCommand(String[] args, int expectedStatus, String expectedOut, String expectedErr, BiConsumer comparisonFunction) { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ByteArrayOutputStream berr = new ByteArrayOutputStream(); + int status = runCommand(args, bout, berr, tools, fileSystem); + assertEquals(expectedStatus, status); + if (expectedOut != null) { + assertMatches(expectedOut, bout, comparisonFunction); + } + if (expectedErr != null) { + assertMatches(expectedErr, berr, comparisonFunction); + } + } + + public static int runCommand(String[] args, ByteArrayOutputStream bout, ByteArrayOutputStream berr, CommandLineTools tools, FileSystem fileSystem) { int status; try (PrintStream out = new PrintStream(bout); PrintStream err = new PrintStream(berr); @@ -173,13 +184,7 @@ public ComputationManager createLongTimeExecutionComputationManager(CommandLine } }); } - assertEquals(expectedStatus, status); - if (expectedOut != null) { - assertMatches(expectedOut, bout, comparisonFunction); - } - if (expectedErr != null) { - assertMatches(expectedErr, berr, comparisonFunction); - } + return status; } @Test diff --git a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java index fecef860095..68288508488 100644 --- a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java +++ b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java @@ -956,7 +956,7 @@ public List getParameters() { private String findExtension(ReadOnlyDataSource dataSource, boolean throwException) throws IOException { for (String ext : EXTENSIONS) { - if (dataSource.exists(null, ext)) { + if (dataSource.isDataExtension(ext) && dataSource.exists(null, ext)) { return ext; } } From 5ce89f481f8dd28f0ed647b5dce2a489a2c0445e Mon Sep 17 00:00:00 2001 From: Nicolas Rol Date: Fri, 2 Aug 2024 17:11:34 +0200 Subject: [PATCH 39/57] Datasources - part 3: TarArchiveDataSource (#3103) * add TarArchiveDataSource * add unit test * make tar work like zip in listFiles * change test to show that files in every subfolder is reached Signed-off-by: Nicolas Rol --- .../datasource/AbstractArchiveDataSource.java | 14 + .../commons/datasource/ArchiveFormat.java | 3 +- .../commons/datasource/DataSourceBuilder.java | 4 + .../commons/datasource/FileInformation.java | 15 +- .../datasource/TarArchiveDataSource.java | 330 ++++++++++++++++++ .../datasource/ZipArchiveDataSource.java | 10 +- .../AbstractArchiveDataSourceTest.java | 53 ++- .../commons/datasource/ArchiveFormatTest.java | 6 +- .../datasource/DataSourceBuilderTest.java | 8 +- .../datasource/FileInformationTest.java | 3 + .../datasource/TarArchiveDataSourceTest.java | 221 ++++++++++++ .../datasource/ZipArchiveDataSourceTest.java | 37 +- commons/src/test/resources/foo.iidm.tar.gz | Bin 0 -> 276 bytes commons/src/test/resources/foo.iidm.zip | Bin 654 -> 1029 bytes 14 files changed, 670 insertions(+), 34 deletions(-) create mode 100644 commons/src/main/java/com/powsybl/commons/datasource/TarArchiveDataSource.java create mode 100644 commons/src/test/java/com/powsybl/commons/datasource/TarArchiveDataSourceTest.java create mode 100644 commons/src/test/resources/foo.iidm.tar.gz diff --git a/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java index 6871f2b1149..4e2bc0bcbe0 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/AbstractArchiveDataSource.java @@ -8,6 +8,7 @@ package com.powsybl.commons.datasource; import java.nio.file.Path; +import java.util.Objects; /** * @author Nicolas Rol {@literal } @@ -26,4 +27,17 @@ public abstract class AbstractArchiveDataSource extends AbstractFileSystemDataSo protected Path getArchiveFilePath() { return directory.resolve(archiveFileName); } + + protected ArchiveFormat getArchiveFormat() { + return archiveFormat; + } + + protected abstract boolean entryExists(Path archiveFilePath, String fileName); + + @Override + public boolean exists(String fileName) { + Objects.requireNonNull(fileName); + Path archiveFilePath = getArchiveFilePath(); + return entryExists(archiveFilePath, fileName); + } } diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ArchiveFormat.java b/commons/src/main/java/com/powsybl/commons/datasource/ArchiveFormat.java index 8de2889c47a..c9bfe975c93 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ArchiveFormat.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ArchiveFormat.java @@ -15,7 +15,8 @@ * @author Nicolas Rol {@literal } */ public enum ArchiveFormat { - ZIP("zip"); + ZIP("zip"), + TAR("tar"); ArchiveFormat(String extension) { this.extension = Objects.requireNonNull(extension); diff --git a/commons/src/main/java/com/powsybl/commons/datasource/DataSourceBuilder.java b/commons/src/main/java/com/powsybl/commons/datasource/DataSourceBuilder.java index 9736c8d3cbe..c3f4746e145 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/DataSourceBuilder.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/DataSourceBuilder.java @@ -69,6 +69,10 @@ DataSource build() { // Create the datasource if (compressionFormat == CompressionFormat.ZIP || archiveFormat == ArchiveFormat.ZIP) { return buildZip(); + } else if (archiveFormat == ArchiveFormat.TAR) { + return archiveFileName == null ? + new TarArchiveDataSource(directory, baseName, dataExtension, compressionFormat, observer) : + new TarArchiveDataSource(directory, archiveFileName, baseName, dataExtension, compressionFormat, observer); } else if (compressionFormat == null) { return new DirectoryDataSource(directory, baseName, dataExtension, observer); } else { diff --git a/commons/src/main/java/com/powsybl/commons/datasource/FileInformation.java b/commons/src/main/java/com/powsybl/commons/datasource/FileInformation.java index 2cb17ece723..15bad57ac66 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/FileInformation.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/FileInformation.java @@ -56,10 +56,21 @@ private void computeInformation(String fileName, boolean dataSourceInitializatio // File name without the compression extension String fileNameWithoutCompressionExtension = compressionFormat == null ? fileName : fileName.substring(0, currentDotIndex); + // Last dot index + currentDotIndex = fileNameWithoutCompressionExtension.lastIndexOf('.'); + // Archive extension String fileNameWithoutCompressionNorArchive; - archiveFormat = compressionFormat == CompressionFormat.ZIP ? ArchiveFormat.ZIP : null; - fileNameWithoutCompressionNorArchive = fileNameWithoutCompressionExtension; + if (compressionFormat == CompressionFormat.ZIP) { + archiveFormat = ArchiveFormat.ZIP; + fileNameWithoutCompressionNorArchive = fileNameWithoutCompressionExtension; + } else if (ArchiveFormat.TAR.getExtension().equals(fileNameWithoutCompressionExtension.substring(currentDotIndex + 1))) { + archiveFormat = ArchiveFormat.TAR; + fileNameWithoutCompressionNorArchive = fileNameWithoutCompressionExtension.substring(0, currentDotIndex); + } else { + archiveFormat = null; + fileNameWithoutCompressionNorArchive = fileNameWithoutCompressionExtension; + } // Last dot index currentDotIndex = fileNameWithoutCompressionNorArchive.lastIndexOf('.'); diff --git a/commons/src/main/java/com/powsybl/commons/datasource/TarArchiveDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/TarArchiveDataSource.java new file mode 100644 index 00000000000..c79f7872f72 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/datasource/TarArchiveDataSource.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import com.google.common.io.ByteStreams; +import com.powsybl.commons.io.ForwardingInputStream; +import com.powsybl.commons.io.ForwardingOutputStream; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; +import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; +import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; +import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream; +import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * @author Nicolas Rol {@literal } + */ +public class TarArchiveDataSource extends AbstractArchiveDataSource { + + public TarArchiveDataSource(Path directory, String tarFileName, String baseName, String dataExtension, CompressionFormat compressionFormat, DataSourceObserver observer) { + super(directory, tarFileName, baseName, dataExtension, compressionFormat, ArchiveFormat.TAR, observer); + } + + public TarArchiveDataSource(Path directory, String tarFileName, String baseName, String dataExtension, CompressionFormat compressionFormat) { + this(directory, tarFileName, baseName, dataExtension, compressionFormat, null); + } + + public TarArchiveDataSource(Path directory, String baseName, String dataExtension, CompressionFormat compressionFormat, DataSourceObserver observer) { + this(directory, + baseName + (dataExtension == null || dataExtension.isEmpty() ? "" : "." + dataExtension) + ".tar" + (compressionFormat == null ? "" : "." + compressionFormat.getExtension()), + baseName, dataExtension, compressionFormat, observer); + } + + public TarArchiveDataSource(Path directory, String baseName, String dataExtension, CompressionFormat compressionFormat) { + this(directory, + baseName + (dataExtension == null || dataExtension.isEmpty() ? "" : "." + dataExtension) + ".tar" + (compressionFormat == null ? "" : "." + compressionFormat.getExtension()), + baseName, dataExtension, compressionFormat, null); + } + + public TarArchiveDataSource(Path directory, String baseName, CompressionFormat compressionFormat, DataSourceObserver observer) { + this(directory, + baseName + ".tar" + (compressionFormat == null ? "" : "." + compressionFormat.getExtension()), + baseName, null, compressionFormat, observer); + } + + public TarArchiveDataSource(Path directory, String baseName, CompressionFormat compressionFormat) { + this(directory, + baseName + ".tar" + (compressionFormat == null ? "" : "." + compressionFormat.getExtension()), + baseName, null, compressionFormat, null); + } + + public TarArchiveDataSource(Path directory, String baseName) { + this(directory, + baseName + ".tar", + baseName, null, null, null); + } + + public TarArchiveDataSource(Path tarFile) { + this(tarFile.getParent(), new FileInformation(tarFile.getFileName().toString(), false)); + } + + private TarArchiveDataSource(Path directory, FileInformation fileInformation) { + this(directory, fileInformation.getBaseName(), fileInformation.getDataExtension(), fileInformation.getCompressionFormat()); + } + + /** + * {@inheritDoc} + * + *

Files are here located in the archive.

+ */ + @Override + public Set listNames(String regex) throws IOException { + // Initialize variables + Pattern p = Pattern.compile(regex); + Set names = new HashSet<>(); + Path tarFilePath = getArchiveFilePath(); + + // Explore the archive + try (BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(tarFilePath)); + InputStream cis = getCompressedInputStream(inputStream, compressionFormat); + TarArchiveInputStream tar = new TarArchiveInputStream(cis)) { + ArchiveEntry entry; + while ((entry = tar.getNextEntry()) != null) { + if (!entry.isDirectory() + && p.matcher(entry.getName()).matches()) { + names.add(entry.getName()); + } + } + } + return names; + } + + protected boolean entryExists(Path tarFilePath, String fileName) { + if (Files.exists(tarFilePath)) { + try (InputStream fis = Files.newInputStream(tarFilePath); + BufferedInputStream bis = new BufferedInputStream(fis); + InputStream is = getCompressedInputStream(bis, compressionFormat); + TarArchiveInputStream tais = new TarArchiveInputStream(is)) { + + TarArchiveEntry entry; + while ((entry = tais.getNextEntry()) != null) { + if (entry.getName().equals(fileName)) { + return true; + } + } + return false; + } catch (IOException | UnsupportedOperationException e) { + return false; + } + } + return false; + } + + @Override + public OutputStream newOutputStream(String suffix, String ext, boolean append) throws IOException { + return newOutputStream(DataSourceUtil.getFileName(baseName, suffix, ext), append); + } + + @Override + public OutputStream newOutputStream(String fileName, boolean append) throws IOException { + Objects.requireNonNull(fileName); + if (append) { + throw new UnsupportedOperationException("append not supported in tar file data source"); + } + Path tarFilePath = getArchiveFilePath(); + OutputStream os = new TarEntryOutputStream(tarFilePath, fileName, compressionFormat); + return observer != null ? new ObservableOutputStream(os, tarFilePath + ":" + fileName, observer) : os; + } + + @Override + public InputStream newInputStream(String suffix, String ext) throws IOException { + return newInputStream(DataSourceUtil.getFileName(baseName, suffix, ext)); + } + + @Override + public InputStream newInputStream(String fileName) throws IOException { + Objects.requireNonNull(fileName); + Path tarFilePath = getArchiveFilePath(); + + // If the file is in the archive, we can open it + if (entryExists(tarFilePath, fileName)) { + InputStream is = new TarEntryInputStream(tarFilePath, fileName, compressionFormat); + return observer != null ? new ObservableInputStream(is, tarFilePath + ":" + fileName, observer) : is; + } + return null; + } + + private static final class TarEntryInputStream extends ForwardingInputStream { + + private TarEntryInputStream(Path tarFilePath, String fileName, CompressionFormat compressionFormat) throws IOException { + super(setStreamToFile(getTmpStream(tarFilePath, compressionFormat), fileName)); + } + + private static TarArchiveInputStream getTmpStream(Path tarFilePath, CompressionFormat compressionFormat) throws IOException { + return new TarArchiveInputStream(getCompressedInputStream(new BufferedInputStream(Files.newInputStream(tarFilePath)), compressionFormat)); + } + + private static InputStream setStreamToFile(TarArchiveInputStream tais, String fileName) throws IOException { + TarArchiveEntry entry; + while ((entry = tais.getNextEntry()) != null) { + if (entry.getName().equals(fileName)) { + return tais; + } + } + return null; + } + } + + private static final class TarEntryOutputStream extends ForwardingOutputStream { + + private final Path tarFilePath; + private final String fileName; + private final CompressionFormat compressionFormat; + private boolean closed; + + private TarEntryOutputStream(Path tarFilePath, String fileName, CompressionFormat compressionFormat) throws IOException { + super(getTmpStream(getTmpStreamFilePath(tarFilePath))); + this.tarFilePath = tarFilePath; + this.fileName = fileName; + this.compressionFormat = compressionFormat; + this.closed = false; + } + + private static OutputStream getTmpStream(Path tarFilePath) throws IOException { + return new BufferedOutputStream(Files.newOutputStream(tarFilePath)); + } + + private static Path getTmpStreamFilePath(Path tarFilePath) { + return tarFilePath.getParent().resolve("tmp_stream_" + tarFilePath.getFileName() + ".stream"); + } + + private static TarArchiveOutputStream getTarStream(Path tmpTarFilePath) throws IOException { + return new TarArchiveOutputStream(new BufferedOutputStream(Files.newOutputStream(tmpTarFilePath))); + } + + private static Path getTmpTarFilePath(Path tarFilePath) { + return tarFilePath.getParent().resolve("tmp_" + tarFilePath.getFileName()); + } + + private static Path getTmpCompressedTarFilePath(Path tarFilePath) { + return tarFilePath.getParent().resolve("tmp_comp_" + tarFilePath.getFileName()); + } + + private void compressTarFile() throws IOException { + try (InputStream fis = Files.newInputStream(getTmpTarFilePath(tarFilePath)); + OutputStream fos = Files.newOutputStream(getTmpCompressedTarFilePath(tarFilePath), StandardOpenOption.CREATE); + OutputStream compressedOS = getCompressedOutputStream(fos, this.compressionFormat)) { + byte[] buffer = new byte[8192]; + int len; + while ((len = fis.read(buffer)) != -1) { + compressedOS.write(buffer, 0, len); + } + } + } + + private static OutputStream getCompressedOutputStream(OutputStream os, CompressionFormat compressionFormat) throws IOException { + return compressionFormat == null ? os : switch (compressionFormat) { + case GZIP -> new GzipCompressorOutputStream(os); + case BZIP2 -> new BZip2CompressorOutputStream(os); + case XZ -> new XZCompressorOutputStream(os); + case ZSTD -> new ZstdCompressorOutputStream(os); + default -> os; + }; + } + + @Override + public void close() throws IOException { + if (!closed) { + + // Close temporary stream file + super.close(); + + // Open a new temporary archive + try (TarArchiveOutputStream taos = getTarStream(getTmpTarFilePath(tarFilePath))) { + + // Useful parameter + taos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX); + taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + + // Temporary stream file path + Path tmpStreamFilePath = getTmpStreamFilePath(tarFilePath); + + // Copy content of temporary stream file into an entry of the temporary archive + try (InputStream is = Files.newInputStream(tmpStreamFilePath)) { + + // New tar entry + TarArchiveEntry entry = new TarArchiveEntry(fileName); + entry.setSize(Files.size(tmpStreamFilePath)); + + // New file to add + taos.putArchiveEntry(entry); + + // Write the data in the entry + ByteStreams.copy(is, taos); + + // close new entry + taos.closeArchiveEntry(); + } + + // Copy existing entries into the temporary archive + if (Files.exists(tarFilePath)) { + try (InputStream fis = Files.newInputStream(tarFilePath); + BufferedInputStream bis = new BufferedInputStream(fis); + InputStream cis = getCompressedInputStream(bis, compressionFormat); + TarArchiveInputStream tarInput = new TarArchiveInputStream(cis)) { + TarArchiveEntry oldEntry; + while ((oldEntry = tarInput.getNextEntry()) != null) { + if (!oldEntry.getName().equals(fileName)) { + taos.putArchiveEntry(oldEntry); + byte[] buffer = new byte[8192]; + int len; + while ((len = tarInput.read(buffer)) != -1) { + taos.write(buffer, 0, len); + } + taos.closeArchiveEntry(); + } + } + } + } + + // Finishes the TAR archive without closing the underlying OutputStream + taos.finish(); + } + + // Compress the archive if needed + compressTarFile(); + + // swap with tmp tar + Path tmpTarFilePath = getTmpCompressedTarFilePath(tarFilePath); + Files.move(tmpTarFilePath, tarFilePath, StandardCopyOption.REPLACE_EXISTING); + + closed = true; + } + } + } + + private static InputStream getCompressedInputStream(InputStream is, CompressionFormat compressionFormat) throws IOException { + if (compressionFormat == null) { + return is; + } + return switch (compressionFormat) { + case GZIP -> new GzipCompressorInputStream(is); + case BZIP2 -> new BZip2CompressorInputStream(is); + case XZ -> new XZCompressorInputStream(is); + case ZSTD -> new ZstdCompressorInputStream(is); + default -> is; + }; + } +} diff --git a/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java b/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java index b6c90f26c19..8372a0fe63e 100644 --- a/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java +++ b/commons/src/main/java/com/powsybl/commons/datasource/ZipArchiveDataSource.java @@ -61,7 +61,7 @@ public ZipArchiveDataSource(Path zipFile) { this(zipFile.getParent(), com.google.common.io.Files.getNameWithoutExtension(zipFile.getFileName().toString())); } - private static boolean entryExists(Path zipFilePath, String fileName) { + protected boolean entryExists(Path zipFilePath, String fileName) { if (Files.exists(zipFilePath)) { try (ZipFile zipFile = ZipFile.builder() .setSeekableByteChannel(Files.newByteChannel(zipFilePath)) @@ -74,13 +74,6 @@ private static boolean entryExists(Path zipFilePath, String fileName) { return false; } - @Override - public boolean exists(String fileName) { - Objects.requireNonNull(fileName); - Path zipFilePath = getArchiveFilePath(); - return entryExists(zipFilePath, fileName); - } - @Override public InputStream newInputStream(String suffix, String ext) throws IOException { return newInputStream(DataSourceUtil.getFileName(baseName, suffix, ext)); @@ -193,7 +186,6 @@ public OutputStream newOutputStream(String suffix, String ext, boolean append) t @Override public Set listNames(String regex) throws IOException { - // Consider only files in the given folder, do not go into folders Pattern p = Pattern.compile(regex); Set names = new HashSet<>(); Path zipFilePath = getArchiveFilePath(); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/AbstractArchiveDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/AbstractArchiveDataSourceTest.java index 90c27ac425b..872e93a9634 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/AbstractArchiveDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/AbstractArchiveDataSourceTest.java @@ -15,6 +15,7 @@ import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; @@ -47,6 +48,18 @@ void tearDown() throws Exception { fileSystem.close(); } + protected void checkDataSource(AbstractArchiveDataSource dataSource, String zipFileName, String baseName, + String dataExtension, ArchiveFormat archiveFormat, + CompressionFormat compressionFormat, DataSourceObserver observer) { + assertEquals(testDir, dataSource.getDirectory()); + assertEquals(zipFileName, dataSource.getArchiveFilePath().getFileName().toString()); + assertEquals(baseName, dataSource.getBaseName()); + assertEquals(dataExtension, dataSource.getDataExtension()); + assertEquals(archiveFormat, dataSource.getArchiveFormat()); + assertEquals(compressionFormat, dataSource.getCompressionFormat()); + assertEquals(observer, dataSource.getObserver()); + } + @Test void testFileInSubfolder() throws IOException { // File @@ -58,11 +71,33 @@ void testFileInSubfolder() throws IOException { // All the files are listed, no filter is applied Set files = dataSource.listNames(".*"); - assertEquals(3, files.size()); + assertEquals(4, files.size()); assertTrue(files.contains("foo.iidm")); assertTrue(files.contains("foo_bar.iidm")); assertFalse(files.contains("foo_baz.iidm")); assertTrue(files.contains("subfolder/foo_baz.iidm")); + assertTrue(files.contains("subfolder/subsubfolder/foo.v3.iidm")); + } + + @Test + void testStreams() throws IOException { + // Create file + createFiles(archiveWithSubfolders); + Path path = fileSystem.getPath(archiveWithSubfolders); + + // Datasource with an observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + DataSource dataSource = DataSourceUtil.createDataSource(fileSystem.getPath("."), archiveWithSubfolders, observer); + assertInstanceOf(ObservableInputStream.class, dataSource.newInputStream("foo.iidm")); + assertInstanceOf(ObservableOutputStream.class, dataSource.newOutputStream("test.iidm", false)); + + // Datasource without an observer + dataSource = DataSource.fromPath(path); + try (InputStream inputStream = dataSource.newInputStream("foo.iidm"); + OutputStream outputStream = dataSource.newOutputStream("test.iidm", false)) { + assertFalse(inputStream instanceof ObservableInputStream); + assertFalse(outputStream instanceof ObservableOutputStream); + } } @Test @@ -81,4 +116,20 @@ void testErrorOnAppend() throws IOException { }); assertEquals(appendException, exception.getMessage()); } + + protected abstract AbstractArchiveDataSource createArchiveDataSource(); + + @Test + void testMissingArchive() throws IOException { + AbstractArchiveDataSource dataSource = createArchiveDataSource(); + assertFalse(dataSource.exists("test.bar")); + assertNull(dataSource.newInputStream("test.bar")); + } + + @Test + void testWrongTypeOfFile() throws IOException { + Files.createFile(testDir.resolve("foo.bar")); + AbstractArchiveDataSource dataSource = createArchiveDataSource(); + assertFalse(dataSource.exists("test.bar")); + } } diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ArchiveFormatTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ArchiveFormatTest.java index 0af78199448..a32e6a5d0cf 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/ArchiveFormatTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/ArchiveFormatTest.java @@ -19,11 +19,13 @@ class ArchiveFormatTest { @Test void test() { - assertEquals(1, ArchiveFormat.values().length); + assertEquals(2, ArchiveFormat.values().length); assertEquals("zip", ArchiveFormat.ZIP.getExtension()); + assertEquals("tar", ArchiveFormat.TAR.getExtension()); List formats = List.of( - ArchiveFormat.ZIP.name()); + ArchiveFormat.ZIP.name(), + ArchiveFormat.TAR.name()); assertEquals(formats, ArchiveFormat.getFormats()); } } diff --git a/commons/src/test/java/com/powsybl/commons/datasource/DataSourceBuilderTest.java b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceBuilderTest.java index 1ad9ecfd6c3..c19aebbf6e5 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/DataSourceBuilderTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/DataSourceBuilderTest.java @@ -54,6 +54,7 @@ void testBuilder() { assertInstanceOf(Bzip2DirectoryDataSource.class, builder.withCompressionFormat(CompressionFormat.BZIP2).build()); // Archive datasources + assertInstanceOf(TarArchiveDataSource.class, builder.withArchiveFormat(ArchiveFormat.TAR).build()); assertInstanceOf(ZipArchiveDataSource.class, builder.withArchiveFormat(ArchiveFormat.ZIP).withCompressionFormat(null).build()); assertInstanceOf(ZipArchiveDataSource.class, builder.withCompressionFormat(CompressionFormat.ZIP).build()); assertInstanceOf(ZipArchiveDataSource.class, builder.withArchiveFormat(null).build()); @@ -96,9 +97,14 @@ void testBuilderErrorsZip() { .withArchiveFileName("bar.zip") .withDataExtension(".baz"); + // Wrong archive format + builder.withCompressionFormat(CompressionFormat.ZIP).withArchiveFormat(ArchiveFormat.TAR); + PowsyblException exception = assertThrows(PowsyblException.class, builder::build); + assertEquals("Incoherence between compression format ZIP and archive format TAR", exception.getMessage()); + // Wrong compression format builder.withCompressionFormat(CompressionFormat.GZIP).withArchiveFormat(ArchiveFormat.ZIP); - PowsyblException exception = assertThrows(PowsyblException.class, builder::build); + exception = assertThrows(PowsyblException.class, builder::build); assertEquals("Incoherence between compression format GZIP and archive format ZIP", exception.getMessage()); } } diff --git a/commons/src/test/java/com/powsybl/commons/datasource/FileInformationTest.java b/commons/src/test/java/com/powsybl/commons/datasource/FileInformationTest.java index 4396afc5586..d0a05c604e5 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/FileInformationTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/FileInformationTest.java @@ -22,6 +22,7 @@ class FileInformationTest { void tests() { unitTest("dummy", "dummy", null, null, ""); unitTest("dummy.iidm", "dummy", null, null, "iidm"); + unitTest("dummy.tar.gz", "dummy", CompressionFormat.GZIP, ArchiveFormat.TAR, ""); unitTest("dummy.xml.xz", "dummy", CompressionFormat.XZ, null, "xml"); // A zip file is a compressed archive @@ -40,6 +41,8 @@ void tests() { unitTest(".dummy", ".dummy", null, null, ""); unitTest(".iidm", ".iidm", null, null, ""); unitTest(".zip", "", CompressionFormat.ZIP, ArchiveFormat.ZIP, ""); + unitTest(".dummy.tar.gz", ".dummy", CompressionFormat.GZIP, ArchiveFormat.TAR, ""); + unitTest(".tar.gz", "", CompressionFormat.GZIP, ArchiveFormat.TAR, ""); unitTest(".dummy.jiidm.zip", ".dummy", CompressionFormat.ZIP, ArchiveFormat.ZIP, "jiidm"); PowsyblException exception = assertThrows(PowsyblException.class, () -> new FileInformation("")); diff --git a/commons/src/test/java/com/powsybl/commons/datasource/TarArchiveDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/TarArchiveDataSourceTest.java new file mode 100644 index 00000000000..6c4ebca3f22 --- /dev/null +++ b/commons/src/test/java/com/powsybl/commons/datasource/TarArchiveDataSourceTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.datasource; + +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; +import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; +import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.Set; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Nicolas Rol {@literal } + */ +class TarArchiveDataSourceTest extends AbstractArchiveDataSourceTest { + + private static final String WORK_DIR = "/work/"; + private static final String MAIN_EXT = "xml"; + private static final String BASENAME = "network"; + private static final String MAIN_FILE = BASENAME + "." + MAIN_EXT; + private static final String TAR_FILENAME = MAIN_FILE + ".tar.gz"; + private static final String TAR_PATH = WORK_DIR + TAR_FILENAME; + private static final String ADDITIONAL_SUFFIX = "_mapping"; + private static final String ADDITIONAL_EXT = "csv"; + private static final String ADDITIONAL_FILE = BASENAME + ADDITIONAL_SUFFIX + "." + ADDITIONAL_EXT; + private static final String UNRELATED_FILE = "other.de"; + + @Override + @BeforeEach + void setUp() throws Exception { + super.setUp(); + archiveWithSubfolders = "foo.iidm.tar.gz"; + appendException = "append not supported in tar file data source"; + archiveFormat = ArchiveFormat.TAR; + compressionFormat = CompressionFormat.GZIP; + } + + @Override + protected String getFileName(String baseName, String dataExtension, CompressionFormat compressionFormat) { + return testDir + "/" + baseName + + (dataExtension == null || dataExtension.isEmpty() ? "" : "." + dataExtension) + + (archiveFormat == null ? "" : "." + archiveFormat.getExtension()) + + (compressionFormat == null ? "" : "." + compressionFormat.getExtension()); + } + + @Test + @Override + void testConstructors() { + // Observer + DataSourceObserver observer = new DefaultDataSourceObserver(); + + // Check constructors + checkDataSource(new TarArchiveDataSource(testDir, "foo_bar.tar.gz", "foo", "iidm", compressionFormat, observer), "foo_bar.tar.gz", "foo", "iidm", archiveFormat, compressionFormat, observer); + checkDataSource(new TarArchiveDataSource(testDir, "foo_bar.tar.gz", "foo", "iidm", compressionFormat), "foo_bar.tar.gz", "foo", "iidm", archiveFormat, compressionFormat, null); + checkDataSource(new TarArchiveDataSource(testDir, "foo", "iidm", compressionFormat, observer), "foo.iidm.tar.gz", "foo", "iidm", archiveFormat, compressionFormat, observer); + checkDataSource(new TarArchiveDataSource(testDir, "foo", "", compressionFormat, observer), "foo.tar.gz", "foo", "", archiveFormat, compressionFormat, observer); + checkDataSource(new TarArchiveDataSource(testDir, "foo", "iidm", compressionFormat), "foo.iidm.tar.gz", "foo", "iidm", archiveFormat, compressionFormat, null); + checkDataSource(new TarArchiveDataSource(testDir, "foo", null, compressionFormat), "foo.tar.gz", "foo", null, archiveFormat, compressionFormat, null); + checkDataSource(new TarArchiveDataSource(testDir, "foo", "", compressionFormat), "foo.tar.gz", "foo", "", archiveFormat, compressionFormat, null); + checkDataSource(new TarArchiveDataSource(testDir, "foo", compressionFormat, observer), "foo.tar.gz", "foo", null, archiveFormat, compressionFormat, observer); + checkDataSource(new TarArchiveDataSource(testDir, "foo", compressionFormat), "foo.tar.gz", "foo", null, archiveFormat, compressionFormat, null); + checkDataSource(new TarArchiveDataSource(testDir, "foo"), "foo.tar", "foo", null, archiveFormat, null, null); + checkDataSource(new TarArchiveDataSource(testDir.resolve("foo_bar.tar.gz")), "foo_bar.tar.gz", "foo_bar", "", archiveFormat, compressionFormat, null); + } + + @Override + protected boolean appendTest() { + // append does not work with tar files + return false; + } + + @Override + protected DataSource createDataSource() { + return new TarArchiveDataSource(testDir, "foo.tar.gz", "foo", null, compressionFormat, null); + } + + @Override + protected DataSource createDataSource(DataSourceObserver observer) { + return new TarArchiveDataSource(testDir, "foo", "iidm", compressionFormat, observer); + } + + @Override + protected AbstractArchiveDataSource createArchiveDataSource() { + return new TarArchiveDataSource(testDir, "foo.bar", "foo", null, compressionFormat, null); + } + + static Stream provideArgumentsForWriteThenReadTest() { + return Stream.of( + Arguments.of("foo", "iidm", CompressionFormat.GZIP), + Arguments.of("foo", "", CompressionFormat.XZ), + Arguments.of("foo", "v3", CompressionFormat.ZSTD), + Arguments.of("foo", "v3", CompressionFormat.BZIP2), + Arguments.of("foo", "v3", null) + ); + } + + static Stream provideArgumentsForClassAndListingTest() { + return Stream.of( + Arguments.of("foo", "iidm", CompressionFormat.GZIP, TarArchiveDataSource.class, + Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", "bar.iidm", "bar"), + Set.of("foo_bar.iidm", "foo_bar", "bar.iidm", "bar")), + Arguments.of("foo", "", CompressionFormat.BZIP2, TarArchiveDataSource.class, + Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", "bar.iidm", "bar"), + Set.of("foo_bar.iidm", "foo_bar", "bar.iidm", "bar")), + Arguments.of("foo", "v3", CompressionFormat.ZSTD, TarArchiveDataSource.class, + Set.of("foo", "foo.txt", "foo.iidm", "foo.xiidm", "foo.v3.iidm", "foo.v3", "foo_bar.iidm", "foo_bar", "bar.iidm", "bar"), + Set.of("foo_bar.iidm", "foo_bar", "bar.iidm", "bar")) + ); + } + + @Override + protected void createFiles(String fileName) throws IOException { + + // File information + FileInformation fileInformation = new FileInformation(fileName); + + // Create the Tar archive and add the files + try (OutputStream fOut = Files.newOutputStream(fileSystem.getPath(fileName)); + BufferedOutputStream buffOut = new BufferedOutputStream(fOut); + OutputStream gzOut = getCompressedOutputStream(buffOut, fileInformation.getCompressionFormat()); + TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) { + filesInArchive.forEach(fileInArchive -> { + try { + TarArchiveEntry e = new TarArchiveEntry(fileInArchive); + e.setSize(11); + tOut.putArchiveEntry(e); + byte[] data = "Test String".getBytes(); + tOut.write(data, 0, data.length); + tOut.closeArchiveEntry(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + }); + tOut.finish(); + } + } + + @Test + void fakeTarTest() throws IOException { + Files.createFile(testDir.resolve("fake.tar")); + assertFalse(new TarArchiveDataSource(testDir, "fake", null).exists("e")); + } + + @Test + void testTarDataSourceWithMoreThanOneDot() throws IOException { + // File information + FileInformation fileInformation = new FileInformation(TAR_PATH); + + // Create the Tar archive + try (OutputStream fOut = Files.newOutputStream(fileSystem.getPath(TAR_PATH)); + BufferedOutputStream buffOut = new BufferedOutputStream(fOut); + OutputStream gzOut = getCompressedOutputStream(buffOut, fileInformation.getCompressionFormat()); + TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) { + // First entry + TarArchiveEntry e = new TarArchiveEntry(UNRELATED_FILE); + e.setSize(11); + tOut.putArchiveEntry(e); + byte[] data = "Test String".getBytes(); + tOut.write(data, 0, data.length); + tOut.closeArchiveEntry(); + + // Another entry + e = new TarArchiveEntry(MAIN_FILE); + e.setSize(13); + tOut.putArchiveEntry(e); + data = "Test String 2".getBytes(); + tOut.write(data, 0, data.length); + tOut.closeArchiveEntry(); + + // A third one + e = new TarArchiveEntry(ADDITIONAL_FILE); + e.setSize(13); + tOut.putArchiveEntry(e); + data = "Test String 2".getBytes(); + tOut.write(data, 0, data.length); + tOut.closeArchiveEntry(); + } + + // Create the datasource + var workdirPath = fileSystem.getPath(WORK_DIR); + DataSource dataSource = DataSourceUtil.createDataSource(workdirPath, TAR_FILENAME, null); + + // Assertions on the files in the archive + assertTrue(dataSource.exists(UNRELATED_FILE)); + assertFalse(dataSource.exists("not.tar.gz")); + assertTrue(dataSource.exists(null, MAIN_EXT)); + assertTrue(dataSource.exists(ADDITIONAL_SUFFIX, ADDITIONAL_EXT)); + assertFalse(dataSource.exists("-not", "there")); + try (InputStream is = dataSource.newInputStream(UNRELATED_FILE)) { + assertEquals("Test String", new String(is.readAllBytes())); + } + } + + private OutputStream getCompressedOutputStream(OutputStream os, CompressionFormat compressionFormat) throws IOException { + return compressionFormat == null ? os : switch (compressionFormat) { + case GZIP -> new GzipCompressorOutputStream(os); + case BZIP2 -> new BZip2CompressorOutputStream(os); + case XZ -> new XZCompressorOutputStream(os); + case ZSTD -> new ZstdCompressorOutputStream(os); + default -> os; + }; + } +} diff --git a/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java b/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java index f715be6551b..582f541c04f 100644 --- a/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java +++ b/commons/src/test/java/com/powsybl/commons/datasource/ZipArchiveDataSourceTest.java @@ -55,24 +55,16 @@ void testConstructors() { DataSourceObserver observer = new DefaultDataSourceObserver(); // Check constructors - checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo", "iidm", observer), "foo_bar.zip", "foo", "iidm", observer); - checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo", "iidm"), "foo_bar.zip", "foo", "iidm", null); - checkDataSource(new ZipArchiveDataSource(testDir, "foo", "iidm", observer), "foo.iidm.zip", "foo", "iidm", observer); - checkDataSource(new ZipArchiveDataSource(testDir, "foo", "", observer), "foo.zip", "foo", "", observer); - checkDataSource(new ZipArchiveDataSource(testDir, "foo", "iidm"), "foo.iidm.zip", "foo", "iidm", null); - checkDataSource(new ZipArchiveDataSource(testDir, "foo", (String) null), "foo.zip", "foo", null, null); - checkDataSource(new ZipArchiveDataSource(testDir, "foo", ""), "foo.zip", "foo", "", null); - checkDataSource(new ZipArchiveDataSource(testDir, "foo", observer), "foo.zip", "foo", null, observer); - checkDataSource(new ZipArchiveDataSource(testDir, "foo"), "foo.zip", "foo", null, null); - checkDataSource(new ZipArchiveDataSource(testDir.resolve("foo_bar.zip")), "foo_bar.zip", "foo_bar", null, null); - } - - private void checkDataSource(ZipArchiveDataSource dataSource, String zipFileName, String baseName, String dataExtension, DataSourceObserver observer) { - assertEquals(testDir, dataSource.getDirectory()); - assertEquals(dataExtension, dataSource.getDataExtension()); - assertEquals(zipFileName, dataSource.getArchiveFilePath().getFileName().toString()); - assertEquals(baseName, dataSource.getBaseName()); - assertEquals(observer, dataSource.getObserver()); + checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo", "iidm", observer), "foo_bar.zip", "foo", "iidm", archiveFormat, compressionFormat, observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo_bar.zip", "foo", "iidm"), "foo_bar.zip", "foo", "iidm", archiveFormat, compressionFormat, null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", "iidm", observer), "foo.iidm.zip", "foo", "iidm", archiveFormat, compressionFormat, observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", "", observer), "foo.zip", "foo", "", archiveFormat, compressionFormat, observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", "iidm"), "foo.iidm.zip", "foo", "iidm", archiveFormat, compressionFormat, null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", (String) null), "foo.zip", "foo", null, archiveFormat, compressionFormat, null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", ""), "foo.zip", "foo", "", archiveFormat, compressionFormat, null); + checkDataSource(new ZipArchiveDataSource(testDir, "foo", observer), "foo.zip", "foo", null, archiveFormat, compressionFormat, observer); + checkDataSource(new ZipArchiveDataSource(testDir, "foo"), "foo.zip", "foo", null, archiveFormat, compressionFormat, null); + checkDataSource(new ZipArchiveDataSource(testDir.resolve("foo_bar.zip")), "foo_bar.zip", "foo_bar", null, archiveFormat, compressionFormat, null); } @Override @@ -90,6 +82,11 @@ protected DataSource createDataSource(DataSourceObserver observer) { return new ZipArchiveDataSource(testDir, "foo", "iidm", observer); } + @Override + protected AbstractArchiveDataSource createArchiveDataSource() { + return new ZipArchiveDataSource(testDir, "foo.bar", "foo", null, null); + } + static Stream provideArgumentsForWriteThenReadTest() { return Stream.of( Arguments.of("foo", "iidm", CompressionFormat.ZIP), @@ -161,8 +158,12 @@ void createZipDataSourceWithMoreThanOneDot() throws IOException { out.closeEntry(); } + + // Create the datasource var workdirPath = fileSystem.getPath(WORK_DIR); DataSource dataSource = DataSourceUtil.createDataSource(workdirPath, ZIP_FILENAME, null); + + // Assertions on the files in the archive assertTrue(dataSource.exists(UNRELATED_FILE)); assertFalse(dataSource.exists("not.zip")); assertTrue(dataSource.exists(null, MAIN_EXT)); diff --git a/commons/src/test/resources/foo.iidm.tar.gz b/commons/src/test/resources/foo.iidm.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..12f886ea42b160f5f3e7261381b19a5a8fb35380 GIT binary patch literal 276 zcmV+v0qg!BiwFP!000001MSvbs=_c31>ij2Rdfa3nY5EENBk=k)IeK(@aZn*F2#j8 zK_pN9fvITE*M}BbXrMi3Qim{Un&rl1M9R6!{ONUb{HMz~5yO=u^5WUCAud_GDJbWn zE_Pi%Ym36PVZCh@*Ppw0|3mewuK#)WBH(T65T^54o3#V(=ijCLd#CxYh%NFzgqVMe z0vN4sH?PBbxoUs&y(1>vd%2N+iuu>W>Vp*LzpyifoZ^8IxfO!noAX2H|_ufKtA1JM5i aK>q^(0000000000e5VsU6=GfhC;$Mi)QdF$ literal 0 HcmV?d00001 diff --git a/commons/src/test/resources/foo.iidm.zip b/commons/src/test/resources/foo.iidm.zip index 37c5709db71d0266eb19fff06e8bbb197e0c96fa..acc26ee76a4ad694b63619980a07aa701fffe39e 100644 GIT binary patch literal 1029 zcmWIWW@h1H0D;AR)e*-`cUrRo*&xipAj6QBpRbphnUWhC!pXo~c#k=)SYcLLX$3a} zBa6Y3Bbi{LB(=DND*$fHN}w?}QcA+vfJT5Y55$=Gq{JeGQFXYDGD0$ITj1vipb>Du z1vIO;G$}1VCndE=AM7X#po45N9ffdDdqpjZx5QA)0D2JUvMPkj;!d%pxnr7#=E);y z4iiT)6zD`WR|Wu0#1eG4U8#iIKv0~N8Kb%wi`h^YGcw6B0^r3<3;)9VY`#L=IG-d7!ArV;*M2 zBfIcAig~EX0BANS8Q?J+Gyaj?ssjvCgj>;41JH0#YQSSSy2F(ikR4u&+i;YWfy)!D UpiBwOnQRP#ObiV7K(WsN0Bkz@Hvj+t delta 153 zcmZqW=wsy#@MdP=VgLcA>e`756xf;1v!$_|XPcbMs5SAUGIQK1w#nj*@)NmKng7m& zFatF~%!#!Uyo^k8%y7+(Pi>aCfmuc j808pePUd9tWqdF>oJmuJl?|kY1qgi@85necn1KNRdy6At From 2de7a0a2e51812149e8d04a599c94367193e7742 Mon Sep 17 00:00:00 2001 From: EtienneLt <32468651+EtienneLt@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:50:48 +0200 Subject: [PATCH 40/57] Quality: code cleaning (#2975) Quality: code cleaning Signed-off-by: Etienne LESOT --- ...AbstractSecurityAnalysisResultBuilder.java | 3 +- .../simulator/tools/ActionSimulatorTool.java | 3 +- .../ampl/converter/AmplNetworkWriter.java | 151 +++++------- .../ampl/converter/AmplNetworkWriterTest.java | 32 +-- .../cgmes/conversion/BoundaryEquipment.java | 22 +- .../powsybl/cgmes/conversion/CgmesImport.java | 4 +- .../powsybl/cgmes/conversion/Conversion.java | 12 +- .../cgmes/conversion/CountryConversion.java | 36 +-- .../powsybl/cgmes/conversion/NodeMapping.java | 15 +- .../elements/hvdc/IslandEndHvdc.java | 13 +- .../conversion/export/CgmesExportUtil.java | 4 - .../export/SteadyStateHypothesisExport.java | 1 - .../test/FakeSubstationsConversionTest.java | 1 - .../test/network/compare/Comparison.java | 4 +- .../cgmes/model/AbstractCgmesModel.java | 6 +- .../config/AbstractMapModuleConfig.java | 24 +- .../com/powsybl/commons/json/JsonUtil.java | 4 +- .../java/com/powsybl/commons/xml/XmlUtil.java | 13 +- .../commons/extensions/FooDeserializer.java | 4 +- .../commons/extensions/FooExtSerializer.java | 4 +- .../test/DockerLocalCommandExecutor.java | 5 +- .../test/DockerLocalCommandExecutorTest.java | 3 +- .../com/powsybl/computation/InputFile.java | 11 +- .../ClassicPlatformConfigProviderTest.java | 10 +- ...IdentifierContingencyListDeserializer.java | 4 +- .../json/ContingencyDeserializer.java | 4 +- .../json/ContingencyElementDeserializer.java | 4 +- .../json/ContingencyListDeserializer.java | 6 +- .../DefaultContingencyListDeserializer.java | 4 +- .../ListOfContingencyListsDeserializer.java | 4 +- .../contingency/BranchContingencyTest.java | 2 +- .../contingency/BusContingencyTest.java | 2 +- .../BusbarSectionContingencyTest.java | 2 +- .../powsybl/contingency/ContingencyTest.java | 4 +- .../DanglingLineContingencyTest.java | 2 +- .../EmptyContingencyListProviderTest.java | 2 +- .../contingency/GeneratorContingencyTest.java | 2 +- .../contingency/HvdcLineContingencyTest.java | 2 +- .../contingency/LoadContingencyTest.java | 2 +- .../ShuntCompensatorContingencyTest.java | 2 +- .../StaticVarCompensatorContingencyTest.java | 2 +- ...reeWindingsTransformerContingencyTest.java | 2 +- .../contingency/TieLineContingencyTest.java | 2 +- .../powsybl/dsl/ExpressionDslLoader.groovy | 48 ++-- .../dsl/PowsyblDslAstTransformation.java | 31 ++- ...namicSimulationParametersDeserializer.java | 4 +- .../DynamicSimulationResultDeserializer.java | 47 ++-- .../groovy/GroovyCurvesSupplierTest.java | 14 +- .../GroovyDynamicModelSupplierTest.java | 7 +- .../groovy/GroovyEventModelSupplierTest.java | 7 +- .../java/com/powsybl/iidm/network/Branch.java | 102 +++----- .../network/ThreeWindingsTransformer.java | 21 +- .../powsybl/iidm/network/ValidationUtil.java | 17 +- .../json/IdentifierDeserializer.java | 4 +- .../powsybl/iidm/network/util/TwtData.java | 227 ++++++------------ .../powsybl/iidm/network/ExportersTest.java | 2 +- .../criteria/json/CriterionDeserializer.java | 4 +- .../iidm/network/impl/AbstractBus.java | 110 +++------ .../network/impl/BusBreakerVoltageLevel.java | 24 +- .../iidm/network/impl/NetworkImpl.java | 2 +- .../iidm/network/impl/NetworkIndex.java | 6 +- .../network/impl/NodeBreakerVoltageLevel.java | 2 +- .../ThreeWindingsTransformerAdderImpl.java | 15 +- .../impl/ThreeWindingsTransformerImpl.java | 18 +- .../network/impl/VoltageLevelAdderImpl.java | 17 +- .../powsybl/iidm/network/impl/BranchTest.java | 167 +++++++++++++ .../json/ScalingParametersDeserializer.java | 4 +- .../iidm/reducer/SubNetworkPredicateTest.java | 29 ++- .../iidm/serde/ConnectableSerDeUtil.java | 15 +- .../com/powsybl/iidm/serde/LoadSerDe.java | 11 +- .../powsybl/iidm/serde/VoltageLevelSerDe.java | 18 +- .../iidm/serde/util/IidmSerDeUtil.java | 2 +- .../BranchObservabilityXmlTest.java | 3 +- .../iidm/network/tck/AbstractBatteryTest.java | 3 +- .../network/tck/AbstractDanglingLineTest.java | 2 +- .../tck/AbstractMoveConnectableNotifTest.java | 4 +- .../iidm/network/tck/AbstractNetworkTest.java | 8 +- ...actNodeBreakerInternalConnectionsTest.java | 4 +- .../tck/AbstractSwitchSetRetainedTest.java | 7 +- .../tck/AbstractTopologyTraverserTest.java | 2 +- .../tck/AbstractVoltageLevelExportTest.java | 6 +- .../json/LoadFlowResultDeserializer.java | 24 +- .../loadflow/LoadFlowParametersTest.java | 11 +- .../loadflow/LoadFlowProviderTest.java | 3 +- .../converter/ContainersMappingHelper.java | 5 +- .../powerfactory/converter/HvdcConverter.java | 2 +- .../powerfactory/converter/LineConverter.java | 11 +- .../converter/PowerFactoryImporter.java | 6 - .../converter/ShuntConverter.java | 13 +- .../converter/SwitchConverter.java | 17 +- .../converter/TransformerConverter.java | 25 +- .../powsybl/powerfactory/model/StudyCase.java | 47 ++-- .../powerfactory/model/StudyCaseTest.java | 3 +- .../SwitchedShuntCompensatorConverter.java | 12 +- .../psse/model/io/RecordGroupIOJson.java | 2 +- .../powsybl/psse/model/pf/PsseValidation.java | 42 ++-- .../SecurityAnalysisResultMerger.java | 2 +- .../json/ActivePowerExtensionSerializer.java | 8 +- .../ConditionalActionsResultDeserializer.java | 4 +- .../json/ConnectivityResultDeserializer.java | 4 +- .../json/CurrentExtensionSerializer.java | 4 +- .../json/LimitViolationDeserializer.java | 4 +- .../LimitViolationResultDeserializer.java | 4 +- .../json/NetworkMetadataDeserializer.java | 4 +- .../json/NetworkResultDeserializer.java | 4 +- .../OperatorStrategyListDeserializer.java | 2 +- .../OperatorStrategyResultDeserializer.java | 4 +- .../PostContingencyResultDeserializer.java | 2 +- .../PreContingencyResultDeserializer.java | 2 +- ...ecurityAnalysisParametersDeserializer.java | 4 +- .../SecurityAnalysisResultDeserializer.java | 4 +- .../json/VoltageExtensionSerializer.java | 4 +- .../LimitReductionListDeserializer.java | 2 +- .../powsybl/security/LimitViolationTest.java | 7 +- .../SecurityAnalysisParametersTest.java | 8 +- .../DistributedSecurityAnalysisTest.java | 48 ---- ...SecurityAnalysisExecutionHandlersTest.java | 6 +- .../SubContingenciesProviderTest.java | 9 +- .../JsonActionAndOperatorStrategyTest.java | 2 +- .../JsonOperatorStrategyExtensionTest.java | 4 +- .../security/impl/SecurityAnalysisTest.java | 3 +- .../SensitivityAnalysisResult.java | 2 +- .../sensitivity/SensitivityFactor.java | 2 +- .../powsybl/sensitivity/SensitivityValue.java | 2 +- .../sensitivity/SensitivityVariableSet.java | 2 +- .../WeightedSensitivityVariable.java | 2 +- ...itivityAnalysisParametersDeserializer.java | 12 +- ...SensitivityAnalysisResultDeserializer.java | 4 +- .../shortcircuit/json/FaultDeserializer.java | 4 +- .../json/FaultParametersDeserializer.java | 20 +- .../json/FaultResultDeserializer.java | 6 +- .../json/FeederResultDeserializer.java | 2 +- .../json/FortescueValuesDeserializer.java | 4 +- ...hortCircuitAnalysisResultDeserializer.java | 4 +- .../ShortCircuitBusResultsDeserializer.java | 4 +- .../ShortCircuitParametersDeserializer.java | 26 +- .../json/VoltageRangeDeserializer.java | 6 +- .../com/powsybl/timeseries/AbstractPoint.java | 4 +- .../powsybl/timeseries/BigDoubleBuffer.java | 2 +- .../powsybl/timeseries/BigStringBuffer.java | 2 +- .../CalculatedTimeSeriesDslLoader.java | 3 +- .../com/powsybl/timeseries/DataChunk.java | 18 +- .../ReadOnlyTimeSeriesStoreAggregator.java | 4 +- .../timeseries/RegularTimeSeriesIndex.java | 2 +- .../com/powsybl/timeseries/TimeSeries.java | 22 +- .../timeseries/TimeSeriesMetadata.java | 2 +- .../powsybl/timeseries/TimeSeriesTable.java | 10 +- .../UncompressedDoubleDataChunk.java | 2 +- .../UncompressedStringDataChunk.java | 7 +- .../ast/AbstractMinMaxNodeCalc.java | 2 +- .../timeseries/ast/BinaryOperation.java | 2 +- .../com/powsybl/timeseries/ast/NodeCalc.java | 2 +- .../timeseries/ast/NodeCalcEvaluator.java | 2 +- .../timeseries/ast/NodeCalcVisitors.java | 7 +- .../timeseries/ast/UnaryOperation.java | 2 +- .../timeseries/DoubleDataChunkTest.java | 25 +- .../IrregularTimeSeriesIndexTest.java | 5 +- .../RegularTimeSeriesIndexTest.java | 7 +- .../timeseries/StringDataChunkTest.java | 28 +-- .../timeseries/TimeSeriesTableTest.java | 5 +- .../dsl/NodeCalcGroovyExtensionModule.groovy | 46 ++-- .../triplestore/api/PrefixNamespace.java | 3 +- .../powsybl/triplestore/api/PropertyBag.java | 9 +- .../powsybl/triplestore/api/PropertyBags.java | 43 ++-- .../powsybl/triplestore/api/QueryCatalog.java | 3 +- .../powsybl/ucte/converter/UcteImporter.java | 94 ++------ .../powsybl/ucte/network/UcteCountryCode.java | 80 +++--- .../ucte/network/UcteElementStatus.java | 18 +- .../ucte/network/ext/UcteNetworkExt.java | 3 +- .../ucte/network/ext/UcteSubstation.java | 2 +- .../ucte/network/io/UcteRecordWriter.java | 4 +- .../ucte/network/UcteNetworkImplTest.java | 3 +- 172 files changed, 1042 insertions(+), 1358 deletions(-) create mode 100644 iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/BranchTest.java diff --git a/action-ial/action-ial-simulator/src/main/java/com/powsybl/action/ial/simulator/tools/AbstractSecurityAnalysisResultBuilder.java b/action-ial/action-ial-simulator/src/main/java/com/powsybl/action/ial/simulator/tools/AbstractSecurityAnalysisResultBuilder.java index b6a56818971..d61b8b82b33 100644 --- a/action-ial/action-ial-simulator/src/main/java/com/powsybl/action/ial/simulator/tools/AbstractSecurityAnalysisResultBuilder.java +++ b/action-ial/action-ial-simulator/src/main/java/com/powsybl/action/ial/simulator/tools/AbstractSecurityAnalysisResultBuilder.java @@ -15,7 +15,6 @@ import com.powsybl.security.results.PostContingencyResult; import java.util.*; -import java.util.stream.Collectors; /** * @author Geoffroy Jamgotchian {@literal } @@ -90,7 +89,7 @@ public void afterAction(RunningContext runningContext, String actionId) { @Override public void afterPostContingencyAnalysis() { onFinalStateResult(new SecurityAnalysisResult(preContingencyResult, preContingencyStatus, - postContingencyResults.entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toList()))); + new ArrayList<>(postContingencyResults.values()))); } public abstract void onFinalStateResult(SecurityAnalysisResult result); diff --git a/action-ial/action-ial-simulator/src/main/java/com/powsybl/action/ial/simulator/tools/ActionSimulatorTool.java b/action-ial/action-ial-simulator/src/main/java/com/powsybl/action/ial/simulator/tools/ActionSimulatorTool.java index 6d505d8caa0..034c6c04204 100644 --- a/action-ial/action-ial-simulator/src/main/java/com/powsybl/action/ial/simulator/tools/ActionSimulatorTool.java +++ b/action-ial/action-ial-simulator/src/main/java/com/powsybl/action/ial/simulator/tools/ActionSimulatorTool.java @@ -42,7 +42,6 @@ import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.Writer; -import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; @@ -283,7 +282,7 @@ public void run(CommandLine line, ToolRunningContext context) throws Exception { ParallelLoadFlowActionSimulator actionSimulator = new ParallelLoadFlowActionSimulator(network, context.getLongTimeExecutionComputationManager(), taskCount, config, applyIfSolved, resultHandlers); - String dsl = new String(Files.readAllBytes(dslFile), StandardCharsets.UTF_8); + String dsl = Files.readString(dslFile); actionSimulator.run(dsl, contingencies); } else { diff --git a/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplNetworkWriter.java b/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplNetworkWriter.java index b9a127116eb..8898ca9a21a 100644 --- a/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplNetworkWriter.java +++ b/ampl-converter/src/main/java/com/powsybl/ampl/converter/AmplNetworkWriter.java @@ -202,18 +202,11 @@ private void writeSubstations() throws IOException { } private boolean isOnlyMainCc() { - switch (config.getExportScope()) { - case ONLY_MAIN_CC: - case ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS: - case ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS: - return true; - - case ALL: - return false; - - default: - throw new IllegalStateException(); - } + return switch (config.getExportScope()) { + case ONLY_MAIN_CC, ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS, ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS -> + true; + case ALL -> false; + }; } private boolean connectedComponentToExport(int numCC) { @@ -335,43 +328,38 @@ private void writeLines(AmplExportContext context, TableFormatter formatter) thr for (Line l : getSortedIdentifiables(network.getLineStream())) { Terminal t1 = l.getTerminal1(); Terminal t2 = l.getTerminal2(); - Bus bus1 = AmplUtil.getBus(t1); - Bus bus2 = AmplUtil.getBus(t2); - if (bus1 != null && bus2 != null && bus1 == bus2) { - LOGGER.warn("Skipping line '{}' connected to the same bus at both sides", l.getId()); - continue; - } - String bus1Id = AmplUtil.getBusId(bus1); - String bus2Id = AmplUtil.getBusId(bus2); - if (isOnlyMainCc() && !(isBusExported(context, bus1Id) || isBusExported(context, bus2Id))) { - continue; + if (addVoltageLevelIdsToExport(context, t1, t2, l.getId())) { + columnsExporter.writeLinesToFormatter(formatter, l); + addExtensions(mapper.getInt(AmplSubset.BRANCH, l.getId()), l); } - context.voltageLevelIdsToExport.add(t1.getVoltageLevel().getId()); - context.voltageLevelIdsToExport.add(t2.getVoltageLevel().getId()); - columnsExporter.writeLinesToFormatter(formatter, l); - addExtensions(mapper.getInt(AmplSubset.BRANCH, l.getId()), l); } } + private boolean addVoltageLevelIdsToExport(AmplExportContext context, Terminal t1, Terminal t2, String id) { + Bus bus1 = AmplUtil.getBus(t1); + Bus bus2 = AmplUtil.getBus(t2); + if (bus2 != null && bus1 == bus2) { + LOGGER.warn("Skipping line '{}' connected to the same bus at both sides", id); + return false; + } + String bus1Id = AmplUtil.getBusId(bus1); + String bus2Id = AmplUtil.getBusId(bus2); + if (isOnlyMainCc() && !(isBusExported(context, bus1Id) || isBusExported(context, bus2Id))) { + return false; + } + context.voltageLevelIdsToExport.add(t1.getVoltageLevel().getId()); + context.voltageLevelIdsToExport.add(t2.getVoltageLevel().getId()); + return true; + } + private void writeTieLines(AmplExportContext context, TableFormatter formatter) throws IOException { for (TieLine l : getSortedIdentifiables(network.getTieLineStream())) { Terminal t1 = l.getDanglingLine1().getTerminal(); Terminal t2 = l.getDanglingLine2().getTerminal(); - Bus bus1 = AmplUtil.getBus(t1); - Bus bus2 = AmplUtil.getBus(t2); - if (bus1 != null && bus2 != null && bus1 == bus2) { - LOGGER.warn("Skipping line '{}' connected to the same bus at both sides", l.getId()); - continue; - } - String bus1Id = AmplUtil.getBusId(bus1); - String bus2Id = AmplUtil.getBusId(bus2); - if (isOnlyMainCc() && !(isBusExported(context, bus1Id) || isBusExported(context, bus2Id))) { - continue; + if (addVoltageLevelIdsToExport(context, t1, t2, l.getId())) { + columnsExporter.writeTieLineToFormatter(formatter, l); + addExtensions(mapper.getInt(AmplSubset.BRANCH, l.getId()), l); } - context.voltageLevelIdsToExport.add(t1.getVoltageLevel().getId()); - context.voltageLevelIdsToExport.add(t2.getVoltageLevel().getId()); - columnsExporter.writeTieLineToFormatter(formatter, l); - addExtensions(mapper.getInt(AmplSubset.BRANCH, l.getId()), l); } } @@ -383,16 +371,12 @@ private void writeTwoWindingsTransformers(AmplExportContext context, TableFormat Bus bus2 = AmplUtil.getBus(t2); if (bus1 != null && bus1 == bus2) { LOGGER.warn("Skipping transformer '{}' connected to the same bus at both sides", twt.getId()); - continue; - } - if (isOnlyMainCc() && !(isBusExported(context, AmplUtil.getBusId(bus1)) || isBusExported(context, - AmplUtil.getBusId(bus2)))) { - continue; + } else if (!isOnlyMainCc() || isBusExported(context, AmplUtil.getBusId(bus1)) || isBusExported(context, AmplUtil.getBusId(bus2))) { + context.voltageLevelIdsToExport.add(t1.getVoltageLevel().getId()); + context.voltageLevelIdsToExport.add(t2.getVoltageLevel().getId()); + columnsExporter.writeTwoWindingsTranformerToFormatter(formatter, twt); + addExtensions(mapper.getInt(AmplSubset.BRANCH, twt.getId()), twt); } - context.voltageLevelIdsToExport.add(t1.getVoltageLevel().getId()); - context.voltageLevelIdsToExport.add(t2.getVoltageLevel().getId()); - columnsExporter.writeTwoWindingsTranformerToFormatter(formatter, twt); - addExtensions(mapper.getInt(AmplSubset.BRANCH, twt.getId()), twt); } } @@ -428,17 +412,17 @@ private void writeThreeWindingsTransformers(AmplExportContext context, TableForm if (!isOnlyMainCc() || isBusExported(context, middleBusId) || isBusExported(context, bus1Id)) { columnsExporter.writeThreeWindingsTransformerLegToFormatter(formatter, twt, middleBusNum, middleVlNum, - ThreeSides.ONE); + ThreeSides.ONE); addExtensions(num1, twt); } if (!isOnlyMainCc() || isBusExported(context, middleBusId) || isBusExported(context, bus2Id)) { columnsExporter.writeThreeWindingsTransformerLegToFormatter(formatter, twt, middleBusNum, middleVlNum, - ThreeSides.TWO); + ThreeSides.TWO); addExtensions(num2, twt); } if (!isOnlyMainCc() || isBusExported(context, middleBusId) || isBusExported(context, bus3Id)) { columnsExporter.writeThreeWindingsTransformerLegToFormatter(formatter, twt, middleBusNum, middleVlNum, - ThreeSides.THREE); + ThreeSides.THREE); addExtensions(num3, twt); } } @@ -450,15 +434,14 @@ private void writeDanglingLines(AmplExportContext context, TableFormatter format Bus bus1 = AmplUtil.getBus(t); String bus1Id = AmplUtil.getBusId(bus1); String middleBusId = AmplUtil.getDanglingLineMiddleBusId(dl); - if (isOnlyMainCc() && !(isBusExported(context, bus1Id) || isBusExported(context, middleBusId))) { - continue; + if (!isOnlyMainCc() || isBusExported(context, bus1Id) || isBusExported(context, middleBusId)) { + VoltageLevel vl = t.getVoltageLevel(); + String middleVlId = AmplUtil.getDanglingLineMiddleVoltageLevelId(dl); + context.voltageLevelIdsToExport.add(vl.getId()); + context.voltageLevelIdsToExport.add(middleVlId); + columnsExporter.writeDanglingLineToFormatter(formatter, dl); + addExtensions(mapper.getInt(AmplSubset.BRANCH, dl.getId()), dl); } - VoltageLevel vl = t.getVoltageLevel(); - String middleVlId = AmplUtil.getDanglingLineMiddleVoltageLevelId(dl); - context.voltageLevelIdsToExport.add(vl.getId()); - context.voltageLevelIdsToExport.add(middleVlId); - columnsExporter.writeDanglingLineToFormatter(formatter, dl); - addExtensions(mapper.getInt(AmplSubset.BRANCH, dl.getId()), dl); } } @@ -517,16 +500,10 @@ private void writePhaseTapChangers() throws IOException { } private boolean exportLoad(AmplExportContext context, String busId) { - switch (config.getExportScope()) { - case ALL: - case ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS: - return true; - case ONLY_MAIN_CC: - case ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS: - return isBusExported(context, busId); - default: - throw new IllegalStateException(); - } + return switch (config.getExportScope()) { + case ALL, ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS -> true; + case ONLY_MAIN_CC, ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS -> isBusExported(context, busId); + }; } private void writeLoads(AmplExportContext context) throws IOException { @@ -548,20 +525,19 @@ private void writeLoads(AmplExportContext context) throws IOException { } if (!exportLoad(context, busId)) { skipped.add(l.getId()); - continue; + } else { + context.loadsToExport.add(l.getId()); + columnsExporter.writeLoadtoFormatter(formatter, l); + addExtensions(mapper.getInt(AmplSubset.LOAD, l.getId()), l); } - context.loadsToExport.add(l.getId()); - - columnsExporter.writeLoadtoFormatter(formatter, l); - addExtensions(mapper.getInt(AmplSubset.LOAD, l.getId()), l); } for (DanglingLine dl : getSortedIdentifiables(network.getDanglingLineStream(DanglingLineFilter.UNPAIRED))) { String middleBusId = AmplUtil.getDanglingLineMiddleBusId(dl); if (!exportLoad(context, middleBusId)) { skipped.add(dl.getId()); - continue; + } else { + columnsExporter.writeDanglingLineLoadToFormatter(formatter, dl); } - columnsExporter.writeDanglingLineLoadToFormatter(formatter, dl); } if (!skipped.isEmpty()) { LOGGER.trace("Skip loads {} because not connected and not connectable", skipped); @@ -570,17 +546,12 @@ private void writeLoads(AmplExportContext context) throws IOException { } private boolean exportGeneratorOrShunt(AmplExportContext context, String busId, String conBusId) { - switch (config.getExportScope()) { - case ALL: - return true; - case ONLY_MAIN_CC: - return isBusExported(context, busId); - case ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS: - case ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS: - return isBusExported(context, conBusId); - default: - throw new IllegalStateException(); - } + return switch (config.getExportScope()) { + case ALL -> true; + case ONLY_MAIN_CC -> isBusExported(context, busId); + case ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS, ONLY_MAIN_CC_AND_CONNECTABLE_GENERATORS_AND_SHUNTS_AND_ALL_LOADS -> + isBusExported(context, conBusId); + }; } private void writeShunts(AmplExportContext context) throws IOException { @@ -748,7 +719,7 @@ private void writeLccConverterStations() throws IOException { AmplConstants.LOCALE, columnsExporter.getLccConverterStationsColumns())) { - for (HvdcConverterStation hvdcStation : getSortedIdentifiables(network.getHvdcConverterStationStream())) { + for (HvdcConverterStation hvdcStation : getSortedIdentifiables(network.getHvdcConverterStationStream())) { if (hvdcStation.getHvdcType().equals(HvdcType.LCC)) { LccConverterStation lccStation = (LccConverterStation) hvdcStation; columnsExporter.writeLccConverterStationToFormatter(formatter, lccStation); @@ -768,7 +739,7 @@ private void writeVscConverterStations() throws IOException { AmplConstants.LOCALE, columnsExporter.getVscConverterStationsColumns())) { - for (HvdcConverterStation hvdcStation : getSortedIdentifiables(network.getHvdcConverterStationStream())) { + for (HvdcConverterStation hvdcStation : getSortedIdentifiables(network.getHvdcConverterStationStream())) { if (hvdcStation.getHvdcType().equals(HvdcType.VSC)) { VscConverterStation vscStation = (VscConverterStation) hvdcStation; columnsExporter.writeVscConverterStationToFormatter(formatter, vscStation); diff --git a/ampl-converter/src/test/java/com/powsybl/ampl/converter/AmplNetworkWriterTest.java b/ampl-converter/src/test/java/com/powsybl/ampl/converter/AmplNetworkWriterTest.java index 163fa476f32..eb58f7b7838 100644 --- a/ampl-converter/src/test/java/com/powsybl/ampl/converter/AmplNetworkWriterTest.java +++ b/ampl-converter/src/test/java/com/powsybl/ampl/converter/AmplNetworkWriterTest.java @@ -144,18 +144,18 @@ void writeBattery() throws IOException { void writeThreeWindingsTransformer() throws IOException { Network network = ThreeWindingsTransformerNetworkFactory.createWithCurrentLimits(); network.getThreeWindingsTransformer("3WT").getLeg1() - .newPhaseTapChanger() - .beginStep() - .setRho(1) - .setR(0.1) - .setX(1.) - .setB(0.) - .setG(0.) - .setAlpha(0) - .endStep() - .setTapPosition(0) - .setLowTapPosition(0) - .add(); + .newPhaseTapChanger() + .beginStep() + .setRho(1) + .setR(0.1) + .setX(1.) + .setB(0.) + .setG(0.) + .setAlpha(0) + .endStep() + .setTapPosition(0) + .setLowTapPosition(0) + .add(); MemDataSource dataSource = new MemDataSource(); export(network, new Properties(), dataSource); @@ -195,10 +195,10 @@ void writeTieLine() throws IOException { Network network = EurostagTutorialExample1Factory.createWithTieLine(); for (DanglingLine danglingLine : network.getDanglingLines()) { danglingLine.newCurrentLimits() - .setPermanentLimit(100.0) - .beginTemporaryLimit().setName("20'").setValue(120.0).setAcceptableDuration(20 * 60).endTemporaryLimit() - .beginTemporaryLimit().setName("10'").setValue(140.0).setAcceptableDuration(10 * 60).endTemporaryLimit() - .add(); + .setPermanentLimit(100.0) + .beginTemporaryLimit().setName("20'").setValue(120.0).setAcceptableDuration(20 * 60).endTemporaryLimit() + .beginTemporaryLimit().setName("10'").setValue(140.0).setAcceptableDuration(10 * 60).endTemporaryLimit() + .add(); } Properties properties = new Properties(); diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/BoundaryEquipment.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/BoundaryEquipment.java index 70322917b06..50bd4e88f22 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/BoundaryEquipment.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/BoundaryEquipment.java @@ -40,22 +40,12 @@ enum BoundaryEquipmentType { } EquipmentAtBoundaryConversion createConversion(Context context) { - EquipmentAtBoundaryConversion c = null; - switch (type) { - case AC_LINE_SEGMENT: - c = new ACLineSegmentConversion(propertyBags.get(0), context); - break; - case SWITCH: - c = new SwitchConversion(propertyBags.get(0), context); - break; - case TRANSFORMER: - c = new TwoWindingsTransformerConversion(propertyBags, context); - break; - case EQUIVALENT_BRANCH: - c = new EquivalentBranchConversion(propertyBags.get(0), context); - break; - } - return c; + return switch (type) { + case AC_LINE_SEGMENT -> new ACLineSegmentConversion(propertyBags.get(0), context); + case SWITCH -> new SwitchConversion(propertyBags.get(0), context); + case TRANSFORMER -> new TwoWindingsTransformerConversion(propertyBags, context); + case EQUIVALENT_BRANCH -> new EquivalentBranchConversion(propertyBags.get(0), context); + }; } boolean isAcLineSegmentDisconnected(Context context) { diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java index 807ea189556..01951005e83 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java @@ -133,7 +133,7 @@ public boolean exists(ReadOnlyDataSource ds) { } // If we are configured to support CIM14, // check if there is this CIM14 data - return importCim14 && cds.existsCim14(); + return IMPORT_CIM_14 && cds.existsCim14(); } @Override @@ -735,5 +735,5 @@ private void copyStream(ReadOnlyDataSource from, DataSource to, String fromName, // Parameters of importers are only passed to importData method, // but to decide if we are importers also for CIM 14 files // we must implement the exists method, that has not access to parameters - private boolean importCim14 = false; + private static final boolean IMPORT_CIM_14 = false; } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java index 224b6331b40..869455c855a 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/Conversion.java @@ -893,13 +893,11 @@ public StateProfile getProfileForInitialValuesShuntSectionsTapPositions() { } public Config setProfileForInitialValuesShuntSectionsTapPositions(String profileForInitialValuesShuntSectionsTapPositions) { - switch (Objects.requireNonNull(profileForInitialValuesShuntSectionsTapPositions)) { - case "SSH": - case "SV": - this.profileForInitialValuesShuntSectionsTapPositions = StateProfile.valueOf(profileForInitialValuesShuntSectionsTapPositions); - break; - default: - throw new CgmesModelException("Unexpected profile used for shunt sections / tap positions state hypothesis: " + profileForInitialValuesShuntSectionsTapPositions); + String forInitialValuesShuntSectionsTapPositions = Objects.requireNonNull(profileForInitialValuesShuntSectionsTapPositions); + if (forInitialValuesShuntSectionsTapPositions.equals("SSH") || forInitialValuesShuntSectionsTapPositions.equals("SV")) { + this.profileForInitialValuesShuntSectionsTapPositions = StateProfile.valueOf(profileForInitialValuesShuntSectionsTapPositions); + } else { + throw new CgmesModelException("Unexpected profile used for shunt sections / tap positions state hypothesis: " + profileForInitialValuesShuntSectionsTapPositions); } return this; } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CountryConversion.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CountryConversion.java index 2bc4e8b902a..a7ca1a160fc 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CountryConversion.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CountryConversion.java @@ -47,32 +47,16 @@ public static Optional fromSubregionName(String name) { if (name == null) { return Optional.empty(); } - switch (name.trim().toUpperCase()) { - case "NO1": - case "NO2": - case "NO3": - case "NO4": - case "NO5": - return Optional.of(Country.NO); - case "SE1": - case "SE2": - case "SE3": - case "SE4": - return Optional.of(Country.SE); - case "FI1": - return Optional.of(Country.FI); - case "DK1": - case "DK2": - return Optional.of(Country.DK); - case "EE1": - return Optional.of(Country.EE); - case "LV1": - return Optional.of(Country.LV); - case "LT1": - return Optional.of(Country.LT); - default: - return Optional.empty(); - } + return switch (name.trim().toUpperCase()) { + case "NO1", "NO2", "NO3", "NO4", "NO5" -> Optional.of(Country.NO); + case "SE1", "SE2", "SE3", "SE4" -> Optional.of(Country.SE); + case "FI1" -> Optional.of(Country.FI); + case "DK1", "DK2" -> Optional.of(Country.DK); + case "EE1" -> Optional.of(Country.EE); + case "LV1" -> Optional.of(Country.LV); + case "LT1" -> Optional.of(Country.LT); + default -> Optional.empty(); + }; } public static Optional fromIsoCode(String iso) { diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/NodeMapping.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/NodeMapping.java index 055779ef626..e5e7778b736 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/NodeMapping.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/NodeMapping.java @@ -91,16 +91,11 @@ public int iidmNodeForTerminal(CgmesTerminal t, boolean isSwitchEnd, VoltageLeve } private static boolean createFictitiousSwitch(CgmesImport.FictitiousSwitchesCreationMode mode, boolean isSwitchEnd) { - switch (mode) { - case ALWAYS: - return true; - case NEVER: - return false; - case ALWAYS_EXCEPT_SWITCHES: - return !isSwitchEnd; - default: - throw new IllegalStateException("Unsupported specified mode to create fictitious switches for disconnected terminals: " + mode.name()); - } + return switch (mode) { + case ALWAYS -> true; + case NEVER -> false; + case ALWAYS_EXCEPT_SWITCHES -> !isSwitchEnd; + }; } public int iidmNodeForConnectivityNode(String id, VoltageLevel vl) { diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/hvdc/IslandEndHvdc.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/hvdc/IslandEndHvdc.java index cc1e2d774b4..193b4768a9f 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/hvdc/IslandEndHvdc.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/hvdc/IslandEndHvdc.java @@ -190,13 +190,13 @@ HvdcEndType computeType() { int c = this.acDcConvertersEnd.size(); int ls = this.dcLineSegmentsEnd.size(); - if (t >= 0 && c == 1 && ls == 1) { + if (c == 1 && ls == 1) { return HvdcEndType.HVDC_TN_C1_LS1; - } else if (t >= 0 && c >= 1 && ls == 2 * c) { + } else if (c >= 1 && ls == 2 * c) { return HvdcEndType.HVDC_TN_CN_LS2N; - } else if (t >= 0 && c == 2 && ls == 1) { + } else if (c == 2 && ls == 1) { return HvdcEndType.HVDC_TN_C2_LS1; - } else if (t >= 0 && c == ls && c > 1) { + } else if (c == ls && c > 1) { return HvdcEndType.HVDC_TN_CN_LSN; } @@ -211,8 +211,7 @@ boolean isMatchingTo(HvdcEnd otherHvdcEnd) { return false; } - return this.dcLineSegmentsEnd.stream() - .allMatch(otherHvdcEnd.dcLineSegmentsEnd::contains); + return otherHvdcEnd.dcLineSegmentsEnd.containsAll(this.dcLineSegmentsEnd); } boolean isAssociatedWith(HvdcEnd otherHvdcEnd) { @@ -223,7 +222,7 @@ static HvdcEnd joinAll(List listHvdcEnd) { HvdcEnd finalHvdcEnd = new HvdcEnd(new ArrayList<>(), new HashSet<>(), new HashSet<>(), new HashSet<>()); - listHvdcEnd.stream().forEach(hvdcEnd -> { + listHvdcEnd.forEach(hvdcEnd -> { finalHvdcEnd.nodesEnd.addAll(hvdcEnd.nodesEnd); finalHvdcEnd.transformersEnd.addAll(hvdcEnd.transformersEnd); finalHvdcEnd.acDcConvertersEnd.addAll(hvdcEnd.acDcConvertersEnd); diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java index e4f532b3e8c..e98d8edb1f4 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/CgmesExportUtil.java @@ -291,10 +291,6 @@ public static String switchClassname(SwitchKind kind) { case BREAKER -> "Breaker"; case DISCONNECTOR -> "Disconnector"; case LOAD_BREAK_SWITCH -> "LoadBreakSwitch"; - default -> { - LOG.warn("It is not possible to determine the type of switch from kind {}", kind); - yield "Switch"; - } }; } diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java index 1a47768509a..f0aa90ddd5e 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/export/SteadyStateHypothesisExport.java @@ -254,7 +254,6 @@ private static void writeShuntCompensators(Network network, String cimNamespace, String shuntType = switch (s.getModelType()) { case LINEAR -> "Linear"; case NON_LINEAR -> "Nonlinear"; - default -> throw new IllegalStateException("Unexpected shunt model type: " + s.getModelType()); }; boolean controlEnabled = s.isVoltageRegulatorOn(); diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/FakeSubstationsConversionTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/FakeSubstationsConversionTest.java index f7ea0fd14c9..c206df8d5c0 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/FakeSubstationsConversionTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/FakeSubstationsConversionTest.java @@ -9,7 +9,6 @@ package com.powsybl.cgmes.conversion.test; import java.util.List; - import java.util.stream.Collectors; import com.powsybl.iidm.network.Substation; diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/network/compare/Comparison.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/network/compare/Comparison.java index 2488acac5dd..bc98c0a8a5d 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/network/compare/Comparison.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/network/compare/Comparison.java @@ -524,9 +524,9 @@ private void compareGeneratorReactiveCapabilityCurve( // so we sort points by active power, then compare resulting lists point by point Comparator comparePoints = (p0, p1) -> Double.compare(p0.getP(), p1.getP()); List e = expected.getPoints().stream().sorted(comparePoints) - .collect(Collectors.toList()); + .toList(); List a = actual.getPoints().stream().sorted(comparePoints) - .collect(Collectors.toList()); + .toList(); compare("reactiveCapabilityCurve.size", e.size(), a.size()); for (int k = 0; k < e.size(); k++) { Point pe = e.get(k); diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/AbstractCgmesModel.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/AbstractCgmesModel.java index 1822f1e114a..1ccbd7dc77b 100644 --- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/AbstractCgmesModel.java +++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/AbstractCgmesModel.java @@ -122,11 +122,7 @@ public double nominalVoltage(String baseVoltageId) { baseVoltages() .forEach(bv -> cachedBaseVoltages.put(bv.getId("BaseVoltage"), bv.asDouble("nominalVoltage"))); } - if (cachedBaseVoltages.containsKey(baseVoltageId)) { - return cachedBaseVoltages.get(baseVoltageId); - } else { - return Double.NaN; - } + return cachedBaseVoltages.getOrDefault(baseVoltageId, Double.NaN); } private CgmesContainer container(CgmesTerminal t, boolean nodeBreaker) { diff --git a/commons/src/main/java/com/powsybl/commons/config/AbstractMapModuleConfig.java b/commons/src/main/java/com/powsybl/commons/config/AbstractMapModuleConfig.java index 8684987f343..1c1c3548fa6 100644 --- a/commons/src/main/java/com/powsybl/commons/config/AbstractMapModuleConfig.java +++ b/commons/src/main/java/com/powsybl/commons/config/AbstractMapModuleConfig.java @@ -29,7 +29,7 @@ public abstract class AbstractMapModuleConfig extends AbstractModuleConfig { private static PowsyblException createUnexpectedPropertyTypeException(String name, Class type, Class[] expectedTypes) { return new PowsyblException("Unexpected value type " + type.getName() - + " for property " + name + ", " + Arrays.toString(expectedTypes) + " is expected "); + + " for property " + name + ", " + Arrays.toString(expectedTypes) + " is expected "); } private static PowsyblException createPropertyIsNotException(String name, String what, Exception e) { @@ -49,7 +49,7 @@ protected AbstractMapModuleConfig(FileSystem fs) { * ({@link Date} or {@link Float} for instance). * * @param propertyName The name of the property to be looked up - * @return The value of the specified property it it exists, {@code null} otherwise. + * @return The value of the specified property it it exists, {@code null} otherwise. */ protected abstract Object getValue(String propertyName); @@ -61,7 +61,7 @@ public Optional getOptionalStringProperty(String name) { return Optional.empty(); } if (!(value instanceof String)) { - throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[] {String.class}); + throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[]{String.class}); } return Optional.of((String) value).map(PlatformEnv::substitute); } @@ -83,7 +83,7 @@ public Optional> getOptionalStringListProperty(String name) { } else if (value instanceof List) { return Optional.of(((List) value).stream().map(PlatformEnv::substitute).collect(Collectors.toList())); } else { - throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[] {String.class, List.class}); + throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[]{String.class, List.class}); } } } @@ -104,7 +104,7 @@ public OptionalInt getOptionalIntProperty(String name) { throw createPropertyIsNotException(name, "an integer", e); } } else { - throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[] {String.class, Integer.class}); + throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[]{String.class, Integer.class}); } } @@ -126,7 +126,7 @@ public OptionalLong getOptionalLongProperty(String name) { throw createPropertyIsNotException(name, "a long", e); } } else { - throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[] {String.class, Long.class, Integer.class}); + throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[]{String.class, Long.class, Integer.class}); } } @@ -146,7 +146,7 @@ public Optional getOptionalFloatProperty(String name) { throw createPropertyIsNotException(name, "a float", e); } } else { - throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[] {Number.class, String.class}); + throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[]{Number.class, String.class}); } } @@ -166,7 +166,7 @@ public OptionalDouble getOptionalDoubleProperty(String name) { throw createPropertyIsNotException(name, "a double", e); } } else { - throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[] {Number.class, String.class}); + throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[]{Number.class, String.class}); } } @@ -182,7 +182,7 @@ public Optional getOptionalBooleanProperty(String name) { } else if (value instanceof String val) { return Optional.of(Boolean.parseBoolean(val)); } else { - throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[] {Boolean.class, String.class}); + throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[]{Boolean.class, String.class}); } } @@ -202,7 +202,7 @@ public Optional getOptionalDateTimeProperty(String name) { throw createPropertyIsNotException(name, "an ISO date time", e); } } else { - throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[] {Date.class, String.class}); + throw createUnexpectedPropertyTypeException(name, value.getClass(), new Class[]{Date.class, String.class}); } } @@ -214,7 +214,7 @@ public Optional getOptionalPathProperty(String name) { @Override public Optional> getOptionalPathListProperty(String name) { return getOptionalStringListProperty(name).flatMap(strings -> Optional.of(strings.stream() - .map(fs::getPath) - .collect(Collectors.toList()))); + .map(fs::getPath) + .collect(Collectors.toList()))); } } diff --git a/commons/src/main/java/com/powsybl/commons/json/JsonUtil.java b/commons/src/main/java/com/powsybl/commons/json/JsonUtil.java index 7ca6119cfe6..d90e3477431 100644 --- a/commons/src/main/java/com/powsybl/commons/json/JsonUtil.java +++ b/commons/src/main/java/com/powsybl/commons/json/JsonUtil.java @@ -367,7 +367,7 @@ public static List> updateExtensions(JsonPar private static > E updateExtension(JsonParser parser, DeserializationContext context, SerializerSupplier supplier, Set extensionsNotFound, T extendable) throws IOException { - String extensionName = parser.getCurrentName(); + String extensionName = parser.currentName(); ExtensionJsonSerializer extensionJsonSerializer = supplier.getSerializer(extensionName); if (extensionJsonSerializer != null) { parser.nextToken(); @@ -513,7 +513,7 @@ public static void parseObject(JsonParser parser, boolean polymorphic, FieldHand } while (token != null) { if (token == JsonToken.FIELD_NAME) { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); boolean found = fieldHandler.onField(fieldName); if (!found) { throw new PowsyblException("Unexpected field " + fieldName); diff --git a/commons/src/main/java/com/powsybl/commons/xml/XmlUtil.java b/commons/src/main/java/com/powsybl/commons/xml/XmlUtil.java index c994cdafa9d..b29581a7b41 100644 --- a/commons/src/main/java/com/powsybl/commons/xml/XmlUtil.java +++ b/commons/src/main/java/com/powsybl/commons/xml/XmlUtil.java @@ -63,23 +63,24 @@ private static boolean readUntilStartElement(String startElement, String endElem int event; while ((event = reader.next()) != XMLStreamConstants.END_DOCUMENT) { switch (event) { - case XMLStreamConstants.START_ELEMENT: + case XMLStreamConstants.START_ELEMENT -> { if (reader.getLocalName().equals(startElement)) { return true; } else { // Skip the current element skipSubElements(reader); } - break; + } - case XMLStreamConstants.END_ELEMENT: + case XMLStreamConstants.END_ELEMENT -> { if (reader.getLocalName().equals(endElement)) { return false; } - break; + } - default: - break; + default -> { + // Do nothing + } } } throw new PowsyblException("Unable to find " + startElement + ": end of document has been reached"); diff --git a/commons/src/test/java/com/powsybl/commons/extensions/FooDeserializer.java b/commons/src/test/java/com/powsybl/commons/extensions/FooDeserializer.java index 88c09641393..a8ecdbfa204 100644 --- a/commons/src/test/java/com/powsybl/commons/extensions/FooDeserializer.java +++ b/commons/src/test/java/com/powsybl/commons/extensions/FooDeserializer.java @@ -39,7 +39,7 @@ public Foo deserialize(JsonParser parser, DeserializationContext context) throws List> extensions = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - if (parser.getCurrentName().equals("extensions")) { + if (parser.currentName().equals("extensions")) { parser.nextToken(); extensions = JsonUtil.readExtensions(parser, context); } @@ -62,7 +62,7 @@ public Foo deserialize(JsonParser parser, DeserializationContext context, Foo in List> extensions = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - if (parser.getCurrentName().equals("extensions")) { + if (parser.currentName().equals("extensions")) { parser.nextToken(); extensions = JsonUtil.updateExtensions(parser, context, initFoo); } diff --git a/commons/src/test/java/com/powsybl/commons/extensions/FooExtSerializer.java b/commons/src/test/java/com/powsybl/commons/extensions/FooExtSerializer.java index 913cac565cd..5343bdfdfe7 100644 --- a/commons/src/test/java/com/powsybl/commons/extensions/FooExtSerializer.java +++ b/commons/src/test/java/com/powsybl/commons/extensions/FooExtSerializer.java @@ -53,11 +53,11 @@ public void serialize(FooExt extension, JsonGenerator jsonGenerator, SerializerP public FooExt deserialize(JsonParser parser, DeserializationContext deserializationContext) throws IOException { Boolean value = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - if (parser.getCurrentName().equals("value")) { + if (parser.currentName().equals("value")) { parser.nextToken(); value = parser.readValueAs(Boolean.class); } else { - throw new PowsyblException("Unexpected field: " + parser.getCurrentName()); + throw new PowsyblException("Unexpected field: " + parser.currentName()); } } diff --git a/computation-local-test/src/main/java/com/powsybl/computation/local/test/DockerLocalCommandExecutor.java b/computation-local-test/src/main/java/com/powsybl/computation/local/test/DockerLocalCommandExecutor.java index 679691a573a..9b6c3e043f2 100644 --- a/computation-local-test/src/main/java/com/powsybl/computation/local/test/DockerLocalCommandExecutor.java +++ b/computation-local-test/src/main/java/com/powsybl/computation/local/test/DockerLocalCommandExecutor.java @@ -14,7 +14,6 @@ import org.testcontainers.utility.DockerImageName; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -65,8 +64,8 @@ public int execute(String program, List args, Path outFile, Path errFile // - subdirectories of containerWorkingDir created inside the container WON'T BE DELETABLE BY THE HOST, // so we need to change their permissions container.execInContainer("chmod", "-R", "777", containerWorkingDir); - Files.write(errFile, execResult.getStderr().getBytes(StandardCharsets.UTF_8)); - Files.write(outFile, execResult.getStdout().getBytes(StandardCharsets.UTF_8)); + Files.writeString(errFile, execResult.getStderr()); + Files.writeString(outFile, execResult.getStdout()); container.stop(); } finally { containers.remove(workingDir); diff --git a/computation-local-test/src/test/java/com/powsybl/computation/local/test/DockerLocalCommandExecutorTest.java b/computation-local-test/src/test/java/com/powsybl/computation/local/test/DockerLocalCommandExecutorTest.java index 2b111951057..afbbd13945e 100644 --- a/computation-local-test/src/test/java/com/powsybl/computation/local/test/DockerLocalCommandExecutorTest.java +++ b/computation-local-test/src/test/java/com/powsybl/computation/local/test/DockerLocalCommandExecutorTest.java @@ -16,7 +16,6 @@ import org.junit.jupiter.api.io.TempDir; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -76,7 +75,7 @@ void testInputFile() { List lines = computationManager.execute(ExecutionEnvironment.createDefault(), new AbstractExecutionHandler>() { @Override public List before(Path workingDir) throws IOException { - Files.write(workingDir.resolve("test.txt"), "hello".getBytes(StandardCharsets.UTF_8)); + Files.writeString(workingDir.resolve("test.txt"), "hello"); return List.of(new CommandExecution(new SimpleCommandBuilder() .id(COMMAND_ID) .program("cat") diff --git a/computation/src/main/java/com/powsybl/computation/InputFile.java b/computation/src/main/java/com/powsybl/computation/InputFile.java index 908ba9d1f8c..410ead19fb7 100644 --- a/computation/src/main/java/com/powsybl/computation/InputFile.java +++ b/computation/src/main/java/com/powsybl/computation/InputFile.java @@ -43,18 +43,17 @@ public static String checkName(String name, FilePreProcessor preProcessor) { } if (preProcessor != null) { switch (preProcessor) { - case FILE_GUNZIP: + case FILE_GUNZIP -> { if (!name.endsWith(".gz")) { throw new IllegalArgumentException(name + " is expected to end with .gz"); } - break; - case ARCHIVE_UNZIP: + } + case ARCHIVE_UNZIP -> { if (!name.endsWith(".zip")) { throw new IllegalArgumentException(name + " is expected to end with .zip"); } - break; - default: - throw new IllegalStateException("Unexpected FilePreProcessor value: " + preProcessor); + } + default -> throw new IllegalStateException("Unexpected FilePreProcessor value: " + preProcessor); } } return name; diff --git a/config-classic/src/test/java/com/powsybl/config/classic/ClassicPlatformConfigProviderTest.java b/config-classic/src/test/java/com/powsybl/config/classic/ClassicPlatformConfigProviderTest.java index 5766d1ccd02..7460fdaf60f 100644 --- a/config-classic/src/test/java/com/powsybl/config/classic/ClassicPlatformConfigProviderTest.java +++ b/config-classic/src/test/java/com/powsybl/config/classic/ClassicPlatformConfigProviderTest.java @@ -51,22 +51,22 @@ private List getAbsolutePaths(String configDirs) { @Test void testNoUserHome() { - assertEquals(Arrays.asList("/.itools"), getAbsolutePaths(null)); + assertEquals(List.of("/.itools"), getAbsolutePaths(null)); } @Test void testEdgeCaseEmptyAfterSplit() { - assertEquals(Arrays.asList("/.itools"), getAbsolutePaths(":")); + assertEquals(List.of("/.itools"), getAbsolutePaths(":")); } @Test void workDir() { - assertEquals(Arrays.asList("/work"), getAbsolutePaths(".")); + assertEquals(List.of("/work"), getAbsolutePaths(".")); } @Test void testEmptyConfigDirs() { - assertEquals(Arrays.asList("/.itools"), getAbsolutePaths("")); + assertEquals(List.of("/.itools"), getAbsolutePaths("")); } @Test @@ -81,6 +81,6 @@ void testModuleRepository() throws IOException { } ModuleConfigRepository loadModuleRepository = ClassicPlatformConfigProvider .loadModuleRepository(new Path[] {fileSystem.getPath("/") }, "config"); - assertEquals("baz", loadModuleRepository.getModuleConfig("foo").get().getStringProperty("bar")); + assertEquals("baz", loadModuleRepository.getModuleConfig("foo").orElseThrow().getStringProperty("bar")); } } diff --git a/contingency/contingency-api/src/main/java/com/powsybl/contingency/contingency/list/IdentifierContingencyListDeserializer.java b/contingency/contingency-api/src/main/java/com/powsybl/contingency/contingency/list/IdentifierContingencyListDeserializer.java index f4d2a009f79..597a1384fe3 100644 --- a/contingency/contingency-api/src/main/java/com/powsybl/contingency/contingency/list/IdentifierContingencyListDeserializer.java +++ b/contingency/contingency-api/src/main/java/com/powsybl/contingency/contingency/list/IdentifierContingencyListDeserializer.java @@ -37,7 +37,7 @@ public IdentifierContingencyList deserialize(JsonParser parser, DeserializationC String version = null; List networkElementIdentifiers = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - String test = parser.getCurrentName(); + String test = parser.currentName(); switch (test) { case "version" -> { version = parser.nextTextValue(); @@ -58,7 +58,7 @@ public IdentifierContingencyList deserialize(JsonParser parser, DeserializationC parser.nextToken(); networkElementIdentifiers = JsonUtil.readList(deserializationContext, parser, NetworkElementIdentifier.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } return new IdentifierContingencyList(name, networkElementIdentifiers); diff --git a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyDeserializer.java b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyDeserializer.java index 9c2720a0ec2..f05a06cf17c 100644 --- a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyDeserializer.java +++ b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyDeserializer.java @@ -47,7 +47,7 @@ public Contingency deserialize(JsonParser parser, DeserializationContext deseria List> extensions = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "id" -> id = parser.nextTextValue(); case "name" -> name = parser.nextTextValue(); case "elements" -> { @@ -58,7 +58,7 @@ public Contingency deserialize(JsonParser parser, DeserializationContext deseria parser.nextToken(); extensions = JsonUtil.readExtensions(parser, deserializationContext, SUPPLIER.get()); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } Contingency contingency = new Contingency(id, name, elements); diff --git a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyElementDeserializer.java b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyElementDeserializer.java index 60101d8bd43..bbccb7da4ec 100644 --- a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyElementDeserializer.java +++ b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyElementDeserializer.java @@ -32,14 +32,14 @@ public ContingencyElement deserialize(JsonParser parser, DeserializationContext ContingencyElementType type = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "id" -> id = parser.nextTextValue(); case "voltageLevelId" -> voltageLevelId = parser.nextTextValue(); case "type" -> { parser.nextToken(); type = JsonUtil.readValue(ctx, parser, ContingencyElementType.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } diff --git a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyListDeserializer.java b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyListDeserializer.java index ab3d29b012d..8be2bbcec1f 100644 --- a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyListDeserializer.java +++ b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ContingencyListDeserializer.java @@ -27,8 +27,8 @@ public ContingencyListDeserializer() { @Override public ContingencyList deserialize(JsonParser parser, DeserializationContext deserializationContext) throws IOException { parser.nextToken(); - while (parser.getCurrentName() != null) { - if ("type".equals(parser.getCurrentName())) { + while (parser.currentName() != null) { + if ("type".equals(parser.currentName())) { switch (parser.nextTextValue()) { case "default" -> { DefaultContingencyListDeserializer defaultContingencyListDeserializer = new DefaultContingencyListDeserializer(); @@ -66,7 +66,7 @@ public ContingencyList deserialize(JsonParser parser, DeserializationContext des IdentifierContingencyListDeserializer identifierContingencyListDeserializer = new IdentifierContingencyListDeserializer(); return identifierContingencyListDeserializer.deserialize(parser, deserializationContext); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } parser.nextToken(); diff --git a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/DefaultContingencyListDeserializer.java b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/DefaultContingencyListDeserializer.java index 751779edafb..3f8881d8bc8 100644 --- a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/DefaultContingencyListDeserializer.java +++ b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/DefaultContingencyListDeserializer.java @@ -34,7 +34,7 @@ public DefaultContingencyList deserialize(JsonParser parser, DeserializationCont List contingencies = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version" -> parser.nextToken(); case "name" -> name = parser.nextTextValue(); case "type" -> { @@ -46,7 +46,7 @@ public DefaultContingencyList deserialize(JsonParser parser, DeserializationCont parser.nextToken(); contingencies = JsonUtil.readList(ctx, parser, Contingency.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } diff --git a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ListOfContingencyListsDeserializer.java b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ListOfContingencyListsDeserializer.java index b33be7352e2..ceb2d1716e8 100644 --- a/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ListOfContingencyListsDeserializer.java +++ b/contingency/contingency-api/src/main/java/com/powsybl/contingency/json/ListOfContingencyListsDeserializer.java @@ -33,7 +33,7 @@ public ListOfContingencyLists deserialize(JsonParser parser, DeserializationCont String name = null; List contingencyLists = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version" -> deserializationContext.setAttribute("version", parser.nextTextValue()); case "name" -> name = parser.nextTextValue(); case "type" -> { @@ -45,7 +45,7 @@ public ListOfContingencyLists deserialize(JsonParser parser, DeserializationCont parser.nextToken(); contingencyLists = JsonUtil.readList(deserializationContext, parser, ContingencyList.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } return new ListOfContingencyLists(name, contingencyLists); diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/BranchContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/BranchContingencyTest.java index e1688c5caba..5e89d2ba2b6 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/BranchContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/BranchContingencyTest.java @@ -31,7 +31,7 @@ void test() { assertEquals(ContingencyElementType.BRANCH, contingency.getType()); assertNotNull(contingency.toModification()); - assertTrue(contingency.toModification() instanceof BranchTripping); + assertInstanceOf(BranchTripping.class, contingency.toModification()); contingency = new BranchContingency("id", "voltageLevelId"); assertEquals("voltageLevelId", contingency.getVoltageLevelId()); diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/BusContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/BusContingencyTest.java index e45a87af242..39ec50d1b5c 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/BusContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/BusContingencyTest.java @@ -36,7 +36,7 @@ void test() { assertEquals(ContingencyElementType.BUS, busContingency.getType()); assertNotNull(busContingency.toModification()); - assertTrue(busContingency.toModification() instanceof BusTripping); + assertInstanceOf(BusTripping.class, busContingency.toModification()); new EqualsTester() .addEqualityGroup(new BusContingency("bus1"), new BusContingency("bus1")) diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/BusbarSectionContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/BusbarSectionContingencyTest.java index eda80808a4f..5f3b80cba9a 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/BusbarSectionContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/BusbarSectionContingencyTest.java @@ -34,7 +34,7 @@ void test() { assertEquals(ContingencyElementType.BUSBAR_SECTION, bbsContingency.getType()); assertNotNull(bbsContingency.toModification()); - assertTrue(bbsContingency.toModification() instanceof BusbarSectionTripping); + assertInstanceOf(BusbarSectionTripping.class, bbsContingency.toModification()); new EqualsTester() .addEqualityGroup(new BusbarSectionContingency("bbs1"), new BusbarSectionContingency("bbs1")) diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/ContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/ContingencyTest.java index 8217e6ebdd3..cf43f0c1c59 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/ContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/ContingencyTest.java @@ -45,7 +45,7 @@ void test() { assertEquals(ContingencyElementType.GENERATOR, elements.get(1).getType()); NetworkModification modification = contingency.toModification(); - assertTrue(modification instanceof NetworkModificationList); + assertInstanceOf(NetworkModificationList.class, modification); ContingencyElement bbsElement = new BusbarSectionContingency("bbs"); contingency.addElement(bbsElement); @@ -71,7 +71,7 @@ void validationTest() { List expectedValidIds = Arrays.asList("GEN contingency", "NHV1_NHV2_1 contingency"); assertEquals(expectedValidIds, - validContingencies.stream().map(Contingency::getId).collect(Collectors.toList())); + validContingencies.stream().map(Contingency::getId).toList()); assertEquals(expectedValidIds, ContingencyList.getValidContingencies(Arrays.asList(generatorContingency, generatorInvalidContingency, lineContingency, lineInvalidContingency), network) diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/DanglingLineContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/DanglingLineContingencyTest.java index 95254cafd26..7024c7f3f7c 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/DanglingLineContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/DanglingLineContingencyTest.java @@ -33,7 +33,7 @@ void test() { assertEquals(ContingencyElementType.DANGLING_LINE, dlContingency.getType()); assertNotNull(dlContingency.toModification()); - assertTrue(dlContingency.toModification() instanceof DanglingLineTripping); + assertInstanceOf(DanglingLineTripping.class, dlContingency.toModification()); new EqualsTester() .addEqualityGroup(new DanglingLineContingency("dl1"), new DanglingLineContingency("dl1")) diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/EmptyContingencyListProviderTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/EmptyContingencyListProviderTest.java index c302f64260a..36a6f7080c2 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/EmptyContingencyListProviderTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/EmptyContingencyListProviderTest.java @@ -20,7 +20,7 @@ void test() { ContingenciesProviderFactory factory = new EmptyContingencyListProviderFactory(); ContingenciesProvider provider = factory.create(); - assertTrue(provider instanceof EmptyContingencyListProvider); + assertInstanceOf(EmptyContingencyListProvider.class, provider); assertEquals(0, provider.getContingencies(null).size()); } } diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/GeneratorContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/GeneratorContingencyTest.java index c021a1385f2..60a7928ee2d 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/GeneratorContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/GeneratorContingencyTest.java @@ -35,7 +35,7 @@ void test() { assertEquals(ContingencyElementType.GENERATOR, genContingency.getType()); assertNotNull(genContingency.toModification()); - assertTrue(genContingency.toModification() instanceof GeneratorTripping); + assertInstanceOf(GeneratorTripping.class, genContingency.toModification()); new EqualsTester() .addEqualityGroup(new GeneratorContingency("g1"), new GeneratorContingency("g1")) diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/HvdcLineContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/HvdcLineContingencyTest.java index f5f4c552dca..687d3b14206 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/HvdcLineContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/HvdcLineContingencyTest.java @@ -31,7 +31,7 @@ void test() { assertEquals(ContingencyElementType.HVDC_LINE, contingency.getType()); assertNotNull(contingency.toModification()); - assertTrue(contingency.toModification() instanceof HvdcLineTripping); + assertInstanceOf(HvdcLineTripping.class, contingency.toModification()); contingency = new HvdcLineContingency("id", "voltageLevelId"); assertEquals("voltageLevelId", contingency.getVoltageLevelId()); diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/LoadContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/LoadContingencyTest.java index 83d92c2e7cf..d55edbc889e 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/LoadContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/LoadContingencyTest.java @@ -35,7 +35,7 @@ void test() { assertEquals(ContingencyElementType.LOAD, loadContingency.getType()); assertNotNull(loadContingency.toModification()); - assertTrue(loadContingency.toModification() instanceof LoadTripping); + assertInstanceOf(LoadTripping.class, loadContingency.toModification()); new EqualsTester() .addEqualityGroup(new LoadContingency("g1"), new LoadContingency("g1")) diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/ShuntCompensatorContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/ShuntCompensatorContingencyTest.java index 7f3454324df..edde0d4cbac 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/ShuntCompensatorContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/ShuntCompensatorContingencyTest.java @@ -34,7 +34,7 @@ void test() { assertEquals(ContingencyElementType.SHUNT_COMPENSATOR, scContingency.getType()); assertNotNull(scContingency.toModification()); - assertTrue(scContingency.toModification() instanceof ShuntCompensatorTripping); + assertInstanceOf(ShuntCompensatorTripping.class, scContingency.toModification()); new EqualsTester() .addEqualityGroup(new ShuntCompensatorContingency("sc1"), new ShuntCompensatorContingency("sc1")) diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/StaticVarCompensatorContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/StaticVarCompensatorContingencyTest.java index 6565dc811c7..81500a87029 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/StaticVarCompensatorContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/StaticVarCompensatorContingencyTest.java @@ -34,7 +34,7 @@ void test() { assertEquals(ContingencyElementType.STATIC_VAR_COMPENSATOR, svcContingency.getType()); assertNotNull(svcContingency.toModification()); - assertTrue(svcContingency.toModification() instanceof StaticVarCompensatorTripping); + assertInstanceOf(StaticVarCompensatorTripping.class, svcContingency.toModification()); new EqualsTester() .addEqualityGroup(new StaticVarCompensatorContingency("svc1"), new StaticVarCompensatorContingency("svc1")) diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/ThreeWindingsTransformerContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/ThreeWindingsTransformerContingencyTest.java index 2224eaa5f25..72a370969b1 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/ThreeWindingsTransformerContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/ThreeWindingsTransformerContingencyTest.java @@ -25,7 +25,7 @@ void test() { assertEquals(ContingencyElementType.THREE_WINDINGS_TRANSFORMER, twt3Contingency.getType()); assertNotNull(twt3Contingency.toModification()); - assertTrue(twt3Contingency.toModification() instanceof ThreeWindingsTransformerTripping); + assertInstanceOf(ThreeWindingsTransformerTripping.class, twt3Contingency.toModification()); new EqualsTester() .addEqualityGroup(new ThreeWindingsTransformerContingency("foo"), new ThreeWindingsTransformerContingency("foo")) diff --git a/contingency/contingency-api/src/test/java/com/powsybl/contingency/TieLineContingencyTest.java b/contingency/contingency-api/src/test/java/com/powsybl/contingency/TieLineContingencyTest.java index 83478a9a64f..4b493b1bb9a 100644 --- a/contingency/contingency-api/src/test/java/com/powsybl/contingency/TieLineContingencyTest.java +++ b/contingency/contingency-api/src/test/java/com/powsybl/contingency/TieLineContingencyTest.java @@ -31,7 +31,7 @@ void test() { assertEquals(ContingencyElementType.TIE_LINE, contingency.getType()); assertNotNull(contingency.toModification()); - assertTrue(contingency.toModification() instanceof TieLineTripping); + assertInstanceOf(TieLineTripping.class, contingency.toModification()); contingency = new TieLineContingency("id", "voltageLevelId"); assertEquals("voltageLevelId", contingency.getVoltageLevelId()); diff --git a/dsl/src/main/groovy/com/powsybl/dsl/ExpressionDslLoader.groovy b/dsl/src/main/groovy/com/powsybl/dsl/ExpressionDslLoader.groovy index 40d2bbb7c52..3d54c81ecfe 100644 --- a/dsl/src/main/groovy/com/powsybl/dsl/ExpressionDslLoader.groovy +++ b/dsl/src/main/groovy/com/powsybl/dsl/ExpressionDslLoader.groovy @@ -40,34 +40,34 @@ class ExpressionDslLoader extends DslLoader { // integer ExpressionNode.metaClass."$op0" = { Integer value -> - ExpressionHelper.newArithmeticBinaryOperator(delegate, ExpressionHelper.newIntegerLiteral(value), ArithmeticBinaryOperator."$op1") + ExpressionHelper.newArithmeticBinaryOperator(delegate as ExpressionNode, ExpressionHelper.newIntegerLiteral(value), ArithmeticBinaryOperator."$op1") } Integer.metaClass."$op0" = { ExpressionNode value -> - ExpressionHelper.newArithmeticBinaryOperator(ExpressionHelper.newIntegerLiteral(delegate), value, ArithmeticBinaryOperator."$op1") + ExpressionHelper.newArithmeticBinaryOperator(ExpressionHelper.newIntegerLiteral(delegate as int), value, ArithmeticBinaryOperator."$op1") } // float ExpressionNode.metaClass."$op0" = { Float value -> - ExpressionHelper.newArithmeticBinaryOperator(delegate, ExpressionHelper.newFloatLiteral(value), ArithmeticBinaryOperator."$op1") + ExpressionHelper.newArithmeticBinaryOperator(delegate as ExpressionNode, ExpressionHelper.newFloatLiteral(value), ArithmeticBinaryOperator."$op1") } Float.metaClass."$op0" = { ExpressionNode value -> - ExpressionHelper.newArithmeticBinaryOperator(ExpressionHelper.newFloatLiteral(delegate), value, ArithmeticBinaryOperator."$op1") + ExpressionHelper.newArithmeticBinaryOperator(ExpressionHelper.newFloatLiteral(delegate as float), value, ArithmeticBinaryOperator."$op1") } // double ExpressionNode.metaClass."$op0" = { Double value -> - ExpressionHelper.newArithmeticBinaryOperator(delegate, ExpressionHelper.newDoubleLiteral(value), ArithmeticBinaryOperator."$op1") + ExpressionHelper.newArithmeticBinaryOperator(delegate as ExpressionNode, ExpressionHelper.newDoubleLiteral(value), ArithmeticBinaryOperator."$op1") } Double.metaClass."$op0" = { ExpressionNode value -> - ExpressionHelper.newArithmeticBinaryOperator(ExpressionHelper.newDoubleLiteral(delegate), value, ArithmeticBinaryOperator."$op1") + ExpressionHelper.newArithmeticBinaryOperator(ExpressionHelper.newDoubleLiteral(delegate as double), value, ArithmeticBinaryOperator."$op1") } // big decimal ExpressionNode.metaClass."$op0" = { BigDecimal value -> - ExpressionHelper.newArithmeticBinaryOperator(delegate, ExpressionHelper.newBigDecimalLiteral(value), ArithmeticBinaryOperator."$op1") + ExpressionHelper.newArithmeticBinaryOperator(delegate as ExpressionNode, ExpressionHelper.newBigDecimalLiteral(value), ArithmeticBinaryOperator."$op1") } BigDecimal.metaClass."$op0" = { ExpressionNode value -> @@ -78,7 +78,7 @@ class ExpressionDslLoader extends DslLoader { // comparison - java.lang.Object.metaClass.compareTo2 = { Object value, String op -> + Object.metaClass.compareTo2 = { Object value, String op -> switch (op) { case ">": return delegate > value @@ -98,31 +98,31 @@ class ExpressionDslLoader extends DslLoader { } ExpressionNode.metaClass.compareTo2 = { ExpressionNode value, String op -> - nodeCompareToNode(delegate, value, op) + nodeCompareToNode(delegate as ExpressionNode, value, op) } ExpressionNode.metaClass.compareTo2 = { Integer value, String op -> - nodeCompareToNode(delegate, ExpressionHelper.newIntegerLiteral(value), op) + nodeCompareToNode(delegate as ExpressionNode, ExpressionHelper.newIntegerLiteral(value), op) } Integer.metaClass.compareTo2 = { ExpressionNode value, String op -> - nodeCompareToNode(ExpressionHelper.newIntegerLiteral(delegate), value, op) + nodeCompareToNode(ExpressionHelper.newIntegerLiteral(delegate as int), value, op) } ExpressionNode.metaClass.compareTo2 = { Float value, String op -> - nodeCompareToNode(delegate, ExpressionHelper.newFloatLiteral(value), op) + nodeCompareToNode(delegate as ExpressionNode, ExpressionHelper.newFloatLiteral(value), op) } Float.metaClass.compareTo2 = { ExpressionNode value, String op -> - nodeCompareToNode(ExpressionHelper.newFloatLiteral(delegate), value, op) + nodeCompareToNode(ExpressionHelper.newFloatLiteral(delegate as float), value, op) } ExpressionNode.metaClass.compareTo2 = { Double value, String op -> - nodeCompareToNode(delegate, ExpressionHelper.newDoubleLiteral(value), op) + nodeCompareToNode(delegate as ExpressionNode, ExpressionHelper.newDoubleLiteral(value), op) } Double.metaClass.compareTo2 = { ExpressionNode value, String op -> - nodeCompareToNode(ExpressionHelper.newDoubleLiteral(delegate), value, op) + nodeCompareToNode(ExpressionHelper.newDoubleLiteral(delegate as double), value, op) } ExpressionNode.metaClass.compareTo2 = { BigDecimal value, String op -> - nodeCompareToNode(delegate, ExpressionHelper.newBigDecimalLiteral(value), op) + nodeCompareToNode(delegate as ExpressionNode, ExpressionHelper.newBigDecimalLiteral(value), op) } BigDecimal.metaClass.compareTo2 = { ExpressionNode value, String op -> - nodeCompareToNode(ExpressionHelper.newBigDecimalLiteral(delegate), value, op) + nodeCompareToNode(ExpressionHelper.newBigDecimalLiteral(delegate as BigDecimal), value, op) } // boolean @@ -130,33 +130,33 @@ class ExpressionDslLoader extends DslLoader { delegate && value } ExpressionNode.metaClass.and2 = { Boolean value -> - ExpressionHelper.newLogicalBinaryOperator(delegate, ExpressionHelper.newBooleanLiteral(value), LogicalBinaryOperator.AND) + ExpressionHelper.newLogicalBinaryOperator(delegate as ExpressionNode, ExpressionHelper.newBooleanLiteral(value), LogicalBinaryOperator.AND) } Boolean.metaClass.and2 = { ExpressionNode value -> - ExpressionHelper.newLogicalBinaryOperator(ExpressionHelper.newBooleanLiteral(delegate), value, LogicalBinaryOperator.AND) + ExpressionHelper.newLogicalBinaryOperator(ExpressionHelper.newBooleanLiteral(delegate as boolean), value, LogicalBinaryOperator.AND) } ExpressionNode.metaClass.and2 = { ExpressionNode value -> - ExpressionHelper.newLogicalBinaryOperator(delegate, value, LogicalBinaryOperator.AND) + ExpressionHelper.newLogicalBinaryOperator(delegate as ExpressionNode, value, LogicalBinaryOperator.AND) } Boolean.metaClass.or2 = { Boolean value -> delegate || value } ExpressionNode.metaClass.or2 = { Boolean value -> - ExpressionHelper.newLogicalBinaryOperator(delegate, ExpressionHelper.newBooleanLiteral(value), LogicalBinaryOperator.OR) + ExpressionHelper.newLogicalBinaryOperator(delegate as ExpressionNode, ExpressionHelper.newBooleanLiteral(value), LogicalBinaryOperator.OR) } Boolean.metaClass.or2 = { ExpressionNode value -> ExpressionHelper.newLogicalBinaryOperator(ExpressionHelper.newBooleanLiteral(delegate), value, LogicalBinaryOperator.OR) } ExpressionNode.metaClass.or2 = { ExpressionNode value -> - ExpressionHelper.newLogicalBinaryOperator(delegate, value, LogicalBinaryOperator.OR) + ExpressionHelper.newLogicalBinaryOperator(delegate as ExpressionNode, value, LogicalBinaryOperator.OR) } Boolean.metaClass.not = { !delegate } ExpressionNode.metaClass.not = { - ExpressionHelper.newLogicalNotOperator(delegate) + ExpressionHelper.newLogicalNotOperator(delegate as ExpressionNode) } } @@ -199,7 +199,7 @@ class ExpressionDslLoader extends DslLoader { } else if (value instanceof String) { ExpressionHelper.newStringLiteral(value) } else { - throw new IllegalStateException(value?.getClass()) + throw new IllegalStateException(value?.getClass() as String) } } diff --git a/dsl/src/main/java/com/powsybl/dsl/PowsyblDslAstTransformation.java b/dsl/src/main/java/com/powsybl/dsl/PowsyblDslAstTransformation.java index 036f3e3af86..0e023c0db2d 100644 --- a/dsl/src/main/java/com/powsybl/dsl/PowsyblDslAstTransformation.java +++ b/dsl/src/main/java/com/powsybl/dsl/PowsyblDslAstTransformation.java @@ -39,25 +39,24 @@ public Expression transform(Expression exp) { if (exp instanceof BinaryExpression binExpr) { String op = binExpr.getOperation().getText(); switch (op) { - case ">": - case ">=": - case "<": - case "<=": - case "==": - case "!=": + case ">", ">=", "<", "<=", "==", "!=" -> { return new MethodCallExpression(transform(binExpr.getLeftExpression()), - "compareTo2", - new ArgumentListExpression(transform(binExpr.getRightExpression()), new ConstantExpression(op))); - case "&&": + "compareTo2", + new ArgumentListExpression(transform(binExpr.getRightExpression()), new ConstantExpression(op))); + } + case "&&" -> { return new MethodCallExpression(transform(binExpr.getLeftExpression()), - "and2", - new ArgumentListExpression(transform(binExpr.getRightExpression()))); - case "||": + "and2", + new ArgumentListExpression(transform(binExpr.getRightExpression()))); + } + case "||" -> { return new MethodCallExpression(transform(binExpr.getLeftExpression()), - "or2", - new ArgumentListExpression(transform(binExpr.getRightExpression()))); - default: - break; + "or2", + new ArgumentListExpression(transform(binExpr.getRightExpression()))); + } + default -> { + // Do nothing + } } } else if (exp instanceof NotExpression notExpression) { return new MethodCallExpression(transform(notExpression.getExpression()), diff --git a/dynamic-simulation/dynamic-simulation-api/src/main/java/com/powsybl/dynamicsimulation/json/DynamicSimulationParametersDeserializer.java b/dynamic-simulation/dynamic-simulation-api/src/main/java/com/powsybl/dynamicsimulation/json/DynamicSimulationParametersDeserializer.java index d3624309c03..dd5783bad38 100644 --- a/dynamic-simulation/dynamic-simulation-api/src/main/java/com/powsybl/dynamicsimulation/json/DynamicSimulationParametersDeserializer.java +++ b/dynamic-simulation/dynamic-simulation-api/src/main/java/com/powsybl/dynamicsimulation/json/DynamicSimulationParametersDeserializer.java @@ -39,7 +39,7 @@ public DynamicSimulationParameters deserialize(JsonParser parser, Deserializatio List> extensions = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version": parser.nextToken(); @@ -61,7 +61,7 @@ public DynamicSimulationParameters deserialize(JsonParser parser, Deserializatio break; default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } diff --git a/dynamic-simulation/dynamic-simulation-api/src/main/java/com/powsybl/dynamicsimulation/json/DynamicSimulationResultDeserializer.java b/dynamic-simulation/dynamic-simulation-api/src/main/java/com/powsybl/dynamicsimulation/json/DynamicSimulationResultDeserializer.java index 415682d9348..99fc083778e 100644 --- a/dynamic-simulation/dynamic-simulation-api/src/main/java/com/powsybl/dynamicsimulation/json/DynamicSimulationResultDeserializer.java +++ b/dynamic-simulation/dynamic-simulation-api/src/main/java/com/powsybl/dynamicsimulation/json/DynamicSimulationResultDeserializer.java @@ -46,33 +46,25 @@ public DynamicSimulationResult deserialize(JsonParser parser, DeserializationCon List timeLine = new ArrayList<>(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { - case "version": - parser.nextToken(); // skip - break; - - case "status": + switch (parser.currentName()) { + case "version" -> parser.nextToken(); // skip + case "status" -> { parser.nextToken(); status = parser.readValueAs(DynamicSimulationResult.Status.class); - break; - - case "error": + } + case "error" -> { parser.nextToken(); error = parser.readValueAs(String.class); - break; - - case "curves": + } + case "curves" -> { parser.nextToken(); deserializeCurves(parser, curves); - break; - - case "timeLine": + } + case "timeLine" -> { parser.nextToken(); deserializeTimeline(parser, timeLine); - break; - - default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + } + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } @@ -100,18 +92,11 @@ private TimelineEvent deserializeTimelineEvent(JsonParser parser) throws IOExcep String modelName = null; String message = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { - case "time": - time = parser.getValueAsDouble(); - break; - case "modelName": - modelName = parser.getValueAsString(); - break; - case "message": - message = parser.getValueAsString(); - break; - default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + switch (parser.currentName()) { + case "time" -> time = parser.getValueAsDouble(); + case "modelName" -> modelName = parser.getValueAsString(); + case "message" -> message = parser.getValueAsString(); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } return new TimelineEvent(time, modelName, message); diff --git a/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyCurvesSupplierTest.java b/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyCurvesSupplierTest.java index 422457ce8af..5d7c14a6582 100644 --- a/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyCurvesSupplierTest.java +++ b/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyCurvesSupplierTest.java @@ -22,9 +22,9 @@ import java.nio.file.FileSystem; import java.nio.file.Files; import java.util.List; +import java.util.Objects; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; /** * @author Mathieu Bague {@literal } @@ -37,7 +37,7 @@ class GroovyCurvesSupplierTest { void setup() throws IOException { fileSystem = Jimfs.newFileSystem(Configuration.unix()); - Files.copy(getClass().getResourceAsStream("/curves.groovy"), fileSystem.getPath("/curves.groovy")); + Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/curves.groovy")), fileSystem.getPath("/curves.groovy")); } @AfterEach @@ -51,7 +51,7 @@ void test() { List extensions = GroovyExtension.find(CurveGroovyExtension.class, "dummy"); assertEquals(1, extensions.size()); - assertTrue(extensions.get(0) instanceof DummyCurveGroovyExtension); + assertInstanceOf(DummyCurveGroovyExtension.class, extensions.get(0)); CurvesSupplier supplier = new GroovyCurvesSupplier(fileSystem.getPath("/curves.groovy"), extensions); @@ -64,7 +64,7 @@ void testWithInputStream() { List extensions = GroovyExtension.find(CurveGroovyExtension.class, "dummy"); assertEquals(1, extensions.size()); - assertTrue(extensions.get(0) instanceof DummyCurveGroovyExtension); + assertInstanceOf(DummyCurveGroovyExtension.class, extensions.get(0)); CurvesSupplier supplier = new GroovyCurvesSupplier(getClass().getResourceAsStream("/curves.groovy"), extensions); @@ -75,12 +75,12 @@ private static void testCurveSupplier(Network network, CurvesSupplier supplier) List curves = supplier.get(network); assertEquals(2, curves.size()); - assertTrue(curves.get(0) instanceof DummyCurve); + assertInstanceOf(DummyCurve.class, curves.get(0)); DummyCurve curve1 = (DummyCurve) curves.get(0); assertEquals("id", curve1.getId()); assertEquals("variable", curve1.getVariable()); - assertTrue(curves.get(1) instanceof DummyCurve); + assertInstanceOf(DummyCurve.class, curves.get(1)); DummyCurve curve2 = (DummyCurve) curves.get(1); assertEquals("LOAD", curve2.getId()); assertEquals("p0", curve2.getVariable()); diff --git a/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyDynamicModelSupplierTest.java b/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyDynamicModelSupplierTest.java index 29b0a57a54e..b0d14e87465 100644 --- a/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyDynamicModelSupplierTest.java +++ b/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyDynamicModelSupplierTest.java @@ -12,6 +12,7 @@ import java.nio.file.FileSystem; import java.nio.file.Files; import java.util.List; +import java.util.Objects; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -37,7 +38,7 @@ class GroovyDynamicModelSupplierTest { void setup() throws IOException { fileSystem = Jimfs.newFileSystem(Configuration.unix()); - Files.copy(getClass().getResourceAsStream("/dynamicModels.groovy"), fileSystem.getPath("/dynamicModels.groovy")); + Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/dynamicModels.groovy")), fileSystem.getPath("/dynamicModels.groovy")); } @AfterEach @@ -73,12 +74,12 @@ void testDynamicModels(DynamicModelsSupplier supplier, Network network) { List dynamicModels = supplier.get(network); assertEquals(2, dynamicModels.size()); - assertTrue(dynamicModels.get(0) instanceof DummyDynamicModel); + assertInstanceOf(DummyDynamicModel.class, dynamicModels.get(0)); DummyDynamicModel dynamicModel1 = (DummyDynamicModel) dynamicModels.get(0); assertEquals("id", dynamicModel1.getId()); assertEquals("parameterSetId", dynamicModel1.getParameterSetId()); - assertTrue(dynamicModels.get(1) instanceof DummyDynamicModel); + assertInstanceOf(DummyDynamicModel.class, dynamicModels.get(1)); DummyDynamicModel dynamicModel2 = (DummyDynamicModel) dynamicModels.get(1); assertEquals("LOAD", dynamicModel2.getId()); assertEquals("LOAD", dynamicModel2.getParameterSetId()); diff --git a/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyEventModelSupplierTest.java b/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyEventModelSupplierTest.java index d01b711a84c..d8af27f958c 100644 --- a/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyEventModelSupplierTest.java +++ b/dynamic-simulation/dynamic-simulation-dsl/src/test/java/com/powsybl/dynamicsimulation/groovy/GroovyEventModelSupplierTest.java @@ -12,6 +12,7 @@ import java.nio.file.FileSystem; import java.nio.file.Files; import java.util.List; +import java.util.Objects; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -37,7 +38,7 @@ class GroovyEventModelSupplierTest { void setup() throws IOException { fileSystem = Jimfs.newFileSystem(Configuration.unix()); - Files.copy(getClass().getResourceAsStream("/eventModels.groovy"), fileSystem.getPath("/eventModels.groovy")); + Files.copy(Objects.requireNonNull(getClass().getResourceAsStream("/eventModels.groovy")), fileSystem.getPath("/eventModels.groovy")); } @AfterEach @@ -75,12 +76,12 @@ private static void testEventModelsSupplier(Network network, EventModelsSupplier List eventModels = supplier.get(network); assertEquals(2, eventModels.size()); - assertTrue(eventModels.get(0) instanceof DummyEventModel); + assertInstanceOf(DummyEventModel.class, eventModels.get(0)); DummyEventModel eventModel1 = (DummyEventModel) eventModels.get(0); assertEquals("NHV1_NHV2_1", eventModel1.getId()); assertEquals(50, eventModel1.getStartTime(), 0); - assertTrue(eventModels.get(1) instanceof DummyEventModel); + assertInstanceOf(DummyEventModel.class, eventModels.get(1)); DummyEventModel eventModel2 = (DummyEventModel) eventModels.get(1); assertEquals("NHV1_NHV2_2", eventModel2.getId()); assertEquals(50, eventModel2.getStartTime(), 0); diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java index 99ba254f50e..09d6a955a5c 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java @@ -373,95 +373,65 @@ default ApparentPowerLimits getNullableApparentPowerLimits2() { ApparentPowerLimitsAdder newApparentPowerLimits2(); default Optional getCurrentLimits(TwoSides side) { - switch (side) { - case ONE: - return getCurrentLimits1(); - case TWO: - return getCurrentLimits2(); - default: - throw new UnsupportedOperationException(String.format("Side %s not supported", side.name())); - } + return switch (side) { + case ONE -> getCurrentLimits1(); + case TWO -> getCurrentLimits2(); + }; } default Optional getActivePowerLimits(TwoSides side) { - switch (side) { - case ONE: - return getActivePowerLimits1(); - case TWO: - return getActivePowerLimits2(); - default: - throw new UnsupportedOperationException(String.format("Side %s not supported", side.name())); - } + return switch (side) { + case ONE -> getActivePowerLimits1(); + case TWO -> getActivePowerLimits2(); + }; } default Optional getApparentPowerLimits(TwoSides side) { - switch (side) { - case ONE: - return getApparentPowerLimits1(); - case TWO: - return getApparentPowerLimits2(); - default: - throw new UnsupportedOperationException(String.format("Side %s not supported", side.name())); - } + return switch (side) { + case ONE -> getApparentPowerLimits1(); + case TWO -> getApparentPowerLimits2(); + }; } default Optional getLimits(LimitType type, TwoSides side) { - switch (type) { - case CURRENT: - return getCurrentLimits(side); - case ACTIVE_POWER: - return getActivePowerLimits(side); - case APPARENT_POWER: - return getApparentPowerLimits(side); - default: + return switch (type) { + case CURRENT -> getCurrentLimits(side); + case ACTIVE_POWER -> getActivePowerLimits(side); + case APPARENT_POWER -> getApparentPowerLimits(side); + default -> throw new UnsupportedOperationException(String.format("Getting %s limits is not supported.", type.name())); - } + }; } default CurrentLimits getNullableCurrentLimits(TwoSides side) { - switch (side) { - case ONE: - return getNullableCurrentLimits1(); - case TWO: - return getNullableCurrentLimits2(); - default: - throw new UnsupportedOperationException(String.format("Side %s not supported", side.name())); - } + return switch (side) { + case ONE -> getNullableCurrentLimits1(); + case TWO -> getNullableCurrentLimits2(); + }; } default ActivePowerLimits getNullableActivePowerLimits(TwoSides side) { - switch (side) { - case ONE: - return getNullableActivePowerLimits1(); - case TWO: - return getNullableActivePowerLimits2(); - default: - throw new UnsupportedOperationException(String.format("Side %s not supported", side.name())); - } + return switch (side) { + case ONE -> getNullableActivePowerLimits1(); + case TWO -> getNullableActivePowerLimits2(); + }; } default ApparentPowerLimits getNullableApparentPowerLimits(TwoSides side) { - switch (side) { - case ONE: - return getNullableApparentPowerLimits1(); - case TWO: - return getNullableApparentPowerLimits2(); - default: - throw new UnsupportedOperationException(String.format("Side %s not supported", side.name())); - } + return switch (side) { + case ONE -> getNullableApparentPowerLimits1(); + case TWO -> getNullableApparentPowerLimits2(); + }; } default LoadingLimits getNullableLimits(LimitType type, TwoSides side) { - switch (type) { - case CURRENT: - return getNullableCurrentLimits(side); - case ACTIVE_POWER: - return getNullableActivePowerLimits(side); - case APPARENT_POWER: - return getNullableApparentPowerLimits(side); - default: + return switch (type) { + case CURRENT -> getNullableCurrentLimits(side); + case ACTIVE_POWER -> getNullableActivePowerLimits(side); + case APPARENT_POWER -> getNullableApparentPowerLimits(side); + default -> throw new UnsupportedOperationException(String.format("Getting %s limits is not supported.", type.name())); - } + }; } /** diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java index 3fbda1666f0..a8a074eb6f7 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ThreeWindingsTransformer.java @@ -7,8 +7,6 @@ */ package com.powsybl.iidm.network; -import com.powsybl.commons.PowsyblException; - import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -39,7 +37,7 @@ * two or more regulating controls are enabled. * *

- * Characteristics + * Characteristics *

* * @@ -118,7 +116,7 @@ public interface ThreeWindingsTransformer extends Connectable - * Characteristics + * Characteristics *

* *
@@ -329,16 +327,11 @@ default Substation getNullableSubstation() { Leg getLeg3(); default Leg getLeg(ThreeSides side) { - switch (side) { - case ONE: - return getLeg1(); - case TWO: - return getLeg2(); - case THREE: - return getLeg3(); - default: - throw new PowsyblException("Can't get leg from side. Unsupported Side \"" + side + "\""); - } + return switch (side) { + case ONE -> getLeg1(); + case TWO -> getLeg2(); + case THREE -> getLeg3(); + }; } /** diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java index a3824c1283d..c578d297ab3 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/ValidationUtil.java @@ -387,26 +387,21 @@ private static ValidationLevel checkSvcRegulator(Validable validable, double vol return ValidationLevel.EQUIPMENT; } switch (regulationMode) { - case VOLTAGE: + case VOLTAGE -> { if (Double.isNaN(voltageSetpoint)) { throwExceptionOrLogErrorForInvalidValue(validable, voltageSetpoint, VOLTAGE_SETPOINT, throwException, reportNode); return ValidationLevel.EQUIPMENT; } - break; - - case REACTIVE_POWER: + } + case REACTIVE_POWER -> { if (Double.isNaN(reactivePowerSetpoint)) { throwExceptionOrLogErrorForInvalidValue(validable, reactivePowerSetpoint, "reactive power setpoint", throwException, reportNode); return ValidationLevel.EQUIPMENT; } - break; - - case OFF: + } + case OFF -> { // nothing to check - break; - - default: - throw new IllegalStateException(); + } } return ValidationLevel.STEADY_STATE_HYPOTHESIS; } diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/identifiers/json/IdentifierDeserializer.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/identifiers/json/IdentifierDeserializer.java index 9cb93645630..4ceb6fddeb3 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/identifiers/json/IdentifierDeserializer.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/identifiers/json/IdentifierDeserializer.java @@ -42,7 +42,7 @@ public NetworkElementIdentifier deserialize(JsonParser parser, DeserializationCo List networkElementIdentifierList = Collections.emptyList(); char order = 0; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "type" -> type = NetworkElementIdentifier.IdentifierType.valueOf(parser.nextTextValue()); case "identifier" -> identifier = parser.nextTextValue(); case CONTINGENCY_ID -> { @@ -62,7 +62,7 @@ public NetworkElementIdentifier deserialize(JsonParser parser, DeserializationCo } order = orderStr.charAt(0); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } if (type == null) { diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/TwtData.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/TwtData.java index 70f25230ff5..36d3d3dcbaa 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/TwtData.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/TwtData.java @@ -25,8 +25,6 @@ */ public class TwtData { - private static final String UNEXPECTED_SIDE = "Unexpected side"; - private final String id; private final double p1; @@ -477,72 +475,47 @@ public String getId() { public double getComputedP(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return computedP1; - case TWO: - return computedP2; - case THREE: - return computedP3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> computedP1; + case TWO -> computedP2; + case THREE -> computedP3; + }; } public double getComputedQ(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return computedQ1; - case TWO: - return computedQ2; - case THREE: - return computedQ3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> computedQ1; + case TWO -> computedQ2; + case THREE -> computedQ3; + }; } public double getP(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return p1; - case TWO: - return p2; - case THREE: - return p3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> p1; + case TWO -> p2; + case THREE -> p3; + }; } public double getQ(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return q1; - case TWO: - return q2; - case THREE: - return q3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> q1; + case TWO -> q2; + case THREE -> q3; + }; } public double getU(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return u1; - case TWO: - return u2; - case THREE: - return u3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> u1; + case TWO -> u2; + case THREE -> u3; + }; } public double getStarU() { @@ -551,16 +524,11 @@ public double getStarU() { public double getTheta(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return theta1; - case TWO: - return theta2; - case THREE: - return theta3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> theta1; + case TWO -> theta2; + case THREE -> theta3; + }; } public double getStarTheta() { @@ -569,128 +537,83 @@ public double getStarTheta() { public double getR(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return r1; - case TWO: - return r2; - case THREE: - return r3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> r1; + case TWO -> r2; + case THREE -> r3; + }; } public double getX(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return x1; - case TWO: - return x2; - case THREE: - return x3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> x1; + case TWO -> x2; + case THREE -> x3; + }; } public double getG1(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return g11; - case TWO: - return g21; - case THREE: - return g31; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> g11; + case TWO -> g21; + case THREE -> g31; + }; } public double getB1(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return b11; - case TWO: - return b21; - case THREE: - return b31; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> b11; + case TWO -> b21; + case THREE -> b31; + }; } public double getG2(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return g12; - case TWO: - return g22; - case THREE: - return g32; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> g12; + case TWO -> g22; + case THREE -> g32; + }; } public double getB2(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return b12; - case TWO: - return b22; - case THREE: - return b32; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> b12; + case TWO -> b22; + case THREE -> b32; + }; } public double getRatedU(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return ratedU1; - case TWO: - return ratedU2; - case THREE: - return ratedU3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> ratedU1; + case TWO -> ratedU2; + case THREE -> ratedU3; + }; } public boolean isConnected(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return connected1; - case TWO: - return connected2; - case THREE: - return connected3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> connected1; + case TWO -> connected2; + case THREE -> connected3; + }; } public boolean isMainComponent(ThreeSides side) { Objects.requireNonNull(side); - switch (side) { - case ONE: - return mainComponent1; - case TWO: - return mainComponent2; - case THREE: - return mainComponent3; - default: - throw new IllegalStateException(UNEXPECTED_SIDE + ": " + side); - } + return switch (side) { + case ONE -> mainComponent1; + case TWO -> mainComponent2; + case THREE -> mainComponent3; + }; } public int getPhaseAngleClock2() { diff --git a/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/ExportersTest.java b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/ExportersTest.java index 8a3ad7139fe..7364d91806f 100644 --- a/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/ExportersTest.java +++ b/iidm/iidm-api/src/test/java/com/powsybl/iidm/network/ExportersTest.java @@ -124,7 +124,7 @@ void exportWithReportNode() throws Exception { testExporter.export(null, null, dataSource, rootReportNode); Optional reportNode = rootReportNode.getChildren().stream().findFirst(); assertTrue(reportNode.isPresent()); - assertTrue(reportNode.get() instanceof ReportNode); + assertInstanceOf(ReportNode.class, reportNode.get()); StringWriter sw = new StringWriter(); rootReportNode.print(sw); diff --git a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/json/CriterionDeserializer.java b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/json/CriterionDeserializer.java index 50f538bbf59..eb304c255b1 100644 --- a/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/json/CriterionDeserializer.java +++ b/iidm/iidm-criteria/src/main/java/com/powsybl/iidm/criteria/json/CriterionDeserializer.java @@ -45,7 +45,7 @@ public Criterion deserialize(JsonParser parser, DeserializationContext deseriali PropertyCriterion.EquipmentToCheck equipmentToCheck = null; PropertyCriterion.SideToCheck sideToCheck = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "type" -> type = CriterionType.valueOf(parser.nextTextValue()); case "voltageInterval" -> { parser.nextToken(); @@ -88,7 +88,7 @@ public Criterion deserialize(JsonParser parser, DeserializationContext deseriali case "equipmentToCheck" -> equipmentToCheck = PropertyCriterion.EquipmentToCheck.valueOf(parser.nextTextValue()); case "sideToCheck" -> sideToCheck = PropertyCriterion.SideToCheck.valueOf(parser.nextTextValue()); - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } if (type == null) { diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractBus.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractBus.java index 4424ad26e5e..20fe28fff9f 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractBus.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractBus.java @@ -76,26 +76,16 @@ public double getP() { for (TerminalExt terminal : getConnectedTerminals()) { AbstractConnectable connectable = terminal.getConnectable(); switch (connectable.getType()) { - case BUSBAR_SECTION: - case SHUNT_COMPENSATOR: - case STATIC_VAR_COMPENSATOR: - case LINE: - case TWO_WINDINGS_TRANSFORMER: - case THREE_WINDINGS_TRANSFORMER: - case DANGLING_LINE: - case GROUND: + case BUSBAR_SECTION, SHUNT_COMPENSATOR, STATIC_VAR_COMPENSATOR, LINE, TWO_WINDINGS_TRANSFORMER, + THREE_WINDINGS_TRANSFORMER, DANGLING_LINE, GROUND -> { // skip - break; - case GENERATOR: - case BATTERY: - case LOAD: - case HVDC_CONVERTER_STATION: + } + case GENERATOR, BATTERY, LOAD, HVDC_CONVERTER_STATION -> { if (!Double.isNaN(terminal.getP())) { p += terminal.getP(); } - break; - default: - throw new IllegalStateException(); + } + default -> throw new IllegalStateException(); } } return p; @@ -110,26 +100,15 @@ public double getQ() { for (TerminalExt terminal : getConnectedTerminals()) { AbstractConnectable connectable = terminal.getConnectable(); switch (connectable.getType()) { - case BUSBAR_SECTION: - case LINE: - case TWO_WINDINGS_TRANSFORMER: - case THREE_WINDINGS_TRANSFORMER: - case DANGLING_LINE: - case GROUND: + case BUSBAR_SECTION, LINE, TWO_WINDINGS_TRANSFORMER, THREE_WINDINGS_TRANSFORMER, DANGLING_LINE, GROUND -> { // skip - break; - case GENERATOR: - case BATTERY: - case LOAD: - case SHUNT_COMPENSATOR: - case STATIC_VAR_COMPENSATOR: - case HVDC_CONVERTER_STATION: + } + case GENERATOR, BATTERY, LOAD, SHUNT_COMPENSATOR, STATIC_VAR_COMPENSATOR, HVDC_CONVERTER_STATION -> { if (!Double.isNaN(terminal.getQ())) { q += terminal.getQ(); } - break; - default: - throw new IllegalStateException(); + } + default -> throw new IllegalStateException(); } } return q; @@ -282,37 +261,23 @@ static void visitEquipments(Iterable terminals, Topology for (T terminal : terminals) { AbstractConnectable connectable = ((TerminalExt) terminal).getConnectable(); switch (connectable.getType()) { - case BUSBAR_SECTION: - visitor.visitBusbarSection((BusbarSectionImpl) connectable); - break; - - case LINE: + case BUSBAR_SECTION -> visitor.visitBusbarSection((BusbarSectionImpl) connectable); + case LINE -> { Line line = (Line) connectable; visitor.visitLine(line, line.getTerminal1() == terminal ? TwoSides.ONE - : TwoSides.TWO); - break; - - case GENERATOR: - visitor.visitGenerator((GeneratorImpl) connectable); - break; - - case BATTERY: - visitor.visitBattery((BatteryImpl) connectable); - break; - - case SHUNT_COMPENSATOR: - visitor.visitShuntCompensator((ShuntCompensatorImpl) connectable); - break; - - case TWO_WINDINGS_TRANSFORMER: + : TwoSides.TWO); + } + case GENERATOR -> visitor.visitGenerator((GeneratorImpl) connectable); + case BATTERY -> visitor.visitBattery((BatteryImpl) connectable); + case SHUNT_COMPENSATOR -> visitor.visitShuntCompensator((ShuntCompensatorImpl) connectable); + case TWO_WINDINGS_TRANSFORMER -> { TwoWindingsTransformer twt = (TwoWindingsTransformer) connectable; visitor.visitTwoWindingsTransformer(twt, - twt.getTerminal1() == terminal + twt.getTerminal1() == terminal ? TwoSides.ONE : TwoSides.TWO); - break; - - case THREE_WINDINGS_TRANSFORMER: + } + case THREE_WINDINGS_TRANSFORMER -> { ThreeWindingsTransformer thwt = (ThreeWindingsTransformer) connectable; ThreeSides side; if (thwt.getLeg1().getTerminal() == terminal) { @@ -323,30 +288,13 @@ static void visitEquipments(Iterable terminals, Topology side = ThreeSides.THREE; } visitor.visitThreeWindingsTransformer(thwt, side); - break; - - case LOAD: - visitor.visitLoad((LoadImpl) connectable); - break; - - case DANGLING_LINE: - visitor.visitDanglingLine((DanglingLineImpl) connectable); - break; - - case STATIC_VAR_COMPENSATOR: - visitor.visitStaticVarCompensator((StaticVarCompensator) connectable); - break; - - case HVDC_CONVERTER_STATION: - visitor.visitHvdcConverterStation((HvdcConverterStation) connectable); - break; - - case GROUND: - visitor.visitGround((GroundImpl) connectable); - break; - - default: - throw new IllegalStateException(); + } + case LOAD -> visitor.visitLoad((LoadImpl) connectable); + case DANGLING_LINE -> visitor.visitDanglingLine((DanglingLineImpl) connectable); + case STATIC_VAR_COMPENSATOR -> visitor.visitStaticVarCompensator((StaticVarCompensator) connectable); + case HVDC_CONVERTER_STATION -> visitor.visitHvdcConverterStation((HvdcConverterStation) connectable); + case GROUND -> visitor.visitGround((GroundImpl) connectable); + default -> throw new IllegalStateException(); } } } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerVoltageLevel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerVoltageLevel.java index 10f211937a4..cc565709153 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerVoltageLevel.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/BusBreakerVoltageLevel.java @@ -202,24 +202,12 @@ protected boolean isBusValid(Set busSet) { for (TerminalExt terminal : FluentIterable.from(busSet).transformAndConcat(ConfiguredBus::getConnectedTerminals)) { AbstractConnectable connectable = terminal.getConnectable(); switch (connectable.getType()) { - case LINE: - case TWO_WINDINGS_TRANSFORMER: - case THREE_WINDINGS_TRANSFORMER: - case HVDC_CONVERTER_STATION: - case DANGLING_LINE: - case LOAD: - case GENERATOR: - case BATTERY: - case SHUNT_COMPENSATOR: - case STATIC_VAR_COMPENSATOR: - feederCount++; - break; - case GROUND: - break; - - case BUSBAR_SECTION: // must not happend in a bus/breaker topology - default: - throw new IllegalStateException(); + case LINE, TWO_WINDINGS_TRANSFORMER, THREE_WINDINGS_TRANSFORMER, HVDC_CONVERTER_STATION, + DANGLING_LINE, LOAD, GENERATOR, BATTERY, SHUNT_COMPENSATOR, STATIC_VAR_COMPENSATOR -> feederCount++; + case GROUND -> { + // Do nothing + } + default -> throw new IllegalStateException(); } } return Networks.isBusValid(feederCount); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java index c1c00967747..d96ee92f8ae 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkImpl.java @@ -1146,7 +1146,7 @@ private void replaceDanglingLineByTieLine(List lines) { } } - class DanglingLinePair { + static class DanglingLinePair { String id; String name; String dl1Id; diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkIndex.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkIndex.java index fcbfcecaec1..1f5012ac875 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkIndex.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NetworkIndex.java @@ -45,11 +45,7 @@ void checkAndAdd(Identifiable obj) { objectsById.put(obj.getId(), obj); obj.getAliases().forEach(alias -> addAlias(obj, alias)); - Set> all = objectsByClass.get(obj.getClass()); - if (all == null) { - all = new LinkedHashSet<>(); - objectsByClass.put(obj.getClass(), all); - } + Set> all = objectsByClass.computeIfAbsent(obj.getClass(), k -> new LinkedHashSet<>()); all.add(obj); } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java index eab64debcd9..f8483387a6d 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/NodeBreakerVoltageLevel.java @@ -965,7 +965,7 @@ public CalculatedBus getBus(String id) { @Override public Bus getMergedBus(String busBarId) { - NodeTerminal nt = (NodeTerminal) nodeBreakerView.getBusbarSection(busBarId).getTerminal(); + NodeTerminal nt = (NodeTerminal) Objects.requireNonNull(nodeBreakerView.getBusbarSection(busBarId)).getTerminal(); int node = nt.getNode(); return variants.get().calculatedBusTopology.getBus(node); } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerAdderImpl.java index 10c331bd410..c426ceaa21a 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerAdderImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerAdderImpl.java @@ -165,17 +165,10 @@ protected void checkConnectableBus() { public ThreeWindingsTransformerAdderImpl add() { checkParams(); switch (side) { - case ONE: - legAdder1 = this; - break; - case TWO: - legAdder2 = this; - break; - case THREE: - legAdder3 = this; - break; - default: - throw new IllegalStateException("Unexpected side: " + side); + case ONE -> legAdder1 = this; + case TWO -> legAdder2 = this; + case THREE -> legAdder3 = this; + default -> throw new IllegalStateException("Unexpected side: " + side); } return ThreeWindingsTransformerAdderImpl.this; } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java index ace549495ba..454691ab6f0 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ThreeWindingsTransformerImpl.java @@ -375,19 +375,11 @@ public double getRatedU0() { @Override public Terminal getTerminal(ThreeSides side) { - switch (side) { - case ONE: - return getLeg1().getTerminal(); - - case TWO: - return getLeg2().getTerminal(); - - case THREE: - return getLeg3().getTerminal(); - - default: - throw new IllegalStateException(); - } + return switch (side) { + case ONE -> getLeg1().getTerminal(); + case TWO -> getLeg2().getTerminal(); + case THREE -> getLeg3().getTerminal(); + }; } @Override diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelAdderImpl.java index cdcc35f89e4..3d8130a7b63 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelAdderImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/VoltageLevelAdderImpl.java @@ -94,17 +94,12 @@ public VoltageLevel add() { ValidationUtil.checkVoltageLimits(this, lowVoltageLimit, highVoltageLimit); ValidationUtil.checkTopologyKind(this, topologyKind); - VoltageLevelExt voltageLevel; - switch (topologyKind) { - case NODE_BREAKER: - voltageLevel = new NodeBreakerVoltageLevel(id, getName(), isFictitious(), substation, networkRef, subnetworkRef, nominalV, lowVoltageLimit, highVoltageLimit); - break; - case BUS_BREAKER: - voltageLevel = new BusBreakerVoltageLevel(id, getName(), isFictitious(), substation, networkRef, subnetworkRef, nominalV, lowVoltageLimit, highVoltageLimit); - break; - default: - throw new IllegalStateException(); - } + VoltageLevelExt voltageLevel = switch (topologyKind) { + case NODE_BREAKER -> + new NodeBreakerVoltageLevel(id, getName(), isFictitious(), substation, networkRef, subnetworkRef, nominalV, lowVoltageLimit, highVoltageLimit); + case BUS_BREAKER -> + new BusBreakerVoltageLevel(id, getName(), isFictitious(), substation, networkRef, subnetworkRef, nominalV, lowVoltageLimit, highVoltageLimit); + }; getNetwork().getIndex().checkAndAdd(voltageLevel); Optional.ofNullable(substation).ifPresent(s -> s.addVoltageLevel(voltageLevel)); getNetwork().getListeners().notifyCreation(voltageLevel); diff --git a/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/BranchTest.java b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/BranchTest.java new file mode 100644 index 00000000000..3f238cbe07a --- /dev/null +++ b/iidm/iidm-impl/src/test/java/com/powsybl/iidm/network/impl/BranchTest.java @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.network.impl; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * @author Etienne Lesot {@literal } + */ +class BranchTest { + + static Network network1; + static Network network2; + static Branch branch1; + static Branch branch2; + + @BeforeAll + public static void setUp() { + network1 = EurostagTutorialExample1Factory.createWithFixedLimits(); + branch1 = network1.getBranch("NHV1_NHV2_2"); + network2 = EurostagTutorialExample1Factory.createWithFixedCurrentLimits(); + branch2 = network2.getBranch("NHV1_NHV2_2"); + } + + @Test + void testActivePowerLimits1Branches() { + // power limits + + Assertions.assertNotNull(branch1.getNullableActivePowerLimits1()); + Assertions.assertEquals(1100, branch1.getNullableActivePowerLimits1().getPermanentLimit()); + Assertions.assertEquals(LimitType.ACTIVE_POWER, branch1.getNullableActivePowerLimits1().getLimitType()); + Assertions.assertEquals(2, branch1.getNullableActivePowerLimits1().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch1.getActivePowerLimits(TwoSides.ONE)); + Assertions.assertTrue(branch1.getActivePowerLimits(TwoSides.ONE).isPresent()); + Assertions.assertEquals(1100, branch1.getActivePowerLimits(TwoSides.ONE).get().getPermanentLimit()); + Assertions.assertEquals(LimitType.ACTIVE_POWER, branch1.getActivePowerLimits(TwoSides.ONE).get().getLimitType()); + Assertions.assertEquals(2, branch1.getActivePowerLimits(TwoSides.ONE).get().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch1.getNullableLimits(LimitType.ACTIVE_POWER, TwoSides.ONE)); + Assertions.assertEquals(1100, branch1.getNullableLimits(LimitType.ACTIVE_POWER, TwoSides.ONE).getPermanentLimit()); + Assertions.assertEquals(LimitType.ACTIVE_POWER, branch1.getNullableLimits(LimitType.ACTIVE_POWER, TwoSides.ONE).getLimitType()); + Assertions.assertEquals(2, branch1.getNullableLimits(LimitType.ACTIVE_POWER, TwoSides.ONE).getTemporaryLimits().size()); + } + + @Test + void testActivePowerLimits2Branches() { + // power limits + + Assertions.assertNotNull(branch1.getNullableActivePowerLimits2()); + Assertions.assertEquals(500, branch1.getNullableActivePowerLimits2().getPermanentLimit()); + Assertions.assertEquals(LimitType.ACTIVE_POWER, branch1.getNullableActivePowerLimits2().getLimitType()); + Assertions.assertEquals(0, branch1.getNullableActivePowerLimits2().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch1.getActivePowerLimits(TwoSides.TWO)); + Assertions.assertTrue(branch1.getActivePowerLimits(TwoSides.TWO).isPresent()); + Assertions.assertEquals(500, branch1.getActivePowerLimits(TwoSides.TWO).get().getPermanentLimit()); + Assertions.assertEquals(LimitType.ACTIVE_POWER, branch1.getActivePowerLimits(TwoSides.TWO).get().getLimitType()); + Assertions.assertEquals(0, branch1.getActivePowerLimits(TwoSides.TWO).get().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch1.getNullableLimits(LimitType.ACTIVE_POWER, TwoSides.TWO)); + Assertions.assertEquals(500, branch1.getNullableLimits(LimitType.ACTIVE_POWER, TwoSides.TWO).getPermanentLimit()); + Assertions.assertEquals(LimitType.ACTIVE_POWER, branch1.getNullableLimits(LimitType.ACTIVE_POWER, TwoSides.TWO).getLimitType()); + Assertions.assertEquals(0, branch1.getNullableLimits(LimitType.ACTIVE_POWER, TwoSides.TWO).getTemporaryLimits().size()); + } + + @Test + void testApparentPowerLimits1Branches() { + // power limits + + Assertions.assertNotNull(branch1.getNullableApparentPowerLimits1()); + Assertions.assertEquals(1100, branch1.getNullableApparentPowerLimits1().getPermanentLimit()); + Assertions.assertEquals(LimitType.APPARENT_POWER, branch1.getNullableApparentPowerLimits1().getLimitType()); + Assertions.assertEquals(2, branch1.getNullableApparentPowerLimits1().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch1.getApparentPowerLimits(TwoSides.ONE)); + Assertions.assertTrue(branch1.getApparentPowerLimits(TwoSides.ONE).isPresent()); + Assertions.assertEquals(1100, branch1.getApparentPowerLimits(TwoSides.ONE).get().getPermanentLimit()); + Assertions.assertEquals(LimitType.APPARENT_POWER, branch1.getApparentPowerLimits(TwoSides.ONE).get().getLimitType()); + Assertions.assertEquals(2, branch1.getApparentPowerLimits(TwoSides.ONE).get().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch1.getNullableLimits(LimitType.APPARENT_POWER, TwoSides.ONE)); + Assertions.assertEquals(1100, branch1.getNullableLimits(LimitType.APPARENT_POWER, TwoSides.ONE).getPermanentLimit()); + Assertions.assertEquals(LimitType.APPARENT_POWER, branch1.getNullableLimits(LimitType.APPARENT_POWER, TwoSides.ONE).getLimitType()); + Assertions.assertEquals(2, branch1.getNullableLimits(LimitType.APPARENT_POWER, TwoSides.ONE).getTemporaryLimits().size()); + } + + @Test + void testApparentPowerLimits2Branches() { + // power limits + + Assertions.assertNotNull(branch1.getNullableApparentPowerLimits2()); + Assertions.assertEquals(500, branch1.getNullableApparentPowerLimits2().getPermanentLimit()); + Assertions.assertEquals(LimitType.APPARENT_POWER, branch1.getNullableApparentPowerLimits2().getLimitType()); + Assertions.assertEquals(0, branch1.getNullableApparentPowerLimits2().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch1.getApparentPowerLimits(TwoSides.TWO)); + Assertions.assertTrue(branch1.getApparentPowerLimits(TwoSides.TWO).isPresent()); + Assertions.assertEquals(500, branch1.getApparentPowerLimits(TwoSides.TWO).get().getPermanentLimit()); + Assertions.assertEquals(LimitType.APPARENT_POWER, branch1.getApparentPowerLimits(TwoSides.TWO).get().getLimitType()); + Assertions.assertEquals(0, branch1.getApparentPowerLimits(TwoSides.TWO).get().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch1.getNullableLimits(LimitType.APPARENT_POWER, TwoSides.TWO)); + Assertions.assertEquals(500, branch1.getNullableLimits(LimitType.APPARENT_POWER, TwoSides.TWO).getPermanentLimit()); + Assertions.assertEquals(LimitType.APPARENT_POWER, branch1.getNullableLimits(LimitType.APPARENT_POWER, TwoSides.TWO).getLimitType()); + Assertions.assertEquals(0, branch1.getNullableLimits(LimitType.APPARENT_POWER, TwoSides.TWO).getTemporaryLimits().size()); + } + + @Test + void testCurrentLimits1Branches() { + // current limits + Assertions.assertNotNull(branch2.getNullableCurrentLimits1()); + Assertions.assertEquals(LimitType.CURRENT, branch2.getNullableCurrentLimits1().getLimitType()); + Assertions.assertEquals(1100, branch2.getNullableCurrentLimits1().getPermanentLimit()); + Assertions.assertEquals(2, branch2.getNullableCurrentLimits1().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch2.getCurrentLimits(TwoSides.ONE)); + Assertions.assertTrue(branch2.getCurrentLimits(TwoSides.ONE).isPresent()); + Assertions.assertEquals(LimitType.CURRENT, branch2.getCurrentLimits(TwoSides.ONE).get().getLimitType()); + Assertions.assertEquals(1100, branch2.getCurrentLimits(TwoSides.ONE).get().getPermanentLimit()); + Assertions.assertEquals(2, branch2.getCurrentLimits(TwoSides.ONE).get().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch2.getNullableCurrentLimits(TwoSides.ONE)); + Assertions.assertEquals(LimitType.CURRENT, branch2.getNullableCurrentLimits(TwoSides.ONE).getLimitType()); + Assertions.assertEquals(1100, branch2.getNullableCurrentLimits(TwoSides.ONE).getPermanentLimit()); + Assertions.assertEquals(2, branch2.getNullableCurrentLimits(TwoSides.ONE).getTemporaryLimits().size()); + + Assertions.assertNotNull(branch2.getNullableLimits(LimitType.CURRENT, TwoSides.ONE)); + Assertions.assertEquals(LimitType.CURRENT, branch2.getNullableLimits(LimitType.CURRENT, TwoSides.ONE).getLimitType()); + Assertions.assertEquals(1100, branch2.getNullableLimits(LimitType.CURRENT, TwoSides.ONE).getPermanentLimit()); + Assertions.assertEquals(2, branch2.getNullableLimits(LimitType.CURRENT, TwoSides.ONE).getTemporaryLimits().size()); + } + + @Test + void testCurrentLimits2Branches() { + // current limits + Assertions.assertNotNull(branch2.getNullableCurrentLimits2()); + Assertions.assertEquals(LimitType.CURRENT, branch2.getNullableCurrentLimits2().getLimitType()); + Assertions.assertEquals(500, branch2.getNullableCurrentLimits2().getPermanentLimit()); + Assertions.assertEquals(0, branch2.getNullableCurrentLimits2().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch2.getCurrentLimits(TwoSides.TWO)); + Assertions.assertTrue(branch2.getCurrentLimits(TwoSides.TWO).isPresent()); + Assertions.assertEquals(LimitType.CURRENT, branch2.getCurrentLimits(TwoSides.TWO).get().getLimitType()); + Assertions.assertEquals(500, branch2.getCurrentLimits(TwoSides.TWO).get().getPermanentLimit()); + Assertions.assertEquals(0, branch2.getCurrentLimits(TwoSides.TWO).get().getTemporaryLimits().size()); + + Assertions.assertNotNull(branch2.getNullableCurrentLimits(TwoSides.TWO)); + Assertions.assertEquals(LimitType.CURRENT, branch2.getNullableCurrentLimits(TwoSides.TWO).getLimitType()); + Assertions.assertEquals(500, branch2.getNullableCurrentLimits(TwoSides.TWO).getPermanentLimit()); + Assertions.assertEquals(0, branch2.getNullableCurrentLimits(TwoSides.TWO).getTemporaryLimits().size()); + + Assertions.assertNotNull(branch2.getNullableLimits(LimitType.CURRENT, TwoSides.TWO)); + Assertions.assertEquals(LimitType.CURRENT, branch2.getNullableLimits(LimitType.CURRENT, TwoSides.TWO).getLimitType()); + Assertions.assertEquals(500, branch2.getNullableLimits(LimitType.CURRENT, TwoSides.TWO).getPermanentLimit()); + Assertions.assertEquals(0, branch2.getNullableLimits(LimitType.CURRENT, TwoSides.TWO).getTemporaryLimits().size()); + } +} diff --git a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/json/ScalingParametersDeserializer.java b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/json/ScalingParametersDeserializer.java index 92a7bfd176f..b1a50062e53 100644 --- a/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/json/ScalingParametersDeserializer.java +++ b/iidm/iidm-modification/src/main/java/com/powsybl/iidm/modification/scalable/json/ScalingParametersDeserializer.java @@ -41,7 +41,7 @@ public ScalingParameters deserialize(JsonParser parser, DeserializationContext c public ScalingParameters deserialize(JsonParser parser, DeserializationContext context, ScalingParameters parameters) throws IOException { String version = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version" -> { parser.nextToken(); // do nothing version = parser.getValueAsString(); @@ -72,7 +72,7 @@ public ScalingParameters deserialize(JsonParser parser, DeserializationContext c JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, "Tag: ignoredInjectionIds", version, "1.2"); parameters.setIgnoredInjectionIds(new HashSet<>(JsonUtil.parseStringArray(parser))); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } return parameters; diff --git a/iidm/iidm-reducer/src/test/java/com/powsybl/iidm/reducer/SubNetworkPredicateTest.java b/iidm/iidm-reducer/src/test/java/com/powsybl/iidm/reducer/SubNetworkPredicateTest.java index 65bae1d482a..8715b467031 100644 --- a/iidm/iidm-reducer/src/test/java/com/powsybl/iidm/reducer/SubNetworkPredicateTest.java +++ b/iidm/iidm-reducer/src/test/java/com/powsybl/iidm/reducer/SubNetworkPredicateTest.java @@ -15,7 +15,6 @@ import org.junit.jupiter.api.Test; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -28,40 +27,40 @@ class SubNetworkPredicateTest { void testEsgTuto() { Network network = EurostagTutorialExample1Factory.create(); NetworkPredicate predicate = new SubNetworkPredicate(network.getVoltageLevel("VLHV1"), 1); - assertEquals(List.of("P1", "P2"), network.getSubstationStream().filter(predicate::test).map(Identifiable::getId).collect(Collectors.toList())); - assertEquals(List.of("VLGEN", "VLHV1", "VLHV2"), network.getVoltageLevelStream().filter(predicate::test).map(Identifiable::getId).collect(Collectors.toList())); + assertEquals(List.of("P1", "P2"), network.getSubstationStream().filter(predicate::test).map(Identifiable::getId).toList()); + assertEquals(List.of("VLGEN", "VLHV1", "VLHV2"), network.getVoltageLevelStream().filter(predicate::test).map(Identifiable::getId).toList()); } @Test void test3wt() { Network network = ThreeWindingsTransformerNetworkFactory.create(); NetworkPredicate predicate = new SubNetworkPredicate(network.getVoltageLevel("VL_132"), 1); - assertEquals(List.of("SUBSTATION"), network.getSubstationStream().filter(predicate::test).map(Identifiable::getId).collect(Collectors.toList())); - assertEquals(List.of("VL_132", "VL_33", "VL_11"), network.getVoltageLevelStream().filter(predicate::test).map(Identifiable::getId).collect(Collectors.toList())); + assertEquals(List.of("SUBSTATION"), network.getSubstationStream().filter(predicate::test).map(Identifiable::getId).toList()); + assertEquals(List.of("VL_132", "VL_33", "VL_11"), network.getVoltageLevelStream().filter(predicate::test).map(Identifiable::getId).toList()); } @Test void testFourSubstations() { Network network = FourSubstationsNodeBreakerFactory.create(); NetworkPredicate predicate0 = new SubNetworkPredicate(network.getVoltageLevel("S1VL1"), 0); - assertEquals(List.of("S1"), network.getSubstationStream().filter(predicate0::test).map(Identifiable::getId).collect(Collectors.toList())); - assertEquals(List.of("S1VL1"), network.getVoltageLevelStream().filter(predicate0::test).map(Identifiable::getId).collect(Collectors.toList())); + assertEquals(List.of("S1"), network.getSubstationStream().filter(predicate0::test).map(Identifiable::getId).toList()); + assertEquals(List.of("S1VL1"), network.getVoltageLevelStream().filter(predicate0::test).map(Identifiable::getId).toList()); NetworkPredicate predicate1 = new SubNetworkPredicate(network.getVoltageLevel("S1VL1"), 1); - assertEquals(List.of("S1"), network.getSubstationStream().filter(predicate1::test).map(Identifiable::getId).collect(Collectors.toList())); - assertEquals(List.of("S1VL1", "S1VL2"), network.getVoltageLevelStream().filter(predicate1::test).map(Identifiable::getId).collect(Collectors.toList())); + assertEquals(List.of("S1"), network.getSubstationStream().filter(predicate1::test).map(Identifiable::getId).toList()); + assertEquals(List.of("S1VL1", "S1VL2"), network.getVoltageLevelStream().filter(predicate1::test).map(Identifiable::getId).toList()); NetworkPredicate predicate2 = new SubNetworkPredicate(network.getVoltageLevel("S1VL1"), 2); - assertEquals(List.of("S1", "S2", "S3"), network.getSubstationStream().filter(predicate2::test).map(Identifiable::getId).collect(Collectors.toList())); - assertEquals(List.of("S1VL1", "S1VL2", "S2VL1", "S3VL1"), network.getVoltageLevelStream().filter(predicate2::test).map(Identifiable::getId).collect(Collectors.toList())); + assertEquals(List.of("S1", "S2", "S3"), network.getSubstationStream().filter(predicate2::test).map(Identifiable::getId).toList()); + assertEquals(List.of("S1VL1", "S1VL2", "S2VL1", "S3VL1"), network.getVoltageLevelStream().filter(predicate2::test).map(Identifiable::getId).toList()); NetworkPredicate predicate3 = new SubNetworkPredicate(network.getVoltageLevel("S1VL1"), 3); - assertEquals(List.of("S1", "S2", "S3", "S4"), network.getSubstationStream().filter(predicate3::test).map(Identifiable::getId).collect(Collectors.toList())); - assertEquals(List.of("S1VL1", "S1VL2", "S2VL1", "S3VL1", "S4VL1"), network.getVoltageLevelStream().filter(predicate3::test).map(Identifiable::getId).collect(Collectors.toList())); + assertEquals(List.of("S1", "S2", "S3", "S4"), network.getSubstationStream().filter(predicate3::test).map(Identifiable::getId).toList()); + assertEquals(List.of("S1VL1", "S1VL2", "S2VL1", "S3VL1", "S4VL1"), network.getVoltageLevelStream().filter(predicate3::test).map(Identifiable::getId).toList()); NetworkPredicate predicate150 = new SubNetworkPredicate(network.getVoltageLevel("S1VL1"), 150); - assertEquals(List.of("S1", "S2", "S3", "S4"), network.getSubstationStream().filter(predicate150::test).map(Identifiable::getId).collect(Collectors.toList())); - assertEquals(List.of("S1VL1", "S1VL2", "S2VL1", "S3VL1", "S4VL1"), network.getVoltageLevelStream().filter(predicate150::test).map(Identifiable::getId).collect(Collectors.toList())); + assertEquals(List.of("S1", "S2", "S3", "S4"), network.getSubstationStream().filter(predicate150::test).map(Identifiable::getId).toList()); + assertEquals(List.of("S1VL1", "S1VL2", "S2VL1", "S3VL1", "S4VL1"), network.getVoltageLevelStream().filter(predicate150::test).map(Identifiable::getId).toList()); } @Test diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ConnectableSerDeUtil.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ConnectableSerDeUtil.java index 2de8f663c97..2574a1ba940 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ConnectableSerDeUtil.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ConnectableSerDeUtil.java @@ -65,17 +65,10 @@ public static void writeNodeOrBus(Integer index, Terminal t, NetworkSerializerCo } TopologyLevel topologyLevel = TopologyLevel.min(t.getVoltageLevel().getTopologyKind(), context.getOptions().getTopologyLevel()); switch (topologyLevel) { - case NODE_BREAKER: - writeNode(index, t, context); - break; - case BUS_BREAKER: - writeBus(index, t.getBusBreakerView().getBus(), t.getBusBreakerView().getConnectableBus(), context); - break; - case BUS_BRANCH: - writeBus(index, t.getBusView().getBus(), t.getBusView().getConnectableBus(), context); - break; - default: - throw new IllegalStateException("Unexpected TopologyLevel value: " + topologyLevel); + case NODE_BREAKER -> writeNode(index, t, context); + case BUS_BREAKER -> writeBus(index, t.getBusBreakerView().getBus(), t.getBusBreakerView().getConnectableBus(), context); + case BUS_BRANCH -> writeBus(index, t.getBusView().getBus(), t.getBusView().getConnectableBus(), context); + default -> throw new IllegalStateException("Unexpected TopologyLevel value: " + topologyLevel); } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LoadSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LoadSerDe.java index 4d49c2dc8f8..59ae80419dd 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LoadSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LoadSerDe.java @@ -54,14 +54,14 @@ protected void writeSubElements(Load load, VoltageLevel parent, NetworkSerialize private void writeModel(LoadModel model, NetworkSerializerContext context) { switch (model.getType()) { - case EXPONENTIAL: + case EXPONENTIAL -> { ExponentialLoadModel expModel = (ExponentialLoadModel) model; context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), EXPONENTIAL_MODEL); context.getWriter().writeDoubleAttribute("np", expModel.getNp()); context.getWriter().writeDoubleAttribute("nq", expModel.getNq()); context.getWriter().writeEndNode(); - break; - case ZIP: + } + case ZIP -> { ZipLoadModel zipModel = (ZipLoadModel) model; context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), ZIP_MODEL); context.getWriter().writeDoubleAttribute("c0p", zipModel.getC0p()); @@ -71,9 +71,8 @@ private void writeModel(LoadModel model, NetworkSerializerContext context) { context.getWriter().writeDoubleAttribute("c1q", zipModel.getC1q()); context.getWriter().writeDoubleAttribute("c2q", zipModel.getC2q()); context.getWriter().writeEndNode(); - break; - default: - throw new PowsyblException("Unexpected load model type: " + model.getType()); + } + default -> throw new PowsyblException("Unexpected load model type: " + model.getType()); } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelSerDe.java index fd1a26849b3..89caa11daa4 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelSerDe.java @@ -59,20 +59,10 @@ protected void writeRootElementAttributes(VoltageLevel vl, Container> c, NetworkSerializerContext context) { TopologyLevel topologyLevel = TopologyLevel.min(vl.getTopologyKind(), context.getOptions().getTopologyLevel()); switch (topologyLevel) { - case NODE_BREAKER: - writeNodeBreakerTopology(vl, context); - break; - - case BUS_BREAKER: - writeBusBreakerTopology(vl, context); - break; - - case BUS_BRANCH: - writeBusBranchTopology(vl, context); - break; - - default: - throw new IllegalStateException("Unexpected TopologyLevel value: " + topologyLevel); + case NODE_BREAKER -> writeNodeBreakerTopology(vl, context); + case BUS_BREAKER -> writeBusBreakerTopology(vl, context); + case BUS_BRANCH -> writeBusBranchTopology(vl, context); + default -> throw new IllegalStateException("Unexpected TopologyLevel value: " + topologyLevel); } writeGenerators(vl, context); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/util/IidmSerDeUtil.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/util/IidmSerDeUtil.java index 06b5ab137d1..d8e8dd64ab4 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/util/IidmSerDeUtil.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/util/IidmSerDeUtil.java @@ -38,7 +38,7 @@ public enum ErrorMessage { NOT_NULL_NOT_SUPPORTED("not null and not supported"), NOT_DEFAULT_NOT_SUPPORTED("not defined as default and not supported"); - private String message; + private final String message; ErrorMessage(String message) { this.message = message; diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/BranchObservabilityXmlTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/BranchObservabilityXmlTest.java index 2ce7aa9fcd4..84445eb8b79 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/BranchObservabilityXmlTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/extensions/BranchObservabilityXmlTest.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.time.ZonedDateTime; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; @@ -38,7 +37,7 @@ class BranchObservabilityXmlTest extends AbstractIidmSerDeTest { private static List fromMinToCurrentVersion(IidmVersion min) { return Stream.of(IidmVersion.values()) .filter(v -> v.compareTo(min) >= 0) - .collect(Collectors.toList()); + .toList(); } @Test diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractBatteryTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractBatteryTest.java index f8f8565754d..f4c2dcf9e68 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractBatteryTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractBatteryTest.java @@ -14,7 +14,6 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -226,7 +225,7 @@ public void testSetterGetterInMultiVariants() { createBattery("testMultiVariant", 11.0, 12, 10, 20.0); Battery battery = network.getBattery("testMultiVariant"); - List variantsToAdd = Arrays.asList("s1", "s2", "s3", "s4"); + List variantsToAdd = List.of("s1", "s2", "s3", "s4"); variantManager.cloneVariant(VariantManagerConstants.INITIAL_VARIANT_ID, variantsToAdd); variantManager.setWorkingVariant("s4"); diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractDanglingLineTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractDanglingLineTest.java index ac9bd8cce14..5f1275426e1 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractDanglingLineTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractDanglingLineTest.java @@ -294,7 +294,7 @@ public void withRegulatingCapabilityTests() { .setMinQ(-500) .add(); assertNotNull(generation.getReactiveLimits()); - assertTrue(generation.getReactiveLimits() instanceof MinMaxReactiveLimits); + assertInstanceOf(MinMaxReactiveLimits.class, generation.getReactiveLimits()); // Test if new Generation is instantiate at each add DanglingLine dl2 = adder.setId(id + "_2").add(); diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMoveConnectableNotifTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMoveConnectableNotifTest.java index 32f500c9a28..f365f9b4e8d 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMoveConnectableNotifTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractMoveConnectableNotifTest.java @@ -34,7 +34,7 @@ public void onUpdate(Identifiable identifiable, String attribute, Object oldValu Load cf = network.getLoad("CF"); cf.getTerminal().getNodeBreakerView().moveConnectable(3, "C"); assertNotNull(obj.getValue()); - assertTrue(obj.getValue() instanceof NodeTopologyPoint); + assertInstanceOf(NodeTopologyPoint.class, obj.getValue()); NodeTopologyPoint topologyPoint = (NodeTopologyPoint) obj.getValue(); assertSame(TopologyKind.NODE_BREAKER, topologyPoint.getTopologyKind()); assertEquals("C", topologyPoint.getVoltageLevelId()); @@ -54,7 +54,7 @@ public void onUpdate(Identifiable identifiable, String attribute, Object oldValu Load load = network.getLoad("LOAD"); load.getTerminal().getBusBreakerView().moveConnectable("NGEN", true); assertNotNull(obj.getValue()); - assertTrue(obj.getValue() instanceof BusTopologyPoint); + assertInstanceOf(BusTopologyPoint.class, obj.getValue()); BusTopologyPoint topologyPoint = (BusTopologyPoint) obj.getValue(); assertSame(TopologyKind.BUS_BREAKER, topologyPoint.getTopologyKind()); assertEquals("VLGEN", topologyPoint.getVoltageLevelId()); diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractNetworkTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractNetworkTest.java index 12efd9b5235..95381cad32d 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractNetworkTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractNetworkTest.java @@ -119,7 +119,7 @@ public void testNetwork1() { assertEquals(Arrays.asList(network.getSwitch("generator1Disconnector1"), network.getSwitch("generator1Breaker1")), topology1.getSwitches(6)); assertEquals(Arrays.asList(network.getSwitch("load1Disconnector1"), network.getSwitch("load1Breaker1")), - topology1.getSwitchStream(3).collect(Collectors.toList())); + topology1.getSwitchStream(3).toList()); assertEquals(Collections.singletonList(network.getSwitch("load1Disconnector1")), topology1.getSwitches(2)); assertEquals(5, Iterables.size(network.getSwitches())); @@ -354,7 +354,7 @@ public void testNetworkWithBattery() { //Specific test on battery assertEquals(battery1, voltageLevel2.getConnectable("BAT", Battery.class)); //Stream test - Function, List> mapper = stream -> stream.map(Identifiable::getId).collect(Collectors.toList()); + Function, List> mapper = stream -> stream.map(Identifiable::getId).toList(); assertEquals(Arrays.asList("BAT", "BAT2"), mapper.apply(network.getBatteryStream())); assertEquals(network.getBatteryCount(), network.getBatteryStream().count()); assertEquals(Arrays.asList("BAT", "BAT2"), mapper.apply(network.getVoltageLevel(VLBAT).getBatteryStream())); @@ -375,13 +375,13 @@ public void testGetConnectable() { Network n = EurostagTutorialExample1Factory.create(); assertEquals(6, n.getConnectableCount()); assertNotNull(n.getConnectable("GEN")); - assertTrue(n.getConnectable("GEN") instanceof Generator); + assertInstanceOf(Generator.class, n.getConnectable("GEN")); assertEquals("GEN", n.getConnectable("GEN").getId()); } @Test public void testStreams() { - Function, List> mapper = stream -> stream.map(Identifiable::getId).collect(Collectors.toList()); + Function, List> mapper = stream -> stream.map(Identifiable::getId).toList(); Function, Set> mapperSet = stream -> stream.map(Identifiable::getId).collect(Collectors.toSet()); Network network = EurostagTutorialExample1Factory.create(); diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractNodeBreakerInternalConnectionsTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractNodeBreakerInternalConnectionsTest.java index fca0cf55187..7c142cdf18a 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractNodeBreakerInternalConnectionsTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractNodeBreakerInternalConnectionsTest.java @@ -33,7 +33,7 @@ public void testTraversalInternalConnections() { VoltageLevel vl = network.getVoltageLevel(S5_10K_V); assertEquals(6, vl.getNodeBreakerView().getInternalConnectionCount()); - List internalConnections = vl.getNodeBreakerView().getInternalConnectionStream().collect(Collectors.toList()); + List internalConnections = vl.getNodeBreakerView().getInternalConnectionStream().toList(); int[] expecteds1 = new int[]{7, 6, 4, 5, 9, 8}; int[] expecteds2 = new int[]{0, 3, 3, 2, 2, 1}; assertEquals(expecteds1.length, expecteds2.length); @@ -51,7 +51,7 @@ public void testTraversalInternalConnections() { assertEquals(9, (int) nodeIterator2.next()); assertFalse(nodeIterator2.hasNext()); - List nodesInternallyConnectedTo3 = vl.getNodeBreakerView().getNodeInternalConnectedToStream(3).boxed().collect(Collectors.toList()); + List nodesInternallyConnectedTo3 = vl.getNodeBreakerView().getNodeInternalConnectedToStream(3).boxed().toList(); assertEquals(Arrays.asList(6, 4), nodesInternallyConnectedTo3); // Find the first internal connection encountered diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSwitchSetRetainedTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSwitchSetRetainedTest.java index 5d4d2526eff..618469957f7 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSwitchSetRetainedTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractSwitchSetRetainedTest.java @@ -12,7 +12,6 @@ import org.junit.jupiter.api.Test; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -86,16 +85,16 @@ public void testCloseRetainedSwitch() { assertTrue(b1.isRetained()); assertFalse(b1.isOpen()); - List buses0 = vl.getBusBreakerView().getBusStream().collect(Collectors.toList()); + List buses0 = vl.getBusBreakerView().getBusStream().toList(); b1.setOpen(true); - List buses1 = vl.getBusBreakerView().getBusStream().collect(Collectors.toList()); + List buses1 = vl.getBusBreakerView().getBusStream().toList(); assertEquals(buses0, buses1); b1.setOpen(false); - List buses2 = vl.getBusBreakerView().getBusStream().collect(Collectors.toList()); + List buses2 = vl.getBusBreakerView().getBusStream().toList(); assertEquals(buses0, buses2); } } diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTopologyTraverserTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTopologyTraverserTest.java index 214607b419a..9ed38dbb935 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTopologyTraverserTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractTopologyTraverserTest.java @@ -257,7 +257,7 @@ protected List> getVisitedList(Terminal start, Function> getVisitedList(Terminal start, Function switchTest, Function terminalTest) { - return getVisitedStream(start, switchTest, terminalTest).collect(Collectors.toList()); + return getVisitedStream(start, switchTest, terminalTest).toList(); } protected Set> getVisitedSet(Terminal start, Function switchTest) { diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractVoltageLevelExportTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractVoltageLevelExportTest.java index 0226eb1e20c..3c6d9f3aafa 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractVoltageLevelExportTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractVoltageLevelExportTest.java @@ -19,8 +19,8 @@ import java.io.StringWriter; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Random; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -37,7 +37,7 @@ public void nodeBreakerTest() throws IOException { writer.flush(); // as Graphviz builder library do not have to stable export (order of nodes and edges can change at each run) // we only compare unsorted lines - List linesRef = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/fictitious-switch-c.dot"))).lines().collect(Collectors.toList()); + List linesRef = new BufferedReader(new InputStreamReader(Objects.requireNonNull(getClass().getResourceAsStream("/fictitious-switch-c.dot")))).lines().toList(); List lines = Arrays.asList(writer.toString().split("[\\r\\n]+")); assertTrue(lines.containsAll(linesRef)); } @@ -51,7 +51,7 @@ public void busBreakerTest() throws IOException { writer.flush(); // as Graphviz builder library do not have to stable export (order of nodes and edges can change at each run) // we only compare unsorted lines - List linesRef = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/eurostag-tutorial-example1-vlhv1.dot"))).lines().collect(Collectors.toList()); + List linesRef = new BufferedReader(new InputStreamReader(Objects.requireNonNull(getClass().getResourceAsStream("/eurostag-tutorial-example1-vlhv1.dot")))).lines().toList(); List lines = Arrays.asList(writer.toString().split("[\\r\\n]+")); assertTrue(lines.containsAll(linesRef)); } diff --git a/loadflow/loadflow-api/src/main/java/com/powsybl/loadflow/json/LoadFlowResultDeserializer.java b/loadflow/loadflow-api/src/main/java/com/powsybl/loadflow/json/LoadFlowResultDeserializer.java index 99c6573925a..a88d3d5df7e 100644 --- a/loadflow/loadflow-api/src/main/java/com/powsybl/loadflow/json/LoadFlowResultDeserializer.java +++ b/loadflow/loadflow-api/src/main/java/com/powsybl/loadflow/json/LoadFlowResultDeserializer.java @@ -51,19 +51,19 @@ public LoadFlowResult.ComponentResult deserializeComponentResult(JsonParser pars Double distributedActivePower = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "connectedComponentNum" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.currentName(), version, "1.2"); parser.nextToken(); connectedComponentNum = parser.getValueAsInt(); } case "synchronousComponentNum" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.currentName(), version, "1.2"); parser.nextToken(); synchronousComponentNum = parser.getValueAsInt(); } case "componentNum" -> { - JsonUtil.assertLessThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.getCurrentName(), version, "1.2"); + JsonUtil.assertLessThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.currentName(), version, "1.2"); parser.nextToken(); synchronousComponentNum = parser.getValueAsInt(); } @@ -102,21 +102,21 @@ public LoadFlowResult.ComponentResult deserializeComponentResult(JsonParser pars deserializeSlackBusResults(parser, slackBusResults); } case "slackBusId" -> { - JsonUtil.assertLessThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.getCurrentName(), version, "1.4"); + JsonUtil.assertLessThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.currentName(), version, "1.4"); parser.nextToken(); slackBusId = parser.getValueAsString(); } case "slackBusActivePowerMismatch" -> { - JsonUtil.assertLessThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.getCurrentName(), version, "1.4"); + JsonUtil.assertLessThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.currentName(), version, "1.4"); parser.nextToken(); slackBusActivePowerMismatch = parser.getValueAsDouble(); } case "distributedActivePower" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.getCurrentName(), version, "1.3"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(LoadFlowResultDeserializer.class.getName(), parser.currentName(), version, "1.3"); parser.nextToken(); distributedActivePower = parser.getValueAsDouble(); } - default -> throw new IllegalStateException(UNEXPECTED_FIELD + parser.getCurrentName()); + default -> throw new IllegalStateException(UNEXPECTED_FIELD + parser.currentName()); } } @@ -164,7 +164,7 @@ public LoadFlowResult.SlackBusResult deserializeSlackBusResult(JsonParser parser Double activePowerMismatch = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "id" -> { parser.nextToken(); id = parser.getValueAsString(); @@ -173,7 +173,7 @@ public LoadFlowResult.SlackBusResult deserializeSlackBusResult(JsonParser parser parser.nextToken(); activePowerMismatch = parser.getValueAsDouble(); } - default -> throw new IllegalStateException(UNEXPECTED_FIELD + parser.getCurrentName()); + default -> throw new IllegalStateException(UNEXPECTED_FIELD + parser.currentName()); } } @@ -208,7 +208,7 @@ public LoadFlowResult deserialize(JsonParser parser, DeserializationContext ctx) List componentResults = new ArrayList<>(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version" -> { parser.nextToken(); version = parser.getValueAsString(); @@ -226,7 +226,7 @@ public LoadFlowResult deserialize(JsonParser parser, DeserializationContext ctx) parser.nextToken(); deserializeComponentResults(parser, componentResults, version); } - default -> throw new IllegalStateException(UNEXPECTED_FIELD + parser.getCurrentName()); + default -> throw new IllegalStateException(UNEXPECTED_FIELD + parser.currentName()); } } diff --git a/loadflow/loadflow-api/src/test/java/com/powsybl/loadflow/LoadFlowParametersTest.java b/loadflow/loadflow-api/src/test/java/com/powsybl/loadflow/LoadFlowParametersTest.java index b967223e362..89d04696490 100644 --- a/loadflow/loadflow-api/src/test/java/com/powsybl/loadflow/LoadFlowParametersTest.java +++ b/loadflow/loadflow-api/src/test/java/com/powsybl/loadflow/LoadFlowParametersTest.java @@ -21,7 +21,6 @@ import java.nio.file.FileSystem; import java.util.HashSet; import java.util.Set; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -123,7 +122,7 @@ void checkConfig() { moduleConfig.setStringProperty("distributedSlack", Boolean.toString(dc)); moduleConfig.setStringProperty("balanceType", balanceType.name()); moduleConfig.setStringProperty("dcUseTransformerRatio", Boolean.toString(dc)); - moduleConfig.setStringListProperty("countriesToBalance", countriesToBalance.stream().map(e -> e.name()).collect(Collectors.toList())); + moduleConfig.setStringListProperty("countriesToBalance", countriesToBalance.stream().map(e -> e.name()).toList()); moduleConfig.setStringProperty("computedConnectedComponent", computedConnectedComponent.name()); moduleConfig.setStringProperty("hvdcAcEmulation", Boolean.toString(hvdcAcEmulation)); @@ -327,8 +326,8 @@ void testExtensions() { assertEquals(1, parameters.getExtensions().size()); assertTrue(parameters.getExtensions().contains(dummyExtension)); - assertTrue(parameters.getExtensionByName("dummy-extension") instanceof DummyExtension); - assertTrue(parameters.getExtension(DummyExtension.class) instanceof DummyExtension); + assertInstanceOf(DummyExtension.class, parameters.getExtensionByName("dummy-extension")); + assertNotNull(parameters.getExtension(DummyExtension.class)); } @Test @@ -350,7 +349,7 @@ void testNoExtensions() { assertEquals(0, parameters.getExtensions().size()); assertFalse(parameters.getExtensions().contains(new DummyExtension())); assertFalse(parameters.getExtensionByName("dummy-extension") instanceof DummyExtension); - assertFalse(parameters.getExtension(DummyExtension.class) instanceof DummyExtension); + assertNull(parameters.getExtension(DummyExtension.class)); } @Test @@ -358,7 +357,7 @@ void testExtensionFromConfig() { LoadFlowParameters parameters = LoadFlowParameters.load(platformConfig); assertEquals(1, parameters.getExtensions().size()); - assertTrue(parameters.getExtensionByName("dummy-extension") instanceof DummyExtension); + assertInstanceOf(DummyExtension.class, parameters.getExtensionByName("dummy-extension")); assertNotNull(parameters.getExtension(DummyExtension.class)); } } diff --git a/loadflow/loadflow-api/src/test/java/com/powsybl/loadflow/LoadFlowProviderTest.java b/loadflow/loadflow-api/src/test/java/com/powsybl/loadflow/LoadFlowProviderTest.java index f57f35f8fc6..d907d0f2340 100644 --- a/loadflow/loadflow-api/src/test/java/com/powsybl/loadflow/LoadFlowProviderTest.java +++ b/loadflow/loadflow-api/src/test/java/com/powsybl/loadflow/LoadFlowProviderTest.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -43,7 +42,7 @@ void findAllProvidersTest() { void testParametersExtension() throws IOException { LoadFlowProvider provider = new LoadFlowProviderMock(); assertEquals(3, provider.getSpecificParameters().size()); - assertEquals(List.of("parameterDouble", "parameterBoolean", "parameterString"), provider.getSpecificParameters().stream().map(Parameter::getName).collect(Collectors.toList())); + assertEquals(List.of("parameterDouble", "parameterBoolean", "parameterString"), provider.getSpecificParameters().stream().map(Parameter::getName).toList()); assertSame(DummyExtension.class, provider.getSpecificParametersClass().orElseThrow()); try (FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix())) { InMemoryPlatformConfig platformConfig = new InMemoryPlatformConfig(fileSystem); diff --git a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/ContainersMappingHelper.java b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/ContainersMappingHelper.java index bd5c193555b..e8cf506b78e 100644 --- a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/ContainersMappingHelper.java +++ b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/ContainersMappingHelper.java @@ -184,10 +184,7 @@ private static void createEdges(List edges, Map vscConnectedToElmTerm(DataObject elmTerm) { private Optional findAcElmTerm(DataObject elmVsc, List dcElmTerms) { List elmTermsAc = elmTermsConnectedToVscs.get(elmVsc).stream() .filter(elmTerm -> !dcElmTerms.contains(elmTerm)) - .collect(Collectors.toList()); + .toList(); return findTheOnlyOneDataObject(elmTermsAc); } diff --git a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/LineConverter.java b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/LineConverter.java index 0a0765d6a21..4a5725c90be 100644 --- a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/LineConverter.java +++ b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/LineConverter.java @@ -71,12 +71,11 @@ private void createLine(NodeRef end1, NodeRef end2, String id, LineModel lineMod private void createFromElmLneFromElmTow(DataObject elmTow, DataObject elmLne) { List nodeRefs = checkNodes(elmLne, 2); - Optional lineModel = LineModel.createFromElmTow(elmTow, elmLne); + LineModel lineModel = LineModel.createFromElmTow(elmTow, elmLne); NodeRef end1 = nodeRefs.get(0); NodeRef end2 = nodeRefs.get(1); - - createLine(end1, end2, elmLne.getLocName(), lineModel.get()); + createLine(end1, end2, elmLne.getLocName(), lineModel); } private static final class LineModel { @@ -147,8 +146,8 @@ private static double typeLneModelShuntSusceptance(DataObject typLne) { return 0.0; } - private static Optional createFromElmTow(DataObject elmTow, DataObject elmLne) { - Float dline = elmLne.getFloatAttributeValue("dline"); + private static LineModel createFromElmTow(DataObject elmTow, DataObject elmLne) { + float dline = elmLne.getFloatAttributeValue("dline"); DataObject typTow = getTypeTow(elmTow); double r = typTow.getDoubleMatrixAttributeValue("R_c1").getEntry(0, 0) * dline; @@ -156,7 +155,7 @@ private static Optional createFromElmTow(DataObject elmTow, DataObjec double g = microSiemensToSiemens(typTow.getDoubleMatrixAttributeValue("G_c1").getEntry(0, 0) * dline); double b = microSiemensToSiemens(typTow.getDoubleMatrixAttributeValue("B_c1").getEntry(0, 0) * dline); - return Optional.of(new LineModel(r, x, g * 0.5, b * 0.5, g * 0.5, b * 0.5)); + return new LineModel(r, x, g * 0.5, b * 0.5, g * 0.5, b * 0.5); } private static DataObject getTypeTow(DataObject elmTow) { diff --git a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java index bc11235be9d..e7d3ac00516 100644 --- a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java +++ b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/PowerFactoryImporter.java @@ -12,7 +12,6 @@ import com.google.common.io.ByteStreams; import com.powsybl.commons.datasource.DataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; -import com.powsybl.commons.parameters.Parameter; import com.powsybl.iidm.network.Importer; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.NetworkFactory; @@ -56,11 +55,6 @@ public List getSupportedExtensions() { return PowerFactoryDataLoader.find(StudyCase.class).stream().map(PowerFactoryDataLoader::getExtension).toList(); } - @Override - public List getParameters() { - return Collections.emptyList(); - } - @Override public String getComment() { return "PowerFactory to IIDM converter"; diff --git a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/ShuntConverter.java b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/ShuntConverter.java index bbf11470740..4415eb1673a 100644 --- a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/ShuntConverter.java +++ b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/ShuntConverter.java @@ -61,14 +61,11 @@ private LinearShuntModel(Section section, double gPerSection, double bPerSection private static LinearShuntModel create(DataObject elmShnt) { int shtype = elmShnt.getIntAttributeValue("shtype"); - switch (shtype) { - case 1: - return rlShunt(elmShnt); - case 2: - return cShunt(elmShnt); - default: - throw new PowerFactoryException("Shunt type not supported: " + shtype); - } + return switch (shtype) { + case 1 -> rlShunt(elmShnt); + case 2 -> cShunt(elmShnt); + default -> throw new PowerFactoryException("Shunt type not supported: " + shtype); + }; } private static LinearShuntModel rlShunt(DataObject elmShnt) { diff --git a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/SwitchConverter.java b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/SwitchConverter.java index 49e74be64db..28d0c9ed72e 100644 --- a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/SwitchConverter.java +++ b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/SwitchConverter.java @@ -109,18 +109,11 @@ private static SwitchKind createSwitchKind(DataObject dataObject) { SwitchKind switchKind; if (aUsage.isPresent()) { - switch (aUsage.get()) { - case "cbk": - case "swt": - switchKind = SwitchKind.BREAKER; - break; - case "dct": - case "sdc": - switchKind = SwitchKind.DISCONNECTOR; - break; - default: - throw new PowerFactoryException("Unknown switch type: " + aUsage); - } + switchKind = switch (aUsage.get()) { + case "cbk", "swt" -> SwitchKind.BREAKER; + case "dct", "sdc" -> SwitchKind.DISCONNECTOR; + default -> throw new PowerFactoryException("Unknown switch type: " + aUsage); + }; } else { switchKind = SwitchKind.BREAKER; } diff --git a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/TransformerConverter.java b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/TransformerConverter.java index e2f98bda107..04236e3b1e0 100644 --- a/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/TransformerConverter.java +++ b/powerfactory/powerfactory-converter/src/main/java/com/powsybl/powerfactory/converter/TransformerConverter.java @@ -111,8 +111,7 @@ void createThreeWindings(DataObject elmTr3) { List windingTypeEnds = createWindingTypeEnds(vl1, vl2, vl3); double vn0 = 1.0; - double ratedU0 = vn0; - Rated3WModel rated3WModel = Rated3WModel.create(typTr3, ratedU0); + Rated3WModel rated3WModel = Rated3WModel.create(typTr3, vn0); RatedModel ratedModel1 = rated3WModel.getEnd(windingTypeEnds.get(0)); RatedModel ratedModel2 = rated3WModel.getEnd(windingTypeEnds.get(1)); RatedModel ratedModel3 = rated3WModel.getEnd(windingTypeEnds.get(2)); @@ -127,7 +126,7 @@ void createThreeWindings(DataObject elmTr3) { Substation substation = vl1.getSubstation().orElseThrow(); ThreeWindingsTransformerAdder adder = substation.newThreeWindingsTransformer() - .setRatedU0(ratedU0) + .setRatedU0(vn0) .setEnsureIdUnicity(true) .setId(elmTr3.getLocName()) .newLeg1() @@ -178,8 +177,8 @@ void createThreeWindings(DataObject elmTr3) { Optional pac3 = pac3WModel.getEnd(windingTypeEnds.get(2)); if (pac2.isPresent() || pac3.isPresent()) { t3wt.newExtension(ThreeWindingsTransformerPhaseAngleClockAdder.class) - .withPhaseAngleClockLeg2(pac2.isPresent() ? pac2.get().pac : 0) - .withPhaseAngleClockLeg3(pac3.isPresent() ? pac3.get().pac : 0).add(); + .withPhaseAngleClockLeg2(pac2.map(model -> model.pac).orElse(0)) + .withPhaseAngleClockLeg3(pac3.map(model -> model.pac).orElse(0)).add(); } } @@ -814,16 +813,12 @@ private static List createWindingTypeEnds(VoltageLevel vl1, Voltage } private static WindingType positionToWindingType(int position) { - switch (position) { - case 0: - return WindingType.HIGH; - case 1: - return WindingType.MEDIUM; - case 2: - return WindingType.LOW; - default: - throw new PowerFactoryException("Unexpected position: " + position); - } + return switch (position) { + case 0 -> WindingType.HIGH; + case 1 -> WindingType.MEDIUM; + case 2 -> WindingType.LOW; + default -> throw new PowerFactoryException("Unexpected position: " + position); + }; } private static final Logger LOGGER = LoggerFactory.getLogger(TransformerConverter.class); diff --git a/powerfactory/powerfactory-model/src/main/java/com/powsybl/powerfactory/model/StudyCase.java b/powerfactory/powerfactory-model/src/main/java/com/powsybl/powerfactory/model/StudyCase.java index 9bbb1c66d48..99f4e6daba1 100644 --- a/powerfactory/powerfactory-model/src/main/java/com/powsybl/powerfactory/model/StudyCase.java +++ b/powerfactory/powerfactory-model/src/main/java/com/powsybl/powerfactory/model/StudyCase.java @@ -62,30 +62,31 @@ static StudyCase parseJson(JsonParser parser) { } catch (IOException e) { throw new UncheckedIOException(e); } - JsonUtil.parseObject(parser, fieldName -> { - switch (fieldName) { - case "name": - context.name = parser.nextTextValue(); - return true; - case "time": - context.time = Instant.parse(parser.nextTextValue()); - return true; - case "classes": - context.scheme = DataScheme.parseJson(parser); - return true; - case "objects": - JsonUtil.parseObjectArray(parser, obj -> { }, - parser2 -> DataObject.parseJson(parser2, context.index, context.scheme)); - return true; - case "elmNets": - context.elmNets = JsonUtil.parseLongArray(parser).stream() - .map(id -> context.index.getDataObjectById(id) - .orElseThrow(() -> new PowerFactoryException("ElmNet object " + id + " not found"))) - .collect(Collectors.toList()); - return true; - default: - return false; + JsonUtil.parseObject(parser, fieldName -> switch (fieldName) { + case "name" -> { + context.name = parser.nextTextValue(); + yield true; } + case "time" -> { + context.time = Instant.parse(parser.nextTextValue()); + yield true; + } + case "classes" -> { + context.scheme = DataScheme.parseJson(parser); + yield true; + } + case "objects" -> { + JsonUtil.parseObjectArray(parser, obj -> { }, parser2 -> DataObject.parseJson(parser2, context.index, context.scheme)); + yield true; + } + case "elmNets" -> { + context.elmNets = JsonUtil.parseLongArray(parser).stream() + .map(id -> context.index.getDataObjectById(id) + .orElseThrow(() -> new PowerFactoryException("ElmNet object " + id + " not found"))) + .collect(Collectors.toList()); + yield true; + } + default -> false; }); return new StudyCase(context.name, context.time, context.elmNets, context.index); } diff --git a/powerfactory/powerfactory-model/src/test/java/com/powsybl/powerfactory/model/StudyCaseTest.java b/powerfactory/powerfactory-model/src/test/java/com/powsybl/powerfactory/model/StudyCaseTest.java index 9aa66d5e633..2c16ac5f37d 100644 --- a/powerfactory/powerfactory-model/src/test/java/com/powsybl/powerfactory/model/StudyCaseTest.java +++ b/powerfactory/powerfactory-model/src/test/java/com/powsybl/powerfactory/model/StudyCaseTest.java @@ -15,7 +15,6 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -54,7 +53,7 @@ void jsonTest() throws IOException { assertEquals(List.of(1.3f, 2.3f, 3.5f), objFoo.getFloatVectorAttributeValue("fv")); assertEquals(List.of(4L, 5L, 6943953495493593L), objFoo.getLongVectorAttributeValue("lv")); assertEquals(List.of(1.3949d, 2.34d, 3.1223d), objFoo.getDoubleVectorAttributeValue("dv")); - assertEquals(List.of(3L), objFoo.getObjectVectorAttributeValue("ov").stream().map(DataObjectRef::getId).collect(Collectors.toList())); + assertEquals(List.of(3L), objFoo.getObjectVectorAttributeValue("ov").stream().map(DataObjectRef::getId).toList()); assertEquals(List.of("AA", "BBB"), objFoo.getStringVectorAttributeValue("sv")); } diff --git a/psse/psse-converter/src/main/java/com/powsybl/psse/converter/SwitchedShuntCompensatorConverter.java b/psse/psse-converter/src/main/java/com/powsybl/psse/converter/SwitchedShuntCompensatorConverter.java index 068c6776900..7205a0c9e7c 100644 --- a/psse/psse-converter/src/main/java/com/powsybl/psse/converter/SwitchedShuntCompensatorConverter.java +++ b/psse/psse-converter/src/main/java/com/powsybl/psse/converter/SwitchedShuntCompensatorConverter.java @@ -168,9 +168,9 @@ private static List defineShuntBlocks(PsseSwitchedShunt psseSwitched double bAdd = 0.0; List shuntBlocks = new ArrayList<>(); if (!psseReactorBlocks.isEmpty()) { - for (int i = 0; i < psseReactorBlocks.size(); i++) { - for (int j = 0; j < psseReactorBlocks.get(i).getN(); j++) { - bAdd = bAdd + psseReactorBlocks.get(i).getB(); + for (ShuntBlock psseReactorBlock : psseReactorBlocks) { + for (int j = 0; j < psseReactorBlock.getN(); j++) { + bAdd = bAdd + psseReactorBlock.getB(); shuntBlocks.add(new ShuntBlock(1, 1, bAdd)); } } @@ -181,9 +181,9 @@ private static List defineShuntBlocks(PsseSwitchedShunt psseSwitched } if (!psseCapacitorBlocks.isEmpty()) { - for (int i = 0; i < psseCapacitorBlocks.size(); i++) { - for (int j = 0; j < psseCapacitorBlocks.get(i).getN(); j++) { - bAdd = bAdd + psseCapacitorBlocks.get(i).getB(); + for (ShuntBlock psseCapacitorBlock : psseCapacitorBlocks) { + for (int j = 0; j < psseCapacitorBlock.getN(); j++) { + bAdd = bAdd + psseCapacitorBlock.getB(); shuntBlocks.add(new ShuntBlock(1, 1, bAdd)); } } diff --git a/psse/psse-model/src/main/java/com/powsybl/psse/model/io/RecordGroupIOJson.java b/psse/psse-model/src/main/java/com/powsybl/psse/model/io/RecordGroupIOJson.java index 1095fdd3d51..fb1bd36e1e9 100644 --- a/psse/psse-model/src/main/java/com/powsybl/psse/model/io/RecordGroupIOJson.java +++ b/psse/psse-model/src/main/java/com/powsybl/psse/model/io/RecordGroupIOJson.java @@ -82,7 +82,7 @@ private JsonNode readJsonNode(JsonParser parser) throws IOException { String nodeName = recordGroup.getIdentification().getJsonNodeName(); while (!parser.isClosed()) { parser.nextToken(); - if (nodeName.equals(parser.getCurrentName())) { + if (nodeName.equals(parser.currentName())) { return mapper.convertValue(parser.readValueAsTree().get("caseid"), JsonNode.class); } } diff --git a/psse/psse-model/src/main/java/com/powsybl/psse/model/pf/PsseValidation.java b/psse/psse-model/src/main/java/com/powsybl/psse/model/pf/PsseValidation.java index ae5b2d5a7ae..e2617316e97 100644 --- a/psse/psse-model/src/main/java/com/powsybl/psse/model/pf/PsseValidation.java +++ b/psse/psse-model/src/main/java/com/powsybl/psse/model/pf/PsseValidation.java @@ -102,8 +102,7 @@ private void validateBuses(List psseBuses, Map> } } - for (int i = 0; i < psseBuses.size(); i++) { - PsseBus psseBus = psseBuses.get(i); + for (PsseBus psseBus : psseBuses) { if (psseBus.getI() < 1 || psseBus.getI() > 999997) { warnings.add(String.format("Bus: Unexpected I: %d", psseBus.getI())); validCase = false; @@ -118,8 +117,7 @@ private void validateBuses(List psseBuses, Map> private void validateLoads(List loads, Map> buses) { Map> busesLoads = new HashMap<>(); - for (int i = 0; i < loads.size(); i++) { - PsseLoad load = loads.get(i); + for (PsseLoad load : loads) { if (!buses.containsKey(load.getI())) { warnings.add(String.format("Load: Unexpected I: %d", load.getI())); validCase = false; @@ -133,8 +131,7 @@ private void validateLoads(List loads, Map> bus private void validateFixedShunts(List fixedShunts, Map> buses) { Map> busesFixedShunts = new HashMap<>(); - for (int i = 0; i < fixedShunts.size(); i++) { - PsseFixedShunt fixedShunt = fixedShunts.get(i); + for (PsseFixedShunt fixedShunt : fixedShunts) { if (!buses.containsKey(fixedShunt.getI())) { warnings.add(String.format("FixedShunt: Unexpected I: %d", fixedShunt.getI())); validCase = false; @@ -149,8 +146,7 @@ private void validateGenerators(List psseBuses, List gen Map> busesGenerators = new HashMap<>(); - for (int i = 0; i < generators.size(); i++) { - PsseGenerator generator = generators.get(i); + for (PsseGenerator generator : generators) { if (!buses.containsKey(generator.getI())) { warnings.add(String.format("Generator: Unexpected I: %d", generator.getI())); validCase = false; @@ -188,8 +184,7 @@ private void validateGeneratorRegulatingBus(List psseBuses, Map nonTransformerBranches, Map> buses) { Map> busesNonTransformerBranches = new HashMap<>(); - for (int i = 0; i < nonTransformerBranches.size(); i++) { - PsseNonTransformerBranch nonTransformerBranch = nonTransformerBranches.get(i); + for (PsseNonTransformerBranch nonTransformerBranch : nonTransformerBranches) { if (!buses.containsKey(nonTransformerBranch.getI())) { warnings.add(String.format("NonTransformerBranch: Unexpected I: %d", nonTransformerBranch.getI())); validCase = false; @@ -209,20 +204,19 @@ private void validateNonTransformerBranches(List nonTr } private void validateTransformers(List transformers, Map> buses) { - List twoWinddingsTransformers = transformers.parallelStream() - .filter(transformer -> transformer.getK() == 0).collect(Collectors.toList()); - validateTwoWindingsTransformers(twoWinddingsTransformers, buses); + List twoWindingsTransformers = transformers.parallelStream() + .filter(transformer -> transformer.getK() == 0).toList(); + validateTwoWindingsTransformers(twoWindingsTransformers, buses); - List threeWinddingsTransformers = transformers.parallelStream() - .filter(transformer -> transformer.getK() != 0).collect(Collectors.toList()); - validateThreeWindingsTransformers(threeWinddingsTransformers, buses); + List threeWindingsTransformers = transformers.parallelStream() + .filter(transformer -> transformer.getK() != 0).toList(); + validateThreeWindingsTransformers(threeWindingsTransformers, buses); } private void validateTwoWindingsTransformers(List transformers, Map> buses) { Map> busesTransformers = new HashMap<>(); - for (int i = 0; i < transformers.size(); i++) { - PsseTransformer transformer = transformers.get(i); + for (PsseTransformer transformer : transformers) { validateTransformerBus(buses, transformer.getI(), "I"); validateTransformerBus(buses, transformer.getJ(), "J"); @@ -243,8 +237,7 @@ private void validateTwoWindingsTransformers(List transformers, private void validateThreeWindingsTransformers(List transformers, Map> buses) { Map> busesTransformers = new HashMap<>(); - for (int i = 0; i < transformers.size(); i++) { - PsseTransformer transformer = transformers.get(i); + for (PsseTransformer transformer : transformers) { validateTransformerBus(buses, transformer.getI(), "I"); validateTransformerBus(buses, transformer.getJ(), "J"); validateTransformerBus(buses, transformer.getK(), "K"); @@ -347,8 +340,7 @@ private void validateTransformerWindingCont(Map> buses, S private void validateSwitchedShunts(List switchedShunts, Map> buses, PsseVersion psseVersion) { Map> busesSwitchedShunts = new HashMap<>(); - for (int i = 0; i < switchedShunts.size(); i++) { - PsseSwitchedShunt switchedShunt = switchedShunts.get(i); + for (PsseSwitchedShunt switchedShunt : switchedShunts) { if (!buses.containsKey(switchedShunt.getI())) { warnings.add(String.format("SwitchedShunt: Unexpected I: %d", switchedShunt.getI())); validCase = false; @@ -464,17 +456,17 @@ private static void addBusesMap(Map> busesMap, int busI, in private static int firstBus(String busKey) { String[] tokens = busKey.split("-"); - return Integer.valueOf(tokens[0]); + return Integer.parseInt(tokens[0]); } private static int secondBus(String busKey) { String[] tokens = busKey.split("-"); - return Integer.valueOf(tokens[1]); + return Integer.parseInt(tokens[1]); } private static int thirdBus(String busKey) { String[] tokens = busKey.split("-"); - return Integer.valueOf(tokens[2]); + return Integer.parseInt(tokens[2]); } private void checkDuplicates(String tag, String tagEquipments, Map> duplicatedBusesEquipments) { diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/SecurityAnalysisResultMerger.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/SecurityAnalysisResultMerger.java index f802f5932a4..8ac0d8df4c1 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/SecurityAnalysisResultMerger.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/SecurityAnalysisResultMerger.java @@ -37,7 +37,7 @@ public static SecurityAnalysisResult merge(SecurityAnalysisResult[] results) { public static SecurityAnalysisResult merge(Collection results) { Objects.requireNonNull(results); - return merge(results.toArray(new SecurityAnalysisResult[results.size()])); + return merge(results.toArray(new SecurityAnalysisResult[0])); } private SecurityAnalysisResultMerger() { diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ActivePowerExtensionSerializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ActivePowerExtensionSerializer.java index a3fd33ab8dc..12d2582bcb7 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ActivePowerExtensionSerializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ActivePowerExtensionSerializer.java @@ -60,17 +60,17 @@ public ActivePowerExtension deserialize(JsonParser parser, DeserializationContex double postContingencyValue = Double.NaN; while (parser.nextToken() != JsonToken.END_OBJECT) { - if (parser.getCurrentName().equals("value")) { + if (parser.currentName().equals("value")) { parser.nextToken(); value = parser.readValueAs(Double.class); - } else if (parser.getCurrentName().equals("preContingencyValue")) { + } else if (parser.currentName().equals("preContingencyValue")) { parser.nextToken(); preContingencyValue = parser.readValueAs(Double.class); - } else if (parser.getCurrentName().equals("postContingencyValue")) { + } else if (parser.currentName().equals("postContingencyValue")) { parser.nextToken(); postContingencyValue = parser.readValueAs(Double.class); } else { - throw new PowsyblException("Unexpected field: " + parser.getCurrentName()); + throw new PowsyblException("Unexpected field: " + parser.currentName()); } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ConditionalActionsResultDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ConditionalActionsResultDeserializer.java index a01b407588c..2092668a326 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ConditionalActionsResultDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ConditionalActionsResultDeserializer.java @@ -36,7 +36,7 @@ public OperatorStrategyResult.ConditionalActionsResult deserialize(JsonParser pa NetworkResult networkResult = null; PostContingencyComputationStatus status = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "conditionalActionsId" -> { parser.nextToken(); conditionalActionsId = JsonUtil.readValue(deserializationContext, parser, String.class); @@ -53,7 +53,7 @@ public OperatorStrategyResult.ConditionalActionsResult deserialize(JsonParser pa parser.nextToken(); status = JsonUtil.readValue(deserializationContext, parser, PostContingencyComputationStatus.class); } - default -> throw new JsonMappingException(parser, "Unexpected field: " + parser.getCurrentName()); + default -> throw new JsonMappingException(parser, "Unexpected field: " + parser.currentName()); } } return new OperatorStrategyResult.ConditionalActionsResult(conditionalActionsId, status, limitViolationsResult, networkResult); diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ConnectivityResultDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ConnectivityResultDeserializer.java index 253cc5f6a98..fd1f48afd43 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ConnectivityResultDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/ConnectivityResultDeserializer.java @@ -37,7 +37,7 @@ public ConnectivityResult deserialize(JsonParser parser, DeserializationContext Set disconnectedElements = Collections.emptySet(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "createdSynchronousComponentCount": parser.nextToken(); createdSynchronousComponentCount = parser.getIntValue(); @@ -59,7 +59,7 @@ public ConnectivityResult deserialize(JsonParser parser, DeserializationContext disconnectedElements = JsonUtil.readSet(deserializationContext, parser, String.class); break; default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/CurrentExtensionSerializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/CurrentExtensionSerializer.java index 519d7011fe4..a71b18c5690 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/CurrentExtensionSerializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/CurrentExtensionSerializer.java @@ -53,11 +53,11 @@ public CurrentExtension deserialize(JsonParser parser, DeserializationContext de double value = Double.NaN; while (parser.nextToken() != JsonToken.END_OBJECT) { - if (parser.getCurrentName().equals("preContingencyValue")) { + if (parser.currentName().equals("preContingencyValue")) { parser.nextToken(); value = parser.readValueAs(Double.class); } else { - throw new PowsyblException("Unexpected field: " + parser.getCurrentName()); + throw new PowsyblException("Unexpected field: " + parser.currentName()); } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/LimitViolationDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/LimitViolationDeserializer.java index cad1a983aff..1425785870d 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/LimitViolationDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/LimitViolationDeserializer.java @@ -52,7 +52,7 @@ public LimitViolation deserialize(JsonParser parser, DeserializationContext dese List> extensions = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "subjectId": subjectId = parser.nextTextValue(); break; @@ -101,7 +101,7 @@ public LimitViolation deserialize(JsonParser parser, DeserializationContext dese break; default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/LimitViolationResultDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/LimitViolationResultDeserializer.java index 8d02fe2f052..e66da5186cc 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/LimitViolationResultDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/LimitViolationResultDeserializer.java @@ -34,7 +34,7 @@ public LimitViolationsResult deserialize(JsonParser parser, DeserializationConte List limitViolations = Collections.emptyList(); List actionsTaken = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "computationOk": parser.nextToken(); computationOk = parser.readValueAs(Boolean.class); @@ -51,7 +51,7 @@ public LimitViolationsResult deserialize(JsonParser parser, DeserializationConte break; default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/NetworkMetadataDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/NetworkMetadataDeserializer.java index be6eb2101f0..0d18e7e892b 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/NetworkMetadataDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/NetworkMetadataDeserializer.java @@ -47,7 +47,7 @@ public NetworkMetadata deserialize(JsonParser parser, DeserializationContext des List> extensions = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "id": id = parser.nextTextValue(); break; @@ -70,7 +70,7 @@ public NetworkMetadata deserialize(JsonParser parser, DeserializationContext des break; default: - throw new PowsyblException("Unexpected field: " + parser.getCurrentName()); + throw new PowsyblException("Unexpected field: " + parser.currentName()); } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/NetworkResultDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/NetworkResultDeserializer.java index 703ab39e4d0..a362fcc4cd2 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/NetworkResultDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/NetworkResultDeserializer.java @@ -36,7 +36,7 @@ public NetworkResult deserialize(JsonParser parser, DeserializationContext deser List busResults = null; List threeWindingsTransformerResults = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "branchResults": parser.nextToken(); branchResults = JsonUtil.readList(deserializationContext, parser, BranchResult.class); @@ -53,7 +53,7 @@ public NetworkResult deserialize(JsonParser parser, DeserializationContext deser break; default: - throw new JsonMappingException(parser, "Unexpected field: " + parser.getCurrentName()); + throw new JsonMappingException(parser, "Unexpected field: " + parser.currentName()); } } return new NetworkResult(branchResults, busResults, threeWindingsTransformerResults); diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/OperatorStrategyListDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/OperatorStrategyListDeserializer.java index b3cd46f0805..e198fd17d17 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/OperatorStrategyListDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/OperatorStrategyListDeserializer.java @@ -36,7 +36,7 @@ private static class ParsingContext { public OperatorStrategyList deserialize(JsonParser parser, DeserializationContext deserializationContext) throws IOException { ParsingContext context = new ParsingContext(); JsonUtil.parseObject(parser, fieldName -> { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version": context.version = parser.nextTextValue(); return true; diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/OperatorStrategyResultDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/OperatorStrategyResultDeserializer.java index 14d452cdd3d..d2116321b16 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/OperatorStrategyResultDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/OperatorStrategyResultDeserializer.java @@ -47,7 +47,7 @@ public OperatorStrategyResult deserialize(JsonParser parser, DeserializationCont version = SecurityAnalysisResultSerializer.VERSION; } while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "operatorStrategy": parser.nextToken(); operatorStrategy = JsonUtil.readValue(deserializationContext, parser, OperatorStrategy.class); @@ -83,7 +83,7 @@ public OperatorStrategyResult deserialize(JsonParser parser, DeserializationCont version, "1.6"); break; default: - throw new JsonMappingException(parser, "Unexpected field: " + parser.getCurrentName()); + throw new JsonMappingException(parser, "Unexpected field: " + parser.currentName()); } } if (version.compareTo("1.3") < 0) { diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/PostContingencyResultDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/PostContingencyResultDeserializer.java index 710cf0fd286..e49e68def76 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/PostContingencyResultDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/PostContingencyResultDeserializer.java @@ -51,7 +51,7 @@ public PostContingencyResult deserialize(JsonParser parser, DeserializationConte if (found) { return true; } - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "contingency": parser.nextToken(); parsingContext.contingency = JsonUtil.readValue(deserializationContext, parser, Contingency.class); diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/PreContingencyResultDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/PreContingencyResultDeserializer.java index c2fb701c75b..fda3716ac48 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/PreContingencyResultDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/PreContingencyResultDeserializer.java @@ -48,7 +48,7 @@ public PreContingencyResult deserialize(JsonParser parser, DeserializationContex if (found) { return true; } - if (parser.getCurrentName().equals("status")) { + if (parser.currentName().equals("status")) { parser.nextToken(); JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, "Tag: status", finalVersion, "1.3"); diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/SecurityAnalysisParametersDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/SecurityAnalysisParametersDeserializer.java index d77de7f055f..a30b6ef5387 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/SecurityAnalysisParametersDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/SecurityAnalysisParametersDeserializer.java @@ -43,7 +43,7 @@ public SecurityAnalysisParameters deserialize(JsonParser parser, Deserialization List> extensions = Collections.emptyList(); String version = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version": parser.nextToken(); version = parser.getValueAsString(); @@ -69,7 +69,7 @@ public SecurityAnalysisParameters deserialize(JsonParser parser, Deserialization extensions = JsonUtil.updateExtensions(parser, deserializationContext, getExtensionSerializers()::get, parameters); break; default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } extensions.forEach(extension -> parameters.addExtension((Class) extension.getClass(), extension)); diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/SecurityAnalysisResultDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/SecurityAnalysisResultDeserializer.java index 669bf30f083..5724318e2af 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/SecurityAnalysisResultDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/SecurityAnalysisResultDeserializer.java @@ -58,7 +58,7 @@ public SecurityAnalysisResult deserialize(JsonParser parser, DeserializationCont PreContingencyResult preContingencyResult = null; List operatorStrategyResults = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version": parser.nextToken(); // skip version = parser.getValueAsString(); @@ -96,7 +96,7 @@ public SecurityAnalysisResult deserialize(JsonParser parser, DeserializationCont break; default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } SecurityAnalysisResult result = null; diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/VoltageExtensionSerializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/VoltageExtensionSerializer.java index b3617a068a6..637cf03e646 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/VoltageExtensionSerializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/VoltageExtensionSerializer.java @@ -53,11 +53,11 @@ public VoltageExtension deserialize(JsonParser parser, DeserializationContext de double value = Double.NaN; while (parser.nextToken() != JsonToken.END_OBJECT) { - if (parser.getCurrentName().equals("preContingencyValue")) { + if (parser.currentName().equals("preContingencyValue")) { parser.nextToken(); value = parser.readValueAs(Float.class); } else { - throw new PowsyblException("Unexpected field: " + parser.getCurrentName()); + throw new PowsyblException("Unexpected field: " + parser.currentName()); } } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/limitreduction/LimitReductionListDeserializer.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/limitreduction/LimitReductionListDeserializer.java index 756618625c5..4a395fee59d 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/limitreduction/LimitReductionListDeserializer.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/json/limitreduction/LimitReductionListDeserializer.java @@ -35,7 +35,7 @@ private static class ParsingContext { public LimitReductionList deserialize(JsonParser parser, DeserializationContext deserializationContext) throws IOException { ParsingContext context = new ParsingContext(); JsonUtil.parseObject(parser, fieldName -> { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version": context.version = parser.nextTextValue(); return true; diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationTest.java index a6ac8deca9b..1d31636df02 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/LimitViolationTest.java @@ -14,7 +14,6 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,7 +29,7 @@ private static List getCountries(Network n, List violat .map(v -> LimitViolationHelper.getCountry(v, n)) .filter(Optional::isPresent) .map(Optional::get) - .collect(Collectors.toList()); + .toList(); } @Test @@ -62,7 +61,7 @@ void testNominalVoltages() { List expectedVoltages = Arrays.asList(380.0, 380.0, 380.0, 380.0, 380.0); List voltages = violations.stream() .map(v -> LimitViolationHelper.getNominalVoltage(v, network)) - .collect(Collectors.toList()); + .toList(); assertEquals(expectedVoltages, voltages); } @@ -74,7 +73,7 @@ void testVoltageLevelIds() { List expectedVoltageLevelIds = Arrays.asList("VLHV1", "VLHV2", "VLHV1", "VLHV2", "VLHV1"); List voltages = violations.stream() .map(v -> LimitViolationHelper.getVoltageLevelId(v, network)) - .collect(Collectors.toList()); + .toList(); assertEquals(expectedVoltageLevelIds, voltages); } diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/SecurityAnalysisParametersTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/SecurityAnalysisParametersTest.java index dd038760ee2..5ea5a353e59 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/SecurityAnalysisParametersTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/SecurityAnalysisParametersTest.java @@ -34,8 +34,8 @@ void testExtensions() { assertEquals(1, parameters.getExtensions().size()); assertTrue(parameters.getExtensions().contains(dummyExtension)); - assertTrue(parameters.getExtensionByName("dummy-extension") instanceof DummyExtension); - assertTrue(parameters.getExtension(DummyExtension.class) instanceof DummyExtension); + assertInstanceOf(DummyExtension.class, parameters.getExtensionByName("dummy-extension")); + assertInstanceOf(DummyExtension.class, parameters.getExtension(DummyExtension.class)); } @Test @@ -45,14 +45,14 @@ void testNoExtensions() { assertEquals(0, parameters.getExtensions().size()); assertFalse(parameters.getExtensions().contains(new DummyExtension())); assertFalse(parameters.getExtensionByName("dummy-extension") instanceof DummyExtension); - assertFalse(parameters.getExtension(DummyExtension.class) instanceof DummyExtension); + assertNull(parameters.getExtension(DummyExtension.class)); } @Test void testExtensionFromConfig() { SecurityAnalysisParameters parameters = SecurityAnalysisParameters.load(); assertEquals(1, parameters.getExtensions().size()); - assertTrue(parameters.getExtensionByName("dummy-extension") instanceof DummyExtension); + assertInstanceOf(DummyExtension.class, parameters.getExtensionByName("dummy-extension")); assertNotNull(parameters.getExtension(DummyExtension.class)); } diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/DistributedSecurityAnalysisTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/DistributedSecurityAnalysisTest.java index 60511fd49c2..7908229d60f 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/DistributedSecurityAnalysisTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/DistributedSecurityAnalysisTest.java @@ -11,29 +11,17 @@ import com.google.common.jimfs.Jimfs; import com.powsybl.commons.config.InMemoryPlatformConfig; import com.powsybl.commons.config.MapModuleConfig; -import com.powsybl.computation.CommandExecution; -import com.powsybl.computation.ComputationManager; -import com.powsybl.computation.ExecutionHandler; -import com.powsybl.contingency.ContingenciesProvider; -import com.powsybl.contingency.Contingency; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import static org.assertj.core.api.Assertions.assertThatNullPointerException; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; /** * @author Sylvain Leclerc {@literal } @@ -42,9 +30,6 @@ class DistributedSecurityAnalysisTest { private FileSystem fileSystem; private Path workingDir; - private ComputationManager cm = mock(ComputationManager.class); - private Network network = EurostagTutorialExample1Factory.create(); - private ContingenciesProvider contingencies = newContingenciesProvider(); @BeforeEach void setUp() throws IOException { @@ -58,39 +43,6 @@ void tearDown() throws IOException { fileSystem.close(); } - private static ContingenciesProvider newContingenciesProvider() { - return new ContingenciesProvider() { - @Override - public List getContingencies(Network network) { - return IntStream.range(1, 6) - .mapToObj(i -> new Contingency("contingency-" + i)) - .collect(Collectors.toList()); - } - - @Override - public String asScript() { - return ""; - } - }; - } - - private void checkInvocationOnExecutionHandler(Path workingDir) throws IOException { - //Capture the execution handler - ArgumentCaptor capt = ArgumentCaptor.forClass(ExecutionHandler.class); - verify(cm, times(1)).execute(any(), capt.capture()); - - //checks methods of the execution handler - List cmd = capt.getValue().before(workingDir); - assertEquals(1, cmd.size()); - assertEquals(5, cmd.get(0).getExecutionCount()); - } - - private void checkWorkingDirContent() { - assertTrue(Files.exists(workingDir.resolve("network.xiidm"))); - assertTrue(Files.exists(workingDir.resolve("contingencies.groovy"))); - assertTrue(Files.exists(workingDir.resolve("parameters.json"))); - } - /** * Checks config class. */ diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/SecurityAnalysisExecutionHandlersTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/SecurityAnalysisExecutionHandlersTest.java index 68eb5ffa654..1f671e58269 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/SecurityAnalysisExecutionHandlersTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/SecurityAnalysisExecutionHandlersTest.java @@ -309,7 +309,7 @@ void distributedAfterWithLogs() throws IOException { "security-analysis-task_1.out", "security-analysis-task_1.err"); for (String logFileName : expectedLogs) { - Files.write(workingDir.resolve(logFileName), "logs".getBytes(StandardCharsets.UTF_8)); + Files.writeString(workingDir.resolve(logFileName), "logs"); } SecurityAnalysisExecutionInput input = new SecurityAnalysisExecutionInput() @@ -331,7 +331,7 @@ void distributedAfterWithLogs() throws IOException { fail(); } catch (Exception e) { // ignored - assertTrue(e instanceof ComputationException); + assertInstanceOf(ComputationException.class, e); } try (Writer writer = Files.newBufferedWriter(workingDir.resolve("task_0_result.json"))) { @@ -368,7 +368,7 @@ void forwardedAfterWithLogs() throws IOException { "security-analysis.err"); for (String logFileName : expectedLogs) { - Files.write(workingDir.resolve(logFileName), "logs".getBytes(StandardCharsets.UTF_8)); + Files.writeString(workingDir.resolve(logFileName), "logs"); } SecurityAnalysisExecutionInput input = new SecurityAnalysisExecutionInput() diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/SubContingenciesProviderTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/SubContingenciesProviderTest.java index 508d7e5962c..101f952e9db 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/SubContingenciesProviderTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/distributed/SubContingenciesProviderTest.java @@ -17,7 +17,6 @@ import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.junit.jupiter.api.Assertions.*; @@ -32,17 +31,17 @@ class SubContingenciesProviderTest { void test() { ContingenciesProvider provider = n -> IntStream.range(1, 5) .mapToObj(i -> new Contingency("contingency-" + i)) - .collect(Collectors.toList()); + .toList(); Network network = Mockito.mock(Network.class); List subList1 = new SubContingenciesProvider(provider, new Partition(1, 2)) .getContingencies(network) - .stream().map(Contingency::getId).collect(Collectors.toList()); + .stream().map(Contingency::getId).toList(); List subList2 = new SubContingenciesProvider(provider, new Partition(2, 2)) .getContingencies(network) - .stream().map(Contingency::getId).collect(Collectors.toList()); + .stream().map(Contingency::getId).toList(); assertEquals(ImmutableList.of("contingency-1", "contingency-2"), subList1); assertEquals(ImmutableList.of("contingency-3", "contingency-4"), subList2); @@ -56,7 +55,7 @@ void testEmpty() { List subList1 = new SubContingenciesProvider(provider, new Partition(1, 1)) .getContingencies(network) - .stream().map(Contingency::getId).collect(Collectors.toList()); + .stream().map(Contingency::getId).toList(); assertEquals(Collections.emptyList(), subList1); } diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/JsonActionAndOperatorStrategyTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/JsonActionAndOperatorStrategyTest.java index c51ace76120..adba4c1067b 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/JsonActionAndOperatorStrategyTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/JsonActionAndOperatorStrategyTest.java @@ -139,7 +139,7 @@ void testJsonPlugins() throws JsonProcessingException { assertEquals(1, parsed.getActions().size()); Action parsedAction = parsed.getActions().get(0); - assertTrue(parsedAction instanceof DummyAction); + assertInstanceOf(DummyAction.class, parsedAction); assertEquals("hello", parsedAction.getId()); } } diff --git a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/JsonOperatorStrategyExtensionTest.java b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/JsonOperatorStrategyExtensionTest.java index 6cdaa30a73d..cbf6fcb3fbb 100644 --- a/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/JsonOperatorStrategyExtensionTest.java +++ b/security-analysis/security-analysis-api/src/test/java/com/powsybl/security/json/JsonOperatorStrategyExtensionTest.java @@ -139,7 +139,7 @@ public DummyExtension deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { DummyExtension dummyExtension = new DummyExtension(); while (jsonParser.nextToken() != JsonToken.END_OBJECT) { - switch (jsonParser.getCurrentName()) { + switch (jsonParser.currentName()) { case "parameterBoolean": jsonParser.nextToken(); dummyExtension.setParameterBoolean(jsonParser.getValueAsBoolean()); @@ -152,7 +152,7 @@ public DummyExtension deserialize(JsonParser jsonParser, dummyExtension.setParameterString(jsonParser.nextTextValue()); break; default: - throw new PowsyblException("Unexpected field " + jsonParser.getCurrentName()); + throw new PowsyblException("Unexpected field " + jsonParser.currentName()); } } return dummyExtension; diff --git a/security-analysis/security-analysis-default/src/test/java/com/powsybl/security/impl/SecurityAnalysisTest.java b/security-analysis/security-analysis-default/src/test/java/com/powsybl/security/impl/SecurityAnalysisTest.java index 2271f5c0e2a..7a2c6099fad 100644 --- a/security-analysis/security-analysis-default/src/test/java/com/powsybl/security/impl/SecurityAnalysisTest.java +++ b/security-analysis/security-analysis-default/src/test/java/com/powsybl/security/impl/SecurityAnalysisTest.java @@ -47,7 +47,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.Executor; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -224,7 +223,7 @@ void testStateMonitors() { // Testing all contingencies at once ContingenciesProvider contingenciesProvider = n -> n.getBranchStream() .map(b -> new Contingency(b.getId(), new BranchContingency(b.getId()))) - .collect(Collectors.toList()); + .toList(); SecurityAnalysisParameters saParameters = new SecurityAnalysisParameters(); diff --git a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityAnalysisResult.java b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityAnalysisResult.java index a2d1461b75e..e9a4e8ed1d9 100644 --- a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityAnalysisResult.java +++ b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityAnalysisResult.java @@ -118,7 +118,7 @@ public static SensitivityContingencyStatus parseJson(JsonParser parser) { } private static void parseJson(JsonParser parser, SensitivityContingencyStatus.ParsingContext context) throws IOException { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); switch (fieldName) { case "contingencyId": parser.nextToken(); diff --git a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityFactor.java b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityFactor.java index 419bcd61408..85f02f1348b 100644 --- a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityFactor.java +++ b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityFactor.java @@ -168,7 +168,7 @@ public static SensitivityFactor parseJson(JsonParser parser) { } static void parseJson(JsonParser parser, ParsingContext context) throws IOException { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); switch (fieldName) { case "functionType": context.functionType = SensitivityFunctionType.valueOf(parser.nextTextValue()); diff --git a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityValue.java b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityValue.java index d92297c721b..0ecd8cb7d08 100644 --- a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityValue.java +++ b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityValue.java @@ -105,7 +105,7 @@ public static SensitivityValue parseJson(JsonParser parser) { } private static void parseJson(JsonParser parser, ParsingContext context) throws IOException { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); switch (fieldName) { case "factorIndex": parser.nextToken(); diff --git a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityVariableSet.java b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityVariableSet.java index 261ae447fd8..5f59439c17d 100644 --- a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityVariableSet.java +++ b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/SensitivityVariableSet.java @@ -95,7 +95,7 @@ public static SensitivityVariableSet parseJson(JsonParser parser) { JsonToken token; while ((token = parser.nextToken()) != null) { if (token == JsonToken.FIELD_NAME) { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); switch (fieldName) { case "id" -> id = parser.nextTextValue(); case "variables" -> variables = WeightedSensitivityVariable.parseJson(parser); diff --git a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/WeightedSensitivityVariable.java b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/WeightedSensitivityVariable.java index 2eb7bf7838b..ee0fdb21c3c 100644 --- a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/WeightedSensitivityVariable.java +++ b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/WeightedSensitivityVariable.java @@ -80,7 +80,7 @@ public static List parseJson(JsonParser parser) { JsonToken token; while ((token = parser.nextToken()) != null) { if (token == JsonToken.FIELD_NAME) { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); switch (fieldName) { case "id" -> context.id = parser.nextTextValue(); case "weight" -> { diff --git a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/json/SensitivityAnalysisParametersDeserializer.java b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/json/SensitivityAnalysisParametersDeserializer.java index d11ac1d2666..606cd0b5e2c 100644 --- a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/json/SensitivityAnalysisParametersDeserializer.java +++ b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/json/SensitivityAnalysisParametersDeserializer.java @@ -44,7 +44,7 @@ public SensitivityAnalysisParameters deserialize(JsonParser parser, Deserializat List> extensions = Collections.emptyList(); String version = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version": parser.nextToken(); @@ -57,27 +57,27 @@ public SensitivityAnalysisParameters deserialize(JsonParser parser, Deserializat break; case "flow-flow-sensitivity-value-threshold": - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, parser.getCurrentName(), version, "1.1"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, parser.currentName(), version, "1.1"); parser.nextToken(); parameters.setFlowFlowSensitivityValueThreshold(parser.readValueAs(Double.class)); break; case "voltage-voltage-sensitivity-value-threshold": - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, parser.getCurrentName(), version, "1.1"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, parser.currentName(), version, "1.1"); parser.nextToken(); parameters.setVoltageVoltageSensitivityValueThreshold(parser.readValueAs(Double.class)); break; case "flow-voltage-sensitivity-value-threshold": - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, parser.getCurrentName(), version, "1.1"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, parser.currentName(), version, "1.1"); parser.nextToken(); parameters.setFlowVoltageSensitivityValueThreshold(parser.readValueAs(Double.class)); break; case "angle-flow-sensitivity-value-threshold": - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, parser.getCurrentName(), version, "1.1"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, parser.currentName(), version, "1.1"); parser.nextToken(); parameters.setAngleFlowSensitivityValueThreshold(parser.readValueAs(Double.class)); @@ -89,7 +89,7 @@ public SensitivityAnalysisParameters deserialize(JsonParser parser, Deserializat break; default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } extensions.forEach(extension -> parameters.addExtension((Class) extension.getClass(), extension)); diff --git a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/json/SensitivityAnalysisResultDeserializer.java b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/json/SensitivityAnalysisResultDeserializer.java index 00240b6c197..e71e2f380bc 100644 --- a/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/json/SensitivityAnalysisResultDeserializer.java +++ b/sensitivity-analysis-api/src/main/java/com/powsybl/sensitivity/json/SensitivityAnalysisResultDeserializer.java @@ -35,7 +35,7 @@ public SensitivityAnalysisResult deserialize(JsonParser parser, DeserializationC List contingencyStatus = null; List factors = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version": parser.nextToken(); // skip version = parser.getValueAsString(); @@ -56,7 +56,7 @@ public SensitivityAnalysisResult deserialize(JsonParser parser, DeserializationC contingencyStatus = JsonUtil.readList(deserializationContext, parser, SensitivityAnalysisResult.SensitivityContingencyStatus.class); break; default: - throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultDeserializer.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultDeserializer.java index 37c7a45605e..e79299e5c6e 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultDeserializer.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultDeserializer.java @@ -37,7 +37,7 @@ public Fault deserialize(JsonParser parser, DeserializationContext deserializati Fault.FaultType faultType = Fault.FaultType.THREE_PHASE; double proportionalLocation = Double.NaN; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "type" -> { parser.nextToken(); type = Fault.Type.valueOf(parser.readValueAs(String.class)); @@ -70,7 +70,7 @@ public Fault deserialize(JsonParser parser, DeserializationContext deserializati parser.nextToken(); proportionalLocation = parser.readValueAs(Double.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } if (null == type) { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultParametersDeserializer.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultParametersDeserializer.java index 728274331d2..9c1c5852b79 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultParametersDeserializer.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultParametersDeserializer.java @@ -53,7 +53,7 @@ public FaultParameters deserialize(JsonParser parser, DeserializationContext des List voltageRanges = null; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version" -> { parser.nextToken(); version = parser.getValueAsString(); @@ -91,46 +91,46 @@ public FaultParameters deserialize(JsonParser parser, DeserializationContext des minVoltageDropProportionalThreshold = parser.readValueAs(Double.class); } case "withFortescueResult" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.1"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.1"); parser.nextToken(); withFortescueResult = parser.readValueAs(Boolean.class); } case "subTransientCoefficient" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); subTransientCoefficient = parser.readValueAs(Double.class); } case "withLoads" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); withLoads = parser.readValueAs(Boolean.class); } case "withShuntCompensators" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); withShuntCompensators = parser.readValueAs(Boolean.class); } case "withVSCConverterStations" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); withVSCConverterStations = parser.readValueAs(Boolean.class); } case "withNeutralPosition" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); withNeutralPosition = parser.readValueAs(Boolean.class); } case "initialVoltageProfileMode" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); initialVoltageProfileMode = InitialVoltageProfileMode.valueOf(parser.readValueAs(String.class)); } case "voltageRanges" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); voltageRanges = JsonUtil.readList(deserializationContext, parser, VoltageRange.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } FaultParameters faultParameters = new FaultParameters(id, withLimitViolations, withVoltageAndVoltageDropProfileResult, withFeederResult, type, diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultResultDeserializer.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultResultDeserializer.java index 3c43006b664..43e4a4b0f24 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultResultDeserializer.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FaultResultDeserializer.java @@ -45,7 +45,7 @@ public List deserialize(JsonParser parser, DeserializationContext d FaultResultParameters faultResultParameters = new FaultResultParameters(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "fault" -> { parser.nextToken(); faultResultParameters.fault = JsonUtil.readValue(deserializationContext, parser, Fault.class); @@ -85,7 +85,7 @@ public List deserialize(JsonParser parser, DeserializationContext d case "shortCircuitBusResults" -> faultResultParameters.shortCircuitBusResults = new ShortCircuitBusResultsDeserializer().deserialize(parser, version); case "status" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, "Tag: " + parser.getCurrentName(), version, "1.1"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, "Tag: " + parser.currentName(), version, "1.1"); parser.nextToken(); faultResultParameters.status = FaultResult.Status.valueOf(parser.getValueAsString()); } @@ -93,7 +93,7 @@ public List deserialize(JsonParser parser, DeserializationContext d parser.nextToken(); extensions = JsonUtil.readExtensions(parser, deserializationContext, SUPPLIER.get()); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FeederResultDeserializer.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FeederResultDeserializer.java index 69b0fdbde42..ec37906a07c 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FeederResultDeserializer.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FeederResultDeserializer.java @@ -54,7 +54,7 @@ public FeederResult deserialize(JsonParser parser, DeserializationContext deseri parser.nextToken(); side = JsonUtil.readValue(deserializationContext, parser, ThreeSides.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } if (current == null) { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FortescueValuesDeserializer.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FortescueValuesDeserializer.java index fb13ab7a9e2..8ce4ca4bf33 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FortescueValuesDeserializer.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/FortescueValuesDeserializer.java @@ -35,7 +35,7 @@ public FortescueValue deserialize(JsonParser parser, DeserializationContext dese double inversePhase = Double.NaN; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "directMagnitude" -> { parser.nextToken(); directMagnitude = parser.readValueAs(Double.class); @@ -60,7 +60,7 @@ public FortescueValue deserialize(JsonParser parser, DeserializationContext dese parser.nextToken(); inversePhase = parser.readValueAs(Double.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } return new FortescueValue(directMagnitude, zeroMagnitude, inverseMagnitude, directPhase, zeroPhase, inversePhase); diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitAnalysisResultDeserializer.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitAnalysisResultDeserializer.java index acb8131c509..430477c549d 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitAnalysisResultDeserializer.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitAnalysisResultDeserializer.java @@ -50,7 +50,7 @@ public ShortCircuitAnalysisResult deserialize(JsonParser parser, Deserialization List> extensions = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version" -> { parser.nextToken(); version = parser.readValueAs(String.class); @@ -60,7 +60,7 @@ public ShortCircuitAnalysisResult deserialize(JsonParser parser, Deserialization parser.nextToken(); extensions = JsonUtil.readExtensions(parser, ctx, SUPPLIER.get()); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } ShortCircuitAnalysisResult result = new ShortCircuitAnalysisResult(faultResults); diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitBusResultsDeserializer.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitBusResultsDeserializer.java index 71be649450a..e1eebe03692 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitBusResultsDeserializer.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitBusResultsDeserializer.java @@ -38,7 +38,7 @@ public List deserialize(JsonParser parser, String versio Double voltageDropProportional = Double.NaN; while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "voltageLevelId" -> { parser.nextToken(); voltageLevelId = parser.readValueAs(String.class); @@ -66,7 +66,7 @@ public List deserialize(JsonParser parser, String versio parser.nextToken(); voltageDropProportional = parser.readValueAs(Double.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } if (voltage != null) { diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitParametersDeserializer.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitParametersDeserializer.java index 6d4ec766049..1e8ae67121e 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitParametersDeserializer.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/ShortCircuitParametersDeserializer.java @@ -44,7 +44,7 @@ public ShortCircuitParameters deserialize(JsonParser parser, DeserializationCont String version = null; List> extensions = Collections.emptyList(); while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "version" -> { parser.nextToken(); version = parser.getValueAsString(); @@ -56,12 +56,12 @@ public ShortCircuitParameters deserialize(JsonParser parser, DeserializationCont parameters.setWithLimitViolations(parser.readValueAs(Boolean.class)); } case "withVoltageMap" -> { - JsonUtil.assertLessThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.1"); + JsonUtil.assertLessThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.1"); parser.nextToken(); parameters.setWithVoltageResult(parser.readValueAs(Boolean.class)); } case "withVoltageResult" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.1"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.1"); parser.nextToken(); parameters.setWithVoltageResult(parser.readValueAs(Boolean.class)); } @@ -78,47 +78,47 @@ public ShortCircuitParameters deserialize(JsonParser parser, DeserializationCont parameters.setMinVoltageDropProportionalThreshold(parser.readValueAs(Double.class)); } case "withFortescueResult" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.1"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.1"); parser.nextToken(); parameters.setWithFortescueResult(parser.readValueAs(Boolean.class)); } case "subTransientCoefficient" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); parameters.setSubTransientCoefficient(parser.readValueAs(Double.class)); } case "withLoads" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); parameters.setWithLoads(parser.readValueAs(Boolean.class)); } case "withShuntCompensators" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); parameters.setWithShuntCompensators(parser.readValueAs(Boolean.class)); } case "withVSCConverterStations" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); parameters.setWithVSCConverterStations(parser.readValueAs(Boolean.class)); } case "withNeutralPosition" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); parameters.setWithNeutralPosition(parser.readValueAs(Boolean.class)); } case "initialVoltageProfileMode" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); parameters.setInitialVoltageProfileMode(JsonUtil.readValue(deserializationContext, parser, InitialVoltageProfileMode.class)); } case "voltageRanges" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.2"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.2"); parser.nextToken(); parameters.setVoltageRanges(JsonUtil.readList(deserializationContext, parser, VoltageRange.class)); } case "detailedReport" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.getCurrentName(), version, "1.3"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, TAG + parser.currentName(), version, "1.3"); parser.nextToken(); parameters.setDetailedReport(parser.readValueAs(Boolean.class)); } @@ -126,7 +126,7 @@ public ShortCircuitParameters deserialize(JsonParser parser, DeserializationCont parser.nextToken(); extensions = JsonUtil.updateExtensions(parser, deserializationContext, getExtensionSerializers()::get, parameters); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } extensions.forEach(extension -> parameters.addExtension((Class) extension.getClass(), extension)); diff --git a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/VoltageRangeDeserializer.java b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/VoltageRangeDeserializer.java index 6af7f6e6eb1..28c55c73a3a 100644 --- a/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/VoltageRangeDeserializer.java +++ b/shortcircuit-api/src/main/java/com/powsybl/shortcircuit/json/VoltageRangeDeserializer.java @@ -39,7 +39,7 @@ public VoltageRange deserialize(JsonParser parser, DeserializationContext contex // from the context with `context.getAttribute(ParametersDeserializationConstants.SOURCE_PARAMETER_TYPE_ATTRIBUTE)` while (parser.nextToken() != JsonToken.END_OBJECT) { - switch (parser.getCurrentName()) { + switch (parser.currentName()) { case "minimumNominalVoltage" -> { parser.nextToken(); minimumVoltage = parser.readValueAs(Double.class); @@ -53,11 +53,11 @@ public VoltageRange deserialize(JsonParser parser, DeserializationContext contex coefficient = parser.readValueAs(Double.class); } case "voltage" -> { - JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, "Tag: " + parser.getCurrentName(), version, "1.3"); + JsonUtil.assertGreaterOrEqualThanReferenceVersion(CONTEXT_NAME, "Tag: " + parser.currentName(), version, "1.3"); parser.nextToken(); voltage = parser.readValueAs(Double.class); } - default -> throw new IllegalStateException("Unexpected field: " + parser.getCurrentName()); + default -> throw new IllegalStateException("Unexpected field: " + parser.currentName()); } } diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/AbstractPoint.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/AbstractPoint.java index d0b8ea40f37..cbb54df32cd 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/AbstractPoint.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/AbstractPoint.java @@ -7,8 +7,6 @@ */ package com.powsybl.timeseries; -import java.util.Objects; - /** * @author Geoffroy Jamgotchian {@literal } */ @@ -23,7 +21,7 @@ protected AbstractPoint(int index, long time) { throw new IllegalArgumentException("Bad index value " + index); } this.index = index; - this.time = Objects.requireNonNull(time); + this.time = time; } public int getIndex() { diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/BigDoubleBuffer.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/BigDoubleBuffer.java index 331ca42ee13..d4f371fea92 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/BigDoubleBuffer.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/BigDoubleBuffer.java @@ -23,7 +23,7 @@ public class BigDoubleBuffer { private static final int BUFFER_MASK = BUFFER_SIZE_DOUBLES - 1; private static final int BUFFER_SIZE_BYTES = BUFFER_SIZE_DOUBLES * Double.BYTES; private DoubleBuffer[] buffers; - private long size; + private final long size; //To remove if we ever get it from somewhere else //package private for tests diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/BigStringBuffer.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/BigStringBuffer.java index 113d4519be8..b79c64c5373 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/BigStringBuffer.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/BigStringBuffer.java @@ -21,7 +21,7 @@ public class BigStringBuffer { private static final int BUFFER_SIZE_INTS = 1 << BUFFER_SHIFT; private static final int BUFFER_MASK = BUFFER_SIZE_INTS - 1; private CompactStringBuffer[] buffers; - private long size; + private final long size; //To remove if we ever get it from somewhere else //package private for tests diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/CalculatedTimeSeriesDslLoader.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/CalculatedTimeSeriesDslLoader.java index ff333559f85..5541cb7b9d8 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/CalculatedTimeSeriesDslLoader.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/CalculatedTimeSeriesDslLoader.java @@ -13,7 +13,6 @@ import java.util.List; import java.util.Map; import java.util.ServiceLoader; -import java.util.stream.Collectors; /** * @author Geoffroy Jamgotchian {@literal } @@ -25,7 +24,7 @@ public interface CalculatedTimeSeriesDslLoader { static CalculatedTimeSeriesDslLoader find() { List loaders = ServiceLoader.load(CalculatedTimeSeriesDslLoader.class).stream() .map(ServiceLoader.Provider::get) - .collect(Collectors.toList()); + .toList(); if (loaders.isEmpty()) { throw new PowsyblException("No calculated time series DSL loader found"); } else if (loaders.size() > 1) { diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/DataChunk.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/DataChunk.java index 8ea99f35831..309f5f5721f 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/DataChunk.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/DataChunk.java @@ -122,8 +122,7 @@ A getChunk2() { /** * Append the chunk with the one given in argument, and return the result. "This" dataChunk and the one in argument remain unchanged. * The two chunks have to be successive, i.e : this.getOffset() + this.length() = otherChunk.getOffset() - * @param otherChunk : the chunk to append with this object. It has to be the same implementation as this object. - * @return + * @param otherChunk the chunk to append with this object. It has to be the same implementation as this object. */ A append(A otherChunk); @@ -131,7 +130,6 @@ A getChunk2() { * Serialize this data chunk to json. * * @param generator a json generator (jackson) - * @throws IOException in case of json writing error */ void writeJson(JsonGenerator generator); @@ -179,8 +177,8 @@ class JsonParsingContext { } private int offset = -1; - private List doubleChunks; - private List stringChunks; + private final List doubleChunks; + private final List stringChunks; private TDoubleArrayList doubleValues; private List stringValues; private TIntArrayList stepLengths; @@ -203,7 +201,7 @@ void addStringValue(String value) { } static void parseFieldName(JsonParser parser, JsonParsingContext context) throws IOException { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); switch (fieldName) { case "offset" -> { context.offset = parser.nextIntValue(-1); @@ -226,8 +224,8 @@ static void addUncompressedChunk(JsonParsingContext context) { if (context.doubleValues != null && context.stringValues == null) { context.doubleChunks.add(new UncompressedDoubleDataChunk(context.offset, context.doubleValues.toArray())); } else if (context.stringValues != null && context.doubleValues == null) { - context.stringChunks.add(new UncompressedStringDataChunk(context.offset, context.stringValues.toArray(new String[context.stringValues.size()]))); - } else if (context.stringValues != null && context.doubleValues != null) { + context.stringChunks.add(new UncompressedStringDataChunk(context.offset, context.stringValues.toArray(new String[0]))); + } else if (context.stringValues != null) { throw new IllegalStateException("doubleValues and stringValues are not expected to be non null at the same time"); } else { throw new IllegalStateException("doubleValues and stringValues are not expected to be null at the same time"); @@ -243,12 +241,12 @@ static void addCompressedChunk(JsonParsingContext context) { context.uncompressedLength = -1; } else if (context.stringValues != null && context.doubleValues == null) { context.stringChunks.add(new CompressedStringDataChunk(context.offset, context.uncompressedLength, - context.stringValues.toArray(new String[context.stringValues.size()]), + context.stringValues.toArray(new String[0]), context.stepLengths.toArray())); context.stringValues = null; context.stepLengths = null; context.uncompressedLength = -1; - } else if (context.stringValues != null && context.doubleValues != null) { + } else if (context.stringValues != null) { throw new IllegalStateException("doubleValues and stringValues are not expected to be non null at the same time"); } else { throw new IllegalStateException("doubleValues and stringValues are not expected to be null at the same time"); diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ReadOnlyTimeSeriesStoreAggregator.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ReadOnlyTimeSeriesStoreAggregator.java index 95b6ab220b6..c6c135c64e9 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ReadOnlyTimeSeriesStoreAggregator.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ReadOnlyTimeSeriesStoreAggregator.java @@ -29,8 +29,8 @@ public ReadOnlyTimeSeriesStoreAggregator(List stores) { @Override public Set getTimeSeriesNames(TimeSeriesFilter filter) { Set timeSeriesNames = new HashSet<>(); - for (int i = 0; i < stores.size(); i++) { - timeSeriesNames.addAll(stores.get(i).getTimeSeriesNames(filter)); + for (ReadOnlyTimeSeriesStore store : stores) { + timeSeriesNames.addAll(store.getTimeSeriesNames(filter)); } return timeSeriesNames; } diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/RegularTimeSeriesIndex.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/RegularTimeSeriesIndex.java index 94004ba9ab9..a67bfd7a543 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/RegularTimeSeriesIndex.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/RegularTimeSeriesIndex.java @@ -86,7 +86,7 @@ public static RegularTimeSeriesIndex parseJson(JsonParser parser) { while ((token = parser.nextToken()) != null) { switch (token) { case FIELD_NAME -> { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); switch (fieldName) { case "startTime" -> startTime = parser.nextLongValue(-1); case "endTime" -> endTime = parser.nextLongValue(-1); diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeries.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeries.java index 5d0b4aaf021..458f8fca2ea 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeries.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeries.java @@ -304,12 +304,12 @@ void parseTokenTime(String[] tokens) { switch (timeFormat) { case DATE_TIME -> times.add(ZonedDateTime.parse(tokens[0]).toInstant().toEpochMilli()); case FRACTIONS_OF_SECOND -> { - Double time = Double.parseDouble(tokens[0]) * 1000; - times.add(time.longValue()); + double time = Double.parseDouble(tokens[0]) * 1000; + times.add((long) time); } case MILLIS -> { - Double millis = Double.parseDouble(tokens[0]); - times.add(millis.longValue()); + double millis = Double.parseDouble(tokens[0]); + times.add((long) millis); } } } @@ -321,7 +321,7 @@ void reInit() { if (dataTypes[i] == TimeSeriesDataType.DOUBLE) { ((TDoubleArrayList) values[i]).clear(); } else if (dataTypes[i] == TimeSeriesDataType.STRING) { - ((List) values[i]).clear(); + ((List) values[i]).clear(); } else { throw assertDataType(dataTypes[i]); } @@ -362,7 +362,7 @@ List createTimeSeries() { } private TimeSeriesIndex getTimeSeriesIndex() { - Long spacing = checkRegularSpacing(); + long spacing = checkRegularSpacing(); if (spacing != Long.MIN_VALUE) { return new RegularTimeSeriesIndex(times.get(0), times.get(times.size() - 1), spacing); } else { @@ -439,9 +439,9 @@ static CsvParsingContext readCsvHeader(ResultIterator static void checkCsvHeader(TimeSeriesCsvConfig timeSeriesCsvConfig, String[] tokens) { String separatorStr = Character.toString(timeSeriesCsvConfig.separator()); - if (timeSeriesCsvConfig.versioned() && (tokens.length < 3 || !"time".equals(tokens[0].toLowerCase()) || !"version".equals(tokens[1].toLowerCase()))) { + if (timeSeriesCsvConfig.versioned() && (tokens.length < 3 || !"time".equalsIgnoreCase(tokens[0]) || !"version".equalsIgnoreCase(tokens[1]))) { throw new TimeSeriesException("Bad CSV header, should be \ntime" + separatorStr + "version" + separatorStr + "..."); - } else if (tokens.length < 2 || !"time".equals(tokens[0].toLowerCase())) { + } else if (tokens.length < 2 || !"time".equalsIgnoreCase(tokens[0])) { throw new TimeSeriesException("Bad CSV header, should be \ntime" + separatorStr + "..."); } } @@ -470,11 +470,11 @@ static Map> parseCsv(BufferedReader reader, TimeSeries long timing = stopwatch.elapsed(TimeUnit.MILLISECONDS); LOGGER.info("{} time series loaded from CSV in {} ms", - timeSeriesPerVersion.entrySet().stream().mapToInt(e -> e.getValue().size()).sum(), + timeSeriesPerVersion.values().stream().mapToInt(List::size).sum(), timing); reportNode.newReportNode() .withMessageTemplate("timeseriesLoadingTime", "${tsNumber} time series loaded from CSV in ${timing} ms") - .withUntypedValue("tsNumber", timeSeriesPerVersion.entrySet().stream().mapToInt(e -> e.getValue().size()).sum()) + .withUntypedValue("tsNumber", timeSeriesPerVersion.values().stream().mapToInt(List::size).sum()) .withUntypedValue("timing", timing) .add(); @@ -543,7 +543,7 @@ static List parseJson(JsonParser parser, boolean single) { JsonToken token; while ((token = parser.nextToken()) != null) { if (token == JsonToken.FIELD_NAME) { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); switch (fieldName) { case "metadata" -> metadata = TimeSeriesMetadata.parseJson(parser); case "chunks" -> { diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeriesMetadata.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeriesMetadata.java index 95d3e69adb1..4972fe550a5 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeriesMetadata.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeriesMetadata.java @@ -97,7 +97,7 @@ public boolean isComplete() { } static void parseFieldName(JsonParser parser, JsonParsingContext context) throws IOException { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); if (context.insideTags) { context.tags.put(fieldName, parser.nextTextValue()); } else { diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeriesTable.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeriesTable.java index 1372cb10c0c..d6b595de419 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeriesTable.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/TimeSeriesTable.java @@ -55,7 +55,7 @@ public class TimeSeriesTable { private static final Logger LOGGER = LoggerFactory.getLogger(TimeSeriesTable.class); - public class Correlation { + public static class Correlation { private final String timeSeriesName1; private final String timeSeriesName2; @@ -86,7 +86,7 @@ public String toString() { } } - private class TimeSeriesNameMap { + private static class TimeSeriesNameMap { private final BiList names = new BiList<>(); @@ -119,9 +119,9 @@ void clear() { } } - private int fromVersion; + private final int fromVersion; - private int toVersion; + private final int toVersion; private List timeSeriesMetadata; @@ -254,7 +254,7 @@ private static BigDoubleBuffer createDoubleBuffer(IntFunction byteBu } private long getTimeSeriesOffset(int version, int timeSeriesNum) { - return (long) timeSeriesNum * tableIndex.getPointCount() * (toVersion - fromVersion + 1) + (version - fromVersion) * tableIndex.getPointCount(); + return (long) timeSeriesNum * tableIndex.getPointCount() * (toVersion - fromVersion + 1) + (long) (version - fromVersion) * tableIndex.getPointCount(); } private int getStatisticsIndex(int version, int timeSeriesNum) { diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/UncompressedDoubleDataChunk.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/UncompressedDoubleDataChunk.java index 3b7b8e18d58..31b17acf264 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/UncompressedDoubleDataChunk.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/UncompressedDoubleDataChunk.java @@ -167,7 +167,7 @@ protected void writeValuesJson(JsonGenerator generator) throws IOException { @Override public int hashCode() { - return Objects.hash(offset, values); + return Objects.hash(offset, Arrays.hashCode(values)); } @Override diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/UncompressedStringDataChunk.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/UncompressedStringDataChunk.java index d4cf437e469..6fad046f746 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/UncompressedStringDataChunk.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/UncompressedStringDataChunk.java @@ -107,7 +107,7 @@ public StringDataChunk tryToCompress() { return this; } } - return new CompressedStringDataChunk(offset, values.length, stepValues.toArray(new String[stepValues.size()]), + return new CompressedStringDataChunk(offset, values.length, stepValues.toArray(new String[0]), stepLengths.toArray()); } @@ -133,11 +133,10 @@ public StringDataChunk append(final StringDataChunk otherChunk) { + " and first size is " + getLength() + "; second offset should be " + (getOffset() + getLength()) + "but is " + otherChunk.getOffset()); } - if (!(otherChunk instanceof UncompressedStringDataChunk)) { + if (!(otherChunk instanceof UncompressedStringDataChunk chunk)) { throw new IllegalArgumentException("The chunks to merge have to have the same implentation. One of them is " + this.getClass() + ", the other one is " + otherChunk.getClass()); } - UncompressedStringDataChunk chunk = (UncompressedStringDataChunk) otherChunk; return new UncompressedStringDataChunk(offset, ArrayUtils.addAll(getValues(), chunk.getValues())); } @@ -182,7 +181,7 @@ protected void writeValuesJson(JsonGenerator generator) throws IOException { @Override public int hashCode() { - return Objects.hash(offset, values); + return Objects.hash(offset, Arrays.hashCode(values)); } @Override diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/AbstractMinMaxNodeCalc.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/AbstractMinMaxNodeCalc.java index 50a56a43799..c41964fa139 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/AbstractMinMaxNodeCalc.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/AbstractMinMaxNodeCalc.java @@ -43,7 +43,7 @@ protected static class ParsingContext { } static void parseFieldName(JsonParser parser, JsonToken token, ParsingContext context) throws IOException { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); if ("value".equals(fieldName)) { parser.nextValue(); context.value = parser.getValueAsDouble(); diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/BinaryOperation.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/BinaryOperation.java index dbdbdbcbb00..cc36ff38b4d 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/BinaryOperation.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/BinaryOperation.java @@ -124,7 +124,7 @@ static class ParsingContext { } static void parseFieldName(JsonParser parser, JsonToken token, ParsingContext context) throws IOException { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); if ("op".equals(fieldName)) { context.operator = Operator.valueOf(parser.nextTextValue()); } else { diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalc.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalc.java index 6682f4baea0..c51750cbc1d 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalc.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalc.java @@ -107,7 +107,7 @@ static NodeCalc parseJson(JsonParser parser, JsonToken token) throws IOException Objects.requireNonNull(parser); Objects.requireNonNull(token); if (token == JsonToken.FIELD_NAME) { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); switch (fieldName) { case IntegerNodeCalc.NAME -> { return IntegerNodeCalc.parseJson(parser); diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalcEvaluator.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalcEvaluator.java index 84b53df0db7..87fc91c43fb 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalcEvaluator.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalcEvaluator.java @@ -18,7 +18,7 @@ */ public class NodeCalcEvaluator implements NodeCalcVisitor { - class EvalContext { + static class EvalContext { DoubleMultiPoint multiPoint; Map cache; diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalcVisitors.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalcVisitors.java index b04c6bc9ba3..8fbc3d3397d 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalcVisitors.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/NodeCalcVisitors.java @@ -77,13 +77,12 @@ public static R visit(NodeCalc root, A arg, NodeCalcVisitor visitor // already allocated. // The stack have a generic type of to allow to insert a null Object // because ArrayDeque doesn't allow nulls. - Deque resultsStack = preOrderStack; while (!postOrderStack.isEmpty()) { Object nodeWrapper = postOrderStack.pop(); - R result = nodeWrapper != NULL ? ((NodeCalc) nodeWrapper).acceptHandle(visitor, arg, resultsStack) : null; - resultsStack.push(result == null ? NULL : result); + R result = nodeWrapper != NULL ? ((NodeCalc) nodeWrapper).acceptHandle(visitor, arg, preOrderStack) : null; + preOrderStack.push(result == null ? NULL : result); } - Object result = resultsStack.pop(); + Object result = preOrderStack.pop(); return result == NULL ? null : (R) result; } diff --git a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/UnaryOperation.java b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/UnaryOperation.java index 5a3991034f5..fc7fca1c139 100644 --- a/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/UnaryOperation.java +++ b/time-series/time-series-api/src/main/java/com/powsybl/timeseries/ast/UnaryOperation.java @@ -106,7 +106,7 @@ static class ParsingContext { } static void parseFieldName(JsonParser parser, JsonToken token, ParsingContext context) throws IOException { - String fieldName = parser.getCurrentName(); + String fieldName = parser.currentName(); if ("op".equals(fieldName)) { context.operator = Operator.valueOf(parser.nextTextValue()); } else { diff --git a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/DoubleDataChunkTest.java b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/DoubleDataChunkTest.java index 9a84e7485ac..dd687a2dbc7 100644 --- a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/DoubleDataChunkTest.java +++ b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/DoubleDataChunkTest.java @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -57,14 +56,14 @@ void baseTest() throws IOException { assertEquals(ImmutableList.of(new DoublePoint(1, Instant.parse("2015-01-01T00:15:00Z").toEpochMilli(), 1d), new DoublePoint(2, Instant.parse("2015-01-01T00:30:00Z").toEpochMilli(), 2d), new DoublePoint(3, Instant.parse("2015-01-01T00:45:00Z").toEpochMilli(), 3d)), - chunk.stream(index).collect(Collectors.toList())); + chunk.stream(index).toList()); } @Test void compressTest() throws IOException { UncompressedDoubleDataChunk chunk = new UncompressedDoubleDataChunk(1, new double[] {1d, 2d, 2d, 2d, 2d, 3d}); DoubleDataChunk maybeCompressedChunk = chunk.tryToCompress(); - assertTrue(maybeCompressedChunk instanceof CompressedDoubleDataChunk); + assertInstanceOf(CompressedDoubleDataChunk.class, maybeCompressedChunk); CompressedDoubleDataChunk compressedChunk = (CompressedDoubleDataChunk) maybeCompressedChunk; assertEquals(1, compressedChunk.getOffset()); assertEquals(6, compressedChunk.getLength()); @@ -101,7 +100,7 @@ void compressTest() throws IOException { assertEquals(compressedChunk, chunks.get(1)); // check base class (DataChunk) deserializer - assertTrue(objectMapper.readValue(objectMapper.writeValueAsString(chunk), DataChunk.class) instanceof DoubleDataChunk); + assertInstanceOf(DoubleDataChunk.class, objectMapper.readValue(objectMapper.writeValueAsString(chunk), DataChunk.class)); // stream test RegularTimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T01:30:00Z"), @@ -109,7 +108,7 @@ void compressTest() throws IOException { assertEquals(ImmutableList.of(new DoublePoint(1, Instant.parse("2015-01-01T00:15:00Z").toEpochMilli(), 1d), new DoublePoint(2, Instant.parse("2015-01-01T00:30:00Z").toEpochMilli(), 2d), new DoublePoint(6, Instant.parse("2015-01-01T01:30:00Z").toEpochMilli(), 3d)), - compressedChunk.stream(index).collect(Collectors.toList())); + compressedChunk.stream(index).toList()); } @Test @@ -155,10 +154,10 @@ void uncompressedSplitTest() throws IOException { assertNotNull(split.getChunk1()); assertNotNull(split.getChunk2()); assertEquals(1, split.getChunk1().getOffset()); - assertTrue(split.getChunk1() instanceof UncompressedDoubleDataChunk); + assertInstanceOf(UncompressedDoubleDataChunk.class, split.getChunk1()); assertArrayEquals(new double[] {1d}, ((UncompressedDoubleDataChunk) split.getChunk1()).getValues(), 0d); assertEquals(2, split.getChunk2().getOffset()); - assertTrue(split.getChunk2() instanceof UncompressedDoubleDataChunk); + assertInstanceOf(UncompressedDoubleDataChunk.class, split.getChunk2()); assertArrayEquals(new double[] {2d, 3d}, ((UncompressedDoubleDataChunk) split.getChunk2()).getValues(), 0d); } @@ -182,12 +181,12 @@ void compressedSplitTest() throws IOException { assertNotNull(split.getChunk1()); assertNotNull(split.getChunk2()); assertEquals(1, split.getChunk1().getOffset()); - assertTrue(split.getChunk1() instanceof CompressedDoubleDataChunk); + assertInstanceOf(CompressedDoubleDataChunk.class, split.getChunk1()); assertEquals(3, ((CompressedDoubleDataChunk) split.getChunk1()).getUncompressedLength()); assertArrayEquals(new double[] {1d, 2d}, ((CompressedDoubleDataChunk) split.getChunk1()).getStepValues(), 0d); assertArrayEquals(new int[] {2, 1}, ((CompressedDoubleDataChunk) split.getChunk1()).getStepLengths()); assertEquals(4, split.getChunk2().getOffset()); - assertTrue(split.getChunk2() instanceof CompressedDoubleDataChunk); + assertInstanceOf(CompressedDoubleDataChunk.class, split.getChunk2()); assertEquals(2, ((CompressedDoubleDataChunk) split.getChunk2()).getUncompressedLength()); assertArrayEquals(new double[] {2d}, ((CompressedDoubleDataChunk) split.getChunk2()).getStepValues(), 0d); assertArrayEquals(new int[] {2}, ((CompressedDoubleDataChunk) split.getChunk2()).getStepLengths()); @@ -202,7 +201,7 @@ void uncompressedMergeTest() { //Merge chunk1 + chunk2 DoubleDataChunk merge = chunk1.append(chunk2); assertNotNull(merge); - assertTrue(merge instanceof UncompressedDoubleDataChunk); + assertInstanceOf(UncompressedDoubleDataChunk.class, merge); assertEquals(1, merge.getOffset()); assertArrayEquals(new double[] {1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d}, ((UncompressedDoubleDataChunk) merge).getValues(), 0d); @@ -224,7 +223,7 @@ void compressedMergeTest() { //Merge chunk1 + chunk2 DoubleDataChunk merge = chunk1.append(chunk2); assertNotNull(merge); - assertTrue(merge instanceof CompressedDoubleDataChunk); + assertInstanceOf(CompressedDoubleDataChunk.class, merge); assertEquals(1, merge.getOffset()); assertEquals(10, merge.getLength()); assertArrayEquals(new double[] {1d, 2d, 3d, 4d}, ((CompressedDoubleDataChunk) merge).getStepValues(), 0d); @@ -233,7 +232,7 @@ void compressedMergeTest() { //Merge chunk2 + chunk3 merge = chunk2.append(chunk3); assertNotNull(merge); - assertTrue(merge instanceof CompressedDoubleDataChunk); + assertInstanceOf(CompressedDoubleDataChunk.class, merge); assertEquals(6, merge.getOffset()); assertEquals(8, merge.getLength()); assertArrayEquals(new double[] {3d, 4d, 5d}, ((CompressedDoubleDataChunk) merge).getStepValues(), 0d); @@ -268,7 +267,7 @@ void nanIssueTest() { }); assertEquals(1, doubleChunks.size()); assertEquals(0, stringChunks.size()); - assertTrue(doubleChunks.get(0) instanceof UncompressedDoubleDataChunk); + assertInstanceOf(UncompressedDoubleDataChunk.class, doubleChunks.get(0)); assertArrayEquals(new double[] {1d, Double.NaN, Double.NaN}, ((UncompressedDoubleDataChunk) doubleChunks.get(0)).getValues(), 0d); } } diff --git a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/IrregularTimeSeriesIndexTest.java b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/IrregularTimeSeriesIndexTest.java index d83ed7f9db5..c4856e47ae1 100644 --- a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/IrregularTimeSeriesIndexTest.java +++ b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/IrregularTimeSeriesIndexTest.java @@ -15,7 +15,6 @@ import java.time.Instant; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -37,7 +36,7 @@ void test() { assertEquals(2, index.getPointCount()); // test iterator and stream - assertEquals(instants, index.stream().collect(Collectors.toList())); + assertEquals(instants, index.stream().toList()); assertEquals(instants, Lists.newArrayList(index.iterator())); // test to string @@ -65,6 +64,6 @@ void testEquals() { @Test void testContructorError() { - assertThrows(IllegalArgumentException.class, () -> IrregularTimeSeriesIndex.create()); + assertThrows(IllegalArgumentException.class, IrregularTimeSeriesIndex::create); } } diff --git a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/RegularTimeSeriesIndexTest.java b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/RegularTimeSeriesIndexTest.java index 6427538300c..a4990ec9301 100644 --- a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/RegularTimeSeriesIndexTest.java +++ b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/RegularTimeSeriesIndexTest.java @@ -18,7 +18,6 @@ import java.time.Instant; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -46,7 +45,7 @@ void test() throws IOException { Instant.parse("2015-01-01T00:30:00Z"), Instant.parse("2015-01-01T00:45:00Z"), Instant.parse("2015-01-01T01:00:00Z")); - assertEquals(instants, index.stream().collect(Collectors.toList())); + assertEquals(instants, index.stream().toList()); assertEquals(instants, Lists.newArrayList(index.iterator())); // test to string @@ -116,8 +115,8 @@ void testPointCountRounded() { @Test void testPointCountHuge() { // 1 data every 30 seconds for ~30years, ~30years+30s, ~30years+60s - assertEquals(30 * 365 * 24 * 120 + 1, new RegularTimeSeriesIndex(0, 30L * 365 * 24 * 60 * 60 * 1000 + 0 * 30 * 1000, 30 * 1000).getPointCount()); - assertEquals(30 * 365 * 24 * 120 + 2, new RegularTimeSeriesIndex(0, 30L * 365 * 24 * 60 * 60 * 1000 + 1 * 30 * 1000, 30 * 1000).getPointCount()); + assertEquals(30 * 365 * 24 * 120 + 1, new RegularTimeSeriesIndex(0, 30L * 365 * 24 * 60 * 60 * 1000, 30 * 1000).getPointCount()); + assertEquals(30 * 365 * 24 * 120 + 2, new RegularTimeSeriesIndex(0, 30L * 365 * 24 * 60 * 60 * 1000 + 30 * 1000, 30 * 1000).getPointCount()); assertEquals(30 * 365 * 24 * 120 + 3, new RegularTimeSeriesIndex(0, 30L * 365 * 24 * 60 * 60 * 1000 + 2 * 30 * 1000, 30 * 1000).getPointCount()); } } diff --git a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/StringDataChunkTest.java b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/StringDataChunkTest.java index 6475e309c07..e7230aa53e1 100644 --- a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/StringDataChunkTest.java +++ b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/StringDataChunkTest.java @@ -20,9 +20,7 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -56,13 +54,13 @@ void baseTest() throws IOException { ObjectMapper objectMapper = JsonUtil.createObjectMapper() .registerModule(new TimeSeriesJsonModule()); - List chunks = objectMapper.readValue(objectMapper.writeValueAsString(Arrays.asList(chunk)), + List chunks = objectMapper.readValue(objectMapper.writeValueAsString(List.of(chunk)), TypeFactory.defaultInstance().constructCollectionType(List.class, StringDataChunk.class)); assertEquals(1, chunks.size()); assertEquals(chunk, chunks.get(0)); // check base class (DataChunk) deserializer - assertTrue(objectMapper.readValue(objectMapper.writeValueAsString(chunk), DataChunk.class) instanceof StringDataChunk); + assertInstanceOf(StringDataChunk.class, objectMapper.readValue(objectMapper.writeValueAsString(chunk), DataChunk.class)); // stream test RegularTimeSeriesIndex index = RegularTimeSeriesIndex.create(Interval.parse("2015-01-01T00:00:00Z/2015-01-01T00:45:00Z"), @@ -70,14 +68,14 @@ void baseTest() throws IOException { assertEquals(ImmutableList.of(new StringPoint(1, Instant.parse("2015-01-01T00:15:00Z").toEpochMilli(), "a"), new StringPoint(2, Instant.parse("2015-01-01T00:30:00Z").toEpochMilli(), "b"), new StringPoint(3, Instant.parse("2015-01-01T00:45:00Z").toEpochMilli(), "c")), - chunk.stream(index).collect(Collectors.toList())); + chunk.stream(index).toList()); } @Test void compressTest() throws IOException { UncompressedStringDataChunk chunk = new UncompressedStringDataChunk(1, new String[] {"aaa", "bbb", "bbb", "bbb", "bbb", "ccc"}); StringDataChunk maybeCompressedChunk = chunk.tryToCompress(); - assertTrue(maybeCompressedChunk instanceof CompressedStringDataChunk); + assertInstanceOf(CompressedStringDataChunk.class, maybeCompressedChunk); CompressedStringDataChunk compressedChunk = (CompressedStringDataChunk) maybeCompressedChunk; assertEquals(1, compressedChunk.getOffset()); assertEquals(6, compressedChunk.getLength()); @@ -102,7 +100,7 @@ void compressTest() throws IOException { assertEquals(ImmutableList.of(new StringPoint(1, Instant.parse("2015-01-01T00:15:00Z").toEpochMilli(), "aaa"), new StringPoint(2, Instant.parse("2015-01-01T00:30:00Z").toEpochMilli(), "bbb"), new StringPoint(6, Instant.parse("2015-01-01T01:30:00Z").toEpochMilli(), "ccc")), - compressedChunk.stream(index).collect(Collectors.toList())); + compressedChunk.stream(index).toList()); } @Test @@ -128,10 +126,10 @@ void splitTest() throws IOException { assertNotNull(split.getChunk1()); assertNotNull(split.getChunk2()); assertEquals(1, split.getChunk1().getOffset()); - assertTrue(split.getChunk1() instanceof UncompressedStringDataChunk); + assertInstanceOf(UncompressedStringDataChunk.class, split.getChunk1()); assertArrayEquals(new String[] {"a"}, ((UncompressedStringDataChunk) split.getChunk1()).getValues()); assertEquals(2, split.getChunk2().getOffset()); - assertTrue(split.getChunk2() instanceof UncompressedStringDataChunk); + assertInstanceOf(UncompressedStringDataChunk.class, split.getChunk2()); assertArrayEquals(new String[] {"b", "c"}, ((UncompressedStringDataChunk) split.getChunk2()).getValues()); } @@ -155,12 +153,12 @@ void compressedSplitTest() throws IOException { assertNotNull(split.getChunk1()); assertNotNull(split.getChunk2()); assertEquals(1, split.getChunk1().getOffset()); - assertTrue(split.getChunk1() instanceof CompressedStringDataChunk); + assertInstanceOf(CompressedStringDataChunk.class, split.getChunk1()); assertEquals(3, ((CompressedStringDataChunk) split.getChunk1()).getUncompressedLength()); assertArrayEquals(new String[] {"a", "b"}, ((CompressedStringDataChunk) split.getChunk1()).getStepValues()); assertArrayEquals(new int[] {2, 1}, ((CompressedStringDataChunk) split.getChunk1()).getStepLengths()); assertEquals(4, split.getChunk2().getOffset()); - assertTrue(split.getChunk2() instanceof CompressedStringDataChunk); + assertInstanceOf(CompressedStringDataChunk.class, split.getChunk2()); assertEquals(2, ((CompressedStringDataChunk) split.getChunk2()).getUncompressedLength()); assertArrayEquals(new String[] {"b"}, ((CompressedStringDataChunk) split.getChunk2()).getStepValues()); assertArrayEquals(new int[] {2}, ((CompressedStringDataChunk) split.getChunk2()).getStepLengths()); @@ -175,7 +173,7 @@ void uncompressedMergeTest() { //Merge chunk1 + chunk2 StringDataChunk merge = chunk1.append(chunk2); assertNotNull(merge); - assertTrue(merge instanceof UncompressedStringDataChunk); + assertInstanceOf(UncompressedStringDataChunk.class, merge); assertEquals(1, merge.getOffset()); assertArrayEquals(new String[] {"a", "b", "c", "d", "e", "f", "g"}, ((UncompressedStringDataChunk) merge).getValues()); @@ -197,7 +195,7 @@ void compressedMergeTest() { //Merge chunk1 + chunk2 StringDataChunk merge = chunk1.append(chunk2); assertNotNull(merge); - assertTrue(merge instanceof CompressedStringDataChunk); + assertInstanceOf(CompressedStringDataChunk.class, merge); assertEquals(1, merge.getOffset()); assertEquals(10, merge.getLength()); assertArrayEquals(new String[] {"a", "b", "c", "d"}, ((CompressedStringDataChunk) merge).getStepValues()); @@ -206,7 +204,7 @@ void compressedMergeTest() { //Merge chunk2 + chunk3 merge = chunk2.append(chunk3); assertNotNull(merge); - assertTrue(merge instanceof CompressedStringDataChunk); + assertInstanceOf(CompressedStringDataChunk.class, merge); assertEquals(6, merge.getOffset()); assertEquals(8, merge.getLength()); assertArrayEquals(new String[] {"c", "d", "e"}, ((CompressedStringDataChunk) merge).getStepValues()); @@ -236,7 +234,7 @@ void nullIssueTest() { }); assertEquals(0, doubleChunks.size()); assertEquals(1, stringChunks.size()); - assertTrue(stringChunks.get(0) instanceof UncompressedStringDataChunk); + assertInstanceOf(UncompressedStringDataChunk.class, stringChunks.get(0)); assertArrayEquals(new String[] {"a", null, null}, ((UncompressedStringDataChunk) stringChunks.get(0)).getValues()); } } diff --git a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/TimeSeriesTableTest.java b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/TimeSeriesTableTest.java index 73d5e4e5b7f..c5132f8e071 100644 --- a/time-series/time-series-api/src/test/java/com/powsybl/timeseries/TimeSeriesTableTest.java +++ b/time-series/time-series-api/src/test/java/com/powsybl/timeseries/TimeSeriesTableTest.java @@ -15,7 +15,6 @@ import java.io.StringReader; import java.time.ZoneId; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.stream.Collectors; @@ -175,7 +174,7 @@ public void run() { } ts = new StringTimeSeries(metadata, new UncompressedStringDataChunk(0, values)); } - table.load(1, Arrays.asList(ts)); + table.load(1, List.of(ts)); } finally { cdl.countDown(); } @@ -191,7 +190,7 @@ public void run() { Stream.of(l.split(";")) .skip(2) // date;version .collect(Collectors.toList())) - .collect(Collectors.toList()); + .toList(); List> expected = new ArrayList<>(timeSeriesLength); for (int j = 0; j < timeSeriesLength; j++) { List line = new ArrayList<>(threadCount); diff --git a/time-series/time-series-dsl/src/main/groovy/com/powsybl/timeseries/dsl/NodeCalcGroovyExtensionModule.groovy b/time-series/time-series-dsl/src/main/groovy/com/powsybl/timeseries/dsl/NodeCalcGroovyExtensionModule.groovy index e81a39eeff6..00963f9773b 100644 --- a/time-series/time-series-dsl/src/main/groovy/com/powsybl/timeseries/dsl/NodeCalcGroovyExtensionModule.groovy +++ b/time-series/time-series-dsl/src/main/groovy/com/powsybl/timeseries/dsl/NodeCalcGroovyExtensionModule.groovy @@ -17,40 +17,26 @@ class NodeCalcGroovyExtensionModule { // restore default comparison behaviour static boolean compareToNodeCalc(Object self, Object value, String op) { - switch (op) { - case ">": - return self > value - case ">=": - return self >= value - case "<": - return self < value - case "<=": - return self <= value - case "==": - return self == value - case "!=": - return self != value - default: - throw new PowsyblException("Unexpected operator: " + op) + return switch (op) { + case ">" -> self > value + case ">=" -> self >= value + case "<" -> self < value + case "<=" -> self <= value + case "==" -> self == value + case "!=" -> self != value + default -> throw new PowsyblException("Unexpected operator: " + op) } } private static NodeCalc createComparisonNode(NodeCalc left, NodeCalc right, String op) { - switch (op) { - case ">": - return BinaryOperation.greaterThan(left, right) - case ">=": - return BinaryOperation.greaterThanOrEqualsTo(left, right) - case "<": - return BinaryOperation.lessThan(left, right) - case "<=": - return BinaryOperation.lessThanOrEqualsTo(left, right) - case "==": - return BinaryOperation.equals(left, right) - case "!=": - return BinaryOperation.notEquals(left, right) - default: - throw new PowsyblException("Unexpected operator: " + op) + return switch (op) { + case ">" -> BinaryOperation.greaterThan(left, right) + case ">=" -> BinaryOperation.greaterThanOrEqualsTo(left, right) + case "<" -> BinaryOperation.lessThan(left, right) + case "<=" -> BinaryOperation.lessThanOrEqualsTo(left, right) + case "==" -> BinaryOperation.equals(left, right) + case "!=" -> BinaryOperation.notEquals(left, right) + default -> throw new PowsyblException("Unexpected operator: " + op) } } diff --git a/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PrefixNamespace.java b/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PrefixNamespace.java index 6650cd0735a..cc614f56daf 100644 --- a/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PrefixNamespace.java +++ b/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PrefixNamespace.java @@ -40,10 +40,9 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof PrefixNamespace)) { + if (!(obj instanceof PrefixNamespace other)) { return false; } - PrefixNamespace other = (PrefixNamespace) obj; return prefix.equals(other.getPrefix()) && namespace.equals(other.getNamespace()); } diff --git a/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PropertyBag.java b/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PropertyBag.java index 5a452dd4fd6..120f21e29b8 100644 --- a/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PropertyBag.java +++ b/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PropertyBag.java @@ -155,8 +155,8 @@ private String tabulate(String title, BiFunction ge // Performance : avoid using concat() -> use a StringBuilder instead. return new StringBuilder(title).append(lineSeparator).append(propertyNames.stream() - .map(n -> new StringBuilder(INDENTATION).append(String.format(format, n)).append(" : ").append(getValue.apply(this, n)).toString()) - .collect(Collectors.joining(lineSeparator))).toString(); + .map(n -> new StringBuilder(INDENTATION).append(String.format(format, n)).append(" : ").append(getValue.apply(this, n)).toString()) + .collect(Collectors.joining(lineSeparator))).toString(); } return ""; } @@ -170,7 +170,7 @@ private String extractIdentifier(String s, boolean isIdentifier) { // rdf:ID is the mRID plus an underscore added at the beginning of the string // We may decide if we want to preserve or not the underscore if (isIdentifier) { - if (removeInitialUnderscoreForIdentifiers && s1.length() > 0 && s1.charAt(0) == '_') { + if (removeInitialUnderscoreForIdentifiers && !s1.isEmpty() && s1.charAt(0) == '_') { s1 = s1.substring(1); } if (decodeEscapedIdentifiers) { @@ -190,10 +190,9 @@ public final boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof PropertyBag)) { + if (!(obj instanceof PropertyBag p)) { return false; } - PropertyBag p = (PropertyBag) obj; if (removeInitialUnderscoreForIdentifiers != p.removeInitialUnderscoreForIdentifiers) { return false; } diff --git a/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PropertyBags.java b/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PropertyBags.java index f620ebb9883..02ee950c76f 100644 --- a/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PropertyBags.java +++ b/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/PropertyBags.java @@ -32,30 +32,30 @@ public PropertyBags(Collection ps) { public List pluck(String property) { return stream() - .map(r -> r.get(property)) - .sorted(Comparator.nullsLast(String::compareTo)) - .collect(Collectors.toList()); + .map(r -> r.get(property)) + .sorted(Comparator.nullsLast(String::compareTo)) + .collect(Collectors.toList()); } public List pluckLocals(String property) { return stream() - .map(r -> r.getLocal(property)) - .sorted(Comparator.nullsLast(String::compareTo)) - .collect(Collectors.toList()); + .map(r -> r.getLocal(property)) + .sorted(Comparator.nullsLast(String::compareTo)) + .collect(Collectors.toList()); } public List pluckIdentifiers(String property) { return stream() - .map(r -> r.getId(property)) - .sorted(Comparator.nullsLast(String::compareTo)) - .collect(Collectors.toList()); + .map(r -> r.getId(property)) + .sorted(Comparator.nullsLast(String::compareTo)) + .collect(Collectors.toList()); } public PropertyBags pivot( - String idProperty, - String keyProperty, - List pivotPropertyNames, - String valueProperty) { + String idProperty, + String keyProperty, + List pivotPropertyNames, + String valueProperty) { int estimatedNumObjects = size() / pivotPropertyNames.size(); Map objects = new HashMap<>(estimatedNumObjects); List propertyNames = new ArrayList<>(pivotPropertyNames.size() + 1); @@ -76,10 +76,10 @@ public PropertyBags pivot( } public PropertyBags pivotLocalNames( - String idProperty, - String keyProperty, - List pivotPropertyLocalNames, - String valueProperty) { + String idProperty, + String keyProperty, + List pivotPropertyLocalNames, + String valueProperty) { int estimatedNumObjects = size() / pivotPropertyLocalNames.size(); Map objects = new HashMap<>(estimatedNumObjects); List propertyNames = new ArrayList<>(pivotPropertyLocalNames.size() + 1); @@ -118,11 +118,10 @@ private String tabulate(BiFunction getValue) { StringBuilder s = new StringBuilder(size() * 80); s.append(names.stream().collect(Collectors.joining(columnSeparator))); s.append(lineSeparator); - s.append(stream() - .map(r -> names.stream() - .map(n -> r.containsKey(n) ? getValue.apply(r, n) : "N/A") - .collect(Collectors.joining(columnSeparator))) - .collect(Collectors.joining(lineSeparator))); + s.append(stream().map(r -> names.stream() + .map(n -> r.containsKey(n) ? getValue.apply(r, n) : "N/A") + .collect(Collectors.joining(columnSeparator))) + .collect(Collectors.joining(lineSeparator))); return s.toString(); } } diff --git a/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/QueryCatalog.java b/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/QueryCatalog.java index 399b4768da3..72808459279 100644 --- a/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/QueryCatalog.java +++ b/triple-store/triple-store-api/src/main/java/com/powsybl/triplestore/api/QueryCatalog.java @@ -54,10 +54,9 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof QueryCatalog)) { + if (!(obj instanceof QueryCatalog q)) { return false; } - QueryCatalog q = (QueryCatalog) obj; return resource.equals(q.resource); } diff --git a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java index 68288508488..237b7e1f570 100644 --- a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java +++ b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java @@ -86,29 +86,18 @@ private static double getSusceptance(UcteElement ucteElement) { } private static boolean isFictitious(UcteElement ucteElement) { - switch (ucteElement.getStatus()) { - case EQUIVALENT_ELEMENT_IN_OPERATION: - case EQUIVALENT_ELEMENT_OUT_OF_OPERATION: - return true; - case REAL_ELEMENT_IN_OPERATION: - case REAL_ELEMENT_OUT_OF_OPERATION: - case BUSBAR_COUPLER_IN_OPERATION: - case BUSBAR_COUPLER_OUT_OF_OPERATION: - return false; - default: - throw new IllegalStateException(UNEXPECTED_UCTE_ELEMENT_STATUS + ucteElement.getStatus()); - } + return switch (ucteElement.getStatus()) { + case EQUIVALENT_ELEMENT_IN_OPERATION, EQUIVALENT_ELEMENT_OUT_OF_OPERATION -> true; + case REAL_ELEMENT_IN_OPERATION, REAL_ELEMENT_OUT_OF_OPERATION, BUSBAR_COUPLER_IN_OPERATION, + BUSBAR_COUPLER_OUT_OF_OPERATION -> false; + }; } private static boolean isFictitious(UcteNode ucteNode) { - switch (ucteNode.getStatus()) { - case EQUIVALENT: - return true; - case REAL: - return false; - default: - throw new IllegalStateException("Unexpected UcteNodeStatus value: " + ucteNode.getStatus()); - } + return switch (ucteNode.getStatus()) { + case EQUIVALENT -> true; + case REAL -> false; + }; } /** @@ -233,28 +222,13 @@ private static void createGenerator(UcteNode ucteNode, VoltageLevel voltageLevel EnergySource energySource = EnergySource.OTHER; if (ucteNode.getPowerPlantType() != null) { - switch (ucteNode.getPowerPlantType()) { - case C: - case G: - case L: - case O: - energySource = EnergySource.THERMAL; - break; - case H: - energySource = EnergySource.HYDRO; - break; - case N: - energySource = EnergySource.NUCLEAR; - break; - case W: - energySource = EnergySource.WIND; - break; - case F: - energySource = EnergySource.OTHER; - break; - default: - throw new IllegalStateException("Unexpected UctePowerPlantType value: " + ucteNode.getPowerPlantType()); - } + energySource = switch (ucteNode.getPowerPlantType()) { + case C, G, L, O -> EnergySource.THERMAL; + case H -> EnergySource.HYDRO; + case N -> EnergySource.NUCLEAR; + case W -> EnergySource.WIND; + case F -> EnergySource.OTHER; + }; } double generatorP = isValueValid(ucteNode.getActivePowerGeneration()) ? -ucteNode.getActivePowerGeneration() : 0; @@ -572,7 +546,7 @@ private static Pair getRhoAndAlpha(UcteAngleRegulation ucteAngle double rho; double alpha; switch (ucteAngleRegulation.getType()) { - case ASYM: + case ASYM -> { if (currentRatioTapChangerRho == null) { rho = 1d / Math.hypot(dy, 1d + dx); alpha = Math.toDegrees(Math.atan2(dy, 1 + dx)); @@ -582,9 +556,8 @@ private static Pair getRhoAndAlpha(UcteAngleRegulation ucteAngle rho = 1d / Math.hypot(dyEq, 1d + dxEq) / currentRatioTapChangerRho; // the formula already takes into account rhoInit, so we divide by rhoInit that will be carried by the ratio tap changer alpha = Math.toDegrees(Math.atan2(dyEq, 1 + dxEq)); } - break; - - case SYMM: + } + case SYMM -> { double dyHalf = dy / 2d; double coeff = 1d; if (currentRatioTapChangerRho != null) { @@ -594,10 +567,8 @@ private static Pair getRhoAndAlpha(UcteAngleRegulation ucteAngle double dy22 = dyHalf * dyHalf; alpha = gamma + Math.toDegrees(Math.atan2(dyHalf, 1d + dx)); // new alpha = defautAlpha/2 + gamma in case there is a ratio tap changer rho = Math.sqrt((1d + dy22) / (1d + dy22 * coeff * coeff)); - break; - - default: - throw new IllegalStateException("Unexpected UcteAngleRegulationType value: " + ucteAngleRegulation.getType()); + } + default -> throw new IllegalStateException("Unexpected UcteAngleRegulationType value: " + ucteAngleRegulation.getType()); } return Pair.of(rho, alpha); } @@ -732,24 +703,11 @@ private static TwoWindingsTransformer createXnodeTransfo(UcteNetworkExt ucteNetw } private static boolean isConnected(UcteElement ucteElement) { - boolean connected; - switch (ucteElement.getStatus()) { - case REAL_ELEMENT_IN_OPERATION: - case EQUIVALENT_ELEMENT_IN_OPERATION: - case BUSBAR_COUPLER_IN_OPERATION: - connected = true; - break; - - case REAL_ELEMENT_OUT_OF_OPERATION: - case EQUIVALENT_ELEMENT_OUT_OF_OPERATION: - case BUSBAR_COUPLER_OUT_OF_OPERATION: - connected = false; - break; - - default: - throw new IllegalStateException(UNEXPECTED_UCTE_ELEMENT_STATUS + ucteElement.getStatus()); - } - return connected; + return switch (ucteElement.getStatus()) { + case REAL_ELEMENT_IN_OPERATION, EQUIVALENT_ELEMENT_IN_OPERATION, BUSBAR_COUPLER_IN_OPERATION -> true; + case REAL_ELEMENT_OUT_OF_OPERATION, EQUIVALENT_ELEMENT_OUT_OF_OPERATION, BUSBAR_COUPLER_OUT_OF_OPERATION -> + false; + }; } private static void addTapChangers(UcteNetworkExt ucteNetwork, UcteTransformer ucteTransfo, TwoWindingsTransformer transformer, diff --git a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/UcteCountryCode.java b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/UcteCountryCode.java index ec233ee3317..67557c04dcc 100644 --- a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/UcteCountryCode.java +++ b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/UcteCountryCode.java @@ -70,46 +70,46 @@ public String getPrettyName() { } public static UcteCountryCode fromUcteCode(char ucteCode) { - switch (ucteCode) { - case 'O': return AT; - case 'A': return AL; - case 'B': return BE; - case 'V': return BG; - case 'W': return BA; - case '3': return BY; - case 'S': return CH; - case 'C': return CZ; - case 'D': return DE; - case 'K': return DK; - case 'E': return ES; - case 'F': return FR; - case '5': return GB; - case 'G': return GR; - case 'M': return HU; - case 'H': return HR; - case 'I': return IT; - case '1': return LU; - case '6': return LT; - case '2': return MA; - case '7': return MD; - case 'Y': return MK; - case '9': return NO; - case 'N': return NL; - case 'P': return PT; - case 'Z': return PL; - case 'R': return RO; - case '4': return RU; - case '8': return SE; - case 'Q': return SK; - case 'L': return SI; - case 'T': return TR; - case 'U': return UA; - case '0': return ME; - case 'J': return RS; - case '_': return KS; - case 'X': return XX; - default: throw new IllegalArgumentException("Unknown UCTE country code " + ucteCode); - } + return switch (ucteCode) { + case 'O' -> AT; + case 'A' -> AL; + case 'B' -> BE; + case 'V' -> BG; + case 'W' -> BA; + case '3' -> BY; + case 'S' -> CH; + case 'C' -> CZ; + case 'D' -> DE; + case 'K' -> DK; + case 'E' -> ES; + case 'F' -> FR; + case '5' -> GB; + case 'G' -> GR; + case 'M' -> HU; + case 'H' -> HR; + case 'I' -> IT; + case '1' -> LU; + case '6' -> LT; + case '2' -> MA; + case '7' -> MD; + case 'Y' -> MK; + case '9' -> NO; + case 'N' -> NL; + case 'P' -> PT; + case 'Z' -> PL; + case 'R' -> RO; + case '4' -> RU; + case '8' -> SE; + case 'Q' -> SK; + case 'L' -> SI; + case 'T' -> TR; + case 'U' -> UA; + case '0' -> ME; + case 'J' -> RS; + case '_' -> KS; + case 'X' -> XX; + default -> throw new IllegalArgumentException("Unknown UCTE country code " + ucteCode); + }; } public static boolean isUcteCountryCode(char character) { diff --git a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/UcteElementStatus.java b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/UcteElementStatus.java index 44c2d61e852..2f27d231048 100644 --- a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/UcteElementStatus.java +++ b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/UcteElementStatus.java @@ -54,14 +54,14 @@ public int getCode() { } public static UcteElementStatus fromCode(int code) { - switch (code) { - case 0: return REAL_ELEMENT_IN_OPERATION; - case 8: return REAL_ELEMENT_OUT_OF_OPERATION; - case 1: return EQUIVALENT_ELEMENT_IN_OPERATION; - case 9: return EQUIVALENT_ELEMENT_OUT_OF_OPERATION; - case 2: return BUSBAR_COUPLER_IN_OPERATION; - case 7: return BUSBAR_COUPLER_OUT_OF_OPERATION; - default: throw new IllegalArgumentException("Unknow element status code " + code); - } + return switch (code) { + case 0 -> REAL_ELEMENT_IN_OPERATION; + case 8 -> REAL_ELEMENT_OUT_OF_OPERATION; + case 1 -> EQUIVALENT_ELEMENT_IN_OPERATION; + case 9 -> EQUIVALENT_ELEMENT_OUT_OF_OPERATION; + case 2 -> BUSBAR_COUPLER_IN_OPERATION; + case 7 -> BUSBAR_COUPLER_OUT_OF_OPERATION; + default -> throw new IllegalArgumentException("Unknow element status code " + code); + }; } } diff --git a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/ext/UcteNetworkExt.java b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/ext/UcteNetworkExt.java index eb2f9555bd2..64f0fde352d 100644 --- a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/ext/UcteNetworkExt.java +++ b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/ext/UcteNetworkExt.java @@ -18,7 +18,6 @@ import org.slf4j.LoggerFactory; import java.util.*; -import java.util.stream.Collectors; /** * @author Geoffroy Jamgotchian {@literal } @@ -217,7 +216,7 @@ private void updateSubstation() { node2voltageLevel = new TreeMap<>(); Graph graph = createSubstationGraph(network); for (Set substationNodes : new ConnectivityInspector<>(graph).connectedSets()) { - List sortedNodes = substationNodes.stream().sorted(UcteNetworkExt::compareUcteNodeCode).collect(Collectors.toList()); + List sortedNodes = substationNodes.stream().sorted(UcteNetworkExt::compareUcteNodeCode).toList(); UcteNodeCode mainNode = sortedNodes.stream().findFirst().orElseThrow(IllegalStateException::new); Multimap nodesByVoltageLevel diff --git a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/ext/UcteSubstation.java b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/ext/UcteSubstation.java index 54592755648..01b72789976 100644 --- a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/ext/UcteSubstation.java +++ b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/ext/UcteSubstation.java @@ -8,13 +8,13 @@ package com.powsybl.ucte.network.ext; import com.powsybl.ucte.network.UcteNodeCode; + import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** - * * @author Geoffroy Jamgotchian {@literal } */ public class UcteSubstation { diff --git a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/io/UcteRecordWriter.java b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/io/UcteRecordWriter.java index 95b8caa1b76..06863098f2b 100644 --- a/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/io/UcteRecordWriter.java +++ b/ucte/ucte-network/src/main/java/com/powsybl/ucte/network/io/UcteRecordWriter.java @@ -47,9 +47,7 @@ private void initNumberFormatter() { private void resizeBuffer(int length) { int n = length - buffer.length(); if (n > 0) { - for (int i = 0; i < n; i++) { - buffer.append(" "); - } + buffer.append(" ".repeat(n)); } } diff --git a/ucte/ucte-network/src/test/java/com/powsybl/ucte/network/UcteNetworkImplTest.java b/ucte/ucte-network/src/test/java/com/powsybl/ucte/network/UcteNetworkImplTest.java index bc5189e3bf2..a64151f32e5 100644 --- a/ucte/ucte-network/src/test/java/com/powsybl/ucte/network/UcteNetworkImplTest.java +++ b/ucte/ucte-network/src/test/java/com/powsybl/ucte/network/UcteNetworkImplTest.java @@ -11,7 +11,6 @@ import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -42,7 +41,7 @@ protected void testNetwork(UcteNetwork network) { assertEquals(0, network.getComments().size()); assertEquals(3, network.getNodes().size()); - List codes = network.getNodes().stream().map(UcteNode::getCode).collect(Collectors.toList()); + List codes = network.getNodes().stream().map(UcteNode::getCode).toList(); assertTrue(codes.containsAll(Arrays.asList(code1, code2, code3))); UcteNode node = network.getNode(code1); assertEquals(1000.0, node.getActivePowerGeneration(), 0.0); From 05d2ec3f682b9ec9d31c4cfa0d14f5c238651b79 Mon Sep 17 00:00:00 2001 From: Jon Harper Date: Mon, 5 Aug 2024 15:46:31 +0200 Subject: [PATCH 41/57] Docs: fix bad edge 7-8 and bad dotted edge 5-6 and make edges follow the underlying equipment in nodeBreakerTopologyGraph.svg (#3108) Signed-off-by: HARPER Jon --- .../doc-files/nodeBreakerTopologyGraph.svg | 156 ++++++++++-------- 1 file changed, 87 insertions(+), 69 deletions(-) diff --git a/iidm/iidm-api/src/main/javadoc/com/powsybl/iidm/network/doc-files/nodeBreakerTopologyGraph.svg b/iidm/iidm-api/src/main/javadoc/com/powsybl/iidm/network/doc-files/nodeBreakerTopologyGraph.svg index 7fa4f9af692..476b33a2438 100644 --- a/iidm/iidm-api/src/main/javadoc/com/powsybl/iidm/network/doc-files/nodeBreakerTopologyGraph.svg +++ b/iidm/iidm-api/src/main/javadoc/com/powsybl/iidm/network/doc-files/nodeBreakerTopologyGraph.svg @@ -278,6 +278,12 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. sodipodi:start="1.521492" sodipodi:end="7.8009537" sodipodi:open="true" /> + + y="10.34386" + style="font-family:sans-serif" />  + BBS2 BBS2 @@ -718,6 +736,12 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. sodipodi:start="1.521492" sodipodi:end="7.8009537" sodipodi:open="true" /> + + +   + @@ -920,6 +965,12 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. id="path4284" inkscape:connector-curvature="0" /> + + + - - - - - - - - - From ce4be01a9954eb2ab602106c2f332d41c2544386 Mon Sep 17 00:00:00 2001 From: Nicolas Rol Date: Fri, 9 Aug 2024 16:53:34 +0200 Subject: [PATCH 42/57] Datasources - part 5: fixes (#3114) * check if file exist before opening stream in CgmesOnDataSource * EXTENSIONS to EXTENSION * Move the IOException catch to CgmesImport::exists(ReadOnlyDataSource) * Use parameterized unit tests on changed file CgmesOnDataSourceTest * Add IOException unit test Signed-off-by: Nicolas Rol Co-authored-by: Florian Dupuy Co-authored-by: Olivier Perrin --- .../powsybl/cgmes/conversion/CgmesImport.java | 14 ++- .../test/conformity/Cgmes3ConversionTest.java | 23 ++++ .../cgmes/model/CgmesOnDataSource.java | 14 +-- .../cgmes/model/CgmesOnDataSourceTest.java | 105 +++++++++--------- 4 files changed, 92 insertions(+), 64 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java index 01951005e83..f482ad57218 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesImport.java @@ -128,12 +128,16 @@ public List getParameters() { @Override public boolean exists(ReadOnlyDataSource ds) { CgmesOnDataSource cds = new CgmesOnDataSource(ds); - if (cds.exists()) { - return true; + try { + if (cds.exists()) { + return true; + } + // If we are configured to support CIM14, + // check if there is this CIM14 data + return IMPORT_CIM_14 && cds.existsCim14(); + } catch (IOException e) { + throw new UncheckedIOException(e); } - // If we are configured to support CIM14, - // check if there is this CIM14 data - return IMPORT_CIM_14 && cds.existsCim14(); } @Override diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/Cgmes3ConversionTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/Cgmes3ConversionTest.java index 29e12eb669e..c559cf54255 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/Cgmes3ConversionTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/conformity/Cgmes3ConversionTest.java @@ -17,12 +17,14 @@ import com.powsybl.cgmes.conversion.test.network.compare.ComparisonConfig; import com.powsybl.cgmes.model.GridModelReference; import com.powsybl.commons.datasource.ReadOnlyDataSource; +import com.powsybl.commons.datasource.ReadOnlyMemDataSource; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.LoadingLimits.TemporaryLimit; import com.powsybl.triplestore.api.TripleStoreFactory; import org.junit.jupiter.api.Test; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.Properties; import static org.junit.jupiter.api.Assertions.*; @@ -350,6 +352,27 @@ void svedalaWithAndWithoutTpSv() { assertTrue(true); } + @Test + void throwsUncheckedIOExceptionOnImport() { + CgmesImport importer = new CgmesImport(); + IOException ioException = new IOException("Error"); + ReadOnlyDataSource ds = new ReadOnlyMemDataSource() { + @Override + public String getDataExtension() { + return "xml"; // Should be equal to CgmesOnDataSource.EXTENSION + } + + @Override + public boolean exists(String fileName) throws IOException { + throw ioException; + } + }; + // When the dataSource throws an IOException during "exists" check, + // the exception should be propagated as an UncheckedIOException + UncheckedIOException exception = assertThrows(UncheckedIOException.class, () -> importer.exists(ds)); + assertEquals(ioException, exception.getCause()); + } + private Network networkModel(GridModelReference testGridModel, Conversion.Config config) { config.setConvertSvInjections(true); return ConversionUtil.networkModel(testGridModel, config); diff --git a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesOnDataSource.java b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesOnDataSource.java index 4654c1a7490..e0d2b7ce7c1 100644 --- a/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesOnDataSource.java +++ b/cgmes/cgmes-model/src/main/java/com/powsybl/cgmes/model/CgmesOnDataSource.java @@ -24,7 +24,7 @@ * @author Luma Zamarreño {@literal } */ public class CgmesOnDataSource { - private static final String EXTENSIONS = "xml"; + private static final String EXTENSION = "xml"; public CgmesOnDataSource(ReadOnlyDataSource ds) { this.dataSource = ds; @@ -34,20 +34,18 @@ public ReadOnlyDataSource dataSource() { return dataSource; } - private boolean checkIfMainFileNotWithCgmesData(boolean isCim14) { + private boolean checkIfMainFileNotWithCgmesData(boolean isCim14) throws IOException { if (dataSource.getDataExtension() == null || dataSource.getDataExtension().isEmpty()) { return false; - } else if (EXTENSIONS.equals(dataSource.getDataExtension())) { - try (InputStream is = dataSource.newInputStream(null, EXTENSIONS)) { + } else if (EXTENSION.equals(dataSource.getDataExtension()) && dataSource.exists(null, EXTENSION)) { + try (InputStream is = dataSource.newInputStream(null, EXTENSION)) { return isCim14 ? !existsNamespacesCim14(NamespaceReader.namespaces(is)) : !existsNamespaces(NamespaceReader.namespaces(is)); - } catch (IOException e) { - throw new UncheckedIOException(e); } } return true; } - public boolean exists() { + public boolean exists() throws IOException { // Check that the main file is a CGMES file if (checkIfMainFileNotWithCgmesData(false)) { return false; @@ -65,7 +63,7 @@ private boolean existsNamespaces(Set namespaces) { return namespaces.contains(CIM_16_NAMESPACE) || namespaces.contains(CIM_100_NAMESPACE); } - public boolean existsCim14() { + public boolean existsCim14() throws IOException { // Check that the main file is a CGMES file if (checkIfMainFileNotWithCgmesData(true)) { return false; diff --git a/cgmes/cgmes-model/src/test/java/com/powsybl/cgmes/model/CgmesOnDataSourceTest.java b/cgmes/cgmes-model/src/test/java/com/powsybl/cgmes/model/CgmesOnDataSourceTest.java index 3c7e4b83aac..4474e0c55ff 100644 --- a/cgmes/cgmes-model/src/test/java/com/powsybl/cgmes/model/CgmesOnDataSourceTest.java +++ b/cgmes/cgmes-model/src/test/java/com/powsybl/cgmes/model/CgmesOnDataSourceTest.java @@ -7,20 +7,50 @@ */ package com.powsybl.cgmes.model; -import org.junit.jupiter.api.Test; - +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.datasource.ResourceDataSource; import com.powsybl.commons.datasource.ResourceSet; +import com.powsybl.commons.datasource.ZipArchiveDataSource; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @author Jon Harper {@literal } */ class CgmesOnDataSourceTest { - private static void doTestExists(String filename, String cimVersion, boolean expectedExists) { + static Stream provideArguments() { + return Stream.of( + Arguments.of("EQ cim14", "empty_cim14_EQ.xml", "14", true), + Arguments.of("EQ cim16", "empty_cim16_EQ.xml", "16", true), + Arguments.of("SV cim14", "empty_cim14_SV.xml", "14", false), + Arguments.of("cim no rdf cim16", "validCim16InvalidContent_EQ.xml", "16", false), + Arguments.of("cim no rdf cim14", "validCim14InvalidContent_EQ.xml", "14", false), + Arguments.of("rdf no cim16", "validRdfInvalidContent_EQ.xml", "16", false), + Arguments.of("rdf no cim14", "validRdfInvalidContent_EQ.xml", "14", false), + Arguments.of("rdf cim16 not cim14", "empty_cim16_EQ.xml", "14", false), + Arguments.of("rdf cim14 not cim16", "empty_cim14_EQ.xml", "16", false) + ); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("provideArguments") + void testExists(String testName, String filename, String cimVersion, boolean expectedExists) throws IOException { ReadOnlyDataSource dataSource = new ResourceDataSource("incomplete", new ResourceSet("/", filename)); CgmesOnDataSource cgmesOnDataSource = new CgmesOnDataSource(dataSource); @@ -28,53 +58,26 @@ private static void doTestExists(String filename, String cimVersion, boolean exp assertEquals(expectedExists, exists); } - private static void doTestExistsEmpty(String profile, String cimVersion, boolean expectedExists) { - String filename = "empty_cim" + cimVersion + "_" + profile + ".xml"; - doTestExists(filename, cimVersion, expectedExists); - } - - @Test - void testEQcim14() { - doTestExistsEmpty("EQ", "14", true); - } - - @Test - void testEQcim16() { - doTestExistsEmpty("EQ", "16", true); - } - - @Test - void testSVcim14() { - doTestExistsEmpty("SV", "14", false); - } - - @Test - void testCimNoRdfcim16() { - doTestExists("validCim16InvalidContent_EQ.xml", "16", false); - } - - @Test - void testCimNoRdfcim14() { - doTestExists("validCim14InvalidContent_EQ.xml", "14", false); - } - - @Test - void testRdfNoCim16() { - doTestExists("validRdfInvalidContent_EQ.xml", "16", false); - } - - @Test - void testRdfNoCim14() { - doTestExists("validRdfInvalidContent_EQ.xml", "14", false); - } - - @Test - void testRdfCim16NotExistsCim14() { - doTestExists("empty_cim16_EQ.xml", "14", false); - } - @Test - void testRdfCim14NotExistsCim16() { - doTestExists("empty_cim14_EQ.xml", "16", false); + void testFileDoesNotExist() throws IOException { + Path testDir; + try (FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix())) { + testDir = fileSystem.getPath("/tmp"); + Files.createDirectories(testDir); + try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(testDir.resolve("foo.iidm.zip")))) { + try { + ZipEntry e = new ZipEntry("foo.bar"); + out.putNextEntry(e); + byte[] data = "Test String".getBytes(); + out.write(data, 0, data.length); + out.closeEntry(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + ReadOnlyDataSource dataSource = new ZipArchiveDataSource(testDir, "foo.iidm.zip", "test", "xml", null); + CgmesOnDataSource cgmesOnDataSource = new CgmesOnDataSource(dataSource); + assertFalse(cgmesOnDataSource.exists()); + } } } From d6a71fe9d94178d61af63e600bfee1a6d1d5b8fc Mon Sep 17 00:00:00 2001 From: jeandemanged Date: Wed, 21 Aug 2024 11:29:09 +0200 Subject: [PATCH 43/57] UCTE Importer: create IIDM Areas (#3097) UCTE Importer: create IIDM Areas Signed-off-by: Damien Jeandemange --- docs/grid_exchange_formats/ucte/import.md | 185 ++++++++++++------ .../powsybl/ucte/converter/UcteImporter.java | 83 +++++--- .../ucte/converter/UcteImporterTest.java | 63 +++++- 3 files changed, 244 insertions(+), 87 deletions(-) diff --git a/docs/grid_exchange_formats/ucte/import.md b/docs/grid_exchange_formats/ucte/import.md index 114a172263f..c65ec452bc0 100644 --- a/docs/grid_exchange_formats/ucte/import.md +++ b/docs/grid_exchange_formats/ucte/import.md @@ -1,7 +1,35 @@ # Import -The import of a UCTE file into an IIDM grid model is done in three steps. First, the file is parsed and a UCTE grid model is created in memory. Then, some inconsistency checks are performed on this model. Finally, the UCTE grid model is converted into an IIDM grid model. +The import of a UCTE file into an IIDM grid model is done in three steps. +First, the file is parsed and a UCTE grid model is created in memory. +Then, some inconsistency checks are performed on this model. +Finally, the UCTE grid model is converted into an IIDM grid model. -The UCTE parser provided by PowSyBl does not support the blocks `##TT` and `##E` providing respectively a special description of the two windings transformers and the scheduled active power exchange between countries. Those blocks are ignored during the parsing step. +The UCTE parser provided by PowSyBl does not support the blocks `##TT` and `##E` providing respectively a special +description of the two windings transformers and the scheduled active power exchange between countries. +Those blocks are ignored during the parsing step. + + +## Options +Parameters for the import can be defined in the configuration file in the +[import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. + +### `ucte.import.ucte.import.combine-phase-angle-regulation` +The `ucte.import.ucte.import.combine-phase-angle-regulation` property is an optional property that defines if +the ratio and phase tap changers of transformers should, when both are present, be combined. + +The default value is `false`. + +### `ucte.import.create-areas` +The `ucte.import.create-areas` property is an optional property that defines if +[areas](../../grid_model/network_subnetwork.md#area) should be created in the imported IIDM grid model. + +The default value is `true`. For more details see further below about [area conversion](#area-conversion). + +### `ucte.import.areas-dc-xnodes` +The `ucte.import.areas-dc-xnodes` property is an optional property that defines the list of X-nodes that should be +considered as [area](../../grid_model/network_subnetwork.md#area) DC boundaries. + +The default value is an empty list. For more details see further below about [area conversion](#area-conversion). ## Inconsistency checks @@ -22,102 +50,130 @@ Notations: - $Vreg$: voltage regulation, which can be enabled or disabled - $Vref$: voltage reference, in V -| Check | Consequence | -| :-------------------: | :----------------: | -| $Vreg$ enabled and $Q$ undefined | $Q = 0$ | -| $P$ undefined | $P = 0$ | -| $Q$ undefined | $Q = 0$| -| $Vreg$ enabled and $Vref$ undefined or $\in [0, 0.0001[$ | Node type = PQ | -| $minP$ undefined | $minP = -9999$ | -| $maxP$ undefined | $maxP = 9999$ | -| $minQ$ undefined | $minQ = -9999$ | -| $maxQ$ undefined | $maxQ = 9999$ | -| $minP > maxP$ | $minP$ switched with $maxP$ | -| $minQ > maxQ$ | $minQ$ switched with $maxQ$ | -| $P > maxP$ | $maxP = P$ | -| $P < minP$ | $minP = P$ | -| $Q > maxQ$ | $maxQ = Q$ | -| $Q < minQ$ | $minQ = Q$ | -| $minP = maxP$ | $maxP = 9999$ and $minP = -9999$ | -| $minQ = maxQ$ | $maxQ = 9999$ and $minQ = -9999$ | -| $maxP > 9999$ | $maxP = 9999$ | -| $minP < -9999$ | $minP = -9999$ | -| $maxQ > 9999$ | $maxQ = 9999$ | -| $minQ < -9999$ | $minQ = -9999$ | +| Check | Consequence | +|:--------------------------------------------------------:|:--------------------------------:| +| $Vreg$ enabled and $Q$ undefined | $Q = 0$ | +| $P$ undefined | $P = 0$ | +| $Q$ undefined | $Q = 0$ | +| $Vreg$ enabled and $Vref$ undefined or $\in [0, 0.0001[$ | Node type = PQ | +| $minP$ undefined | $minP = -9999$ | +| $maxP$ undefined | $maxP = 9999$ | +| $minQ$ undefined | $minQ = -9999$ | +| $maxQ$ undefined | $maxQ = 9999$ | +| $minP > maxP$ | $minP$ switched with $maxP$ | +| $minQ > maxQ$ | $minQ$ switched with $maxQ$ | +| $P > maxP$ | $maxP = P$ | +| $P < minP$ | $minP = P$ | +| $Q > maxQ$ | $maxQ = Q$ | +| $Q < minQ$ | $minQ = Q$ | +| $minP = maxP$ | $maxP = 9999$ and $minP = -9999$ | +| $minQ = maxQ$ | $maxQ = 9999$ and $minQ = -9999$ | +| $maxP > 9999$ | $maxP = 9999$ | +| $minP < -9999$ | $minP = -9999$ | +| $maxQ > 9999$ | $maxQ = 9999$ | +| $minQ < -9999$ | $minQ = -9999$ | ### Lines or two-windings transformers Notations: - $X$: reactance in $\Omega$ -| Check | Consequence | -| :-------------------: | :----------------: | -| $X \in [-0.05, 0.05]$ | $X = \pm 0.05$ | -| Current limit $<0$ | Current limit value ignored | +| Check | Consequence | +|:---------------------:|:---------------------------:| +| $X \in [-0.05, 0.05]$ | $X = \pm 0.05$ | +| Current limit $<0$ | Current limit value ignored | ### Regulations #### Phase regulation -| Check | Consequence | -| :-------------------: | :----------------: | -| Voltage value $<=0$ | Voltage value set to NaN | -| Invalid ($n = 0$) or undefined ($n$, $n'$ or $\delta U$) data provided | Regulation ignored | +| Check | Consequence | +|:----------------------------------------------------------------------:|:------------------------:| +| Voltage value $<=0$ | Voltage value set to NaN | +| Invalid ($n = 0$) or undefined ($n$, $n'$ or $\delta U$) data provided | Regulation ignored | #### Angle regulation -| Check | Consequence | -| :-------------------: | :----------------: | +| Check | Consequence | +|:----------------------------------------------------------------------:|:------------------:| | Invalid ($n = 0$) or undefined ($n$, $n'$ or $\delta U$) data provided | Regulation ignored | -| Undefined type | type set to `ASYM` | +| Undefined type | type set to `ASYM` | ## From UCTE to IIDM The UCTE file name is parsed to extract metadata required to initialize the IIDM network, such as its ID, the case date and the forecast distance. ### Node conversion -There is no equivalent [voltage level](../../grid_model/network_subnetwork.md#voltage-level) or [substation](../../grid_model/network_subnetwork.md#substation) concept in the UCTE-DEF format, so we have to create substations and voltage levels from the nodes description and the topology. Two nodes are in the same substation if they have the same geographical spot (the 1st-6th character of the node code) or are connected by a two windings transformer, a coupler or a low impedance line. Two nodes are in the same voltage level if their code only differ by the 8th character (busbars identifier). +There is no equivalent [voltage level](../../grid_model/network_subnetwork.md#voltage-level) or [substation](../../grid_model/network_subnetwork.md#substation) concept in the UCTE-DEF format, +so we have to create substations and voltage levels from the nodes description and the topology. +Two nodes are in the same substation if they have the same geographical spot (the 1st-6th character of the node code) +or are connected by a two windings transformer, a coupler or a low impedance line. +Two nodes are in the same voltage level if their code only differ by the 8th character (busbars identifier). **Note:** We do not create a substation, a voltage level or a bus for X-nodes. They are converted to [dangling lines](../../grid_model/network_subnetwork.md#dangling-line). -For nodes with a valid active or reactive load, a [load](../../grid_model/network_subnetwork.md#load) is created. It's ID is equal to the ID of the bus post-fixed by `_load`. The `P0` and `Q0` are equal to the active load and the reactive load of the UCTE node. For those with a valid generator, a [generator](../../grid_model/network_subnetwork.md#generator) is created. It's ID is equal to the ID of the bus post-fixed by `_generator`. The power plant type is converted to an [energy source]() value (see the mapping table below for the matching). +For nodes with a valid active or reactive load, a [load](../../grid_model/network_subnetwork.md#load) is created. +It's ID is equal to the ID of the bus post-fixed by `_load`. +The `P0` and `Q0` are equal to the active load and the reactive load of the UCTE node. +For those with a valid generator, a [generator](../../grid_model/network_subnetwork.md#generator) is created. It's ID is equal to the ID of the bus post-fixed by `_generator`. +The power plant type is converted to an [energy source]() value (see the mapping table below for the matching). **Mapping table for power plant types:** | UCTE Power plant type | IIDM Energy source | -| :-------------------: | :----------------: | -| Hydro (H) | Hydro | -| Nuclear (N) | Nuclear | -| Lignite (L) | Thermal | -| Hard coal (C) | Thermal | -| Gas (G) | Thermal | -| Oil (O) | Thermal | -| Wind (W) | Wind | -| Further (F) | Other | - -The list of the power plant types is more detailed than the list of available [energy source]() types in IIDM, so we add the `powerPlantType` property to each generator to keep the initial value. +|:---------------------:|:------------------:| +| Hydro (H) | Hydro | +| Nuclear (N) | Nuclear | +| Lignite (L) | Thermal | +| Hard coal (C) | Thermal | +| Gas (G) | Thermal | +| Oil (O) | Thermal | +| Wind (W) | Wind | +| Further (F) | Other | + +The list of the power plant types is more detailed than the list of available [energy source]() types in IIDM, +so we add the `powerPlantType` property to each generator to keep the initial value. ### Line conversion -The busbar couplers connecting two real nodes are converted into a [switch](../../grid_model/network_subnetwork.md#breakerswitch). This switch is open or closed depending on the status of the coupler. In the UCTE-DEF format, the coupler can have extra information not supported in IIDM we keep as properties: +The busbar couplers connecting two real nodes are converted into a [switch](../../grid_model/network_subnetwork.md#breakerswitch). This switch is open or closed depending +on the status of the coupler. In the UCTE-DEF format, the coupler can have extra information not supported in IIDM we keep +as properties: - the current limits is stored in the `currentLimit` property - the order code is stored in the `orderCode` property - the element name is stored in the `elementName` property -The lines connected between two real nodes are converted into a [AC line](../../grid_model/network_subnetwork.md#line), except if its impedance is too small (e.g. smaller than `0.05`). In that particular case, the line is considered as a busbar coupler, and a [switch](../../grid_model/network_subnetwork.md#breakerswitch) is created. The susceptance of the UCTE line is splitted in two, to initialize `B1` and `B2` with equal values. If the current limits is defined, a permanent limit is created for both ends of the line. The element name of the UCTE line is stored in the `elementName` property. +The lines connected between two real nodes are converted into a [AC line](../../grid_model/network_subnetwork.md#line), except if its impedance is too small (e.g. smaller than `0.05`). +In that particular case, the line is considered as a busbar coupler, and a [switch](../../grid_model/network_subnetwork.md#breakerswitch) is created. +The susceptance of the UCTE line is splitted in two, to initialize `B1` and `B2` with equal values. +If the current limits is defined, a permanent limit is created for both ends of the line. +The element name of the UCTE line is stored in the `elementName` property. -The lines connected between a read node and an X-node are converted into a [dangling line](../../grid_model/network_subnetwork.md#dangling-line). In IIDM, a dangling line is a line segment connected to a constant load. The sum of the active load and generation (rep. reactive) is computed to initialize the `P0` (resp. `Q0`) of the dangling line. The element name of the UCTE line is stored in the `elementName` property and the geographical name of the X-node is stored in the `geographicalName` property. +The lines connected between a read node and an X-node are converted into a [dangling line](../../grid_model/network_subnetwork.md#dangling-line). +In IIDM, a dangling line is a line segment connected to a constant load. +The sum of the active load and generation (rep. reactive) is computed to initialize the `P0` (resp. `Q0`) of the dangling line. +The element name of the UCTE line is stored in the `elementName` property and the geographical name of the X-node is stored in the `geographicalName` property. ### Two windings transformer conversion -The two windings transformers connected between two real nodes are converted into a [two windings transformer](../../grid_model/network_subnetwork.md#two-windings-transformer). If the current limits is defined, a permanent limit is created only for the second side. The element name of the transformer is stored in the `elementName` property and the nominal power is stored in the `nominalPower` property. +The two windings transformers connected between two real nodes are converted into a [two windings transformer](../../grid_model/network_subnetwork.md#two-windings-transformer). +If the current limits is defined, a permanent limit is created only for the second side. +The element name of the transformer is stored in the `elementName` property and the nominal power is stored in the `nominalPower` property. -If a two windings transformer is connected between a real node and an X-node, a fictitious intermediate voltage level is created, with a single bus called an Y-node. This new voltage level is created in the same substation than the real node. The transformer is created between the real node and the new Y-node, and the X-node is converted into a dangling line. The only difference with a classic X-node conversion, is that the electrical characteristic are hold by the transformer and set to `0` for the dangling line, except for the reactance that is set to $0.05\space\Omega$. +If a two windings transformer is connected between a real node and an X-node, a fictitious intermediate voltage level is created, +with a single bus called an Y-node. This new voltage level is created in the same substation than the real node. +The transformer is created between the real node and the new Y-node, and the X-node is converted into a dangling line. +The only difference with a classic X-node conversion, is that the electrical characteristic are hold by the transformer and set to `0` for the dangling line, +except for the reactance that is set to $0.05\space\Omega$. **TODO**: insert a schema #### Phase regulation -If a phase regulation is defined for a transformer, it is converted into a [ratio tap changer](../../grid_model/additional.md#ratio-tap-changer). If the voltage setpoint is defined, the ratio tap changer will regulate the voltage to this setpoint. The regulating terminal is assigned to the first side of the transformer. The ρ of each step is calculated according to the following formula: $\rho = 1 / (1 + i * \delta U / 100)$. +If a phase regulation is defined for a transformer, it is converted into a [ratio tap changer](../../grid_model/additional.md#ratio-tap-changer). +If the voltage setpoint is defined, the ratio tap changer will regulate the voltage to this setpoint. +The regulating terminal is assigned to the first side of the transformer. +The ρ of each step is calculated according to the following formula: $\rho = 1 / (1 + i * \delta U / 100)$. #### Angle regulation -If an angle regulation is defined for a transformer, it is converted into a [phase tap changer](../../grid_model/additional.md#phase-tap-changer), with a `FIXED_TAP` regulation mode. ρ and α of each step are calculated according to the following formulas: +If an angle regulation is defined for a transformer, it is converted into a [phase tap changer](../../grid_model/additional.md#phase-tap-changer), with a `FIXED_TAP` regulation mode. +ρ and α of each step are calculated according to the following formulas: - for an asymmetrical regulation (e.g. $\Theta \ne 90°$) $$ @@ -141,3 +197,22 @@ dy & = & step_i \times \dfrac{\delta U}{100} \times \sin(\Theta) \\[1em] $$ **Note:** The sign of $\alpha$ is changed because the phase tap changer is on side 2 in UCTE-DEF, and on side 1 in IIDM. + +### Area conversion + +When the [`ucte.import.create-areas` option](#ucteimportcreate-areas) is set to `true` (default), the importer will create +[areas](../../grid_model/network_subnetwork.md#area) in the IIDM Network. + +The areas are created on the basis of the countries present in the input UCTE-DEF file. +Each country present results in the creation of an Area in the IIDM model with: +- Area **ID**: set to the country alpha-2 code +- Area **Type**: hardcoded to `ControlArea` +- Area **VoltageLevels**: all VoltageLevels created in the Country (see [Node conversion](#node-conversion)) +- Area **Boundaries**: all DanglingLines in the Country + +By default, all Area Boundaries are flagged as AC, because the UCTE-DEF format does not have any HVDC explicit description. +In order to specify which boundaries should be considered as DC in the conversion, you may supply a list of X-nodes IDs in the +[`ucte.import.areas-dc-xnodes` option](#ucteimportareas-dc-xnodes). + +**Note:** Creating areas for German sub-regions / TSOs is not supported today (D2, D4, D7, D8). +Let us know if you have this use case by entering an issue in our GitHub. We also welcome contributions. diff --git a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java index 237b7e1f570..d936ac17bea 100644 --- a/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java +++ b/ucte/ucte-converter/src/main/java/com/powsybl/ucte/converter/UcteImporter.java @@ -35,6 +35,7 @@ import java.io.*; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static com.powsybl.ucte.converter.util.UcteConstants.*; @@ -51,13 +52,24 @@ public class UcteImporter implements Importer { private static final String[] EXTENSIONS = {"uct", "UCT"}; public static final String COMBINE_PHASE_ANGLE_REGULATION = "ucte.import.combine-phase-angle-regulation"; + public static final String CREATE_AREAS = "ucte.import.create-areas"; + public static final String AREAS_DC_XNODES = "ucte.import.areas-dc-xnodes"; public static final String UNEXPECTED_UCTE_ELEMENT_STATUS = "Unexpected UcteElementStatus value: "; public static final String X_NODE = "_XNode"; private static final Parameter COMBINE_PHASE_ANGLE_REGULATION_PARAMETER = new Parameter(COMBINE_PHASE_ANGLE_REGULATION, ParameterType.BOOLEAN, "Combine phase and angle regulation", false); - private static final List PARAMETERS = List.of(COMBINE_PHASE_ANGLE_REGULATION_PARAMETER); + private static final Parameter CREATE_AREAS_PARAMETER + = new Parameter(CREATE_AREAS, ParameterType.BOOLEAN, "Create Areas", true); + private static final Parameter AREAS_DC_XNODES_PARAMETER + = new Parameter(AREAS_DC_XNODES, ParameterType.STRING_LIST, "X-Nodes to be considered as DC when creating area boundaries", List.of()); + + private static final List PARAMETERS = List.of( + COMBINE_PHASE_ANGLE_REGULATION_PARAMETER, + CREATE_AREAS_PARAMETER, + AREAS_DC_XNODES_PARAMETER + ); private final ParameterDefaultValueConfig defaultValueConfig; @@ -447,15 +459,14 @@ private static void createLines(UcteNetworkExt ucteNetwork, Network network) { UcteVoltageLevel ucteVoltageLevel2 = ucteNetwork.getVoltageLevel(nodeCode2); switch (ucteLine.getStatus()) { - case BUSBAR_COUPLER_IN_OPERATION: - case BUSBAR_COUPLER_OUT_OF_OPERATION: + case BUSBAR_COUPLER_IN_OPERATION, BUSBAR_COUPLER_OUT_OF_OPERATION: createCoupler(ucteNetwork, network, ucteLine, nodeCode1, nodeCode2, ucteVoltageLevel1, ucteVoltageLevel2); break; - case REAL_ELEMENT_IN_OPERATION: - case REAL_ELEMENT_OUT_OF_OPERATION: - case EQUIVALENT_ELEMENT_IN_OPERATION: - case EQUIVALENT_ELEMENT_OUT_OF_OPERATION: + case REAL_ELEMENT_IN_OPERATION, + REAL_ELEMENT_OUT_OF_OPERATION, + EQUIVALENT_ELEMENT_IN_OPERATION, + EQUIVALENT_ELEMENT_OUT_OF_OPERATION: createLine(ucteNetwork, network, ucteLine, nodeCode1, nodeCode2, ucteVoltageLevel1, ucteVoltageLevel2); break; @@ -551,10 +562,9 @@ private static Pair getRhoAndAlpha(UcteAngleRegulation ucteAngle rho = 1d / Math.hypot(dy, 1d + dx); alpha = Math.toDegrees(Math.atan2(dy, 1 + dx)); } else { - double dyEq = dy; double dxEq = dx + 1 / currentRatioTapChangerRho - 1.; - rho = 1d / Math.hypot(dyEq, 1d + dxEq) / currentRatioTapChangerRho; // the formula already takes into account rhoInit, so we divide by rhoInit that will be carried by the ratio tap changer - alpha = Math.toDegrees(Math.atan2(dyEq, 1 + dxEq)); + rho = 1d / Math.hypot(dy, 1d + dxEq) / currentRatioTapChangerRho; // the formula already takes into account rhoInit, so we divide by rhoInit that will be carried by the ratio tap changer + alpha = Math.toDegrees(Math.atan2(dy, 1 + dxEq)); } } case SYMM -> { @@ -565,7 +575,7 @@ private static Pair getRhoAndAlpha(UcteAngleRegulation ucteAngle } double gamma = Math.toDegrees(Math.atan2(dyHalf * coeff, dx + 1d)); double dy22 = dyHalf * dyHalf; - alpha = gamma + Math.toDegrees(Math.atan2(dyHalf, 1d + dx)); // new alpha = defautAlpha/2 + gamma in case there is a ratio tap changer + alpha = gamma + Math.toDegrees(Math.atan2(dyHalf, 1d + dx)); // new alpha = defaultAlpha/2 + gamma in case there is a ratio tap changer rho = Math.sqrt((1d + dy22) / (1d + dy22 * coeff * coeff)); } default -> throw new IllegalStateException("Unexpected UcteAngleRegulationType value: " + ucteAngleRegulation.getType()); @@ -705,8 +715,7 @@ private static TwoWindingsTransformer createXnodeTransfo(UcteNetworkExt ucteNetw private static boolean isConnected(UcteElement ucteElement) { return switch (ucteElement.getStatus()) { case REAL_ELEMENT_IN_OPERATION, EQUIVALENT_ELEMENT_IN_OPERATION, BUSBAR_COUPLER_IN_OPERATION -> true; - case REAL_ELEMENT_OUT_OF_OPERATION, EQUIVALENT_ELEMENT_OUT_OF_OPERATION, BUSBAR_COUPLER_OUT_OF_OPERATION -> - false; + case REAL_ELEMENT_OUT_OF_OPERATION, EQUIVALENT_ELEMENT_OUT_OF_OPERATION, BUSBAR_COUPLER_OUT_OF_OPERATION -> false; }; } @@ -788,10 +797,6 @@ private static void createTransformers(UcteNetworkExt ucteNetwork, Network netwo } } - private static String getBusId(Bus bus) { - return bus != null ? bus.getId() : null; - } - private static DanglingLine getMatchingDanglingLine(DanglingLine dl1, Map> danglingLinesByPairingKey) { String otherPairingKey = dl1.getPairingKey(); List matchingDanglingLines = danglingLinesByPairingKey.get(otherPairingKey) @@ -829,7 +834,7 @@ private static void addElementNameProperty(Map properties, Dangl } } - private static void addElementNameProperty(UcteElement ucteElement, Identifiable identifiable) { + private static void addElementNameProperty(UcteElement ucteElement, Identifiable identifiable) { if (ucteElement.getElementName() != null && !ucteElement.getElementName().isEmpty()) { identifiable.setProperty(ELEMENT_NAME_PROPERTY_KEY, ucteElement.getElementName()); } @@ -841,13 +846,13 @@ private static void addCurrentLimitProperty(UcteLine ucteLine, Switch aSwitch) { } } - private static void addGeographicalNameProperty(UcteNode ucteNode, Identifiable identifiable) { + private static void addGeographicalNameProperty(UcteNode ucteNode, Identifiable identifiable) { if (ucteNode.getGeographicalName() != null) { identifiable.setProperty(GEOGRAPHICAL_NAME_PROPERTY_KEY, ucteNode.getGeographicalName()); } } - private static void addGeographicalNameProperty(UcteNetwork ucteNetwork, Map properties, DanglingLine dl1, DanglingLine dl2) { + private static void addGeographicalNameProperty(UcteNetwork ucteNetwork, Map properties, DanglingLine dl1) { Optional optUcteNodeCode = UcteNodeCode.parseUcteNodeCode(dl1.getPairingKey()); if (optUcteNodeCode.isPresent()) { @@ -869,7 +874,7 @@ private static void addNominalPowerProperty(UcteTransformer transformer, TwoWind } } - private static void addXnodeStatusProperty(UcteNode ucteNode, Identifiable identifiable) { + private static void addXnodeStatusProperty(UcteNode ucteNode, Identifiable identifiable) { identifiable.setProperty(STATUS_PROPERTY_KEY + X_NODE, ucteNode.getStatus().toString()); } @@ -879,14 +884,14 @@ private static void addXnodeStatusProperty(Map properties, Dangl private static void addDanglingLineCouplerProperty(UcteLine ucteLine, DanglingLine danglingLine) { switch (ucteLine.getStatus()) { - case BUSBAR_COUPLER_IN_OPERATION: - case BUSBAR_COUPLER_OUT_OF_OPERATION: + case BUSBAR_COUPLER_IN_OPERATION, + BUSBAR_COUPLER_OUT_OF_OPERATION: danglingLine.setProperty(IS_COUPLER_PROPERTY_KEY, "true"); break; - case REAL_ELEMENT_IN_OPERATION: - case REAL_ELEMENT_OUT_OF_OPERATION: - case EQUIVALENT_ELEMENT_IN_OPERATION: - case EQUIVALENT_ELEMENT_OUT_OF_OPERATION: + case REAL_ELEMENT_IN_OPERATION, + REAL_ELEMENT_OUT_OF_OPERATION, + EQUIVALENT_ELEMENT_IN_OPERATION, + EQUIVALENT_ELEMENT_OUT_OF_OPERATION: danglingLine.setProperty(IS_COUPLER_PROPERTY_KEY, "false"); break; } @@ -940,7 +945,7 @@ public boolean exists(ReadOnlyDataSource dataSource) { } } - private void mergeDanglingLines(UcteNetwork ucteNetwork, Network network) { + private static void mergeDanglingLines(UcteNetwork ucteNetwork, Network network) { Map> danglingLinesByPairingKey = new HashMap<>(); for (DanglingLine dl : network.getDanglingLines(DanglingLineFilter.ALL)) { danglingLinesByPairingKey.computeIfAbsent(dl.getPairingKey(), code -> new ArrayList<>()).add(dl); @@ -965,7 +970,7 @@ private void mergeDanglingLines(UcteNetwork ucteNetwork, Network network) { } } - private void createTieLine(UcteNetwork ucteNetwork, Network network, DanglingLine dlAtSideOne, DanglingLine dlAtSideTwo) { + private static void createTieLine(UcteNetwork ucteNetwork, Network network, DanglingLine dlAtSideOne, DanglingLine dlAtSideTwo) { // lexical sort to always end up with same merge line id String mergeLineId = dlAtSideOne.getId() + " + " + dlAtSideTwo.getId(); @@ -977,7 +982,7 @@ private void createTieLine(UcteNetwork ucteNetwork, Network network, DanglingLin Map properties = new HashMap<>(); addElementNameProperty(properties, dlAtSideOne, dlAtSideTwo); - addGeographicalNameProperty(ucteNetwork, properties, dlAtSideOne, dlAtSideTwo); + addGeographicalNameProperty(ucteNetwork, properties, dlAtSideOne); addXnodeStatusProperty(properties, dlAtSideOne); properties.forEach(mergeLine::setProperty); @@ -1007,6 +1012,8 @@ public Network importData(ReadOnlyDataSource dataSource, NetworkFactory networkF Stopwatch stopwatch = Stopwatch.createStarted(); boolean combinePhaseAngleRegulation = Parameter.readBoolean(getFormat(), parameters, COMBINE_PHASE_ANGLE_REGULATION_PARAMETER, defaultValueConfig); + boolean createAreas = Parameter.readBoolean(getFormat(), parameters, CREATE_AREAS_PARAMETER, defaultValueConfig); + Set areaDcXnodes = Parameter.readStringList(getFormat(), parameters, AREAS_DC_XNODES_PARAMETER, defaultValueConfig).stream().collect(Collectors.toUnmodifiableSet()); UcteNetworkExt ucteNetwork = new UcteNetworkExt(new UcteReader().read(reader, reportNode), LINE_MIN_Z); String fileName = dataSource.getBaseName(); @@ -1023,6 +1030,10 @@ public Network importData(ReadOnlyDataSource dataSource, NetworkFactory networkF mergeDanglingLines(ucteNetwork, network); + if (createAreas) { + createAreas(network, areaDcXnodes); + } + stopwatch.stop(); LOGGER.debug("UCTE import done in {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS)); @@ -1034,4 +1045,16 @@ public Network importData(ReadOnlyDataSource dataSource, NetworkFactory networkF } } + private static void createAreas(Network network, Set areaDcXnodes) { + Map countryArea = new EnumMap<>(Country.class); + network.getSubstationStream().forEach(substation -> { + var country = substation.getCountry().orElseThrow(() -> new IllegalStateException("No country set for substation '" + substation.getId() + "'")); + Area area = countryArea.computeIfAbsent(country, k -> network.newArea().setAreaType("ControlArea").setId(country.toString()).add()); + substation.getVoltageLevelStream().forEach(vl -> { + area.addVoltageLevel(vl); + vl.getDanglingLines().forEach(dl -> area.newAreaBoundary().setBoundary(dl.getBoundary()).setAc(!areaDcXnodes.contains(dl.getPairingKey())).add()); + }); + }); + } + } diff --git a/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteImporterTest.java b/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteImporterTest.java index 2f739d34710..079cf5ea9ea 100644 --- a/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteImporterTest.java +++ b/ucte/ucte-converter/src/test/java/com/powsybl/ucte/converter/UcteImporterTest.java @@ -24,6 +24,8 @@ import java.io.IOException; import java.util.List; import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -154,7 +156,9 @@ void testEmptyLastCharacterOfLineImport() { void testImportLinesDifferentNominalvoltage() { ResourceDataSource dataSource = new ResourceDataSource("differentLinesVoltage", new ResourceSet("/", "differentLinesVoltage.uct")); - IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> new UcteImporter().importData(dataSource, NetworkFactory.findDefault(), null)); + NetworkFactory networkFactory = NetworkFactory.findDefault(); + Importer ucteImporter = new UcteImporter(); + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> ucteImporter.importData(dataSource, networkFactory, null)); assertTrue(e.getMessage().contains("with two different nominal voltages")); } @@ -237,7 +241,9 @@ void importOfNetworkWithXnodesConnectedToTwoClosedLineMustSucceed() { @Test void importOfNetworkWithXnodesConnectedToMoreThanTwoClosedLineMustFail() { ResourceDataSource dataSource = new ResourceDataSource("xnodeThreeClosedLine", new ResourceSet("/", "xnodeTwoClosedLine.uct")); - assertThrows(UcteException.class, () -> new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), null)); + NetworkFactory networkFactory = new NetworkFactoryImpl(); + Importer ucteImporter = new UcteImporter(); + assertThrows(UcteException.class, () -> ucteImporter.importData(dataSource, networkFactory, null)); } @Test @@ -291,6 +297,59 @@ void lineBetweenTwoXnodesTest() { assertEquals("Line between 2 X-nodes: 'XXNODE11' and 'XXNODE12'", e.getMessage()); } + @Test + void testCreateAreas() { + ResourceDataSource dataSource = new ResourceDataSource("uxTestGridForMerging", new ResourceSet("/", "uxTestGridForMerging.uct")); + Properties parameters = new Properties(); + Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), parameters); + assertEquals(1, network.getAreaTypeCount()); + assertEquals(List.of("ControlArea"), network.getAreaTypeStream().toList()); + assertEquals(2, network.getAreaCount()); + var frArea = network.getArea("FR"); + var beArea = network.getArea("BE"); + assertNotNull(frArea); + assertNotNull(beArea); + assertEquals(2, frArea.getAreaBoundaryStream().count()); + assertNotNull(frArea.getAreaBoundary(network.getDanglingLine("FFFFFF11 XXXXXX11 1").getBoundary())); + assertNotNull(frArea.getAreaBoundary(network.getDanglingLine("FFFFFF11 XXXXXX12 1").getBoundary())); + assertEquals(2, beArea.getAreaBoundaryStream().count()); + assertNotNull(beArea.getAreaBoundary(network.getDanglingLine("BBBBBB11 XXXXXX11 1").getBoundary())); + assertNotNull(beArea.getAreaBoundary(network.getDanglingLine("BBBBBB11 XXXXXX12 1").getBoundary())); + frArea.getAreaBoundaries().forEach(ab -> assertTrue(ab.isAc())); + beArea.getAreaBoundaries().forEach(ab -> assertTrue(ab.isAc())); + + assertEquals(Set.of("FFFFFF1"), frArea.getVoltageLevelStream().map(Identifiable::getId).collect(Collectors.toUnmodifiableSet())); + assertEquals(Set.of("BBBBBB1"), beArea.getVoltageLevelStream().map(Identifiable::getId).collect(Collectors.toUnmodifiableSet())); + } + + @Test + void testCreateAreasDcXnode() { + ResourceDataSource dataSource = new ResourceDataSource("uxTestGridForMerging", new ResourceSet("/", "uxTestGridForMerging.uct")); + Properties parameters = new Properties(); + parameters.put("ucte.import.areas-dc-xnodes", "XXXXXX11"); + Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), parameters); + var frArea = network.getArea("FR"); + var beArea = network.getArea("BE"); + var frDanglingLine1 = network.getDanglingLine("FFFFFF11 XXXXXX11 1"); + var frDanglingLine2 = network.getDanglingLine("FFFFFF11 XXXXXX12 1"); + var beDanglingLine1 = network.getDanglingLine("BBBBBB11 XXXXXX11 1"); + var beDanglingLine2 = network.getDanglingLine("BBBBBB11 XXXXXX12 1"); + assertFalse(frArea.getAreaBoundary(frDanglingLine1.getBoundary()).isAc()); + assertTrue(frArea.getAreaBoundary(frDanglingLine2.getBoundary()).isAc()); + assertFalse(beArea.getAreaBoundary(beDanglingLine1.getBoundary()).isAc()); + assertTrue(beArea.getAreaBoundary(beDanglingLine2.getBoundary()).isAc()); + } + + @Test + void testDontCreateAreas() { + ResourceDataSource dataSource = new ResourceDataSource("uxTestGridForMerging", new ResourceSet("/", "uxTestGridForMerging.uct")); + Properties parameters = new Properties(); + parameters.put("ucte.import.create-areas", "false"); + Network network = new UcteImporter().importData(dataSource, new NetworkFactoryImpl(), parameters); + assertEquals(0, network.getAreaTypeCount()); + assertEquals(0, network.getAreaCount()); + } + @Test void testMetaInfos() throws IOException { try (var fs = Jimfs.newFileSystem(Configuration.unix())) { From ee9089483944f6e844dd0ef329c50d4ca29935ca Mon Sep 17 00:00:00 2001 From: Olivier Perrin Date: Wed, 28 Aug 2024 15:43:20 +0200 Subject: [PATCH 44/57] Documentation about limit reductions (#3119) Signed-off-by: Olivier Perrin Co-authored-by: Anne Tilloy --- docs/simulation/security/index.md | 20 ++++- docs/simulation/security/limit-reductions.md | 89 ++++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 docs/simulation/security/limit-reductions.md diff --git a/docs/simulation/security/index.md b/docs/simulation/security/index.md index 81642b2bd4c..34d67a7cb2f 100644 --- a/docs/simulation/security/index.md +++ b/docs/simulation/security/index.md @@ -6,14 +6,15 @@ configuration.md implementations.md contingency-dsl.md action-dsl.md +limit-reductions.md ``` The security analysis is a simulation that check violations on a network. These checks can be done on the base case or after a contingency, with or without remedial actions. A security analysis can monitor network states, in pre-contingency state, after a contingency and after a remedial action. There is a violation if the computed value is greater than the maximum allowed value. Depending on the equipments, the violations can have different types: -- Current, active power and apparent power: this kind of violations can be detected on a branch (line, two windings transformer, tie line) or on a three windings transformer, if the computed value is greater than its [permanent limit](../../grid_model/additional.md#loading-limits) or one of its [temporary limits](../../grid_model/additional.md#loading-limits). -- Voltage: this kind of violations can be detected on a bus or a bus bar section, if the computed voltage is out of the low-high voltage limits of a [voltage level](../../grid_model/network_subnetwork.md#voltage-level). -- Voltage angle: this kind of violations can be detected if the voltage angle difference between the buses associated to two terminals is out of the low-high voltage angle limits defined in the network. +- Current, active power and apparent power: this kind of violation can be detected on a branch (line, two windings transformer, tie line) or on a three windings transformer, if the computed value is greater than its [permanent limit](../../grid_model/additional.md#loading-limits) or one of its [temporary limits](../../grid_model/additional.md#loading-limits). +- Voltage: this kind of violation can be detected on a bus or a bus bar section, if the computed voltage is out of the low-high voltage limits of a [voltage level](../../grid_model/network_subnetwork.md#voltage-level). +- Voltage angle: this kind of violation can be detected if the voltage angle difference between the buses associated to two terminals is out of the low-high voltage angle limits defined in the network. ## Inputs @@ -67,6 +68,19 @@ A stateMonitor allows to get information about branch, bus and three-winding tra - If we want information about a branch in pre-contingency state, the contingencyContext will contain a null contingencyId, contextType `NONE` and the state monitor will contain the id of the branch. - If we want information about a branch in pre-contingency state and after security analysis on contingency `c1`, the contingencyContext will contain contingencyId `c1`, contextType `ALL` and the state monitor will contain the id of the branch. +### Limit reductions +Limit reductions can be specified in order to detect when a specific limit is **nearly** reached, without having to artificially modify the limit itself. +For instance, with a limit reduction set to 95% for a limit of 1000 MW, the security analysis will flag a limit violation for any value exceeding 950 MW. + +Each limit reduction has its own criteria specifying for which limits and under what conditions it should be applied. These criteria can include: +- the type of limit (current, active power or apparent power); +- the use case (for monitoring only or also for applying remedial actions); +- the contingency context (pre-contingency, after a specific contingency or after all contingencies, etc.); +- the network elements targeted by the reduction (by ids, countries and/or nominal voltages); +- which operational limits are affected by the reduction (permanent or temporary + acceptable duration). + +You can find more details about limit reductions [here](./limit-reductions). + ## Outputs ### Pre-contingency results diff --git a/docs/simulation/security/limit-reductions.md b/docs/simulation/security/limit-reductions.md new file mode 100644 index 00000000000..def6d026978 --- /dev/null +++ b/docs/simulation/security/limit-reductions.md @@ -0,0 +1,89 @@ +# Limit reductions + +## General description + +Limit reductions can be specified in order to detect when a specific limit is **nearly** reached, without having to artificially modify the limit itself. +For instance, with a limit reduction set to 95% for a limit of 1000 MW, the security analysis will flag a limit violation for any value exceeding 950 MW. + +Each limit reduction has its own criteria specifying for which limits and under what conditions it should be applied. These criteria can include: +- the type of limit (current, active power or apparent power) +- the use case: for monitoring only or also for applying remedial actions +- the contingency context (pre-contingency, after a specific contingency or after all contingencies, etc.) +- the network elements targeted by the reduction (branches, three-windings transformers, ...), which can be described by the following criteria: + - a set of their ids; + - their countries; + - their nominal voltages. +- which operational limits are affected by the reduction: + - the severity of the limit: permanent or temporary; + - and for temporary limits, their acceptable duration: + - equal to a specific value; + - inside an interval. + +These criteria can be cumulative; multiple criteria can be used simultaneously to define a limit reduction. + +Since a network operational limit may meet the criteria of several limit reductions, the order in which these reductions +are declared is important: the last one encountered when reading them from start to finish is applied. + +## Criteria details + +### Limit type + +The type of limits targeted by the reduction must be specified (mandatory item). The supported types are: +- `CURRENT`: for current limits; +- `ACTIVE_POWER`: for active power limits; +- `APPARENT_POWER`: for apparent power limits. + + +### Use cases (monitoring or action) + +The reduction may affect results: + +1. Monitoring only (`monitoringOnly` set to `true`) means that if reductions are provided in a security analysis, only +reported violations are affected. + +2. Else (`monitoringOnly` set to `false`) if reductions are provided in a security analysis, they affect not only the +reported violations but also the conditions for applying remedial actions. + + +### Contingency context + +A contingency context can be optionally specified. It contains: +- a type among: + - `ALL`: corresponding to all contingencies and pre-contingency situations; + - `NONE`: corresponding to pre-contingency situations; + - `SPECIFIC`: corresponding to a specific contingency situation; + - `ONLY_CONTINGENCIES`: corresponding to all contingency situations (without the pre-contingency one). +- and when the type is `SPECIFIC`, the id of the contingency. + +When no contingency context is present, the `ALL` policy is used. + + +### Network elements + +The network elements whose limits will be affected by the limit reductions can be selected in using several criteria: +- a set of the network elements' ids; +- one or two countries (respectively for elements with one or two substations); +- their nominal voltages, by defining an interval for each of the voltage levels. + +If no network elements is specified, the limit reduction applies to all of them. + + +### Limit duration criteria + +Duration criteria can be optionally specified. It contains: +- a type among: + - `PERMANENT`: corresponding to permanent limits only; + - `TEMPORARY`: corresponding to temporary limits only. +- and when the type is `TEMPORARY`, one of the following options to restrict them accordingly to their acceptable duration: + - `ALL`: to select all temporary limits, regardless their acceptable duration; + - `EQUALITY`: to select the temporary limits whose acceptable duration is equal to a specified value, with: + - `value`: the said value; + - `INTERVAL`: to select the temporary limits whose acceptable duration is within an interval, with: + - `lowBound` and `highBound`: minimum and maximum duration, each can be null; + - `lowClosed` and `highClosed`: to indicate if the interval is open (`false`) or closed (`true`) on respectively the lower and the upper boundaries. + This attribute is facultative if the corresponding bound value is `null`. + +When no duration criteria are present, the reduction is applied to all permanent and temporary limits. + +When several duration criteria are specified, the limit reductions applies to each one. +For instance, if both criteria `PERMANENT` and (`TEMPORARY` ; `EQUALITY`: 600) are defined, the limit reduction will apply to permanent limits and 600 s limits. From 73baf587633da0354931ed63a5e986215d2504bb Mon Sep 17 00:00:00 2001 From: Naledi <151443525+nao1345678@users.noreply.github.com> Date: Wed, 28 Aug 2024 16:13:28 +0200 Subject: [PATCH 45/57] Create setters for temporary limits (#3113) Create setters for temporary limits (#3113) Signed-off-by: Naledi EL CHEIKH --- .../powsybl/iidm/network/LoadingLimits.java | 11 ++++ .../network/impl/AbstractLoadingLimits.java | 61 +++++++++++++++++-- .../impl/AbstractLoadingLimitsAdder.java | 1 + .../impl/ActivePowerLimitsAdderImpl.java | 1 + .../impl/ApparentPowerLimitsAdderImpl.java | 1 + .../iidm/network/impl/CurrentLimitsImpl.java | 1 + .../impl/OperationalLimitsGroupImpl.java | 9 +++ .../tck/AbstractCurrentLimitsTest.java | 44 +++++++++++++ .../result/AbstractReducedLoadingLimits.java | 5 ++ 9 files changed, 130 insertions(+), 4 deletions(-) diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/LoadingLimits.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/LoadingLimits.java index 889de9d9c86..9396132499b 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/LoadingLimits.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/LoadingLimits.java @@ -44,6 +44,7 @@ interface TemporaryLimit { * @return false if it is a real limit, false otherwise */ boolean isFictitious(); + } /** @@ -80,4 +81,14 @@ interface TemporaryLimit { * @return the temporary limit value or NaN if there is no temporary limit for this acceptable duration */ double getTemporaryLimitValue(int acceptableDuration); + + /** + * Set the temporary limit value. + *

Throws an exception when no temporary limit of the given acceptable duration is found, + * and changes the value but logs a warning when the new value is not valid.

+ * @param acceptableDuration the acceptable duration + * @param temporaryLimitValue the temporary limit value + * @return itself for method chaining + */ + LoadingLimits setTemporaryLimitValue(int acceptableDuration, double temporaryLimitValue); } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimits.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimits.java index fb5ff1a9151..22a8d619e4e 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimits.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimits.java @@ -8,17 +8,19 @@ package com.powsybl.iidm.network.impl; import com.powsybl.iidm.network.LoadingLimits; +import com.powsybl.iidm.network.ValidationException; import com.powsybl.iidm.network.ValidationUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.Collection; -import java.util.Objects; -import java.util.TreeMap; +import java.util.*; /** * @author Miora Ralambotiana {@literal } */ abstract class AbstractLoadingLimits> implements LoadingLimits { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLoadingLimits.class); protected final OperationalLimitsGroupImpl group; private double permanentLimit; private final TreeMap temporaryLimits; @@ -27,7 +29,7 @@ static class TemporaryLimitImpl implements TemporaryLimit { private final String name; - private final double value; + private double value; private final int acceptableDuration; @@ -85,6 +87,57 @@ public L setPermanentLimit(double permanentLimit) { return (L) this; } + @Override + public L setTemporaryLimitValue(int acceptableDuration, double temporaryLimitValue) { + if (temporaryLimitValue < 0 || Double.isNaN(temporaryLimitValue)) { + throw new ValidationException(group.getValidable(), "Temporary limit value must be a positive double"); + } + + // Identify the limit that needs to be modified + TemporaryLimit identifiedLimit = getTemporaryLimit(acceptableDuration); + if (identifiedLimit == null) { + throw new ValidationException(group.getValidable(), "No temporary limit found for the given acceptable duration"); + } + + TreeMap temporaryLimitTreeMap = new TreeMap<>(this.temporaryLimits); + // Creation of index markers + Map.Entry biggerDurationEntry = temporaryLimitTreeMap.lowerEntry(acceptableDuration); + Map.Entry smallerDurationEntry = temporaryLimitTreeMap.higherEntry(acceptableDuration); + + double oldValue = identifiedLimit.getValue(); + + if (isTemporaryLimitValueValid(biggerDurationEntry, smallerDurationEntry, acceptableDuration, temporaryLimitValue)) { + LOGGER.info("{}Temporary limit value changed from {} to {}", group.getValidable().getMessageHeader(), oldValue, temporaryLimitValue); + } else { + LOGGER.warn("{}Temporary limit value changed from {} to {}, but it is not valid", group.getValidable().getMessageHeader(), oldValue, temporaryLimitValue); + } + + this.temporaryLimits.put(acceptableDuration, new TemporaryLimitImpl(identifiedLimit.getName(), temporaryLimitValue, + identifiedLimit.getAcceptableDuration(), identifiedLimit.isFictitious())); + + group.notifyTemporaryLimitValueUpdate(getLimitType(), oldValue, temporaryLimitValue, acceptableDuration); + + return (L) this; + } + + protected boolean isTemporaryLimitValueValid(Map.Entry biggerDurationEntry, + Map.Entry smallerDurationEntry, + int acceptableDuration, + double temporaryLimitValue) { + + boolean checkAgainstBigger = true; + boolean checkAgainstSmaller = true; + if (biggerDurationEntry != null) { + checkAgainstBigger = biggerDurationEntry.getValue().getAcceptableDuration() > acceptableDuration + && biggerDurationEntry.getValue().getValue() < temporaryLimitValue; + } + if (smallerDurationEntry != null) { + checkAgainstSmaller = smallerDurationEntry.getValue().getAcceptableDuration() < acceptableDuration + && smallerDurationEntry.getValue().getValue() > temporaryLimitValue; + } + return temporaryLimitValue > this.permanentLimit && checkAgainstBigger && checkAgainstSmaller; + } + @Override public Collection getTemporaryLimits() { return temporaryLimits.values(); diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java index e8ea74fda2b..10dc96e8e8e 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import java.util.*; + import static java.lang.Integer.MAX_VALUE; /** diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ActivePowerLimitsAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ActivePowerLimitsAdderImpl.java index ebf81e2febb..2bfe2776ac4 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ActivePowerLimitsAdderImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ActivePowerLimitsAdderImpl.java @@ -37,4 +37,5 @@ public ActivePowerLimits add() { group.setActivePowerLimits(limits); return limits; } + } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ApparentPowerLimitsAdderImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ApparentPowerLimitsAdderImpl.java index c4b309c9886..1fcb0d74132 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ApparentPowerLimitsAdderImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/ApparentPowerLimitsAdderImpl.java @@ -40,4 +40,5 @@ public ApparentPowerLimits add() { group.setApparentPowerLimits(limits); return limits; } + } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/CurrentLimitsImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/CurrentLimitsImpl.java index 02caf7a0420..3e3146452b5 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/CurrentLimitsImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/CurrentLimitsImpl.java @@ -25,4 +25,5 @@ public class CurrentLimitsImpl extends AbstractLoadingLimits public void remove() { group.removeCurrentLimits(); } + } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/OperationalLimitsGroupImpl.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/OperationalLimitsGroupImpl.java index a22a5ffea32..66e910eb2b5 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/OperationalLimitsGroupImpl.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/OperationalLimitsGroupImpl.java @@ -130,6 +130,12 @@ private void notifyUpdate(LimitType limitType, OperationalLimits oldValue, Opera doNotify(attributeName + "_" + limitType, oldOperationalLimitsInfo, newOperationalLimitsInfo); } + public void notifyTemporaryLimitValueUpdate(LimitType limitType, double oldValue, double newValue, int acceptableDuration) { + TemporaryLimitInfo oldTemporaryLimitInfo = new TemporaryLimitInfo(oldValue, id, id.equals(selectedGroupId), acceptableDuration); + TemporaryLimitInfo newTemporaryLimitInfo = new TemporaryLimitInfo(newValue, id, id.equals(selectedGroupId), acceptableDuration); + doNotify(attributeName + "_" + limitType + ".temporaryLimit.value", oldTemporaryLimitInfo, newTemporaryLimitInfo); + } + private void doNotify(String attribute, Object oldValue, Object newValue) { if (listeners != null) { listeners.notifyUpdate(identifiable, attribute, oldValue, newValue); @@ -155,4 +161,7 @@ public record PermanentLimitInfo(double value, String groupId, boolean inSelecte public record OperationalLimitsInfo(OperationalLimits value, String groupId, boolean inSelectedGroup) { } + + public record TemporaryLimitInfo(double value, String groupId, boolean inSelectedGroup, int acceptableDuration) { + } } diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java index f1412ec5a6e..24b058a1d0d 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java @@ -697,4 +697,48 @@ public void testAdderWithValueZero() { assertEquals(0, limits.getPermanentLimit()); assertEquals(0, limits.getTemporaryLimit(Integer.MAX_VALUE).getValue()); } + + @Test + public void testSetTemporaryLimitValue() { + Line line = createNetwork().getLine("L"); + CurrentLimitsAdder adder = line.newCurrentLimits1() + .setPermanentLimit(1000.) + .beginTemporaryLimit() + .setName("TL1") + .setAcceptableDuration(20 * 60) + .setValue(1200.0) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("TL2") + .setAcceptableDuration(10 * 60) + .setValue(1400.0) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("TL3") + .setAcceptableDuration(5 * 60) + .setValue(1600.0) + .endTemporaryLimit(); + adder.add(); + + Optional optionalLimits = line.getCurrentLimits(TwoSides.ONE); + assertTrue(optionalLimits.isPresent()); + CurrentLimits limits = optionalLimits.get(); + + limits.setTemporaryLimitValue(20 * 60, 1050.0); + assertEquals(1050.0, limits.getTemporaryLimit(20 * 60).getValue()); + + limits.setTemporaryLimitValue(10 * 60, 1450.0); + assertEquals(1450.0, limits.getTemporaryLimit(10 * 60).getValue()); + + limits.setTemporaryLimitValue(5 * 60, 1750.0); + assertEquals(1750.0, limits.getTemporaryLimit(5 * 60).getValue()); + + // Tests with invalid values + assertEquals(1750.0, limits.getTemporaryLimit(5 * 60).getValue()); + limits.setTemporaryLimitValue(5 * 60, 1250.0); + assertThrows(ValidationException.class, () -> limits.setTemporaryLimitValue(7 * 60, 1550.0)); + assertThrows(ValidationException.class, () -> limits.setTemporaryLimitValue(5 * 60, Double.NaN)); + assertThrows(ValidationException.class, () -> limits.setTemporaryLimitValue(5 * 60, -6.0)); + } + } diff --git a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/result/AbstractReducedLoadingLimits.java b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/result/AbstractReducedLoadingLimits.java index ba4e4484ca1..ca662816ace 100644 --- a/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/result/AbstractReducedLoadingLimits.java +++ b/security-analysis/security-analysis-api/src/main/java/com/powsybl/security/limitreduction/result/AbstractReducedLoadingLimits.java @@ -101,6 +101,11 @@ public LoadingLimits setPermanentLimit(double permanentLimit) { throw new UnsupportedOperationException("Unsupported operation for reduced loading limits."); } + @Override + public LoadingLimits setTemporaryLimitValue(int acceptableDuration, double temporaryLimitValue) { + throw new UnsupportedOperationException("Unsupported operation for reduced loading limits."); + } + @Override public void remove() { throw new UnsupportedOperationException("Reduced loading limits are not linked to a network element and thus cannot be removed."); From 8c98c02c32c6e16f0e5c7e33557ab6ba0ddc11e6 Mon Sep 17 00:00:00 2001 From: Naledi <151443525+nao1345678@users.noreply.github.com> Date: Wed, 28 Aug 2024 16:59:41 +0200 Subject: [PATCH 46/57] Limits copier (#3116) * first try at copying a current limit * successful copy of current limit object * add : copier on apparent, active and current limits * add : tests * update : javadoc Signed-off-by: Naledi EL CHEIKH --- .../java/com/powsybl/iidm/network/Branch.java | 88 ++++++++++++++++++- .../iidm/network/FlowsLimitsHolder.java | 45 +++++++++- .../iidm/network/LoadingLimitsAdder.java | 1 + .../iidm/network/util/LoadingLimitsUtil.java | 17 ++++ .../impl/AbstractLoadingLimitsAdder.java | 1 + .../tck/AbstractActivePowerLimitsTest.java | 48 +++++++++- .../tck/AbstractApparentPowerLimitsTest.java | 49 ++++++++++- .../tck/AbstractCurrentLimitsTest.java | 47 +++++++++- .../tck/AbstractIdenticalLimitsTest.java | 32 +++++++ .../AbstractThreeWindingsTransformerTest.java | 67 ++++++++++++++ .../tck/internal/AbstractTransformerTest.java | 3 +- 11 files changed, 387 insertions(+), 11 deletions(-) create mode 100644 iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractIdenticalLimitsTest.java diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java index 09d6a955a5c..20107ac6326 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Branch.java @@ -10,6 +10,8 @@ import java.util.Collection; import java.util.Optional; +import static com.powsybl.iidm.network.util.LoadingLimitsUtil.initializeFromLoadingLimits; + /** * An equipment with two terminals. * @@ -227,23 +229,62 @@ default ApparentPowerLimits getNullableApparentPowerLimits1() { CurrentLimitsAdder newCurrentLimits1(); /** - *

Create an adder to add a new {@link ActivePowerLimits} in the selected {@link OperationalLimitsGroup} on side 1.

+ *

Create an adder to add a copy of the {@link CurrentLimits} in parameters in the selected {@link OperationalLimitsGroup} on side 1.

*

If there's no selected group, the adder will also create a new group with the default name and set it as selected. * This operation is performed when the limits are created via {@link CurrentLimitsAdder#add()}, only if the limits to add * are valid.

+ * @param currentLimits a set of existing current limits. + * @return an adder allowing to create a new {@link CurrentLimits} initialized from the limits in parameters in the selected {@link OperationalLimitsGroup} on side 1. + */ + default CurrentLimitsAdder newCurrentLimits1(CurrentLimits currentLimits) { + CurrentLimitsAdder currentLimitsAdder = newCurrentLimits1(); + return initializeFromLoadingLimits(currentLimitsAdder, currentLimits); + } + + /** + *

Create an adder to add a new {@link ActivePowerLimits} in the selected {@link OperationalLimitsGroup} on side 1.

+ *

If there's no selected group, the adder will also create a new group with the default name and set it as selected. + * This operation is performed when the limits are created via {@link ActivePowerLimitsAdder#add()}, only if the limits to add + * are valid.

* @return an adder allowing to create a new {@link ActivePowerLimits} in the selected {@link OperationalLimitsGroup} on side 1. */ ActivePowerLimitsAdder newActivePowerLimits1(); + /** + *

Create an adder to add a copy of the {@link ActivePowerLimits} in parameters in the selected {@link OperationalLimitsGroup} on side 1.

+ *

If there's no selected group, the adder will also create a new group with the default name and set it as selected. + * This operation is performed when the limits are created via {@link ActivePowerLimitsAdder#add()}, only if the limits to add + * are valid.

+ * @param activePowerLimits a set of existing active power limits. + * @return an adder allowing to create a new {@link ActivePowerLimits} initialized from the limits in parameters in the selected {@link OperationalLimitsGroup} on side 1. + */ + default ActivePowerLimitsAdder newActivePowerLimits1(ActivePowerLimits activePowerLimits) { + ActivePowerLimitsAdder activePowerLimitsAdder = newActivePowerLimits1(); + return initializeFromLoadingLimits(activePowerLimitsAdder, activePowerLimits); + } + /** *

Create an adder to add a new {@link ApparentPowerLimits} in the selected {@link OperationalLimitsGroup} on side 1.

*

If there's no selected group, the adder will also create a new group with the default name and set it as selected. - * This operation is performed when the limits are created via {@link CurrentLimitsAdder#add()}, only if the limits to add + * This operation is performed when the limits are created via {@link ApparentPowerLimitsAdder#add()}, only if the limits to add * are valid.

* @return an adder allowing to create a new {@link ApparentPowerLimits} in the selected {@link OperationalLimitsGroup} on side 1. */ ApparentPowerLimitsAdder newApparentPowerLimits1(); + /** + *

Create an adder to add a copy of the {@link ApparentPowerLimits} in parameters in the selected {@link OperationalLimitsGroup} on side 1.

+ *

If there's no selected group, the adder will also create a new group with the default name and set it as selected. + * This operation is performed when the limits are created via {@link ApparentPowerLimitsAdder#add()}, only if the limits to add + * are valid.

+ * @param apparentPowerLimits a set of existing apparent power limits. + * @return an adder allowing to create a new {@link ApparentPowerLimits} initialized from the limits in parameters in the selected {@link OperationalLimitsGroup} on side 1. + */ + default ApparentPowerLimitsAdder newApparentPowerLimits1(ApparentPowerLimits apparentPowerLimits) { + ApparentPowerLimitsAdder apparentPowerLimitsAdder = newApparentPowerLimits1(); + return initializeFromLoadingLimits(apparentPowerLimitsAdder, apparentPowerLimits); + } + /** * Get the collection of the defined {@link OperationalLimitsGroup} on side 2. * @return the {@link OperationalLimitsGroup} s on side 2. @@ -355,23 +396,62 @@ default ApparentPowerLimits getNullableApparentPowerLimits2() { CurrentLimitsAdder newCurrentLimits2(); /** - *

Create an adder to add a new {@link ActivePowerLimits} in the selected {@link OperationalLimitsGroup} on side 2.

+ *

Create an adder to add a copy of the {@link CurrentLimits} in parameters in the selected {@link OperationalLimitsGroup} on side 2.

*

If there's no selected group, the adder will also create a new group with the default name and set it as selected. * This operation is performed when the limits are created via {@link CurrentLimitsAdder#add()}, only if the limits to add * are valid.

+ * @param currentLimits a set of existing current limits. + * @return an adder allowing to create a new {@link CurrentLimits} initialized from the limits in parameters in the selected {@link OperationalLimitsGroup} on side 2. + */ + default CurrentLimitsAdder newCurrentLimits2(CurrentLimits currentLimits) { + CurrentLimitsAdder currentLimitsAdder = newCurrentLimits2(); + return initializeFromLoadingLimits(currentLimitsAdder, currentLimits); + } + + /** + *

Create an adder to add a new {@link ActivePowerLimits} in the selected {@link OperationalLimitsGroup} on side 2.

+ *

If there's no selected group, the adder will also create a new group with the default name and set it as selected. + * This operation is performed when the limits are created via {@link ActivePowerLimitsAdder#add()}, only if the limits to add + * are valid.

* @return an adder allowing to create a new {@link ActivePowerLimits} in the selected {@link OperationalLimitsGroup} on side 2. */ ActivePowerLimitsAdder newActivePowerLimits2(); + /** + *

Create an adder to add a copy of the {@link ActivePowerLimits} in parameters in the selected {@link OperationalLimitsGroup} on side 2.

+ *

If there's no selected group, the adder will also create a new group with the default name and set it as selected. + * This operation is performed when the limits are created via {@link ActivePowerLimitsAdder#add()}, only if the limits to add + * are valid.

+ * @param activePowerLimits a set of existing active power limits. + * @return an adder allowing to create a new {@link ActivePowerLimits} initialized from the limits in parameters in the selected {@link OperationalLimitsGroup} on side 2. + */ + default ActivePowerLimitsAdder newActivePowerLimits2(ActivePowerLimits activePowerLimits) { + ActivePowerLimitsAdder activePowerLimitsAdder = newActivePowerLimits2(); + return initializeFromLoadingLimits(activePowerLimitsAdder, activePowerLimits); + } + /** *

Create an adder to add a new {@link ApparentPowerLimits} in the selected {@link OperationalLimitsGroup} on side 2.

*

If there's no selected group, the adder will also create a new group with the default name and set it as selected. - * This operation is performed when the limits are created via {@link CurrentLimitsAdder#add()}, only if the limits to add + * This operation is performed when the limits are created via {@link ApparentPowerLimitsAdder#add()}, only if the limits to add * are valid.

* @return an adder allowing to create a new {@link ApparentPowerLimits} in the selected {@link OperationalLimitsGroup} on side 2. */ ApparentPowerLimitsAdder newApparentPowerLimits2(); + /** + *

Create an adder to add a copy of the {@link ApparentPowerLimits} in parameters in the selected {@link OperationalLimitsGroup} on side 2.

+ *

If there's no selected group, the adder will also create a new group with the default name and set it as selected. + * This operation is performed when the limits are created via {@link ApparentPowerLimitsAdder#add()}, only if the limits to add + * are valid.

+ * @param apparentPowerLimits a set of existing apparent power limits. + * @return an adder allowing to create a new {@link ApparentPowerLimits} initialized from the limits in parameters in the selected {@link OperationalLimitsGroup} on side 2. + */ + default ApparentPowerLimitsAdder newApparentPowerLimits2(ApparentPowerLimits apparentPowerLimits) { + ApparentPowerLimitsAdder apparentPowerLimitsAdder = newApparentPowerLimits2(); + return initializeFromLoadingLimits(apparentPowerLimitsAdder, apparentPowerLimits); + } + default Optional getCurrentLimits(TwoSides side) { return switch (side) { case ONE -> getCurrentLimits1(); diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/FlowsLimitsHolder.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/FlowsLimitsHolder.java index 380f4ad7a64..b510a162a75 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/FlowsLimitsHolder.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/FlowsLimitsHolder.java @@ -10,6 +10,8 @@ import java.util.Collection; import java.util.Optional; +import static com.powsybl.iidm.network.util.LoadingLimitsUtil.initializeFromLoadingLimits; + /** * @author Miora Ralambotiana {@literal } */ @@ -126,20 +128,59 @@ default ApparentPowerLimits getNullableApparentPowerLimits() { CurrentLimitsAdder newCurrentLimits(); /** - *

Create an adder to add a new {@link ApparentPowerLimits} in the selected {@link OperationalLimitsGroup}.

+ *

Create an adder to add a copy of the {@link CurrentLimits} in parameters in the selected {@link OperationalLimitsGroup}.

*

If there's no selected group, the adder will also create a new group with the default name and set it as selected. * This operation is performed when the limits are created via {@link CurrentLimitsAdder#add()}, only if the limits to add * are valid.

+ * @param limits a set of existing current limits. + * @return an adder allowing to create a new {@link CurrentLimits} initialized from the limits in parameters in the selected {@link OperationalLimitsGroup}. + */ + default CurrentLimitsAdder newCurrentLimits(CurrentLimits limits) { + CurrentLimitsAdder adder = newCurrentLimits(); + return initializeFromLoadingLimits(adder, limits); + } + + /** + *

Create an adder to add a new {@link ApparentPowerLimits} in the selected {@link OperationalLimitsGroup}.

+ *

If there's no selected group, the adder will also create a new group with the default name and set it as selected. + * This operation is performed when the limits are created via {@link ApparentPowerLimitsAdder#add()}, only if the limits to add + * are valid.

* @return an adder allowing to create a new {@link ApparentPowerLimits} in the selected {@link OperationalLimitsGroup}. */ ApparentPowerLimitsAdder newApparentPowerLimits(); + /** + *

Create an adder to add a copy of the {@link ApparentPowerLimits} in parameters in the selected {@link OperationalLimitsGroup}.

+ *

If there's no selected group, the adder will also create a new group with the default name and set it as selected. + * This operation is performed when the limits are created via {@link ApparentPowerLimitsAdder#add()}, only if the limits to add + * are valid.

+ * @param limits a set of existing apparent power limits. + * @return an adder allowing to create a new {@link ApparentPowerLimits} initialized from the limits in parameters in the selected {@link OperationalLimitsGroup}. + */ + default ApparentPowerLimitsAdder newApparentPowerLimits(ApparentPowerLimits limits) { + ApparentPowerLimitsAdder adder = newApparentPowerLimits(); + return initializeFromLoadingLimits(adder, limits); + } + /** *

Create an adder to add a new {@link ActivePowerLimits} in the selected {@link OperationalLimitsGroup}.

*

If there's no selected group, the adder will also create a new group with the default name and set it as selected. - * This operation is performed when the limits are created via {@link CurrentLimitsAdder#add()}, only if the limits to add + * This operation is performed when the limits are created via {@link ActivePowerLimitsAdder#add()}, only if the limits to add * are valid.

* @return an adder allowing to create a new {@link ActivePowerLimits} in the selected {@link OperationalLimitsGroup}. */ ActivePowerLimitsAdder newActivePowerLimits(); + + /** + *

Create an adder to add a copy of the {@link ActivePowerLimits} in parameters in the selected {@link OperationalLimitsGroup}.

+ *

If there's no selected group, the adder will also create a new group with the default name and set it as selected. + * This operation is performed when the limits are created via {@link ActivePowerLimitsAdder#add()}, only if the limits to add + * are valid.

+ * @param limits a set of existing active power limits. + * @return an adder allowing to create a new {@link ActivePowerLimits} initialized from the limits in parameters in the selected {@link OperationalLimitsGroup}. + */ + default ActivePowerLimitsAdder newActivePowerLimits(ActivePowerLimits limits) { + ActivePowerLimitsAdder adder = newActivePowerLimits(); + return initializeFromLoadingLimits(adder, limits); + } } diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/LoadingLimitsAdder.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/LoadingLimitsAdder.java index a41949a0c73..128ec690fea 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/LoadingLimitsAdder.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/LoadingLimitsAdder.java @@ -132,4 +132,5 @@ default A fixLimits(double missingPermanentLimitPercentage, LoadingLimitsUtil.Li LoadingLimitsUtil.fixMissingPermanentLimit(this, missingPermanentLimitPercentage, getOwnerId(), limitFixLogger); return (A) this; } + } diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/LoadingLimitsUtil.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/LoadingLimitsUtil.java index 81920816daa..068fd9cdb4a 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/LoadingLimitsUtil.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/LoadingLimitsUtil.java @@ -81,4 +81,21 @@ public static > void adder.setPermanentLimit(fixedPermanentLimit); } } + + /** + *

Initialize an adder filled with a copy of an existing limits set

+ * @param adder the empty adder in which we initialize the new limits + * @param limits the limits to copy + */ + public static > A initializeFromLoadingLimits(A adder, L limits) { + adder.setPermanentLimit(limits.getPermanentLimit()); + limits.getTemporaryLimits().forEach(limit -> + adder.beginTemporaryLimit() + .setName(limit.getName()) + .setAcceptableDuration(limit.getAcceptableDuration()) + .setValue(limit.getValue()) + .setFictitious(limit.isFictitious()) + .endTemporaryLimit()); + return adder; + } } diff --git a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java index 10dc96e8e8e..97a54719d0e 100644 --- a/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java +++ b/iidm/iidm-impl/src/main/java/com/powsybl/iidm/network/impl/AbstractLoadingLimitsAdder.java @@ -188,4 +188,5 @@ public void removeTemporaryLimit(String name) { public String getOwnerId() { return ownerId; } + } diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractActivePowerLimitsTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractActivePowerLimitsTest.java index a10503012fe..37629c35e98 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractActivePowerLimitsTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractActivePowerLimitsTest.java @@ -11,12 +11,14 @@ import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import org.junit.jupiter.api.Test; +import java.util.Optional; + import static org.junit.jupiter.api.Assertions.*; /** * @author Miora Ralambotiana {@literal } */ -public abstract class AbstractActivePowerLimitsTest { +public abstract class AbstractActivePowerLimitsTest extends AbstractIdenticalLimitsTest { private static Network createNetwork() { Network network = EurostagTutorialExample1Factory.create(); @@ -75,4 +77,48 @@ public void test() { limits2.remove(); assertTrue(l.getActivePowerLimits2().isEmpty()); } + + @Test + public void testAdderByCopy() { + // First limit + Network network = createNetwork(); + Line line = network.getLine("NHV1_NHV2_2"); + + ActivePowerLimitsAdder adder = line.newActivePowerLimits1() + .setPermanentLimit(1000.) + .beginTemporaryLimit() + .setName("TL1") + .setAcceptableDuration(20 * 60) + .setValue(1200.0) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("TL2") + .setAcceptableDuration(10 * 60) + .setValue(1400.0) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("TL3") + .setAcceptableDuration(5 * 60) + .setValue(1600.0) + .endTemporaryLimit(); + adder.add(); + ActivePowerLimits limits1 = line.getActivePowerLimits1().get(); + + // Second limit + ActivePowerLimitsAdder adder2 = line.newActivePowerLimits2(limits1); + + adder2.add(); + + Optional optionalLimits2 = line.getActivePowerLimits2(); + assertTrue(optionalLimits2.isPresent()); + ActivePowerLimits limits2 = optionalLimits2.get(); + + // Tests + assertTrue(areLimitsIdentical(limits1, limits2)); + + adder = line.newActivePowerLimits1(limits2); + adder.add(); + + assertTrue(areLimitsIdentical(limits1, limits2)); + } } diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractApparentPowerLimitsTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractApparentPowerLimitsTest.java index 3c786159d49..3f92123e22d 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractApparentPowerLimitsTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractApparentPowerLimitsTest.java @@ -11,12 +11,14 @@ import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import org.junit.jupiter.api.Test; +import java.util.Optional; + import static org.junit.jupiter.api.Assertions.*; /** * @author Miora Ralambotiana {@literal } */ -public abstract class AbstractApparentPowerLimitsTest { +public abstract class AbstractApparentPowerLimitsTest extends AbstractIdenticalLimitsTest { private static Network createNetwork() { Network network = EurostagTutorialExample1Factory.create(); @@ -75,4 +77,49 @@ public void test() { apparentPowerLimits2.remove(); assertTrue(l.getActivePowerLimits2().isEmpty()); } + + @Test + public void testAdderByCopy() { + // First limit + Network network = createNetwork(); + Line line = network.getLine("NHV1_NHV2_2"); + + ApparentPowerLimitsAdder adder = line.newApparentPowerLimits1() + .setPermanentLimit(1000.) + .beginTemporaryLimit() + .setName("TL1") + .setAcceptableDuration(20 * 60) + .setValue(1200.0) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("TL2") + .setAcceptableDuration(10 * 60) + .setValue(1400.0) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("TL3") + .setAcceptableDuration(5 * 60) + .setValue(1600.0) + .endTemporaryLimit(); + adder.add(); + ApparentPowerLimits limits1 = line.getApparentPowerLimits1().get(); + + // Second limit + ApparentPowerLimitsAdder adder2 = line.newApparentPowerLimits2(limits1); + + adder2.add(); + + Optional optionalLimits2 = line.getApparentPowerLimits2(); + assertTrue(optionalLimits2.isPresent()); + ApparentPowerLimits limits2 = optionalLimits2.get(); + + // Tests + assertTrue(areLimitsIdentical(limits1, limits2)); + + adder = line.newApparentPowerLimits1(limits2); + adder.add(); + + assertTrue(areLimitsIdentical(limits1, limits2)); + } + } diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java index 24b058a1d0d..88bdb4f4dc1 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractCurrentLimitsTest.java @@ -18,7 +18,7 @@ /** * @author Geoffroy Jamgotchian {@literal } */ -public abstract class AbstractCurrentLimitsTest { +public abstract class AbstractCurrentLimitsTest extends AbstractIdenticalLimitsTest { private static Network createNetwork() { Network network = Network.create("test", "test"); @@ -698,6 +698,48 @@ public void testAdderWithValueZero() { assertEquals(0, limits.getTemporaryLimit(Integer.MAX_VALUE).getValue()); } + @Test + public void testAdderByCopy() { + // First limit + Line line = createNetwork().getLine("L"); + CurrentLimitsAdder adder = line.newCurrentLimits1() + .setPermanentLimit(1000.) + .beginTemporaryLimit() + .setName("TL1") + .setAcceptableDuration(20 * 60) + .setValue(1200.0) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("TL2") + .setAcceptableDuration(10 * 60) + .setValue(1400.0) + .endTemporaryLimit() + .beginTemporaryLimit() + .setName("TL3") + .setAcceptableDuration(5 * 60) + .setValue(1600.0) + .endTemporaryLimit(); + adder.add(); + CurrentLimits limits1 = line.getCurrentLimits1().get(); + + // Second limit + CurrentLimitsAdder adder2 = line.newCurrentLimits2(limits1); + + adder2.add(); + + Optional optionalLimits2 = line.getCurrentLimits2(); + assertTrue(optionalLimits2.isPresent()); + CurrentLimits limits2 = optionalLimits2.get(); + + // Tests + assertTrue(areLimitsIdentical(limits1, limits2)); + + adder = line.newCurrentLimits1(limits2); + adder.add(); + + assertTrue(areLimitsIdentical(limits1, limits2)); + } + @Test public void testSetTemporaryLimitValue() { Line line = createNetwork().getLine("L"); @@ -740,5 +782,6 @@ public void testSetTemporaryLimitValue() { assertThrows(ValidationException.class, () -> limits.setTemporaryLimitValue(5 * 60, Double.NaN)); assertThrows(ValidationException.class, () -> limits.setTemporaryLimitValue(5 * 60, -6.0)); } - } + + diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractIdenticalLimitsTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractIdenticalLimitsTest.java new file mode 100644 index 00000000000..e735a113ed8 --- /dev/null +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractIdenticalLimitsTest.java @@ -0,0 +1,32 @@ +package com.powsybl.iidm.network.tck; + +import com.powsybl.iidm.network.LoadingLimits; + +import java.util.List; + +public abstract class AbstractIdenticalLimitsTest { + public boolean areLimitsIdentical(LoadingLimits limits1, LoadingLimits limits2) { + boolean areIdentical = limits1.getPermanentLimit() == limits2.getPermanentLimit(); + + List tempLimits1 = limits1.getTemporaryLimits().stream().toList(); + List tempLimits2 = limits2.getTemporaryLimits().stream().toList(); + + if (areIdentical && tempLimits1.size() == tempLimits2.size()) { + for (int i = 0; i < tempLimits1.size(); i++) { + LoadingLimits.TemporaryLimit limit1 = tempLimits1.get(i); + LoadingLimits.TemporaryLimit limit2 = tempLimits2.get(i); + + if (!limit1.getName().equals(limit2.getName()) || + limit1.getAcceptableDuration() != limit2.getAcceptableDuration() || + limit1.getValue() != limit2.getValue()) { + areIdentical = false; + break; + } + } + } else { + areIdentical = false; + } + + return areIdentical; + } +} diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractThreeWindingsTransformerTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractThreeWindingsTransformerTest.java index 18581cd479b..36ad8cbb36f 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractThreeWindingsTransformerTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/AbstractThreeWindingsTransformerTest.java @@ -12,8 +12,11 @@ import com.powsybl.iidm.network.PhaseTapChanger.RegulationMode; import com.powsybl.iidm.network.ThreeWindingsTransformer.Leg; import com.powsybl.iidm.network.tck.internal.AbstractTransformerTest; + import org.junit.jupiter.api.Test; +import java.util.Optional; + import static org.junit.jupiter.api.Assertions.*; public abstract class AbstractThreeWindingsTransformerTest extends AbstractTransformerTest { @@ -1038,4 +1041,68 @@ private void createThreeWindingsTransformerWithLeg3(double r, double x, double g .add() .add(); } + + @Test + public void testAdderByCopy() { + // First limit + ThreeWindingsTransformerAdder transformerAdder = createThreeWindingsTransformerAdder(); + ThreeWindingsTransformer transformer = transformerAdder.add(); + ThreeWindingsTransformer.Leg leg1 = transformer.getLeg1(); + ThreeWindingsTransformer.Leg leg2 = transformer.getLeg2(); + + CurrentLimitsAdder currentLimitsAdder1 = leg1.newCurrentLimits() + .setPermanentLimit(1000.) + .beginTemporaryLimit() + .setName("TL1") + .setAcceptableDuration(20 * 60) + .setValue(1200.0) + .endTemporaryLimit(); + currentLimitsAdder1.add(); + + ActivePowerLimitsAdder activePowerLimitsAdder1 = leg1.newActivePowerLimits() + .setPermanentLimit(1000.) + .beginTemporaryLimit() + .setName("TL1") + .setAcceptableDuration(20 * 60) + .setValue(1200.0) + .endTemporaryLimit(); + activePowerLimitsAdder1.add(); + + ApparentPowerLimitsAdder apparentPowerLimitsAdder1 = leg1.newApparentPowerLimits() + .setPermanentLimit(1000.) + .beginTemporaryLimit() + .setName("TL1") + .setAcceptableDuration(20 * 60) + .setValue(1200.0) + .endTemporaryLimit(); + apparentPowerLimitsAdder1.add(); + + CurrentLimits currentLimits1 = leg1.getCurrentLimits().get(); + ActivePowerLimits activePowerLimits1 = leg1.getActivePowerLimits().get(); + ApparentPowerLimits apparentPowerLimits1 = leg1.getApparentPowerLimits().get(); + + CurrentLimitsAdder currentLimitsAdder2 = leg2.newCurrentLimits(currentLimits1); + currentLimitsAdder2.add(); + Optional optionalCurrentLimits2 = leg2.getCurrentLimits(); + assertTrue(optionalCurrentLimits2.isPresent()); + CurrentLimits currentLimits2 = optionalCurrentLimits2.get(); + + ActivePowerLimitsAdder activePowerLimitsAdder2 = leg2.newActivePowerLimits(activePowerLimits1); + activePowerLimitsAdder2.add(); + Optional optionalActivePowerLimits2 = leg2.getActivePowerLimits(); + assertTrue(optionalActivePowerLimits2.isPresent()); + ActivePowerLimits activePowerLimits2 = optionalActivePowerLimits2.get(); + + ApparentPowerLimitsAdder apparentPowerLimitsAdder2 = leg2.newApparentPowerLimits(apparentPowerLimits1); + apparentPowerLimitsAdder2.add(); + Optional optionalApparentPowerLimits2 = leg2.getApparentPowerLimits(); + assertTrue(optionalApparentPowerLimits2.isPresent()); + ApparentPowerLimits apparentPowerLimits2 = optionalApparentPowerLimits2.get(); + + // Tests + assertTrue(areLimitsIdentical(currentLimits1, currentLimits2)); + assertTrue(areLimitsIdentical(activePowerLimits1, activePowerLimits2)); + assertTrue(areLimitsIdentical(apparentPowerLimits1, apparentPowerLimits2)); + + } } diff --git a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/internal/AbstractTransformerTest.java b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/internal/AbstractTransformerTest.java index 9988476da49..243f446e708 100644 --- a/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/internal/AbstractTransformerTest.java +++ b/iidm/iidm-tck/src/test/java/com/powsybl/iidm/network/tck/internal/AbstractTransformerTest.java @@ -8,10 +8,11 @@ package com.powsybl.iidm.network.tck.internal; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.tck.AbstractIdenticalLimitsTest; import com.powsybl.iidm.network.test.NoEquipmentNetworkFactory; import org.junit.jupiter.api.BeforeEach; -public abstract class AbstractTransformerTest { +public abstract class AbstractTransformerTest extends AbstractIdenticalLimitsTest { protected Network network; protected Substation substation; From b70d8f0eca25655ae46261d59114f511d353b828 Mon Sep 17 00:00:00 2001 From: bc-rte <142609826+bc-rte@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:18:06 +0200 Subject: [PATCH 47/57] Add Overload Management System documentation (#3110) Signed-off-by: CHIQUET Benoit Co-authored-by: Anne Tilloy --- docs/grid_model/network_subnetwork.md | 49 ++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/docs/grid_model/network_subnetwork.md b/docs/grid_model/network_subnetwork.md index 44ae4455340..876dc5ec418 100644 --- a/docs/grid_model/network_subnetwork.md +++ b/docs/grid_model/network_subnetwork.md @@ -807,10 +807,12 @@ A busbar section is a non impedant element used in a node/breaker substation top ## Breaker/switch [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Switch.html)
+A switch is a device designed to close or open one or more electric circuits. There are several types of switches: +- breakers are capable of breaking currents under abnormal operating conditions (e.g. short-circuit); +- load break switches are capable of breaking currents under normal operating conditions; +- and disconnectors can only make or break negligible current. - +A switch has an attribute to say if it is open or closed. **Available extensions** @@ -819,9 +821,40 @@ A busbar section is a non impedant element used in a node/breaker substation top (internal-connection)= ## Internal connection -**Internal connection** -An internal connection is a non-impedant connection between two components in a voltage level. +[![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/VoltageLevel.NodeBreakerView.InternalConnection.html)
+An internal connection is a zero-impedance connection between two elements in a voltage level. - \ No newline at end of file +Contrary to the switch, the internal connection does not have any attribute to say of it is open or closed. + +(overload-management-system)= +## Overload management systems + +[![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/OverloadManagementSystem.html)
+An overload management system is an automation system that monitors current on a terminal, defined by an equipment and an optional side. +Based on the measured values, various strategies (referred as 'trippings' in the model) can be implemented to reduce the current. +In a given strategy, if the current exceeds a threshold, a switch can be opened or closed to resolve the violation. + +The switch open or close operation could be modeled using a switch ID, an association of a branch ID and a side, +or an association of a three-windings transformer ID and a side. + +**Overload management system Characteristics** + +| Attribute | Unit | Description | +| --------- | ---- |---------------------------------------------------------| +| $Substation$ | | The substation associated where the system is installed | +| $MonitoredElementId$ | | The network element on which the limit will be monitored | +| $MonitoredSide$ | | The side of the element that is monitored | + +**Tripping Characteristics** + +The supported trippings are: +- Branch tripping, +- Switch tripping, +- and three-windings transformer tripping. + +| Attribute | Unit | Description | +| --------- | ---- | ----------- | +| $Type$ | | The type of tripping (e.g. BranchTripping, SwitchTripping, ...) | +| $CurrentLimit$ | A | The current limit for which the action will be triggered | +| $Key$ | | The tripping key | +| $OpenAction$ | bool | Whether the tripping should be opened or closed | From e8d8a13ea43065427759a25b4854ce6bf7336888 Mon Sep 17 00:00:00 2001 From: Jon Harper Date: Fri, 6 Sep 2024 17:55:47 +0200 Subject: [PATCH 48/57] CompletableFutureTask, fix spurious error logging when cancelling (#3135) When calling cancel(true), the CompletableFutureTask was throwing a NullPointerException in its run method because cancel(true) causes future.get() to throw a CancellationException without a cause (null) and completeExceptionally requires a non null Exception the exception was thrown to the executor and the very end. Executors usually catch and ignore everything (just logs to stdout) so the problem was just ugly logs and a slight performance drop. This was a regression from 5bef72fa5ee, the actual code is reverted to the previous version (except changing throwable to exception to please avoid sonar warnings I guess) The code change was not logical, we need to unwrap the cause when we have an ExecutionException and we also need to not unwrap the cause otherwise. Signed-off-by: HARPER Jon --- .../computation/CompletableFutureTask.java | 4 ++- .../CompletableFutureTaskTest.java | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/computation/src/main/java/com/powsybl/computation/CompletableFutureTask.java b/computation/src/main/java/com/powsybl/computation/CompletableFutureTask.java index 806edd34f79..17530b5b420 100644 --- a/computation/src/main/java/com/powsybl/computation/CompletableFutureTask.java +++ b/computation/src/main/java/com/powsybl/computation/CompletableFutureTask.java @@ -45,11 +45,13 @@ public void run() { future.run(); try { complete(future.get()); + } catch (ExecutionException exc) { + completeExceptionally(exc.getCause()); } catch (InterruptedException exc) { Thread.currentThread().interrupt(); completeExceptionally(exc); } catch (Exception exc) { - completeExceptionally(exc.getCause()); + completeExceptionally(exc); } } diff --git a/computation/src/test/java/com/powsybl/computation/CompletableFutureTaskTest.java b/computation/src/test/java/com/powsybl/computation/CompletableFutureTaskTest.java index a97d6e69dd4..3e2c191ea40 100644 --- a/computation/src/test/java/com/powsybl/computation/CompletableFutureTaskTest.java +++ b/computation/src/test/java/com/powsybl/computation/CompletableFutureTaskTest.java @@ -40,10 +40,39 @@ private static List executors() { Executors.newSingleThreadExecutor(), Executors.newCachedThreadPool(), Executors.newWorkStealingPool(), + new MyTestExecutorWithException(), ForkJoinPool.commonPool() ); } + // Very basic executor that spawns a new thread + // and allows to wait for the end of the command. + // It just keeps an exception to be able to assert it. + // You should use it to launch only one command + // because it has just one latch and one exception + private static class MyTestExecutorWithException implements Executor { + + Exception exception = null; + CountDownLatch waitForDone; + + @Override + public void execute(Runnable command) { + (new Thread() { + @Override + public void run() { + waitForDone = new CountDownLatch(1); + try { + command.run(); + } catch (Exception e) { + MyTestExecutorWithException.this.exception = e; + } finally { + waitForDone.countDown(); + } + } + }).start(); + } + } + @ParameterizedTest @MethodSource("parameters") void whenSupplyObjectThenReturnIt(Executor executor) throws Exception { @@ -125,6 +154,10 @@ private void testEffectiveInterrupt(boolean addDependant, Executor executor) thr //Second call to cancel should return false cancelled = task.cancel(true); assertFalse(cancelled); + if (executor instanceof MyTestExecutorWithException myTestExecutor) { + myTestExecutor.waitForDone.await(); + assertNull(myTestExecutor.exception); + } } @ParameterizedTest From 8fb8cbde678ac068cc7cb477c8d21efb5aa86470 Mon Sep 17 00:00:00 2001 From: Nicolas Rol Date: Mon, 9 Sep 2024 11:48:10 +0200 Subject: [PATCH 49/57] Create directory for resources in TestPlatformConfigProvider (#3136) Signed-off-by: Nicolas Rol --- .../config/test/TestPlatformConfigProvider.java | 1 + .../config/test/TestPlatformConfigProviderTest.java | 11 ++++++++--- .../resources/com/powsybl/config/test/filelist.txt | 1 + .../powsybl/config/test/subfolder/subfolder_file.txt | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 config-test/src/test/resources/com/powsybl/config/test/subfolder/subfolder_file.txt diff --git a/config-test/src/main/java/com/powsybl/config/test/TestPlatformConfigProvider.java b/config-test/src/main/java/com/powsybl/config/test/TestPlatformConfigProvider.java index 91e9f860a0c..08abd246cf1 100644 --- a/config-test/src/main/java/com/powsybl/config/test/TestPlatformConfigProvider.java +++ b/config-test/src/main/java/com/powsybl/config/test/TestPlatformConfigProvider.java @@ -74,6 +74,7 @@ public PlatformConfig getPlatformConfig() { // The resources have relative paths (no leading slash) with full package path. Path dest = cfgDir.resolve(resource); LOGGER.info("Copying classpath resource: {} -> {}", resource, dest); + Files.createDirectories(dest.getParent()); Files.copy(TestPlatformConfigProvider.class.getResourceAsStream(resource), dest); } } catch (IOException e) { diff --git a/config-test/src/test/java/com/powsybl/config/test/TestPlatformConfigProviderTest.java b/config-test/src/test/java/com/powsybl/config/test/TestPlatformConfigProviderTest.java index 3b28bbed509..3545bd0ada0 100644 --- a/config-test/src/test/java/com/powsybl/config/test/TestPlatformConfigProviderTest.java +++ b/config-test/src/test/java/com/powsybl/config/test/TestPlatformConfigProviderTest.java @@ -30,11 +30,16 @@ void test() throws IOException { assertEquals("/work/" + TestPlatformConfigProvider.CONFIG_DIR, platformConfig.getConfigDir().map(Path::toString).orElse(null)); - Path testPath = platformConfig.getConfigDir().map(p -> p.resolve("other.txt")).orElse(null); + checkFileContent(platformConfig, "other.txt", "conf"); + checkFileContent(platformConfig, "subfolder/subfolder_file.txt", "subfile content"); + assertEquals("baz", platformConfig.getOptionalModuleConfig("foo").flatMap(c -> c.getOptionalStringProperty("bar")).orElse("")); + } + + private void checkFileContent(PlatformConfig platformConfig, String file, String content) throws IOException { + Path testPath = platformConfig.getConfigDir().map(p -> p.resolve(file)).orElse(null); assertNotNull(testPath); String testContent = Files.readAllLines(testPath, StandardCharsets.UTF_8).get(0); - assertEquals("conf", testContent); - assertEquals("baz", platformConfig.getOptionalModuleConfig("foo").flatMap(c -> c.getOptionalStringProperty("bar")).orElse("")); + assertEquals(content, testContent); } @Test diff --git a/config-test/src/test/resources/com/powsybl/config/test/filelist.txt b/config-test/src/test/resources/com/powsybl/config/test/filelist.txt index 8c98f97de36..4e6ac675e5d 100644 --- a/config-test/src/test/resources/com/powsybl/config/test/filelist.txt +++ b/config-test/src/test/resources/com/powsybl/config/test/filelist.txt @@ -1,3 +1,4 @@ config.yml other.txt base-voltages.yml +subfolder/subfolder_file.txt diff --git a/config-test/src/test/resources/com/powsybl/config/test/subfolder/subfolder_file.txt b/config-test/src/test/resources/com/powsybl/config/test/subfolder/subfolder_file.txt new file mode 100644 index 00000000000..7b98f0ca009 --- /dev/null +++ b/config-test/src/test/resources/com/powsybl/config/test/subfolder/subfolder_file.txt @@ -0,0 +1 @@ +subfile content \ No newline at end of file From bae7c6d97f8252af7471bd57a69984ed5b49b020 Mon Sep 17 00:00:00 2001 From: FranckLecuyer <47824306+FranckLecuyer@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:32:12 +0200 Subject: [PATCH 50/57] Distinguish between line and 2 windings transformer in TerminalFinder default rules (#3117) Signed-off-by: Franck LECUYER --- .../java/com/powsybl/iidm/network/util/TerminalFinder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/TerminalFinder.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/TerminalFinder.java index 49bb1afa601..837b40102d8 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/TerminalFinder.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/util/TerminalFinder.java @@ -86,7 +86,8 @@ private static List> getDefaultRules() { List> rules = new ArrayList<>(); rules.add(t -> t.getConnectable() instanceof BusbarSection); rules.add(t -> t.getConnectable() instanceof Injection); - rules.add(t -> t.getConnectable() instanceof Branch); + rules.add(t -> t.getConnectable() instanceof Line); + rules.add(t -> t.getConnectable() instanceof TwoWindingsTransformer); rules.add(t -> t.getConnectable() instanceof ThreeWindingsTransformer); rules.add(t -> t.getConnectable() instanceof HvdcConverterStation); rules.add(Objects::nonNull); From 72b52962283938f5393df099f32fee0b59f16720 Mon Sep 17 00:00:00 2001 From: Coline Piloquet <55250145+colinepiloquet@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:09:19 +0200 Subject: [PATCH 51/57] Documentation wording corrections (#3124) Signed-off-by: Coline PILOQUET --- docs/README.md | 8 +- docs/data/timeseries.md | 6 +- docs/grid_exchange_formats/ampl/index.md | 2 +- docs/grid_exchange_formats/cgmes/export.md | 38 +- docs/grid_exchange_formats/cgmes/import.md | 54 +-- docs/grid_exchange_formats/cgmes/index.md | 6 +- docs/grid_exchange_formats/iidm/export.md | 6 +- docs/grid_exchange_formats/iidm/import.md | 4 +- docs/grid_exchange_formats/iidm/index.md | 2 +- docs/grid_exchange_formats/index.md | 2 +- docs/grid_exchange_formats/matpower/import.md | 4 +- docs/grid_exchange_formats/psse/import.md | 46 +-- docs/grid_exchange_formats/psse/index.md | 4 +- docs/grid_exchange_formats/ucte/export.md | 2 +- .../ucte/format_specification.md | 14 +- docs/grid_exchange_formats/ucte/import.md | 46 +-- docs/grid_exchange_formats/ucte/index.md | 4 +- docs/grid_features/extraction.md | 18 +- docs/grid_features/import_post_processor.md | 6 +- docs/grid_features/loadflow_validation.md | 16 +- docs/grid_model/additional.md | 22 +- docs/grid_model/extensions.md | 70 ++-- docs/grid_model/index.md | 2 +- docs/grid_model/network_subnetwork.md | 338 +++++++++--------- docs/index.md | 2 +- docs/simulation/dynamic/configuration.md | 2 +- docs/simulation/dynamic/index.md | 4 +- .../dynamic_security/configuration.md | 4 +- docs/simulation/dynamic_security/index.md | 6 +- docs/simulation/loadflow/configuration.md | 10 +- docs/simulation/loadflow/index.md | 6 +- docs/simulation/security/action-dsl.md | 30 +- docs/simulation/security/configuration.md | 10 +- docs/simulation/security/contingency-dsl.md | 12 +- docs/simulation/security/index.md | 12 +- docs/simulation/security/limit-reductions.md | 6 +- docs/simulation/sensitivity/configuration.md | 8 +- docs/simulation/sensitivity/index.md | 24 +- docs/simulation/shortcircuit/index.md | 2 +- docs/simulation/shortcircuit/inputs.md | 4 +- docs/simulation/shortcircuit/outputs.md | 2 +- docs/simulation/shortcircuit/parameters.md | 4 +- .../configuration/componentDefaultConfig.md | 2 +- .../import-export-parameters-default-value.md | 2 +- docs/user/configuration/index.md | 20 +- .../limit-violation-default-filter.md | 2 +- .../load-flow-action-simulator.md | 4 +- .../user/configuration/loadflow-validation.md | 4 +- docs/user/configuration/security-analysis.md | 4 +- docs/user/itools/action-simulator.md | 10 +- .../compare-security-analysis-results.md | 8 +- docs/user/itools/dynamic-security-analysis.md | 8 +- docs/user/itools/dynamic-simulation.md | 4 +- docs/user/itools/index.md | 6 +- docs/user/itools/loadflow-validation.md | 68 ++-- docs/user/itools/loadflow.md | 4 +- docs/user/itools/run-script.md | 2 +- docs/user/itools/security-analysis.md | 12 +- 58 files changed, 514 insertions(+), 514 deletions(-) diff --git a/docs/README.md b/docs/README.md index 1b0c2db5991..556c14f0395 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ -These are the documentation sources for PowSybl core features. +These are the documentation sources for PowSyBl core features. Please keep them up to date with your developments. They are published on powsybl.readthedocs.io/ and pull requests are built and previewed automatically. @@ -27,8 +27,8 @@ sphinx-build -a . ../build-docs Then open `build-docs/index.html` in your browser. If you want to add links to another documentation, add the corresponding repository to the `conf.py` file. -In order to automatically get the version specified in the `pom.xml`, please use the same naming as the version: if you define the -Groovy version with ``, then use `groovy` as key. The specified URL should start with `https://` and end with `latest/` (the final `/` is mandatory). +To automatically get the version specified in the `pom.xml`, please use the same naming as the version: if you define the +Groovy version with ``, then use `groovy` as a key. The specified URL should start with `https://` and end with `latest/` (the final `/` is mandatory). For example, to add a link to the documentation of Sphinx, you need to add the following lines: ~~~python # This parameter might already be present, just add the new value @@ -38,7 +38,7 @@ intersphinx_mapping = { ~~~ Then in your documentation file, you can add links to PowSyBl-Core documentation. If you want to link to a whole page, -use one of the following example: +use one of the following examples: ~~~Markdown - {doc}`sphinx:usage/extensions/intersphinx` - {doc}`Intersphinx ` diff --git a/docs/data/timeseries.md b/docs/data/timeseries.md index cde68cf179d..206cf5e350e 100644 --- a/docs/data/timeseries.md +++ b/docs/data/timeseries.md @@ -12,7 +12,7 @@ in the framework, depending on the need: - [Irregular index](https://en.wikipedia.org/wiki/Unevenly_spaced_time_series): the time step size varies - Infinite index: the time series contains only two points, one at instant 0 and another at instant `Long.MAX_VALUE` - Metadata: a list of key/value string data -- Data chunks: an ordered list of data that will be associated to instants of the time index. The data chunks may be compressed or uncompressed. +- Data chunks: an ordered list of data that will be associated with instants of the time index. The data chunks may be compressed or uncompressed. An uncompressed JSON data chunk looks like: ```json @@ -24,7 +24,7 @@ An uncompressed JSON data chunk looks like: An uncompressed data chunk is modeled with a double (or String) array and an offset. It defines values associated to instants of the time index from `offset` to `offset + values.length`. -It is possible to compress the data chunks, using for example the [RLE](https://fr.wikipedia.org/wiki/Run-length_encoding). +It is possible to compress the data chunks, using, for example, the [RLE](https://fr.wikipedia.org/wiki/Run-length_encoding). The JSON serialization of compressed data chunks looks like: Output: ```json @@ -114,7 +114,7 @@ Here is the list of supported vector operations: In the Groovy DSL syntax, both `timeSeries['a']` and `ts['a']` are supported and are equivalent. To compare a time index vector to a literal date, the `time('2018-01-01T00:00:01Z')` function is available. For instance, the -following code create a time series of 0 and 1 values: +following code creates a time series of 0 and 1 values: ```groovy a = ts['dts'].time() < time('2018-01-01T00:00:01Z') ``` diff --git a/docs/grid_exchange_formats/ampl/index.md b/docs/grid_exchange_formats/ampl/index.md index 5376030ff21..4dfa82fd1ac 100644 --- a/docs/grid_exchange_formats/ampl/index.md +++ b/docs/grid_exchange_formats/ampl/index.md @@ -9,4 +9,4 @@ example.md The [AMPL](https://ampl.com/) (**A** **M**athematical **P**rogramming **L**anguage) format is an algebraic modeling language to describe and solve high-complexity problems for large-scale mathematical computing (i.e. large-scale optimization and scheduling-type problems). -IIDM networks can be converted in flat text files easy to read in AMPL, each of which describing one type of the network equipments (batteries, generators, loads, branches, buses, etc.). +IIDM networks can be converted in flat text files easy to read in AMPL, each of which describing one type of the network equipment (batteries, generators, loads, branches, buses, etc.). diff --git a/docs/grid_exchange_formats/cgmes/export.md b/docs/grid_exchange_formats/cgmes/export.md index a5ca33a8773..33c52979b80 100644 --- a/docs/grid_exchange_formats/cgmes/export.md +++ b/docs/grid_exchange_formats/cgmes/export.md @@ -1,7 +1,7 @@ # Export There are two main use-cases supported: - * Export IGM (Individual Grid Model) instance files. There is a single network and a unique CGMES modelling authority. + * Export IGM (Individual Grid Model) instance files. There is a single network and a unique CGMES modeling authority. * Export CGM (Common Grid Model) instance files. A network composed of multiple subnetworks, where each subnetwork is an IGM. In both cases, the metadata model information in the exported files is built from metadata information read from the input files and stored in IIDM or received through parameters. @@ -10,7 +10,7 @@ Information received through parameters takes precedence over information availa For a quick CGM export, the user may rely on the parameter **iidm.export.cgmes.cgm_export** to write in a single export multiple updated SSH files (one for each IGM) and a single SV for the whole common grid model. Specifics about this option are explained in the section [below](#cgm-common-grid-model-quick-export). If you need complete control over the exported files in a CGM scenario, you may prefer to iterate through the subnetworks and make multiple calls to the export function. This is described in detail in the section [below](#cgm-common-grid-model-manual-export). -Please note that when exporting equipment, PowSyBl always use the CGMES node/breaker level of detail, without considering the topology +Please note that when exporting equipment, PowSyBl always uses the CGMES node/breaker level of detail, without considering the topology level of the PowSyBl network. The user can specify the profiles to be exported using the parameter **iidm.export.cgmes.profiles**. The list of currently supported export instance files are: EQ, SSH, SV, TP. @@ -47,7 +47,7 @@ The filenames of the exported instance files will follow the pattern: The basename is determined from the parameters, or the basename of the export data source or the main network name. -As an example, you can export one of the test configurations that has been provided by ENTSO-E. It is available in the cgmes-conformity module of the powsybl-core repository. If you run the following code: +As an example, you can export one of the test configurations that have been provided by ENTSO-E. It is available in the cgmes-conformity module of the powsybl-core repository. If you run the following code: ```java Network cgmNetwork = Network.read(CgmesConformity1Catalog.microGridBaseCaseAssembled().dataSource()); @@ -76,7 +76,7 @@ where the updated SSH files will supersede the original ones, and the SV will co If you want to intervene in how the updated IGM SSH files or the CGM SV are exported, you can make multiple calls to the CGMES export function. -You can use following code for reference: +You can use the following code for reference: ```java Network cgmNetwork = Network.read(CgmesConformity1Catalog.microGridBaseCaseAssembled().dataSource()); @@ -145,7 +145,7 @@ And the file `manualExampleBasename_SV.xml` will contain: http://entsoe.eu/CIM/StateVariables/4/1 -myModellinAuthority +myModelingAuthority ... ``` @@ -180,8 +180,8 @@ PowSyBl [`Generator`](../../grid_model/network_subnetwork.md#generator) is expor #### Regulating control -If the network comes from a CIM-CGMES model and a generator has initially a `RegulatingControl`, it always has at export -too. Otherwise, a `RegulatingControl` is always exported for generators, except if it has no regulating capabilities because +If the network comes from a CIM-CGMES model and a generator initially has a `RegulatingControl`, it will still have one at export. +Otherwise, a `RegulatingControl` is always exported for generators, except if it has no regulating capabilities because $minQ = maxQ$. A `RegulatingControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.reactivePower` when a @@ -213,10 +213,10 @@ PowSyBl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compen #### Regulating control -If the network comes from a CIM-CGMES model and a shunt compensator has initially a `RegulatingControl`, it always -has at export too. +If the network comes from a CIM-CGMES model and a shunt compensator initially has a `RegulatingControl`, it will still +have one at export. -A shunt compensator with local voltage control (i.e. the regulating terminal is the same of the terminal of connection) +A shunt compensator with local voltage control (i.e., the regulating terminal is the same of the terminal of connection) and no valid voltage target will not have any exported regulating control. In all other cases, a `RegulatingControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.voltage`. @@ -226,10 +226,10 @@ PowSyBl [`StaticVarCompensator`](../../grid_model/network_subnetwork.md#static-v #### Regulating control -If the network comes from a CIM-CGMES model and a static VAR compensator has initially a `RegulatingControl`, it always -has at export too. +If the network comes from a CIM-CGMES model and a static VAR compensator initially has a `RegulatingControl`, it will still +have one at export. -A static VAR compensator which voltage control is local (i.e. the regulating terminal is the same of the terminal of +A static VAR compensator which voltage control is local (i.e., the regulating terminal is the same of the terminal of connection) and no valid voltage or reactive power target will not have any exported regulating control. A `RegulatingControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.reactivePower` when @@ -252,7 +252,7 @@ PowSyBl [`Switch`](../../grid_model/network_subnetwork.md#breakerswitch) is expo ### ThreeWindingsTransformer -PowSyBl [`ThreeWindingsTransformer`](../../grid_model/network_subnetwork.md#three-windings-transformer) is exported as `PowerTransformer` with three `PowerTransformerEnds`. +PowSyBl [`ThreeWindingsTransformer`](../../grid_model/network_subnetwork.md#three-winding-transformer) is exported as `PowerTransformer` with three `PowerTransformerEnds`. #### Tap changer control @@ -272,13 +272,13 @@ when `PhaseTapChanger` `regulationMode` is set to `CURRENT_LIMITER`. ### TwoWindingsTransformer -PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-windings-transformer) is exported as `PowerTransformer` with two `PowerTransformerEnds`. +PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-winding-transformer) is exported as `PowerTransformer` with two `PowerTransformerEnds`. -Tap changer controls for two windings transformers are exported following the same rules explained in the previous section about three windings transformers. See [tap changer control](#tap-changer-control). +Tap changer controls for two-winding transformers are exported following the same rules explained in the previous section about three-winding transformers. See [tap changer control](#tap-changer-control). ### Voltage level -PowSybl [`VoltatgeLevel`](../../grid_model/network_subnetwork.md#voltage-level) is exported as `VoltageLevel`. +PowSyBl [`VoltageLevel`](../../grid_model/network_subnetwork.md#voltage-level) is exported as `VoltageLevel`. TODO details @@ -362,7 +362,7 @@ Optional property defining whether the transformers should be exported with the This property is set to `false` by default. **iidm.export.cgmes.export-load-flow-status** -Optional property that indicates whether the loadflow status (`converged` or `diverged`) should be +Optional property that indicates whether the load flow status (`converged` or `diverged`) should be written for the `TopologicalIslands` in the SV file. If `true`, the status will be computed by checking, for every bus, if the voltage and angle are valid, and if the bus is respecting Kirchhoff's first law. For the latter, we check that the sums of active power and reactive power at the bus are higher than a threshold defined by the properties @@ -380,7 +380,7 @@ Optional property to specify if the total mismatch left after power flow calcula This property is set to `true` by default. **iidm.export.cgmes.sourcing-actor** -Optional property allowing to specify a custom sourcing actor. If a Boundary set with reference data is provided for the export through the parameter `iidm.import.cgmes.boundary-location`, the value of this property will be used to look for the modelling authority set and the geographical region to be used in the export. +Optional property allowing to specify a custom sourcing actor. If a Boundary set with reference data is provided for the export through the parameter `iidm.import.cgmes.boundary-location`, the value of this property will be used to look for the modeling authority set and the geographical region to be used in the export. No default value is given. If this property is not given, the export process will still try to determine the sourcing actor from the IIDM network if it only contains one country. diff --git a/docs/grid_exchange_formats/cgmes/import.md b/docs/grid_exchange_formats/cgmes/import.md index 5a3cbaf6669..3ef15fd9534 100644 --- a/docs/grid_exchange_formats/cgmes/import.md +++ b/docs/grid_exchange_formats/cgmes/import.md @@ -5,13 +5,13 @@ The CIM-CGMES importer reads and converts a CIM-CGMES model to the PowSyBl grid - Convert CIM-CGMES data retrieved by SPARQL requests from the created triplestore to PowSyBl grid model The data in input CIM/XML files uses RDF (Resource Description Framework) syntax. In RDF, data is described making statements about resources using triplet expressions: (subject, predicate, object). -To describe the conversion from CGMES to PowSyBl we first introduce some generic considerations about the level of detail of the model (node/breaker or bus/branch), the identity of the equipments and equipment containment in substations and voltage levels. After that, the conversion for every CGMES relevant class is explained. Consistency checks and validations performed during the conversion are mentioned in the corresponding sections. +To describe the conversion from CGMES to PowSyBl, we first introduce some generic considerations about the level of detail of the model (node/breaker or bus/branch), the identity of the equipments and equipment containment in substations and voltage levels. After that, the conversion for every CGMES relevant class is explained. Consistency checks and validations performed during the conversion are mentioned in the corresponding sections. ## Levels of detail: node/breaker and bus/branch CGMES models defined at node/breaker level of detail will be mapped to PowSyBl node/breaker topology level. CGMES models defined at bus/branch level will be mapped to PowSyBl bus/breaker topology level. -For each equipment in the PowSyBl grid model it is necessary to specify how it should be connected to the network. +For each equipment in the PowSyBl grid model, it is necessary to specify how it should be connected to the network. If the model is specified at the bus/breaker level, a `Bus` must be specified for the equipment. @@ -21,7 +21,7 @@ Using the `Node` or `Bus` information, PowSyBl creates a `Terminal` that will be Some equipment, like switches, lines or transformers, have more than one point of connection to the Network. -In PowSyBl, a `Node` can have zero or one terminal. In CGMES, the `ConnectivityNode` objects may have more than one associated terminals. To be able to represent this in PowSyBl, the conversion process will automatically create internal connections between the PowSyBl nodes that represent equipment connections and the nodes created to map `ConnectivityNode` objects. +In PowSyBl, a `Node` can have zero or one terminal. In CGMES, the `ConnectivityNode` objects may have more than one associated terminal. To be able to represent this in PowSyBl, the conversion process will automatically create internal connections between the PowSyBl nodes that represent equipment connections and the nodes created to map `ConnectivityNode` objects. ## Identity of model equipments @@ -31,9 +31,9 @@ Terminals are used by CGMES and PowSyBl to define the points of connection of th ## Equipment containers: substations and voltage levels -The PowSyBl grid model establishes the substation as a required container of voltage levels and transformers (two and three windings transformers and phase shifters). Voltage levels are the required container of the rest network equipments, except for the AC and DC transmission lines that establish connections between substations and are associated directly to the network model. All buses at transformer ends should be kept at the same substation. +The PowSyBl grid model establishes the substation as a required container of voltage levels and transformers (two- and three-winding transformers and phase shifters). Voltage levels are the required container of the rest of the network equipment, except for the AC and DC transmission lines that establish connections between substations and are directly associated with the network model. All buses at the transformer ends should be kept in the same substation. -The CGMES model does not guarantee these hierarchical constraints, so the first step in the conversion process is to identify all the transformers with ends in different substations and all the breakers and switches with ends in different voltage levels. All the voltage levels connected by breakers or switches should be mapped to a single voltage level in the PowSyBl grid model. The first CGMES voltage level, in alphabetical order, will be the representative voltage level associated to the PowSyBl voltage level. The same criterion is used for substations, and the first CGMES substation will be the representative substation associated to the PowSyBl one. The joined voltage levels and substations information is used almost in every step of the mapping between CGMES and PowSyBl models, and it is recorded in the `Context` conversion class, that keeps data throughout the overall conversion process. +The CGMES model does not guarantee these hierarchical constraints, so the first step in the conversion process is to identify all the transformers with ends in different substations and all the breakers and switches with ends in different voltage levels. All the voltage levels connected by breakers or switches should be mapped to a single voltage level in the PowSyBl grid model. The first CGMES voltage level, in alphabetical order, will be the representative voltage level associated with the PowSyBl voltage level. The same criterion is used for substations, and the first CGMES substation will be the representative substation associated with the PowSyBl one. The joined voltage level and substation information is used in almost every step of the mapping between CGMES and PowSyBl models, and it is recorded in the `Context` conversion class, which keeps the data throughout the entire conversion process. ## Conversion from CGMES to PowSyBl grid model @@ -42,12 +42,12 @@ The following sections describe in detail how each supported CGMES network objec ### Substation For each substation (considering only the representative substation if they are connected by transformers) in the CGMES model a new substation is created in the PowSyBl grid model with the following attributes created as such: -- `Country` It is obtained from the `regionName` property as first option, from `subRegionName` as second option. Otherwise, is assigned to `null`. +- `Country` It is obtained from the `regionName` property as a first option, from `subRegionName` as second option. Otherwise, is assigned to `null`. - `GeographicalTags` It is obtained from the `SubRegion` property. ### VoltageLevel -As for substations, for each voltage level (considering only the representative voltage level if they are connected by switches) in the CGMES model a new voltage level is created in the PowSyBl grid model with the following attributes created as such: +As for substations, for each voltage level (considering only the representative voltage level if they are connected by switches) in the CGMES model, a new voltage level is created in the PowSyBl grid model with the following attributes created as such: - `NominalV` It is copied from the `nominalVoltage` property of the CGMES voltage level. - `TopologyKind` It will be `NODE_BREAKER` or `BUS_BREAKER` depending on the level of detail of the CGMES grid model. - `LowVoltageLimit` It is copied from the `lowVoltageLimit` property. @@ -105,7 +105,7 @@ For each `EnergySource` object in the CGMES model a new PowSyBl `Load` is create ### SvInjection -CMES uses `SvInjection` objects to report mismatches on calculated buses: they record the calculated bus injection minus the sum of the terminal flows. According to the documentation, the values will thus follow generator sign convention: positive sign means injection into the bus. Note that all the reference cases used for development follow load sign convention to report these mismatches, so we have decided to follow this load sign convention as a first approach. +CGMES uses `SvInjection` objects to report mismatches on calculated buses: they record the calculated bus injection minus the sum of the terminal flows. According to the documentation, the values will thus follow generator sign convention: positive sign means injection into the bus. Note that all the reference cases used for development follow load sign convention to report these mismatches, so we have decided to follow this load sign convention as a first approach. For each `SvInjection` in the CGMES network model a new PowSyBl `Load` with attributes created as such: - `P0`, `Q0` are set from `SvInjection.pInjection/qInjection`. @@ -124,7 +124,7 @@ an [`EquivalentBranch`](#equivalentbranch) or a [`PowerTransformer`](#powertrans Attributes of the PowSyBl generator or of the PowSyBl dangling line generation are created as such: - `MinP`/`MaxP` are copied from CGMES `minP`/`maxP` if defined, otherwise they are set to `-Double.MAX_VALUE`/`Double.MAX_VALUE`. - `TargetP`/`TargetQ` are set from `SSH` or `SV` values depending on which are defined. CGMES values for `p`/`q` are given with load sign convention, so a change in sign is applied when copying them to `TargetP`/`TargetQ`. -- `TargetV` The `regulationTarget` property is copied if it is not equal to zero. Otherwise, the nominal voltage associated to the connected terminal of the `equivalentInjection` is assigned. For CGMES Equivalent Injections the voltage regulation is allowed only at the point of connection. +- `TargetV` The `regulationTarget` property is copied if it is not equal to zero. Otherwise, the nominal voltage associated to the connected terminal of the `equivalentInjection` is assigned. For CGMES Equivalent Injections, the voltage regulation is allowed only at the point of connection. - `VoltageRegulatorOn` It is assigned to `true` if both properties, `regulationCapability` and `regulationStatus` are `true` and the terminal is connected. - `EnergySource` is set to `OTHER`. @@ -147,7 +147,7 @@ If the `ACLineSegment` has one side inside the boundary area and one side outsid If the `ACLineSegment` is mapped to a PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line): - `R` is copied from CGMES `r` - `X` is copied from CGMES `x` -- `G1` is calculated as half of CMGES `gch` if defined, `0.0` otherwise +- `G1` is calculated as half of CGMES `gch` if defined, `0.0` otherwise - `G2` is calculated as half of CGMES `gch` if defined, `0.0` otherwise - `B1` is calculated as half of CGMES `bch` - `B2` is calculated as half of CGMES `bch` @@ -155,7 +155,7 @@ If the `ACLineSegment` is mapped to a PowSyBl [`Line`](../../grid_model/network_ If the `ACLineSegment` is mapped to an unpaired PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line): - `R` is copied from CGMES `r` - `X` is copied from CGMES `x` -- `G` is copied from CMGES `gch` if defined, `0.0` otherwise +- `G` is copied from CGMES `gch` if defined, `0.0` otherwise - `B` is copied from CGMES `bch` - `PairingKey` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries - `P0` is copied from CGMES `P` of the terminal at boundary side @@ -164,10 +164,10 @@ If the `ACLineSegment` is mapped to an unpaired PowSyBl [`DanglingLine`](../../g If the `ACLineSegment` is mapped to a paired PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line): - `R` is copied from CGMES `r` - `X` is copied from CGMES `x` -- `G1` is `0.0` is the dangling line is on side `ONE` of the Tie Line. If the dangling line Line is on side `TWO` of the Tie Line, it is copied from CGMES `gch` if defined, `0.0` otherwise. -- `G2` is `0.0` is the dangling line is on side `TWO` of the Tie Line. If the dangling line Line is on side `ONE` of the Tie Line, it is copied from CGMES `gch` if defined, `0.0` otherwise. -- `B1` is `0.0` is the dangling line is on side `ONE` of the Tie Line. If the dangling line Line is on side `TWO` of the Tie Line, it is copied from CGMES `bch`. -- `B2` is `0.0` is the dangling line is on side `TWO` of the Tie Line. If the dangling line Line is on side `ONE` of the Tie Line, it is copied from CGMES `bch`. +- `G1` is `0.0` is the dangling line is on side `ONE` of the Tie Line. If the dangling line is on side `TWO` of the Tie Line, it is copied from CGMES `gch` if defined, `0.0` otherwise. +- `G2` is `0.0` is the dangling line is on side `TWO` of the Tie Line. If the dangling line is on side `ONE` of the Tie Line, it is copied from CGMES `gch` if defined, `0.0` otherwise. +- `B1` is `0.0` is the dangling line is on side `ONE` of the Tie Line. If the dangling line is on side `TWO` of the Tie Line, it is copied from CGMES `bch`. +- `B2` is `0.0` is the dangling line is on side `TWO` of the Tie Line. If the dangling line is on side `ONE` of the Tie Line, it is copied from CGMES `bch`. - `PairingKey` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries ### EquivalentBranch @@ -262,8 +262,8 @@ An `ExternalNetworkinjection` is mapped to a PowSyBl [`Generator`](../../grid_mo Linear shunt compensators represent shunt compensators with banks or sections with equal admittance values. -A `LinearShuntCompensator` is mapped to a PowSybl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) with `SectionCount` copied from CGMES SSH `sections` or CGMES `SvShuntCompensatorSections.sections`, depending on the import option. If none is defined, it is copied from CGMES `normalSections`. -The created PowSyBl shunt compensator is linear and its attributes are defined as described below: +A `LinearShuntCompensator` is mapped to a PowSyBl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) with `SectionCount` copied from CGMES SSH `sections` or CGMES `SvShuntCompensatorSections.sections`, depending on the import option. If none is defined, it is copied from CGMES `normalSections`. +The created PowSyBl shunt compensator is linear, and its attributes are defined as described below: - `BPerSection` is copied from CGMES `bPerSection` if defined. Else, it is `Float.MIN_VALUE`. - `GPerSection` is copied from CGMES `gPerSection` if defined. Else, it is left undefined. - `MaximumSectionCount` is copied from CGMES `maximumSections`. @@ -272,7 +272,7 @@ The created PowSyBl shunt compensator is linear and its attributes are defined a ### NonlinearShuntCompensator -Non-linear shunt compensators represent shunt compensators with banks or section addmittance values that differs. +Non-linear shunt compensators represent shunt compensators with banks or section admittance values that differ. A `NonlinearShuntCompensator` is mapped to a PowSyBl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) with `SectionCount` copied from CGMES SSH `sections` or CGMES `SvShuntCompensatorSections.sections`, depending on the import option. If none is defined, it is copied from CGMES `normalSections`. The created PowSyBl shunt compensator is non-linear and has as many `Sections` as there are `NonlinearShuntCompensatorPoint` associated with the `NonlinearShuntCompensator` it is mapped to. @@ -292,10 +292,10 @@ Power transformers represent electrical devices consisting of two or more couple #### PowerTransformer with two PowerTransformerEnds -If a `PowerTransformer` has two `PowerTransformerEnds`, both outside the boundary area, it is mapped to a PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-windings-transformer). +If a `PowerTransformer` has two `PowerTransformerEnds`, both outside the boundary area, it is mapped to a PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-winding-transformer). Please note that in this case, if `PowerTransformerEnds` are in different substations, the substations are merged into one. -If a `PowerTransformer` has two `PowerTransformerEnds`, both completely inside the boundary area, and if the boundary area is not imported, the `PowerTransformer` is ignored. Otherwise, it is mapped to a PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-windings-transformer). +If a `PowerTransformer` has two `PowerTransformerEnds`, both completely inside the boundary area, and if the boundary area is not imported, the `PowerTransformer` is ignored. Otherwise, it is mapped to a PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-winding-transformer). If the `PowerTransformer` has one `PowerTransformerEnd` inside the boundary area and the other outside the boundary area, the importer checks if another branch is connected to the same [`TopologicalNode`](#topologicalnode) in the boundary area. - If there is no other connected to this `TopologicalNode`, it is mapped to a PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line). @@ -305,7 +305,7 @@ If the `PowerTransformer` has one `PowerTransformerEnd` inside the boundary area - If there are only two branches with their boundary terminal connected and in different `SubGeographicalRegion`, they will both be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line), which are part of the same PowSyBl [`TieLine`](../../grid_model/network_subnetwork.md#tie-line) and all other `EquivalentBranches` will be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). - Otherwise, they will all be mapped to PowSyBl [`DanglingLines`](../../grid_model/network_subnetwork.md#dangling-line). -In every case, a `PowerTransformer` with two `PowerTransformerEnds` is mapped to an intermediary model that corresponds to a PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-windings-transformer). +In every case, a `PowerTransformer` with two `PowerTransformerEnds` is mapped to an intermediary model that corresponds to a PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-winding-transformer). For more information about this conversion, please look at the classes [`InterpretedT2xModel`](https://github.com/powsybl/powsybl-core/blob/main/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/transformers/InterpretedT2xModel.java) and [`ConvertedT2xModel`](https://github.com/powsybl/powsybl-core/blob/main/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/transformers/ConvertedT2xModel.java). @@ -316,7 +316,7 @@ If the `PowerTransformer` is finally mapped to a PowSyBl [`DanglingLine`](../../ #### PowerTransformer with three PowerTransformerEnds -A `PowerTransformer` with three `PowerTransformerEnds` is mapped to a PowSyBl [`ThreeWindingsTransformer`](../../grid_model/network_subnetwork.md#three-windings-transformer). +A `PowerTransformer` with three `PowerTransformerEnds` is mapped to a PowSyBl [`ThreeWindingsTransformer`](../../grid_model/network_subnetwork.md#three-winding-transformer). Please note that in this case, if `PowerTransformerEnds` are in different substations, the substations are merged into one. For more information about this conversion, please look at the classes [`InterpretedT3xModel`](https://github.com/powsybl/powsybl-core/blob/main/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/transformers/InterpretedT3xModel.java) @@ -379,7 +379,7 @@ If the CGMES `Switch` is mapped to a PowSyBl [`DanglingLine`](../../grid_model/n ## Extensions -The CIM-CGMES format contains more information than what the `iidm` grid model needs for calculation. The additional data, that are needed to export a network in CIM-CGMES format, are stored in several extensions. +The CIM-CGMES format contains more information than what the `iidm` grid model needs for calculation. The additional data that are needed to export a network in CIM-CGMES format are stored in several extensions. ### CGMES control areas @@ -451,13 +451,13 @@ Optional property that defines the directory path where the CGMES importer can f This property can also be used at CGMES export if the network was not imported from a CGMES to indicate the boundary files that should be used for reference. **iidm.import.cgmes.convert-boundary** -Optional property that defines if the equipment located inside the boundary are imported as part of the network. Used for debugging purposes. `false` by default. +Optional property that defines if the equipment located inside the boundary is imported as part of the network. Used for debugging purposes. `false` by default. **iidm.import.cgmes.convert-sv-injections** Optional property that defines if `SvInjection` objects are converted to IIDM loads. `true` by default. **iidm.import.cgmes.create-active-power-control-extension** -Optional property that defines if active power control extensions are created for the converted generators. `true` by default. If `true`, the extension will created for the CGMES `SynchronousMachines` with the attribute `normalPF` defined. For these generators, the `normalPF` value will be saved as the `participationFactor` and the flag `participate` set to `true`. +Optional property that defines if active power control extensions are created for the converted generators. `true` by default. If `true`, the extension will be created for the CGMES `SynchronousMachines` with the attribute `normalPF` defined. For these generators, the `normalPF` value will be saved as the `participationFactor` and the flag `participate` set to `true`. **iidm.import.cgmes.create-busbar-section-for-every-connectivity-node** Optional property that defines if the CGMES importer creates an [IIDM Busbar Section](../../grid_model/network_subnetwork.md#busbar-section) for each CGMES connectivity node. Used for debugging purposes. `false` by default. @@ -481,7 +481,7 @@ Optional property that defines if IDs' and aliases' unicity is ensured during CG Optional property that defines if control areas must be imported or not. `true` by default. **iidm.import.cgmes.naming-strategy** -Optional property that defines which naming strategy is used to transform GMES identifiers to IIDM identifiers. Currently, all naming strategies assign CGMES Ids directly to IIDM Ids during import, without any transformation. The default value is `identity`. +Optional property that defines which naming strategy is used to transform CGMES identifiers to IIDM identifiers. Currently, all naming strategies assign CGMES Ids directly to IIDM Ids during import, without any transformation. The default value is `identity`. **iidm.import.cgmes.post-processors** Optional property that defines all the CGMES post-processors which will be activated after import. @@ -501,7 +501,7 @@ Optional property that defines if IIDM IDs must be obtained from the CGMES `mRID Optional property that defines if the whole CGMES model is stored in the imported IIDM network as an [extension](import.md#cgmes-model). The default value is `true`. **iidm.import.cgmes.store-cgmes-conversion-context-as-network-extension** -Optional property that defines if the CGMES conversion context will be stored as an extension of the IIDM output network. It is useful for external validation of the mapping made between CGMES and IIDM. Its default value is `false`. +Optional property that defines if the CGMES conversion context is stored as an extension of the IIDM output network. It is useful for external validation of the mapping made between CGMES and IIDM. Its default value is `false`. **iidm.import.cgmes.import-node-breaker-as-bus-breaker** Optional property that forces CGMES model to be in topology bus/breaker in IIDM. This is a key feature when some models do not have all the breakers to connect and disconnect equipments in IIDM. In bus/breaker topology, connect and disconnect equipment only rely on terminal statuses and not on breakers. Its default value is `false`. diff --git a/docs/grid_exchange_formats/cgmes/index.md b/docs/grid_exchange_formats/cgmes/index.md index 8cab44ba676..0faf82127ba 100644 --- a/docs/grid_exchange_formats/cgmes/index.md +++ b/docs/grid_exchange_formats/cgmes/index.md @@ -10,7 +10,7 @@ export.md examples.md ``` -The CGMES (**C**ommon **G**rid **M**odel **E**xchange **S**pecification) is an IEC technical specification (TS 61970-600-1, TS 61970-600-2) based on the IEC CIM (**C**ommon **I**nformation **M**odel) family of standards. It was developed to meet necessary requirements for TSO data exchanges in the areas of system development and system operation. In this scenario the agents (the Modelling Authorities) generate their Individual Grid Models (IGM) that can be assembled to build broader Common Grid Models (CGM). Boundaries between IGMs are well defined: the boundary data is shared between the modelling agents and contain all boundary points required for a given grid model exchange. +The CGMES (**C**ommon **G**rid **M**odel **E**xchange **S**pecification) is an IEC technical specification (TS 61970-600-1, TS 61970-600-2) based on the IEC CIM (**C**ommon **I**nformation **M**odel) family of standards. It was developed to meet the necessary requirements for TSO data exchanges in the areas of system development and system operation. In this scenario the agents (the Modelling Authorities) generate their Individual Grid Models (IGM) that can be assembled to build broader Common Grid Models (CGM). Boundaries between IGMs are well-defined: the boundary data is shared between the modeling agents and contain all boundary points required for a given grid model exchange. In CGMES, an electric power system model is described by data grouped in different subsets (profiles) and exchanged as CIM/XML files, with each file associated to a given profile. The profiles considered in PowSyBl are: - `EQ` Equipment. Contains data that describes the equipment present in the network and its physical characteristics. @@ -18,12 +18,12 @@ In CGMES, an electric power system model is described by data grouped in differe - `TP` Topology. Describe how the equipment is electrically connected. Contains the definition of power flow buses. - `SV` State Variables. Contains all the information required to describe a steady-state power flow solution over the network. - `EQBD` Equipment Boundary. Contains definitions of the equipment in the boundary. -- `TPBD` Topology Boundary. Topology information associated to the boundary. +- `TPBD` Topology Boundary. Topology information associated with the boundary. - `DL` Diagram Layout. Contains information about diagram positions. - `GL` Geographical Layout. Contains information about geographical positions. CGMES model connectivity can be defined at two different levels of detail: -`Node/breaker` This is the level of detail required for Operation. The `EQ` contains Connectivity Nodes where the conducting equipment are attached through its Terminals. All switching devices (breakers, disconnectors, ...) are modelled. The contents of the `TP` file must be the result of the topology processing over the graph defined by connectivity nodes and switching devices, taking into account its open/closed status. +`Node/breaker` This is the level of detail required for Operation. The `EQ` contains Connectivity Nodes where the conducting equipment is attached through its Terminals. All switching devices (breakers, disconnectors, ...) are modelled. The contents of the `TP` file must be the result of the topology processing over the graph defined by connectivity nodes and switching devices, taking into account its open/closed status. `Bus/branch` No Connectivity Nodes are present in the `EQ` file. The association of every equipment to a bus is defined directly in the `TP` file, that must be provided. diff --git a/docs/grid_exchange_formats/iidm/export.md b/docs/grid_exchange_formats/iidm/export.md index 25cebec2615..92a783b2d65 100644 --- a/docs/grid_exchange_formats/iidm/export.md +++ b/docs/grid_exchange_formats/iidm/export.md @@ -29,7 +29,7 @@ The default value is `NODE_BREAKER` to export all voltage levels in the same lev The `iidm.export.xml.throw-exception-if-extension-not-found` property is an optional property that defines whether the XIIDM exporter throws an exception if the network contains an unknown or unserializable extension or if it just ignores it. Its default value is `false`. **iidm.export.xml.extensions** -The `iidm.export.xml.extensions` property is an optional property that defines the list of extensions that we will be exported by the XIIDM exporter. By default all extensions will be exported. +The `iidm.export.xml.extensions` property is an optional property that defines the list of extensions that we will be exported by the XIIDM exporter. By default, all extensions will be exported. **iidm.export.xml.sorted** The `iidm.export.xml.sorted` property is an optional property that defines whether the XIIDM file generated by the XIIDM exporter will be sorted or not for some objects. @@ -47,7 +47,7 @@ The `iidm.export.xml.version` property is an optional property that defines the **iidm.export.xml.iidm-version-incompatibility-behavior** The `iidm.export.xml.iidm-version-incompatibility-behavior` property is an optional property that defines the behavior of the XIIDM exporter when there is a version incompatibility between the IIDM network and the XIIDM version in which the export is done. -There is two possible behaviors: +There are two possible behaviors: - `LOG_ERROR`: an error is logged when there is a version incompatibility - `THROW_EXCEPTION`: an exception is thrown when there is a version incompatibility By default, this behavior is set as `THROW_EXCEPTION` @@ -57,7 +57,7 @@ There is two possible behaviors: **iidm.export.xml.export-mode** The `iidm.export.xml.export-mode` property is an optional property that defines the export mode of the XIIDM exporter. The export mode can be: - `UNIQUE_FILE`: Exports the network and its extensions in a unique file. -- `EXTENSIONS_IN_ONE_SEPARATED_FILE`: Exports the network to a file and the extensions to another file. In this mode, if the network file name is `network.xiidm`, the extensions file name must be `network-ext.xiidm`. +- `EXTENSIONS_IN_ONE_SEPARATED_FILE`: Exports the network to a file and the extensions to another file. In this mode, if the network file name is `network.xiidm`, the extension file name must be `network-ext.xiidm`. - `ONE_SEPARATED_FILE_PER_EXTENSION_TYPE`: Exports the network to a file and each extension type to a separate file. In this mode, if the network file name is `network.xiidm`, each extension file name must be `network-extensionName.xiidm`. Example: if our network has two extensions `loadFoo` and `loadBar`, then the network will be exported to the `network.xiidm` file and `loadFoo` and `loadBar` will be exported respectively to `network-loadFoo.xiidm` and `network-loadBar.xiidm`. The default value for this parameter is `IidmImportExportMode.NO_SEPARATED_FILE_FOR_EXTENSIONS`. This property has been removed in v3.3.0. diff --git a/docs/grid_exchange_formats/iidm/import.md b/docs/grid_exchange_formats/iidm/import.md index b3e123c1912..16392f31df1 100644 --- a/docs/grid_exchange_formats/iidm/import.md +++ b/docs/grid_exchange_formats/iidm/import.md @@ -9,7 +9,7 @@ These properties can be defined in the configuration file in the [import-export- The `iidm.import.xml.throw-exception-if-extension-not-found` property is an optional property that defines if the XIIDM importer throws an exception while trying to import an unknown or undeserializable extension or if it just ignores it. Its default value is `false`. **iidm.import.xml.extensions** -The `iidm.import.xml.extensions` property is an optional property that defines the list of extensions that will be imported by the XIIDM importer. By default all extensions will be imported. +The `iidm.import.xml.extensions` property is an optional property that defines the list of extensions that will be imported by the XIIDM importer. By default, all extensions will be imported. ### Deprecated properties @@ -23,7 +23,7 @@ The `iidm.import.xml.import-mode` property is an optional property that defines Its possible values are : - `UNIQUE_FILE`: Imports the network and its extensions from a unique file. -- `EXTENSIONS_IN_ONE_SEPARATED_FILE`: Imports the network from a file and the extensions from another file. In this mode, if the network file name is network.xiidm, the extensions file name must be network-ext.xiidm. +- `EXTENSIONS_IN_ONE_SEPARATED_FILE`: Imports the network from a file and the extensions from another file. In this mode, if the network file name is network.xiidm, the extension file name must be network-ext.xiidm. - `ONE_SEPARATED_FILE_PER_EXTENSION_TYPE`: Imports the network from a file and each extension type from a separate file. In this mode, if the network file name is `network.xiidm`, each extension file name must be `network-extensionName.xiidm`. Example: if our network has two extensions `loadFoo` and `loadBar`, then the network will be imported from the `network.xiidm` file and `loadFoo` and `loadBar` will be imported respectively from `network-loadFoo.xiidm` and `network-loadBar.xiidm`. The default value of this parameter is `NO_SEPARATED_FILE_FOR_EXTENSIONS`. This property has been removed in v3.3.0. diff --git a/docs/grid_exchange_formats/iidm/index.md b/docs/grid_exchange_formats/iidm/index.md index b94416c5fb8..d90d736f70b 100644 --- a/docs/grid_exchange_formats/iidm/index.md +++ b/docs/grid_exchange_formats/iidm/index.md @@ -14,7 +14,7 @@ Several exchange formats result from this internal format: - JIIDM, which corresponds to a JSON export of IIDM, - BIIDM, which corresponds to a binary export (this is still a beta-feature). -Below are two export from a same network: +Below are two exports from the same network: - one XML export (XIIDM exchange format) - one JSON export (JIIDM exchange format) diff --git a/docs/grid_exchange_formats/index.md b/docs/grid_exchange_formats/index.md index 37dbd4b5507..4634f1463e3 100644 --- a/docs/grid_exchange_formats/index.md +++ b/docs/grid_exchange_formats/index.md @@ -17,7 +17,7 @@ or network simulation via different tools: check them out below. | [AMPL](ampl/index.md) | a data separated value format easy to parse with AMPL | | | \* Note that updated export is available, that is, export is possible if the file was imported with the same format. -For instance, if you import a UCTE-DEF file in powsybl you can update some elements and then export it back to UCTE-DEF +For instance, if you import a UCTE-DEF file in powsybl, you can update some elements and then export it back to UCTE-DEF format, but you cannot export to UCTE-DEF format a file imported from another format. ```{toctree} diff --git a/docs/grid_exchange_formats/matpower/import.md b/docs/grid_exchange_formats/matpower/import.md index 9203fcad62a..84fa4358d2d 100644 --- a/docs/grid_exchange_formats/matpower/import.md +++ b/docs/grid_exchange_formats/matpower/import.md @@ -1,8 +1,8 @@ # Import -There is a lot of Matpower open source cases available all over the internet. Most of the time, those cases are provided as a `.m` file. This is Matlab code and only Matlab can interpret this kind of data. PowSyBl converter can only import `.mat` file which is a binary serialization of Matlab case data structure created from `.m` file. +There are a lot of Matpower open source cases available all over the internet. Most of the time, those cases are provided as a `.m` file. This is Matlab code, and only Matlab can interpret this kind of data. PowSyBl converter can only import `.mat` file which is a binary serialization of Matlab case data structure created from `.m` file. ## Matpower cases conversions -To import a Matpower cases, they have to be converted to `.mat` files first. This can be done using Matlab of course, but it is also possible to use [Octave](https://www.gnu.org/software/octave/), an open source scientific programming language which is mostly compatible with Matlab. Matpower toolbox can be installed with Octave. +To import a Matpower cases, they have to be converted to `.mat` files first. This can be done using Matlab, but it is also possible to use [Octave](https://www.gnu.org/software/octave/), an open source scientific programming language which is mostly compatible with Matlab. Matpower toolbox can be installed with Octave. ### Octave and Matpower installation To install Octave, please follow the installation guide for your operating system, available on their [wiki](https://wiki.octave.org/Category:Installation). diff --git a/docs/grid_exchange_formats/psse/import.md b/docs/grid_exchange_formats/psse/import.md index aaa8ca1c675..fdfc85d0625 100644 --- a/docs/grid_exchange_formats/psse/import.md +++ b/docs/grid_exchange_formats/psse/import.md @@ -1,11 +1,11 @@ # Import The import module reads and converts a PSS®E power flow data file to the PowSyBl grid model. The current implementation supports RAW format for versions 33 and 35 and RAWX format for version 35. The import process is performed in three steps: -- Read input file. +- Read the input file. - Validate input data. - Convert input data into PowSyBl grid model. -First, input data is obtained by reading and parsing the input file and as result a PSS®E model is created in memory. This model can be viewed as a set of Java classes where each data block of the PSS®E model is associated with a specific Java class that describes all their attributes or data items. Then, some inconsistency checks are performed on this model. If the validation succeeds the PSS®E model is converted to a PowSyBl grid model. +First, input data is obtained by reading and parsing the input file, and as a result, a PSS®E model is created in memory. This model can be viewed as a set of Java classes where each data block of the PSS®E model is associated with a specific Java class that describes all their attributes or data items. Then, some inconsistency checks are performed on this model. If the validation succeeds, the PSS®E model is converted to a PowSyBl grid model. ## Options Parameters for the import can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. @@ -20,13 +20,13 @@ The `psse.import.ignore-base-voltage` property is an optional property that defi A PSS®E file specifies a Bus/Branch network model where typically there is a bus for each voltage level inside a substation and where substation objects are not explicitly defined. Breakers and switches were traditionally modeled as zero impedance lines with special identifiers. Since version 35 PSS®E supports explicit definition of substation data and switching devices at both system and substation level. However, this information is optional and may not be present in the case. -The PowSyBl grid model establishes the substation as a required container of voltage levels and equipments. The first step in the conversion process assigns a substation for each PSS®E bus, ensuring that all buses at transformer ends are kept in the same substation. +The PowSyBl grid model establishes the substation as a required container of voltage levels and equipment. The first step in the conversion process assigns a substation for each PSS®E bus, ensuring that all buses at transformer ends are kept in the same substation. The current conversion does not use explicit PSS®E substation information. Instead, buses are grouped using zero impedance branches and transformers as connectors. A new voltage level is created for each group of buses connected by zero impedance branches, and a new substation is created for the voltage levels connected by transformers. Explicit substations will be supported in future versions, allowing to represent the internal connectivity. -In the PowSyBl grid model all the network components are identified through a global and unique alphanumeric identifier (**Id**). Optionally, they may receive a name (**Name**). +In the PowSyBl grid model, all the network components are identified through a global and unique alphanumeric identifier (**Id**). Optionally, they may receive a name (**Name**). -For each substation the following attributes are defined: +For each substation, the following attributes are defined: - **Id** following the pattern `S` where `n` represents a consecutive integer number starting from 1. Every voltage level is assigned to its corresponding substation, with attributes: @@ -61,13 +61,13 @@ PSS®E supports loads with three different characteristics: Constant Power, Cons ### _Fixed Bus Shunt Data_ -Each _Fixed Bus Shunt Data_ record defines a PowSyBl shunt compensator with a linear model and a single section. It is possible to define multiple fixed shunts at the same bus. The PowSyBl shunt compensator is associated with its corresponding voltage level and has the following attributes: +Each _Fixed Bus Shunt Data_ record defines a PowSyBl shunt compensator with a linear model and a single section. It is possible to define multiple fixed shunts at the same bus. The PowSyBl shunt compensator is associated with its corresponding voltage level and has the following attributes: - **Id** according to the pattern `-SH` where `n` represents the PSS®E bus number (field `I` in the _Fixed Bus Shunt Data_ record) and `m` is the PSS®E alphanumeric shunt identifier (field `ID` in the _Fixed Bus Shunt Data_ record). - **ConnectableBus** PowSyBl bus identifier assigned to the PSS®E bus number (field `I` in the _Fixed Bus Shunt Data_ record). - **SectionCount** Always `1`. - **gPerSection** Positive sequence shunt (charging) conductance per section. It is defined as `GL` / (`vnom` *`vnom`), where `GL` is the active component of shunt admittance to ground, entered in MW at one per unit voltage (field `GL` in the _Fixed Bus Shunt Data_ record) and `vnom` is the nominal voltage of the corresponding voltage level. -- **bPerSection** Positive sequence shunt (charging) susceptance per section. It is defined as `BL` / (`vnom` *`vnom`), where `BL` is the reactive component of shunt admittance to ground, entered in MVAR at one per unit voltage (field `BL` in the _Fixed Bus Shunt Data_ record). +- **bPerSection** Positive sequence shunt (charging) susceptance per section. It is defined as `BL` / (`vnom` *`vnom`), where `BL` is the reactive component of shunt admittance to ground, entered in MVAR at one per-unit voltage (field `BL` in the _Fixed Bus Shunt Data_ record). - **MaximumSectionCount** Always `1`. The shunt compensator is connected to the ConnectableBus if fixed shunt status (field `STATUS` in the _Fixed Bus Shunt Data_ record) is `1` (In-service). @@ -75,7 +75,7 @@ The shunt compensator is connected to the ConnectableBus if fixed shunt status ( ### _Switched Shunt Data_ -In the PSS®E version 33 only one switched shunt element can be defined at each bus. Version 35 allows multiple switched shunts at the same bus, adding an alphanumeric switched shunt identifier. +In the PSS®E version 33, only one switched shunt element can be defined on each bus. Version 35 allows multiple switched shunts on the same bus, adding an alphanumeric switched shunt identifier. A switched shunt device may be a mix of reactors and capacitors; it is divided in blocks and steps: a block contains `n` steps of the same admittance. The steps and blocks can be adjusted to regulate a given magnitude: a voltage at a bus, a reactive power output or an admittance. Only voltage regulation is considered when mapping this equipment to PowSyBl. @@ -88,7 +88,7 @@ Each switched shunt record defines a PowSyBl shunt compensator with a non-linear - **SectionCount** Defined as the section count (section index + 1) where section `B` is closer to the initial switched shunt admittance (field `BINIT` in the _Switched Shunt Data_ record). - **TargetV** Voltage setpoint defined as `0.5` * (`VSWLO` + `VSWHI`) * `vnom`, where `VSWLO` is the controlled voltage lower limit (field `VSWLO` in the _Switched Shunt Data_ record) and `VSWHI` is the controlled voltage upper limit (field `VSWHI` in the _Switched Shunt Data_ record). - **TargetDeadband** defined as (`VSWHI` - `VSWLO`) * `vnom`. -- **RegulatingTerminal** Regulating terminal assigned to the bus where voltage is controlled by this switched shunt (field `SWREM` in version 33 or field `SWREG` in version 35, both in the _Switched Shunt Data_ record if they are not `0`. Otherwise field `I` in the _Switched Shunt Data_ record). +- **RegulatingTerminal** Regulating terminal assigned to the bus where voltage is controlled by this switched shunt (field `SWREM` in version 33 or field `SWREG` in version 35, both in the _Switched Shunt Data_ record if they are not `0`. Otherwise, field `I` in the _Switched Shunt Data_ record). - **VoltageRegulatorOn** defined as `true` if the control mode is not `0` (field `MODSW` in the _Switched Shunt Data_ record) and `TargetV` is greater than `0.0`. The shunt compensator is connected to the ConnectableBus if switched shunt status (field `STAT` in the _Switched Shunt Data_ record) is `1` (In-service). @@ -97,7 +97,7 @@ The sections of the PowSyBl shunt compensator non-linear model are defined accor The attributes of each section in the PowSyBl shunt compensator non-linear model are defined as: - **Section G** Positive sequence shunt (charging) conductance of this section. Always `0.0`. -- **Section B** Positive sequence shunt (charging) susceptance of this section. It is defined as `B` / (`vnom` *`vnom`), where `B` is the reactive component of shunt admittance to ground, entered in MVAR at one per unit voltage assigned to this section and `vnom` is the nominal voltage of the corresponding voltage level. +- **Section B** Positive sequence shunt (charging) susceptance of this section. It is defined as `B` / (`vnom` *`vnom`), where `B` is the reactive component of shunt admittance to ground, entered in MVAR at one per-unit voltage assigned to this section and `vnom` is the nominal voltage of the corresponding voltage level. When the adjustment method `ADJM` is `0`, the behaviour of the switched shunt can be mapped directly to the shunt compensator non-linear model with sections based on the switched shunt blocks/steps and its order in the PSS@E input record. A section is assigned to each step of the reactor and capacitor shunt blocks by accumulating the admittance of the corresponding steps that are in-service. Only the in-service switched shunt blocks are considered (field `SI` in version 35 of the _Switched Shunt Data_ record, always in-service in version 33). A section with 0.0 susceptance is added between sections assigned to reactor and capacitor blocks. @@ -115,7 +115,7 @@ Every _Generator Data_ single line record represents one generator. Multiple gen - **MinQ** Minimum generator reactive power. It is copied from PSS®E field `QB`. - **MaxQ** Maximum generator reactive power. It is copied from PSS®E field `QT`. - **TargetV** Voltage setpoint defined as `VS` * `vnom`, where `VS` is the regulated voltage (field `VS` in the _Generator Data_ record) and `vnom` is the nominal voltage of the corresponding voltage level. -- **RegulatingTerminal** Regulating terminal assigned to the bus where voltage is controlled by this generator (field `IREG` in the _Generator Data_ record if it is not `0`. Otherwise field `I` in the _Generator Data_ record). +- **RegulatingTerminal** Regulating terminal assigned to the bus where voltage is controlled by this generator (field `IREG` in the _Generator Data_ record if it is not `0`. Otherwise, field `I` in the _Generator Data_ record). - **VoltageRegulatorOn** defined as `true` if the type code of the associated bus is `2` or `3` (field `IDE` in the _Bus Data_ record) and `TargetV` is greater than `0.0` and `MaxQ` is greater than `MinQ`. The generator is connected to the ConnectableBus if generator status (field `STAT` in the _Generator Data_ record) is `1` (In-service). @@ -143,9 +143,9 @@ A set of current permanent limits is defined as `1000.0` * `rateMva` / (`sqrt(3. ### _Transformer Data_ -The _Transformer Data_ block defines two windings and three windings transformers. Two windings transformers have four line records, while three windings transformers have five line records. A `0` value in the field `K` of the _Transformer Data_ record first line is used to indicate that is a two windings transformer, otherwise is considered a three windings transformer. +The _Transformer Data_ block defines two- and three-winding transformers. Two-winding transformers have four line records, while three-winding transformers have five line records. A `0` value in the field `K` of the _Transformer Data_ record first line is used to indicate that is a two-winding transformer, otherwise is considered a three-winding transformer. -PSS@E two windings transformer records are mapped to two windings transformers in the PowSyBl grid model. They are associated with corresponding voltage levels inside the same substation and defined with the following attributes: +PSS@E two-winding transformer records are mapped to two-winding transformers in the PowSyBl grid model. They are associated with corresponding voltage levels inside the same substation and defined with the following attributes: - **Id** according to the pattern `T---

` where `n` represents the PSS®E bus `1` number (field `I` in the _Transformer Data_ record), `m` represents the bus `2` number (field `J` in the _Transformer Data_ record) and `p` is the circuit identifier (field `CKT` in the _Transformer Data_ record). - **ConnectableBus1** PowSyBl bus identifier assigned to the PSS®E bus `1` number (field `I` in the _Transformer Data_ record). @@ -158,7 +158,7 @@ PSS@E two windings transformer records are mapped to two windings transformers i - **X** Transmission reactance. - **G** Shunt conductance. - **B** Shunt susceptance. -- **TapChanger** Could be a ratio tap changer o a phase tap changer. +- **TapChanger** Could be a ratio tap changer or a phase tap changer. - **OperationalLimits** Current limits for both ends. The transformer is connected at both ends if the branch status (field `STAT` in the _Transformer Data_ record) is `1` (In-service) @@ -168,11 +168,11 @@ In PSS®E the transformer model allows to define a ratio and angle at the end `1 ![TwoWindingsTransformerModels](img/two-winding-transformer-model.svg){width="100%" align=center class="only-light"} ![TwoWindingsTransformerModels](img/dark_mode/two-winding-transformer-model.svg){width="100%" align=center class="only-dark"} -To express the PSS®E electric attributes of the transformer in the PowSyBl grid model the following conversions are performed: +To express the PSS®E electric attributes of the transformer in the PowSyBl grid model, the following conversions are performed: -- The first step is to define the complex impedance between windings (`Z`) by using the resistance and reactance (fields `R1-2` and `X1-2` in the _Transformer Data_ record), the winding base MVA (field `SBASE1-2` in the _Transformer Data_ record) and the system MVA base (field `SBASE` in the _Case Identification Data_ record) according to the code that defines the units in which the winding impedances `R1-2`, `X1-2` are specified (field `CZ` in the _Transformer Data_ record). Then the complex impedance (`Z`) is converted to engineering units using the nominal voltage of the voltage level at end `2` and the system MVA base. Finally it should be adjusted after fixing an ideal ratio at end `2` and moving the configured ratio to the end `1`. The obtained result is assigned to the transmission resistance and reactance of the PowSyBl transformer. +- The first step is to define the complex impedance between windings (`Z`) by using the resistance and reactance (fields `R1-2` and `X1-2` in the _Transformer Data_ record), the winding base MVA (field `SBASE1-2` in the _Transformer Data_ record) and the system MVA base (field `SBASE` in the _Case Identification Data_ record) according to the code that defines the units in which the winding impedances `R1-2`, `X1-2` are specified (field `CZ` in the _Transformer Data_ record). Then the complex impedance (`Z`) is converted to engineering units using the nominal voltage of the voltage level at end `2` and the system MVA base. Finally, it should be adjusted after fixing an ideal ratio at end `2` and moving the configured ratio to the end `1`. The obtained result is assigned to the transmission resistance and reactance of the PowSyBl transformer. -- The complex shunt admittance `Ysh` is calculated using the transformer magnetizing admittance connected to ground at bus `1` (fields `MAG1` and `MAG2` in the _Transformer Data_ record), the winding base MVA (field `SBASE1-2` in the _Transformer Data_ record), the system MVA base, the bus base voltage (field `BASKV` in the _Bus Data_ record) of the transformer bus `I` and the nominal (rated) winding `1` voltage base (field `NOMV1` in the _Transformer Data_ record) according to the magnetizing admittance code that defines the units in which `MAG1` and `MAG2` are specified (filed `CM` in the _Transformer Data_ record). The next step is to convert the complex `Ysh` to engineering units using the nominal voltage of the voltage level at end `2` and the system MVA base and finally the obtained value is assigned to the shunt conductance and susceptance of the PowSyBl transformer. The shunt conductance in the PowSyBl grid model is located after the ratio so is necessary to add a step correction by each different ratio in the tabular `tapChanger`. +- The complex shunt admittance `Ysh` is calculated using the transformer magnetizing admittance connected to ground at bus `1` (fields `MAG1` and `MAG2` in the _Transformer Data_ record), the winding base MVA (field `SBASE1-2` in the _Transformer Data_ record), the system MVA base, the bus base voltage (field `BASKV` in the _Bus Data_ record) of the transformer bus `I` and the nominal (rated) winding `1` voltage base (field `NOMV1` in the _Transformer Data_ record) according to the magnetizing admittance code that defines the units in which `MAG1` and `MAG2` are specified (filed `CM` in the _Transformer Data_ record). The next step is to convert the complex `Ysh` to engineering units using the nominal voltage of the voltage level at end `2` and the system MVA base and finally the obtained value is assigned to the shunt conductance and susceptance of the PowSyBl transformer. The shunt conductance in the PowSyBl grid model is located after the ratio, so is necessary to add a step correction by each different ratio in the tabular `tapChanger`. To define the `tapChanger` the first step is to calculate the complex ratio at end `1` and the ratio at end `2`. The ratio at end `1` is calculated using the winding ratio (field `WINDV1` in the _Transformer Data_ record), the nominal (rated) winding voltage base (field `NOMV1` in the _Transformer Data_ record) and the bus base voltage (field `BASKV` in the _Bus Data_ record) according to the code that defines the units in which the turns ratios are specified (field `CZ` in the _Transformer Data_ record). The angle at end `1` is copied from the winding phase shift angle (field `ANG1` in the _Transformer Data_ record). The ratio at end `2` is calculated in the same way as at end `1` but using the following fields (fields `WINDV2`, `NOMV1` in the _Transformer Data_ record) and the corresponding bus base voltage at bus `J` (field `BASKV` in the _Bus Data_ record).
Then a `tapChanger` at end `1` is defined by fixing one of the components of the complex ratio at end `1` and moving the other in each step using the number of tap positions available (field `NTP1` in the _Transformer Data_ record) and the upper and lower limits (fields `RMA1`, `RMI1` in the _Transformer Data_ record).
@@ -193,11 +193,11 @@ When the `tapChanger` is a `phaseTapChanger` and the transformer control mode (f - **RegulatingTerminal** Regulating terminal assigned to the bus where active power flow is controlled (field `CONT1` in the _Transformer Data_ record). - **RegulatingOn** defined as `true` if `TargetV` is greater than `0.0`. -A set of current operational limits is defined for the two windings transformer as `1000.0` * `rateMva` / (`sqrt(3.0)` * `vnom1`) at the end `1` and `1000.0` * `rateMva` / (`sqrt(3.0)` * `vnom2`) at the end `2` where `rateMva` is the first rating of the transformer (field `RATA1` in version 33 of the _Transformer Data_ record and field `RATE11` in version 35) and `vnom1` and `vnom2` are the nominal voltage of the associated voltage levels. +A set of current operational limits is defined for the two-winding transformer as `1000.0` * `rateMva` / (`sqrt(3.0)` * `vnom1`) at the end `1` and `1000.0` * `rateMva` / (`sqrt(3.0)` * `vnom2`) at the end `2` where `rateMva` is the first rating of the transformer (field `RATA1` in version 33 of the _Transformer Data_ record and field `RATE11` in version 35) and `vnom1` and `vnom2` are the nominal voltage of the associated voltage levels. -When a three windings transformer is modeled the two windings transformer steps should be followed for each leg of the transformer taking into account the following considerations: +When a three-winding transformer is modeled, the two-winding transformer steps should be followed for each leg of the transformer taking into account the following considerations: - The **Id** is defined according to the pattern `T----

` where `n` represents the PSS®E bus `1` number (field `I` in the _Transformer Data_ record), `m` represents the bus `2` number (field `J` in the _Transformer Data_ record), `o` represents the bus `3` number (field `K` in the _Transformer Data_ record) and `p` is the circuit identifier (field `CKT` in the _Transformer Data_ record). -- The three windings transformer is modeled in PowSyBl as three two windings transformers connected to an fictitious bus defined with a nominal base voltage and rated voltage of `1.0 kV` (star configuration).
+- The three-winding transformers are modeled in PowSyBl as three two-winding transformers connected to a fictitious bus defined with a nominal base voltage and rated voltage of `1.0 kV` (star configuration).
- In PSS®E the between windings transmission impedances `Z1-2`, `Z2-3` and `Z3-1` are specified in the input file. These impedances are generally supplied on a transformer data sheet or test report. The transmission impedances `Z1`, `Z2` and `Z3` of the star network equivalent model are related to them according to the following expressions (see [Modeling of Three-Winding Voltage Regulating Transformers for Positive Sequence Load Flow Analysis in PSS®E](https://static.dc.siemens.com/datapool/us/SmartGrid/docs/pti/2010July/PDFS/Modeling%20of%20Three%20Winding%20Voltage%20Regulating%20Transformers.pdf)): @@ -215,9 +215,9 @@ When a three windings transformer is modeled the two windings transformer steps `Z3 = 0.5 * (Z2-3 + Z3-1 - Z1-2)` -- All the shunt admittances of the three windings transformer in the PSS®E model are assigned to the winding `1`. There is not shunt admittance at windings `2` and `3`. -- Each winding can have a complex ratio and a `ratioTapChanger` or `phaseTapChanger` with its corresponding control, always at end `1`. The current PowSyBl version only supports one enabled control by three windings transformer so if there is more than one enabled only the first (winding `1`, winding `2`, winding `3`) is kept enabled, the rest are automatically disabled. -- In three windings transformers the status attribute (field `STAT` in the _Transformer Data_ record) could be `0` that means all the windings disconnected, `1` for all windings connected, `2` for only the second winding disconnected, `3` for the third winding disconnected and `4` for the first winding disconnected. +- All the shunt admittances of the three-winding transformers in the PSS®E model are assigned to the winding `1`. There is no shunt admittance at windings `2` and `3`. +- Each winding can have a complex ratio and a `ratioTapChanger` or `phaseTapChanger` with its corresponding control, always at end `1`. The current PowSyBl version only supports one enabled control by three-winding transformers so if there is more than one enabled only the first (winding `1`, winding `2`, winding `3`) is kept enabled, the rest are automatically disabled. +- In three-winding transformers the status attribute (field `STAT` in the _Transformer Data_ record) could be `0` that means all the windings disconnected, `1` for all windings connected, `2` for only the second winding disconnected, `3` for the third winding disconnected and `4` for the first winding disconnected. ![ThreeWindingsTransformerModels](img/three-winding-transformer-model.svg){width="100%" align=center class="only-light"} ![ThreeWindingsTransformerModels](img/dark_mode/three-winding-transformer-model.svg){width="100%" align=center class="only-dark"} diff --git a/docs/grid_exchange_formats/psse/index.md b/docs/grid_exchange_formats/psse/index.md index 78610792497..89366b28c49 100644 --- a/docs/grid_exchange_formats/psse/index.md +++ b/docs/grid_exchange_formats/psse/index.md @@ -12,6 +12,6 @@ PSS®E uses different types of files to exchange data about the network. One of The RAW file has multiple groups of records (data blocks), with each group containing a particular type of data needed in power flow. The last record of each data block is a record specifying a value of zero to indicate the end of the category. -Each record in a data block contains a set of data items separated by a comma or one or more blanks where alphanumeric attributes must be enclosed in single quotes. As many of the data items specified in the RAW file have a default value only the specific information needed should be defined in the record. +Each record in a data block contains a set of data items separated by a comma or one or more blanks where alphanumeric attributes must be enclosed in single quotes. As many of the data items specified in the RAW file have a default value, only the specific information needed should be defined in the record. -In PSS®E version 35 a new RAWX file format (Extensible Power Flow Data File) based on JSON has been introduced. It will be the standard text-based data format for PSS®E power flow data exchange. The RAWX files contain two types of data objects: Parameter Sets and Data Tables. A Parameter Set has an array with field names and a single array with field values. A Data Table has an array with field names an and array of records, each record being an array of field values. The field names array indicates the order and subset of fields for which data is provided in the data arrays. +In PSS®E version 35, a new RAWX file format (Extensible Power Flow Data File) based on JSON has been introduced. It will be the standard text-based data format for PSS®E power flow data exchange. The RAWX files contain two types of data objects: Parameter Sets and Data Tables. A Parameter Set has an array with field names and a single array with field values. A Data Table has an array with field names an and array of records, each record being an array of field values. The field names array indicates the order and subset of fields for which data is provided in the data arrays. diff --git a/docs/grid_exchange_formats/ucte/export.md b/docs/grid_exchange_formats/ucte/export.md index bcf6fae2a79..355f7ee2815 100644 --- a/docs/grid_exchange_formats/ucte/export.md +++ b/docs/grid_exchange_formats/ucte/export.md @@ -8,4 +8,4 @@ These properties can be defined in the configuration file in the [import-export- **ucte.export.naming-strategy** The `ucte.export.naming-strategy` property is an optional property that defines the naming strategy to be used for UCTE export. -Its default value is `Default`, which corresponds to an implementation that expects the network elements' ID to be totally compatible with UCTE-DEF norm (e.g. a network initially imported from a UCTE-DEF file), and throws an exception if any network element does not respect the norm. It does not do any ID modification. +Its default value is `Default`, which corresponds to an implementation that expects the network elements' ID to be totally compatible with UCTE-DEF norm (e.g., a network initially imported from a UCTE-DEF file), and throws an exception if any network element does not respect the norm. It does not do any ID modification. diff --git a/docs/grid_exchange_formats/ucte/format_specification.md b/docs/grid_exchange_formats/ucte/format_specification.md index a3a489c7a39..6b1b5c9c846 100644 --- a/docs/grid_exchange_formats/ucte/format_specification.md +++ b/docs/grid_exchange_formats/ucte/format_specification.md @@ -1,17 +1,17 @@ # Format specification -The [UCTE-DEF](https://cimug.ucaiug.org/Groups/Model%20Exchange/UCTE-format.pdf) (UCTE **D**ata **E**xchange **F**ormat) format is an exchange format specified by the UCTE, for the exchange of grid model among its members. The data refer to load flow and three-phase short-circuit studies and describe the interconnected extra high voltage network. The data are contained in an unformatted standard US ASCII file. The file is divided into 7 different blocks: +The [UCTE-DEF](https://cimug.ucaiug.org/Groups/Model%20Exchange/UCTE-format.pdf) (UCTE **D**ata **E**xchange **F**ormat) format is an exchange format specified by the UCTE, for the exchange of grid model among its members. The data refer to load flow and three-phase short-circuit studies and describe the interconnected extra high-voltage network. The data are contained in an unformatted standard US ASCII file. The file is divided into 7 different blocks: - Comments (C) - Nodes (N) - Lines (L) -- Two windings transformers (T) -- Two windings transformers regulation (RR) -- Two windings transformers special description (TT) +- Two-winding transformers (T) +- Two-winding transformers regulation (RR) +- Two-winding transformers special description (TT) - Exchange powers (E) -Each block is introduced by a key line consisting of the two characters `##` and of the character given above in brackets. The end of a block is given by the next key line or the end of the file. The information of the each block is written in lines and the contents are separated by a blank (empty space). +Each block is introduced by a key line consisting of the two characters `##` and of the character given above in brackets. The end of a block is given by the next key line or the end of the file. The information of each block is written in lines, and the contents are separated by a blank (empty space). -The grid is described in Bus/Branch topology, and only few types of equipments are supported (nodal injections, AC line, two windings transformer). Fictitious nodes are located at the electric middle of each tie line. The defined X-nodes are binding for all users. +The grid is described in Bus/Branch topology, and only a few types of equipment are supported (nodal injections, AC line, two-winding transformer). Fictitious nodes are located at the electric middle of each tie line. The defined X-nodes are binding for all users. ## File name convention The UCTE-DEF format use the following file name convention: `___.uct` with: @@ -23,7 +23,7 @@ The UCTE-DEF format use the following file name convention: `__< - `SN`: Snapshots - `RE`: Reference - `LT`: Long-term reference - - `01`...`23`: Intra-day ahead congenstion forecast. The value is the number of hours separating the case date and the generation date. + - `01`...`23`: Intra-day ahead congestion forecast. The value is the number of hours separating the case date and the generation date. - `w`: day of the week, starting with 1 for Monday - `cc`: The ISO country-code for national datasets, `UC` for UCTE-wide merged datasets without X nodes and `UX` for UCTE-wide merged datasets with X nodes - `v`: version number starting with 0 diff --git a/docs/grid_exchange_formats/ucte/import.md b/docs/grid_exchange_formats/ucte/import.md index c65ec452bc0..f2a1bd3f3a9 100644 --- a/docs/grid_exchange_formats/ucte/import.md +++ b/docs/grid_exchange_formats/ucte/import.md @@ -5,7 +5,7 @@ Then, some inconsistency checks are performed on this model. Finally, the UCTE grid model is converted into an IIDM grid model. The UCTE parser provided by PowSyBl does not support the blocks `##TT` and `##E` providing respectively a special -description of the two windings transformers and the scheduled active power exchange between countries. +description of the two-winding transformers and the scheduled active power exchange between countries. Those blocks are ignored during the parsing step. @@ -33,20 +33,20 @@ The default value is an empty list. For more details see further below about [ar ## Inconsistency checks -Once the UCTE grid model is created in memory, a consistency check is performed on the different elements (nodes, lines, two windings transformers and regulations). +Once the UCTE grid model is created in memory, a consistency check is performed on the different elements (nodes, lines, two-winding transformers and regulations). In the tables below, we summarize the inconsistency checks performed on the network for each type of equipment. -For the sake of clarity we use notations that are made explicit before each table. +For the sake of clarity, we use notations that are made explicit before each table. ### Nodes with active or reactive power generation Notations: - $P$: active power generation, in MW - $Q$: reactive power generation, in MVar -- $minP$: minimum permissible active power generation, in MW -- $maxP$: maximum permissible active power generation, in MW -- $minQ$: minimum permissible reactive power generation, in MVar -- $maxQ$: maximum permissible reactive power generation, in MVar +- $minP$: minimum permissible active power generation in MW +- $maxP$: maximum permissible active power generation in MW +- $minQ$: minimum permissible reactive power generation in MVar +- $maxQ$: maximum permissible reactive power generation in MVar - $Vreg$: voltage regulation, which can be enabled or disabled - $Vref$: voltage reference, in V @@ -73,7 +73,7 @@ Notations: | $maxQ > 9999$ | $maxQ = 9999$ | | $minQ < -9999$ | $minQ = -9999$ | -### Lines or two-windings transformers +### Lines or two-winding transformers Notations: - $X$: reactance in $\Omega$ @@ -104,16 +104,16 @@ The UCTE file name is parsed to extract metadata required to initialize the IIDM ### Node conversion There is no equivalent [voltage level](../../grid_model/network_subnetwork.md#voltage-level) or [substation](../../grid_model/network_subnetwork.md#substation) concept in the UCTE-DEF format, -so we have to create substations and voltage levels from the nodes description and the topology. +so we have to create substations and voltage levels from the node description and the topology. Two nodes are in the same substation if they have the same geographical spot (the 1st-6th character of the node code) -or are connected by a two windings transformer, a coupler or a low impedance line. -Two nodes are in the same voltage level if their code only differ by the 8th character (busbars identifier). +or are connected by a two-winding transformer, a coupler or a low-impedance line. +Two nodes are in the same voltage level if their code only differs by the eighth character (busbars identifier). **Note:** We do not create a substation, a voltage level or a bus for X-nodes. They are converted to [dangling lines](../../grid_model/network_subnetwork.md#dangling-line). For nodes with a valid active or reactive load, a [load](../../grid_model/network_subnetwork.md#load) is created. -It's ID is equal to the ID of the bus post-fixed by `_load`. +Its ID is equal to the ID of the bus post-fixed by `_load`. The `P0` and `Q0` are equal to the active load and the reactive load of the UCTE node. -For those with a valid generator, a [generator](../../grid_model/network_subnetwork.md#generator) is created. It's ID is equal to the ID of the bus post-fixed by `_generator`. +For those with a valid generator, a [generator](../../grid_model/network_subnetwork.md#generator) is created. Its ID is equal to the ID of the bus post-fixed by `_generator`. The power plant type is converted to an [energy source]() value (see the mapping table below for the matching). **Mapping table for power plant types:** @@ -140,10 +140,10 @@ as properties: - the order code is stored in the `orderCode` property - the element name is stored in the `elementName` property -The lines connected between two real nodes are converted into a [AC line](../../grid_model/network_subnetwork.md#line), except if its impedance is too small (e.g. smaller than `0.05`). +The lines connected between two real nodes are converted into an [AC line](../../grid_model/network_subnetwork.md#line), except if its impedance is too small (e.g. smaller than `0.05`). In that particular case, the line is considered as a busbar coupler, and a [switch](../../grid_model/network_subnetwork.md#breakerswitch) is created. -The susceptance of the UCTE line is splitted in two, to initialize `B1` and `B2` with equal values. -If the current limits is defined, a permanent limit is created for both ends of the line. +The susceptance of the UCTE line is split in two, to initialize `B1` and `B2` with equal values. +If the current limits are defined, a permanent limit is created for both ends of the line. The element name of the UCTE line is stored in the `elementName` property. The lines connected between a read node and an X-node are converted into a [dangling line](../../grid_model/network_subnetwork.md#dangling-line). @@ -151,13 +151,13 @@ In IIDM, a dangling line is a line segment connected to a constant load. The sum of the active load and generation (rep. reactive) is computed to initialize the `P0` (resp. `Q0`) of the dangling line. The element name of the UCTE line is stored in the `elementName` property and the geographical name of the X-node is stored in the `geographicalName` property. -### Two windings transformer conversion -The two windings transformers connected between two real nodes are converted into a [two windings transformer](../../grid_model/network_subnetwork.md#two-windings-transformer). -If the current limits is defined, a permanent limit is created only for the second side. +### Two-winding transformer conversion +The two-winding transformers connected between two real nodes are converted into a [two-winding transformer](../../grid_model/network_subnetwork.md#two-winding-transformer). +If the current limits are defined, a permanent limit is created only for the second side. The element name of the transformer is stored in the `elementName` property and the nominal power is stored in the `nominalPower` property. -If a two windings transformer is connected between a real node and an X-node, a fictitious intermediate voltage level is created, -with a single bus called an Y-node. This new voltage level is created in the same substation than the real node. +If a two-winding transformer is connected between a real node and an X-node, a fictitious intermediate voltage level is created, +with a single bus called a Y-node. This new voltage level is created in the same substation as the real node. The transformer is created between the real node and the new Y-node, and the X-node is converted into a dangling line. The only difference with a classic X-node conversion, is that the electrical characteristic are hold by the transformer and set to `0` for the dangling line, except for the reactance that is set to $0.05\space\Omega$. @@ -211,8 +211,8 @@ Each country present results in the creation of an Area in the IIDM model with: - Area **Boundaries**: all DanglingLines in the Country By default, all Area Boundaries are flagged as AC, because the UCTE-DEF format does not have any HVDC explicit description. -In order to specify which boundaries should be considered as DC in the conversion, you may supply a list of X-nodes IDs in the +To specify which boundaries should be considered as DC in the conversion, you may supply a list of X-nodes IDs in the [`ucte.import.areas-dc-xnodes` option](#ucteimportareas-dc-xnodes). -**Note:** Creating areas for German sub-regions / TSOs is not supported today (D2, D4, D7, D8). +**Note:** Creating areas for German subregions / TSOs is not supported today (D2, D4, D7, D8). Let us know if you have this use case by entering an issue in our GitHub. We also welcome contributions. diff --git a/docs/grid_exchange_formats/ucte/index.md b/docs/grid_exchange_formats/ucte/index.md index a08671e8ea2..d6cb7fbc4c4 100644 --- a/docs/grid_exchange_formats/ucte/index.md +++ b/docs/grid_exchange_formats/ucte/index.md @@ -9,6 +9,6 @@ export.md The [**U**nion for the **C**o-ordination of **T**ransmission of **E**lectricity](https://www.ucte.org), created in 1951, coordinated the operation and development of the electricity transmission grid for the Continental European synchronously operated transmission grid, thus providing a reliable platform to all participants of the Internal Electricity Market and beyond. -In 1999, UCTE re-defined itself as an association of TSOs in the context of the Internal Energy Market. Building on its experience with recommendations, UCTE turned to make its technical standards. These standards became indispensable for the reliable international operation of the high voltage grids which are all working at one “heart beat”: the 50 Hz UCTE frequency related to the nominal balance between generation and the electricity demand of some 500 million people in one of the biggest electrical synchronous interconnections worldwide. +In 1999, UCTE re-defined itself as an association of TSOs in the context of the Internal Energy Market. Building on its experience with recommendations, UCTE turned to make its technical standards. These standards became indispensable for the reliable international operation of the high-voltage grids which are all working at one “heart beat”: the 50Hz UCTE frequency related to the nominal balance between generation and the electricity demand of some 500 million people in one of the biggest electrical synchronous interconnections worldwide. -On 1 July 2009 UCTE was wound up. All operational tasks were transferred to ENTSO-E. +On 1 July 2009, UCTE was wound up. All operational tasks were transferred to ENTSO-E. diff --git a/docs/grid_features/extraction.md b/docs/grid_features/extraction.md index 4311c74f293..92a77f5086a 100644 --- a/docs/grid_features/extraction.md +++ b/docs/grid_features/extraction.md @@ -4,8 +4,8 @@ This module is used to extract a portion of a network on an area of interest def ## Define an area of interest -The network reduction is relying on a `NetworkPredicate` instance, to define an area of interest (i.e. a list of equipments to keep in the network after the reduction). -The equipments outside this area are removed and the lines, transformers and HVDC lines connecting voltage levels inside and outside this area will be replaced by injections (loads or dangling lines, depending on the implementation). +The network reduction is relying on a `NetworkPredicate` instance, to define an area of interest (i.e., a list of equipments to keep in the network after the reduction). +The equipments outside this area are removed, and the lines, transformers and HVDC lines connecting voltage levels inside and outside this area will be replaced by injections (loads or dangling lines, depending on the implementation). Before doing the reduction, one has to define the area of interest, using the `com.powsybl.iidm.reducer.NetworkPredicate` interface. This interface declares two methods: @@ -94,9 +94,9 @@ PowSyBl provides a default implementation of this interface, but you can provide The `com.powsybl.iidm.reducer.DefaultNetworkReducer` class is the PowSyBl implementation of the `NetworkReducer` interface. -It replaces the lines in the _border_ group by [loads](../grid_model/network_subnetwork.md#load) or [dangling lines](../grid_model/network_subnetwork.md#dangling-line) depending on the [options](#options), the two windings transformers and the HVDC lines by [loads](../grid_model/network_subnetwork.md#load). +It replaces the lines in the _border_ group by [loads](../grid_model/network_subnetwork.md#load) or [dangling lines](../grid_model/network_subnetwork.md#dangling-line) depending on the [options](#options), the two-winding transformers and the HVDC lines by [loads](../grid_model/network_subnetwork.md#load). -The three windings transformers are replaced by a [load](../grid_model/network_subnetwork.md#load) if only one connected voltage level is kept. If two out of three connected voltage levels are kept, the third one is automatically added by the `DefaultNetworkReducer` to the voltage levels to keep. +The three-winding transformers are replaced by a [load](../grid_model/network_subnetwork.md#load) if only one connected voltage level is kept. If two out of three connected voltage levels are kept, the third one is automatically added by the `DefaultNetworkReducer` to the voltage levels to keep. #### Replacement @@ -111,7 +111,7 @@ However, the operational limits and extensions from the original branch are not ##### Replacements by dangling lines The dangling line created in place of a line has the same ID and name as the replaced line. The resistance and reactance of the dangling line are equals to half of the resistance and reactance of the replaced line (we consider that the line is cut in the middle). -The conductance and susceptance are set to the $G_1$ and $B_1$ or to $G_2$ and $B_2$ depending on which side is kept in the network. +The conductance and susceptance are set to the $G_1$ and $B_1$ or to $G_2$ and $B_2$, depending on which side is kept in the network. The $P_0$ and $Q_0$ are set to the $P$ and $Q$ of the corresponding terminal, depending on which side is kept in the network. If the line is disconnected, $P_0$ and $Q_0$ are set to `NaN`. The connectivity information (node or bus depending on the voltage level topology) is kept. @@ -141,9 +141,9 @@ ReductionOptions options = new ReductionOptions() #### Observers -The `com.powsybl.iidm.reducer.NetworkReducerObserver` is an interface that allows to be notified each time an `Identifiable` is removed or replaced. This interface provides several methods, one per `Identifiable` sub class managed by the `DefaultNetworkReducer` implementation. There are 2 types of events: -- a _replace_ event, when an AC line, a two or three windings transformer or an HVDC line is replaced by a load or a danging line -- a _remove_ event, when a substation, a voltage level, a line, a two or three windings transformer or an HVDC line is removed. +The `com.powsybl.iidm.reducer.NetworkReducerObserver` is an interface that allows to be notified each time an `Identifiable` is removed or replaced. This interface provides several methods, one per `Identifiable` subclass managed by the `DefaultNetworkReducer` implementation. There are 2 types of events: +- a _replace_ event, when an AC line, a two or three-winding transformer or an HVDC line is replaced by a load or a danging line +- a _remove_ event, when a substation, a voltage level, a line, a two or three-winding transformer or an HVDC line is removed. ```java public interface NetworkReducerObserver { @@ -243,7 +243,7 @@ $> ./itools convert-network --input-file /home/user/input.xiidm ``` ### Observers -This example shows how to implement the `NetworkReducerObserver` and log information each time an equipment is replaced. +This example shows how to implement the `NetworkReducerObserver` and log information each time the equipment is replaced. ```java NetworkReducerObserver observer = new DefaultNetworkReducerObserver() { diff --git a/docs/grid_features/import_post_processor.md b/docs/grid_features/import_post_processor.md index 64c695dcf50..ec19a6825cb 100644 --- a/docs/grid_features/import_post_processor.md +++ b/docs/grid_features/import_post_processor.md @@ -38,7 +38,7 @@ println "Network " + network.getId() + " (" + network.getSourceFormat()+ ") is i ``` ## LoadFlow post-processor -Mathematically speaking, a [load flow](../simulation/loadflow/index) result is fully defined by the complex voltages at each node. The consequence is that most load flow algorithms converge very fast if they are initialized with voltages. As a result, it happens that load flow results include only voltages and not flows on branches. This post-processors computes the flows given the voltages. The equations (Kirchhoff law) used are the same as the one used in the [load flow validation](../user/itools/loadflow-validation.md#load-flow-results-validation) to compute $P_1^{\text{calc}}$, $Q_1^{\text{calc}}$, $P_2^{\text{calc}}$, $Q_2^{\text{calc}}$ for branches and $P_3^{\text{calc}}$, $Q_3^{\text{calc}}$ in addition for three-windings transformers. +Mathematically speaking, a [load flow](../simulation/loadflow/index) result is fully defined by the complex voltages at each node. The consequence is that most load flow algorithms converge very fast if they are initialized with voltages. As a result, it happens that load flow results include only voltages and not flows on branches. This post-processors computes the flows given the voltages. The equations (Kirchhoff law) used are the same as the one used in the [load flow validation](../user/itools/loadflow-validation.md#load-flow-results-validation) to compute $P_1^{\text{calc}}$, $Q_1^{\text{calc}}$, $P_2^{\text{calc}}$, $Q_2^{\text{calc}}$ for branches and $P_3^{\text{calc}}$, $Q_3^{\text{calc}}$ in addition for three-winding transformers. To use this post-processor, add the `com.powsybl:powsybl-loadflow-results-completion` to your classpath and enable it setting the `postProcessors` property of the `import` module. @@ -56,7 +56,7 @@ import: ``` -**Note:** This post-processor rely on the [load flow results completion]() module. +**Note:** This post-processor relies on the [load flow results completion]() module. ## Geographical data import post-processor @@ -72,7 +72,7 @@ Using the links in the table below, you can obtain the RTE data CSV files, to be | Aerial lines | [https://odre.opendatasoft.com/explore/dataset/lignes-aeriennes-rte-nv/export/](https://odre.opendatasoft.com/explore/dataset/lignes-aeriennes-rte-nv/export/) | | Underground lines | [https://odre.opendatasoft.com/explore/dataset/lignes-souterraines-rte-nv/export/](https://odre.opendatasoft.com/explore/dataset/lignes-souterraines-rte-nv/export/) | -(To download these files, you should first accept the usage conditions of the ODRÉ website, which can be found - in French only - at the bottom of the pages, and the Etalab Open License v2.0, available in English [here](https://www.etalab.gouv.fr/wp-content/uploads/2018/11/open-licence.pdf).) +(To download these files, you should first accept the usage conditions of the ODRÉ website, which can be found—in French only—at the bottom of the pages, and the Etalab Open License v2.0, available in English [here](https://www.etalab.gouv.fr/wp-content/uploads/2018/11/open-licence.pdf).)
diff --git a/docs/grid_features/loadflow_validation.md b/docs/grid_features/loadflow_validation.md index aac90676b4d..3a5cb4989ad 100644 --- a/docs/grid_features/loadflow_validation.md +++ b/docs/grid_features/loadflow_validation.md @@ -14,7 +14,7 @@ $$\begin{equation} \end{equation}$$ ## Branches -Lines and two windings transformers are converted into classical PI models: +Lines and two-winding transformers are converted into classical PI models: ``` V1*exp(j*theta1) rho1*exp(j*alpha1) r+j*x rho2*exp(j*alpha2) V2*exp(j*theta2) @@ -36,19 +36,19 @@ Lines and two windings transformers are converted into classical PI models: - $(g_1, b_1)$ and $(g_2, b_2)$: Complex shunt impedance on each side (S). - $(r, x)$: Complex series impedance $(\Omega)$. -Thanks to Kirchhoff laws (see the [line](../grid_model/network_subnetwork.md#line) and [2-winding transformer](../grid_model/network_subnetwork.md#two-windings-transformer) documentation), estimations of powers are computed according to the voltages and the characteristics of the branch: +Thanks to Kirchhoff laws (see the [line](../grid_model/network_subnetwork.md#line) and [2-winding transformer](../grid_model/network_subnetwork.md#two-winding-transformer) documentation), estimations of powers are computed according to the voltages and the characteristics of the branch: $(P_1^{calc}, Q_1^{calc}, P_2^{calc}, Q_2^{calc}) = f(\text{Voltages}, \text{Characteristics})$ -## Three-windings transformers -To be implemented, based on a conversion into 3 two-windings transformers. +## Three-winding transformers +To be implemented, based on a conversion into 3 two-winding transformers. ## Generators ### Active power There may be an imbalance between the sum of generator active power setpoints $\text{targetP}$ on one side and consumption -and losses on the other side, after the load flow optimization process. Note that, if it is possible to modify the setpoints during the computation -(for example if the results were computed by an Optimal Power Flow and not a Power Flow), there should be no imbalance left. +and losses on the other side, after the load flow optimization process. Note that if it is possible to modify the setpoints during the computation +(for example, if the results were computed by an Optimal Power Flow and not a Power Flow), there should be no imbalance left. In case of an imbalance between the sum of generator active power setpoints $\text{targetP}$ on one side and consumption and losses on the other side, the generation $P$ of some units has to be adjusted. @@ -101,7 +101,7 @@ A shunt is expected to generate reactive power according to the number of activa $\left| Q + \text{#sections} * B V^2 \right| < \epsilon$ ## Static VAR Compensators -Static VAR Compensators behave like generators producing 0 active power except that their reactive bounds are expressed +Static VAR Compensators behave like generators producing zero active power except that their reactive bounds are expressed in susceptance, so that they are voltage dependent. $targetP = 0$ MW @@ -123,7 +123,7 @@ To be done. ## Transformers with a ratio tap changer -Transformers with a ratio tap changer have a tap with a finite discrete number of position that allows to change their transformer ratio. +Transformers with a ratio tap changer have a tap with a finite discrete number of positions that allows to change their transformer ratio. Let's assume that the logic is based on deadband: if the deviation between the measurement and the setpoint is higher than the deadband width, the tap position is increased or decreased by one unit. diff --git a/docs/grid_model/additional.md b/docs/grid_model/additional.md index 448c3dd24b7..cc504bf09fb 100644 --- a/docs/grid_model/additional.md +++ b/docs/grid_model/additional.md @@ -68,11 +68,11 @@ generator.newReactiveCapabilityCurve() ## Loading Limits [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/LoadingLimits.html) -Some equipment have operational limits regarding the current, active power or apparent power value, corresponding to the equipment's physical limitations (related to heating). +Some equipment has operational limits regarding the current, active power or apparent power value, corresponding to the equipment's physical limitations (related to heating). Loading limits can be declined into active power limits (in MW), apparent power limits (in kVA) and current limits (in A). They may be set for [lines](./network_subnetwork.md#line), -[dangling lines](./network_subnetwork.md#dangling-line), [two windings transformers](./network_subnetwork.md#two-windings-transformer) and [three windings transformers](./network_subnetwork.md#three-windings-transformer). The active power limits are in absolute value. +[dangling lines](./network_subnetwork.md#dangling-line), [two-winding transformers](./network_subnetwork.md#two-winding-transformer) and [three-winding transformers](./network_subnetwork.md#three-winding-transformer). The active power limits are in absolute value. Loading limits are defined by one permanent limit and any number of temporary limits (zero or more). The permanent limit sets the current, active power or apparent power absolute value under which the equipment can safely @@ -83,16 +83,16 @@ A temporary limit thus has an **acceptable duration**. The component on which the current limits are applied can safely remain between the preceding limit (it could be another temporary limit or a permanent limit) and this limit for a duration up to the acceptable duration. -Please look at this scheme to fully understand the modelling (the following example shows current limits but this modelling is valid for all loading limits): +Please look at this scheme to fully understand the modeling (the following example shows current limits, but this modeling is valid for all loading limits): ![Loading limits model](img/current-limits.svg){width="50%" align=center class="only-light"} ![Loading limits model](img/dark_mode/current-limits.svg){width="50%" align=center class="only-dark"} -Note that, following this modelling, in general the last temporary limit (the higher one in value) should be infinite with an acceptable duration different from zero, except for tripping current modeling where the last temporary limit is infinite with an acceptable duration equal to zero. If temporary limits are modeled, the permanent limit becomes mandatory. +Note that, following this modeling, in general, the last temporary limit (the higher one in value) should be infinite with an acceptable duration different from zero, except for tripping current modeling where the last temporary limit is infinite with an acceptable duration equal to zero. If temporary limits are modeled, the permanent limit becomes mandatory. (limit-group-collection)= ### Limit group collection -In network development studies or in an operational context (CGMES), we can have a set of operational limits according to the season (winter vs summer for example), the time of the day (day vs night) etc. +In network development studies or in an operational context (CGMES), we can have a set of operational limits according to the season (winter vs summer, for example), the time of the day (day vs night) etc. In PowSyBl, users can store a collection of limits: - Active power limits, apparent power limits and current limits are gathered into an `OperationalLimitsGroup` object. This group has an `id`. - Lines and transformers are associated with a collection of `OperationalLimitsGroup` (one collection per side/leg). @@ -151,11 +151,11 @@ CurrentLimits currentLimits = network.getDanglingLine("DL").newCurrentLimits() ## Phase tap changer [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/PhaseTapChanger.html) -A phase tap changer can be added to either [two windings transformers](./network_subnetwork.md#two-windings-transformer) or [three windings transformers' legs](./network_subnetwork.md#three-windings-transformer-leg). +A phase tap changer can be added to either [two-winding transformers](./network_subnetwork.md#two-winding-transformer) or [three-winding transformers' legs](./network_subnetwork.md#three-winding-transformer-leg). **Specifications** -A phase tap changer is described by a set of tap positions (or steps) within which the transformer or transformer leg can operate. Additionally to that set of steps, it is necessary to specify: +A phase tap changer is described by a set of tap positions (or steps) within which the transformer or transformer leg can operate. Additionally, to that set of steps, it is necessary to specify: - the lowest tap position - the highest tap position - the position index of the current tap (which has to be within the highest and lowest tap position bounds) @@ -184,7 +184,7 @@ Each step of a phase tap changer has the following attributes: **Example** -This example shows how to add a phase tap changer to a two windings transformer: +This example shows how to add a phase tap changer to a two-winding transformer: ```java twoWindingsTransformer.newPhaseTapChanger() .setLowTapPosition(-1) @@ -224,11 +224,11 @@ twoWindingsTransformer.newPhaseTapChanger() ## Ratio tap changer [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/RatioTapChanger.html) -A ratio tap changer can be added to either [two windings transformers](./network_subnetwork.md#two-windings-transformer) or [three windings transformers' legs](./network_subnetwork.md#three-windings-transformer-leg). +A ratio tap changer can be added to either [two-winding transformers](./network_subnetwork.md#two-winding-transformer) or [three-winding transformers' legs](./network_subnetwork.md#three-winding-transformer-leg). **Specifications** -A ratio tap changer is described by a set of tap positions (or steps) within which the transformer or transformer leg can operate (or be operated offload). Additionally to that set of steps, it is necessary to specify: +A ratio tap changer is described by a set of tap positions (or steps) within which the transformer or transformer leg can operate (or be operated offload). Additionally, to that set of steps, it is necessary to specify: - the lowest tap position - the highest tap position - the position index of the current tap (which has to be within the highest and lowest tap position bounds) @@ -252,7 +252,7 @@ Each step of a ratio tap changer has the following attributes: **Example** -This example shows how to add a ratio tap changer to a two windings transformer: +This example shows how to add a ratio tap changer to a two-winding transformer: ```java twoWindingsTransformer.newRatioTapChanger() .setLowTapPosition(-1) diff --git a/docs/grid_model/extensions.md b/docs/grid_model/extensions.md index 54519106dce..363b38a1fb1 100644 --- a/docs/grid_model/extensions.md +++ b/docs/grid_model/extensions.md @@ -2,10 +2,10 @@ # Grid model extensions The grid model contains enough data to basically describe supported components and run power flow computations, but it may not be sufficient for more complex studies. -The extensions are a way to add additional structured data to an equipment to extend its features. +The extensions are a way to add additional structured data to a piece of equipment to extend its features. The extensions can be attached to any objects of a network or to the network itself. -Some extensions are mono-variant meaning the data are identical for all the variants of the network. However, some of them are multi-variants to allow a different value for each variant of the network. It's typically the case for the [LoadDetail](#load-detail) extension that give the distribution of the constant part and the thermo-sensitive part of a consumption. +Some extensions are mono-variant, meaning the data are identical for all the variants of the network. However, some of them are multi-variants to allow a different value for each variant of the network. It's typically the case for the [LoadDetail](#load-detail) extension that give the distribution of the constant part and the thermosensitive part of the consumption. Note that some extensions provided by PowSyBl aren't supported in the [persistent implementation of IIDM](../../developer/repositories/powsybl-network-store-server.md). @@ -39,7 +39,7 @@ This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. (branch-observability-extension)= ## Branch observability -This extension models branches' flows' observability on both sides, obtained after a state estimation. +This extension models branch flow observability on both sides, obtained after a state estimation. | Attribute | Type | Unit | Required | Default value | Description | |------------|----------------------|------|----------|---------------|---------------------------------------------------------| @@ -62,7 +62,7 @@ This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. (busbar-section-position-extension)= ## Busbar section position -This extension gives positions information about a busbar section. The `busbarIndex` gives the position of the busbar section relatively to other busbars. The `sectionIndex` gives the position of the busbar section within the corresponding busbar. Note that a busbar is a set of busbar sections. Hence, the sections of a same busbar should have the same busbar index. The busbar indices induce an order of busbars within the voltage level, which usually reflects the busbars physical relative positions. Similarly, the section indices induce an order of sections of a same busbar, which usually reflects their physical relative position. +This extension gives positions information about a busbar section. The `busbarIndex` gives the position of the busbar section relative to other busbar sections. The `sectionIndex` gives the position of the busbar section within the corresponding busbar. Note that a busbar is a set of busbar sections. Hence, the sections of the same busbar should have the same busbar index. The busbar indices induce an order of busbars within the voltage level, which usually reflects the busbars physical relative positions. Similarly, the section indices induce an order of sections of the same busbar, which usually reflects their physical relative position. (connectable-position-extension)= ## Connectable position @@ -72,7 +72,7 @@ This extension gives positions information about a busbar section. The `busbarIn (coordinated-reactive-control-extension)= ## Coordinated reactive control -Some generators can be coordinated to control reactive power in a point of the network. This extension is used to configure the percent of reactive coordinated control that comes from a generator. This extension is attached to a [generator](network_subnetwork.md#generator). +Some generators can be coordinated to control reactive power in a point of the network. This extension is used to configure the percentage of reactive-coordinated control that comes from a generator. This extension is attached to a [generator](network_subnetwork.md#generator). | Attribute | Type | Unit | Required | Default value | Description | |-----------|-----------------|------|----------|---------------|-----------------------------------------------| @@ -85,14 +85,14 @@ generator.newExtension(CoordinatedReactiveControlAdder.class) .add(); ``` -Please note that the sum of the $qPercent$ values of the generators coordinating a same point of the network must be 100. +Please note that the sum of the $qPercent$ values of the generators coordinating the same point of the network must be 100. This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. (discrete-measurements-extensions)= ## Discrete measurements -This extension is used to store discrete measurements (such as tap positions, switch positions etc.) collected in substations. +This extension is used to store discrete measurements (such as tap positions, switch positions, etc.) collected in substations. | Attribute | Type | Unit | Required | Default value | Description | |----------------------|---------------------------------|------|----------|---------------|------------------------------------------------------| @@ -131,7 +131,7 @@ $$P = P0 + k~(ph1 - ph2)$$ (hvdc-operator-active-power-range-extension)= ## HVDC operator active power range -This extension enables to replace the operational limits of an DC line in AC emulation. In that case, the VSC converter stations min active power and max active power are not used. +This extension enables to replace the operational limits of a DC line in AC emulation. In that case, the VSC converter stations min active power and max active power are not used. (generator-enstoe-category-extension)= ## Generator ENTSO-E category @@ -141,7 +141,7 @@ This extension enables to replace the operational limits of an DC line in AC emu (generator-short-circuit)= ## Generator short-circuit -This extension models the generators data used for short-circuit calculations. Depending on the type of short-circuit study to be +This extension models the generator data used for short-circuit calculations. Depending on the type of short-circuit study to be performed, either the transient or the sub-transient reactance should be filled. The reactance of the step-up transformer should be filled if the generator has a transformer that is not directly modeled in the network. @@ -186,7 +186,7 @@ The code is similar for every identifiable. (injection-observability-extension)= ## Injection observability -This extension models injections' flows' observability, obtained after a state estimation. +This extension models injection flow observability, obtained after a state estimation. | Attribute | Type | Unit | Required | Default value | Description | |-----------|----------------------|------|----------|---------------|---------------------------------------------| @@ -248,7 +248,7 @@ S_{Ci_{Load}}=S_{C}=P_{C}+j.Q_{C} \\ \end{align} $$ -But for a balanced load flow, the constant power load $P$ and $Q$ refer to the positive sequence load. Given that, in balanced conditions, the load for zero and negative sequences should always be zero. However, in real life, power loads are better defined in the ABC three phases representation. The load extension addresses this issue keeping the default behavior for balanced conditions. +But for a balanced load flow, the constant power load $P$ and $Q$ refer to the positive sequence load. Given that, in balanced conditions, the load for zero and negative sequences should always be zero. However, in real life, power loads are better defined in the ABC three-phase representation. The load extension addresses this issue keeping the default behavior for balanced conditions. Balanced load flow conditions: @@ -292,14 +292,14 @@ S_{Ci_{Load}}=P_{Load}+j.Q_{Load} \\ \end{align} $$ -| Attribute | Type | Unit | Required | Default value | Description | -| --------- | ---- | ---- | -------- | ------------- | ----------- | -| deltaPa | double | MW | No | 0 | The unbalanced part of the active power setpoint at phase A (balanced parts for each phase are described by its active power setpoint $P0$ and its reactive power setpoint $Q0$) | -| deltaQa | double | MVar | No | 0 | The unbalanced part of the reactive power setpoint at phase A | -| deltaPb | double | MW | No | 0 | The unbalanced part of the active power setpoint at phase B | -| deltaQb | double | MVar | No | 0 | The unbalanced part of the reactive power setpoint at phase B | -| deltaPc | double | MW | No | 0 | The unbalanced part of the active power setpoint at phase C | -| deltaQc | double | MVar | No | 0 | The unbalanced part of the reactive power setpoint at phase C | +| Attribute | Type | Unit | Required | Default value | Description | +|-----------|--------|------|----------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| deltaPa | double | MW | No | 0 | The unbalanced part of the active power setpoint at phase A (balanced parts for each phase are described by its active power setpoint $P0$ and its reactive power setpoint $Q0$) | +| deltaQa | double | MVar | No | 0 | The unbalanced part of the reactive power setpoint at phase A | +| deltaPb | double | MW | No | 0 | The unbalanced part of the active power setpoint at phase B | +| deltaQb | double | MVar | No | 0 | The unbalanced part of the reactive power setpoint at phase B | +| deltaPc | double | MW | No | 0 | The unbalanced part of the active power setpoint at phase C | +| deltaQc | double | MVar | No | 0 | The unbalanced part of the reactive power setpoint at phase C | Here is how to add a load detail extension to a load: ```java @@ -316,8 +316,8 @@ This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. (load-detail-extension)= ## Load detail A load is described by its active power setpoint $P0$ and its reactive power setpoint $Q0$. This extension is used to detail : -- In the total amount of active power what is fixed and what is time-dependant (also called variable). The time-dependant part can be adjusted for production equals consumption. -- In the total amount of reactive power what is fixed and what is time-dependant (also called variable). +- In the total amount of active power what is fixed and what is time-dependent (also called variable). The time-dependent part can be adjusted for production equals consumption. +- In the total amount of reactive power what is fixed and what is time-dependent (also called variable). | Attribute | Type | Unit | Required | Default value | Description | |-----------------------|--------|------|----------|---------------|---------------------------------------------------------------------| @@ -374,7 +374,7 @@ three-winding transformers, HVDC line and a dangling line. The status could be: ## Reference Priority This extension is attached to a Generator, or a BusBarSection or a Load and is used to define the angle reference bus of -a power flow calculation, i.e. which bus will be used with a zero voltage angle. +a power flow calculation, i.e. which bus will be used with a zero-voltage angle. Use this extension before a computation to force the reference bus selection. The support of this feature by Load Flow implementations may vary. For example, the [OpenLoadFlow](../simulation/powerflow/openlf.md) implementation today supports Reference Priorities on generators only when this feature is activated. @@ -422,7 +422,7 @@ This extension is provided by the `com.powsybl:powsybl-iidm-api` module. (remote-reactive-power-control-extension)= ## Remote reactive power control -This extensions is used for generators with a remote reactive control. +This extension is used for generators with a remote reactive control. | Attribute | Type | Unit | Required | Default value | Description | |--------------------|------------|------|----------|---------------|----------------------------------------------------| @@ -481,10 +481,10 @@ station.newExtension(SubstationPositionAdder.class) .add(); ``` -(three-windings-transformer-phase-angle-clock-extension)= -## Three-windings transformer phase angle clock +(three-winding-transformer-phase-angle-clock-extension)= +## Three-winding transformer phase angle clock -This extension is used to model the Vector Group of a three windings transformer. The phase angle clock could be modeled at leg 2, leg 3 or both legs 2 and 3 and of a three windings transformer (network side). The voltage phase angle displacement is represented with clock hours. The valid values are `0` to `11`. This extension is attached to a [three windings transformer](network_subnetwork.md#three-windings-transformer). +This extension is used to model the Vector Group of a three-winding transformer. The phase angle clock could be modeled at leg 2, leg 3 or both legs 2 and 3 and of a three-winding transformer (network side). The voltage phase angle displacement is represented with clock hours. The valid values are `0` to `11`. This extension is attached to a [three-winding transformer](network_subnetwork.md#three-winding-transformer). | Attribute | Type | Unit | Required | Default value | Description | |---------------------|------------|-------|----------|---------------|-----------------------------------------------| @@ -500,10 +500,10 @@ transformer.newExtension(ThreeWindingsTransformerPhaseAngleClock.class) This extension is provided by the `com.powsybl:powsybl-iidm-extensions` module. -(three-windings-transformer-to-be-estimated-extension)= -## Three-windings transformer to be estimated +(three-winding-transformer-to-be-estimated-extension)= +## Three-winding transformer to be estimated -This extension is used to indicate if a three-winding transformer tap changer is to be estimated during a state estimation, i.e. if its tap position should be an output of the state estimation. +This extension is used to indicate if a three-winding transformer tap changer is to be estimated during a state estimation, i.e., if its tap position should be an output of the state estimation. * The three-winding transformer model offers the possibility to have up to 3 ratio tap changers and up to 3 phase tap changers. Each tap changer can be estimated or not. * If a tap changer is not to be estimated, it should not be changed during a state estimation (its tap position is merely an input of the state estimation). @@ -529,10 +529,10 @@ transformer.newExtension(ThreeWindingsTransformerToBeEstimatedAdder.class) .add(); ``` -(two-windings-transformer-phase-angle-clock-extension)= -## Two-windings transformer phase angle clock +(two-winding-transformer-phase-angle-clock-extension)= +## Two-winding transformer phase angle clock -This extension is used to model the Vector Group of a two windings transformer. The phase angle clock is modeled at side 2 of a two windings transformer. The voltage phase angle displacement is represented with clock hours. The valid values are 0 to 11. This extension is attached to a [two windings transformer](network_subnetwork.md#two-windings-transformer). +This extension is used to model the Vector Group of a two-winding transformer. The phase angle clock is modeled at side 2 of a two-winding transformer. The voltage phase angle displacement is represented with clock hours. The valid values are 0 to 11. This extension is attached to a [two-winding transformer](network_subnetwork.md#two-winding-transformer). | Attribute | Type | Unit | Required | Default value | Description | |-----------------|------------|-------|----------|---------------|--------------------------------------| @@ -546,10 +546,10 @@ transformer.newExtension(TwoWindingsTransformerPhaseAngleClockAdder.class) This extension is provided in the module `com.powsybl:powsybl-iidm-extensions`. -(two-windings-transformer-to-be-estimated-extension)= -## Two-windings transformer to be estimated +(two-winding-transformer-to-be-estimated-extension)= +## Two-winding transformer to be estimated -This extension is used to indicate if a two-winding transformer tap changer is to be estimated during a state estimation, i.e. if its tap position should be an output of the state estimation. +This extension is used to indicate if a two-winding transformer tap changer is to be estimated during a state estimation, i.e., if its tap position should be an output of the state estimation. * A two-winding transformer has a ratio tap changer and/or a phase tap changer. Each tap changer can be estimated or not. * If a tap changer is not to be estimated, it should not be changed during a state estimation (its tap position is merely an input of the state estimation). diff --git a/docs/grid_model/index.md b/docs/grid_model/index.md index 30e37971a04..c8dccbba5d6 100644 --- a/docs/grid_model/index.md +++ b/docs/grid_model/index.md @@ -10,7 +10,7 @@ going_further.md Powsybl features are strongly based on an internal grid model initially developed under the iTesla project, a research project funded by the [European Union 7th Framework programme](https://cordis.europa.eu/project/id/283012) (FP7). The grid model is known as `iidm` (iTesla Internal Data Model). One of the iTesla outputs was a toolbox designed to support the decision-making process of power system operation from two-days ahead to real time. The `iidm` grid model was at the center of the toolbox. -To build an electrical network model, the substations must be defined first. The equipment of a substation (busbar sections, switches, buses, loads, generators, shunt compensators, static VAR compensators, HVDC converters stations, etc.) are grouped in voltage levels. Transformers present in a substation connect its different voltage levels. Transmission lines (AC and DC) connect the substations. +To build an electrical network model, the substations must be defined first. The equipment of a substation (busbar sections, switches, buses, loads, generators, shunt compensators, static VAR compensators, HVDC converters stations, etc.) is grouped in voltage levels. Transformers present in a substation connect its different voltage levels. Transmission lines (AC and DC) connect the substations. The grid model allows a full representation of the substation connectivity where all the switching devices and busbar sections are defined, this topology is called node/breaker view. Automated topology calculation allows for the calculation of the network bus/breaker view as well as the network bus view. diff --git a/docs/grid_model/network_subnetwork.md b/docs/grid_model/network_subnetwork.md index 876dc5ec418..93fc2b1d287 100644 --- a/docs/grid_model/network_subnetwork.md +++ b/docs/grid_model/network_subnetwork.md @@ -1,16 +1,16 @@ # Network and subnetwork -In the following sections the different network components are described in terms of their main attributes and electrotechnical representation. The attributes shared by all the network components are described in the next table: +In the following sections, the different network components are described in terms of their main attributes and electrotechnical representation. The attributes shared by all the network components are described in the next table: -| Attribute | Description | -| --------- | ----------- | -| $Id$ | Unique Id assigned to each network component | -| $Name$ | Human readable identifier (not necessary unique) | -| $Fictitious$ | To identify non-physical network components | -| $Aliases$ | Additional unique identifiers associated with each network component | -| $Properties$ | To add additional data items to network components | +| Attribute | Description | +|--------------|----------------------------------------------------------------------| +| $Id$ | Unique Id assigned to each network component | +| $Name$ | Human readable identifier (not necessary unique) | +| $Fictitious$ | To identify non-physical network components | +| $Aliases$ | Additional unique identifiers associated with each network component | +| $Properties$ | To add additional data items to network components | -All equipment and the network itself are identified by a unique identifier which is the only required attribute. They can have a human-readable name. offer the possibility of adding additional unique identifiers to each component. An alias can be qualified to indicate what it corresponds to. +All equipment and the network itself are identified by a unique identifier which is the only required attribute. They can have a human-readable name. Offers the possibility of adding additional unique identifiers to each component. An alias can be qualified to indicate what it corresponds to. Properties allow associating additional arbitrary data items under the general schema of pairs ``. @@ -31,10 +31,10 @@ In the PowSyBl grid model, the Network contains [substations](#substation), whic **Characteristics** -| Attribute | Description | -| --------- | ----------- | -| $SourceFormat$ | Source format of the imported network model | -| $CaseDate$ | Date and time of the target network that is being modeled | +| Attribute | Description | +|--------------------|-------------------------------------------------------------------------| +| $SourceFormat$ | Source format of the imported network model | +| $CaseDate$ | Date and time of the target network that is being modeled | | $ForecastDistance$ | Number of minutes between the network generation date and the case date | The `SourceFormat` attribute is a required attribute that indicates the origin of the network model automatically set by the [importers](../grid_exchange_formats/index.md). If the case date and the forecast distance cannot be found in the case file, the network is considered as a snapshot: the case date is set to the current date, and the forecast distance is set to `0`. @@ -49,11 +49,11 @@ A substation represents a specific geographical location with equipment grouped **Characteristics** -| Attribute | Description | -| --------- |---------------------------------------------------------------------| -| $Country$ | To specify in which country the substation is located | -| $GeographicalTags$ | They make it possible to accurately locate the substation | -| $TSO$ | To track to which Transmission System Operator the substation belongs | +| Attribute | Description | +|--------------------|-----------------------------------------------------------------------| +| $Country$ | To specify in which country the substation is located | +| $GeographicalTags$ | They make it possible to accurately locate the substation | +| $TSO$ | To track to which Transmission System Operator the substation belongs | All three attributes are optional. @@ -68,12 +68,12 @@ A voltage level contains equipment with the same nominal voltage. Two voltage le **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $NominalVoltage$ | kV | Nominal base voltage | -| $LowVoltageLimit$ | kV | Low voltage limit magnitude | -| $HighVoltageLimit$ | kV | High voltage limit magnitude | -| $TopologyKind$ | | Level of connectivity detail | +| Attribute | Unit | Description | +|--------------------|------|------------------------------| +| $NominalVoltage$ | kV | Nominal base voltage | +| $LowVoltageLimit$ | kV | Low voltage limit magnitude | +| $HighVoltageLimit$ | kV | High voltage limit magnitude | +| $TopologyKind$ | | Level of connectivity detail | **Specifications** @@ -81,7 +81,7 @@ Only `NominalVoltage` and `TopologyKind` are required. The connectivity in each voltage level of the network can be defined at one of two levels: `node/breaker` or `bus/breaker`. The connectivity level can be different in each voltage level of the model. -In `node/breaker` the connectivity is described with the finest level of detail and can provide an exact field representation. This level could be described as a graph structure where the vertices are `Nodes` and the edges are `Switches` (breakers, disconnectors) or internal connections. Each equipment is associated to one `Node` (busbar sections, loads, generators, ..), two `Nodes` (transmission lines, two-windings transformers, ...) or three `Nodes` (three-windings transformers). Each `Node` can only have one associated equipment. `Nodes` do not have an alphanumeric `Id` or `Name`, they are identified by an integer. +In `node/breaker` the connectivity is described with the finest level of detail and can provide an exact field representation. This level could be described as a graph structure where the vertices are `Nodes` and the edges are `Switches` (breakers, disconnectors) or internal connections. Each equipment is associated to one `Node` (busbar sections, loads, generators, ..), two `Nodes` (transmission lines, two-winding transformers, ...) or three `Nodes` (three-winding transformers). Each `Node` can only have one associated equipment. `Nodes` do not have an alphanumeric `Id` or `Name`, they are identified by an integer. Using `bus/breaker` the voltage level connectivity is described with a coarser level of detail. In this case the vertices of the graph are `Buses`, defined explicitly by the user. A `Bus` has an `Id`, and may have a `Name`. Each equipment defines the bus or buses to which it is connected. `Switches` can be defined between buses. @@ -92,7 +92,7 @@ The following diagram represents an example voltage level with two busbars separ ![VoltageLevel](img/voltage-level.svg){width="100%" align=center class="only-light"} ![VoltageLevel](img/dark_mode/voltage-level.svg){width="100%" align=center class="only-dark"} -When defining the model, the user has to specify how the different equipment connect to the network. If the voltage level is built at node/breaker level, the user has to specify a `Node` when adding equipment to the model. If the user is building using bus/breaker level, the `Bus` of the equipment must be specified. Using this information, the model creates a `Terminal` that will be used to manage the point of connection of the equipment to the network. +When defining the model, the user has to specify how the different pieces of equipment connect to the network. If the voltage level is built at node/breaker level, the user has to specify a `Node` when adding equipment to the model. If the user is building using bus/breaker level, the `Bus` of the equipment must be specified. Using this information, the model creates a `Terminal` that will be used to manage the point of connection of the equipment to the network. **Available extensions** @@ -111,14 +111,14 @@ Area boundaries can be terminals of equipments or `Boundary` objects from [dangl The area type is used to distinguish between various area concepts of different granularity. For instance: control areas, bidding zones, countries... -A [voltage level](#voltage-level) can belong to several areas, as long as all areas are of different type. +A [voltage level](#voltage-level) can belong to several areas, as long as all areas are of a different type. -The area boundaries define how interchange are to be calculated for the area. +The area boundaries define how interchange is to be calculated for the area. Area interchange is calculated by summing the active power flows across the area boundaries and can be obtained for AC part only (considering only AC boundaries), for DC part only (considering only DC boundaries) and in total (AC+DC). -Note that if the Area has no boundary explicitly defined, the interchange is considered 0 MW. +Note that if the Area has no boundary explicitly defined, the interchange is considered 0MW. -For area types that are meant to be used for area interchange control, e.g. in Load Flow simulations, the interchange target of the area can be specified as an input for the simulation. +For area types that are meant to be used for area interchange control, e.g., in Load Flow simulations, the interchange target of the area can be specified as an input for the simulation. All area interchange values use the load sign convention: positive values indicate that the area is importing, negative values that the area is exporting. @@ -153,7 +153,7 @@ which are then separated for AC and DC parts. ## Generator [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Generator.html) -A generator is an equipment that injects or consumes active power, and injects or consumes reactive power. It may be used as a controller to hold a voltage or reactive target somewhere in the network, not necessarily directly where it is connected. In that specific case, the voltage or reactive power control is remote. +A generator is a piece of equipment that injects or consumes active power, and injects or consumes reactive power. It may be used as a controller to hold a voltage or reactive target somewhere in the network, not necessarily directly where it is connected. In that specific case, the voltage or reactive power control is remote. ![GeneratorSignConvention](img/generator-sign-convention.svg){width="50%" align=center class="only-light"} ![GeneratorSignConvention](img/dark_mode/generator-sign-convention.svg){width="50%" align=center class="only-dark"} @@ -176,7 +176,7 @@ A generator is an equipment that injects or consumes active power, and injects o **Specifications** -The values `MinP`, `MaxP` and `TargetP` are required. The minimum active power output can not be greater than the maximum active power output. `TargetP` must be inside this active power limits. `RatedS` specifies the nameplate apparent power rating for the unit, it is optional and should be a positive value if it is defined. The [reactive limits](./additional.md#reactive-limits) of the generator are optional, if they are not given the generator is considered with unlimited reactive power. Reactive limits can be given as a pair of [min/max values](./additional.md#min-max-reactive-limits) or as a [reactive capability curve](./additional.md#reactive-capability-curve). +The values `MinP`, `MaxP` and `TargetP` are required. The minimum active power output cannot be greater than the maximum active power output. `TargetP` must be inside this active power limits. `RatedS` specifies the nameplate apparent power rating for the unit, it is optional and should be a positive value if it is defined. The [reactive limits](./additional.md#reactive-limits) of the generator are optional, if they are not given the generator is considered with unlimited reactive power. Reactive limits can be given as a pair of [min/max values](./additional.md#min-max-reactive-limits) or as a [reactive capability curve](./additional.md#reactive-capability-curve). The `VoltageRegulatorOn` attribute is required. It voltage regulation is enabled, then `TargetV` and `RegulatingTerminal` must also be defined. If the voltage regulation is disabled, then `TargetQ` is required. `EnergySource` is optional, it can be: `HYDRO`, `NUCLEAR`, `WIND`, `THERMAL`, `SOLAR` or `OTHER`. @@ -204,10 +204,10 @@ A load is a passive equipment representing a delivery point that consumes or pro **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $P0$ | MW | The active power setpoint | -| $Q0$ | MVar | The reactive power setpoint | +| Attribute | Unit | Description | +|-----------|------|-----------------------------| +| $P0$ | MW | The active power setpoint | +| $Q0$ | MVar | The reactive power setpoint | **Specifications** @@ -254,16 +254,16 @@ In the grid model, loads comprise the following metadata: ## Battery [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/Battery.html) -A battery on the electric grid is an energy storage device that is either capable of capturing energy from the grid or of injecting it into the grid. The electric energy on the grid side is thus transformed into chemical energy on the battery side and vice versa. The power flow is bidirectional and it is controlled via a power electronic converter. +A battery on the electric grid is an energy storage device that is either capable of capturing energy from the grid or of injecting it into the grid. The electric energy on the grid side is thus transformed into chemical energy on the battery side and vice versa. The power flow is bidirectional, and it is controlled via a power electronic converter. **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $P0$ | MW | The Constant active power | -| $Q0$ | MVar | The Constant reactive power | -| $MinP$ | MW | The Minimal active power | -| $MaxP$ | MW | The Maximum active power | +| Attribute | Unit | Description | +|-----------|------|-----------------------------| +| $P0$ | MW | The Constant active power | +| $Q0$ | MVar | The Constant reactive power | +| $MinP$ | MW | The Minimal active power | +| $MaxP$ | MW | The Maximum active power | **Available extensions** @@ -278,12 +278,12 @@ A battery on the electric grid is an energy storage device that is either capabl ## Dangling line [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/DanglingLine.html) -A network may be connected to other networks for which a full description is not available or unwanted. In this case, a boundary line exists between the two networks. In the network of interest, that connection could be represented through a dangling line, which represents the part of that boundary line which is located in it. A dangling line is thus a passive or active component that aggregates a line chunk and a constant power injection, in passive-sign convention. The active and reactive power set points are fixed: the injection represents the power flow that would occur through the connection, were the other network fully described. +A network may be connected to other networks for which a full description is not available or unwanted. In this case, a boundary line exists between the two networks. In the network of interest, that connection could be represented through a dangling line, which represents the part of that boundary line which is located in it. A dangling line is thus a passive or active component that aggregates a line chunk and a constant power injection in passive-sign convention. The active and reactive power set points are fixed: the injection represents the power flow that would occur through the connection, were the other network fully described. ![Dangling line model](img/dangling-line.svg){width="50%" align=center class="only-light"} ![Dangling line model](img/dark_mode/dangling-line.svg){width="50%" align=center class="only-dark"} -A generation part, at boundary side can also be modeled, with a constant active power injection and a constant reactive power injection if the generation part of the dangling line is out of voltage regulation or a voltage target if the regulation is enabled. This fictitious generator can only regulate voltage locally: the regulating terminal can not be set, it is necessary the boundary side of the dangling line. Limits are modeled through $MinP$ and $MaxP$ for active power limits and through [reactive limits](./additional.md#reactive-limits). This generation part is optional. The generation part of the dangling line follows the classical generator sign convention. +A generation part, at boundary side can also be modeled with a constant active power injection and a constant reactive power injection if the generation part of the dangling line is out of voltage regulation or a voltage target if the regulation is enabled. This fictitious generator can only regulate voltage locally: the regulating terminal cannot be set, it is necessary for the boundary side of the dangling line. Limits are modeled through $MinP$ and $MaxP$ for active power limits and through [reactive limits](./additional.md#reactive-limits). This generation part is optional. The generation part of the dangling line follows the classical generator sign convention. Resulting flows at the dangling line terminal all follow the same passive-sign convention, either for the injection part or for the generation part. @@ -291,26 +291,26 @@ Dangling lines are key objects for merging networks. Merging will be described s **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $P0$ | MW | The active power setpoint | -| $Q0$ | MVar | The reactive power setpoint | -| $R$ | $\Omega$ | The series resistance | -| $X$ | $\Omega$ | The series reactance | -| $G$ | S | The shunt conductance | -| $B$ | S | The shunt susceptance | +| Attribute | Unit | Description | +|-----------|----------|-----------------------------| +| $P0$ | MW | The active power setpoint | +| $Q0$ | MVar | The reactive power setpoint | +| $R$ | $\Omega$ | The series resistance | +| $X$ | $\Omega$ | The series reactance | +| $G$ | S | The shunt conductance | +| $B$ | S | The shunt susceptance | Optional: -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $MinP$ | MW | Minimum generation part active power output | -| $MaxP$ | MW | Maximum generation part active power output | -| $ReactiveLimits$ | MVar | Operational limits of the generation part (P/Q/V diagram) | -| $TargetP$ | MW | The active power target | -| $TargetQ$ | MVAr | The reactive power target | -| $TargetV$ | kV | The voltage target | -| $VoltageRegulatorOn$ | | True if the generation part regulates voltage | +| Attribute | Unit | Description | +|----------------------|------|-----------------------------------------------------------| +| $MinP$ | MW | Minimum generation part active power output | +| $MaxP$ | MW | Maximum generation part active power output | +| $ReactiveLimits$ | MVar | Operational limits of the generation part (P/Q/V diagram) | +| $TargetP$ | MW | The active power target | +| $TargetQ$ | MVAr | The reactive power target | +| $TargetV$ | kV | The voltage target | +| $VoltageRegulatorOn$ | | True if the generation part regulates voltage | **Specifications** @@ -318,10 +318,10 @@ Optional: - $R$, $X$, $G$ and $B$ correspond to a fraction of the original line and have to be consistent with the declared length of the dangling line. -In case the line is a boundary, a pairing key $pairingKey$ (in previous network versions $UcteXnodeCode$) is defined besides the characteristics of the table. It is a key to match two dangling lines and reconstruct the full boundary line, for both UCTE or CIM-CGMES formats. +In case the line is a boundary, a pairing key $pairingKey$ (in previous network versions $UcteXnodeCode$) is defined beside the characteristics of the table. It is a key to match two dangling lines and reconstruct the full boundary line for both UCTE or CIM-CGMES formats. A dangling line has a `Boundary` object that emulates a terminal located at boundary side. A dangling line is a connectable -with a single terminal located on network side, but sometimes we need state variables such as active or reactive powers on +with a single terminal located on the network side, but sometimes we need state variables such as active or reactive powers on the other side, voltage angle and voltage magnitude at fictitious boundary bus. Note that $P$, $Q$, $V$ and $Angle$ at boundary are automatically computed using information from the terminal of the dangling line. @@ -353,54 +353,54 @@ Shunt compensators follow a passive-sign convention: **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- |------------ | -| $MaximumSectionCount$ | - | The maximum number of sections that may be switched on | -| $SectionCount$ | - | The current number of sections that are switched on | -| $B$ | S | The susceptance of the shunt compensator in its current state | -| $G$ | S | The conductance of the shunt compensator in its current state | -| $TargetV$ | kV | The voltage target | -| $TargetDeadband$ | kV | The deadband used to avoid excessive update of controls | -| $RegulatingTerminal$ | - | Associated node or bus for which voltage is to be regulated | -| $VoltageRegulatorOn$ | - | True if the shunt compensator regulates voltage | +| Attribute | Unit | Description | +|-----------------------|------|---------------------------------------------------------------| +| $MaximumSectionCount$ | - | The maximum number of sections that may be switched on | +| $SectionCount$ | - | The current number of sections that are switched on | +| $B$ | S | The susceptance of the shunt compensator in its current state | +| $G$ | S | The conductance of the shunt compensator in its current state | +| $TargetV$ | kV | The voltage target | +| $TargetDeadband$ | kV | The deadband used to avoid excessive update of controls | +| $RegulatingTerminal$ | - | Associated node or bus for which voltage is to be regulated | +| $VoltageRegulatorOn$ | - | True if the shunt compensator regulates voltage | - For Linear Shunt Compensators -| Attribute | Unit | Description | -| --------- | ---- |------------ | -| $bPerSection$ | S | The Positive sequence shunt (charging) susceptance per section | -| $gPerSection$ | S | The Positive sequence shunt (charging) conductance per section | +| Attribute | Unit | Description | +|---------------|------|----------------------------------------------------------------| +| $bPerSection$ | S | The Positive sequence shunt (charging) susceptance per section | +| $gPerSection$ | S | The Positive sequence shunt (charging) conductance per section | -We expect $bPerSection$ to be a non zero value. The disconnected status of the linear shunt compensator can be modeled by setting the $SectionCount$ attribute to zero. +We expect $bPerSection$ to be a non-zero value. The disconnected status of the linear shunt compensator can be modeled by setting the $SectionCount$ attribute to zero. -- For Non Linear Shunt Compensators +- For Non-Linear Shunt Compensators -| Attribute | Unit | Description | -| --------- |---------------------|------------ | +| Attribute | Unit | Description | +|------------|---------------------|-------------------------------------------------------| | $Sections$ | [Section](#section) | The Partition of all the shunt compensator's sections | ### Section -| Attribute | Unit | Description | -| --------- | ---- |------------ | -| $B$ | S | The accumulated positive sequence shunt (charging) susceptance of the section if this section and all the previous ones are activated | -| $G$ | S | The accumulated positive sequence shunt (charging) conductance of the section if this section and all the previous ones are activated | +| Attribute | Unit | Description | +|-----------|------|---------------------------------------------------------------------------------------------------------------------------------------| +| $B$ | S | The accumulated positive sequence shunt (charging) susceptance of the section if this section and all the previous ones are activated | +| $G$ | S | The accumulated positive sequence shunt (charging) conductance of the section if this section and all the previous ones are activated | -$B$ and $G$ attributes can be equal zero, but the disconnected status of the non linear shunt compensator can be modeled by setting the $SectionCount$ attribute to zero. The section which $SectionCount$ equal to $1$ is the first effective section, and it would be more efficient to affect it a non zero susceptance. +$B$ and $G$ attributes can be equal zero, but the disconnected status of the non-linear shunt compensator can be modeled by setting the $SectionCount$ attribute to zero. The section which $SectionCount$ equal to $1$ is the first effective section, and it would be more efficient to affect it a non-zero susceptance. **Specifications** - A section of a shunt compensator is an individual capacitor or reactor. - A value of bPerSection positive means it is modeling a capacitor, an equipment that injects reactive + A positive value of bPerSection means that it models a capacitor, a device that injects reactive power into the bus. - A value of bPerSection negative means a reactor, an equipment that can absorb excess reactive power + A negative value of bPerSection means a reactor, a device that can absorb excess reactive power from the network. - The current section count is expected to be greater than one and lesser or equal to the maximum section count. - Regulation for shunt compensators does not necessarily model automation, it can represent human actions on the network - e.g. an operator activating or deactivating a shunt compensator). However, it can of course be integrated on a power flow + e.g. an operator activating or deactivating a shunt compensator). However, it can be integrated on a power flow calculation or not, depending on what is wanted to be shown. -- In case of a capacitor, the value for its Q will be negative. -- In case of a reactor, the value for its Q will be positive. +- In the case of a capacitor, the value for its Q will be negative. +- In the case of a reactor, the value for its Q will be positive. **Available extensions** @@ -421,22 +421,22 @@ Static VAR compensators follow a passive-sign convention: **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $Bmin$ | S | The minimum susceptance | -| $Bmax$ | S | The maximum susceptance | -| $VoltageSetpoint$ | kV | The voltage setpoint | +| Attribute | Unit | Description | +|-------------------------|------|-----------------------------| +| $Bmin$ | S | The minimum susceptance | +| $Bmax$ | S | The maximum susceptance | +| $VoltageSetpoint$ | kV | The voltage setpoint | | $ReactivePowerSetpoint$ | MVar | The reactive power setpoint | **Specifications** -- $Bmin$ and $Bmax$ are the susceptance bounds of the static VAR compensator. Reactive power output of a static VAR compensator is limited by the maximum and the minimum susceptance values. The min/max reactive power of a static VAR compensator are determined by: +- $Bmin$ and $Bmax$ are the susceptance bounds of the static VAR compensator. Reactive power output of a static VAR compensator is limited by the maximum and the minimum susceptance values. The min/max reactive power of a static VAR compensator is determined by: $$Qmin = -Bmin \times V^2$$ $$Qmax = -Bmax \times V^2$$ - where $V$ is the voltage of the bus that connects the static VAR compensator to the network. Even if the regulating terminal is remote, only the local voltage has to be considered to retrive the minimum and the maximum amouts of reactive power. Reactive limits can be handled in an approximative way using the nominal voltage of the connected bus. + where $V$ is the voltage of the bus that connects the static VAR compensator to the network. Even if the regulating terminal is remote, only the local voltage has to be considered to retrieve the minimum and the maximum amount of reactive power. Reactive limits can be handled in an approximate way using the nominal voltage of the connected bus. - The voltage setpoint is required when the regulation mode is set to `VOLTAGE`. - The reactive power setpoint is required when the regulation mode is set to `REACTIVE_POWER`. @@ -447,7 +447,7 @@ In IIDM the static VAR compensator also comprises some metadata: - `VOLTAGE` - `REACTIVE_POWER` - `OFF` - Note that it is different from the generators' regulation definition, which is only done through a boolean. `OFF` is equivalent to a disconnected equipment. + Note that it is different from the generator regulation definition, which is only done through a boolean. `OFF` is equivalent to a disconnected element. - The regulating terminal, which can be local or remote: it is the specific connection point on the network where the setpoint is measured. **Available extensions** @@ -480,7 +480,7 @@ y_2 & = & g_2 +j. b_2 \end{align*} $$ -The equations of the line, in complex notations, are as follow: +The equations of the line, in complex notations, are as follows: $$ \begin{align*} @@ -499,14 +499,14 @@ $$ **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $R$ | $\Omega$ | The series resistance | -| $X$ | $\Omega$ | The series reactance | -| $G1$ | S | The first side shunt conductance | -| $B1$ | S | The first side shunt susceptance | -| $G2$ | S | The second side shunt conductance | -| $B2$ | S | The second side shunt susceptance | +| Attribute | Unit | Description | +|-----------|----------|-----------------------------------| +| $R$ | $\Omega$ | The series resistance | +| $X$ | $\Omega$ | The series reactance | +| $G1$ | S | The first side shunt conductance | +| $B1$ | S | The first side shunt susceptance | +| $G2$ | S | The second side shunt conductance | +| $B2$ | S | The second side shunt susceptance | **Metadata** @@ -534,30 +534,30 @@ $G2$ (resp. $B2$) is equal to the second dangling line's $G2$ (resp. $B2$). **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $R$ | $\Omega$ | The series resistance | -| $X$ | $\Omega$ | The series reactance | -| $G1$ | S | The first side shunt conductance | -| $B1$ | S | The first side shunt susceptance | -| $G2$ | S | The second side shunt conductance | -| $B2$ | S | The second side shunt susceptance | +| Attribute | Unit | Description | +|-----------|----------|-----------------------------------| +| $R$ | $\Omega$ | The series resistance | +| $X$ | $\Omega$ | The series reactance | +| $G1$ | S | The first side shunt conductance | +| $B1$ | S | The first side shunt susceptance | +| $G2$ | S | The second side shunt conductance | +| $B2$ | S | The second side shunt susceptance | A tie line is not a connectable. It is just a container of two underlying dangling lines with the same pairing key. When connected together, each dangling line `P0` and `Q0` (and generation part if present) is ignored: only global tie line characteristics are used to compute flow. Removing a tie line leads to two free dangling lines, with an optional update of `P0` and `Q0` to match the flows in the global network context. ## Transformers -(two-windings-transformer)= -### Two windings transformer +(two-winding-transformer)= +### Two-winding transformer [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/TwoWindingsTransformer.html) -A two windings power transformer is connected to two voltage levels (side 1 and side 2) that belong to a same substation. -Two windings transformers are modeled with the following equivalent $\Pi$ model: +A two-winding power transformer is connected to two voltage levels (side 1 and side 2) that belong to the same substation. +Two winding transformers are modeled with the following equivalent $\Pi$ model: ![Power line model](img/two-winding-transformer-model.svg){width="50%" align=center class="only-light"} ![Power line model](img/dark_mode/two-winding-transformer-model.svg){width="50%" align=center class="only-dark"} -With the series impedance $z$ and the shunt admittance $y$ and the voltage ratio $\rho$ and the angle difference $\alpha$ and potentially parameters from the current step of a [ratio tap changer](./additional.md#ratio-tap-changer) and/or a [phase tap changer](./additional.md#phase-tap-changer), we have: +With the series impedance $z$ and the shunt admittance $y$ and the voltage ratio $\rho$ and the angle difference $\alpha$ and potential parameters from the current step of a [ratio tap changer](./additional.md#ratio-tap-changer) and/or a [phase tap changer](./additional.md#phase-tap-changer), we have: $$ \begin{array}{lcl} @@ -574,7 +574,7 @@ I_{0} & = & \dfrac{I_{1}}{\rho e^{-j\alpha}}\\ \end{array} $$ -Using the above notation, the equations of the two windings transformer, in complex notations, are as follow: +Using the above notation, the equations of the two-winding transformers, in complex notations, are as follows: $$ \left(\begin{array}{c} @@ -591,20 +591,20 @@ $$ **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $R_{nom}$ | $\Omega$ | The nominal series resistance at the side 2 of the transformer | -| $X_{nom}$ | $\Omega$ | The nominal series reactance at the side 2 of the transformer | -| $G_{nom}$ | S | The nominal magnetizing conductance at the side 2 of the transformer | -| $B_{nom}$ | S | The nominal magnetizing susceptance at the side 2 of the transformer | -| $V_{1\ nom}$ | kV | The rated voltage at side 1 | -| $V_{2\ nom}$ | kV | The rated voltage at side 2 | -| $RatedS$ | MVA | The normal apparent power | +| Attribute | Unit | Description | +|--------------|----------|----------------------------------------------------------------------| +| $R_{nom}$ | $\Omega$ | The nominal series resistance at the side 2 of the transformer | +| $X_{nom}$ | $\Omega$ | The nominal series reactance at the side 2 of the transformer | +| $G_{nom}$ | S | The nominal magnetizing conductance at the side 2 of the transformer | +| $B_{nom}$ | S | The nominal magnetizing susceptance at the side 2 of the transformer | +| $V_{1\ nom}$ | kV | The rated voltage at side 1 | +| $V_{2\ nom}$ | kV | The rated voltage at side 2 | +| $RatedS$ | MVA | The normal apparent power | **Specifications** -- A [ratio tap changer](./additional.md#ratio-tap-changer) and/or a [phase tap changer](./additional.md#phase-tap-changer) can be associated with a two windings power transformer. -- For a two windings transformer, the normal apparent power shall be identical at both sides 1 and 2. +- A [ratio tap changer](./additional.md#ratio-tap-changer) and/or a [phase tap changer](./additional.md#phase-tap-changer) can be associated with a two-winding power transformer. +- For a two-winding transformer, the normal apparent power shall be identical at both sides 1 and 2. **Available extensions** @@ -617,17 +617,17 @@ $$ - [Two-windings Transformer Phase Angle Clock](extensions.md#two-windings-transformer-phase-angle-clock) - [Two-windings Transformer To Be Estimated](extensions.md#two-windings-transformer-to-be-estimated) -(three-windings-transformer)= -### Three windings transformer +(three-winding-transformer)= +### Three-winding transformer [![Javadoc](https://img.shields.io/badge/-javadoc-blue.svg)](https://javadoc.io/doc/com.powsybl/powsybl-core/latest/com/powsybl/iidm/network/ThreeWindingsTransformer.html) -A three windings power transformer is connected to three voltage levels (side 1, side 2 and side 3) that belong to the +A three-winding power transformer is connected to three voltage levels (side 1, side 2 and side 3) that belong to the same substation. We usually have: - Side 1 as the primary side (side with the highest rated voltage) - Side 2 as the secondary side (side with the medium rated voltage) - Side 3 as the tertiary side (side with the lowest rated voltage) -A three windings transformer is modeled with three legs, where every leg model is electrically equivalent to a two windings transformer. +A three-winding transformer is modeled with three legs, where every leg model is electrically equivalent to a two-winding transformer. For each leg, the network bus is at side 1 and the star bus is at side 2. ![Line model](img/three-winding-transformer-model.svg){width="50%" align=center class="only-light"} @@ -641,7 +641,7 @@ For each leg, the network bus is at side 1 and the star bus is at side 2. **Specifications** -- A [ratio tap changer](./additional.md#ratio-tap-changer) and/or a [phase tap changer](./additional.md#phase-tap-changer) can be associated to all three sides of a three windings power transformer. +- A [ratio tap changer](./additional.md#ratio-tap-changer) and/or a [phase tap changer](./additional.md#phase-tap-changer) can be associated to all three sides of a three-winding power transformer. Only one tap changer (either ratio or phase tap changer) is allowed to be regulating on the equipment at a given time. **Available extensions** @@ -654,18 +654,18 @@ For each leg, the network bus is at side 1 and the star bus is at side 2. - [Three-windings Transformer Phase Angle Clock](extensions.md#three-windings-transformer-phase-angle-clock) - [Three-windings Transformer To Be Estimated](extensions.md#three-windings-transformer-to-be-estimated) -#### Three windings transformer leg +#### Three-winding transformer leg **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $R$ | $\Omega$ | The nominal series resistance specified at the voltage of the leg | -| $X$ | $\Omega$ | The nominal series reactance specified at the voltage of the leg | -| $G$ | S | The nominal magnetizing conductance specified at the voltage of the leg | -| $B$ | S | The nominal magnetizing susceptance specified at the voltage of the leg | -| $RatedU$ | kV | The rated voltage | -| $RatedS$ | MVA | The normal apparent power | +| Attribute | Unit | Description | +|-----------|----------|-------------------------------------------------------------------------| +| $R$ | $\Omega$ | The nominal series resistance specified at the voltage of the leg | +| $X$ | $\Omega$ | The nominal series reactance specified at the voltage of the leg | +| $G$ | S | The nominal magnetizing conductance specified at the voltage of the leg | +| $B$ | S | The nominal magnetizing susceptance specified at the voltage of the leg | +| $RatedU$ | kV | The rated voltage | +| $RatedS$ | MVA | The normal apparent power | **Specifications** @@ -679,16 +679,16 @@ An HVDC line is connected to the DC side of two HVDC converter stations, either **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $R$ | $\Omega$ | The resistance of the HVDC line | -| $NominalV$ | kV | The nominal voltage | -| $ActivePowerSetpoint$ | MW | The active power setpoint | -| $MaxP$ | MW | The maximum active power | +| Attribute | Unit | Description | +|-----------------------|----------|---------------------------------| +| $R$ | $\Omega$ | The resistance of the HVDC line | +| $NominalV$ | kV | The nominal voltage | +| $ActivePowerSetpoint$ | MW | The active power setpoint | +| $MaxP$ | MW | The maximum active power | **Specifications** -- The HVDC line operation depends on a converters mode, which indicates the flow direction. In the specification it is thus mandatory to define `ConvertersMode`, which can be: +- The HVDC line operation depends on a converter mode, which indicates the flow direction. In the specification it is thus mandatory to define `ConvertersMode`, which can be: - `SIDE_1_RECTIFIER_SIDE_2_INVERTER`: the flow goes from side 1 to side 2 - `SIDE_1_INVERTER_SIDE_2_RECTIFIER`: the flow goes from side 2 to side 1 @@ -709,16 +709,16 @@ Electronic converters for HVDC are divided into two main categories: line-commut **Characteristics** -| Attribute | Type | Unit | Required | Default value | Description | -| --------- | ---- | ---- | -------- | ------------- | ----------- | -| HvdcType | `HvdcType` | - | yes | - | The HVDC type | -| LossFactor | float | % | yes | - | The loss factor | +| Attribute | Type | Unit | Required | Default value | Description | +|------------|------------|------|----------|---------------|-----------------| +| HvdcType | `HvdcType` | - | yes | - | The HVDC type | +| LossFactor | float | % | yes | - | The loss factor | The LossFactor should be greater than 0. **Specifications** -The HVDC type, `LCC` or `VSC`, determines if the Converter Station is a LCC Converter Station or a VSC Converter Station. +The HVDC type, `LCC` or `VSC`, determines if the Converter Station is an LCC Converter Station or a VSC Converter Station. The positive loss factor `LossFactor` is used to model the losses during the conversion. In case of: - A rectifier operation (conversion from AC to DC), we have @@ -745,9 +745,9 @@ An LCC converter station is made with electronic switches that can only be turne **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $PowerFactor$ | % | Ratio between the active power $P$ and the apparent power $S$. | +| Attribute | Unit | Description | +|---------------|------|----------------------------------------------------------------| +| $PowerFactor$ | % | Ratio between the active power $P$ and the apparent power $S$. | **Available extensions** @@ -763,23 +763,23 @@ A VSC converter station is made with switching devices that can be turned both o - Current direction changes to change the power direction - Store energy capacitively - Use semiconductors which can turn on or off by control action -- Turn-off is independant of external circuit +- Turn-off is independent of external circuit **Characteristics** -| Attribute | Unit | Description | -| --------- | ---- | ----------- | -| $VoltageSetpoint$ | kV | The voltage setpoint for regulation | +| Attribute | Unit | Description | +|-------------------------|------|--------------------------------------------| +| $VoltageSetpoint$ | kV | The voltage setpoint for regulation | | $ReactivePowerSetpoint$ | MVar | The reactive power setpoint for regulation | **Specifications** - The voltage setpoint (in kV) is required if the voltage regulator is on for the VSC station. - The reactive power setpoint (in MVar) is required if the voltage regulator is off for the VSC station. A positive value of $ReactivePowerSetpoint$ means an injection into the bus, thus a negative value at the corresponding terminal (which is in passive-sign convention). -- A set of reactive limits can be associated to a VSC converter station. All the reactive limits modelings available in the library are described [here](./additional.md#reactive-limits). +- A set of reactive limits can be associated to a VSC converter station. All the reactive limits modeling available in the library are described [here](./additional.md#reactive-limits). **Metadata** -- The participation to regulation (through a boolean) +- The participation in regulation (through a boolean) **Available extensions** diff --git a/docs/index.md b/docs/index.md index 82cbef6adea..99e78d718d6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,7 +2,7 @@ # PowSyBl Core PowSyBl (Power System Blocks) is an open source library written in Java, -dedicated to electrical grid modelling, simulation and visualisation, licensed under the [Mozilla Public License version 2.0](https://www.mozilla.org/en-US/MPL/2.0/). +dedicated to electrical grid modeling, simulation and visualisation, licensed under the [Mozilla Public License version 2.0](https://www.mozilla.org/en-US/MPL/2.0/). It is part of [LF Energy](https://www.lfenergy.org/), an open source foundation focused on energy sector, hosted within The Linux Foundation. PowSyBl is used through Python scripts using the library [Pypowsybl](https://powsybl.readthedocs.io/projects/pypowsybl/en/stable/), but also diff --git a/docs/simulation/dynamic/configuration.md b/docs/simulation/dynamic/configuration.md index 8b63b29d849..1e2b522e8d9 100644 --- a/docs/simulation/dynamic/configuration.md +++ b/docs/simulation/dynamic/configuration.md @@ -1,7 +1,7 @@ # Configuration ## Implementation -If you have several implementation in your classpath, you need to choose which implementation to use in your configuration file with the `default-impl-name` property. +If you have several implementations in your classpath, you need to choose which implementation to use in your configuration file with the `default-impl-name` property. Each implementation is identified by its name, that may be unique in the classpath: - use "DynaWaltz" to use powsybl-dynawo implementation diff --git a/docs/simulation/dynamic/index.md b/docs/simulation/dynamic/index.md index 61a4d237caa..465c4bbcf96 100644 --- a/docs/simulation/dynamic/index.md +++ b/docs/simulation/dynamic/index.md @@ -43,9 +43,9 @@ For the moment, the only way to monitor dynamic variables of the simulation in o The outputs of a dynamic simulation are: - the updated static network (which may have been topologically modified depending on the events or automatons defined as inputs) - the different results of the dynamic simulation: - - some curves, asked for by the user to track the evolution of specific variables throughout the simulation + - some curves asked for by the user to track the evolution of specific variables throughout the simulation - some aggregated data regarding constraints, like a security analysis output - - timelines, that contain the list of events that occurred during the dynamic simulation, be them planned beforehand through events, or not + - timelines that contain the list of events that occurred during the dynamic simulation, be them planned beforehand through events, or not - logs about the execution of the dynamic simulator ## Implementations diff --git a/docs/simulation/dynamic_security/configuration.md b/docs/simulation/dynamic_security/configuration.md index 494a99c2748..df40b7077bc 100644 --- a/docs/simulation/dynamic_security/configuration.md +++ b/docs/simulation/dynamic_security/configuration.md @@ -1,7 +1,7 @@ # Configuration ## Implementation -If you have several implementation in your classpath, you need to choose which implementation to use in your configuration file with the `default-impl-name` property. +If you have several implementations in your classpath, you need to choose which implementation to use in your configuration file with the `default-impl-name` property. Each implementation is identified by its name, that may be unique in the classpath: - use "DynaWaltz" to use powsybl-dynawo implementation @@ -20,7 +20,7 @@ dynamic-security-analysis: ## Parameters The `dynamic-security-analysis-default-parameters` module is used every time a dynamic security analysis is run. It defines the default values for the most common parameters a `com.powsybl.security.dynamic.DynamicSecurityAnalysis` implementation should be able to handle. -In addition to its own set of parameter, the dynamic security analysis reuse [dynamic simulation parameters](../dynamic/parameters.md). +In addition to its own set of parameters, the dynamic security analysis reuses [dynamic simulation parameters](../dynamic/parameters.md). You may configure some generic parameters for all implementations: ```yaml diff --git a/docs/simulation/dynamic_security/index.md b/docs/simulation/dynamic_security/index.md index c2c1bb8151d..3db2a273947 100644 --- a/docs/simulation/dynamic_security/index.md +++ b/docs/simulation/dynamic_security/index.md @@ -5,15 +5,15 @@ configuration.md ``` -The dynamic security analysis is a [security analysis](../security/index.md) using dynamic models associated with static equipments of the network. +The dynamic security analysis is a [security analysis](../security/index.md) using dynamic models associated with static equipment of the network. ## Inputs ### Dynamic models mapping The dynamic models mapping is exactly the same [mapping](../dynamic/index.md#dynamic-models-mapping) used for a dynamic simulation. -### Others inputs -Beside dynamic models mapping, the dynamic security analysis requires the same [inputs as the standard one](../security/index.md#inputs). +### Other inputs +Besides dynamic models mapping, the dynamic security analysis requires the same [inputs as the standard one](../security/index.md#inputs). ## Outputs The dynamic security analysis produces the same outputs as the standard one. All outputs can be found [here](../security/index.md#outputs). diff --git a/docs/simulation/loadflow/configuration.md b/docs/simulation/loadflow/configuration.md index 97ac8f2cf96..43558efe00d 100644 --- a/docs/simulation/loadflow/configuration.md +++ b/docs/simulation/loadflow/configuration.md @@ -117,16 +117,16 @@ The `shuntCompensatorVoltageControlOn` property is an optional property that def The default value is `false`. **connectedComponentMode** -The `connectedComponentMode` property is an optional property that defines if the power flow has to be computed over all connected component (choose `ALL` mode) or just on the main connected component (choose `MAIN` mode). +The `connectedComponentMode` property is an optional property that defines if the power flow has to be computed over all connected components (choose `ALL` mode) or just on the main connected component (choose `MAIN` mode). The default value of this parameter is `MAIN`. **twtSplitShuntAdmittance** -The `twtSplitShuntAdmittance` property is an optional property that defines whether the shunt admittance is split at each side of the serie impedance for transformers. +The `twtSplitShuntAdmittance` property is an optional property that defines whether the shunt admittance is split at each side of the series impedance for transformers. The default value is `false`. **dcUseTransformerRatio** -The `dcUseTransformerRatio` property is an optional property that defines if ratio of transformers should be used in the -flow equations in a DC power flow. +The `dcUseTransformerRatio` property is an optional property that defines if the ratio of transformers should be used in +the flow equations in a DC power flow. The default value of this parameter is `true`. **dcPowerFactor** @@ -134,6 +134,6 @@ The `dcPowerFactor` property is an optional property that defines the power fact The default value is `1.0`. ### Specific parameters -Some implementation use specific parameters that can be defined in the configuration file or in the JSON parameters file: +Some implementations use specific parameters that can be defined in the configuration file or in the JSON parameters file: - [PowSyBl OpenLoadFlow](TODO) - [DynaFlow](TODO) \ No newline at end of file diff --git a/docs/simulation/loadflow/index.md b/docs/simulation/loadflow/index.md index 5f0d49c2995..eab082913b3 100644 --- a/docs/simulation/loadflow/index.md +++ b/docs/simulation/loadflow/index.md @@ -11,8 +11,8 @@ configuration.md A load flow is a numerical analysis of the flow of electric power in an interconnected system, where that system is considered to be in normal steady-state operation. Load flow studies are important for planning future expansion of power systems as well as in determining the best operation of existing systems. The principal outputs obtained from -the load flow study is the magnitude and phase angle of the voltage at each bus, and the active and reactive power -flowing in each line, the current is computed from both of them. In this page we will go into some details about what +the load flow study are the magnitude and phase angle of the voltage at each bus, and the active and reactive power +flowing in each line; the current is computed from both of them. In this page we will go into some details about what are the inputs and outputs of a load flow simulation, what is expected from a load flow result, how the load flow validation feature works, what load flow implementations are compatible with the generic interface, and how to configure it for the different implementations. @@ -35,7 +35,7 @@ only some of them (`Partially Converged` status), or for none of them (`Failed` not relevant for a specific implementation), the selected reference bus (voltage angle reference), the selected slack buses (the buses at which the power balance has been done), active power mismatch at slack buses, and amount of distributed active power (zero MW if slack distribution is disabled). -- Metrics regarding the computation. Depending on the load flow implementation the content of these metrics may vary. +- Metrics regarding the computation. Depending on the load flow implementation, the content of these metrics may vary. - Logs in a simulator specific format. ## Implementations diff --git a/docs/simulation/security/action-dsl.md b/docs/simulation/security/action-dsl.md index 49202a6831c..215a24cd1d7 100644 --- a/docs/simulation/security/action-dsl.md +++ b/docs/simulation/security/action-dsl.md @@ -1,14 +1,14 @@ # Action DSL -The action DSL is a domain specific language written in groovy for the creation of a strategy to solve violations, used in [security analyses with remedial actions](index.md#operator-strategies). This strategy is constituted of a set of [contingencies](contingency-dsl.md), a set of modification on the network called **actions**, and a set of rules to determine in which circumstances to apply the actions. +The action DSL is a domain-specific language written in groovy for the creation of a strategy to solve violations, used in [security analyses with remedial actions](index.md#operator-strategies). This strategy is constituted of a set of [contingencies](contingency-dsl.md), a set of modification on the network called **actions**, and a set of rules to determine in which circumstances to apply the actions. ## Contingencies -The contingencies are defined in the same way that is described in the [contingency DSL](contingency-dsl.md). Are supported: -- N-1 contingencies that triggers a single equipment at a time +The contingencies are defined in the same way described in the [contingency DSL](contingency-dsl.md). Are supported: +- N-1 contingencies that trigger a single piece of equipment at a time - N-K contingencies that triggers multiple equipments at a time -- busbar contingencies that triggers a [busbar section](../../grid_model/network_subnetwork.md#busbar-section), a N-K contingency that triggers all the equipments connected to that busbar section. +- busbar contingencies that trigger a [busbar section](../../grid_model/network_subnetwork.md#busbar-section), a N-K contingency that triggers all the equipment connected to that busbar section. ## Actions -The actions are modifications that could be made on the network to solve a security issue. It could be topology modification like opening or closing a switch, setpoints modification, tap position modification. PowSyBl provides a set of predefined actions, but this possibilities are infinite as you can create custom actions or groovy scripts. +The actions are modifications that could be made on the network to solve a security issue. It could be topology modification like opening or closing a switch, setpoint modification, tap position modification. PowSyBl provides a set of predefined actions, but this possibility is infinite as you can create custom actions or groovy scripts. An action is constituted of: - a unique identifier to reference this action in the [rules](#rules) @@ -26,7 +26,7 @@ action('actionID') { ``` ### Opening / Closing a switch -Topology changes are usually used to reduce the intensity on a AC branch. The following snippet shows how to create an action that opens the switch `SW1` and another one to close it. +Topology changes are usually used to reduce the intensity on an AC branch. The following snippet shows how to create an action that opens the switch `SW1` and another one to close it. ```groovy action('open-switch-SW1') { description "Open switch SW1" @@ -43,7 +43,7 @@ action('close-switch-SW1') { } ``` -Note that it's possible to open or close several switch at a time: +Note that it's possible to open or close several switches at a time: ```groovy action('open-SW1-and-SW2') { tasks { @@ -169,7 +169,7 @@ load-flow-based-phase-shifter-optimizer: ``` ### Scripting -The `script` task allow you to execute groovy code to modify the network. You can access to the network and the computation manager, using the `network` and `computationManager` variables. With this task, possibilities are unlimited as you have a complete access to the IIDM API. +The `script` task allow you to execute groovy code to modify the network. You can access to the network and the computation manager, using the `network` and `computationManager` variables. With this task, possibilities are unlimited as you have complete access to the IIDM API. ```groovy action('custom-action') { description "Disconnect LOAD1 and LOAD2, open the coupler COUPLER and change the setpoints of LOAD3" @@ -191,7 +191,7 @@ The rules are the most important in this DSL: they define the activation criteri - a unique identifier - an optional description to explain the purpose of the rule - an activation criteria -- a list of actions to applied if the activation criteria is verified +- a list of actions to be applied if the activation criteria is verified - an optional life count to limit the number of times a rule can be verified and its actions applied ### Activation criteria @@ -205,13 +205,13 @@ rule('rule-ID') { } ``` -**Note:** The activation criteria is evaluated during the simulation. If you want to save the initial value of a network's variable, you should declare a global variable at the beginning of your script. +**Note:** The activation criteria are evaluated during the simulation. If you want to save the initial value of a network's variable, you should declare a global variable at the beginning of your script. #### Network binding -The network binding adds keywords in the DSL to get equipments by their IDs. At the moment, the following keywords are supported: +The network binding adds keywords in the DSL to get equipment by their IDs. At the moment, the following keywords are supported: - `line`: to retrieve a [line](../../grid_model/network_subnetwork.md#line) -- `transformer`: to retrieve a [two windings transformer](../../grid_model/network_subnetwork.md#two-windings-transformer) -- `branch`: to retrieve a [line](../../grid_model/network_subnetwork.md#line), a [tie line](../../grid_model/network_subnetwork.md#tie-line) or a [two windings transformer](../../grid_model/network_subnetwork.md#two-windings-transformer) +- `transformer`: to retrieve a [two-winding transformer](../../grid_model/network_subnetwork.md#two-winding-transformer) +- `branch`: to retrieve a [line](../../grid_model/network_subnetwork.md#line), a [tie line](../../grid_model/network_subnetwork.md#tie-line) or a [two-winding transformer](../../grid_model/network_subnetwork.md#two-winding-transformer) - `generator`: to retrieve a [generator](../../grid_model/network_subnetwork.md#generator) - `load`: to retrieve a [load](../../grid_model/network_subnetwork.md#load) - `_switch` and `switch_` to retrieve a [switch](../../grid_model/network_subnetwork.md#breakerswitch) @@ -222,7 +222,7 @@ The network binding adds keywords in the DSL to get equipments by their IDs. At #### Predefined functions The following predefined functions are available and can be used in the `when` statement: -- `actionTaken`: returns `true` if the given action has already be applied +- `actionTaken`: returns `true` if the given action has already been applied - `contingencyOccured`: returns `true` if a contingency is currently simulated, and `false` if the N state is simulated - `loadingRank`: returns the rank of a given branch among a list of branches regarding their overload level - `mostLoaded`: returns the ID of the most loaded branch among a list of branches @@ -274,7 +274,7 @@ action('change-tap-position') { rule('example3') { description "If LINE1 and LINE2 are overloaded then change the PST tap position" when allOverloaded(["LINE1", "LINE2"]) - apply 'change-tap-postition' + apply 'change-tap-position' } ``` diff --git a/docs/simulation/security/configuration.md b/docs/simulation/security/configuration.md index 1ca9a5bf677..9b9ae14f546 100644 --- a/docs/simulation/security/configuration.md +++ b/docs/simulation/security/configuration.md @@ -8,13 +8,13 @@ The user can provide parameters to define which violations must be raised after a contingency, if the violation was already present in the pre-contingency state (`IncreasedViolationsParameters`). **flow-proportional-threshold** -After a contingency, only flow violations (either current, active power or apparent power violations) that have increased in proportion by more than a threshold value compared to the pre-contingency state are listed in the limit violations. The other ones are filtered. The threshold value is unitless and should be positive. This method gets the flow violation proportional threshold. The default value is 0.1, meaning that only violations that have increased by more than 10% appear in the limit violations. +After a contingency, only flow violations (either current, active power or apparent power violations) that have increased in proportion by more than a threshold value, compared to the pre-contingency state, are listed in the limit violations. The other ones are filtered. The threshold value is unitless and should be positive. This method gets the flow violation proportional threshold. The default value is 0.1, meaning that only violations that have increased by more than 10% appear in the limit violations. **low-voltage-proportional-threshold** -After a contingency, only low-voltage violations that have increased by more than the proportional threshold compared to the pre-contingency state, are listed in the limit violations, the other ones are filtered. This method gets the low voltage violation proportional threshold (unitless, should be positive). The default value is 0.0, meaning that only violations that have increased by more than 0.0 % appear in the limit violations (note that for low-voltage violation, it means that the voltage in the post-contingency state is lower than the voltage in the pre-contingency state). +After a contingency, only low-voltage violations that have increased by more than the proportional threshold compared to the pre-contingency state, are listed in the limit violations, the other ones are filtered. This method gets the low-voltage violation proportional threshold (unitless, should be positive). The default value is 0.0, meaning that only violations that have increased by more than 0.0 % appear in the limit violations (note that for low-voltage violation, it means that the voltage in the post-contingency state is lower than the voltage in the pre-contingency state). **low-voltage-absolute-threshold** -After a contingency, only low-voltage violations that have increased by more than an absolute threshold compared to the pre-contingency state, are listed in the limit violations, the other ones are filtered. This method gets the low voltage violation absolute threshold (in kV, should be positive). The default value is 0.0, meaning that only violations that have increased by more than 0.0 kV appear in the limit violations (note that for low-voltage violation, it means that the voltage in the post-contingency state is lower than the voltage in the pre-contingency state). +After a contingency, only low-voltage violations that have increased by more than an absolute threshold compared to the pre-contingency state, are listed in the limit violations, the other ones are filtered. This method gets the low-voltage violation absolute threshold (in kV, should be positive). The default value is 0.0, meaning that only violations that have increased by more than 0.0 kV appear in the limit violations (note that for low-voltage violation, it means that the voltage in the post-contingency state is lower than the voltage in the pre-contingency state). **high-voltage-proportional-threshold** Same as before but for high-voltage violations. @@ -24,10 +24,10 @@ Same as before but for high-voltage violations. (violation-filtering)= ### Violations filtering -The violations listed in the results can be filtered to consider only certain type of violations, to consider only few voltage levels or to limit the geographical area by filtering equipments by countries. Check out the documentation of the [limit-violation-default-filter](../../user/configuration/limit-violation-default-filter.md) configuration module. +The violations listed in the results can be filtered to consider only a certain type of violations, to consider only a few voltage levels or to limit the geographical area by filtering equipment by countries. Check out the documentation of the [limit-violation-default-filter](../../user/configuration/limit-violation-default-filter.md) configuration module. **Example** -Using the following configuration, the results will contain only voltage violations for equipments in France or Belgium: +Using the following configuration, the results will contain only voltage violations for equipment in France or Belgium: ```yaml limit-violation-default-filter: countries: diff --git a/docs/simulation/security/contingency-dsl.md b/docs/simulation/security/contingency-dsl.md index 3f301c37725..542c67464f0 100644 --- a/docs/simulation/security/contingency-dsl.md +++ b/docs/simulation/security/contingency-dsl.md @@ -1,14 +1,14 @@ # Contingency DSL -The contingency DSL is a domain specific language written in groovy for the creation of a contingency list, used in [security analyses](./index.md) or [sensitivity analyses](../sensitivity/index). At the moment, it's possible to simulate the loss of a generator, a static VAR compensator, a shunt, a power line, a power transformer, a HVDC line or a busbar section. +The contingency DSL is a domain specific language written in groovy for the creation of a contingency list, used in [security analyses](./index.md) or [sensitivity analyses](../sensitivity/index). At the moment, it's possible to simulate the loss of a generator, a static VAR compensator, a shunt, a power line, a power transformer, an HVDC line or a busbar section. ## N-1 contingency -A N-1 contingency is a contingency that triggers a single equipment at a time. +A N-1 contingency is a contingency that triggers a single piece of equipment at a time. ```groovy contingency('contingencyID') { equipments 'equipmentID' } ``` -where the `contingencyID` is the identifier of the contingency and the `equipmentID` is the identifier of a supported equipment. If the equipment doesn't exist or is not supported, an error will occur. +where the `contingencyID` is the identifier of the contingency and the `equipmentID` is the identifier of a supported piece of equipment. If the equipment doesn't exist or is not supported, an error will occur. ## N-K contingency A N-K contingency is a contingency that triggers several equipments at a time. The syntax is the same as for the N-1 contingencies, except that you have to pass a list of equipments' IDs. @@ -31,7 +31,7 @@ contingency('contingency2') { ``` ## Automatic contingency list -As the DSL is written in Groovy, it's possible to write more complex script. For example, it's possible to iterate over the equipments of the network to generate the contingency list. The network is accessible using the `network` variable. +As the DSL is written in Groovy, it's possible to write a more complex script. For example, it's possible to iterate over the equipment of the network to generate the contingency list. The network is accessible using the `network` variable. The following example creates a N-1 contingency for each line of the network. We use the ID of the lines as identifier for the contingencies. ```groovy @@ -42,7 +42,7 @@ for (l in network.lines) { } ``` -It's also possible to filter the lines, for example to consider on the border lines: +It's also possible to filter the lines, for example, to consider on the boundary lines: ```groovy import com.powsybl.iidm.network.Country @@ -105,7 +105,7 @@ network.generatorStream ``` ## Configuration -To provide contingencies list using this DSL, you have to add the following lines to your configuration file: +To provide a contingency list using this DSL, you have to add the following lines to your configuration file: **YAML configuration:** ```yaml diff --git a/docs/simulation/security/index.md b/docs/simulation/security/index.md index 34d67a7cb2f..2de6edf9cfb 100644 --- a/docs/simulation/security/index.md +++ b/docs/simulation/security/index.md @@ -9,10 +9,10 @@ action-dsl.md limit-reductions.md ``` -The security analysis is a simulation that check violations on a network. These checks can be done on the base case or after a contingency, with or without remedial actions. A security analysis can monitor network states, in pre-contingency state, after a contingency and after a remedial action. +The security analysis is a simulation that checks violations on a network. These checks can be done on the base case or after a contingency, with or without remedial actions. A security analysis can monitor network states, in pre-contingency state, after a contingency and after a remedial action. -There is a violation if the computed value is greater than the maximum allowed value. Depending on the equipments, the violations can have different types: -- Current, active power and apparent power: this kind of violation can be detected on a branch (line, two windings transformer, tie line) or on a three windings transformer, if the computed value is greater than its [permanent limit](../../grid_model/additional.md#loading-limits) or one of its [temporary limits](../../grid_model/additional.md#loading-limits). +There is a violation if the computed value is greater than the maximum allowed value. Depending on the equipment, the violations can have different types: +- Current, active power and apparent power: this kind of violation can be detected on a branch (line, two-winding transformer, tie line) or on a three-winding transformer, if the computed value is greater than its [permanent limit](../../grid_model/additional.md#loading-limits) or one of its [temporary limits](../../grid_model/additional.md#loading-limits). - Voltage: this kind of violation can be detected on a bus or a bus bar section, if the computed voltage is out of the low-high voltage limits of a [voltage level](../../grid_model/network_subnetwork.md#voltage-level). - Voltage angle: this kind of violation can be detected if the voltage angle difference between the buses associated to two terminals is out of the low-high voltage angle limits defined in the network. @@ -48,7 +48,7 @@ Remedial actions are actions that are applied when limit violations occur. Suppo Remedial actions can be *preventive* or *curative*: - preventive: these actions are implemented before the violation occurs, for example if the flow of a monitored line is between `90%` and `100%`. Use contingency context `NONE` for that. -- curative: these actions are implemented after a violation occurs, for example if the flow of the monitored line is greater than `100%`. +- curative: these actions are implemented after a violation occurs, for example, if the flow of the monitored line is greater than `100%`. ### Conditions Actions are applied if a condition is met. The conditions can be diversified and extended in the future: @@ -63,7 +63,7 @@ An operator strategy is applied in pre-contingency or after a contingency, depen An operator strategy groups a condition and a list of remedial actions. ### State monitors -A stateMonitor allows to get information about branch, bus and three-winding transformers on the network after a security analysis computation. Contingency context allows to specify if the information asked are about pre-contingency state or post-contingency state with a contingency id or both. For example: +A stateMonitor allows getting information about branch, bus and three-winding transformers on the network after a security analysis computation. Contingency context allows to specify if the information asked are about pre-contingency state or post-contingency state with a contingency id or both. For example: - If we want information about a branch after security analysis on contingency `c1`, the contingencyContext will contain the contingencyId `c1`, contextType `SPECIFIC` and the state monitor will contain the id of the branch. - If we want information about a branch in pre-contingency state, the contingencyContext will contain a null contingencyId, contextType `NONE` and the state monitor will contain the id of the branch. - If we want information about a branch in pre-contingency state and after security analysis on contingency `c1`, the contingencyContext will contain contingencyId `c1`, contextType `ALL` and the state monitor will contain the id of the branch. @@ -84,7 +84,7 @@ You can find more details about limit reductions [here](./limit-reductions). ## Outputs ### Pre-contingency results -The violations are detected on the network at state N, meaning before a contingency occurred. This determines a reference for the simulation. For each violation, we get the ID of the overloaded equipment, the limit type (`CURRENT`, `ACTIVE_POWER`, `APPARENT_POWER`, `LOW_VOLTAGE` or `HIGH_VOLTAGE`, `LOW_VOLTAGE_ANGLE` or `HIGH_VOLTAGE_ANGLE`), the acceptable value and the computed value. For branches and three windings transformers, we also have the side where the violation has been detected. +The violations are detected on the network at state N, meaning before a contingency occurred. This determines a reference for the simulation. For each violation, we get the ID of the overloaded equipment, the limit type (`CURRENT`, `ACTIVE_POWER`, `APPARENT_POWER`, `LOW_VOLTAGE` or `HIGH_VOLTAGE`, `LOW_VOLTAGE_ANGLE` or `HIGH_VOLTAGE_ANGLE`), the acceptable value and the computed value. For branches and three-winding transformers, we also have the side where the violation has been detected. The pre-contingency results also contain the network results based on given state monitors. A network result groups branch results, bus results and three-winding transformer results. All elementary results are fully extendable. diff --git a/docs/simulation/security/limit-reductions.md b/docs/simulation/security/limit-reductions.md index def6d026978..6e14189bd33 100644 --- a/docs/simulation/security/limit-reductions.md +++ b/docs/simulation/security/limit-reductions.md @@ -9,7 +9,7 @@ Each limit reduction has its own criteria specifying for which limits and under - the type of limit (current, active power or apparent power) - the use case: for monitoring only or also for applying remedial actions - the contingency context (pre-contingency, after a specific contingency or after all contingencies, etc.) -- the network elements targeted by the reduction (branches, three-windings transformers, ...), which can be described by the following criteria: +- the network elements targeted by the reduction (branches, three-winding transformers, ...), which can be described by the following criteria: - a set of their ids; - their countries; - their nominal voltages. @@ -65,7 +65,7 @@ The network elements whose limits will be affected by the limit reductions can b - one or two countries (respectively for elements with one or two substations); - their nominal voltages, by defining an interval for each of the voltage levels. -If no network elements is specified, the limit reduction applies to all of them. +If no network elements are specified, the limit reduction applies to all of them. ### Limit duration criteria @@ -85,5 +85,5 @@ Duration criteria can be optionally specified. It contains: When no duration criteria are present, the reduction is applied to all permanent and temporary limits. -When several duration criteria are specified, the limit reductions applies to each one. +When several duration criteria are specified, the limit reductions apply to each one. For instance, if both criteria `PERMANENT` and (`TEMPORARY` ; `EQUALITY`: 600) are defined, the limit reduction will apply to permanent limits and 600 s limits. diff --git a/docs/simulation/sensitivity/configuration.md b/docs/simulation/sensitivity/configuration.md index 67a6d712d2b..276a64566e5 100644 --- a/docs/simulation/sensitivity/configuration.md +++ b/docs/simulation/sensitivity/configuration.md @@ -16,7 +16,7 @@ Use "OpenLoadFlow" to use PowSyBl OpenLoadFlow's sensitivity analysis implementa **flowFlowSensitivityValueThreshold** -The `flowFlowSensitivityValueThreshold` is the threshold under which sensitivity values having variable type among +The `flowFlowSensitivityValueThreshold` is the threshold under which sensitivity values having a variable type among `INJECTION_ACTIVE_POWER`, `INJECTION_REACTIVE_POWER` and `HVDC_LINE_ACTIVE_POWER` and function type among `BRANCH_ACTIVE_POWER_1/2/3`, `BRANCH_REACTIVE_POWER_1/2/3` and `BRANCH_CURRENT_1/2/3` will be filtered from the analysis results. The default value is 0.0. @@ -28,12 +28,12 @@ The `voltageVoltageSensitivityValueThreshold` is the threshold under which sensi **flowVoltageSensitivityValueThreshold** -The `flowVoltageSensitivityValueThreshold` is the threshold under which sensitivity values having variable type among +The `flowVoltageSensitivityValueThreshold` is the threshold under which sensitivity values having a variable type among `INJECTION_REACTIVE_POWER` and function type among `BUS_VOLTAGE`, or variable type among `BUS_TARGET_VOLTAGE` and function type among `BRANCH_REACTIVE_POWER_1/2/3`, `BRANCH_CURRENT_1/2/3` or `BUS_REACTIVE_POWER` will be filtered from the analysis results. The default value is 0.0. **angleFlowSensitivityValueThreshold** -The `angleFlowSensitivityValueThreshold` is the threshold under which sensitivity values having variable type among -`TRANSFORMER_PHASE` and `TRANSFORMER_PHASE_1/2/3` and function type among `BRANCH_ACTIVE_POWER_1/2/3`, `BRANCH_REACTIVE_POWER_1/2/3` +The `angleFlowSensitivityValueThreshold` is the threshold under which sensitivity values having a variable type among +`TRANSFORMER_PHASE` and `TRANSFORMER_PHASE_1/2/3` and a function type among `BRANCH_ACTIVE_POWER_1/2/3`, `BRANCH_REACTIVE_POWER_1/2/3` and `BRANCH_CURRENT_1/2/3` will be filtered from the analysis results. The default value is 0.0. \ No newline at end of file diff --git a/docs/simulation/sensitivity/index.md b/docs/simulation/sensitivity/index.md index 9709a764ebe..d5729117a1c 100644 --- a/docs/simulation/sensitivity/index.md +++ b/docs/simulation/sensitivity/index.md @@ -14,7 +14,7 @@ The sensitivity analysis module is dedicated to computing the linearized impact A sensitivity value is the numerical estimation of the partial derivative of the observed function with respect to the variable of impact. The sensitivity analysis can also be seen as the computation of partial derivatives on the network model. -For example, it may be used to know, among a group of selected lines, which are the most impacted by a change in a generator production or a change of tap on a phase tap changer. The user story about [RSC capacity calculation](https://www.powsybl.org/pages/documentation/user/user-stories/capacity_calculation_rsc.html) provides an example of application of the sensitivity analysis. +For example, it may be used to know among a group of selected lines, which are the most impacted by a change in a generator production or a change of tap on a phase tap changer. The user story about [RSC capacity calculation](https://www.powsybl.org/pages/documentation/user/user-stories/capacity_calculation_rsc.html) provides an example of application of the sensitivity analysis. (sensitivity-analysis-inputs)= ## Inputs @@ -25,10 +25,10 @@ The first input for the sensitivity analysis module is an IIDM network. (sensitivity-factors)= ### Sensitivity factors Aside from providing an input network, it is necessary to specify which equipments are going to be studied: -- what impacted equipments are selected to be monitored (lines for example) +- what impacted equipment is selected to be monitored (lines, for example) - according to a change on which equipment (a generator's production or a group of generator's production, or the tap position of a phase tap changer, etc.) -It is also necessary to specify which quantity is being observed: the active power or the current on the monitored equipments, the voltage at a bus. +It is also necessary to specify which quantity is being observed: the active power or the current on the monitored equipment, the voltage of a bus. This set of information constitutes the sensitivity factors (`SensitivityFactor`). These factors correspond to the definition of the expected partial derivatives to be extracted from the input network. @@ -41,19 +41,19 @@ A standard sensitivity analysis input thus comprises a list of sensitivity facto A sensitivity variable represents a change on an equipment or on a group of equipments. The supported variable types are: - Use `INJECTION_ACTIVE_POWER` to model a change on active production of a generator or on a group of generators, on the active consumption of a load or on a group of loads or on GLSK (for Generation and Load Shift keys) that describes a linear combination of active power injection shifts on generators and loads. The variable increase is in MW. - Use `INJECTION_REACTIVE_POWER` to model a change on reactive production of a generator or on the reactive consumption of a load. The variable increase is in MVar. -- Use `TRANSFORMER_PHASE` to model the change of the tap position of a phase tap changer of a two windings transformer. The increase is in degree. -- Use `BUS_TARGET_VOLTAGE` to model an increase of the voltage target of a generator, a static var compensator, a two or three windings transformer, a shunt compensator or a VSC converter station. The increase is in KV. +- Use `TRANSFORMER_PHASE` to model the change of the tap position of a phase tap changer of a two-winding transformer. The increase is in degree. +- Use `BUS_TARGET_VOLTAGE` to model an increase of the voltage target of a generator, a static var compensator, a two or three-winding transformer, a shunt compensator or a VSC converter station. The increase is in KV. - Use `HVDC_LINE_ACTIVE_POWER` to model the change of the active power set point of an HVDC line. The increase is in MW. -- Use `TRANSFORMER_PHASE_1`, `TRANSFORMER_PHASE_2` or `TRANSFORMER_PHASE_3` to model the change of the tap position of a phase tap changer of a three windings transformer that contains several phase tap changers. +- Use `TRANSFORMER_PHASE_1`, `TRANSFORMER_PHASE_2` or `TRANSFORMER_PHASE_3` to model the change of the tap position of a phase tap changer of a three-winding transformer that contains several phase tap changers. The supported sensitivity function types, related to the equipment to monitor, are: -- Use `BRANCH_ACTIVE_POWER_1` and `BRANCH_ACTIVE_POWER_2` if you want to monitor the active power in MW of a network branch (lines, two windings transformer, dangling lines, etc.). Use 1 for side 1 and 2 for side 2. In case of a three windings transformer, use `BRANCH_ACTIVE_POWER_3` to monitor the active power in MW of the leg 3 (network side). -- Use `BRANCH_REACTIVE_POWER_1` and `BRANCH_REACTIVE_POWER_2` if you want to monitor the reactive power in MVar of a network branch (lines, two-winding transformer, dangling lines, etc.). Use 1 for side 1 and 2 for side 2. In case of a three-winding transformer, use `BRANCH_REACTIVE_POWER_3` to monitor the reactive power in MVar of the leg 3 (network side). -- Use `BRANCH_CURRENT_1` and `BRANCH_CURRENT_2` if you want to monitor the current in A of a network branch (lines, two windings transformer, dangling lines, etc.). Use 1 for side 1 and use 2 for side 2. In case of a three windings transformer, use `BRANCH_CURRENT_3` to monitor the current in A of the leg 3 (network side). +- Use `BRANCH_ACTIVE_POWER_1` and `BRANCH_ACTIVE_POWER_2` if you want to monitor the active power in MW of a network branch (lines, two-winding transformer, dangling lines, etc.). Use 1 for side 1 and 2 for side 2. In case of a three-winding transformer, use `BRANCH_ACTIVE_POWER_3` to monitor the active power in MW of leg 3 (network side). +- Use `BRANCH_REACTIVE_POWER_1` and `BRANCH_REACTIVE_POWER_2` if you want to monitor the reactive power in MVar of a network branch (lines, two-winding transformer, dangling lines, etc.). Use 1 for side 1 and 2 for side 2. In case of a three-winding transformer, use `BRANCH_REACTIVE_POWER_3` to monitor the reactive power in MVar of leg 3 (network side). +- Use `BRANCH_CURRENT_1` and `BRANCH_CURRENT_2` if you want to monitor the current in A of a network branch (lines, two-winding transformer, dangling lines, etc.). Use 1 for side 1 and use 2 for side 2. In case of a three-winding transformer, use `BRANCH_CURRENT_3` to monitor the current in A of leg 3 (network side). - `BUS_VOLTAGE` if you want to monitor the voltage in KV of a specific network bus. - `BUS_REACTIVE_POWER` if you want to monitor the reactive power injection in MVar of a specific network bus. -A sensitivity variable can group some equipments and has to be modeled as a variable set. In a `SensitivityVariableSet`, we have a list of individual variables, each one with a weight (called `WeightedSensitivityVariable`). We use variable sets to model what it commonly called GLSK. +A sensitivity variable can group some equipment and has to be modeled as a variable set. In a `SensitivityVariableSet`, we have a list of individual variables, each one with a weight (called `WeightedSensitivityVariable`). We use variable sets to model what it commonly called GLSK. #### How to provide the sensitivity factors input @@ -79,7 +79,7 @@ The sensitivity factors may be created directly through Java code, or be provide ### Contingencies The sensitivity analysis may also take, optionally, a list of contingencies as an input. When contingencies are provided, the sensitivity values -shall be calculated on the network at state N, but also after the application of each contingency. The contingencies are provided in the same way than for the [security analysis](../security/index.md/). This then constitutes a systematic sensitivity analysis. +shall be calculated on the network at state N, but also after the application of each contingency. The contingencies are provided in the same way as for the [security analysis](../security/index.md/). This then constitutes a systematic sensitivity analysis. ```json { @@ -96,7 +96,7 @@ shall be calculated on the network at state N, but also after the application of } ``` -At the moment the only available sensitivity simulator officially compatible with PowSyBl is the one available through OpenLoadFlow. In this case, the network is provided only once in state N, and then all the calculations are done successively by modifying the Jacobian matrix directly in the solver based on the contingencies input. The network is thus loaded only once, which improves performance. +At the moment, the only available sensitivity simulator officially compatible with PowSyBl is the one available through OpenLoadFlow. In this case, the network is provided only once in state N, and then all the calculations are done successively by modifying the Jacobian matrix directly in the solver based on the contingency input. The network is thus loaded only once, which improves performance. (sensitivity-analysis-outputs)= ## Outputs diff --git a/docs/simulation/shortcircuit/index.md b/docs/simulation/shortcircuit/index.md index c0cef7a2267..1055ba7e108 100644 --- a/docs/simulation/shortcircuit/index.md +++ b/docs/simulation/shortcircuit/index.md @@ -1,7 +1,7 @@ # Short-circuit analysis When a short circuit occurs in a network, the currents on equipment can be so high that they exceed their rated values. -Simulating faults on the network is important to verify that the short circuits are well detected and do not damage the equipments. +Simulating faults on the network is important to verify that the short circuits are well detected and do not damage the equipment. The short-circuit API allows the calculation of currents and voltages on a network after a fault. A first implementation of the API is available in [powsybl-incubator](https://github.com/powsybl/powsybl-incubator/tree/main/simulator/short-circuit). diff --git a/docs/simulation/shortcircuit/inputs.md b/docs/simulation/shortcircuit/inputs.md index 9fc4f09de2e..7c2af287911 100644 --- a/docs/simulation/shortcircuit/inputs.md +++ b/docs/simulation/shortcircuit/inputs.md @@ -16,7 +16,7 @@ The attributes to fill of a `BusFault` are: | Attribute | Type | Unit | Required | Default value | Description | |------------|----------------|------|----------|-------------------------|---------------------------------------------------------------------------------------------------------| | id | String | - | yes | - | The id of the fault | -| elementId | String | - | yes | - | The id of the bus on which the fault will be simulated (bus/view topology) | +| elementId | String | - | yes | - | The id of the bus on which the fault will be simulated (bus/view topology) | | r | double | Ω | no | 0 | The fault resistance to ground | | x | double | Ω | no | 0 | The fault reactance to ground | | connection | ConnectionType | - | no | `ConnectionType.SERIES` | The way the resistance and reactance of the fault are connected to the ground: in series or in parallel | @@ -25,7 +25,7 @@ The attributes to fill of a `BusFault` are: The attributes to fill of a `BranchFault` are: | Attribute | Type | Unit | Required | Default value | Description | -|----------------------|----------------|------|----------|----------------------- |---------------------------------------------------------------------------------------------------------| +|----------------------|----------------|------|----------|-------------------------|---------------------------------------------------------------------------------------------------------| | id | String | - | yes | - | The id of the fault | | elementId | String | - | yes | - | The id of the branch on which the fault will be simulated | | r | double | Ω | no | 0 | The fault resistance to ground | diff --git a/docs/simulation/shortcircuit/outputs.md b/docs/simulation/shortcircuit/outputs.md index 09549a4a682..9192bd85043 100644 --- a/docs/simulation/shortcircuit/outputs.md +++ b/docs/simulation/shortcircuit/outputs.md @@ -41,7 +41,7 @@ In `FortescueFaultResult`, they are: **The status of the computation** This status can be: -- `SUCCESS`: the computation went as planned and the results are full considering the parameters. +- `SUCCESS`: the computation went as planned, and the results are full considering the parameters. - `NO_SHORT_CIRCUIT_DATA`: this status should be returned if no short-circuit data are available in the network, i.e., the sub-transient or transient reactance of generators and the minimum and maximum admissible short-circuit currents. - `SOLVER_FAILURE`: the computation failed because of an error linked to the solver. - `FAILURE`: the computation failed for any other reason. diff --git a/docs/simulation/shortcircuit/parameters.md b/docs/simulation/shortcircuit/parameters.md index 13de70c4772..110c9f67af5 100644 --- a/docs/simulation/shortcircuit/parameters.md +++ b/docs/simulation/shortcircuit/parameters.md @@ -51,7 +51,7 @@ The default value is `TRANSIENT`. The transient and sub-transient reactance of t **sub-transient-coefficient** -This property allows to define an optional coefficient, in case of a sub-transient study, to apply to the transient reactance of generators to get the sub-transient one: +This property allows defining an optional coefficient, in case of a sub-transient study, to apply to the transient reactance of generators to get the sub-transient one: $X''_d = c \times X'_d$ @@ -75,7 +75,7 @@ The voltage drop is calculated as the ratio between the initial voltage magnitud **with-loads** -This property indicates whether loads should be taken into account during the calculation. If `true`, the loads are modelled as an equivalent reactance and the equivalent reactance at the fault point will be lowered. If `false`, then they will be ignored. +This property indicates whether loads should be taken into account during the calculation. If `true`, the loads are modelled as an equivalent reactance, and the equivalent reactance at the fault point will be lowered. If `false`, then they will be ignored. **with-shunt-compensators** diff --git a/docs/user/configuration/componentDefaultConfig.md b/docs/user/configuration/componentDefaultConfig.md index 50a5409aa61..dc7ccb66d2a 100644 --- a/docs/user/configuration/componentDefaultConfig.md +++ b/docs/user/configuration/componentDefaultConfig.md @@ -1,5 +1,5 @@ # componentDefaultConfig -The `componentDefaultConfig` module is used to configure the implementation of plugins the framework has to use for specific features (e.g. computation, etc.). Contrary to the other modules, it is impossible to give an exhaustive list of the existing properties. +The `componentDefaultConfig` module is used to configure the implementation of plugins that the framework has to use for specific features (e.g. computation, etc.). Contrary to the other modules, it is impossible to give an exhaustive list of the existing properties. The names of the properties are the names of Java interfaces of the powsybl framework. Each value must be the complete name of a class which implements this interface. - ContingenciesProviderFactory diff --git a/docs/user/configuration/import-export-parameters-default-value.md b/docs/user/configuration/import-export-parameters-default-value.md index 0f537575798..d04d145642e 100644 --- a/docs/user/configuration/import-export-parameters-default-value.md +++ b/docs/user/configuration/import-export-parameters-default-value.md @@ -5,7 +5,7 @@ The `import-export-parameters-default-value` module is an optional module used b - List of Strings As the parameters are different from an importer to another, it is impossible to give an exhaustive list of supported -properties. Please refer to the documentation of each [supported formats](../../grid_exchange_formats/index.md) to know their specific configuration. +properties. Please refer to the documentation of each [supported format](../../grid_exchange_formats/index.md) to know their specific configuration. ## Examples diff --git a/docs/user/configuration/index.md b/docs/user/configuration/index.md index 8a16cbdf488..6c17e6b5338 100644 --- a/docs/user/configuration/index.md +++ b/docs/user/configuration/index.md @@ -18,7 +18,7 @@ loadflow-validation.md security-analysis.md ``` -The configuration mechanism supports YAML and XML file formats. The framework looks inside all the folders specified to the [powsybl_config_dirs](../itools/index.md#configuration) property in the [itools.conf](../itools/index.md#configuration) file for configuration files. The framework uses the [powsybl_config_name](../itools/index.md#configuration) property as the basename of the configuration files. It looks for a YAML file first, then for a XML file. The XML file will be used only if the YAML configuration file has not been found. +The configuration mechanism supports YAML and XML file formats. The framework looks inside all the folders specified to the [powsybl_config_dirs](../itools/index.md#configuration) property in the [itools.conf](../itools/index.md#configuration) file for configuration files. The framework uses the [powsybl_config_name](../itools/index.md#configuration) property as the basename of the configuration files. It looks for a YAML file first, then for an XML file. The XML file will be used only if the YAML configuration file has not been found. The configuration can also be configured using the system's environment variables. These variables should respect the following format: `MODULE_NAME__PROPERTY_NAME`. Note that these variables will overload the XML/YAML configuration files. @@ -26,7 +26,7 @@ following format: `MODULE_NAME__PROPERTY_NAME`. Note that these variables will o The default configuration folder and the configuration file name can be configured in the `POWSYBL_HOME/etc/itools.conf`. ## Modules and properties -The configuration file contains a list of modules, that can be required or optional. Each module contains one or +The configuration file contains a list of modules that can be required or optional. Each module contains one or several properties. These properties can also be required or optional. Names in configuration files are case-sensitive. ### Example @@ -59,15 +59,15 @@ module2: ``` ### System's environment variables -Configuration properties can also be overridden using system's environment variables. The module and the property are separated using two underscores. The table below give examples on the way to declare environment variables for PowSyBl: +Configuration properties can also be overridden using system's environment variables. The module and the property are separated using two underscores. The table below gives examples on the way to declare environment variables for PowSyBl: -| Environment variable | Module name | Property name | -| -------------------- | ----------- | ------------- | -| MODULE1__PROPERTY1=1 | module1 | property1 | -| LOWER_HYPHEN__PROPERTY2=2 | lower-hyphen | property2 | -| LOWER_CAMEL__PROPERTY3=3 | lowerCamel | property3 | -| UPPER_CAMEL__PROPERTY4=4 | UpperCamel | property4 | -| SNAKE_CASE__PROPERTY5=5 | snake_case | property5 | +| Environment variable | Module name | Property name | +|---------------------------|--------------|---------------| +| MODULE1__PROPERTY1=1 | module1 | property1 | +| LOWER_HYPHEN__PROPERTY2=2 | lower-hyphen | property2 | +| LOWER_CAMEL__PROPERTY3=3 | lowerCamel | property3 | +| UPPER_CAMEL__PROPERTY4=4 | UpperCamel | property4 | +| SNAKE_CASE__PROPERTY5=5 | snake_case | property5 | ## Modules list - [componentDefaultConfig](componentDefaultConfig.md) diff --git a/docs/user/configuration/limit-violation-default-filter.md b/docs/user/configuration/limit-violation-default-filter.md index b798a2fd057..ab0afab4f6e 100644 --- a/docs/user/configuration/limit-violation-default-filter.md +++ b/docs/user/configuration/limit-violation-default-filter.md @@ -5,7 +5,7 @@ The `limit-violation-default-filter` module is used by the [security-analysis](. ## Optional properties **countries** -The `countries` property is an optional property that defines a list of [ISO-3166](https://en.wikipedia.org/wiki/ISO_3166-1) country codes used for violations filtering. A violation is displayed only if at least one of its side has its substation's country in the list. If this property is not set, there is no filtering based on the countries. +The `countries` property is an optional property that defines a list of [ISO-3166](https://en.wikipedia.org/wiki/ISO_3166-1) country codes used for violations filtering. A violation is displayed only if at least one of its sides has its substation's country in the list. If this property is not set, there is no filtering based on the countries. **minBaseVoltage** The `minBaseVoltage` property is an optional property that defines a threshold value for the nominal voltage of the voltage levels. The default value of this property is `0`. diff --git a/docs/user/configuration/load-flow-action-simulator.md b/docs/user/configuration/load-flow-action-simulator.md index 88648cea378..7abdec99b84 100644 --- a/docs/user/configuration/load-flow-action-simulator.md +++ b/docs/user/configuration/load-flow-action-simulator.md @@ -6,7 +6,7 @@ The `load-flow-action-simulator` module is used by the [action-simulator]() tool **copy-strategy** Use the `copy-strategy` to define how the action-simulator will store and restore network state internally. This choice can greatly impact performances. Possible values are: - `STATE`: will only save and restore state data. Optimizes performances, but will not behave correctly if some actions modify the structure of the network. -- `DEEP`: will save and restore all network data. Decreases performances, but allows to use any type of action. +- `DEEP`: will save and restore all network data. Decreases performance, but allows to use any type of action. **ignore-pre-contingency-violations** Set the `ignore-pre-contingency-violations` to `true` to ignore the pre-contingency violations and continue the simulation even if there are still violations after the pre-contingency simulation. @@ -17,7 +17,7 @@ If this property is not set, the default load flow implementation is used. See [ configure the default load flow. **max-iterations** -Use the `max-iterations` parameter to limit the number of iteration needed to solve the violations. +Use the `max-iterations` parameter to limit the number of iterations needed to solve the violations. ## Examples diff --git a/docs/user/configuration/loadflow-validation.md b/docs/user/configuration/loadflow-validation.md index bbfd0409631..e024b379481 100644 --- a/docs/user/configuration/loadflow-validation.md +++ b/docs/user/configuration/loadflow-validation.md @@ -32,7 +32,7 @@ The `ok-missing-values` property is an optional property that defines whether th **output-writer** The `output-writer` property is an optional property that defines the output format. Currently, `CSV` and `CSV_MULTILINE` are supported. The default value of this property is set to `CSV_MULTILINE`. -If this property is set to `CSV`, in the output files a line contains all values of a validated equipment. If the property is set to `CSV_MULTILINE`, in the output files the values of an equipment are split in multiple lines, one value for each line, see examples below: +If this property is set to `CSV`, in the output files a line contains all values of validated equipment. If the property is set to `CSV_MULTILINE`, in the output files the equipment values are split in multiple lines, one value for each line, see examples below: **Example of output in CSV format:** ``` @@ -66,7 +66,7 @@ The `threshold` property is an optional property that defines the margin used fo **verbose:** The `verbose` property is an optional property that defines whether the [loadflow-validation](../itools/loadflow-validation.md) command runs in verbose or quiet mode. -If this property is set to `true`, the output files contain all the data of the validated equipments, otherwise they contain only the main data of the validated equipments. +If this property is set to `true`, the output files contain all the data of the validated equipment; otherwise they contain only the main data of the validated equipment. ## Examples diff --git a/docs/user/configuration/security-analysis.md b/docs/user/configuration/security-analysis.md index 2981849b582..0428ed570cf 100644 --- a/docs/user/configuration/security-analysis.md +++ b/docs/user/configuration/security-analysis.md @@ -5,7 +5,7 @@ The `security-analysis` module is used to configure the execution of the [securi ## Optional property **preprocessor** -The `preprocessor` property is an optional property which requires that the `SecurityAnalysisPreprocessor` with specified name is used to preprocess inputs, based on the contingencies file, before actually running the security analysis. +The `preprocessor` property is an optional property which requires that the `SecurityAnalysisPreprocessor` with specified name is used to preprocess inputs, based on the contingency file, before actually running the security analysis. Such a preprocessor will have the possibility to programmatically transform the following objects before the security analysis is actually executed : - The `Network` @@ -17,7 +17,7 @@ Such a preprocessor will have the possibility to programmatically transform the It enables, for example, to customize what should be considered a limit violation and what should not. -If absent, the default behavior of the tool is used: the contingencies file is simply interpreted by the configured contingencies provider. +If absent, the default behavior of the tool is used: the contingency file is simply interpreted by the configured contingency provider. ## Examples diff --git a/docs/user/itools/action-simulator.md b/docs/user/itools/action-simulator.md index abde565c267..871b4cc98c9 100644 --- a/docs/user/itools/action-simulator.md +++ b/docs/user/itools/action-simulator.md @@ -59,7 +59,7 @@ This option defines the path of the strategy to evaluate. This is a groovy scrip This option defines the list of contingencies to simulate. If this parameter is omitted, all the contingencies defined in the DSL file are simulated. `--export-after-each-round` -If this option is passed, a case file is exported after each round of the simulation. Otherwise, a single case file is exported at the end of the simulation (once there is no more violations or matching rules). +If this option is passed, a case file is exported after each round of the simulation. Otherwise, a single case file is exported at the end of the simulation (once there are no more violations or matching rules). `--import-parameters` This option defines the path of the importer's configuration file. It's possible to overload one or many parameters using the `-I property=value` syntax. The list of supported properties depends on the [input format](../../grid_exchange_formats/index.md). @@ -71,19 +71,19 @@ This option defines the path to the folder in which the case files will be expor This option defines the format of the output case files. The list of [supported formats](../../grid_exchange_formats/index.md) are listed between brackets in the command help. `--output-compression-format` -This option defines the compression format of the case files. The list of supported formats are listed between brackets in the command help. +This option defines the compression format of the case files. The list of supported formats is mentioned between brackets in the command help. `--output-file` This option defines the path of the result file. If this option is omitted, the results are displayed in the console. `--output-format` -This option defines the format of the result file. This option is required if `--output-file` is used. The supported format are listed between brackets in the command help. +This option defines the format of the result file. This option is required if `--output-file` is used. The supported formats are listed between brackets in the command help. `--verbose` This option enables the verbose mode, to display more information during the simulation. ## Simulators -Currently, the only simulator which is supported is the [load-flow based]() simulator. +Currently, the only simulator which is supported is the [load-flow-based]() simulator. ## Parameters TODO @@ -127,7 +127,7 @@ action('load_shed_100') { ``` -The following example show the results of the simulation of the previous script: +The following example shows the results of the simulation of the previous script: ```shell $> itools action-simulator --case-file $HOME/eurostag-tutorial.xiidm --dsl-file $HOME/actions.groovy Loading network '$HOME/eurostag-tutorial.xiidm' diff --git a/docs/user/itools/compare-security-analysis-results.md b/docs/user/itools/compare-security-analysis-results.md index f495f8eca1d..e14bb690fd2 100644 --- a/docs/user/itools/compare-security-analysis-results.md +++ b/docs/user/itools/compare-security-analysis-results.md @@ -9,13 +9,13 @@ This tool compares for the pre-contingency state and for all the post-contingenc - the values of the constraints violations Two security analysis results are considered equivalent if all the following conditions are satisfied: -- for all the pre-contingency and post-contingency states, the corresponding (i.e. related to the same state) outcome of the load flow computation is the same -- for all the constraints violations, the difference of value of a corresponding (i.e. related to the same contingency +- for all the pre-contingency and post-contingency states, the corresponding (i.e., related to the same state) outcome of the load flow computation is the same +- for all the constraint violations, the difference of value of a corresponding (i.e., related to the same contingency and equipment) violation is less than a predefined threshold -- if a constraints violation is contained in just one result, the violation is less than a predefined threshold +- if a constraint violation is contained in just one result, the violation is less than a predefined threshold - if a contingency is contained in just one result, all the post-contingency violations are less than a predefined threshold -The comparison process can optionally output in a CSV file all the compared values (pre and post-contingency load flow computation outcomes, and related constraints violations), with a corresponding comparison result (`equivalent`,`different`). See example below. +The comparison process can optionally output in a CSV file all the compared values (pre- and post-contingency load flow computation outcomes, and related constraints violations), with a corresponding comparison result (`equivalent`,`different`). See example below. ``` Contingency;StatusResult1;StatusResult2;Equipment;End;ViolationType;ViolationNameResult1;ValueResult1;LimitResult1;ViolationNameResult2;ValueResult2;LimitResult2;ActionsResult1;ActionsResult2;Comparison diff --git a/docs/user/itools/dynamic-security-analysis.md b/docs/user/itools/dynamic-security-analysis.md index da55de41c06..afd22662d73 100644 --- a/docs/user/itools/dynamic-security-analysis.md +++ b/docs/user/itools/dynamic-security-analysis.md @@ -1,6 +1,6 @@ # iTools dynamic-security-analysis -The `dynamic-security-analysis` command loads a grid file, apply dynamic models, and run a [dynamic security analysis](../../simulation/dynamic_security/index.md) simulation, to detect security violations on pre- or post-contingencies states. At the end of the simulation the results are printed or exported to a file. +The `dynamic-security-analysis` command loads a grid file, apply dynamic models, and run a [dynamic security analysis](../../simulation/dynamic_security/index.md) simulation, to detect security violations on pre- or post-contingencies states. At the end of the simulation, the results are printed or exported to a file. ## Usage ``` @@ -54,7 +54,7 @@ This option defines the path of the mapping file used to associate dynamic model ### Optional arguments `--contingencies-file` -This option defines the path of the contingencies files. If this parameter is not set, the security violations are checked on the base state only. This file is a groovy script that respects the [contingency DSL](../../simulation/security/contingency-dsl.md) syntax. +This option defines the path of the contingency files. If this parameter is not set, the security violations are checked on the base state only. This file is a groovy script that respects the [contingency DSL](../../simulation/security/contingency-dsl.md) syntax. `--external` TODO: Use this argument to run the security analysis as an external process. @@ -63,7 +63,7 @@ This option defines the path of the contingencies files. If this parameter is no This option defines the path of the importer's configuration file. It's possible to overload one or many parameters using the `-I property=value` syntax. The list of supported properties depends on the [input format](../../grid_exchange_formats/index.md). `--limit-types` -This option allows to filter certain types of violations. It overrides the default configuration defined in the [limit-violation-default-filter](../configuration/limit-violation-default-filter.md) configuration module. The supported types are the following: `CURRENT`, `LOW_VOLTAGE`, `HIGH_VOLTAGE`, `LOW_SHORT_CIRCUIT_CURRENT`, `HIGH_SHORT_CIRCUIT_CURRENT` and `OTHER`. +This option allows filtering certain types of violations. It overrides the default configuration defined in the [limit-violation-default-filter](../configuration/limit-violation-default-filter.md) configuration module. The supported types are the following: `CURRENT`, `LOW_VOLTAGE`, `HIGH_VOLTAGE`, `LOW_SHORT_CIRCUIT_CURRENT`, `HIGH_SHORT_CIRCUIT_CURRENT` and `OTHER`. `--log-file` TODO @@ -78,7 +78,7 @@ This option defines the format of the output file. This option is required if th This option defines the path of the [parameters](#parameters) file of the simulation. If this option is not used, the simulation is run with the default parameters. `--with-extensions` -This option defines the list of extensions to complete the simulation results with additional data. This list of available extensions are listed in the usage of the command. +This option defines the list of extensions to complete the simulation results with additional data. The available extensions are listed in the usage of the command. ## Simulators TODO diff --git a/docs/user/itools/dynamic-simulation.md b/docs/user/itools/dynamic-simulation.md index 942e80b3358..a466cf75ac0 100644 --- a/docs/user/itools/dynamic-simulation.md +++ b/docs/user/itools/dynamic-simulation.md @@ -1,7 +1,7 @@ # iTools dynamic-simulation The `dynamic-simulation` command loads a grid file and run a [time domain](../../simulation/dynamic/index.md) simulation. -At the end, the results and the modified network can be exported to files. +In the end, the results and the modified network can be exported to files. ## Usage ``` @@ -83,7 +83,7 @@ dynamic simulation results: +--------+ ``` -The following example shows how to run a time domain simulation, using a parameters file: +The following example shows how to run a time domain simulation, using a parameter file: ``` $> itools dynamic-simulation --case-file IEEE14.iidm --dynamic-models-file dynamicModels.groovy --parameters-file dynawoParameters.json dynamic simulation results: diff --git a/docs/user/itools/index.md b/docs/user/itools/index.md index 71629611314..2d5a2898e05 100644 --- a/docs/user/itools/index.md +++ b/docs/user/itools/index.md @@ -18,7 +18,7 @@ dynamic-security-analysis.md sensitivity-computation.md ``` -The `iTools` script provides a command-line interface to interact with PowSyBl, available under Linux and Windows (MacOS is not supported yet). +The `iTools` script provides a command-line interface to interact with PowSyBl, available under Linux and Windows (macOS is not supported yet). An `iTools` package is constituted of: - a `bin` directory containing the executable scripts and the binaries @@ -66,7 +66,7 @@ java_xmx=8G The list of the deprecated properties is available [here]() ### Logging -The `iTools` script uses [logback](https://logback.qos.ch/) as logging framework. To configure the logging framework, edit the `/etc/logback-itools.xml` configuration file. Please refer to the [logback manual](https://logback.qos.ch/manual/index.html) for the available logging options. +The `iTools` script uses [logback](https://logback.qos.ch/) as a logging framework. To configure the logging framework, edit the `/etc/logback-itools.xml` configuration file. Please refer to the [logback manual](https://logback.qos.ch/manual/index.html) for the available logging options. Sometimes, it could be useful for a user to have its own logging configuration to filter unexpected logs or to have more details for some features. The simplest way to proceed is to copy the global configuration file in the `/.itools` folder and then customize it. @@ -89,7 +89,7 @@ Sometimes, it could be useful for a user to have its own logging configuration t The `iTools` script relies on a [plugin mechanism](): the commands are discovered at runtime and depend on the jars present in the `share/java` folder. | Command | Theme | Description | -|-----------------------------------------------------------------------------| --------------- |-----------------------------------------------| +|-----------------------------------------------------------------------------|-----------------|-----------------------------------------------| | [action-simulator](./action-simulator.md) | Computation | Run a security analysis with remedial actions | | [cim-anonymizer](cim_anonymizer.md) | Data conversion | Anonymize CIM files | | [compare-security-analysis-results](./compare-security-analysis-results.md) | Computation | Compare security analysis results | diff --git a/docs/user/itools/loadflow-validation.md b/docs/user/itools/loadflow-validation.md index 6131c7102e8..29d985376c2 100644 --- a/docs/user/itools/loadflow-validation.md +++ b/docs/user/itools/loadflow-validation.md @@ -66,13 +66,13 @@ Use the `--output-folder` parameter to define the path of the folder where the o ### Optional arguments `--compare-case-file` -Use the `--compare-case-file` parameter to define the path of the second case file, in order to compare the loadflow +Use the `--compare-case-file` parameter to define the path of the second case file, in order to compare the load flow results of two case files. `--compare-results` Use the `--compare-results` parameter to define the type of results to compare. The available types are: -- `BASECASE`: compare results of the two basecases -- `COMPUTATION`: run a computation on the two basecases and compare results of the resulting states. +- `BASECASE`: compare results of the two base cases +- `COMPUTATION`: run a computation on the two base cases and compare results of the resulting states. `--import-parameters` Use the `--import-parameters` parameter to specify the path of the configuration file of the importer. It is possible to @@ -86,8 +86,8 @@ Use the `--load-flow` parameter to run a load-flow before the validation. This o `--output-format` Use the `--output-format` parameter to specify the format of the output files. The available output formats are `CSV` or `CSV_MULTILINE`. -If this parameter is set to `CSV`, in the output files a line contains all values of a validated equipment. If the parameter -is set to `CSV_MULTILINE`, in the output files the values of an equipment are split in multiple lines, one value for each +If this parameter is set to `CSV`, in the output files a line contains all values of validated equipment. If the parameter +is set to `CSV_MULTILINE`, in the output files the values of a piece of equipment are split in multiple lines, one value for each line, see examples below: **CSV** @@ -125,40 +125,40 @@ corresponding case states validated and written in the output files. Some remark - State 2 is the state analyzed in the second validation (columns with the suffix `_postComp` in the output files) - Case 1 is the value of `case-file` parameter - Case 2 is the value of `compare-case-file` parameter -- some combinations are not available, e.g. if you use the `compare-results` parameter, with the `COMPUTATION` value, +- some combinations are not available, e.g., if you use the `compare-results` parameter, with the `COMPUTATION` value, you have to use the `run-computation` (or `load-flow`) parameter. -| Number | compare-results | run-computation | State 1 | State 2 (_postComp) | -| ------- | ------- | ------- | ------- | ------- | -| 1 | absent | absent | Case 1 after import | None | -| 2 | absent | `loadflow`/`loadflowResultsCompletion` | Case 1 after import and computation | None | -| 3 | `BASECASE` | absent | Case 1 after import | Case 2 after import | -| 4 | `BASECASE` | `loadflow`/`loadflowResultsCompletion` | Case 1 after import and computation | Case 2 after import | -| 5 | `COMPUTATION` | `loadflow`/`loadflowResultsCompletion` | Case 1 after import | Case 1 after import and computation | +| Number | compare-results | run-computation | State 1 | State 2 (_postComp) | +|--------|-----------------|----------------------------------------|-------------------------------------|-------------------------------------| +| 1 | absent | absent | Case 1 after import | None | +| 2 | absent | `loadflow`/`loadflowResultsCompletion` | Case 1 after import and computation | None | +| 3 | `BASECASE` | absent | Case 1 after import | Case 2 after import | +| 4 | `BASECASE` | `loadflow`/`loadflowResultsCompletion` | Case 1 after import and computation | Case 2 after import | +| 5 | `COMPUTATION` | `loadflow`/`loadflowResultsCompletion` | Case 1 after import | Case 1 after import and computation | ## Parameters To learn how to configure the `loadflow-validation` command, read the documentation of the [loadflow validation](../configuration/loadflow-validation.md) module. -You may also configure the loadflow itself to tune the loadflow validation using the `--run-computation` option (check the [loadflow configuration page](../configuration/load-flow.md)). +You may also configure the load flow itself to tune the load flow validation using the `--run-computation` option (check the [loadflow configuration page](../configuration/load-flow.md)). ## Load flow results validation -Overall, in the PowSyBl validation the tests are not made overly tight. In particular, leniency is preferred to tightness in case approximations are needed or +Overall, in the PowSyBl validation, the tests are not made overly tight. In particular, leniency is preferred to tightness in case approximations are needed or when expectations are unclear (typically when the input data is inconsistent). For example, there is a switch to test only the main component because it is not clear what to expect from load flow results on small connected components. Another important global setting available in the PowSyBl validation is the `ok-missing-values` parameter, which determines if is OK to have missing -values or `NaN`. Normally, it should be set to false but it may be useful in the cases where the power flow results are +values or `NaN`. Normally, it should be set to false, but it may be useful in the cases where the power flow results are incomplete to go through the rest of the validation. -In this section we go into more details about the checks performed by the validation feature of load-flow results available in PowSyBl. +In this section, we go into more details about the checks performed by the validation feature of load-flow results available in PowSyBl. ### Buses If all values are present, or if only one value is missing, the result is considered to be consistent. Note that if the result contains only the voltages (phase and angle), the PowSyBl validation provides a load-flow results completion feature. -It can be used to compute the flows from the voltages in order to ensure the results consistency, with the run-computation option of +It can be used to compute the flows from the voltages to ensure the result consistency, with the run-computation option of the PowSyBl validation. ### Branches @@ -167,19 +167,19 @@ The result on the branch is considered consistent if: $$\max( \left| P_1^{calc} - P_1 \right|, \left| Q_1^{calc} - Q_1 \right|, \left| P_2^{calc} - P_2 \right|, \left| Q_2^{calc} - Q_2 \right| ) \leq \epsilon$$ -For a branch that is disconnected on one end (for example end 2), then $P_2 = Q_2 = 0$. As a result, it is +For a branch that is disconnected on one end (for example, end 2), then $P_2 = Q_2 = 0$. As a result, it is possible to recompute $(V_2, \theta_2)$ which are usually not returned by power flows and which are not stored in node-breaker [network](../../grid_model/index.md) format. The quality checks are done when this is done. In case of missing results (usually the powers $P_1$, $Q_1$, $P_2$, $Q_2$ which are not mandatory), the PowSyBl validation will consider the results as inconsistent, unless `ok-missing-values` was set to `true` by the user on purpose to make the consistency -check more lenient. +check more leniently. -In case the voltages are available but not the powers, the results completion feature of the PowSyBl validation -can be used to recompute them using the validation equations (meaning that the branch validation tests will always be OK, so that it allows to perform the bus validation tests). +In case the voltages are available but not the powers, the result completion feature of the PowSyBl validation +can be used to recompute them using the validation equations (meaning that the branch validation tests will always be OK, so that it allows performing the bus validation tests). -### Three-windings transformers -To be implemented, based on a conversion into 3 two-windings transformers. +### Three-winding transformers +To be implemented, based on a conversion into 3 two-winding transformers. ### Generators @@ -212,7 +212,7 @@ $$ $$ In the PowSyBl validation, there are a few tricks to handle special cases: -- if $minQ > maxQ$, then the values are switched to recover a meaningfull interval if `noRequirementIfReactiveBoundInversion = false` +- if $minQ > maxQ$, then the values are switched to recover a meaningful interval if `noRequirementIfReactiveBoundInversion = false` - in case of a missing value, the corresponding test is OK - $minQ$ and $maxQ$ are function of $P$. If $targetP$ is outside $[minP, maxP]$, no test is done. @@ -252,8 +252,8 @@ $$\sum_{\text{stations}}{P} = \sum_{\text{stations}}{Loss} + Loss_{cable}$$ To check a steady-state has been reached, an upper bound of the deadband value is needed. Generally, the value of the deadband is not available in data models. Usual load flow solvers simply consider a continuous tap that is rounded -afterwards. As a result, one should compute an upper bound of the effect of the rounding. Under the usual situation where -the low voltage (side one) is controlled, the maximum effect is expected if the high voltage is fixed (usually it decreases) +afterward. As a result, one should compute an upper bound of the effect of the rounding. Under the usual situation where +the low voltage (side one) is controlled, the maximum effect is expected if the high voltage is fixed (usually it decreases), and if the network connected to the low voltage is an antenna. If the transformer is perfect, the equations are: - With the current tap `tap`, and if the regulated side is side `TWO`: @@ -277,17 +277,17 @@ $$ $$ Finally, we check that the voltage deviation $$\text{deviation} = V_2(tap) - targetV2$$ stays inside the deadband. -- If $deviation < 0$, meaning that the voltage is too low, it should be checked if the deviation would be smaller by -increasing V2, i.e. the following condition should be satisfied: $$\left| deviation \right| < down deadband + threshold$$ -- If $$deviation > 0$$, meaning that the voltage is too high, it should be checked if the deviation would be smaller by -decreasing V2, i.e. the following condition should be satisfied: $$deviation < up deadband + threshold$$ +- If $deviation < 0$, meaning that the voltage is too low, it should be checked if the deviation is smaller by +increasing V2, i.e., the following condition should be satisfied: $$\left| deviation \right| < down deadband + threshold$$ +- If $$deviation > 0$$, meaning that the voltage is too high, it should be checked if the deviation is smaller by +decreasing V2, i.e., the following condition should be satisfied: $$deviation < up deadband + threshold$$ -The test is done only if the regulated voltage is on one end of the transformer and it always returns OK if the controlled voltage is remote. +The test is done only if the regulated voltage is on one end of the transformer, and it always returns OK if the controlled voltage is remote. ## Examples ### Example 1 -The following example shows how to run a loadflow validation on a UCTE network model: +The following example shows how to run a load flow validation on a UCTE network model: ``` $> itools loadflow-validation --case-file 20170322_1844_SN3_FR2.uct --output-folder /tmp/results ``` @@ -306,7 +306,7 @@ Validate load-flow results of network 20170322_1844_SN3_FR2.uct - validation typ Eventually, you will find in your output-folder one csv file for each validation type. ### Example 2 -In this example we are comparing results of two validation: before and after load flow computation. Two additional +In this example, we are comparing results of two validations: before and after load flow computation. Two additional arguments are needed: - `load-flow` - `compare_results`: COMPUTATION diff --git a/docs/user/itools/loadflow.md b/docs/user/itools/loadflow.md index 5b0bc890efe..53e54a00c55 100644 --- a/docs/user/itools/loadflow.md +++ b/docs/user/itools/loadflow.md @@ -1,6 +1,6 @@ # iTools loadflow -The `loadflow` command loads a grid file and run a [load flow](../../simulation/loadflow/index.md) simulation. At the end, the results and the modified network can be exported to files. +The `loadflow` command loads a grid file and run a [load flow](../../simulation/loadflow/index.md) simulation. In the end, the results and the modified network can be exported to files. ## Usage ``` @@ -88,7 +88,7 @@ Components results: +------------------+-----------+-----------------+--------------+--------------------+ ``` -The following example shows how to run a power flow simulation, using a parameters file: +The following example shows how to run a power flow simulation, using a parameter file: ``` $> itools loadflow --case-file case.xiidm --parameters-file loadflowparameters.json loadflow results: diff --git a/docs/user/itools/run-script.md b/docs/user/itools/run-script.md index bd24b64b79d..f44be8905cb 100644 --- a/docs/user/itools/run-script.md +++ b/docs/user/itools/run-script.md @@ -21,7 +21,7 @@ Available arguments are: This option defines the path of the script to execute. Current, only Groovy scripts are supported. ## Groovy extensions -The `run-script` command relies on a [plugin mechanism]() to load extensions. Those extensions provide utility functions to make the usage of PowSyBl easier through the scripts. It avoids the user to write boilerplate code hiding the technical complexity of framework into more user-friendly functions. PowSyBl provides the following extensions to: +The `run-script` command relies on a [plugin mechanism]() to load extensions. Those extensions provide utility functions to make the usage of PowSyBl easier through the scripts. It prevents the user from writing boilerplate code and hides the technical complexity of the framework in more user-friendly functions. PowSyBl provides the following extensions to: - [load a network from a file](#load-a-network) - [save a network to a file](#save-a-network) - [run a power flow simulation](#run-a-power-flow) diff --git a/docs/user/itools/security-analysis.md b/docs/user/itools/security-analysis.md index 32fa2d1c529..0e705313f73 100644 --- a/docs/user/itools/security-analysis.md +++ b/docs/user/itools/security-analysis.md @@ -1,6 +1,6 @@ # iTools security-analysis -The `security-analysis` command loads a grid file and run a [security analysis](../../simulation/security/index.md) simulation, to detect security violations on pre- or post-contingencies states. At the end of the simulation the results are printed or exported to a file. +The `security-analysis` command loads a grid file and run a [security analysis](../../simulation/security/index.md) simulation, to detect security violations on pre- or post-contingencies states. At the end of the simulation, the results are printed or exported to a file. ## Usage ``` @@ -45,7 +45,7 @@ This option defines the path of the case file on which the power flow simulation ### Optional arguments `--contingencies-file` -This option defines the path of the contingencies files. If this parameter is not set, the security violations are checked on the base state only. This file is a groovy script that respects the [contingency DSL](../../simulation/security/contingency-dsl.md) syntax. +This option defines the path of the contingency files. If this parameter is not set, the security violations are checked on the base state only. This file is a groovy script that respects the [contingency DSL](../../simulation/security/contingency-dsl.md) syntax. `--external` TODO: Use this argument to run the security analysis as an external process. @@ -55,7 +55,7 @@ This option defines the path of the contingencies files. If this parameter is no This option defines the path of the importer's configuration file. It's possible to overload one or many parameters using the `-I property=value` syntax. The list of supported properties depends on the [input format](../../grid_exchange_formats/index.md). `--limit-types` -This option allows to filter certain types of violations. It overrides the default configuration defined in the [limit-violation-default-filter](../configuration/limit-violation-default-filter.md) configuration module. The supported types are the following: `CURRENT`, `LOW_VOLTAGE`, `HIGH_VOLTAGE`, `LOW_SHORT_CIRCUIT_CURRENT`, `HIGH_SHORT_CIRCUIT_CURRENT` and `OTHER`. +This option allows filtering certain types of violations. It overrides the default configuration defined in the [limit-violation-default-filter](../configuration/limit-violation-default-filter.md) configuration module. The supported types are the following: `CURRENT`, `LOW_VOLTAGE`, `HIGH_VOLTAGE`, `LOW_SHORT_CIRCUIT_CURRENT`, `HIGH_SHORT_CIRCUIT_CURRENT` and `OTHER`. `--log-file` TODO @@ -70,7 +70,7 @@ This option defines the format of the output file. This option is required if th This option defines the path of the [parameters](#parameters) file of the simulation. If this option is not used, the simulation is run with the default parameters. `--with-extensions` -This option defines the list of extensions to complete the simulation results with additional data. This list of available extensions are listed in the usage of the command. +This option defines the list of extensions to complete the simulation results with additional data. The available extensions are listed in the usage of the command. ## Simulators TODO @@ -87,7 +87,7 @@ This option defines the list of extensions to complete the simulation results wi ## Examples ### Example 1 -The following example shows how to run a security analysis simulation to detect only pre-contingency violations, for a given network: +The following example shows how to run a security analysis simulation to detect only pre-contingency violations for a given network: ``` $> itools security-analysis --case-file 20170322_1844_SN3_FR2.uct ``` @@ -104,7 +104,7 @@ Pre-contingency violations: ``` ### Example 2 -The following example shows how to run a security analysis simulation to detect the post-contingency violations status of given network and a set of contingencies. +The following example shows how to run a security analysis simulation to detect the post-contingency violations status of a given network and a set of contingencies. **Content of the contingencies.groovy file:**`` ``` From faacf1fadd18f7fd2ca559e24d868b87c6d435ae Mon Sep 17 00:00:00 2001 From: Coline Piloquet <55250145+colinepiloquet@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:25:17 +0200 Subject: [PATCH 52/57] Add anchors in the documentation (#3126) Signed-off-by: Coline PILOQUET --- docs/grid_exchange_formats/ampl/export.md | 1 + docs/grid_exchange_formats/cgmes/export.md | 18 ++++++++++ docs/grid_exchange_formats/cgmes/import.md | 33 +++++++++++++++++++ docs/grid_exchange_formats/iidm/export.md | 1 + docs/grid_exchange_formats/iidm/import.md | 1 + docs/grid_exchange_formats/iidm/index.md | 2 ++ docs/grid_exchange_formats/psse/import.md | 12 +++++-- .../ucte/format_specification.md | 1 + docs/grid_exchange_formats/ucte/import.md | 10 ++++-- docs/grid_features/import_post_processor.md | 3 ++ docs/grid_features/loadflow_validation.md | 11 +++++++ docs/simulation/shortcircuit/parameters.md | 2 +- docs/user/itools/index.md | 2 ++ 13 files changed, 92 insertions(+), 5 deletions(-) diff --git a/docs/grid_exchange_formats/ampl/export.md b/docs/grid_exchange_formats/ampl/export.md index fe015b58f1c..49d2587d3d6 100644 --- a/docs/grid_exchange_formats/ampl/export.md +++ b/docs/grid_exchange_formats/ampl/export.md @@ -1,6 +1,7 @@ # Export TODO +(ampl-export-options)= ## Options These properties can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. diff --git a/docs/grid_exchange_formats/cgmes/export.md b/docs/grid_exchange_formats/cgmes/export.md index 33c52979b80..5c30e44e3a5 100644 --- a/docs/grid_exchange_formats/cgmes/export.md +++ b/docs/grid_exchange_formats/cgmes/export.md @@ -25,6 +25,7 @@ If the dependencies have to be updated automatically (see parameter **iidm.expor The output filenames will follow the pattern `_.xml`. The basename is determined from the parameters, or the basename of the export data source or the main network name. +(cgmes-cgm-quick-export)= ## CGM (Common Grid Model) quick export When exporting a CGM, we need an IIDM network (CGM) that contains multiple subnetworks (one for each IGM). @@ -72,6 +73,7 @@ exampleBase_SV.xml where the updated SSH files will supersede the original ones, and the SV will contain the correct dependencies of new SSH and original TPs. +(cgmes-cgm-manual-export)= ## CGM (Common Grid Model) manual export If you want to intervene in how the updated IGM SSH files or the CGM SV are exported, you can make multiple calls to the CGMES export function. @@ -155,18 +157,21 @@ Remember that, in addition to setting the info for metadata models in the IIDM e The following sections describe in detail how each supported PowSyBl network model object is converted to CGMES network components. +(cgmes-battery-export)= ### Battery PowSyBl [`Battery`](../../grid_model/network_subnetwork.md#battery) is exported as `SynchronousMachine` with `HydroGeneratingUnit`. TODO details +(cgmes-busbar-section-export)= ### BusbarSection PowSyBl [`BusbarSection`](../../grid_model/network_subnetwork.md#busbar-section) is exported as CGMES `BusbarSection`. TODO details +(cgmes-dangling-line-export)= ### DanglingLine PowSyBl [`DanglingLine`](../../grid_model/network_subnetwork.md#dangling-line) is exported as several CGMES network objects. @@ -174,6 +179,7 @@ Each dangling line will be exported as one `EquivalentInjection` and one `ACLine TODO details +(cgmes-generator-export)= ### Generator PowSyBl [`Generator`](../../grid_model/network_subnetwork.md#generator) is exported as CGMES `SynchronousMachine`. @@ -189,24 +195,28 @@ generator has the extension [`RemoteReactivePowerControl`](../../grid_model/exte with the `enabled` activated and the generator attribute `voltageRegulatorOn` set to `false`. In all other cases, a `RegulatingControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.voltage`. +(cgmes-hvdc-export)= ### HVDC line and HVDC converter stations A PowSyBl [`HVDCLine`](../../grid_model/network_subnetwork.md#hvdc-line) and its two [`HVDCConverterStations`](../../grid_model/network_subnetwork.md#hvdc-converter-station) are exported as a `DCLineSegment` with two `DCConverterUnits`. TODO details +(cgmes-line-export)= ### Line PowSyBl [`Line`](../../grid_model/network_subnetwork.md#line) is exported as `ACLineSegment`. TODO details +(cgmes-load-export)= ### Load PowSyBl [`Load`](../../grid_model/network_subnetwork.md#load) is exported as `ConformLoad`, `NonConformLoad` or `EnergyConsumer` depending on the extension [`LoadDetail`](../../grid_model/extensions.md#load-detail). TODO details +(cgmes-shunt-compensator-export)= ### Shunt compensator PowSyBl [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator) is exported as `LinearShuntCompensator` or `NonlinearShuntCompensator` depending on their models. @@ -220,6 +230,7 @@ A shunt compensator with local voltage control (i.e., the regulating terminal is and no valid voltage target will not have any exported regulating control. In all other cases, a `RegulatingControl` is exported with `RegulatingControl.mode` set to `RegulatingControlModeKind.voltage`. +(cgmes-static-var-compensator-export)= ### StaticVarCompensator PowSyBl [`StaticVarCompensator`](../../grid_model/network_subnetwork.md#static-var-compensator) is exported as `StaticVarCompensator`. @@ -238,18 +249,21 @@ to `RegulatingControlModeKind.voltage` when the static VAR compensator mode is ` is `OFF`, the exported regulating control mode will be reactive power only if the voltage target is not valid but the reactive power target is. Otherwise, the exported mode will be voltage. +(cgmes-substation-export)= ### Substation PowSyBl [`Substation`](../../grid_model/network_subnetwork.md#substation) is exported as `Substation`. TODO details +(cgmes-switch-export)= ### Switch PowSyBl [`Switch`](../../grid_model/network_subnetwork.md#breakerswitch) is exported as CGMES `Breaker`, `Disconnector` or `LoadBreakSwitch` depending on its `SwitchKind`. TODO details +(cgmes-three-winding-transformer-export)= ### ThreeWindingsTransformer PowSyBl [`ThreeWindingsTransformer`](../../grid_model/network_subnetwork.md#three-winding-transformer) is exported as `PowerTransformer` with three `PowerTransformerEnds`. @@ -270,12 +284,14 @@ In a `PhaseTapChanger`, the `TapChangerControl` is exported with `RegulatingCont `PhaseTapChanger` `regulationMode` is set to `ACTIVE_POWER_CONTROL`, and with `RegulatingControl.mode` set to `RegulatingControlModeKind.currentFlow` when `PhaseTapChanger` `regulationMode` is set to `CURRENT_LIMITER`. +(cgmes-two-winding-transformer-export)= ### TwoWindingsTransformer PowSyBl [`TwoWindingsTransformer`](../../grid_model/network_subnetwork.md#two-winding-transformer) is exported as `PowerTransformer` with two `PowerTransformerEnds`. Tap changer controls for two-winding transformers are exported following the same rules explained in the previous section about three-winding transformers. See [tap changer control](#tap-changer-control). +(cgmes-voltage-level-export)= ### Voltage level PowSyBl [`VoltageLevel`](../../grid_model/network_subnetwork.md#voltage-level) is exported as `VoltageLevel`. @@ -284,12 +300,14 @@ PowSyBl [`VoltageLevel`](../../grid_model/network_subnetwork.md#voltage-level) i ## Extensions +(cgmes-control-areas-export)= ### Control areas PowSyBl [`ControlAreas`](import.md#cgmes-control-areas) are exported as several `ControlArea`. TODO details +(cgmes-export-options)= ## Options These properties can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. diff --git a/docs/grid_exchange_formats/cgmes/import.md b/docs/grid_exchange_formats/cgmes/import.md index 3ef15fd9534..a9ca9aef94e 100644 --- a/docs/grid_exchange_formats/cgmes/import.md +++ b/docs/grid_exchange_formats/cgmes/import.md @@ -7,6 +7,7 @@ The CIM-CGMES importer reads and converts a CIM-CGMES model to the PowSyBl grid The data in input CIM/XML files uses RDF (Resource Description Framework) syntax. In RDF, data is described making statements about resources using triplet expressions: (subject, predicate, object). To describe the conversion from CGMES to PowSyBl, we first introduce some generic considerations about the level of detail of the model (node/breaker or bus/branch), the identity of the equipments and equipment containment in substations and voltage levels. After that, the conversion for every CGMES relevant class is explained. Consistency checks and validations performed during the conversion are mentioned in the corresponding sections. +(cgmes-import-level-of-detail)= ## Levels of detail: node/breaker and bus/branch CGMES models defined at node/breaker level of detail will be mapped to PowSyBl node/breaker topology level. CGMES models defined at bus/branch level will be mapped to PowSyBl bus/breaker topology level. @@ -23,12 +24,14 @@ Some equipment, like switches, lines or transformers, have more than one point o In PowSyBl, a `Node` can have zero or one terminal. In CGMES, the `ConnectivityNode` objects may have more than one associated terminal. To be able to represent this in PowSyBl, the conversion process will automatically create internal connections between the PowSyBl nodes that represent equipment connections and the nodes created to map `ConnectivityNode` objects. +(cgmes-id-import)= ## Identity of model equipments Almost all the equipments of the PowSyBl grid model require a unique identifier `Id` and may optionally have a human-readable `Name`. Whenever possible, these attributes will be directly copied from original CGMES attributes. Terminals are used by CGMES and PowSyBl to define the points of connection of the equipment to the network. CGMES terminals have unique identifiers. PowSyBl does not allow terminals to have an associated identifier. Information about original CGMES terminal identifiers is stored in each PowSyBl object using aliases. +(cgmes-substation-voltage-level-import)= ## Equipment containers: substations and voltage levels The PowSyBl grid model establishes the substation as a required container of voltage levels and transformers (two- and three-winding transformers and phase shifters). Voltage levels are the required container of the rest of the network equipment, except for the AC and DC transmission lines that establish connections between substations and are directly associated with the network model. All buses at the transformer ends should be kept in the same substation. @@ -39,12 +42,14 @@ The CGMES model does not guarantee these hierarchical constraints, so the first The following sections describe in detail how each supported CGMES network object is converted to PowSyBl network model objects. +(cgmes-substation-import)= ### Substation For each substation (considering only the representative substation if they are connected by transformers) in the CGMES model a new substation is created in the PowSyBl grid model with the following attributes created as such: - `Country` It is obtained from the `regionName` property as a first option, from `subRegionName` as second option. Otherwise, is assigned to `null`. - `GeographicalTags` It is obtained from the `SubRegion` property. +(cgmes-voltage-level-import)= ### VoltageLevel As for substations, for each voltage level (considering only the representative voltage level if they are connected by switches) in the CGMES model, a new voltage level is created in the PowSyBl grid model with the following attributes created as such: @@ -53,6 +58,7 @@ As for substations, for each voltage level (considering only the representative - `LowVoltageLimit` It is copied from the `lowVoltageLimit` property. - `HighVoltageLimit` It is copied from the `highVoltageLimit` property. +(cgmes-connectivity-node-import)= ### ConnectivityNode If the CGMES model is a node/breaker model then `ConnectivityNode` objects are present in the CGMES input files, and for each of them a new `Node` is created in the corresponding PowSyBl voltage level. A `Node` in the PowSyBl model is an integer identifier that is unique by voltage level. @@ -61,6 +67,7 @@ If the import option `iidm.import.cgmes.create-busbar-section-for-every-connecti - Identity attributes `Id` and `Name` are copied from the `ConnectivityNode`. - `Node` The same `Node` assigned to the mapped `ConnectivityNode`. +(cgmes-topological-node-import)= ### TopologicalNode If the CGMES model is defined at bus/branch detail, then `TopologicalNode` objects are used in the conversion, and for each of them a `Bus` is created in the PowSyBl grid model inside the corresponding voltage level container, at the PowSyBl bus/breaker topology level. The created `Bus` has the following attributes: @@ -68,6 +75,7 @@ If the CGMES model is defined at bus/branch detail, then `TopologicalNode` objec - `V` The voltage of the `TopologicalNode` is copied if it is valid (greater than `0`). - `Angle` The angle the `TopologicalNode` is copied if the previous voltage is valid. +(cgmes-busbar-section-import)= ### BusbarSection Busbar sections can be created in PowSyBl grid model only at node/breaker level. @@ -76,6 +84,7 @@ CGMES Busbar sections are mapped to PowSyBl busbar sections only if CGMES is nod - Identity attributes `Id` and `Name` are copied from the CGMES `BusbarSection`. - `Node` A new `Node` in the corresponding voltage level. +(cgmes-energy-consumer-import)= ### EnergyConsumer Every `EnergyConsumer` in the CGMES model creates a new `Load` in PowSyBl. The attributes are created as such: @@ -95,6 +104,7 @@ When the type is a non-conform load: - `withVariableActivePower` is set to `0`. - `withVariableReactivePower` is set to `0`. +(cgmes-energy-source-import)= ### EnergySource An `EnergySource` is a generic equivalent for an energy supplier, with the injection given using load sign convention. @@ -103,6 +113,7 @@ For each `EnergySource` object in the CGMES model a new PowSyBl `Load` is create - `P0`, `Q0` set from `SSH` or `SV` values depending on which are defined. - `LoadType` It will be `FICTITIOUS` if the `Id` of the `energySource` contains the pattern `fict`. Otherwise `UNDEFINED`. +(cgmes-sv-injection-import)= ### SvInjection CGMES uses `SvInjection` objects to report mismatches on calculated buses: they record the calculated bus injection minus the sum of the terminal flows. According to the documentation, the values will thus follow generator sign convention: positive sign means injection into the bus. Note that all the reference cases used for development follow load sign convention to report these mismatches, so we have decided to follow this load sign convention as a first approach. @@ -112,6 +123,7 @@ For each `SvInjection` in the CGMES network model a new PowSyBl `Load` with attr - `LoadType` is always set to `FICTITIOUS`. - `Fictitious` is set to `true`. +(cgmes-equivalent-injection-import)= ### EquivalentInjection The mapping of an `EquivalentInjection` depends on its location relative to the boundary area. @@ -128,6 +140,7 @@ Attributes of the PowSyBl generator or of the PowSyBl dangling line generation a - `VoltageRegulatorOn` It is assigned to `true` if both properties, `regulationCapability` and `regulationStatus` are `true` and the terminal is connected. - `EnergySource` is set to `OTHER`. +(cgmes-ac-line-segment-import)= ### ACLineSegment `ACLineSegments`' mapping depends on its location relative to the boundary area. @@ -170,6 +183,7 @@ If the `ACLineSegment` is mapped to a paired PowSyBl [`DanglingLine`](../../grid - `B2` is `0.0` is the dangling line is on side `TWO` of the Tie Line. If the dangling line is on side `ONE` of the Tie Line, it is copied from CGMES `bch`. - `PairingKey` is copied from the name of the `TopologicalNode` or the `ConnectivityNode` (respectively in `NODE-BREAKER` or `BUS-BRANCH`) inside boundaries +(cgmes-equivalent-branch-import)= ### EquivalentBranch Equivalent branches mapping depends on its location relative to the boundary area. @@ -203,6 +217,7 @@ If the `EquivalentBranch` is mapped to a PowSyBl [`DanglingLine`](../../grid_mod - `P0` is copied from CGMES `P` of the terminal at boundary side - `Q0` is copied from CGMES `Q` of the terminal at boundary side +(cgmes-asynchronous-machine-import)= ### AsychronousMachine Asynchronous machines represent rotating machines whose shaft rotates asynchronously with the electrical field. @@ -212,6 +227,7 @@ An `AsynchronousMachine` is mapped to a PowSyBl [`Load`](../../grid_model/networ - `P0`, `Q0` are set from CGMES values taken from `SSH` or `SV`data depending on which are defined. If there is no defined data, it is `0.0`. - `LoadType` is `FICTITIOUS` if the CGMES ID contains "fict". Otherwise, it is `UNDEFINED`. +(cgmes-synchronous-machine-import)= ### SynchronousMachine Synchronous machines represent rotating machines whose shaft rotates synchronously with the electrical field. @@ -236,6 +252,7 @@ A `SynchronousMachine` is mapped to a PowSyBl [`Generator`](../../grid_model/net TODO normalPF +(cgmes-equivalent-shunt-import)= ### EquivalentShunt An `EquivalentShunt` is mapped to a PowSyBl linear [`ShuntCompensator`](../../grid_model/network_subnetwork.md#shunt-compensator). A linear shunt compensator has banks or sections with equal admittance values. @@ -244,6 +261,7 @@ Its attributes are created as described below: - `BPerSection` is copied from CGMES `b` - `MaximumSectionCount` is set to `1` +(cgmes-external-network-injection-import)= ### ExternalNetworkInjection External network injections are injections representing the flows from an entire external network. @@ -258,6 +276,7 @@ An `ExternalNetworkinjection` is mapped to a PowSyBl [`Generator`](../../grid_mo TODO regulation +(cgmes-linear-shunt-compensator-import)= ### LinearShuntCompensator Linear shunt compensators represent shunt compensators with banks or sections with equal admittance values. @@ -270,6 +289,7 @@ The created PowSyBl shunt compensator is linear, and its attributes are defined TODO regulation +(cgmes-nonlinear-shunt-compensator-import)= ### NonlinearShuntCompensator Non-linear shunt compensators represent shunt compensators with banks or section admittance values that differ. @@ -283,9 +303,11 @@ Sections are created from the lowest CGMES `sectionNumber` to the highest and ea TODO regulation +(cgmes-operational_limit-import)= ### OperationalLimit TODO +(cgmes-power-transformer-import)= ### PowerTransformer Power transformers represent electrical devices consisting of two or more coupled windings, each represented by a `PowerTransformerEnd`. PowSyBl only supports `PowerTransformers` with two or three windings. @@ -322,6 +344,7 @@ Please note that in this case, if `PowerTransformerEnds` are in different substa For more information about this conversion, please look at the classes [`InterpretedT3xModel`](https://github.com/powsybl/powsybl-core/blob/main/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/transformers/InterpretedT3xModel.java) and [`ConvertedT3xModel`](https://github.com/powsybl/powsybl-core/blob/main/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/elements/transformers/ConvertedT3xModel.java). +(cgmes-series-compensator-import)= ### SeriesCompensator Series compensators represent series capacitors or reactors or AC transmission lines without charging susceptance. @@ -334,6 +357,7 @@ If a `SeriesCompensator` has its ends inside different voltage levels, it is map - `X` is copied from CGMES `x` - `G1`, `G2`, `B1` and `B2` are set to `0` +(cgmes-static-var-compensator-import)= ### StaticVarCompensator Static VAR compensators represent a facility for providing variable and controllable shunt reactive power. @@ -346,6 +370,7 @@ A PowSyBl [`VoltagePerReactivePowerControl`](../../grid_model/extensions.md#volt TODO regulation +(cgmes-switch-import)= ### Switch (Switch, Breaker, Disconnector, LoadBreakSwitch, ProtectedSwitch, GroundDisconnector) Switches, breakers, disconnectors, load break switches, protected switches and ground disconnectors are @@ -381,6 +406,7 @@ If the CGMES `Switch` is mapped to a PowSyBl [`DanglingLine`](../../grid_model/n The CIM-CGMES format contains more information than what the `iidm` grid model needs for calculation. The additional data that are needed to export a network in CIM-CGMES format are stored in several extensions. +(cgmes-control-areas-import)= ### CGMES control areas This extension models all the control areas contained in the network as modeled in CIM-CGMES. @@ -404,6 +430,7 @@ It is possible to retrieve a control area by its ID. It is also possible to iter This extension is provided by the `com.powsybl:powsybl-cgmes-extensions` module. +(cgmes-dangling-line-boundary-node-import)= ### CGMES dangling line boundary node This extension is used to add some CIM-CGMES characteristics to dangling lines. @@ -416,6 +443,7 @@ This extension is used to add some CIM-CGMES characteristics to dangling lines. This extension is provided by the `com.powsybl:powsybl-cgmes-extensions` module. +(cgmes-line-boundary-node-import)= ### CGMES line boundary node This extension is used to add some CIM-CGMES characteristics to tie lines. @@ -427,20 +455,25 @@ This extension is used to add some CIM-CGMES characteristics to tie lines. This extension is provided by the `com.powsybl:powsybl-cgmes-extensions` module. +(cgmes-tap-changers-import)= ### CGMES Tap Changers TODO +(cgmes-metadata-model-import)= ### CGMES metadata model TODO +(cgmes-cim-characteristics-import)= ### CIM characteristics TODO +(cgmes-model-import)= ### CGMES model +(cgmes-import-options)= ## Options These properties can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md) module. diff --git a/docs/grid_exchange_formats/iidm/export.md b/docs/grid_exchange_formats/iidm/export.md index 92a783b2d65..5d2b5f6195c 100644 --- a/docs/grid_exchange_formats/iidm/export.md +++ b/docs/grid_exchange_formats/iidm/export.md @@ -2,6 +2,7 @@ TODO +(iidm-export-options)= ## Options These properties can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. diff --git a/docs/grid_exchange_formats/iidm/import.md b/docs/grid_exchange_formats/iidm/import.md index 16392f31df1..9259a22b935 100644 --- a/docs/grid_exchange_formats/iidm/import.md +++ b/docs/grid_exchange_formats/iidm/import.md @@ -2,6 +2,7 @@ TODO +(iidm-import-options)= ## Options These properties can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. diff --git a/docs/grid_exchange_formats/iidm/index.md b/docs/grid_exchange_formats/iidm/index.md index d90d736f70b..ea04c69f517 100644 --- a/docs/grid_exchange_formats/iidm/index.md +++ b/docs/grid_exchange_formats/iidm/index.md @@ -18,6 +18,7 @@ Below are two exports from the same network: - one XML export (XIIDM exchange format) - one JSON export (JIIDM exchange format) +(xiidm)= ## XIIDM ```xml @@ -64,6 +65,7 @@ Below are two exports from the same network: ``` +(jiidm)= ## JIIDM ```json { diff --git a/docs/grid_exchange_formats/psse/import.md b/docs/grid_exchange_formats/psse/import.md index fdfc85d0625..cad323b5a93 100644 --- a/docs/grid_exchange_formats/psse/import.md +++ b/docs/grid_exchange_formats/psse/import.md @@ -7,12 +7,14 @@ The import module reads and converts a PSS®E power flow data file to the PowSyB First, input data is obtained by reading and parsing the input file, and as a result, a PSS®E model is created in memory. This model can be viewed as a set of Java classes where each data block of the PSS®E model is associated with a specific Java class that describes all their attributes or data items. Then, some inconsistency checks are performed on this model. If the validation succeeds, the PSS®E model is converted to a PowSyBl grid model. +(psse-import-options)= ## Options Parameters for the import can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. **psse.import.ignore-base-voltage** The `psse.import.ignore-base-voltage` property is an optional property that defines if the importer should ignore the base voltage information present in the PSS®E file. The default value is `false`. +(psse-inconsistency-checks)= ## Inconsistency checks -TODO @@ -37,6 +39,7 @@ Every voltage level is assigned to its corresponding substation, with attributes The following sections describe in detail how each supported PSS®E data block is converted to PowSyBl network model objects. +(psse-bus-data-conversion)= ### _Bus Data_ There is a one-to-one correspondence between the records of the PSS®E _Bus Data_ block and the buses of the PowSyBl network model. For each record in the _Bus Data_ block a PowSyBl bus is created and assigned to its corresponding voltage level with the following attributes: @@ -46,6 +49,7 @@ There is a one-to-one correspondence between the records of the PSS®E _Bus Data - **Angle** is copied from the PSS®E bus voltage phase angle, `VA`. +(psse-load-data-conversion)= ### _Load Data_ Every _Load Data_ record represents one load. Multiple loads are allowed at the same bus by specifying one _Load Data_ record with the same bus and different load identifiers. Each record defines a new load in the PowSyBl grid model associated with its corresponding voltage level and with the following attributes: @@ -59,6 +63,7 @@ The load is connected to the ConnectableBus if load status (field `STATUS` in th PSS®E supports loads with three different characteristics: Constant Power, Constant Current and Constant Admittance. The current version only takes into consideration the Constant Power component, discarding the Constant Current component (fields `IP` and `IQ` in the _Load Data_ record) and the Constant Admittance component (fields `YP` and `YQ` in the _Load Data_ record). +(psse-fixed-bus-shunt-data-conversion)= ### _Fixed Bus Shunt Data_ Each _Fixed Bus Shunt Data_ record defines a PowSyBl shunt compensator with a linear model and a single section. It is possible to define multiple fixed shunts at the same bus. The PowSyBl shunt compensator is associated with its corresponding voltage level and has the following attributes: @@ -73,6 +78,7 @@ Each _Fixed Bus Shunt Data_ record defines a PowSyBl shunt compensator with a li The shunt compensator is connected to the ConnectableBus if fixed shunt status (field `STATUS` in the _Fixed Bus Shunt Data_ record) is `1` (In-service). +(psse-switched-shunt-data-conversion)= ### _Switched Shunt Data_ In the PSS®E version 33, only one switched shunt element can be defined on each bus. Version 35 allows multiple switched shunts on the same bus, adding an alphanumeric switched shunt identifier. @@ -103,6 +109,7 @@ When the adjustment method `ADJM` is `0`, the behaviour of the switched shunt ca If the adjustment method `ADJM` is `1`, the reactor and capacitor blocks can be specified at any order, and all the switching combinations are considered in PSS®E. Current conversion does not support building a separate section for each switching combination. To map the PSS@E shunt blocks/steps into PowSyBl sections, first the reactor and capacitor blocks are increasingly ordered by susceptance (field `BI` in the _Switched Shunt Data_ record) and then sections are created like in the previous adjustment considering that blocks are switched on following the sorted order. +(psse-generator-data-conversion)= ### _Generator Data_ Every _Generator Data_ single line record represents one generator. Multiple generators are allowed at a PSS®E bus by specifying the same bus and a different identifier. Each record defines a new generator in the PowSyBl grid model associated with its corresponding voltage level and with the following attributes: @@ -120,7 +127,7 @@ Every _Generator Data_ single line record represents one generator. Multiple gen The generator is connected to the ConnectableBus if generator status (field `STAT` in the _Generator Data_ record) is `1` (In-service). - +(psse-branch-data-conversion)= ### _Non-Transformer Branch Data_ In PSS®E each AC transmission line is represented as a non-transformer branch record and defines a new line in the PowSyBl grid model with the following attributes: @@ -140,7 +147,7 @@ The line is connected at both ends if the branch status (field `ST` in the _Non- A set of current permanent limits is defined as `1000.0` * `rateMva` / (`sqrt(3.0)` * `vnom1`) at the end `1` and `1000.0` * `rateMva` / (`sqrt(3.0)` * `vnom2`) at the end `2` where `rateMva` is the first rating of the branch (field `RATEA` in version 33 of the _Non-Transformer Branch Data_ record and field `RATE1` in version 35) and `vnom1` and `vnom2` are the nominal voltages of the associated voltage levels. - +(psse-transformer-data-conversion)= ### _Transformer Data_ The _Transformer Data_ block defines two- and three-winding transformers. Two-winding transformers have four line records, while three-winding transformers have five line records. A `0` value in the field `K` of the _Transformer Data_ record first line is used to indicate that is a two-winding transformer, otherwise is considered a three-winding transformer. @@ -222,6 +229,7 @@ When a three-winding transformer is modeled, the two-winding transformer steps s ![ThreeWindingsTransformerModels](img/three-winding-transformer-model.svg){width="100%" align=center class="only-light"} ![ThreeWindingsTransformerModels](img/dark_mode/three-winding-transformer-model.svg){width="100%" align=center class="only-dark"} +(psse-slack-bus-conversion)= ### Slack bus The buses defined as slack terminal are the buses with type code `3` (field `IDE` in the _Bus Data_ record). diff --git a/docs/grid_exchange_formats/ucte/format_specification.md b/docs/grid_exchange_formats/ucte/format_specification.md index 6b1b5c9c846..c513d46566e 100644 --- a/docs/grid_exchange_formats/ucte/format_specification.md +++ b/docs/grid_exchange_formats/ucte/format_specification.md @@ -13,6 +13,7 @@ Each block is introduced by a key line consisting of the two characters `##` and The grid is described in Bus/Branch topology, and only a few types of equipment are supported (nodal injections, AC line, two-winding transformer). Fictitious nodes are located at the electric middle of each tie line. The defined X-nodes are binding for all users. +(ucte-file-name-convention)= ## File name convention The UCTE-DEF format use the following file name convention: `___.uct` with: - `yyyymmdd`: year, month and day diff --git a/docs/grid_exchange_formats/ucte/import.md b/docs/grid_exchange_formats/ucte/import.md index f2a1bd3f3a9..5e484fa9b28 100644 --- a/docs/grid_exchange_formats/ucte/import.md +++ b/docs/grid_exchange_formats/ucte/import.md @@ -8,7 +8,7 @@ The UCTE parser provided by PowSyBl does not support the blocks `##TT` and `##E` description of the two-winding transformers and the scheduled active power exchange between countries. Those blocks are ignored during the parsing step. - +(ucte-import-options)= ## Options Parameters for the import can be defined in the configuration file in the [import-export-parameters-default-value](../../user/configuration/import-export-parameters-default-value.md#import-export-parameters-default-value) module. @@ -31,6 +31,7 @@ considered as [area](../../grid_model/network_subnetwork.md#area) DC boundaries. The default value is an empty list. For more details see further below about [area conversion](#area-conversion). +(ucte-inconsistency-checks)= ## Inconsistency checks Once the UCTE grid model is created in memory, a consistency check is performed on the different elements (nodes, lines, two-winding transformers and regulations). @@ -102,6 +103,7 @@ Notations: ## From UCTE to IIDM The UCTE file name is parsed to extract metadata required to initialize the IIDM network, such as its ID, the case date and the forecast distance. +(ucte-node-conversion)= ### Node conversion There is no equivalent [voltage level](../../grid_model/network_subnetwork.md#voltage-level) or [substation](../../grid_model/network_subnetwork.md#substation) concept in the UCTE-DEF format, so we have to create substations and voltage levels from the node description and the topology. @@ -132,6 +134,7 @@ The power plant type is converted to an [energy source]() value (see the mapping The list of the power plant types is more detailed than the list of available [energy source]() types in IIDM, so we add the `powerPlantType` property to each generator to keep the initial value. +(ucte-line-conversion)= ### Line conversion The busbar couplers connecting two real nodes are converted into a [switch](../../grid_model/network_subnetwork.md#breakerswitch). This switch is open or closed depending on the status of the coupler. In the UCTE-DEF format, the coupler can have extra information not supported in IIDM we keep @@ -151,6 +154,7 @@ In IIDM, a dangling line is a line segment connected to a constant load. The sum of the active load and generation (rep. reactive) is computed to initialize the `P0` (resp. `Q0`) of the dangling line. The element name of the UCTE line is stored in the `elementName` property and the geographical name of the X-node is stored in the `geographicalName` property. +(ucte-two-winding-transformer-conversion)= ### Two-winding transformer conversion The two-winding transformers connected between two real nodes are converted into a [two-winding transformer](../../grid_model/network_subnetwork.md#two-winding-transformer). If the current limits are defined, a permanent limit is created only for the second side. @@ -164,13 +168,14 @@ except for the reactance that is set to $0.05\space\Omega$. **TODO**: insert a schema - +(ucte-phase-regulation-conversion)= #### Phase regulation If a phase regulation is defined for a transformer, it is converted into a [ratio tap changer](../../grid_model/additional.md#ratio-tap-changer). If the voltage setpoint is defined, the ratio tap changer will regulate the voltage to this setpoint. The regulating terminal is assigned to the first side of the transformer. The ρ of each step is calculated according to the following formula: $\rho = 1 / (1 + i * \delta U / 100)$. +(ucte-angle-regulation-conversion)= #### Angle regulation If an angle regulation is defined for a transformer, it is converted into a [phase tap changer](../../grid_model/additional.md#phase-tap-changer), with a `FIXED_TAP` regulation mode. ρ and α of each step are calculated according to the following formulas: @@ -198,6 +203,7 @@ $$ **Note:** The sign of $\alpha$ is changed because the phase tap changer is on side 2 in UCTE-DEF, and on side 1 in IIDM. +(ucte-area-conversion)= ### Area conversion When the [`ucte.import.create-areas` option](#ucteimportcreate-areas) is set to `true` (default), the importer will create diff --git a/docs/grid_features/import_post_processor.md b/docs/grid_features/import_post_processor.md index ec19a6825cb..d2ad055832f 100644 --- a/docs/grid_features/import_post_processor.md +++ b/docs/grid_features/import_post_processor.md @@ -6,6 +6,7 @@ PowSyBl provides 2 different implementations of post-processors: - [Groovy](#groovy-post-processor): to execute a groovy script - [LoadFlow](#loadflow-post-processor): to run a power flow simulation +(groovy-post-processor)= ## Groovy post-processor This post-processor executes a groovy script, loaded from a file. The script can access to the network and the [computation manager]() using the variables `network` and `computationManager`. To use this post-processor, add the `com.powsybl:powsybl-iidm-scripting` dependency to your classpath, and configure both `import` and `groovy-post-processor` modules: @@ -37,6 +38,7 @@ The following example prints meta-information from the network: println "Network " + network.getId() + " (" + network.getSourceFormat()+ ") is imported" ``` +(loadflow-post-processor)= ## LoadFlow post-processor Mathematically speaking, a [load flow](../simulation/loadflow/index) result is fully defined by the complex voltages at each node. The consequence is that most load flow algorithms converge very fast if they are initialized with voltages. As a result, it happens that load flow results include only voltages and not flows on branches. This post-processors computes the flows given the voltages. The equations (Kirchhoff law) used are the same as the one used in the [load flow validation](../user/itools/loadflow-validation.md#load-flow-results-validation) to compute $P_1^{\text{calc}}$, $Q_1^{\text{calc}}$, $P_2^{\text{calc}}$, $Q_2^{\text{calc}}$ for branches and $P_3^{\text{calc}}$, $Q_3^{\text{calc}}$ in addition for three-winding transformers. @@ -58,6 +60,7 @@ import: **Note:** This post-processor relies on the [load flow results completion]() module. +(geographical-data-import-post-processor)= ## Geographical data import post-processor One way to add geographical positions on a network is to use the import post-processor named odreGeoDataImporter, that will automatically add the [LinePosition](../grid_model/extensions.md#line-position) and [SubstationPosition](../grid_model/extensions.md#substation-position) extensions to the network model. diff --git a/docs/grid_features/loadflow_validation.md b/docs/grid_features/loadflow_validation.md index 3a5cb4989ad..bbf2c78e458 100644 --- a/docs/grid_features/loadflow_validation.md +++ b/docs/grid_features/loadflow_validation.md @@ -4,6 +4,7 @@ A load flow result is considered *acceptable* if it describes a feasible steady- More practically, generations of practitioners have set quasi-standard ways to describe them that makes it possible to define precise rules. They are described below for the different elements of the network. +(loadflow-validation-buses)= ## Buses The first law of Kirchhoff must be satisfied for every bus for active and reactive power: @@ -13,6 +14,7 @@ $$\begin{equation} \left| \sum_{branches} Q + \sum_{injections} Q \right| \leq \epsilon \\ \end{equation}$$ +(loadflow-validation-branches)= ## Branches Lines and two-winding transformers are converted into classical PI models: @@ -40,9 +42,11 @@ Thanks to Kirchhoff laws (see the [line](../grid_model/network_subnetwork.md#lin $(P_1^{calc}, Q_1^{calc}, P_2^{calc}, Q_2^{calc}) = f(\text{Voltages}, \text{Characteristics})$ +(loadflow-validation-three-winding-transformers)= ## Three-winding transformers To be implemented, based on a conversion into 3 two-winding transformers. +(loadflow-validation-generators)= ## Generators ### Active power @@ -89,9 +93,11 @@ V - targetV & < & -& \epsilon && \& && |Q-maxQ| & \leq & \epsilon \\ targetV - V & < && \epsilon && \& && |Q-minQ| & \leq & \epsilon \\ \end{align*} +(loadflow-validation-loads)= ## Loads To be implemented, with tests similar to generators with voltage regulation. +(loadflow-validation-shunts)= ## Shunts A shunt is expected not to generate or absorb active power: @@ -100,6 +106,7 @@ $\left| P \right| < \epsilon$ A shunt is expected to generate reactive power according to the number of activated sections and to the susceptance per section $B$: $\left| Q + \text{#sections} * B V^2 \right| < \epsilon$ +(loadflow-validation-static-var-compensators)= ## Static VAR Compensators Static VAR Compensators behave like generators producing zero active power except that their reactive bounds are expressed in susceptance, so that they are voltage dependent. @@ -111,16 +118,20 @@ $targetP = 0$ MW - If the regulation mode is `VOLTAGE`, it behaves like a generator with voltage regulation with the following bounds (dependent on the voltage, which is not the case for generators): $minQ = - Bmax * V^2$ and $maxQ = - Bmin V^2$ +(loadflow-validation-hvdc)= ## HVDC lines To be done. +(loadflow-validation-vsc)= ## VSC VSC converter stations behave like generators with the additional constraints that the sum of active power on converter stations paired by a cable is equal to the losses on the converter stations plus the losses on the cable. +(loadflow-validation-lcc)= ## LCC To be done. +(loadflow-validation-transformers-ratio-tap-changer)= ## Transformers with a ratio tap changer Transformers with a ratio tap changer have a tap with a finite discrete number of positions that allows to change their transformer ratio. diff --git a/docs/simulation/shortcircuit/parameters.md b/docs/simulation/shortcircuit/parameters.md index 110c9f67af5..6f53f70e9c4 100644 --- a/docs/simulation/shortcircuit/parameters.md +++ b/docs/simulation/shortcircuit/parameters.md @@ -129,7 +129,7 @@ Here is an example of this JSON file: ] ```` - +(shortcircuit-fault-parameters)= ## FaultParameters It is possible to override parameters for each fault by creating an instance of `com.powsybl.shortcircuit.FaultParameters`. This object will take the fault to which it applies and all the parameters diff --git a/docs/user/itools/index.md b/docs/user/itools/index.md index 2d5a2898e05..03f68c50b11 100644 --- a/docs/user/itools/index.md +++ b/docs/user/itools/index.md @@ -42,6 +42,7 @@ Available commands are: `--config-name` Use this option to overload the default base name for the configuration file. It overrides the [powsybl_config_name](#powsybl_config_name) property defined in the `itools.conf` file. +(itools-configuration)= ### Configuration The `iTools` script reads its configuration from the `/etc/itools.conf` [property file](https://en.wikipedia.org/wiki/.properties). The properties defined in this file are used to configure the Java Virtual Machine. @@ -85,6 +86,7 @@ Sometimes, it could be useful for a user to have its own logging configuration t ``` +(itools-available-commands)= ## Available commands The `iTools` script relies on a [plugin mechanism](): the commands are discovered at runtime and depend on the jars present in the `share/java` folder. From ad619aa2296dd95253cd917f98533dcbc7231101 Mon Sep 17 00:00:00 2001 From: rcourtier <129156292+rcourtier@users.noreply.github.com> Date: Mon, 16 Sep 2024 08:28:28 +0200 Subject: [PATCH 53/57] Fix CGM export dependencies issues (#3128) Signed-off-by: Romain Courtier --- .../powsybl/cgmes/conversion/CgmesExport.java | 44 ++++++++---- .../export/CommonGridModelExportTest.java | 72 ++++++++++++++++--- docs/grid_exchange_formats/cgmes/export.md | 6 +- 3 files changed, 94 insertions(+), 28 deletions(-) diff --git a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java index d64e9d0650f..1fa2f960162 100644 --- a/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java +++ b/cgmes/cgmes-conversion/src/main/java/com/powsybl/cgmes/conversion/CgmesExport.java @@ -107,14 +107,16 @@ public void export(Network network, Properties parameters, DataSource dataSource private void exportCGM(Network network, DataSource dataSource, CgmesExportContext context) { checkCgmConsistency(network, context); - // Initialize models for export. The original IGM TP and SSH don't get exported, + // Initialize models for export. The original IGM EQ, SSH, TP and TP_BD don't get exported, // but we need to init their models to retrieve their IDs when building the dependencies. Map igmModels = new HashMap<>(); for (Network subnetwork : network.getSubnetworks()) { IgmModelsForCgm igmModelsForCgm = new IgmModelsForCgm( - initializeModelForExport(subnetwork, CgmesSubset.STEADY_STATE_HYPOTHESIS, context, false, false), initializeModelForExport(subnetwork, CgmesSubset.STEADY_STATE_HYPOTHESIS, context, false, true), - initializeModelForExport(subnetwork, CgmesSubset.TOPOLOGY, context, false, false) + initializeModelForExport(subnetwork, CgmesSubset.EQUIPMENT, context, false, false), + initializeModelForExport(subnetwork, CgmesSubset.STEADY_STATE_HYPOTHESIS, context, false, false), + initializeModelForExport(subnetwork, CgmesSubset.TOPOLOGY, context, false, false), + initializeModelForExport(subnetwork, CgmesSubset.TOPOLOGY_BOUNDARY, context, false, false) ); igmModels.put(subnetwork, igmModelsForCgm); } @@ -122,7 +124,7 @@ private void exportCGM(Network network, DataSource dataSource, CgmesExportContex // Update dependencies if (context.updateDependencies()) { - updateDependenciesCGM(igmModels.values(), updatedCgmSvModel); + updateDependenciesCGM(igmModels.values(), updatedCgmSvModel, context.getBoundaryTpId()); } // Export the SSH for the IGMs and the SV for the CGM @@ -227,22 +229,29 @@ public static CgmesMetadataModel initializeModelForExport( } /** - * Update cross dependencies between the subset models through the dependentOn relationship. - * The IGMs updated SSH supersede the original ones. - * The CGM updated SV depends on the IGMs updated SSH and on the IGMs original TP. - * @param igmModels For each IGM: the original SSH model, the updated SSH model and the original TP model. + * Update cross dependencies between the subset models (including boundaries) through the dependentOn relationship. + * The IGMs updated SSH supersede the original ones and depend on the original EQ. Other dependencies are kept. + * The CGM updated SV depends on the IGMs updated SSH and on the IGMs original TP and TP_BD. + * @param igmModels For each IGM: the updated SSH model and the original SSH, TP and TP_BD models. * @param updatedCgmSvModel The SV model for the CGM. + * @param boundaryTpId The model id for the TP_BD subset. */ - private void updateDependenciesCGM(Collection igmModels, CgmesMetadataModel updatedCgmSvModel) { + private void updateDependenciesCGM(Collection igmModels, CgmesMetadataModel updatedCgmSvModel, String boundaryTpId) { + // Each updated SSH model depends on the original EQ model + igmModels.forEach(m -> m.updatedSsh.addDependentOn(m.originalEq.getId())); + // Each updated SSH model supersedes the original one - // Clear previous dependencies - igmModels.forEach(m -> m.updatedSsh.clearDependencies()); igmModels.forEach(m -> m.updatedSsh.clearSupersedes()); igmModels.forEach(m -> m.updatedSsh.addSupersedes(m.originalSsh.getId())); - // Updated SV model depends on updated SSH models and original TP models + // Updated SV model depends on updated SSH models and original TP and TP_BD models updatedCgmSvModel.addDependentOn(igmModels.stream().map(m -> m.updatedSsh.getId()).collect(Collectors.toSet())); updatedCgmSvModel.addDependentOn(igmModels.stream().map(m -> m.originalTp.getId()).collect(Collectors.toSet())); + if (boundaryTpId != null) { + updatedCgmSvModel.addDependentOn(boundaryTpId); + } else { + updatedCgmSvModel.addDependentOn(igmModels.stream().map(m -> m.originalTpBd.getId()).collect(Collectors.toSet())); + } } /** @@ -492,14 +501,19 @@ private String getBaseName(CgmesExportContext context, DataSource dataSource, Ne * when setting the relationships (dependOn, supersedes) between them in a CGM export. */ private static class IgmModelsForCgm { - CgmesMetadataModel originalSsh; CgmesMetadataModel updatedSsh; + CgmesMetadataModel originalEq; + CgmesMetadataModel originalSsh; CgmesMetadataModel originalTp; + CgmesMetadataModel originalTpBd; - public IgmModelsForCgm(CgmesMetadataModel originalSsh, CgmesMetadataModel updatedSsh, CgmesMetadataModel originalTp) { - this.originalSsh = originalSsh; + public IgmModelsForCgm(CgmesMetadataModel updatedSsh, CgmesMetadataModel originalEq, CgmesMetadataModel originalSsh, + CgmesMetadataModel originalTp, CgmesMetadataModel originalTpBd) { this.updatedSsh = updatedSsh; + this.originalEq = originalEq; + this.originalSsh = originalSsh; this.originalTp = originalTp; + this.originalTpBd = originalTpBd; } } diff --git a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java index 47ae912b2ae..26f2917527d 100644 --- a/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java +++ b/cgmes/cgmes-conversion/src/test/java/com/powsybl/cgmes/conversion/test/export/CommonGridModelExportTest.java @@ -140,14 +140,20 @@ void testCgmExportNoModelsNoProperties() throws IOException { String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_2_1D__FM"; String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; - Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId); + String originalBeTpBdId = "urn:uuid:Network_BE_N_TOPOLOGY_BOUNDARY_2021-02-03T04:30:00Z_1_1D__FM"; + String originalNlTpBdId = "urn:uuid:Network_NL_N_TOPOLOGY_BOUNDARY_2021-02-03T04:30:00Z_1_1D__FM"; + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, originalBeTpBdId, originalNlTpBdId); assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); - // Each updated IGM SSH should supersede the original one + // Each updated IGM SSH should supersede the original one and depend on the original EQ String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + String originalBeEqId = "urn:uuid:Network_BE_N_EQUIPMENT_2021-02-03T04:30:00Z_1_1D__FM"; String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + String originalNlEqId = "urn:uuid:Network_NL_N_EQUIPMENT_2021-02-03T04:30:00Z_1_1D__FM"; assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); + assertEquals(originalBeEqId, getFirstOccurrence(updatedBeSshXml, REGEX_DEPENDENT_ON)); assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + assertEquals(originalNlEqId, getFirstOccurrence(updatedNlSshXml, REGEX_DEPENDENT_ON)); // Profiles should be consistent with the instance files assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE)); @@ -195,14 +201,17 @@ void testCgmExportWithModelsForSubnetworks() throws IOException { String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_2_1D__FM"; String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; - Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId); + String originalTpBdId = "Common TP_BD model ID"; + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, originalTpBdId); assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); - // Each updated IGM SSH should supersede the original one + // Each updated IGM SSH should supersede the original one and depend on the original EQ String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + assertEquals(Set.of("BE EQ model ID"), getOccurrences(updatedBeSshXml, REGEX_DEPENDENT_ON)); + assertEquals(Set.of("NL EQ model ID"), getOccurrences(updatedNlSshXml, REGEX_DEPENDENT_ON)); // Profiles should be consistent with the instance files assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE)); @@ -254,15 +263,19 @@ void testCgmExportWithModelsForAllNetworks() throws IOException { String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_4_1D__FM"; String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2022-03-04T05:30:00Z_1_1D__FM"; String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2022-03-04T05:30:00Z_1_1D__FM"; + String originalTpBdId = "Common TP_BD model ID"; String additionalDependency = "Additional dependency"; - Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, additionalDependency); + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, + originalNlTpId, originalTpBdId, additionalDependency); assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); - // Each updated IGM SSH should supersede the original one + // Each updated IGM SSH should supersede the original one and depend on the original EQ String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM"; String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM"; assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + assertEquals(Set.of("BE EQ model ID"), getOccurrences(updatedBeSshXml, REGEX_DEPENDENT_ON)); + assertEquals(Set.of("NL EQ model ID"), getOccurrences(updatedNlSshXml, REGEX_DEPENDENT_ON)); // Profiles should be consistent with the instance files // The model of the main network brings an additional profile @@ -288,6 +301,7 @@ void testCgmExportWithProperties() throws IOException { exportParams.put(CgmesExport.MODELING_AUTHORITY_SET, "Regional Coordination Center"); exportParams.put(CgmesExport.MODEL_DESCRIPTION, "Common Grid Model export"); exportParams.put(CgmesExport.MODEL_VERSION, "4"); + exportParams.put(CgmesExport.BOUNDARY_TP_ID, "ENTSOE TP_BD model ID"); String basename = "test_bare+properties"; network.write("CGMES", exportParams, tmpDir.resolve(basename)); String updatedBeSshXml = Files.readString(tmpDir.resolve(basename + "_BE_SSH.xml")); @@ -314,14 +328,19 @@ void testCgmExportWithProperties() throws IOException { String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_4_1D__FM"; String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2021-02-03T04:30:00Z_1_1D__FM"; - Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId); + String originalTpBdId = "ENTSOE TP_BD model ID"; + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, originalTpBdId); assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); - // Each updated IGM SSH should supersede the original one + // Each updated IGM SSH should supersede the original one and depend on the original EQ String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + String originalBeEqId = "urn:uuid:Network_BE_N_EQUIPMENT_2021-02-03T04:30:00Z_1_1D__FM"; String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2021-02-03T04:30:00Z_1_1D__FM"; + String originalNlEqId = "urn:uuid:Network_NL_N_EQUIPMENT_2021-02-03T04:30:00Z_1_1D__FM"; assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); + assertEquals(originalBeEqId, getFirstOccurrence(updatedBeSshXml, REGEX_DEPENDENT_ON)); assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + assertEquals(originalNlEqId, getFirstOccurrence(updatedNlSshXml, REGEX_DEPENDENT_ON)); // Profiles should be consistent with the instance files assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE)); @@ -347,6 +366,7 @@ void testCgmExportWithModelsAndProperties() throws IOException { exportParams.put(CgmesExport.MODELING_AUTHORITY_SET, "Regional Coordination Center"); exportParams.put(CgmesExport.MODEL_DESCRIPTION, "Common Grid Model export"); exportParams.put(CgmesExport.MODEL_VERSION, "4"); + exportParams.put(CgmesExport.BOUNDARY_TP_ID, "ENTSOE TP_BD model ID"); String basename = "test_bare+models+properties"; network.write("CGMES", exportParams, tmpDir.resolve(basename)); String updatedBeSshXml = Files.readString(tmpDir.resolve(basename + "_BE_SSH.xml")); @@ -375,15 +395,19 @@ void testCgmExportWithModelsAndProperties() throws IOException { String updatedNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_4_1D__FM"; String originalBeTpId = "urn:uuid:Network_BE_N_TOPOLOGY_2022-03-04T05:30:00Z_1_1D__FM"; String originalNlTpId = "urn:uuid:Network_NL_N_TOPOLOGY_2022-03-04T05:30:00Z_1_1D__FM"; + String originalTpBdId = "ENTSOE TP_BD model ID"; // the parameter prevails on the extension String additionalDependency = "Additional dependency"; - Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, originalNlTpId, additionalDependency); + Set expectedDependencies = Set.of(updatedBeSshId, updatedNlSshId, originalBeTpId, + originalNlTpId, originalTpBdId, additionalDependency); assertEquals(expectedDependencies, getOccurrences(updatedCgmSvXml, REGEX_DEPENDENT_ON)); - // Each updated IGM SSH should supersede the original one + // Each updated IGM SSH should supersede the original one and depend on the original EQ String originalBeSshId = "urn:uuid:Network_BE_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM"; String originalNlSshId = "urn:uuid:Network_NL_N_STEADY_STATE_HYPOTHESIS_2022-03-04T05:30:00Z_1_1D__FM"; assertEquals(originalBeSshId, getFirstOccurrence(updatedBeSshXml, REGEX_SUPERSEDES)); assertEquals(originalNlSshId, getFirstOccurrence(updatedNlSshXml, REGEX_SUPERSEDES)); + assertEquals(Set.of("BE EQ model ID"), getOccurrences(updatedBeSshXml, REGEX_DEPENDENT_ON)); + assertEquals(Set.of("NL EQ model ID"), getOccurrences(updatedNlSshXml, REGEX_DEPENDENT_ON)); // Profiles should be consistent with the instance files, CGM SV has an additional profile assertEquals("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1", getFirstOccurrence(updatedBeSshXml, REGEX_PROFILE)); @@ -647,6 +671,20 @@ private void addModelsForSubnetworks(Network network, int version) { .addSupersedes("BE SSH previous ID") .addProfile("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1") .add() + .newModel() + .setId("BE EQ model ID") + .setSubset(CgmesSubset.EQUIPMENT) + .setVersion(1) + .setModelingAuthoritySet("http://elia.be/CGMES/2.4.15") + .addProfile("http://entsoe.eu/CIM/EquipmentCore/3/1") + .add() + .newModel() + .setId("Common TP_BD model ID") + .setSubset(CgmesSubset.TOPOLOGY_BOUNDARY) + .setVersion(1) + .setModelingAuthoritySet("http://www.entsoe.eu/OperationalPlanning") + .addProfile("http://entsoe.eu/CIM/TopologyBoundary/3/1") + .add() .add(); network.getSubnetwork("Network_NL") .newExtension(CgmesMetadataModelsAdder.class) @@ -659,6 +697,20 @@ private void addModelsForSubnetworks(Network network, int version) { .addSupersedes("NL SSH previous ID") .addProfile("http://entsoe.eu/CIM/SteadyStateHypothesis/1/1") .add() + .newModel() + .setId("NL EQ model ID") + .setSubset(CgmesSubset.EQUIPMENT) + .setVersion(1) + .setModelingAuthoritySet("http://tennet.nl/CGMES/2.4.15") + .addProfile("http://entsoe.eu/CIM/EquipmentCore/3/1") + .add() + .newModel() + .setId("Common TP_BD model ID") + .setSubset(CgmesSubset.TOPOLOGY_BOUNDARY) + .setVersion(1) + .setModelingAuthoritySet("http://www.entsoe.eu/OperationalPlanning") + .addProfile("http://entsoe.eu/CIM/TopologyBoundary/3/1") + .add() .add(); } diff --git a/docs/grid_exchange_formats/cgmes/export.md b/docs/grid_exchange_formats/cgmes/export.md index 5c30e44e3a5..b8121191c1f 100644 --- a/docs/grid_exchange_formats/cgmes/export.md +++ b/docs/grid_exchange_formats/cgmes/export.md @@ -39,8 +39,8 @@ If a version number is given as a parameter, it is used for the exported files. The quick CGM export will always write updated SSH files for IGMs and a single SV for the CGM. The parameter for selecting which profiles to export is ignored in this kind of export. If the dependencies have to be updated automatically (see parameter **iidm.export.cgmes.update-dependencies** below), the exported instance files will contain metadata models where: -* Updated SSH for IGMs supersede the original ones. -* Updated SV for the CGM depends on the updated SSH from IGMs and on the original TP from IGMs. +* Updated SSH for IGMs supersede the original ones, and depend on the original EQ from IGMs. +* Updated SV for the CGM depends on the updated SSH from IGMs and on the original TP and TP_BD from IGMs. The filenames of the exported instance files will follow the pattern: * For the CGM SV: `_SV.xml`. @@ -71,7 +71,7 @@ exampleBase_NL_SSH.xml exampleBase_SV.xml ``` -where the updated SSH files will supersede the original ones, and the SV will contain the correct dependencies of new SSH and original TPs. +where the updated SSH files will supersede the original ones and depend on the original EQs, and the SV will contain the correct dependencies of new SSH and original TPs and TP_BD. (cgmes-cgm-manual-export)= ## CGM (Common Grid Model) manual export From 1a935cb0769b30df703cb1810449894fa3cd7b42 Mon Sep 17 00:00:00 2001 From: jeandemanged Date: Tue, 17 Sep 2024 07:55:40 +0200 Subject: [PATCH 54/57] Fix CGMES importer documentation of parameter cgm-with-subnetworks-defined-by (#3141) Signed-off-by: Damien Jeandemange --- docs/grid_exchange_formats/cgmes/import.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/grid_exchange_formats/cgmes/import.md b/docs/grid_exchange_formats/cgmes/import.md index a9ca9aef94e..1a1544448b9 100644 --- a/docs/grid_exchange_formats/cgmes/import.md +++ b/docs/grid_exchange_formats/cgmes/import.md @@ -548,5 +548,6 @@ Optional property used when in operational limits, temporary limits are present **iidm.import.cgmes.cgm-with-subnetworks** Optional property to define if subnetworks must be added to the network when importing a Common Grid Model (CGM). Each subnetwork will model an Individual Grid Model (IGM). By default `true`: subnetworks are added, and the merging is done at IIDM level, with a main IIDM network representing the CGM and containing a set of subnetworks, one for each IGM. If the value is set to `false` all the CGMES data will be flattened in a single network and information about the ownership of each equipment will be lost. -**iidm.import.cgmes.cgm-with-subnetworks-defined** +**iidm.import.cgmes.cgm-with-subnetworks-defined-by** If `iidm.import.cgmes.cgm-with-subnetworks` is set to `true`, use this property to specify how the set of input files should be split by IGM: based on their filenames (use the value `FILENAME`) or by its modeling authority, read from the header (use the value `MODELING_AUTHORITY`). +Its default value is `MODELING_AUTHORITY`. From b0ce0f5e0f080d155099b6499b1ed13f107f36fc Mon Sep 17 00:00:00 2001 From: Geoffroy Jamgotchian Date: Wed, 18 Sep 2024 10:28:16 +0200 Subject: [PATCH 55/57] Fix Signed-off-by: Geoffroy Jamgotchian --- .../src/main/java/com/powsybl/iidm/network/Importer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java index 557dfa601e4..0a5ef1fbaa9 100644 --- a/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java +++ b/iidm/iidm-api/src/main/java/com/powsybl/iidm/network/Importer.java @@ -60,6 +60,11 @@ public String getFormat() { return importer.getFormat(); } + @Override + public List getSupportedExtensions() { + return importer.getSupportedExtensions(); + } + @Override public List getParameters() { return importer.getParameters(); From 6c84801fa5a607b94a9f34d528ee4049b704452d Mon Sep 17 00:00:00 2001 From: Geoffroy Jamgotchian Date: Wed, 18 Sep 2024 11:58:05 +0200 Subject: [PATCH 56/57] Improve test Signed-off-by: Geoffroy Jamgotchian --- pom.xml | 5 +++++ powerfactory/powerfactory-converter/pom.xml | 5 +++++ .../powerfactory/converter/PowerFactoryImporterTest.java | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 69e086aa000..b868dd2b4c5 100644 --- a/pom.xml +++ b/pom.xml @@ -539,6 +539,11 @@ powsybl-powerfactory-dgs ${project.version} + + ${project.groupId} + powsybl-powerfactory-db + ${project.version} + ${project.groupId} powsybl-powerfactory-converter diff --git a/powerfactory/powerfactory-converter/pom.xml b/powerfactory/powerfactory-converter/pom.xml index 00ae1759be9..5d0963a4ae4 100644 --- a/powerfactory/powerfactory-converter/pom.xml +++ b/powerfactory/powerfactory-converter/pom.xml @@ -87,5 +87,10 @@ powsybl-powerfactory-dgs test + + ${project.groupId} + powsybl-powerfactory-db + test + diff --git a/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java b/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java index 6c2cf66ac18..293d785b2c2 100644 --- a/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java +++ b/powerfactory/powerfactory-converter/src/test/java/com/powsybl/powerfactory/converter/PowerFactoryImporterTest.java @@ -45,7 +45,7 @@ void testBase() { assertEquals("POWER-FACTORY", importer.getFormat()); assertTrue(importer.getParameters().isEmpty()); assertEquals("PowerFactory to IIDM converter", importer.getComment()); - assertEquals(List.of("json", "dgs"), importer.getSupportedExtensions()); + assertEquals(List.of("json", "dgs", "properties"), importer.getSupportedExtensions()); } @Test From 4e8ca9ede0566b33cd286c730cca6b9b9ce2f0a0 Mon Sep 17 00:00:00 2001 From: Geoffroy Jamgotchian Date: Wed, 18 Sep 2024 12:23:18 +0200 Subject: [PATCH 57/57] Fix Signed-off-by: Geoffroy Jamgotchian --- .../test/java/com/powsybl/iidm/serde/BinaryImporterTest.java | 2 +- .../src/test/java/com/powsybl/iidm/serde/JsonImporterTest.java | 2 +- .../src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/BinaryImporterTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/BinaryImporterTest.java index d8ad2cac407..cc746f18b0e 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/BinaryImporterTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/BinaryImporterTest.java @@ -24,7 +24,7 @@ void testMetaInfos() { var importer = new BinaryImporter(); assertEquals("BIIDM", importer.getFormat()); assertEquals("IIDM binary v " + CURRENT_IIDM_VERSION.toString(".") + " importer", importer.getComment()); - assertEquals(List.of("biidm", "bin", "iidm.bin"), importer.getSupportedExtensions()); + assertEquals(List.of("biidm", "bin"), importer.getSupportedExtensions()); assertEquals(5, importer.getParameters().size()); } } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/JsonImporterTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/JsonImporterTest.java index 9888d723d7e..c5badb4e12c 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/JsonImporterTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/JsonImporterTest.java @@ -24,7 +24,7 @@ void testMetaInfos() { var importer = new JsonImporter(); assertEquals("JIIDM", importer.getFormat()); assertEquals("IIDM JSON v " + CURRENT_IIDM_VERSION.toString(".") + " importer", importer.getComment()); - assertEquals(List.of("jiidm", "json", "iidm.json"), importer.getSupportedExtensions()); + assertEquals(List.of("jiidm", "json"), importer.getSupportedExtensions()); assertEquals(5, importer.getParameters().size()); } } diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java index 1962f41b130..08ff31a9b06 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/XMLImporterTest.java @@ -136,7 +136,7 @@ void backwardCompatibilityTest() throws IOException { void testMetaInfos() { assertEquals("XIIDM", importer.getFormat()); assertEquals("IIDM XML v " + CURRENT_IIDM_VERSION.toString(".") + " importer", importer.getComment()); - assertEquals(List.of("xiidm", "iidm", "xml", "iidm.xml"), importer.getSupportedExtensions()); + assertEquals(List.of("xiidm", "iidm", "xml"), importer.getSupportedExtensions()); assertEquals(5, importer.getParameters().size()); assertEquals("iidm.import.xml.throw-exception-if-extension-not-found", importer.getParameters().get(0).getName()); assertEquals(Arrays.asList("iidm.import.xml.throw-exception-if-extension-not-found", "throwExceptionIfExtensionNotFound"), importer.getParameters().get(0).getNames());