From 05313538c29624a4120431366ce077e3cad5786d Mon Sep 17 00:00:00 2001 From: Lisrte Date: Wed, 3 Jul 2024 15:28:38 +0200 Subject: [PATCH] Add dynawo dynamic security analysis (#277) * Refactor Macro connections creation: add MacroConnectionsAdder, MultipleJobsXml, ContingenciesDydXml && ContingenciesParXml * Refactor providers * Add DynaWaltzSecurityAnalysisProvider and Handler * Use DynamicSecurityAnalysisParameters * Fix contingencies Dyd parameter file name * Create dynamic security analysis module * Refactor provider/handler/config * Add integration test * Rename dynawaltz to dynawo for DSA * Update readthedocs Signed-off-by: lisrte --- ...oConfig.java => AbstractDynawoConfig.java} | 14 +- .../dynawo/commons/DynawoConstants.java | 4 +- .../powsybl/dynawo/commons/DynawoUtil.java | 4 + distribution/pom.xml | 4 + .../configuration.md | 30 +++ docs/dynamic_security_analysis/index.md | 18 ++ ...nawo-configuration.md => configuration.md} | 0 docs/dynamic_simulation/index.md | 2 +- docs/index.md | 10 +- ...flow-configuration.md => configuration.md} | 0 docs/load_flow/index.md | 2 +- .../dynaflow/ContingencyResultsUtils.java | 81 ++++++ .../com/powsybl/dynaflow/DynaFlowConfig.java | 10 +- .../powsybl/dynaflow/DynaFlowConstants.java | 1 - .../com/powsybl/dynaflow/DynaFlowHandler.java | 108 ++++++++ .../powsybl/dynaflow/DynaFlowProvider.java | 100 +------ .../dynaflow/DynaFlowSecurityAnalysis.java | 211 --------------- .../DynaFlowSecurityAnalysisHandler.java | 120 +++++++++ .../DynaFlowSecurityAnalysisProvider.java | 32 ++- .../com/powsybl/dynaflow/DynaflowReports.java | 4 +- .../dynaflow/SecurityAnalysisConstants.java | 22 ++ .../dynaflow/DynaFlowParametersTest.java | 2 + .../dynaflow/DynaFlowProviderTest.java | 2 + .../powsybl/dynawaltz/DynaWaltzConfig.java | 14 +- .../powsybl/dynawaltz/DynaWaltzConstants.java | 20 ++ .../powsybl/dynawaltz/DynaWaltzContext.java | 4 +- .../powsybl/dynawaltz/DynaWaltzHandler.java | 220 ++++++++++++++++ .../dynawaltz/DynaWaltzParameters.java | 8 + .../powsybl/dynawaltz/DynaWaltzProvider.java | 249 ++---------------- .../models/AbstractBlackBoxModel.java | 4 +- .../AbstractEquipmentBlackBoxModel.java | 7 +- .../AbstractPureDynamicBlackBoxModel.java | 7 +- .../dynawaltz/models/BlackBoxModel.java | 2 + .../TapChangerAutomationSystem.java | 5 +- .../TapChangerBlockingAutomationSystem.java | 5 +- .../dynawaltz/models/buses/StandardBus.java | 3 +- .../models/macroconnections/MacroConnect.java | 8 + .../models/utils/BlackBoxSupplierUtils.java | 40 +++ .../dynawaltz/xml/DynaWaltzConstants.java | 4 + .../powsybl/dynawaltz/xml/ParametersXml.java | 2 +- .../AbstractLocalCommandExecutor.java | 2 +- .../com/powsybl/config/test/config.yml | 7 + dynawo-integration-tests/pom.xml | 4 + .../dynawo/it/DynawoSecurityAnalysisTest.java | 113 ++++++++ .../dynamic-security-analysis/results.json | 51 ++++ .../timeline_report.txt | 52 ++++ dynawo-security-analysis/pom.xml | 82 ++++++ .../security/ContingencyEventModels.java | 29 ++ .../DynamicSecurityAnalysisReports.java | 26 ++ .../security/DynawoAlgorithmsConfig.java | 43 +++ .../DynawoSecurityAnalysisHandler.java | 115 ++++++++ .../DynawoSecurityAnalysisProvider.java | 124 +++++++++ .../security/SecurityAnalysisContext.java | 87 ++++++ .../security/xml/ContingenciesDydXml.java | 54 ++++ .../security/xml/ContingenciesParXml.java | 37 +++ .../dynawo/security/xml/MultipleJobsXml.java | 57 ++++ .../powsybl/dynawo/security/xml/XmlUtil.java | 90 +++++++ .../security/DynawoAlgorithmsConfigTest.java | 54 ++++ .../security/xml/ContingenciesXmlTest.java | 50 ++++ .../security/xml/MultiplesJobsXmlTest.java | 44 ++++ .../resources/DisconnectLineGenerator.xml | 13 + .../resources/DisconnectLineGenerator_par.xml | 12 + .../src/test/resources/LOAD.xml | 8 + .../src/test/resources/LOAD_par.xml | 7 + .../src/test/resources/multipleJobs.xml | 8 + .../src/test/resources/multipleJobs.xsd | 56 ++++ pom.xml | 6 + 67 files changed, 2036 insertions(+), 578 deletions(-) rename commons/src/main/java/com/powsybl/dynawo/commons/{DynawoConfig.java => AbstractDynawoConfig.java} (77%) create mode 100644 docs/dynamic_security_analysis/configuration.md create mode 100644 docs/dynamic_security_analysis/index.md rename docs/dynamic_simulation/{dynawo-configuration.md => configuration.md} (100%) rename docs/load_flow/{dynaflow-configuration.md => configuration.md} (100%) create mode 100644 dynaflow/src/main/java/com/powsybl/dynaflow/ContingencyResultsUtils.java create mode 100644 dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowHandler.java delete mode 100644 dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysis.java create mode 100644 dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisHandler.java create mode 100644 dynaflow/src/main/java/com/powsybl/dynaflow/SecurityAnalysisConstants.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzConstants.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzHandler.java create mode 100644 dynawaltz/src/main/java/com/powsybl/dynawaltz/models/utils/BlackBoxSupplierUtils.java create mode 100644 dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSecurityAnalysisTest.java create mode 100644 dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/results.json create mode 100644 dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/timeline_report.txt create mode 100644 dynawo-security-analysis/pom.xml create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/ContingencyEventModels.java create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynamicSecurityAnalysisReports.java create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoAlgorithmsConfig.java create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoSecurityAnalysisHandler.java create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoSecurityAnalysisProvider.java create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/SecurityAnalysisContext.java create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/ContingenciesDydXml.java create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/ContingenciesParXml.java create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/MultipleJobsXml.java create mode 100644 dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/XmlUtil.java create mode 100644 dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/DynawoAlgorithmsConfigTest.java create mode 100644 dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/xml/ContingenciesXmlTest.java create mode 100644 dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/xml/MultiplesJobsXmlTest.java create mode 100644 dynawo-security-analysis/src/test/resources/DisconnectLineGenerator.xml create mode 100644 dynawo-security-analysis/src/test/resources/DisconnectLineGenerator_par.xml create mode 100644 dynawo-security-analysis/src/test/resources/LOAD.xml create mode 100644 dynawo-security-analysis/src/test/resources/LOAD_par.xml create mode 100644 dynawo-security-analysis/src/test/resources/multipleJobs.xml create mode 100644 dynawo-security-analysis/src/test/resources/multipleJobs.xsd diff --git a/commons/src/main/java/com/powsybl/dynawo/commons/DynawoConfig.java b/commons/src/main/java/com/powsybl/dynawo/commons/AbstractDynawoConfig.java similarity index 77% rename from commons/src/main/java/com/powsybl/dynawo/commons/DynawoConfig.java rename to commons/src/main/java/com/powsybl/dynawo/commons/AbstractDynawoConfig.java index 02c1d03c1..4f3f373c8 100644 --- a/commons/src/main/java/com/powsybl/dynawo/commons/DynawoConfig.java +++ b/commons/src/main/java/com/powsybl/dynawo/commons/AbstractDynawoConfig.java @@ -19,15 +19,15 @@ /** * @author Florian Dupuy {@literal } */ -public class DynawoConfig { +public abstract class AbstractDynawoConfig { private static final boolean DEBUG_DEFAULT = false; - protected static T load(Function configFactory, String moduleName) { + protected static T load(Function configFactory, String moduleName) { return load(configFactory, moduleName, PlatformConfig.defaultConfig()); } - protected static T load(Function configFactory, String moduleName, PlatformConfig platformConfig) { + protected static T load(Function configFactory, String moduleName, PlatformConfig platformConfig) { return platformConfig.getOptionalModuleConfig(moduleName) .map(configFactory) .orElseThrow(() -> new PowsyblException("PlatformConfig incomplete: Module " + moduleName + " not found")); @@ -36,12 +36,12 @@ protected static T load(Function confi private final Path homeDir; private final boolean debug; - public DynawoConfig(Path homeDir, boolean debug) { + protected AbstractDynawoConfig(Path homeDir, boolean debug) { this.homeDir = Objects.requireNonNull(homeDir); this.debug = debug; } - public DynawoConfig(ModuleConfig config) { + protected AbstractDynawoConfig(ModuleConfig config) { this(config.getPathProperty("homeDir"), config.getBooleanProperty("debug", DEBUG_DEFAULT)); } @@ -53,9 +53,7 @@ public boolean isDebug() { return debug; } - public String getProgram() { - return getProgram(DynawoConstants.DYNAWO_CMD_NAME); - } + public abstract String getProgram(); public String getProgram(String programName) { String extension = SystemUtils.IS_OS_WINDOWS ? ".cmd" : ".sh"; diff --git a/commons/src/main/java/com/powsybl/dynawo/commons/DynawoConstants.java b/commons/src/main/java/com/powsybl/dynawo/commons/DynawoConstants.java index 83d077995..a8061a831 100644 --- a/commons/src/main/java/com/powsybl/dynawo/commons/DynawoConstants.java +++ b/commons/src/main/java/com/powsybl/dynawo/commons/DynawoConstants.java @@ -24,8 +24,6 @@ private DynawoConstants() { */ public static final String IIDM_VERSION = IidmVersion.V_1_4.toString("."); - public static final String DYNAWO_CMD_NAME = "dynawo"; - public static final String DYNAWO_TIMELINE_FOLDER = "timeLine"; public static final DynawoVersion VERSION_MIN = new DynawoVersion(1, 5, 0); @@ -37,4 +35,6 @@ private DynawoConstants() { "hvdcAngleDroopActivePowerControl", "hvdcOperatorActivePowerRange", "standbyAutomaton"); + + public static final String OUTPUT_IIDM_FILENAME = "outputIIDM.xml"; } diff --git a/commons/src/main/java/com/powsybl/dynawo/commons/DynawoUtil.java b/commons/src/main/java/com/powsybl/dynawo/commons/DynawoUtil.java index 1fd602023..ee5e529cf 100644 --- a/commons/src/main/java/com/powsybl/dynawo/commons/DynawoUtil.java +++ b/commons/src/main/java/com/powsybl/dynawo/commons/DynawoUtil.java @@ -31,6 +31,10 @@ public final class DynawoUtil { private DynawoUtil() { } + public static List getCommandExecutions(Command command) { + return Collections.singletonList(new CommandExecution(command, 1, 0)); + } + public static void writeIidm(Network network, Path file) { Objects.requireNonNull(network); Objects.requireNonNull(file); diff --git a/distribution/pom.xml b/distribution/pom.xml index 9982132b7..79ab02933 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -41,6 +41,10 @@ ${project.groupId} powsybl-dynawaltz-dsl + + ${project.groupId} + powsybl-dynawo-security-analysis + ${project.groupId} powsybl-dynawo-integration-tests diff --git a/docs/dynamic_security_analysis/configuration.md b/docs/dynamic_security_analysis/configuration.md new file mode 100644 index 000000000..36a1b937e --- /dev/null +++ b/docs/dynamic_security_analysis/configuration.md @@ -0,0 +1,30 @@ +# Configuration + +## Dynawo-algorithms properties +The `dynawo-algorithms` module defines the required parameters to run with Dynawo. + +**homeDir** +Use the `homeDir` property to defines the installation directory of the dynawo simulator. + +**debug** +Use the `debug` property to specify if the temporary folder where the inputs are generated should be kept after the simulation. + +### Examples + +**YAML configuration:** +```yaml +dynawo-algorithms: + homeDir: /home/user/dynawo-algorithms + debug: false +``` + +**XML configuration:** +```xml + + /home/user/dynawo + false + +``` + +## Default parameters +The dynamic security analysis reuse the `dynawo-default-parameters` [module](../dynamic_simulation/configuration.md#default-parameters). diff --git a/docs/dynamic_security_analysis/index.md b/docs/dynamic_security_analysis/index.md new file mode 100644 index 000000000..3db8e4e84 --- /dev/null +++ b/docs/dynamic_security_analysis/index.md @@ -0,0 +1,18 @@ +# Dynamic security analysis + +```{toctree} +:hidden: +configuration.md +``` + +PowSyBl provides an implementation of the [DynamicSecurityAnalysis API from powsybl-core](inv:powsyblcore:*:*#simulation/dynamic_security/index) with [Dynaωo-algorithms](https://dynawo.github.io/about/dynalgo), a wrapper around [Dynaωo](https://dynawo.github.io) providing utility algorithms to calculate complex key values of a power system. + +## Installation + +Read this [documentation page](https://dynawo.github.io/install/dynalgo) to learn how to install and configure Dynaωo-algorithms. + +## Going further + +You may find an extensive documentation of the Dynawo project [here](https://github.com/dynawo/dynawo/releases/latest/download/DynawoDocumentation.pdf). +The dynamic security analysis use the same dynamic models supplier as the dynamic simulation implementation, documentation can be found [here](../dynamic_simulation/dynamic-models-dsl.md). + diff --git a/docs/dynamic_simulation/dynawo-configuration.md b/docs/dynamic_simulation/configuration.md similarity index 100% rename from docs/dynamic_simulation/dynawo-configuration.md rename to docs/dynamic_simulation/configuration.md diff --git a/docs/dynamic_simulation/index.md b/docs/dynamic_simulation/index.md index 67bc6ac78..b96ba4a78 100644 --- a/docs/dynamic_simulation/index.md +++ b/docs/dynamic_simulation/index.md @@ -2,7 +2,7 @@ ```{toctree} :hidden: -dynawo-configuration.md +configuration.md dynamic-models-dsl.md event-models-dsl.md curves-dsl.md diff --git a/docs/index.md b/docs/index.md index bdf6e07cb..a5525d149 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,11 +1,13 @@ # Powsybl-dynawo documentation -Powsybl-dynawo is an interface between PowSyBl and [Dynaωo](https://dynawo.github.io) open source suites, providing: -- an implementation of [LoadFlow API from powsybl-core](inv:powsyblcore:*:*#simulation/loadflow/index), -- an implementation of [SecurityAnalysis API from powsybl-core](inv:powsyblcore:*:*#simulation/security/index), -- an implementation of [DynamicSimulation API from powsybl-core](inv:powsyblcore:*:*#simulation/dynamic/index). +Powsybl-dynawo is an interface between PowSyBl and [Dynaωo](https://dynawo.github.io) open source suites, providing implementations of: +- [LoadFlow API from powsybl-core](inv:powsyblcore:*:*#simulation/loadflow/index), +- [SecurityAnalysis API from powsybl-core](inv:powsyblcore:*:*#simulation/security/index), +- [DynamicSimulation API from powsybl-core](inv:powsyblcore:*:*#simulation/dynamic/index), +- [DynamicSecurityAnalysis API from powsybl-core](inv:powsyblcore:*:*#simulation/dynamic_security/index). ```{toctree} :hidden: load_flow/index.md dynamic_simulation/index.md +dynamic_security_analysis/index.md ``` diff --git a/docs/load_flow/dynaflow-configuration.md b/docs/load_flow/configuration.md similarity index 100% rename from docs/load_flow/dynaflow-configuration.md rename to docs/load_flow/configuration.md diff --git a/docs/load_flow/index.md b/docs/load_flow/index.md index 21df76d21..d949adfef 100644 --- a/docs/load_flow/index.md +++ b/docs/load_flow/index.md @@ -2,7 +2,7 @@ ```{toctree} :hidden: -dynaflow-configuration.md +configuration.md ``` PowSyBl provides an implementation of the [LoadFlow API from powsybl-core](inv:powsyblcore:*:*#simulation/loadflow/index) with [DynaFlow](https://dynawo.github.io/about/dynaflow). diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/ContingencyResultsUtils.java b/dynaflow/src/main/java/com/powsybl/dynaflow/ContingencyResultsUtils.java new file mode 100644 index 000000000..776366d0a --- /dev/null +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/ContingencyResultsUtils.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2023, 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.dynaflow; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.contingency.Contingency; +import com.powsybl.dynaflow.xml.ConstraintsReader; +import com.powsybl.dynawo.commons.CommonReports; +import com.powsybl.dynawo.commons.timeline.TimelineEntry; +import com.powsybl.dynawo.commons.timeline.XmlTimeLineParser; +import com.powsybl.iidm.network.Network; +import com.powsybl.loadflow.LoadFlowResult; +import com.powsybl.security.*; +import com.powsybl.security.results.NetworkResult; +import com.powsybl.security.results.PostContingencyResult; +import com.powsybl.security.results.PreContingencyResult; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Laurent Issertial + */ +public final class ContingencyResultsUtils { + + private ContingencyResultsUtils() { + } + + /** + * Build the pre-contingency results from the input network + */ + public static PreContingencyResult getPreContingencyResult(Network network, LimitViolationFilter violationFilter) { + List limitViolations = Security.checkLimits(network); + List filteredViolations = violationFilter.apply(limitViolations, network); + NetworkResult networkResult = new NetworkResult(Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + return new PreContingencyResult(LoadFlowResult.ComponentResult.Status.CONVERGED, new LimitViolationsResult(filteredViolations), networkResult); + } + + /** + * Build the post-contingency results from the constraints files written by dynawo + */ + public static List getPostContingencyResults(Network network, LimitViolationFilter violationFilter, + Path constraintsDir, List contingencies) { + return contingencies.stream() + .map(c -> getPostContingencyResult(network, violationFilter, constraintsDir, c)) + .collect(Collectors.toList()); + } + + private static PostContingencyResult getPostContingencyResult(Network network, LimitViolationFilter violationFilter, + Path constraintsDir, Contingency contingency) { + Path constraintsFile = constraintsDir.resolve("constraints_" + contingency.getId() + ".xml"); + if (Files.exists(constraintsFile)) { + List limitViolationsRead = ConstraintsReader.read(network, constraintsFile); + List limitViolationsFiltered = violationFilter.apply(limitViolationsRead, network); + return new PostContingencyResult(contingency, PostContingencyComputationStatus.CONVERGED, new LimitViolationsResult(limitViolationsFiltered)); + } else { + return new PostContingencyResult(contingency, PostContingencyComputationStatus.FAILED, Collections.emptyList()); + } + } + + // Report the timeline events from the timeline files written by dynawo + public static void reportContingenciesTimelines(List contingencies, Path timelineDir, ReportNode reportNode) { + contingencies.forEach(c -> { + ReportNode contingencyReporter = DynaflowReports.createContingenciesTimelineReportNode(reportNode, c.getId()); + getTimeline(timelineDir, c).forEach(e -> CommonReports.reportTimelineEntry(contingencyReporter, e)); + }); + } + + private static List getTimeline(Path timelineDir, Contingency c) { + Path timelineFile = timelineDir.resolve("timeline_" + c.getId() + ".xml"); + return new XmlTimeLineParser().parse(timelineFile); + } +} diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowConfig.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowConfig.java index 1b3a9ad85..20148076b 100644 --- a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowConfig.java +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowConfig.java @@ -9,7 +9,7 @@ import com.google.common.collect.ImmutableMap; import com.powsybl.commons.config.ModuleConfig; import com.powsybl.commons.config.PlatformConfig; -import com.powsybl.dynawo.commons.DynawoConfig; +import com.powsybl.dynawo.commons.AbstractDynawoConfig; import java.nio.file.Path; import java.util.Map; @@ -18,8 +18,9 @@ * * @author Guillaume Pernin {@literal } */ -public class DynaFlowConfig extends DynawoConfig { +public class DynaFlowConfig extends AbstractDynawoConfig { + public static final String DYNAFLOW_LAUNCHER_PROGRAM_NAME = "dynaflow-launcher"; private static final String DYNAFLOW_MODULE_NAME = "dynaflow"; public static DynaFlowConfig load() { @@ -42,4 +43,9 @@ public Map createEnv() { return ImmutableMap.builder() .build(); } + + @Override + public String getProgram() { + return getProgram(DYNAFLOW_LAUNCHER_PROGRAM_NAME); + } } diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowConstants.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowConstants.java index 79f7287a6..cc3e5dd17 100644 --- a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowConstants.java +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowConstants.java @@ -14,7 +14,6 @@ */ public final class DynaFlowConstants { - public static final String DYNAFLOW_LAUNCHER_PROGRAM_NAME = "dynaflow-launcher"; public static final String DYNAFLOW_NAME = "DynaFlow"; public static final String CONFIG_FILENAME = "config.json"; public static final String IIDM_FILENAME = "network.xiidm"; diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowHandler.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowHandler.java new file mode 100644 index 000000000..0b937561c --- /dev/null +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowHandler.java @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2023, 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.dynaflow; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.AbstractExecutionHandler; +import com.powsybl.computation.Command; +import com.powsybl.computation.CommandExecution; +import com.powsybl.computation.ExecutionReport; +import com.powsybl.dynaflow.json.DynaFlowConfigSerializer; +import com.powsybl.dynawo.commons.CommonReports; +import com.powsybl.dynawo.commons.DynawoUtil; +import com.powsybl.dynawo.commons.NetworkResultsUpdater; +import com.powsybl.dynawo.commons.loadmerge.LoadsMerger; +import com.powsybl.dynawo.commons.timeline.TimelineEntry; +import com.powsybl.dynawo.commons.timeline.XmlTimeLineParser; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.serde.NetworkSerDe; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.loadflow.LoadFlowResult; +import com.powsybl.loadflow.LoadFlowResultImpl; +import com.powsybl.loadflow.json.LoadFlowResultDeserializer; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.powsybl.dynaflow.DynaFlowConstants.*; +import static com.powsybl.dynawo.commons.DynawoConstants.DYNAWO_TIMELINE_FOLDER; +import static com.powsybl.dynawo.commons.DynawoUtil.getCommandExecutions; + +/** + * @author Laurent Issertial + */ +public class DynaFlowHandler extends AbstractExecutionHandler { + private final Network network; + private final Network dynawoInput; + private final String workingStateId; + private final DynaFlowParameters dynaFlowParameters; + private final LoadFlowParameters loadFlowParameters; + private final Command command; + private final ReportNode reportNode; + + public DynaFlowHandler(Network network, String workingStateId, DynaFlowParameters dynaFlowParameters, LoadFlowParameters loadFlowParameters, Command command, ReportNode reportNode) { + this.network = network; + this.workingStateId = workingStateId; + this.dynaFlowParameters = dynaFlowParameters; + this.loadFlowParameters = loadFlowParameters; + this.command = command; + this.dynawoInput = this.dynaFlowParameters.isMergeLoads() ? LoadsMerger.mergeLoads(this.network) : this.network; + this.reportNode = reportNode; + } + + @Override + public List before(Path workingDir) throws IOException { + network.getVariantManager().setWorkingVariant(workingStateId); + DynawoUtil.writeIidm(dynawoInput, workingDir.resolve(IIDM_FILENAME)); + DynaFlowConfigSerializer.serialize(loadFlowParameters, dynaFlowParameters, Path.of("."), workingDir.resolve(CONFIG_FILENAME)); + return getCommandExecutions(command); + } + + @Override + public LoadFlowResult after(Path workingDir, ExecutionReport report) { + reportTimeLine(workingDir); + + report.log(); + network.getVariantManager().setWorkingVariant(workingStateId); + boolean status = true; + Path outputNetworkFile = workingDir.resolve("outputs").resolve("finalState").resolve(DynaFlowConstants.OUTPUT_IIDM_FILENAME); + if (Files.exists(outputNetworkFile)) { + NetworkResultsUpdater.update(network, NetworkSerDe.read(outputNetworkFile), dynaFlowParameters.isMergeLoads()); + } else { + status = false; + } + Path resultsPath = workingDir.resolve(OUTPUT_RESULTS_FILENAME); + if (!Files.exists(resultsPath)) { + Map metrics = new HashMap<>(); + List componentResults = new ArrayList<>(1); + componentResults.add(new LoadFlowResultImpl.ComponentResultImpl(0, + 0, + status ? LoadFlowResult.ComponentResult.Status.CONVERGED : LoadFlowResult.ComponentResult.Status.FAILED, + 0, + "not-found", + 0., + Double.NaN)); + return new LoadFlowResultImpl(status, metrics, null, componentResults); + } + return LoadFlowResultDeserializer.read(resultsPath); + } + + private void reportTimeLine(Path workingDir) { + ReportNode dfReporter = DynaflowReports.createDynaFlowReportNode(reportNode, network.getId()); + Path timelineFile = workingDir.resolve(DYNAFLOW_OUTPUTS_FOLDER) + .resolve(DYNAWO_TIMELINE_FOLDER) + .resolve(DYNAFLOW_TIMELINE_FILE); + List tl = new XmlTimeLineParser().parse(timelineFile); + tl.forEach(e -> CommonReports.reportTimelineEntry(dfReporter, e)); + } +} diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowProvider.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowProvider.java index 1c3064884..b580df72b 100644 --- a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowProvider.java +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowProvider.java @@ -15,35 +15,23 @@ import com.powsybl.commons.parameters.Parameter; import com.powsybl.commons.report.ReportNode; import com.powsybl.computation.*; -import com.powsybl.dynaflow.json.DynaFlowConfigSerializer; import com.powsybl.dynaflow.json.JsonDynaFlowParametersSerializer; -import com.powsybl.dynawo.commons.CommonReports; import com.powsybl.dynawo.commons.DynawoUtil; -import com.powsybl.dynawo.commons.NetworkResultsUpdater; import com.powsybl.dynawo.commons.PowsyblDynawoVersion; -import com.powsybl.dynawo.commons.loadmerge.LoadsMerger; -import com.powsybl.dynawo.commons.timeline.TimelineEntry; -import com.powsybl.dynawo.commons.timeline.XmlTimeLineParser; import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.serde.NetworkSerDe; import com.powsybl.loadflow.LoadFlowParameters; import com.powsybl.loadflow.LoadFlowProvider; import com.powsybl.loadflow.LoadFlowResult; -import com.powsybl.loadflow.LoadFlowResultImpl; -import com.powsybl.loadflow.json.LoadFlowResultDeserializer; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.*; import java.util.concurrent.CompletableFuture; import static com.powsybl.dynaflow.DynaFlowConstants.*; -import static com.powsybl.dynawo.commons.DynawoConstants.DYNAWO_TIMELINE_FOLDER; /** * * @author Guillaume Pernin {@literal } + * @author Laurent Issertial */ @AutoService(LoadFlowProvider.class) public class DynaFlowProvider implements LoadFlowProvider { @@ -62,16 +50,12 @@ public DynaFlowProvider(Supplier configSupplier) { this.configSupplier = Suppliers.memoize(Objects.requireNonNull(configSupplier, "Config supplier is null")); } - private static String getProgram(DynaFlowConfig config) { - return config.getProgram(DynaFlowConstants.DYNAFLOW_LAUNCHER_PROGRAM_NAME); - } - public static Command getCommand(DynaFlowConfig config) { List args = Arrays.asList("--network", IIDM_FILENAME, "--config", CONFIG_FILENAME); return new SimpleCommandBuilder() .id("dynaflow_lf") - .program(getProgram(config)) + .program(config.getProgram()) .args(args) .inputFiles(new InputFile(IIDM_FILENAME), new InputFile(CONFIG_FILENAME)) @@ -84,7 +68,7 @@ public static Command getVersionCommand(DynaFlowConfig config) { List args = Collections.singletonList("--version"); return new SimpleCommandBuilder() .id("dynaflow_version") - .program(getProgram(config)) + .program(config.getProgram()) .args(args) .build(); } @@ -114,13 +98,14 @@ public CompletableFuture run(Network network, ComputationManager Objects.requireNonNull(computationManager); Objects.requireNonNull(workingStateId); Objects.requireNonNull(loadFlowParameters); + DynaFlowParameters dynaFlowParameters = getParametersExt(loadFlowParameters); DynaFlowParameters.log(loadFlowParameters, dynaFlowParameters); DynaFlowConfig config = Objects.requireNonNull(configSupplier.get()); - ExecutionEnvironment env = new ExecutionEnvironment(config.createEnv(), WORKING_DIR_PREFIX, config.isDebug()); + ExecutionEnvironment execEnv = new ExecutionEnvironment(config.createEnv(), WORKING_DIR_PREFIX, config.isDebug()); Command versionCmd = getVersionCommand(config); - DynawoUtil.requireDynaMinVersion(env, computationManager, versionCmd, DYNAFLOW_LAUNCHER_PROGRAM_NAME, true); - return computationManager.execute(env, new DynaFlowHandler(network, workingStateId, dynaFlowParameters, loadFlowParameters, config, reportNode)); + DynawoUtil.requireDynaMinVersion(execEnv, computationManager, versionCmd, DynaFlowConfig.DYNAFLOW_LAUNCHER_PROGRAM_NAME, true); + return computationManager.execute(execEnv, new DynaFlowHandler(network, workingStateId, dynaFlowParameters, loadFlowParameters, getCommand(config), reportNode)); } @Override @@ -161,75 +146,4 @@ public Optional getSpecificParametersSerializer() { public void updateSpecificParameters(Extension extension, Map properties) { getParametersExt(extension.getExtendable()).update(properties); } - - private static class DynaFlowHandler extends AbstractExecutionHandler { - private final Network network; - private final Network dynawoInput; - private final String workingStateId; - private final DynaFlowParameters dynaFlowParameters; - private final LoadFlowParameters loadFlowParameters; - private final DynaFlowConfig config; - private final ReportNode reportNode; - - public DynaFlowHandler(Network network, String workingStateId, DynaFlowParameters dynaFlowParameters, LoadFlowParameters loadFlowParameters, DynaFlowConfig config, ReportNode reportNode) { - this.network = network; - this.workingStateId = workingStateId; - this.dynaFlowParameters = dynaFlowParameters; - this.loadFlowParameters = loadFlowParameters; - this.config = config; - this.dynawoInput = this.dynaFlowParameters.isMergeLoads() ? LoadsMerger.mergeLoads(this.network) : this.network; - this.reportNode = reportNode; - } - - @Override - public List before(Path workingDir) throws IOException { - network.getVariantManager().setWorkingVariant(workingStateId); - DynawoUtil.writeIidm(dynawoInput, workingDir.resolve(IIDM_FILENAME)); - DynaFlowConfigSerializer.serialize(loadFlowParameters, dynaFlowParameters, Path.of("."), workingDir.resolve(CONFIG_FILENAME)); - return Collections.singletonList(createCommandExecution(config)); - } - - private static CommandExecution createCommandExecution(DynaFlowConfig config) { - Command cmd = getCommand(config); - return new CommandExecution(cmd, 1, 0); - } - - @Override - public LoadFlowResult after(Path workingDir, ExecutionReport report) { - reportTimeLine(workingDir); - - report.log(); - network.getVariantManager().setWorkingVariant(workingStateId); - boolean status = true; - Path outputNetworkFile = workingDir.resolve("outputs").resolve("finalState").resolve(OUTPUT_IIDM_FILENAME); - if (Files.exists(outputNetworkFile)) { - NetworkResultsUpdater.update(network, NetworkSerDe.read(outputNetworkFile), dynaFlowParameters.isMergeLoads()); - } else { - status = false; - } - Path resultsPath = workingDir.resolve(OUTPUT_RESULTS_FILENAME); - if (!Files.exists(resultsPath)) { - Map metrics = new HashMap<>(); - List componentResults = new ArrayList<>(1); - componentResults.add(new LoadFlowResultImpl.ComponentResultImpl(0, - 0, - status ? LoadFlowResult.ComponentResult.Status.CONVERGED : LoadFlowResult.ComponentResult.Status.FAILED, - 0, - "not-found", - 0., - Double.NaN)); - return new LoadFlowResultImpl(status, metrics, null, componentResults); - } - return LoadFlowResultDeserializer.read(resultsPath); - } - - private void reportTimeLine(Path workingDir) { - ReportNode dfReportNode = DynaflowReports.createDynaFlowReportNode(reportNode, network.getId()); - Path timelineFile = workingDir.resolve(DYNAFLOW_OUTPUTS_FOLDER) - .resolve(DYNAWO_TIMELINE_FOLDER) - .resolve(DYNAFLOW_TIMELINE_FILE); - List tl = new XmlTimeLineParser().parse(timelineFile); - tl.forEach(e -> CommonReports.reportTimelineEntry(dfReportNode, e)); - } - } } diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysis.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysis.java deleted file mode 100644 index ab7cba442..000000000 --- a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysis.java +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright (c) 2021, 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.dynaflow; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.powsybl.commons.json.JsonUtil; -import com.powsybl.commons.report.ReportNode; -import com.powsybl.computation.*; -import com.powsybl.contingency.ContingenciesProvider; -import com.powsybl.contingency.Contingency; -import com.powsybl.contingency.contingency.list.ContingencyList; -import com.powsybl.contingency.json.ContingencyJsonModule; -import com.powsybl.dynaflow.json.DynaFlowConfigSerializer; -import com.powsybl.dynaflow.xml.ConstraintsReader; -import com.powsybl.dynawo.commons.CommonReports; -import com.powsybl.dynawo.commons.DynawoUtil; -import com.powsybl.dynawo.commons.timeline.TimelineEntry; -import com.powsybl.dynawo.commons.timeline.XmlTimeLineParser; -import com.powsybl.iidm.network.Network; -import com.powsybl.loadflow.LoadFlowParameters; -import com.powsybl.loadflow.LoadFlowResult; -import com.powsybl.security.*; -import com.powsybl.security.interceptors.CurrentLimitViolationInterceptor; -import com.powsybl.security.interceptors.SecurityAnalysisInterceptor; -import com.powsybl.security.results.NetworkResult; -import com.powsybl.security.results.PostContingencyResult; -import com.powsybl.security.results.PreContingencyResult; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; - -import static com.powsybl.dynaflow.DynaFlowConstants.CONFIG_FILENAME; -import static com.powsybl.dynaflow.DynaFlowConstants.IIDM_FILENAME; -import static com.powsybl.dynawo.commons.DynawoConstants.DYNAWO_TIMELINE_FOLDER; - -/** - * @author Luma Zamarreno {@literal } - */ -public class DynaFlowSecurityAnalysis { - - private static final String WORKING_DIR_PREFIX = "dynaflow_sa_"; - private static final String CONTINGENCIES_FILENAME = "contingencies.json"; - private static final String DYNAWO_CONSTRAINTS_FOLDER = "constraints"; - - private final Supplier configSupplier; - - private final ComputationManager computationManager; - private final Network network; - private final LimitViolationFilter violationFilter; - private final List interceptors; - - public DynaFlowSecurityAnalysis(Network network, LimitViolationFilter filter, ComputationManager computationManager, - Supplier configSupplier) { - this.network = Objects.requireNonNull(network); - this.violationFilter = Objects.requireNonNull(filter); - this.interceptors = new ArrayList<>(); - this.computationManager = Objects.requireNonNull(computationManager); - - interceptors.add(new CurrentLimitViolationInterceptor()); - this.configSupplier = Objects.requireNonNull(configSupplier); - } - - private static DynaFlowParameters getParametersExt(LoadFlowParameters parameters) { - DynaFlowParameters parametersExt = parameters.getExtension(DynaFlowParameters.class); - if (parametersExt == null) { - parametersExt = new DynaFlowParameters(); - } - return parametersExt; - } - - private static String getProgram(DynaFlowConfig config) { - return config.getProgram(DynaFlowConstants.DYNAFLOW_LAUNCHER_PROGRAM_NAME); - } - - public static Command getCommand(DynaFlowConfig config) { - List args = Arrays.asList("--network", IIDM_FILENAME, - "--config", CONFIG_FILENAME, - "--contingencies", CONTINGENCIES_FILENAME); - return new SimpleCommandBuilder() - .id("dynaflow_sa") - .program(getProgram(config)) - .args(args) - .build(); - } - - private static CommandExecution createCommandExecution(DynaFlowConfig config) { - Command cmd = getCommand(config); - return new CommandExecution(cmd, 1, 0); - } - - public static Command getVersionCommand(DynaFlowConfig config) { - List args = Collections.singletonList("--version"); - return new SimpleCommandBuilder() - .id("dynaflow_version") - .program(getProgram(config)) - .args(args) - .build(); - } - - private static void writeContingencies(List contingencies, Path workingDir) throws IOException { - try (OutputStream os = Files.newOutputStream(workingDir.resolve(CONTINGENCIES_FILENAME))) { - ObjectMapper mapper = JsonUtil.createObjectMapper(); - ContingencyJsonModule module = new ContingencyJsonModule(); - mapper.registerModule(module); - ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter(); - writer.writeValue(os, ContingencyList.of(contingencies.toArray(Contingency[]::new))); - } - } - - private static void writeParameters(SecurityAnalysisParameters securityAnalysisParameters, Path workingDir) throws IOException { - // TODO(Luma) Take into account also Security Analysis parameters - LoadFlowParameters loadFlowParameters = securityAnalysisParameters.getLoadFlowParameters(); - DynaFlowParameters dynaFlowParameters = getParametersExt(loadFlowParameters); - DynaFlowConfigSerializer.serialize(loadFlowParameters, dynaFlowParameters, Path.of("."), workingDir.resolve(CONFIG_FILENAME)); - } - - public void addInterceptor(SecurityAnalysisInterceptor interceptor) { - interceptors.add(Objects.requireNonNull(interceptor)); - } - - public boolean removeInterceptor(SecurityAnalysisInterceptor interceptor) { - return interceptors.remove(interceptor); - } - - public CompletableFuture run(String workingVariantId, - SecurityAnalysisParameters securityAnalysisParameters, - ContingenciesProvider contingenciesProvider, - ReportNode reportNode) { - Objects.requireNonNull(workingVariantId); - Objects.requireNonNull(securityAnalysisParameters); - Objects.requireNonNull(contingenciesProvider); - Objects.requireNonNull(reportNode); - - DynaFlowConfig config = Objects.requireNonNull(configSupplier.get()); - ExecutionEnvironment env = new ExecutionEnvironment(config.createEnv(), WORKING_DIR_PREFIX, config.isDebug()); - Command versionCmd = getVersionCommand(config); - DynawoUtil.requireDynaMinVersion(env, computationManager, versionCmd, DynaFlowConstants.DYNAFLOW_LAUNCHER_PROGRAM_NAME, true); - List contingencies = contingenciesProvider.getContingencies(network); - return computationManager.execute(env, new AbstractExecutionHandler<>() { - @Override - public List before(Path workingDir) throws IOException { - network.getVariantManager().setWorkingVariant(workingVariantId); - - DynawoUtil.writeIidm(network, workingDir.resolve(IIDM_FILENAME)); - writeParameters(securityAnalysisParameters, workingDir); - writeContingencies(contingencies, workingDir); - return Collections.singletonList(createCommandExecution(config)); - } - - @Override - public SecurityAnalysisReport after(Path workingDir, ExecutionReport report) throws IOException { - super.after(workingDir, report); - network.getVariantManager().setWorkingVariant(workingVariantId); - - // Build the pre-contingency results from the input network - PreContingencyResult preContingencyResult = getPreContingencyResult(network, violationFilter); - - // Build the post-contingency results from the constraints files written by dynawo - Path constraintsDir = workingDir.resolve(DYNAWO_CONSTRAINTS_FOLDER); - List contingenciesResults = contingencies.stream() - .map(c -> getPostContingencyResult(network, violationFilter, constraintsDir, c)) - .toList(); - - // Report the timeline events from the timeline files written by dynawo - Path timelineDir = workingDir.resolve(DYNAWO_TIMELINE_FOLDER); - contingencies.forEach(c -> { - ReportNode contingencyReportNode = DynaflowReports.createDynaFlowTimelineReportNode(reportNode, c.getId()); - getTimeline(timelineDir, c).forEach(e -> CommonReports.reportTimelineEntry(contingencyReportNode, e)); - }); - - return new SecurityAnalysisReport( - new SecurityAnalysisResult(preContingencyResult, contingenciesResults, Collections.emptyList()) - ); - } - }); - } - - private static PreContingencyResult getPreContingencyResult(Network network, LimitViolationFilter violationFilter) { - List limitViolations = Security.checkLimits(network); - List filteredViolations = violationFilter.apply(limitViolations, network); - NetworkResult networkResult = new NetworkResult(Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - return new PreContingencyResult(LoadFlowResult.ComponentResult.Status.CONVERGED, new LimitViolationsResult(filteredViolations), networkResult); - } - - private static PostContingencyResult getPostContingencyResult(Network network, LimitViolationFilter violationFilter, - Path constraintsDir, Contingency c) { - Path constraintsFile = constraintsDir.resolve("constraints_" + c.getId() + ".xml"); - if (Files.exists(constraintsFile)) { - List limitViolationsRead = ConstraintsReader.read(network, constraintsFile); - List limitViolationsFiltered = violationFilter.apply(limitViolationsRead, network); - return new PostContingencyResult(c, PostContingencyComputationStatus.CONVERGED, new LimitViolationsResult(limitViolationsFiltered)); - } else { - return new PostContingencyResult(c, PostContingencyComputationStatus.FAILED, Collections.emptyList()); - } - } - - private List getTimeline(Path timelineDir, Contingency c) { - Path timelineFile = timelineDir.resolve("timeline_" + c.getId() + ".xml"); - return new XmlTimeLineParser().parse(timelineFile); - } -} diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisHandler.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisHandler.java new file mode 100644 index 000000000..314c2ed94 --- /dev/null +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisHandler.java @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2023, 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.dynaflow; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.powsybl.commons.json.JsonUtil; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.AbstractExecutionHandler; +import com.powsybl.computation.Command; +import com.powsybl.computation.CommandExecution; +import com.powsybl.computation.ExecutionReport; +import com.powsybl.contingency.Contingency; +import com.powsybl.contingency.contingency.list.ContingencyList; +import com.powsybl.contingency.json.ContingencyJsonModule; +import com.powsybl.dynaflow.json.DynaFlowConfigSerializer; +import com.powsybl.dynawo.commons.DynawoUtil; +import com.powsybl.iidm.network.Network; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.security.LimitViolationFilter; +import com.powsybl.security.SecurityAnalysisParameters; +import com.powsybl.security.SecurityAnalysisReport; +import com.powsybl.security.SecurityAnalysisResult; +import com.powsybl.security.interceptors.SecurityAnalysisInterceptor; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dynaflow.DynaFlowConstants.CONFIG_FILENAME; +import static com.powsybl.dynaflow.DynaFlowConstants.IIDM_FILENAME; +import static com.powsybl.dynaflow.SecurityAnalysisConstants.CONTINGENCIES_FILENAME; +import static com.powsybl.dynaflow.SecurityAnalysisConstants.DYNAWO_CONSTRAINTS_FOLDER; +import static com.powsybl.dynawo.commons.DynawoConstants.DYNAWO_TIMELINE_FOLDER; +import static com.powsybl.dynawo.commons.DynawoUtil.getCommandExecutions; + +/** + * @author Laurent Issertial + */ +public final class DynaFlowSecurityAnalysisHandler extends AbstractExecutionHandler { + + private final Command command; + private final Network network; + private final String workingVariantId; + private final SecurityAnalysisParameters securityAnalysisParameters; + private final List contingencies; + private final LimitViolationFilter violationFilter; + private final List interceptors; + private final ReportNode reportNode; + + public DynaFlowSecurityAnalysisHandler(Network network, String workingVariantId, Command command, + SecurityAnalysisParameters securityAnalysisParameters, List contingencies, + LimitViolationFilter violationFilter, List interceptors, + ReportNode reportNode) { + this.network = network; + this.workingVariantId = workingVariantId; + this.command = command; + this.securityAnalysisParameters = securityAnalysisParameters; + this.contingencies = contingencies; + this.violationFilter = violationFilter; + this.interceptors = interceptors; + this.reportNode = reportNode; + } + + @Override + public List before(Path workingDir) throws IOException { + network.getVariantManager().setWorkingVariant(workingVariantId); + + DynawoUtil.writeIidm(network, workingDir.resolve(IIDM_FILENAME)); + writeParameters(securityAnalysisParameters, workingDir); + writeContingencies(contingencies, workingDir); + return getCommandExecutions(command); + } + + @Override + public SecurityAnalysisReport after(Path workingDir, ExecutionReport report) throws IOException { + super.after(workingDir, report); + network.getVariantManager().setWorkingVariant(workingVariantId); + ContingencyResultsUtils.reportContingenciesTimelines(contingencies, workingDir.resolve(DYNAWO_TIMELINE_FOLDER), reportNode); + return new SecurityAnalysisReport( + new SecurityAnalysisResult( + ContingencyResultsUtils.getPreContingencyResult(network, violationFilter), + ContingencyResultsUtils.getPostContingencyResults(network, violationFilter, workingDir.resolve(DYNAWO_CONSTRAINTS_FOLDER), contingencies), + Collections.emptyList()) + ); + } + + private static void writeContingencies(List contingencies, Path workingDir) throws IOException { + try (OutputStream os = Files.newOutputStream(workingDir.resolve(CONTINGENCIES_FILENAME))) { + ObjectMapper mapper = JsonUtil.createObjectMapper(); + ContingencyJsonModule module = new ContingencyJsonModule(); + mapper.registerModule(module); + ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter(); + writer.writeValue(os, ContingencyList.of(contingencies.toArray(Contingency[]::new))); + } + } + + private static void writeParameters(SecurityAnalysisParameters securityAnalysisParameters, Path workingDir) throws IOException { + // TODO(Luma) Take into account also Security Analysis parameters + LoadFlowParameters loadFlowParameters = securityAnalysisParameters.getLoadFlowParameters(); + DynaFlowParameters dynaFlowParameters = getParametersExt(loadFlowParameters); + DynaFlowConfigSerializer.serialize(loadFlowParameters, dynaFlowParameters, Path.of("."), workingDir.resolve(CONFIG_FILENAME)); + } + + private static DynaFlowParameters getParametersExt(LoadFlowParameters parameters) { + DynaFlowParameters parametersExt = parameters.getExtension(DynaFlowParameters.class); + if (parametersExt == null) { + parametersExt = new DynaFlowParameters(); + } + return parametersExt; + } +} diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisProvider.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisProvider.java index 2a2cea440..fb02e1bde 100644 --- a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisProvider.java +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaFlowSecurityAnalysisProvider.java @@ -8,7 +8,12 @@ import com.google.auto.service.AutoService; import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.Command; +import com.powsybl.computation.ExecutionEnvironment; +import com.powsybl.computation.SimpleCommandBuilder; import com.powsybl.contingency.ContingenciesProvider; +import com.powsybl.contingency.Contingency; +import com.powsybl.dynawo.commons.DynawoUtil; import com.powsybl.dynawo.commons.PowsyblDynawoVersion; import com.powsybl.iidm.network.Network; import com.powsybl.security.SecurityAnalysisProvider; @@ -17,10 +22,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; +import static com.powsybl.dynaflow.DynaFlowConstants.*; +import static com.powsybl.dynaflow.SecurityAnalysisConstants.CONTINGENCIES_FILENAME; import static com.powsybl.dynaflow.DynaFlowConstants.DYNAFLOW_NAME; /** @@ -30,7 +39,7 @@ public class DynaFlowSecurityAnalysisProvider implements SecurityAnalysisProvider { private static final Logger LOG = LoggerFactory.getLogger(DynaFlowSecurityAnalysisProvider.class); - + private static final String WORKING_DIR_PREFIX = "dynaflow_sa_"; private final Supplier configSupplier; public DynaFlowSecurityAnalysisProvider() { @@ -58,11 +67,15 @@ public CompletableFuture run(Network network, if (!runParameters.getLimitReductions().isEmpty()) { LOG.error("Limit reductions are not implemented in Dynaflow"); } - DynaFlowSecurityAnalysis securityAnalysis = new DynaFlowSecurityAnalysis(network, runParameters.getFilter(), runParameters.getComputationManager(), configSupplier); - runParameters.getInterceptors().forEach(securityAnalysis::addInterceptor); + DynaFlowConfig config = Objects.requireNonNull(configSupplier.get()); + ExecutionEnvironment execEnv = new ExecutionEnvironment(config.createEnv(), WORKING_DIR_PREFIX, config.isDebug()); + DynawoUtil.requireDynaMinVersion(execEnv, runParameters.getComputationManager(), DynaFlowProvider.getVersionCommand(config), DynaFlowConfig.DYNAFLOW_LAUNCHER_PROGRAM_NAME, true); + List contingencies = contingenciesProvider.getContingencies(network); ReportNode dfsaReportNode = DynaflowReports.createDynaFlowSecurityAnalysisReportNode(runParameters.getReportNode(), network.getId()); - return securityAnalysis.run(workingVariantId, runParameters.getSecurityAnalysisParameters(), contingenciesProvider, dfsaReportNode); + + DynaFlowSecurityAnalysisHandler executionHandler = new DynaFlowSecurityAnalysisHandler(network, workingVariantId, getCommand(config), runParameters.getSecurityAnalysisParameters(), contingencies, runParameters.getFilter(), runParameters.getInterceptors(), dfsaReportNode); + return runParameters.getComputationManager().execute(execEnv, executionHandler); } @Override @@ -74,4 +87,15 @@ public String getName() { public String getVersion() { return new PowsyblDynawoVersion().getMavenProjectVersion(); } + + public static Command getCommand(DynaFlowConfig config) { + List args = Arrays.asList("--network", IIDM_FILENAME, + "--config", CONFIG_FILENAME, + "--contingencies", CONTINGENCIES_FILENAME); + return new SimpleCommandBuilder() + .id("dynaflow_sa") + .program(config.getProgram()) + .args(args) + .build(); + } } diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaflowReports.java b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaflowReports.java index 9223cbc4f..8e5ec5bfb 100644 --- a/dynaflow/src/main/java/com/powsybl/dynaflow/DynaflowReports.java +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/DynaflowReports.java @@ -31,9 +31,9 @@ public static ReportNode createDynaFlowSecurityAnalysisReportNode(ReportNode rep .add(); } - public static ReportNode createDynaFlowTimelineReportNode(ReportNode reportNode, String contingencyId) { + public static ReportNode createContingenciesTimelineReportNode(ReportNode reportNode, String contingencyId) { return reportNode.newReportNode() - .withMessageTemplate("dynaflowSaContingency", "Contingency '${contingencyId}'") + .withMessageTemplate("saContingency", "Contingency '${contingencyId}'") .withUntypedValue("contingencyId", contingencyId) .add(); } diff --git a/dynaflow/src/main/java/com/powsybl/dynaflow/SecurityAnalysisConstants.java b/dynaflow/src/main/java/com/powsybl/dynaflow/SecurityAnalysisConstants.java new file mode 100644 index 000000000..3d4e3f377 --- /dev/null +++ b/dynaflow/src/main/java/com/powsybl/dynaflow/SecurityAnalysisConstants.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2023, 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.dynaflow; + +/** + * @author Laurent Issertial + */ +public final class SecurityAnalysisConstants { + + public static final String DYNAWO_CONSTRAINTS_FOLDER = "constraints"; + + public static final String CONTINGENCIES_FILENAME = "contingencies.json"; + + private SecurityAnalysisConstants() { + } + +} diff --git a/dynaflow/src/test/java/com/powsybl/dynaflow/DynaFlowParametersTest.java b/dynaflow/src/test/java/com/powsybl/dynaflow/DynaFlowParametersTest.java index ad4aca131..e690e54f6 100644 --- a/dynaflow/src/test/java/com/powsybl/dynaflow/DynaFlowParametersTest.java +++ b/dynaflow/src/test/java/com/powsybl/dynaflow/DynaFlowParametersTest.java @@ -40,12 +40,14 @@ class DynaFlowParametersTest extends AbstractSerDeTest { private InMemoryPlatformConfig platformConfig; @BeforeEach + @Override public void setUp() { fileSystem = Jimfs.newFileSystem(Configuration.unix()); platformConfig = new InMemoryPlatformConfig(fileSystem); } @AfterEach + @Override public void tearDown() throws IOException { fileSystem.close(); } diff --git a/dynaflow/src/test/java/com/powsybl/dynaflow/DynaFlowProviderTest.java b/dynaflow/src/test/java/com/powsybl/dynaflow/DynaFlowProviderTest.java index 5df23c61b..6bb76f9b2 100644 --- a/dynaflow/src/test/java/com/powsybl/dynaflow/DynaFlowProviderTest.java +++ b/dynaflow/src/test/java/com/powsybl/dynaflow/DynaFlowProviderTest.java @@ -34,6 +34,7 @@ import static com.powsybl.commons.test.ComparisonUtils.assertXmlEquals; import static com.powsybl.dynaflow.DynaFlowConstants.*; +import static com.powsybl.dynawo.commons.DynawoConstants.OUTPUT_IIDM_FILENAME; import static com.powsybl.loadflow.LoadFlowResult.Status.FAILED; import static com.powsybl.loadflow.LoadFlowResult.Status.FULLY_CONVERGED; import static org.assertj.core.api.Assertions.assertThat; @@ -49,6 +50,7 @@ class DynaFlowProviderTest extends AbstractSerDeTest { private DynaFlowProvider provider; @BeforeEach + @Override public void setUp() throws IOException { super.setUp(); homeDir = fileSystem.getPath("/home/dynaflow"); diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzConfig.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzConfig.java index b17f64e97..829e9d3e0 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzConfig.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzConfig.java @@ -8,16 +8,17 @@ import com.powsybl.commons.config.ModuleConfig; import com.powsybl.commons.config.PlatformConfig; -import com.powsybl.dynawo.commons.DynawoConfig; +import com.powsybl.dynawo.commons.AbstractDynawoConfig; import java.nio.file.Path; /** * @author Marcos de Miguel {@literal } */ -public class DynaWaltzConfig extends DynawoConfig { +public class DynaWaltzConfig extends AbstractDynawoConfig { - private static final String DYNAWALTZ_MODULE_NAME = "dynawaltz"; + public static final String DYNAWALTZ_LAUNCHER_PROGRAM_NAME = "dynawo"; + protected static final String DYNAWALTZ_MODULE_NAME = "dynawaltz"; public static DynaWaltzConfig load() { return load(DynaWaltzConfig::new, DYNAWALTZ_MODULE_NAME); @@ -31,7 +32,12 @@ public DynaWaltzConfig(Path homeDir, boolean debug) { super(homeDir, debug); } - private DynaWaltzConfig(ModuleConfig config) { + protected DynaWaltzConfig(ModuleConfig config) { super(config); } + + @Override + public String getProgram() { + return getProgram(DYNAWALTZ_LAUNCHER_PROGRAM_NAME); + } } diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzConstants.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzConstants.java new file mode 100644 index 000000000..afcb6d99d --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzConstants.java @@ -0,0 +1,20 @@ +/** + * 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.dynawaltz; + +/** + * @author Laurent Issertial + */ +public final class DynaWaltzConstants { + + private DynaWaltzConstants() { + } + + public static final String OUTPUTS_FOLDER = "outputs"; + public static final String FINAL_STATE_FOLDER = "finalState"; +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzContext.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzContext.java index 34207b28b..7db4b9572 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzContext.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzContext.java @@ -47,7 +47,7 @@ public class DynaWaltzContext { private static final String MODEL_ID_EXCEPTION = "The model identified by the static id %s does not match the expected model (%s)"; private static final String MODEL_ID_LOG = "The model identified by the static id {} does not match the expected model ({})"; - private final Network network; + protected final Network network; private final String workingVariantId; private final DynamicSimulationParameters parameters; private final DynaWaltzParameters dynaWaltzParameters; @@ -61,7 +61,7 @@ public class DynaWaltzContext { private final DefaultModelsHandler defaultModelsHandler = new DefaultModelsHandler(); private final FrequencySynchronizerModel frequencySynchronizer; private final List dynamicModelsParameters = new ArrayList<>(); - private final MacroConnectionsAdder macroConnectionsAdder; + protected final MacroConnectionsAdder macroConnectionsAdder; public DynaWaltzContext(Network network, String workingVariantId, List dynamicModels, List eventModels, List curves, DynamicSimulationParameters parameters, DynaWaltzParameters dynaWaltzParameters) { diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzHandler.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzHandler.java new file mode 100644 index 000000000..b9460d065 --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzHandler.java @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2023, 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.dynawaltz; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.AbstractExecutionHandler; +import com.powsybl.computation.Command; +import com.powsybl.computation.CommandExecution; +import com.powsybl.computation.ExecutionReport; +import com.powsybl.dynamicsimulation.DynamicSimulationResult; +import com.powsybl.dynamicsimulation.DynamicSimulationResultImpl; +import com.powsybl.dynamicsimulation.TimelineEvent; +import com.powsybl.dynawaltz.xml.CurvesXml; +import com.powsybl.dynawaltz.xml.DydXml; +import com.powsybl.dynawaltz.xml.JobsXml; +import com.powsybl.dynawaltz.xml.ParametersXml; +import com.powsybl.dynawo.commons.CommonReports; +import com.powsybl.dynawo.commons.DynawoUtil; +import com.powsybl.dynawo.commons.NetworkResultsUpdater; +import com.powsybl.dynawo.commons.dynawologs.CsvLogParser; +import com.powsybl.dynawo.commons.loadmerge.LoadsMerger; +import com.powsybl.dynawo.commons.timeline.CsvTimeLineParser; +import com.powsybl.dynawo.commons.timeline.TimeLineParser; +import com.powsybl.dynawo.commons.timeline.XmlTimeLineParser; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.serde.NetworkSerDe; +import com.powsybl.timeseries.DoubleTimeSeries; +import com.powsybl.timeseries.TimeSeries; +import com.powsybl.timeseries.TimeSeriesConstants; +import com.powsybl.timeseries.TimeSeriesCsvConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.powsybl.dynawaltz.DynaWaltzConstants.FINAL_STATE_FOLDER; +import static com.powsybl.dynawaltz.DynaWaltzConstants.OUTPUTS_FOLDER; +import static com.powsybl.dynawaltz.xml.DynaWaltzConstants.*; +import static com.powsybl.dynawo.commons.DynawoConstants.DYNAWO_TIMELINE_FOLDER; +import static com.powsybl.dynawo.commons.DynawoUtil.getCommandExecutions; + +/** + * @author Laurent Issertial + */ +public final class DynaWaltzHandler extends AbstractExecutionHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(DynaWaltzHandler.class); + private static final String LOGS_FOLDER = "logs"; + private static final String OUTPUT_IIDM_FILENAME = "outputIIDM.xml"; + private static final String OUTPUT_DUMP_FILENAME = "outputState.dmp"; + private static final String TIMELINE_FILENAME = "timeline"; + private static final String LOGS_FILENAME = "dynawaltz.log"; + private static final String ERROR_FILENAME = "dyn_fs_0.err"; + private static final String DYNAWO_ERROR_PATTERN = "DYN Error: "; + + private final DynaWaltzContext context; + private final Command command; + private final Network dynawoInput; + private final ReportNode reportNode; + + private final List timeline = new ArrayList<>(); + private final Map curves = new HashMap<>(); + private DynamicSimulationResult.Status status = DynamicSimulationResult.Status.SUCCESS; + private String statusText = ""; + + public DynaWaltzHandler(DynaWaltzContext context, Command command, ReportNode reportNode) { + this.context = context; + this.dynawoInput = context.getDynaWaltzParameters().isMergeLoads() + ? LoadsMerger.mergeLoads(context.getNetwork()) + : context.getNetwork(); + this.command = command; + this.reportNode = reportNode; + } + + @Override + public List before(Path workingDir) throws IOException { + Path outputNetworkFile = workingDir.resolve(OUTPUTS_FOLDER).resolve(FINAL_STATE_FOLDER).resolve(OUTPUT_IIDM_FILENAME); + if (Files.exists(outputNetworkFile)) { + Files.delete(outputNetworkFile); + } + Path curvesPath = workingDir.resolve(CURVES_OUTPUT_PATH).toAbsolutePath().resolve(CURVES_FILENAME); + if (Files.exists(curvesPath)) { + Files.delete(curvesPath); + } + writeInputFiles(workingDir); + return getCommandExecutions(command); + } + + @Override + public DynamicSimulationResult after(Path workingDir, ExecutionReport report) throws IOException { + + Path outputsFolder = workingDir.resolve(OUTPUTS_FOLDER); + context.getNetwork().getVariantManager().setWorkingVariant(context.getWorkingVariantId()); + DynaWaltzParameters parameters = context.getDynaWaltzParameters(); + DumpFileParameters dumpFileParameters = parameters.getDumpFileParameters(); + + setDynawoLog(outputsFolder, parameters.getSpecificLogs()); + // Error file + Path errorFile = workingDir.resolve(ERROR_FILENAME); + if (Files.exists(errorFile)) { + Matcher errorMatcher = Pattern.compile(DYNAWO_ERROR_PATTERN + "(.*)").matcher(Files.readString(errorFile)); + if (!errorMatcher.find()) { + if (parameters.isWriteFinalState()) { + updateNetwork(outputsFolder); + } + if (dumpFileParameters.exportDumpFile()) { + setDumpFile(outputsFolder, dumpFileParameters.dumpFileFolder(), workingDir.getFileName()); + } + setTimeline(outputsFolder); + if (context.withCurves()) { + setCurves(workingDir); + } + } else { + status = DynamicSimulationResult.Status.FAILURE; + statusText = errorMatcher.group().substring(DYNAWO_ERROR_PATTERN.length()); + } + } else { + LOGGER.warn("Error file not found"); + status = DynamicSimulationResult.Status.FAILURE; + statusText = "Dynawo error log file not found"; + } + + return new DynamicSimulationResultImpl(status, statusText, curves, timeline); + } + + private void setDynawoLog(Path outputsFolder, Set specificLogs) throws IOException { + Path logFolder = outputsFolder.resolve(LOGS_FOLDER); + if (Files.exists(logFolder)) { + Path logFile = logFolder.resolve(LOGS_FILENAME); + if (Files.exists(logFile)) { + ReportNode logReportNode = CommonReports.createDynawoLogReportNode(reportNode); + new CsvLogParser().parse(logFile).forEach(e -> CommonReports.reportLogEntry(logReportNode, e)); + } + for (DynaWaltzParameters.SpecificLog specificLog : specificLogs) { + Path specificLogFile = logFolder.resolve(specificLog.getFileName()); + if (Files.exists(specificLogFile)) { + ReportNode logReport = DynawaltzReports.createDynawoSpecificLogReportNode(reportNode, specificLog); + DynawaltzReports.reportSpecificLogEntry(logReport, Files.readString(specificLogFile)); + } + } + } else { + LOGGER.warn("Dynawo logs file not found"); + } + } + + private void updateNetwork(Path outputsFolder) { + Path outputNetworkFile = outputsFolder.resolve(FINAL_STATE_FOLDER).resolve(OUTPUT_IIDM_FILENAME); + if (Files.exists(outputNetworkFile)) { + NetworkResultsUpdater.update(context.getNetwork(), NetworkSerDe.read(outputNetworkFile), context.getDynaWaltzParameters().isMergeLoads()); + } else { + LOGGER.warn("Output IIDM file not found"); + status = DynamicSimulationResult.Status.FAILURE; + statusText = "Dynawo Output IIDM file not found"; + } + } + + private void setDumpFile(Path outputsFolder, Path dumpFileFolder, Path fileName) throws IOException { + Path outputDumpFile = outputsFolder.resolve(FINAL_STATE_FOLDER).resolve(OUTPUT_DUMP_FILENAME); + if (Files.exists(outputDumpFile)) { + Files.copy(outputDumpFile, dumpFileFolder.resolve(fileName + "_" + OUTPUT_DUMP_FILENAME), StandardCopyOption.REPLACE_EXISTING); + } else { + LOGGER.warn("Dump file {} not found, export will be skipped", OUTPUT_DUMP_FILENAME); + } + } + + private void setTimeline(Path outputsFolder) { + DynaWaltzParameters.ExportMode exportMode = context.getDynaWaltzParameters().getTimelineExportMode(); + Path timelineFile = outputsFolder.resolve(DYNAWO_TIMELINE_FOLDER).resolve(TIMELINE_FILENAME + exportMode.getFileExtension()); + if (Files.exists(timelineFile)) { + TimeLineParser parser = switch (exportMode) { + case CSV -> new CsvTimeLineParser(';'); + case TXT -> new CsvTimeLineParser(); + case XML -> new XmlTimeLineParser(); + }; + parser.parse(timelineFile).forEach(e -> timeline.add(new TimelineEvent(e.time(), e.modelName(), e.message()))); + } else { + LOGGER.warn("Timeline file not found"); + } + } + + private void setCurves(Path workingDir) { + Path curvesPath = workingDir.resolve(CURVES_OUTPUT_PATH).toAbsolutePath().resolve(CURVES_FILENAME); + if (Files.exists(curvesPath)) { + TimeSeries.parseCsv(curvesPath, new TimeSeriesCsvConfig(TimeSeriesConstants.DEFAULT_SEPARATOR, false, TimeSeries.TimeFormat.FRACTIONS_OF_SECOND)) + .values().forEach(l -> l.forEach(curve -> curves.put(curve.getMetadata().getName(), (DoubleTimeSeries) curve))); + } else { + LOGGER.warn("Curves folder not found"); + status = DynamicSimulationResult.Status.FAILURE; + statusText = "Dynawo curves folder not found"; + } + } + + private void writeInputFiles(Path workingDir) throws IOException { + DynawoUtil.writeIidm(dynawoInput, workingDir.resolve(NETWORK_FILENAME)); + JobsXml.write(workingDir, context); + DydXml.write(workingDir, context); + ParametersXml.write(workingDir, context); + if (context.withCurves()) { + CurvesXml.write(workingDir, context); + } + DumpFileParameters dumpFileParameters = context.getDynaWaltzParameters().getDumpFileParameters(); + if (dumpFileParameters.useDumpFile()) { + Path dumpFilePath = dumpFileParameters.getDumpFilePath(); + if (dumpFilePath != null) { + Files.copy(dumpFilePath, workingDir.resolve(dumpFileParameters.dumpFile()), StandardCopyOption.REPLACE_EXISTING); + } + } + } +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzParameters.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzParameters.java index a446cd8dd..3d7e8b8cc 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzParameters.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzParameters.java @@ -163,6 +163,14 @@ private static Path resolveParameterPath(String fileName, PlatformConfig platfor return platformConfig.getConfigDir().map(configDir -> configDir.resolve(fileName)).orElse(fileSystem.getPath(fileName)); } + public static DynaWaltzParameters load(DynamicSimulationParameters parameters) { + DynaWaltzParameters dynaWaltzParameters = parameters.getExtension(DynaWaltzParameters.class); + if (dynaWaltzParameters == null) { + dynaWaltzParameters = DynaWaltzParameters.load(); + } + return dynaWaltzParameters; + } + @Override public String getName() { return "DynaWaltzParameters"; diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzProvider.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzProvider.java index a20972ed2..c56441966 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzProvider.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/DynaWaltzProvider.java @@ -11,40 +11,17 @@ import com.powsybl.commons.report.ReportNode; import com.powsybl.computation.*; import com.powsybl.dynamicsimulation.*; -import com.powsybl.dynawaltz.models.BlackBoxModel; -import com.powsybl.dynawaltz.xml.CurvesXml; -import com.powsybl.dynawaltz.xml.DydXml; -import com.powsybl.dynawaltz.xml.JobsXml; -import com.powsybl.dynawaltz.xml.ParametersXml; -import com.powsybl.dynawo.commons.*; +import com.powsybl.dynawaltz.models.utils.BlackBoxSupplierUtils; +import com.powsybl.dynawo.commons.DynawoUtil; import com.powsybl.dynawo.commons.PowsyblDynawoVersion; -import com.powsybl.dynawo.commons.dynawologs.CsvLogParser; -import com.powsybl.dynawo.commons.loadmerge.LoadsMerger; -import com.powsybl.dynawo.commons.timeline.CsvTimeLineParser; -import com.powsybl.dynawo.commons.timeline.TimeLineParser; -import com.powsybl.dynawo.commons.timeline.XmlTimeLineParser; import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.serde.NetworkSerDe; -import com.powsybl.timeseries.DoubleTimeSeries; -import com.powsybl.timeseries.TimeSeries; -import com.powsybl.timeseries.TimeSeries.TimeFormat; -import com.powsybl.timeseries.TimeSeriesConstants; -import com.powsybl.timeseries.TimeSeriesCsvConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import static com.powsybl.dynawaltz.xml.DynaWaltzConstants.*; -import static com.powsybl.dynawo.commons.DynawoConstants.DYNAWO_TIMELINE_FOLDER; +import static com.powsybl.dynawaltz.xml.DynaWaltzConstants.JOBS_FILENAME; /** * @author Marcos de Miguel {@literal } @@ -55,19 +32,7 @@ public class DynaWaltzProvider implements DynamicSimulationProvider { public static final String NAME = "DynaWaltz"; private static final String WORKING_DIR_PREFIX = "powsybl_dynawaltz_"; - private static final String OUTPUTS_FOLDER = "outputs"; - private static final String FINAL_STATE_FOLDER = "finalState"; - private static final String LOGS_FOLDER = "logs"; - private static final String OUTPUT_IIDM_FILENAME = "outputIIDM.xml"; - private static final String OUTPUT_DUMP_FILENAME = "outputState.dmp"; - private static final String TIMELINE_FILENAME = "timeline"; - private static final String LOGS_FILENAME = "dynawaltz.log"; - private static final String ERROR_FILENAME = "dyn_fs_0.err"; - private static final String DYNAWO_ERROR_PATTERN = "DYN Error: "; - - private static final Logger LOGGER = LoggerFactory.getLogger(DynaWaltzProvider.class); - - private final DynaWaltzConfig dynaWaltzConfig; + private final DynaWaltzConfig config; public DynaWaltzProvider() { this(PlatformConfig.defaultConfig()); @@ -77,8 +42,8 @@ public DynaWaltzProvider(PlatformConfig platformConfig) { this(DynaWaltzConfig.load(platformConfig)); } - public DynaWaltzProvider(DynaWaltzConfig dynawoConfig) { - this.dynaWaltzConfig = Objects.requireNonNull(dynawoConfig); + public DynaWaltzProvider(DynaWaltzConfig config) { + this.config = Objects.requireNonNull(config); } @Override @@ -119,193 +84,19 @@ public CompletableFuture run(Network network, DynamicMo Objects.requireNonNull(workingVariantId); Objects.requireNonNull(parameters); Objects.requireNonNull(reportNode); - DynaWaltzParameters dynaWaltzParameters = getDynaWaltzSimulationParameters(parameters); - return run(network, dynamicModelsSupplier, eventModelsSupplier, curvesSupplier, workingVariantId, computationManager, parameters, dynaWaltzParameters, reportNode); - } - - private DynaWaltzParameters getDynaWaltzSimulationParameters(DynamicSimulationParameters parameters) { - DynaWaltzParameters dynaWaltzParameters = parameters.getExtension(DynaWaltzParameters.class); - if (dynaWaltzParameters == null) { - dynaWaltzParameters = DynaWaltzParameters.load(); - } - return dynaWaltzParameters; - } - - private CompletableFuture run(Network network, DynamicModelsSupplier dynamicModelsSupplier, EventModelsSupplier eventsModelsSupplier, CurvesSupplier curvesSupplier, - String workingVariantId, ComputationManager computationManager, DynamicSimulationParameters parameters, DynaWaltzParameters dynaWaltzParameters, ReportNode reportNode) { ReportNode dsReportNode = DynawaltzReports.createDynaWaltzReportNode(reportNode, network.getId()); network.getVariantManager().setWorkingVariant(workingVariantId); - ExecutionEnvironment execEnv = new ExecutionEnvironment(Collections.emptyMap(), WORKING_DIR_PREFIX, dynaWaltzConfig.isDebug()); - Command versionCmd = getVersionCommand(dynaWaltzConfig); - DynawoUtil.requireDynaMinVersion(execEnv, computationManager, versionCmd, DynawoConstants.DYNAWO_CMD_NAME, false); - - List blackBoxModels = dynamicModelsSupplier.get(network, dsReportNode).stream() - .filter(BlackBoxModel.class::isInstance) - .map(BlackBoxModel.class::cast) - .collect(Collectors.toList()); - List blackBoxEventModels = eventsModelsSupplier.get(network, dsReportNode).stream() - .filter(BlackBoxModel.class::isInstance) - .map(BlackBoxModel.class::cast) - .collect(Collectors.toList()); - DynaWaltzContext context = new DynaWaltzContext(network, workingVariantId, blackBoxModels, blackBoxEventModels, curvesSupplier.get(network, dsReportNode), parameters, dynaWaltzParameters, reportNode); - return computationManager.execute(execEnv, new DynaWaltzHandler(context, reportNode)); - } - - private final class DynaWaltzHandler extends AbstractExecutionHandler { - - private final DynaWaltzContext context; - private final Network dynawoInput; - private final ReportNode reportNode; - - private final List timeline = new ArrayList<>(); - private final Map curves = new HashMap<>(); - private DynamicSimulationResult.Status status = DynamicSimulationResult.Status.SUCCESS; - private String statusText = ""; - - public DynaWaltzHandler(DynaWaltzContext context, ReportNode reportNode) { - this.context = context; - this.dynawoInput = context.getDynaWaltzParameters().isMergeLoads() - ? LoadsMerger.mergeLoads(context.getNetwork()) - : context.getNetwork(); - this.reportNode = reportNode; - } - - @Override - public List before(Path workingDir) throws IOException { - Path outputNetworkFile = workingDir.resolve(OUTPUTS_FOLDER).resolve(FINAL_STATE_FOLDER).resolve(OUTPUT_IIDM_FILENAME); - if (Files.exists(outputNetworkFile)) { - Files.delete(outputNetworkFile); - } - Path curvesPath = workingDir.resolve(CURVES_OUTPUT_PATH).toAbsolutePath().resolve(CURVES_FILENAME); - if (Files.exists(curvesPath)) { - Files.delete(curvesPath); - } - writeInputFiles(workingDir); - Command cmd = getCommand(dynaWaltzConfig); - return Collections.singletonList(new CommandExecution(cmd, 1)); - } - - @Override - public DynamicSimulationResult after(Path workingDir, ExecutionReport report) throws IOException { - - Path outputsFolder = workingDir.resolve(OUTPUTS_FOLDER); - context.getNetwork().getVariantManager().setWorkingVariant(context.getWorkingVariantId()); - DynaWaltzParameters parameters = context.getDynaWaltzParameters(); - DumpFileParameters dumpFileParameters = parameters.getDumpFileParameters(); - - setDynawoLog(outputsFolder, parameters.getSpecificLogs()); - // Error file - Path errorFile = workingDir.resolve(ERROR_FILENAME); - if (Files.exists(errorFile)) { - Matcher errorMatcher = Pattern.compile(DYNAWO_ERROR_PATTERN + "(.*)") - .matcher(Files.readString(errorFile)); - if (!errorMatcher.find()) { - if (parameters.isWriteFinalState()) { - updateNetwork(outputsFolder); - } - if (dumpFileParameters.exportDumpFile()) { - setDumpFile(outputsFolder, dumpFileParameters.dumpFileFolder(), workingDir.getFileName()); - } - setTimeline(outputsFolder); - if (context.withCurves()) { - setCurves(workingDir); - } - } else { - status = DynamicSimulationResult.Status.FAILURE; - statusText = errorMatcher.group().substring(DYNAWO_ERROR_PATTERN.length()); - } - } else { - LOGGER.warn("Error file not found"); - status = DynamicSimulationResult.Status.FAILURE; - statusText = "Dynawo error log file not found"; - } - - return new DynamicSimulationResultImpl(status, statusText, curves, timeline); - } - - private void setDynawoLog(Path outputsFolder, Set specificLogs) throws IOException { - Path logFolder = outputsFolder.resolve(LOGS_FOLDER); - if (Files.exists(logFolder)) { - Path logFile = logFolder.resolve(LOGS_FILENAME); - if (Files.exists(logFile)) { - ReportNode logReportNode = CommonReports.createDynawoLogReportNode(reportNode); - new CsvLogParser().parse(logFile).forEach(e -> CommonReports.reportLogEntry(logReportNode, e)); - } - for (DynaWaltzParameters.SpecificLog specificLog : specificLogs) { - Path specificLogFile = logFolder.resolve(specificLog.getFileName()); - if (Files.exists(specificLogFile)) { - ReportNode logReport = DynawaltzReports.createDynawoSpecificLogReportNode(reportNode, specificLog); - DynawaltzReports.reportSpecificLogEntry(logReport, Files.readString(specificLogFile)); - } - } - } else { - LOGGER.warn("Dynawo logs file not found"); - } - } - - private void updateNetwork(Path outputsFolder) { - Path outputNetworkFile = outputsFolder.resolve(FINAL_STATE_FOLDER).resolve(OUTPUT_IIDM_FILENAME); - if (Files.exists(outputNetworkFile)) { - NetworkResultsUpdater.update(context.getNetwork(), NetworkSerDe.read(outputNetworkFile), context.getDynaWaltzParameters().isMergeLoads()); - } else { - LOGGER.warn("Output IIDM file not found"); - status = DynamicSimulationResult.Status.FAILURE; - statusText = "Dynawo Output IIDM file not found"; - } - } - - private void setDumpFile(Path outputsFolder, Path dumpFileFolder, Path fileName) throws IOException { - Path outputDumpFile = outputsFolder.resolve(FINAL_STATE_FOLDER).resolve(OUTPUT_DUMP_FILENAME); - if (Files.exists(outputDumpFile)) { - Files.copy(outputDumpFile, dumpFileFolder.resolve(fileName + "_" + OUTPUT_DUMP_FILENAME), StandardCopyOption.REPLACE_EXISTING); - } else { - LOGGER.warn("Dump file {} not found, export will be skipped", OUTPUT_DUMP_FILENAME); - } - } - - private void setTimeline(Path outputsFolder) { - DynaWaltzParameters.ExportMode exportMode = context.getDynaWaltzParameters().getTimelineExportMode(); - Path timelineFile = outputsFolder.resolve(DYNAWO_TIMELINE_FOLDER).resolve(TIMELINE_FILENAME + exportMode.getFileExtension()); - if (Files.exists(timelineFile)) { - TimeLineParser parser = switch (exportMode) { - case CSV -> new CsvTimeLineParser(';'); - case TXT -> new CsvTimeLineParser(); - case XML -> new XmlTimeLineParser(); - }; - parser.parse(timelineFile).forEach(e -> timeline.add(new TimelineEvent(e.time(), e.modelName(), e.message()))); - } else { - LOGGER.warn("Timeline file not found"); - } - } - - private void setCurves(Path workingDir) { - Path curvesPath = workingDir.resolve(CURVES_OUTPUT_PATH).toAbsolutePath().resolve(CURVES_FILENAME); - if (Files.exists(curvesPath)) { - TimeSeries.parseCsv(curvesPath, new TimeSeriesCsvConfig(TimeSeriesConstants.DEFAULT_SEPARATOR, false, TimeFormat.FRACTIONS_OF_SECOND)) - .values().forEach(l -> l.forEach(curve -> curves.put(curve.getMetadata().getName(), (DoubleTimeSeries) curve))); - } else { - LOGGER.warn("Curves folder not found"); - status = DynamicSimulationResult.Status.FAILURE; - statusText = "Dynawo curves folder not found"; - } - } - - private void writeInputFiles(Path workingDir) throws IOException { - DynawoUtil.writeIidm(dynawoInput, workingDir.resolve(NETWORK_FILENAME)); - JobsXml.write(workingDir, context); - DydXml.write(workingDir, context); - ParametersXml.write(workingDir, context); - if (context.withCurves()) { - CurvesXml.write(workingDir, context); - } - DumpFileParameters dumpFileParameters = context.getDynaWaltzParameters().getDumpFileParameters(); - if (dumpFileParameters.useDumpFile()) { - Path dumpFilePath = dumpFileParameters.getDumpFilePath(); - if (dumpFilePath != null) { - Files.copy(dumpFilePath, workingDir.resolve(dumpFileParameters.dumpFile()), StandardCopyOption.REPLACE_EXISTING); - } - } - } + ExecutionEnvironment execEnv = new ExecutionEnvironment(Collections.emptyMap(), WORKING_DIR_PREFIX, config.isDebug()); + DynawoUtil.requireDynaMinVersion(execEnv, computationManager, getVersionCommand(config), DynaWaltzConfig.DYNAWALTZ_LAUNCHER_PROGRAM_NAME, false); + DynaWaltzContext context = new DynaWaltzContext(network, workingVariantId, + BlackBoxSupplierUtils.getBlackBoxModelList(dynamicModelsSupplier, network, dsReportNode), + BlackBoxSupplierUtils.getBlackBoxModelList(eventModelsSupplier, network, dsReportNode), + curvesSupplier.get(network), + parameters, + DynaWaltzParameters.load(parameters), + reportNode); + + return computationManager.execute(execEnv, new DynaWaltzHandler(context, getCommand(config), reportNode)); } } diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractBlackBoxModel.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractBlackBoxModel.java index 0df30fdc5..dd91a7c01 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractBlackBoxModel.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractBlackBoxModel.java @@ -86,10 +86,10 @@ public List getVarsMapping() { return Collections.emptyList(); } - protected void writeDynamicAttributes(XMLStreamWriter writer, DynaWaltzContext context) throws XMLStreamException { + protected void writeDynamicAttributes(XMLStreamWriter writer, String parFileName) throws XMLStreamException { writer.writeAttribute("id", getDynamicModelId()); writer.writeAttribute("lib", getLib()); - writer.writeAttribute("parFile", getParFile(context)); + writer.writeAttribute("parFile", parFileName); writer.writeAttribute("parId", getParameterSetId()); } } diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractEquipmentBlackBoxModel.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractEquipmentBlackBoxModel.java index a2b095735..341d6e88b 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractEquipmentBlackBoxModel.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractEquipmentBlackBoxModel.java @@ -42,13 +42,18 @@ public T getEquipment() { @Override public void write(XMLStreamWriter writer, DynaWaltzContext context) throws XMLStreamException { + write(writer, getParFile(context)); + } + + @Override + public void write(XMLStreamWriter writer, String parFileName) throws XMLStreamException { boolean hasVarMapping = !getVarsMapping().isEmpty(); if (hasVarMapping) { writer.writeStartElement(DYN_URI, "blackBoxModel"); } else { writer.writeEmptyElement(DYN_URI, "blackBoxModel"); } - writeDynamicAttributes(writer, context); + writeDynamicAttributes(writer, parFileName); writer.writeAttribute("staticId", getStaticId()); if (hasVarMapping) { MacroStaticReference.writeMacroStaticRef(writer, getLib()); diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractPureDynamicBlackBoxModel.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractPureDynamicBlackBoxModel.java index ccabfb8d3..c22f40400 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractPureDynamicBlackBoxModel.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/AbstractPureDynamicBlackBoxModel.java @@ -38,7 +38,12 @@ public final List getVarsMapping() { @Override public void write(XMLStreamWriter writer, DynaWaltzContext context) throws XMLStreamException { + write(writer, getParFile(context)); + } + + @Override + public void write(XMLStreamWriter writer, String parFileName) throws XMLStreamException { writer.writeEmptyElement(DYN_URI, "blackBoxModel"); - writeDynamicAttributes(writer, context); + writeDynamicAttributes(writer, parFileName); } } diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/BlackBoxModel.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/BlackBoxModel.java index 3dbc510d2..b651db0e7 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/BlackBoxModel.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/BlackBoxModel.java @@ -37,6 +37,8 @@ public interface BlackBoxModel extends Model { void write(XMLStreamWriter writer, DynaWaltzContext context) throws XMLStreamException; + void write(XMLStreamWriter writer, String parFileName) throws XMLStreamException; + void createDynamicModelParameters(DynaWaltzContext context, Consumer parametersAdder); void createNetworkParameter(ParametersSet networkParameters); diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/automationsystems/TapChangerAutomationSystem.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/automationsystems/TapChangerAutomationSystem.java index c756db5a7..b7fa0c237 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/automationsystems/TapChangerAutomationSystem.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/automationsystems/TapChangerAutomationSystem.java @@ -7,7 +7,6 @@ */ package com.powsybl.dynawaltz.models.automationsystems; -import com.powsybl.dynawaltz.DynaWaltzContext; import com.powsybl.dynawaltz.DynawaltzReports; import com.powsybl.dynawaltz.models.AbstractPureDynamicBlackBoxModel; import com.powsybl.dynawaltz.models.TransformerSide; @@ -77,9 +76,9 @@ public List getTapChangerBlockerVarConnections() { } @Override - public void write(XMLStreamWriter writer, DynaWaltzContext context) throws XMLStreamException { + public void write(XMLStreamWriter writer, String parFileName) throws XMLStreamException { if (ConnectionState.CONNECTED == connection) { - super.write(writer, context); + super.write(writer, parFileName); } } diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/automationsystems/TapChangerBlockingAutomationSystem.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/automationsystems/TapChangerBlockingAutomationSystem.java index e066d1043..c6db675c3 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/automationsystems/TapChangerBlockingAutomationSystem.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/automationsystems/TapChangerBlockingAutomationSystem.java @@ -8,7 +8,6 @@ package com.powsybl.dynawaltz.models.automationsystems; import com.powsybl.commons.PowsyblException; -import com.powsybl.dynawaltz.DynaWaltzContext; import com.powsybl.dynawaltz.DynawaltzReports; import com.powsybl.dynawaltz.models.AbstractPureDynamicBlackBoxModel; import com.powsybl.dynawaltz.models.MeasurementPointSuffix; @@ -102,9 +101,9 @@ private List getVarConnectionsWith(ActionConnectionPoint connecte } @Override - public void write(XMLStreamWriter writer, DynaWaltzContext context) throws XMLStreamException { + public void write(XMLStreamWriter writer, String parFileName) throws XMLStreamException { if (isConnected) { - super.write(writer, context); + super.write(writer, parFileName); } } } diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/buses/StandardBus.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/buses/StandardBus.java index 4adea7278..3b965c7e2 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/buses/StandardBus.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/buses/StandardBus.java @@ -7,7 +7,6 @@ */ package com.powsybl.dynawaltz.models.buses; -import com.powsybl.dynawaltz.DynaWaltzContext; import com.powsybl.iidm.network.Bus; import javax.xml.stream.XMLStreamException; @@ -24,7 +23,7 @@ protected StandardBus(String dynamicModelId, Bus bus, String parameterSetId, Str } @Override - protected void writeDynamicAttributes(XMLStreamWriter writer, DynaWaltzContext context) throws XMLStreamException { + protected void writeDynamicAttributes(XMLStreamWriter writer, String parFileName) throws XMLStreamException { writer.writeAttribute("id", getDynamicModelId()); writer.writeAttribute("lib", getLib()); } diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/macroconnections/MacroConnect.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/macroconnections/MacroConnect.java index 5159397c1..66e5f53b4 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/macroconnections/MacroConnect.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/macroconnections/MacroConnect.java @@ -38,6 +38,14 @@ public static List getDefaultAttributesTo() { return List.of(MacroConnectAttribute.of("id2", "NETWORK")); } + public String getId() { + return id; + } + + public List getAttributesFrom() { + return attributesFrom; + } + public void write(XMLStreamWriter writer) throws XMLStreamException { writer.writeEmptyElement(DYN_URI, "macroConnect"); writer.writeAttribute("connector", id); diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/utils/BlackBoxSupplierUtils.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/utils/BlackBoxSupplierUtils.java new file mode 100644 index 000000000..277ef399c --- /dev/null +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/models/utils/BlackBoxSupplierUtils.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2023, 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.dynawaltz.models.utils; + +import com.powsybl.commons.report.ReportNode; +import com.powsybl.dynamicsimulation.DynamicModelsSupplier; +import com.powsybl.dynamicsimulation.EventModelsSupplier; +import com.powsybl.dynawaltz.models.BlackBoxModel; +import com.powsybl.iidm.network.Network; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Laurent Issertial + */ +public final class BlackBoxSupplierUtils { + + private BlackBoxSupplierUtils() { + } + + public static List getBlackBoxModelList(DynamicModelsSupplier dynamicModelsSupplier, Network network, ReportNode reporter) { + return dynamicModelsSupplier.get(network, reporter).stream() + .filter(BlackBoxModel.class::isInstance) + .map(BlackBoxModel.class::cast) + .collect(Collectors.toList()); + } + + public static List getBlackBoxModelList(EventModelsSupplier eventModelsSupplier, Network network, ReportNode reporter) { + return eventModelsSupplier.get(network, reporter).stream() + .filter(BlackBoxModel.class::isInstance) + .map(BlackBoxModel.class::cast) + .collect(Collectors.toList()); + } +} diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/DynaWaltzConstants.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/DynaWaltzConstants.java index 73732baad..497a5fa79 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/DynaWaltzConstants.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/DynaWaltzConstants.java @@ -23,6 +23,10 @@ public final class DynaWaltzConstants { public static final String CURVES_FILENAME = "curves.csv"; + public static final String MULTIPLE_JOBS_FILENAME = "multiple_jobs.xml"; + + public static final String AGGREGATED_RESULTS = "aggregatedResults.xml"; + private DynaWaltzConstants() { } } diff --git a/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/ParametersXml.java b/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/ParametersXml.java index 9263cad20..77b1265e4 100644 --- a/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/ParametersXml.java +++ b/dynawaltz/src/main/java/com/powsybl/dynawaltz/xml/ParametersXml.java @@ -188,7 +188,7 @@ public static void write(Path workingDir, DynaWaltzContext context) { write(List.of(parameters.getSolverParameters()), DynaWaltzParameters.SOLVER_OUTPUT_PARAMETERS_FILE, workingDir, ""); } - private static void write(Collection parametersSets, String filename, Path workingDir, String dynPrefix) { + public static void write(Collection parametersSets, String filename, Path workingDir, String dynPrefix) { Path parametersPath = workingDir.resolve(filename); try (Writer writer = Files.newBufferedWriter(parametersPath, StandardCharsets.UTF_8)) { XMLStreamWriter xmlWriter = XmlStreamWriterFactory.newInstance(writer); diff --git a/dynawaltz/src/test/java/com/powsybl/dynawaltz/AbstractLocalCommandExecutor.java b/dynawaltz/src/test/java/com/powsybl/dynawaltz/AbstractLocalCommandExecutor.java index cd8ccadd4..3112d62aa 100644 --- a/dynawaltz/src/test/java/com/powsybl/dynawaltz/AbstractLocalCommandExecutor.java +++ b/dynawaltz/src/test/java/com/powsybl/dynawaltz/AbstractLocalCommandExecutor.java @@ -17,7 +17,7 @@ /** * @author Marcos de Miguel {@literal } */ -abstract class AbstractLocalCommandExecutor implements LocalCommandExecutor { +public abstract class AbstractLocalCommandExecutor implements LocalCommandExecutor { @Override public void stop(Path workingDir) { diff --git a/dynawaltz/src/test/resources/com/powsybl/config/test/config.yml b/dynawaltz/src/test/resources/com/powsybl/config/test/config.yml index 11eab076f..bc1d37ba4 100644 --- a/dynawaltz/src/test/resources/com/powsybl/config/test/config.yml +++ b/dynawaltz/src/test/resources/com/powsybl/config/test/config.yml @@ -6,6 +6,13 @@ dynawaltz: homeDir: /home/dynawaltz debug: false +dynawo-algorithms: + homeDir: /home/dynawo-algorithms + debug: false + +dynamic-security-analysis-default-parameters: + contingencies-start-time: 10 + dynawaltz-default-parameters: parametersFile: /work/unittests/configModels.par network.parametersFile: /work/unittests/configNetwork.par diff --git a/dynawo-integration-tests/pom.xml b/dynawo-integration-tests/pom.xml index 144a42cad..f54237e3e 100644 --- a/dynawo-integration-tests/pom.xml +++ b/dynawo-integration-tests/pom.xml @@ -42,6 +42,10 @@ com.powsybl powsybl-dynawaltz-dsl + + com.powsybl + powsybl-dynawo-security-analysis + com.powsybl powsybl-ieee-cdf-converter diff --git a/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSecurityAnalysisTest.java b/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSecurityAnalysisTest.java new file mode 100644 index 000000000..56e5d6e45 --- /dev/null +++ b/dynawo-integration-tests/src/test/java/com/powsybl/dynawo/it/DynawoSecurityAnalysisTest.java @@ -0,0 +1,113 @@ +/** + * 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.dynawo.it; + +import com.google.common.io.ByteStreams; +import com.powsybl.commons.datasource.ResourceDataSource; +import com.powsybl.commons.datasource.ResourceSet; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.commons.test.ComparisonUtils; +import com.powsybl.commons.test.TestUtil; +import com.powsybl.contingency.Contingency; +import com.powsybl.dynamicsimulation.DynamicSimulationParameters; +import com.powsybl.dynamicsimulation.groovy.DynamicModelGroovyExtension; +import com.powsybl.dynamicsimulation.groovy.GroovyDynamicModelsSupplier; +import com.powsybl.dynamicsimulation.groovy.GroovyExtension; +import com.powsybl.dynawaltz.DynaWaltzParameters; +import com.powsybl.dynawaltz.DynaWaltzProvider; +import com.powsybl.dynawaltz.parameters.ParametersSet; +import com.powsybl.dynawaltz.xml.ParametersXml; + +import com.powsybl.dynawo.security.DynawoAlgorithmsConfig; +import com.powsybl.dynawo.security.DynawoSecurityAnalysisProvider; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VariantManagerConstants; +import com.powsybl.security.SecurityAnalysisResult; +import com.powsybl.security.dynamic.DynamicSecurityAnalysisParameters; +import com.powsybl.security.dynamic.DynamicSecurityAnalysisProvider; +import com.powsybl.security.dynamic.DynamicSecurityAnalysisRunParameters; +import com.powsybl.security.json.SecurityAnalysisResultSerializer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Laurent Issertial + */ +class DynawoSecurityAnalysisTest extends AbstractDynawoTest { + + private DynamicSecurityAnalysisProvider provider; + + private DynamicSecurityAnalysisParameters parameters; + + private DynaWaltzParameters dynaWaltzParameters; + + @Override + @BeforeEach + void setUp() throws Exception { + super.setUp(); + provider = new DynawoSecurityAnalysisProvider(new DynawoAlgorithmsConfig(Path.of("/dynaflow-launcher"), false)); + parameters = new DynamicSecurityAnalysisParameters() + .setDynamicSimulationParameters(new DynamicSimulationParameters(0, 100)) + .setDynamicContingenciesParameters(new DynamicSecurityAnalysisParameters.ContingenciesParameters(50)); + dynaWaltzParameters = new DynaWaltzParameters(); + parameters.getDynamicSimulationParameters().addExtension(DynaWaltzParameters.class, dynaWaltzParameters); + } + + @Test + void testIeee14() throws IOException { + Network network = Network.read(new ResourceDataSource("IEEE14", new ResourceSet("/ieee14", "IEEE14.iidm"))); + + GroovyDynamicModelsSupplier dynamicModelsSupplier = new GroovyDynamicModelsSupplier( + getResourceAsStream("/ieee14/disconnectline/dynamicModels.groovy"), + GroovyExtension.find(DynamicModelGroovyExtension.class, DynaWaltzProvider.NAME)); + + List modelsParameters = ParametersXml.load(getResourceAsStream("/ieee14/disconnectline/models.par")); + ParametersSet networkParameters = ParametersXml.load(getResourceAsStream("/ieee14/disconnectline/network.par"), "8"); + ParametersSet solverParameters = ParametersXml.load(getResourceAsStream("/ieee14/disconnectline/solvers.par"), "2"); + dynaWaltzParameters.setModelsParameters(modelsParameters) + .setNetworkParameters(networkParameters) + .setSolverParameters(solverParameters) + .setSolverType(DynaWaltzParameters.SolverType.IDA); + + ReportNode reportNode = ReportNode.newRootReportNode() + .withMessageTemplate("root", "Root message") + .build(); + List contingencies = List.of(Contingency.load("_LOAD__11_EC")); + + DynamicSecurityAnalysisRunParameters runParameters = new DynamicSecurityAnalysisRunParameters() + .setComputationManager(computationManager) + .setDynamicSecurityAnalysisParameters(parameters) + .setReportNode(reportNode); + + SecurityAnalysisResult result = provider.run(network, VariantManagerConstants.INITIAL_VARIANT_ID, + dynamicModelsSupplier, n -> contingencies, runParameters) + .join() + .getResult(); + + StringWriter swReporterAs = new StringWriter(); + reportNode.print(swReporterAs); + InputStream refStreamReporterAs = Objects.requireNonNull(getClass().getResourceAsStream("/ieee14/dynamic-security-analysis/timeline_report.txt")); + String refLogExportAs = TestUtil.normalizeLineSeparator(new String(ByteStreams.toByteArray(refStreamReporterAs), StandardCharsets.UTF_8)); + String logExportAs = TestUtil.normalizeLineSeparator(swReporterAs.toString()); + assertEquals(refLogExportAs, logExportAs); + + StringWriter serializedResult = new StringWriter(); + SecurityAnalysisResultSerializer.write(result, serializedResult); + InputStream expected = Objects.requireNonNull(getClass().getResourceAsStream("/ieee14/dynamic-security-analysis/results.json")); + ComparisonUtils.assertTxtEquals(expected, serializedResult.toString()); + } +} diff --git a/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/results.json b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/results.json new file mode 100644 index 000000000..569f08673 --- /dev/null +++ b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/results.json @@ -0,0 +1,51 @@ +{ + "version" : "1.6", + "preContingencyResult" : { + "status" : "CONVERGED", + "limitViolationsResult" : { + "limitViolations" : [ { + "subjectId" : "_BUS____1-BUS____2-1_AC", + "subjectName" : "BUS 1-BUS 2-1", + "limitType" : "CURRENT", + "limitName" : "permanent", + "limit" : 836.74, + "limitReduction" : 1.0, + "value" : 1248.0773003764798, + "side" : "ONE" + } ], + "actionsTaken" : [ ] + }, + "networkResult" : { + "branchResults" : [ ], + "busResults" : [ ], + "threeWindingsTransformerResults" : [ ] + } + }, + "postContingencyResults" : [ { + "contingency" : { + "id" : "_LOAD__11_EC", + "elements" : [ { + "id" : "_LOAD__11_EC", + "type" : "LOAD" + } ] + }, + "status" : "FAILED", + "limitViolationsResult" : { + "limitViolations" : [ ], + "actionsTaken" : [ ] + }, + "networkResult" : { + "branchResults" : [ ], + "busResults" : [ ], + "threeWindingsTransformerResults" : [ ] + }, + "connectivityResult" : { + "createdSynchronousComponentCount" : 0, + "createdConnectedComponentCount" : 0, + "disconnectedLoadActivePower" : 0.0, + "disconnectedGenerationActivePower" : 0.0, + "disconnectedElements" : [ ] + } + } ], + "operatorStrategyResults" : [ ] +} \ No newline at end of file diff --git a/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/timeline_report.txt b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/timeline_report.txt new file mode 100644 index 000000000..d5d5def84 --- /dev/null +++ b/dynawo-integration-tests/src/test/resources/ieee14/dynamic-security-analysis/timeline_report.txt @@ -0,0 +1,52 @@ ++ Root message + + Dynawo dynamic security analysis on network 'ieee14bus' + + Groovy Dynamic Models Supplier + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD__10_EC will be used instead + Model _LOAD__10_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD__11_EC will be used instead + Model _LOAD__11_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD__12_EC will be used instead + Model _LOAD__12_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD__13_EC will be used instead + Model _LOAD__13_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD__14_EC will be used instead + Model _LOAD__14_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD___2_EC will be used instead + Model _LOAD___2_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD___3_EC will be used instead + Model _LOAD___3_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD___9_EC will be used instead + Model _LOAD___9_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD___4_EC will be used instead + Model _LOAD___4_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD___6_EC will be used instead + Model _LOAD___6_EC instantiation successful + + DSL model builder for LoadAlphaBeta + 'dynamicModelId' field is not set, staticId _LOAD___5_EC will be used instead + Model _LOAD___5_EC instantiation successful + + DSL model builder for GeneratorSynchronousFourWindingsProportionalRegulations + 'dynamicModelId' field is not set, staticId _GEN____1_SM will be used instead + Model _GEN____1_SM instantiation successful + + DSL model builder for GeneratorSynchronousFourWindingsProportionalRegulations + 'dynamicModelId' field is not set, staticId _GEN____2_SM will be used instead + Model _GEN____2_SM instantiation successful + + DSL model builder for GeneratorSynchronousFourWindingsProportionalRegulations + 'dynamicModelId' field is not set, staticId _GEN____3_SM will be used instead + Model _GEN____3_SM instantiation successful + + DSL model builder for GeneratorSynchronousThreeWindingsProportionalRegulations + 'dynamicModelId' field is not set, staticId _GEN____6_SM will be used instead + Model _GEN____6_SM instantiation successful + + DSL model builder for GeneratorSynchronousThreeWindingsProportionalRegulations + 'dynamicModelId' field is not set, staticId _GEN____8_SM will be used instead + Model _GEN____8_SM instantiation successful + Contingency '_LOAD__11_EC' diff --git a/dynawo-security-analysis/pom.xml b/dynawo-security-analysis/pom.xml new file mode 100644 index 000000000..6b7b3b919 --- /dev/null +++ b/dynawo-security-analysis/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + com.powsybl + powsybl-dynawo + 2.5.0-SNAPSHOT + + + powsybl-dynawo-security-analysis + Dynawo dynamic security analysis + Dynawo algorithms dynamic security analysis integration module for powsybl + + + + com.powsybl + powsybl-contingency-api + + + com.powsybl + powsybl-dynamic-security-analysis + + + ${project.groupId} + powsybl-dynawaltz + + + ${project.groupId} + powsybl-dynaflow + + + + + com.google.jimfs + jimfs + test + + + org.assertj + assertj-core + test + + + org.slf4j + slf4j-simple + test + + + com.powsybl + powsybl-commons-test + test + + + com.powsybl + powsybl-config-test + test + + + com.powsybl + powsybl-iidm-impl + test + + + com.powsybl + powsybl-iidm-test + test + + + ${project.groupId} + powsybl-dynawaltz + test-jar + test + + + diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/ContingencyEventModels.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/ContingencyEventModels.java new file mode 100644 index 000000000..bf783ec2a --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/ContingencyEventModels.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2023, 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.dynawo.security; + +import com.powsybl.contingency.Contingency; +import com.powsybl.dynawaltz.models.BlackBoxModel; +import com.powsybl.dynawaltz.models.macroconnections.MacroConnect; +import com.powsybl.dynawaltz.models.macroconnections.MacroConnector; +import com.powsybl.dynawaltz.parameters.ParametersSet; + +import java.util.List; +import java.util.Map; + +/** + * @author Laurent Issertial + */ +public record ContingencyEventModels(Contingency contingency, List eventModels, + Map macroConnectorsMap, + List macroConnectList, List eventParameters) { + + public String getId() { + return contingency.getId(); + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynamicSecurityAnalysisReports.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynamicSecurityAnalysisReports.java new file mode 100644 index 000000000..591141ef3 --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynamicSecurityAnalysisReports.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dynawo.security; + +import com.powsybl.commons.report.ReportNode; + +/** + * @author Laurent Issertial {@literal } + */ +public final class DynamicSecurityAnalysisReports { + + private DynamicSecurityAnalysisReports() { + } + + public static ReportNode createDynamicSecurityAnalysisReportNode(ReportNode reportNode, String networkId) { + return reportNode.newReportNode() + .withMessageTemplate("dsa", "Dynawo dynamic security analysis on network '${networkId}'") + .withUntypedValue("networkId", networkId) + .add(); + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoAlgorithmsConfig.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoAlgorithmsConfig.java new file mode 100644 index 000000000..bb073a577 --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoAlgorithmsConfig.java @@ -0,0 +1,43 @@ +/** + * 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.dynawo.security; + +import com.powsybl.commons.config.ModuleConfig; +import com.powsybl.commons.config.PlatformConfig; +import com.powsybl.dynawaltz.DynaWaltzConfig; + +import java.nio.file.Path; + +/** + * @author Laurent Issertial + */ +public class DynawoAlgorithmsConfig extends DynaWaltzConfig { + + public static final String DYNAWO_ALGORITHMS_LAUNCHER_PROGRAM_NAME = "dynawo-algorithms"; + + public static DynawoAlgorithmsConfig load() { + return load(DynawoAlgorithmsConfig::new, DYNAWALTZ_MODULE_NAME); + } + + public static DynawoAlgorithmsConfig load(PlatformConfig platformConfig) { + return load(DynawoAlgorithmsConfig::new, DYNAWALTZ_MODULE_NAME, platformConfig); + } + + public DynawoAlgorithmsConfig(Path homeDir, boolean debug) { + super(homeDir, debug); + } + + private DynawoAlgorithmsConfig(ModuleConfig config) { + super(config); + } + + @Override + public String getProgram() { + return getProgram(DYNAWO_ALGORITHMS_LAUNCHER_PROGRAM_NAME); + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoSecurityAnalysisHandler.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoSecurityAnalysisHandler.java new file mode 100644 index 000000000..91f2bc0cf --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoSecurityAnalysisHandler.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2023, 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.dynawo.security; + +import com.powsybl.commons.exceptions.UncheckedXmlStreamException; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.AbstractExecutionHandler; +import com.powsybl.computation.Command; +import com.powsybl.computation.CommandExecution; +import com.powsybl.computation.ExecutionReport; +import com.powsybl.dynaflow.ContingencyResultsUtils; +import com.powsybl.dynawaltz.xml.DydXml; +import com.powsybl.dynawaltz.xml.DynaWaltzConstants; +import com.powsybl.dynawaltz.xml.JobsXml; +import com.powsybl.dynawaltz.xml.ParametersXml; +import com.powsybl.dynawo.commons.DynawoConstants; +import com.powsybl.dynawo.commons.DynawoUtil; +import com.powsybl.dynawo.commons.NetworkResultsUpdater; +import com.powsybl.dynawo.security.xml.ContingenciesDydXml; +import com.powsybl.dynawo.security.xml.ContingenciesParXml; +import com.powsybl.dynawo.security.xml.MultipleJobsXml; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.serde.NetworkSerDe; +import com.powsybl.security.LimitViolationFilter; +import com.powsybl.security.SecurityAnalysisReport; +import com.powsybl.security.SecurityAnalysisResult; +import com.powsybl.security.interceptors.SecurityAnalysisInterceptor; + +import javax.xml.stream.XMLStreamException; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +import static com.powsybl.dynaflow.SecurityAnalysisConstants.DYNAWO_CONSTRAINTS_FOLDER; +import static com.powsybl.dynawaltz.DynaWaltzConstants.FINAL_STATE_FOLDER; +import static com.powsybl.dynawaltz.DynaWaltzConstants.OUTPUTS_FOLDER; +import static com.powsybl.dynawo.commons.DynawoConstants.DYNAWO_TIMELINE_FOLDER; +import static com.powsybl.dynawo.commons.DynawoUtil.getCommandExecutions; + +/** + * @author Laurent Issertial + */ +public final class DynawoSecurityAnalysisHandler extends AbstractExecutionHandler { + + private final SecurityAnalysisContext context; + private final Command command; + private final Network network; + private final LimitViolationFilter violationFilter; + private final List interceptors; + private final ReportNode reportNode; + + public DynawoSecurityAnalysisHandler(SecurityAnalysisContext context, Command command, + LimitViolationFilter violationFilter, List interceptors, + ReportNode reportNode) { + this.context = context; + this.network = context.getNetwork(); + this.command = command; + this.violationFilter = violationFilter; + this.interceptors = interceptors; + this.reportNode = reportNode; + } + + @Override + public List before(Path workingDir) throws IOException { + network.getVariantManager().setWorkingVariant(context.getWorkingVariantId()); + Path outputNetworkFile = workingDir.resolve(OUTPUTS_FOLDER).resolve(FINAL_STATE_FOLDER).resolve(DynawoConstants.OUTPUT_IIDM_FILENAME); + if (Files.exists(outputNetworkFile)) { + Files.delete(outputNetworkFile); + } + writeInputFiles(workingDir); + return getCommandExecutions(command); + } + + @Override + public SecurityAnalysisReport after(Path workingDir, ExecutionReport report) throws IOException { + super.after(workingDir, report); + context.getNetwork().getVariantManager().setWorkingVariant(context.getWorkingVariantId()); + Path outputNetworkFile = workingDir.resolve(OUTPUTS_FOLDER).resolve(FINAL_STATE_FOLDER).resolve(DynawoConstants.OUTPUT_IIDM_FILENAME); + if (Files.exists(outputNetworkFile)) { + NetworkResultsUpdater.update(context.getNetwork(), NetworkSerDe.read(outputNetworkFile), context.getDynaWaltzParameters().isMergeLoads()); + } + ContingencyResultsUtils.reportContingenciesTimelines(context.getContingencies(), workingDir.resolve(DYNAWO_TIMELINE_FOLDER), reportNode); + + return new SecurityAnalysisReport( + new SecurityAnalysisResult( + ContingencyResultsUtils.getPreContingencyResult(network, violationFilter), + ContingencyResultsUtils.getPostContingencyResults(network, violationFilter, workingDir.resolve(DYNAWO_CONSTRAINTS_FOLDER), context.getContingencies()), + Collections.emptyList()) + ); + } + + private void writeInputFiles(Path workingDir) { + try { + DynawoUtil.writeIidm(network, workingDir.resolve(DynaWaltzConstants.NETWORK_FILENAME)); + JobsXml.write(workingDir, context); + DydXml.write(workingDir, context); + ParametersXml.write(workingDir, context); + MultipleJobsXml.write(workingDir, context); + ContingenciesDydXml.write(workingDir, context); + ContingenciesParXml.write(workingDir, context); + } catch (IOException e) { + throw new UncheckedIOException(e); + } catch (XMLStreamException e) { + throw new UncheckedXmlStreamException(e); + } + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoSecurityAnalysisProvider.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoSecurityAnalysisProvider.java new file mode 100644 index 000000000..a6fe958ed --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/DynawoSecurityAnalysisProvider.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2023, 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.dynawo.security; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.config.PlatformConfig; +import com.powsybl.commons.report.ReportNode; +import com.powsybl.computation.Command; +import com.powsybl.computation.ExecutionEnvironment; +import com.powsybl.computation.SimpleCommandBuilder; +import com.powsybl.contingency.ContingenciesProvider; +import com.powsybl.contingency.Contingency; +import com.powsybl.dynamicsimulation.DynamicModelsSupplier; +import com.powsybl.dynawaltz.DynaWaltzParameters; +import com.powsybl.dynawaltz.DynaWaltzProvider; +import com.powsybl.dynawaltz.models.utils.BlackBoxSupplierUtils; +import com.powsybl.dynawaltz.xml.DynaWaltzConstants; +import com.powsybl.dynawo.commons.DynawoUtil; +import com.powsybl.dynawo.commons.PowsyblDynawoVersion; +import com.powsybl.iidm.network.Network; +import com.powsybl.security.SecurityAnalysisReport; +import com.powsybl.security.dynamic.DynamicSecurityAnalysisParameters; +import com.powsybl.security.dynamic.DynamicSecurityAnalysisProvider; +import com.powsybl.security.dynamic.DynamicSecurityAnalysisRunParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +import static com.powsybl.dynawaltz.DynaWaltzConfig.DYNAWALTZ_LAUNCHER_PROGRAM_NAME; + +/** + * @author Laurent Issertial + */ +@AutoService(DynamicSecurityAnalysisProvider.class) +public class DynawoSecurityAnalysisProvider implements DynamicSecurityAnalysisProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(DynawoSecurityAnalysisProvider.class); + private static final String WORKING_DIR_PREFIX = "dynawaltz_sa_"; + private final DynawoAlgorithmsConfig config; + + public DynawoSecurityAnalysisProvider() { + this(PlatformConfig.defaultConfig()); + } + + public DynawoSecurityAnalysisProvider(PlatformConfig platformConfig) { + this(DynawoAlgorithmsConfig.load(platformConfig)); + } + + public DynawoSecurityAnalysisProvider(DynawoAlgorithmsConfig config) { + this.config = Objects.requireNonNull(config); + } + + @Override + public CompletableFuture run(Network network, String workingVariantId, + DynamicModelsSupplier dynamicModelsSupplier, + ContingenciesProvider contingenciesProvider, + DynamicSecurityAnalysisRunParameters runParameters) { + + if (!runParameters.getMonitors().isEmpty()) { + LOGGER.error("Monitoring is not possible with Dynawo implementation. There will not be supplementary information about monitored equipment."); + } + if (!runParameters.getOperatorStrategies().isEmpty()) { + LOGGER.error("Strategies are not implemented in Dynawo"); + } + if (!runParameters.getActions().isEmpty()) { + LOGGER.error("Actions are not implemented in Dynawo"); + } + + ReportNode dsaReportNode = DynamicSecurityAnalysisReports.createDynamicSecurityAnalysisReportNode(runParameters.getReportNode(), network.getId()); + network.getVariantManager().setWorkingVariant(workingVariantId); + ExecutionEnvironment execEnv = new ExecutionEnvironment(Collections.emptyMap(), WORKING_DIR_PREFIX, config.isDebug()); + DynawoUtil.requireDynaMinVersion(execEnv, runParameters.getComputationManager(), getVersionCommand(config), DYNAWALTZ_LAUNCHER_PROGRAM_NAME, false); + List contingencies = contingenciesProvider.getContingencies(network); + DynamicSecurityAnalysisParameters parameters = runParameters.getDynamicSecurityAnalysisParameters(); + SecurityAnalysisContext context = new SecurityAnalysisContext(network, workingVariantId, + BlackBoxSupplierUtils.getBlackBoxModelList(dynamicModelsSupplier, network, dsaReportNode), + parameters, + DynaWaltzParameters.load(parameters.getDynamicSimulationParameters()), + contingencies); + + return runParameters.getComputationManager().execute(execEnv, new DynawoSecurityAnalysisHandler(context, getCommand(config), runParameters.getFilter(), runParameters.getInterceptors(), dsaReportNode)); + } + + @Override + public String getName() { + return DynaWaltzProvider.NAME; + } + + @Override + public String getVersion() { + return new PowsyblDynawoVersion().getMavenProjectVersion(); + } + + public static Command getCommand(DynawoAlgorithmsConfig config) { + List args = Arrays.asList( + "SA", + "--input", DynaWaltzConstants.MULTIPLE_JOBS_FILENAME, + "--output", DynaWaltzConstants.AGGREGATED_RESULTS); + return new SimpleCommandBuilder() + .id("dynawo_dynamic_sa") + .program(config.getProgram()) + .args(args) + .build(); + } + + public static Command getVersionCommand(DynawoAlgorithmsConfig config) { + List args = Collections.singletonList("--version"); + return new SimpleCommandBuilder() + .id("dynawo_version") + .program(config.getProgram()) + .args(args) + .build(); + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/SecurityAnalysisContext.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/SecurityAnalysisContext.java new file mode 100644 index 000000000..70f00f0ef --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/SecurityAnalysisContext.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2023, 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.dynawo.security; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.contingency.Contingency; +import com.powsybl.contingency.ContingencyElement; +import com.powsybl.dynawaltz.DynaWaltzContext; +import com.powsybl.dynawaltz.DynaWaltzParameters; +import com.powsybl.dynawaltz.models.BlackBoxModel; +import com.powsybl.dynawaltz.models.events.ContextDependentEvent; +import com.powsybl.dynawaltz.models.events.EventDisconnectionBuilder; +import com.powsybl.dynawaltz.models.macroconnections.MacroConnect; +import com.powsybl.dynawaltz.models.macroconnections.MacroConnector; +import com.powsybl.dynawaltz.parameters.ParametersSet; +import com.powsybl.iidm.network.Network; +import com.powsybl.security.dynamic.DynamicSecurityAnalysisParameters; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Laurent Issertial + */ +public class SecurityAnalysisContext extends DynaWaltzContext { + + private final List contingencies; + private final List contingencyEventModels; + + public SecurityAnalysisContext(Network network, String workingVariantId, + List dynamicModels, + DynamicSecurityAnalysisParameters parameters, + DynaWaltzParameters dynaWaltzParameters, + List contingencies) { + super(network, workingVariantId, dynamicModels, List.of(), Collections.emptyList(), + parameters.getDynamicSimulationParameters(), dynaWaltzParameters); + double contingenciesStartTime = parameters.getDynamicContingenciesParameters().getContingenciesStartTime(); + this.contingencies = contingencies; + this.contingencyEventModels = contingencies.stream() + .map(c -> { + List contEventModels = c.getElements().stream() + .map(ce -> { + BlackBoxModel bbm = this.createContingencyEventModel(ce, contingenciesStartTime); + if (bbm instanceof ContextDependentEvent cde) { + cde.setEquipmentHasDynamicModel(this); + } + return bbm; + }) + .collect(Collectors.toList()); + Map macroConnectorsMap = new HashMap<>(); + List macroConnects = new ArrayList<>(); + List parametersSets = new ArrayList<>(contEventModels.size()); + macroConnectionsAdder.setMacroConnectorAdder(macroConnectorsMap::computeIfAbsent); + macroConnectionsAdder.setMacroConnectAdder(macroConnects::add); + for (BlackBoxModel bbm : contEventModels) { + bbm.createMacroConnections(macroConnectionsAdder); + bbm.createDynamicModelParameters(this, parametersSets::add); + } + return new ContingencyEventModels(c, contEventModels, macroConnectorsMap, macroConnects, parametersSets); + }) + .collect(Collectors.toList()); + } + + private BlackBoxModel createContingencyEventModel(ContingencyElement element, double contingenciesStartTime) { + BlackBoxModel bbm = EventDisconnectionBuilder.of(network) + .staticId(element.getId()) + .startTime(contingenciesStartTime) + .build(); + if (bbm == null) { + throw new PowsyblException("Contingency element " + element.getType() + " not supported"); + } + return bbm; + } + + public List getContingencies() { + return contingencies; + } + + public List getContingencyEventModels() { + return contingencyEventModels; + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/ContingenciesDydXml.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/ContingenciesDydXml.java new file mode 100644 index 000000000..9209bc6b6 --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/ContingenciesDydXml.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2023, 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.dynawo.security.xml; + +import com.powsybl.dynawaltz.DynaWaltzContext; +import com.powsybl.dynawaltz.models.BlackBoxModel; +import com.powsybl.dynawaltz.models.macroconnections.MacroConnect; +import com.powsybl.dynawaltz.models.macroconnections.MacroConnector; +import com.powsybl.dynawo.security.ContingencyEventModels; +import com.powsybl.dynawo.security.SecurityAnalysisContext; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Objects; + +/** + * @author Laurent Issertial + */ +public final class ContingenciesDydXml { + + private ContingenciesDydXml() { + } + + public static void write(Path workingDir, SecurityAnalysisContext context) throws IOException, XMLStreamException { + Objects.requireNonNull(workingDir); + for (ContingencyEventModels model : context.getContingencyEventModels()) { + Path file = workingDir.resolve(createDydFileName(model)); + XmlUtil.write(file, context, "dynamicModelsArchitecture", ContingenciesDydXml::writeEvent, model); + } + } + + private static void writeEvent(XMLStreamWriter writer, DynaWaltzContext context, ContingencyEventModels model) throws XMLStreamException { + for (BlackBoxModel ev : model.eventModels()) { + ev.write(writer, ContingenciesParXml.createParFileName(model)); + } + for (MacroConnector mcr : model.macroConnectorsMap().values()) { + mcr.write(writer); + } + for (MacroConnect mc : model.macroConnectList()) { + mc.write(writer); + } + } + + public static String createDydFileName(ContingencyEventModels contingency) { + return contingency.getId() + ".dyd"; + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/ContingenciesParXml.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/ContingenciesParXml.java new file mode 100644 index 000000000..6516d0c01 --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/ContingenciesParXml.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2023, 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.dynawo.security.xml; + +import com.powsybl.dynawaltz.xml.ParametersXml; +import com.powsybl.dynawo.security.ContingencyEventModels; +import com.powsybl.dynawo.security.SecurityAnalysisContext; + +import java.nio.file.Path; +import java.util.Objects; + +import static com.powsybl.dynawaltz.xml.DynaWaltzXmlConstants.DYN_PREFIX; + +/** + * @author Laurent Issertial + */ +public final class ContingenciesParXml { + + private ContingenciesParXml() { + } + + public static void write(Path workingDir, SecurityAnalysisContext context) { + Objects.requireNonNull(workingDir); + for (ContingencyEventModels model : context.getContingencyEventModels()) { + ParametersXml.write(model.eventParameters(), createParFileName(model), workingDir, DYN_PREFIX); + } + } + + public static String createParFileName(ContingencyEventModels contingency) { + return contingency.getId() + ".par"; + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/MultipleJobsXml.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/MultipleJobsXml.java new file mode 100644 index 000000000..da429a7cd --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/MultipleJobsXml.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2023, 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.dynawo.security.xml; + +import com.powsybl.dynawo.security.ContingencyEventModels; +import com.powsybl.dynawo.security.SecurityAnalysisContext; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Objects; + +import static com.powsybl.dynawaltz.xml.DynaWaltzConstants.JOBS_FILENAME; +import static com.powsybl.dynawaltz.xml.DynaWaltzConstants.MULTIPLE_JOBS_FILENAME; + +/** + * @author Laurent Issertial + */ +public final class MultipleJobsXml { + + private MultipleJobsXml() { + } + + public static void write(Path workingDir, SecurityAnalysisContext context) throws IOException, XMLStreamException { + Objects.requireNonNull(workingDir); + Path file = workingDir.resolve(MULTIPLE_JOBS_FILENAME); + + XmlUtil.write(file, context, "multipleJobs", MultipleJobsXml::writeContingencies); + } + + private static void writeContingencies(XMLStreamWriter writer, SecurityAnalysisContext context) throws XMLStreamException { + writer.writeStartElement("scenarios"); + writer.writeAttribute("jobsFile", JOBS_FILENAME); + for (ContingencyEventModels model : context.getContingencyEventModels()) { + writeScenario(writer, model.getId()); + } + writeBaseScenario(writer); + writer.writeEndElement(); + } + + private static void writeScenario(XMLStreamWriter writer, String id) throws XMLStreamException { + writer.writeEmptyElement("scenario"); + writer.writeAttribute("id", id); + writer.writeAttribute("dydFile", id + ".dyd"); + } + + private static void writeBaseScenario(XMLStreamWriter writer) throws XMLStreamException { + writer.writeEmptyElement("scenario"); + writer.writeAttribute("id", "Base"); + } +} diff --git a/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/XmlUtil.java b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/XmlUtil.java new file mode 100644 index 000000000..04c74f928 --- /dev/null +++ b/dynawo-security-analysis/src/main/java/com/powsybl/dynawo/security/xml/XmlUtil.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2023, 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.dynawo.security.xml; + +import com.powsybl.dynawaltz.xml.XmlStreamWriterFactory; +import com.powsybl.dynawo.security.ContingencyEventModels; +import com.powsybl.dynawo.security.SecurityAnalysisContext; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +import static com.powsybl.dynawaltz.xml.DynaWaltzXmlConstants.DYN_PREFIX; +import static com.powsybl.dynawaltz.xml.DynaWaltzXmlConstants.DYN_URI; + +/** + * @author Laurent Issertial + */ +public final class XmlUtil { + + @FunctionalInterface + public interface XmlDynawaltzContingenciesWriter { + void writeContingencies(XMLStreamWriter writer, SecurityAnalysisContext dynaWaltzContext) throws XMLStreamException; + } + + @FunctionalInterface + public interface XmlDynawaltzEventWriter { + void writeEvent(XMLStreamWriter writer, SecurityAnalysisContext dynaWaltzContext, ContingencyEventModels model) throws XMLStreamException; + } + + private XmlUtil() { + } + + public static void write(Path file, SecurityAnalysisContext context, String elementName, XmlDynawaltzEventWriter xmlDynawaltzEventWriter, ContingencyEventModels model) throws IOException, XMLStreamException { + Objects.requireNonNull(file); + Objects.requireNonNull(context); + Objects.requireNonNull(elementName); + Objects.requireNonNull(xmlDynawaltzEventWriter); + + try (Writer writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { + XMLStreamWriter xmlWriter = XmlStreamWriterFactory.newInstance(writer); + try { + xmlWriter.writeStartDocument(StandardCharsets.UTF_8.toString(), "1.0"); + xmlWriter.setPrefix(DYN_PREFIX, DYN_URI); + xmlWriter.writeStartElement(DYN_URI, elementName); + xmlWriter.writeNamespace(DYN_PREFIX, DYN_URI); + + xmlDynawaltzEventWriter.writeEvent(xmlWriter, context, model); + + xmlWriter.writeEndElement(); + xmlWriter.writeEndDocument(); + } finally { + xmlWriter.close(); + } + } + } + + public static void write(Path file, SecurityAnalysisContext context, String elementName, XmlDynawaltzContingenciesWriter xmlDynawaltzWriter) throws IOException, XMLStreamException { + Objects.requireNonNull(file); + Objects.requireNonNull(context); + Objects.requireNonNull(elementName); + Objects.requireNonNull(xmlDynawaltzWriter); + + try (Writer writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { + XMLStreamWriter xmlWriter = XmlStreamWriterFactory.newInstance(writer); + try { + xmlWriter.writeStartDocument(StandardCharsets.UTF_8.toString(), "1.0"); + xmlWriter.writeStartElement(elementName); + xmlWriter.writeNamespace("", DYN_URI); + + xmlDynawaltzWriter.writeContingencies(xmlWriter, context); + + xmlWriter.writeEndElement(); + xmlWriter.writeEndDocument(); + } finally { + xmlWriter.close(); + } + } + } +} diff --git a/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/DynawoAlgorithmsConfigTest.java b/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/DynawoAlgorithmsConfigTest.java new file mode 100644 index 000000000..d7e1e633d --- /dev/null +++ b/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/DynawoAlgorithmsConfigTest.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2020, 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.dynawo.security; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.commons.config.InMemoryPlatformConfig; +import com.powsybl.commons.config.MapModuleConfig; +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 static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Marcos de Miguel {@literal } + */ +class DynawoAlgorithmsConfigTest { + + private InMemoryPlatformConfig platformConfig; + private FileSystem fileSystem; + + @BeforeEach + void setUp() { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + platformConfig = new InMemoryPlatformConfig(fileSystem); + } + + @AfterEach + void tearDown() throws IOException { + fileSystem.close(); + } + + @Test + void checkConfig() { + String homeDir = "homeDir"; + boolean debug = true; + + MapModuleConfig moduleConfig = platformConfig.createModuleConfig("dynawaltz"); + moduleConfig.setStringProperty("homeDir", homeDir); + moduleConfig.setStringProperty("debug", Boolean.toString(debug)); + DynawoAlgorithmsConfig config = DynawoAlgorithmsConfig.load(platformConfig); + assertEquals(homeDir, config.getHomeDir().toString()); + assertEquals(debug, config.isDebug()); + } + +} diff --git a/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/xml/ContingenciesXmlTest.java b/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/xml/ContingenciesXmlTest.java new file mode 100644 index 000000000..55cde9df9 --- /dev/null +++ b/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/xml/ContingenciesXmlTest.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2023, 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.dynawo.security.xml; + +import com.powsybl.contingency.Contingency; +import com.powsybl.dynamicsimulation.DynamicSimulationParameters; +import com.powsybl.dynawaltz.DynaWaltzParameters; +import com.powsybl.dynawo.security.SecurityAnalysisContext; +import com.powsybl.dynawaltz.xml.DynaWaltzTestUtil; +import com.powsybl.security.dynamic.DynamicSecurityAnalysisParameters; +import org.junit.jupiter.api.Test; +import org.xml.sax.SAXException; + +import javax.xml.stream.XMLStreamException; +import java.io.IOException; +import java.util.List; + +/** + * @author Laurent Issertial + */ +class ContingenciesXmlTest extends DynaWaltzTestUtil { + + @Test + void writeDyds() throws SAXException, IOException, XMLStreamException { + DynamicSecurityAnalysisParameters parameters = DynamicSecurityAnalysisParameters.load(); + parameters.setDynamicSimulationParameters(new DynamicSimulationParameters(0, 20)); + parameters.setDynamicContingenciesParameters(new DynamicSecurityAnalysisParameters.ContingenciesParameters(10)); + DynaWaltzParameters dynawaltzParameters = DynaWaltzParameters.load(); + List contingencies = List.of( + Contingency.load("LOAD"), + Contingency.builder("DisconnectLineGenerator") + .addLine("NHV1_NHV2_1") + .addGenerator("GEN2") + .build()); + SecurityAnalysisContext context = new SecurityAnalysisContext(network, network.getVariantManager().getWorkingVariantId(), dynamicModels, parameters, dynawaltzParameters, contingencies); + + ContingenciesDydXml.write(tmpDir, context); + ContingenciesParXml.write(tmpDir, context); + validate("dyd.xsd", "LOAD.xml", tmpDir.resolve("LOAD.dyd")); + validate("dyd.xsd", "DisconnectLineGenerator.xml", tmpDir.resolve("DisconnectLineGenerator.dyd")); + validate("parameters.xsd", "LOAD_par.xml", tmpDir.resolve("LOAD.par")); + validate("parameters.xsd", "DisconnectLineGenerator_par.xml", tmpDir.resolve("DisconnectLineGenerator.par")); + } + +} diff --git a/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/xml/MultiplesJobsXmlTest.java b/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/xml/MultiplesJobsXmlTest.java new file mode 100644 index 000000000..8b8872ff4 --- /dev/null +++ b/dynawo-security-analysis/src/test/java/com/powsybl/dynawo/security/xml/MultiplesJobsXmlTest.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2023, 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.dynawo.security.xml; + +import com.powsybl.contingency.Contingency; +import com.powsybl.dynawaltz.DynaWaltzParameters; +import com.powsybl.dynawo.security.SecurityAnalysisContext; +import com.powsybl.dynawaltz.xml.DynaWaltzConstants; +import com.powsybl.dynawaltz.xml.DynaWaltzTestUtil; +import com.powsybl.security.dynamic.DynamicSecurityAnalysisParameters; +import org.junit.jupiter.api.Test; +import org.xml.sax.SAXException; + +import javax.xml.stream.XMLStreamException; +import java.io.IOException; +import java.util.List; + +/** + * @author Laurent Issertial + */ +class MultiplesJobsXmlTest extends DynaWaltzTestUtil { + + @Test + void writeMultiplesJobs() throws SAXException, IOException, XMLStreamException { + DynamicSecurityAnalysisParameters parameters = DynamicSecurityAnalysisParameters.load(); + DynaWaltzParameters dynawaltzParameters = DynaWaltzParameters.load(); + List contingencies = List.of( + Contingency.load("LOAD"), + Contingency.builder("DisconnectLineGenerator") + .addLine("NHV1_NHV2_1") + .addGenerator("GEN2") + .build()); + SecurityAnalysisContext context = new SecurityAnalysisContext(network, network.getVariantManager().getWorkingVariantId(), dynamicModels, parameters, dynawaltzParameters, contingencies); + + MultipleJobsXml.write(tmpDir, context); + validate("multipleJobs.xsd", "multipleJobs.xml", tmpDir.resolve(DynaWaltzConstants.MULTIPLE_JOBS_FILENAME)); + } + +} diff --git a/dynawo-security-analysis/src/test/resources/DisconnectLineGenerator.xml b/dynawo-security-analysis/src/test/resources/DisconnectLineGenerator.xml new file mode 100644 index 000000000..c62da5520 --- /dev/null +++ b/dynawo-security-analysis/src/test/resources/DisconnectLineGenerator.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/dynawo-security-analysis/src/test/resources/DisconnectLineGenerator_par.xml b/dynawo-security-analysis/src/test/resources/DisconnectLineGenerator_par.xml new file mode 100644 index 000000000..288de90f2 --- /dev/null +++ b/dynawo-security-analysis/src/test/resources/DisconnectLineGenerator_par.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/dynawo-security-analysis/src/test/resources/LOAD.xml b/dynawo-security-analysis/src/test/resources/LOAD.xml new file mode 100644 index 000000000..183517f48 --- /dev/null +++ b/dynawo-security-analysis/src/test/resources/LOAD.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dynawo-security-analysis/src/test/resources/LOAD_par.xml b/dynawo-security-analysis/src/test/resources/LOAD_par.xml new file mode 100644 index 000000000..a1e998f10 --- /dev/null +++ b/dynawo-security-analysis/src/test/resources/LOAD_par.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dynawo-security-analysis/src/test/resources/multipleJobs.xml b/dynawo-security-analysis/src/test/resources/multipleJobs.xml new file mode 100644 index 000000000..da5e3c488 --- /dev/null +++ b/dynawo-security-analysis/src/test/resources/multipleJobs.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dynawo-security-analysis/src/test/resources/multipleJobs.xsd b/dynawo-security-analysis/src/test/resources/multipleJobs.xsd new file mode 100644 index 000000000..41d5ca2be --- /dev/null +++ b/dynawo-security-analysis/src/test/resources/multipleJobs.xsd @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index bf79c56b8..e8168d146 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ dynaflow dynawaltz dynawaltz-dsl + dynawo-security-analysis distribution @@ -140,6 +141,11 @@ powsybl-dynawaltz-dsl ${project.version} + + com.powsybl + powsybl-dynawo-security-analysis + ${project.version} + com.powsybl powsybl-dynawo-integration-tests