diff --git a/docs/img/sa/nad.png b/docs/img/sa/nad.png new file mode 100644 index 0000000..86b5a0e Binary files /dev/null and b/docs/img/sa/nad.png differ diff --git a/docs/index.md b/docs/index.md index bc3ed77..1b24b62 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,6 +11,7 @@ intellij.md iTools Load flow Sensitivity analysis +Security analysis Topology Diagrams Downscaling @@ -29,6 +30,7 @@ Network modifications in Groovy ## Simulation - [Create the Java code to run power flows](loadflow.md): Learn how to create the Java code to setup and run power flows - [Create the Java code to run sensitivity analyses](sensitivity-analysis.md): Learn how to create the Java code to setup and run sensitivity analyses +- [Security analyses](security-analysis.md): Learn how to use security analyses ## Topology - [Manage bus/breaker and node/breaker topology](topology.md): Learn how to manipulate topological views of the network diff --git a/docs/security-analysis.md b/docs/security-analysis.md new file mode 100644 index 0000000..a7da3eb --- /dev/null +++ b/docs/security-analysis.md @@ -0,0 +1,195 @@ +--- +layout: default +--- + +# Write the Java code to perform security analysis + +A `security analysis` is a simulation run on a `network` to check for violations. +To have more information about the contingencies we want to simulate and the network elements we want to monitor +please refer to the [Powsybl security analysis](inv:powsyblcore:*:*#simulation/security/index) documentation page. + +## What will you build? + +This tutorial presents 4 use cases of security analysis simulation. +Each example follows a brief and straightforward workflow, using input data that includes an XIIDM file and at least one contingency. + +> The three workflows can be summarized as follows: +```text +Network + contingency --> Security Analysis +Network + contingency + operators strategy --> Security Analysis +Network + contingency + limit reduction --> Security Analysis +Network + contingency + state monitor --> Security Analysis +``` + +## Tutorial steps + +Loading the network is a common step for all workflows. Then: + +1. Define a contingency and run the security analysis. +2. Add operator strategies and reference the associated actions. +3. Configure limit reduction parameters. +4. Add state monitor parameters. + +### Import the network from an XML IIDM file + +The network we use here is available in the tutorial's resources and is described in the IIDM format. +Start by adding the following lines in the main function of the tutorial: + +```java +Network network = Network.read("network.xiidm", SecurityAnalysisTutorials.class.getResourceAsStream("/network.xiidm")); +``` +We now have a network in memory. + +The following are shown in the (Network Area Diagram) below. +- 4 `voltage levels` +- There is one `generator` in the voltage levels `VLGEN`. +- There is one `load` on the voltage levels `VLLOAD`. +- The voltage levels `VLHV1` and `VLHV2` they are connected by two parallel lines. + +![Workflow](./img/sa/nad.png){width="75%" .center-image} + +### Create a contingency + +Before creating a contingency, make sure that contingency can actually cause a network violation. +As an example, we will add a limit to the line `NHV1_NHV2_1` to simulate a default violation. + +```java +network.getLine("NHV1_NHV2_1") + .getOrCreateSelectedOperationalLimitsGroup1("DEFAULT") + .newCurrentLimits() + .setPermanentLimit(460) + .add(); +network.getLine("NHV1_NHV2_1").setSelectedOperationalLimitsGroup1("DEFAULT"); +``` + +Next, on the other line `NHV1_NHV2_2`, we can add a contingency. + +```java +Contingency contingency = Contingency.line("NHV1_NHV2_2"); +``` + +Now the security-analysis inputs are prepared, +we can run a security analysis. This is done in the following way. +```java +SecurityAnalysis.run(network, List.of(contingency)); +``` +Here are the corresponding prints in the tutorial: + +```` +:: SecurityAnalysis :: network and contingency +Pre contingency results +Post contingency results + Contingency : NHV1_NHV2_2 + Violation Value: 1008.9287882269946 MW/° + Violation Limit: 460.0 MW/° +Operator strategy results +```` + +### Create an operator strategy with actions + +```java +LoadAction loadAction = new LoadActionBuilder() + .withId("loadActionId") + .withLoadId("LOAD") + .withActivePowerValue(300) + .withRelativeValue(false) + .build(); +GeneratorAction generatorAction = new GeneratorActionBuilder() + .withId("generatorActionId") + .withGeneratorId("GEN") + .withActivePowerValue(300) + .withActivePowerRelativeValue(false) + .build(); + +OperatorStrategy operatorStrategy = new OperatorStrategy("id1", ContingencyContext.specificContingency(contingency.getId()), + List.of(new ConditionalActions("stage1", new TrueCondition(), List.of(loadAction.getId(), generatorAction.getId())))); + +SecurityAnalysisRunParameters parameters = new SecurityAnalysisRunParameters(); +parameters.addOperatorStrategy(operatorStrategy); +parameters.addAction(loadAction); +parameters.addAction(generatorAction); + +// Run security analysis +SecurityAnalysis.run(network, List.of(contingency), parameters); +``` + +Here are the corresponding prints in the tutorial: +```` +:: SecurityAnalysis :: network, contingency, operator strategies and actions +Pre contingency results +Post contingency results + Contingency : NHV1_NHV2_2 + Violation Value: 1008.9287882269946 MW/° + Violation Limit: 460.0 MW/° +Operator strategy results + OperatorStrategy : id1 + Violation Value: 516.0706108379493 MW/° + Violation Limit: 460.0 MW/° +```` + +### Create a limit reduction + +Note that the limit reductions also affect the pre-contingency violations results. + +As an example, we are going to reduce the current limit of `NHV1_NHV2_1` line by 10%. + +```java +// Limit Reduction +LimitReduction limitReduction = LimitReduction.builder(LimitType.CURRENT, 0.9) + .withMonitoringOnly(false) + .withContingencyContext(ContingencyContext.all()) + .build(); +parameters.addLimitReduction(limitReduction); + +// Run security analysis +SecurityAnalysis.run(network, List.of(contingency), parameters); +``` + +Here are the corresponding prints in the tutorial: + +```` +:: SecurityAnalysis :: network, contingency and limit reduction +Pre contingency results + Value: 456.7689759899928 MW/° + Limit: 460.0 MW/° +Post contingency results + Contingency : NHV1_NHV2_2 + Violation Value: 1008.9287882269946 MW/° + Violation Limit: 460.0 MW/° +Operator strategy results +```` + +### Use state monitor + +A `StateMonitor` provides information about the state of network elements we want to monitor such as branch, bus and three-winding transformers... + +As an example, we are going to add state monitor parameter, we will add branch and voltage levels ids that we want to monitor. + +```java + +Contingency contingency = Contingency.line("NHV1_NHV2_2"); +SecurityAnalysisRunParameters parameters = new SecurityAnalysisRunParameters(); +// State Monitor +StateMonitor stateMonitor = new StateMonitor(new ContingencyContext(contingency.getId(), SPECIFIC), + Set.of("NHV1_NHV2_1"), // <= branch id + Set.of("VLGEN", "VLHV1", "VLHV2", "VLLOAD"), // <= Voltage Levels id + Set.of()); +parameters.addMonitor(stateMonitor); + +// Run security analysis +SecurityAnalysis.run(network, List.of(contingency), parameters); +``` + +Here are the corresponding prints in the tutorial: +```` +:: SecurityAnalysis :: network, contingency and state Monitor +Pre contingency results +Post contingency results + Contingency : NHV1_NHV2_2 + branchResult: BranchResult{branchId='NHV1_NHV2_1', p1=610.56215354332, q1=334.0562715296571, i1=1008.9287882269946, p2=-600.9961559564288, q2=-285.3791465506596, i2=1047.8257691455576, flowTransfer=NaN} + busResult: BusResults{voltageLevelId='VLGEN', busId='NGEN', v=24.5, angle=2.3579552596552684} + busResult: BusResults{voltageLevelId='VLHV1', busId='NHV1', v=398.26472467308224, angle=0.0} + busResult: BusResults{voltageLevelId='VLHV2', busId='NHV2', v=366.58481145130054, angle=-7.499211315976122} + busResult: BusResults{voltageLevelId='VLLOAD', busId='NLOAD', v=137.74213838955504, angle=-14.464666247602445} +Operator strategy results +```` \ No newline at end of file diff --git a/pom.xml b/pom.xml index cbc147d..97ec2bd 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,7 @@ sensitivity sld-custom-node topology + security-analysis diff --git a/security-analysis/README.md b/security-analysis/README.md new file mode 100644 index 0000000..2062376 --- /dev/null +++ b/security-analysis/README.md @@ -0,0 +1,26 @@ +# Security Analysis tutorial + +This tutorial aims at computing security analysis on a network. After loading a network then: +- [x] Create a contingency on a line and compute a security analysis. +- [x] Create an operator strategy containing references to actions. +- [x] Create a limit reduction. +- [x] Add a state monitor to monitor elements on the network. + +The complete tutorial is described in the [security analysis](https://powsybl.readthedocs.io/projects/powsybl-tutorials/en/latest/security-analysis.html) powsybl-tutorials documentation. + +# How to install the security analysis simulator +In the tutorial, we use the OpenLoadFlow implementation. Please visit this page: [security analysis](https://powsybl.readthedocs.io/projects/powsybl-core/en/stable/simulation/security/index.html#implementation) in powsybl-core documentation for more information about it. + +# How to configure this tutorial +The configuration file is: +``` +/security-analysis/src/main/resources/config.yml +``` + +# Running the tutorial +You just need to execute the following command lines: +``` +cd /security-analysis/ +mvn clean package exec:exec@run +``` + diff --git a/security-analysis/pom.xml b/security-analysis/pom.xml new file mode 100644 index 0000000..8114137 --- /dev/null +++ b/security-analysis/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + + + com.powsybl.tutorials + powsybl-tutorials + 2.1.0-SNAPSHOT + + + security-analysis + Security analysis tutorial + + + + false + com.powsybl.tutorials.sa.SecurityAnalysisTutorials + + + + + + org.codehaus.mojo + exec-maven-plugin + + + + powsybl.config.dirs + ${project.basedir}/src/main/resources + + + + + + + + + + com.powsybl + powsybl-config-classic + + + com.powsybl + powsybl-iidm-api + + + com.powsybl + powsybl-iidm-impl + + + com.powsybl + powsybl-open-loadflow + + + org.slf4j + slf4j-simple + runtime + + + \ No newline at end of file diff --git a/security-analysis/src/main/java/com/powsybl/tutorials/sa/SecurityAnalysisTutorials.java b/security-analysis/src/main/java/com/powsybl/tutorials/sa/SecurityAnalysisTutorials.java new file mode 100644 index 0000000..11beb98 --- /dev/null +++ b/security-analysis/src/main/java/com/powsybl/tutorials/sa/SecurityAnalysisTutorials.java @@ -0,0 +1,172 @@ +/** + * Copyright (c) 2026, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.tutorials.sa; + +import com.powsybl.action.*; +import com.powsybl.contingency.Contingency; +import com.powsybl.contingency.ContingencyContext; +import com.powsybl.iidm.network.*; +import com.powsybl.security.SecurityAnalysis; +import com.powsybl.security.SecurityAnalysisResult; +import com.powsybl.security.SecurityAnalysisRunParameters; +import com.powsybl.security.condition.TrueCondition; +import com.powsybl.security.limitreduction.LimitReduction; +import com.powsybl.security.monitor.StateMonitor; +import com.powsybl.security.strategy.ConditionalActions; +import com.powsybl.security.strategy.OperatorStrategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Set; + +import static com.powsybl.contingency.ContingencyContextType.SPECIFIC; + +/** + * @author Samir Romdhani {@literal } + */ +public final class SecurityAnalysisTutorials { + private static final Logger LOGGER = LoggerFactory.getLogger(SecurityAnalysisTutorials.class); + + private SecurityAnalysisTutorials() { + } + + public static void main(String[] args) { + // Network and contingency + log(runSecurityAnalysisWithContingency()); + // Network, contingency, operator strategies and actions + log(runSecurityAnalysisWithOperatorStrategyAndActions()); + // Network, contingency and limit reduction + log(runSecurityAnalysisWithLimitReduction()); + // Network, contingency and state monitor + log(runSecurityAnalysisWithStateMonitor()); + } + + public static SecurityAnalysisResult runSecurityAnalysisWithContingency() { + Network network = Network.read("network.xml", SecurityAnalysisTutorials.class.getResourceAsStream("/network.xiidm")); + LOGGER.info(":: SecurityAnalysis :: network and contingency"); + + addLimitInLine1(network); + Contingency contingency = Contingency.line("NHV1_NHV2_2"); + return SecurityAnalysis.run(network, List.of(contingency)).getResult(); + } + + public static SecurityAnalysisResult runSecurityAnalysisWithOperatorStrategyAndActions() { + Network network = Network.read("network.xml", SecurityAnalysisTutorials.class.getResourceAsStream("/network.xiidm")); + LOGGER.info(":: SecurityAnalysis :: network, contingency, operator strategies and actions"); + + addLimitInLine1(network); + Contingency contingency = Contingency.line("NHV1_NHV2_2"); + + LoadAction loadAction = new LoadActionBuilder() + .withId("loadActionId") + .withLoadId("LOAD") + .withActivePowerValue(300) + .withRelativeValue(false) + .build(); + GeneratorAction generatorAction = new GeneratorActionBuilder() + .withId("generatorActionId") + .withGeneratorId("GEN") + .withActivePowerValue(300) + .withActivePowerRelativeValue(false) + .build(); + + OperatorStrategy operatorStrategy = new OperatorStrategy("id1", ContingencyContext.specificContingency(contingency.getId()), + List.of(new ConditionalActions("stage1", new TrueCondition(), List.of(loadAction.getId(), generatorAction.getId())))); + + SecurityAnalysisRunParameters parameters = new SecurityAnalysisRunParameters(); + parameters.addOperatorStrategy(operatorStrategy); + parameters.addAction(loadAction); + parameters.addAction(generatorAction); + return SecurityAnalysis.run(network, List.of(contingency), parameters).getResult(); + } + + public static SecurityAnalysisResult runSecurityAnalysisWithLimitReduction() { + Network network = Network.read("network.xml", SecurityAnalysisTutorials.class.getResourceAsStream("/network.xiidm")); + LOGGER.info(":: SecurityAnalysis :: network, contingency and limit reduction"); + + addLimitInLine1(network); + Contingency contingency = Contingency.line("NHV1_NHV2_2"); + + SecurityAnalysisRunParameters parameters = new SecurityAnalysisRunParameters(); + LimitReduction limitReduction = LimitReduction.builder(LimitType.CURRENT, 0.9) + .withMonitoringOnly(false) + .withContingencyContext(ContingencyContext.all()) + .build(); + parameters.addLimitReduction(limitReduction); + return SecurityAnalysis.run(network, List.of(contingency), parameters).getResult(); + } + + public static SecurityAnalysisResult runSecurityAnalysisWithStateMonitor() { + Network network = Network.read("network.xml", SecurityAnalysisTutorials.class.getResourceAsStream("/network.xiidm")); + LOGGER.info(":: SecurityAnalysis :: network, contingency and state Monitor"); + Contingency contingency = Contingency.line("NHV1_NHV2_2"); + SecurityAnalysisRunParameters parameters = new SecurityAnalysisRunParameters(); + StateMonitor stateMonitor = new StateMonitor(new ContingencyContext(contingency.getId(), SPECIFIC), + Set.of("NHV1_NHV2_1"), // <= branch id + Set.of("VLGEN", "VLHV1", "VLHV2", "VLLOAD"), // <= Voltage Levels id + Set.of()); + parameters.addMonitor(stateMonitor); + return SecurityAnalysis.run(network, List.of(contingency), parameters).getResult(); + } + + private static void log(SecurityAnalysisResult result) { + LOGGER.info("\t Pre contingency results"); + result.getPreContingencyResult().getLimitViolationsResult().getLimitViolations() + .forEach(limitViolation -> { + LOGGER.info("\t\t Value: {} MW/°", limitViolation.getValue()); + LOGGER.info("\t\t Limit: {} MW/°", limitViolation.getLimit()); + }); + result.getPreContingencyResult().getNetworkResult().getBranchResults() + .forEach(branchResult -> LOGGER.info("\t\t branchResult: {}", branchResult.toString())); + result.getPreContingencyResult().getNetworkResult().getBusResults() + .forEach(busResult -> LOGGER.info("\t\t busResult: {}", busResult.toString())); + result.getPreContingencyResult().getNetworkResult().getThreeWindingsTransformerResults() + .forEach(transformerResult -> LOGGER.info("\t\t TWT Result: {}", transformerResult.toString())); + + LOGGER.info("\t Post contingency results"); + result.getPostContingencyResults().forEach(postContingencyResult -> { + LOGGER.info("\t\t Contingency : {}", postContingencyResult.getContingency().getId()); + postContingencyResult.getLimitViolationsResult().getLimitViolations() + .forEach(value -> { + LOGGER.info("\t\t\t Violation Value: {} MW/°", value.getValue()); + LOGGER.info("\t\t\t Violation Limit: {} MW/°", value.getLimit()); + }); + postContingencyResult.getNetworkResult().getBranchResults() + .forEach(branchResult -> LOGGER.info("\t\t branchResult: {}", branchResult.toString())); + postContingencyResult.getNetworkResult().getBusResults() + .forEach(busResult -> LOGGER.info("\t\t busResult: {}", busResult.toString())); + postContingencyResult.getNetworkResult().getThreeWindingsTransformerResults() + .forEach(transformerResult -> LOGGER.info("\t\t TWT Result: {}", transformerResult.toString())); + }); + + LOGGER.info("\t Operator strategy results"); + result.getOperatorStrategyResults().forEach(operatorStrategyResult -> { + LOGGER.info("\t\t OperatorStrategy : {}", operatorStrategyResult.getOperatorStrategy().getId()); + operatorStrategyResult.getLimitViolationsResult().getLimitViolations() + .forEach(value -> { + LOGGER.info("\t\t\t Violation Value: {} MW/°", value.getValue()); + LOGGER.info("\t\t\t Violation Limit: {} MW/°", value.getLimit()); + }); + operatorStrategyResult.getNetworkResult().getBranchResults() + .forEach(branchResult -> LOGGER.info("\t\t branchResult: {}", branchResult.toString())); + operatorStrategyResult.getNetworkResult().getBusResults() + .forEach(busResult -> LOGGER.info("\t\t busResult: {}", busResult.toString())); + operatorStrategyResult.getNetworkResult().getThreeWindingsTransformerResults() + .forEach(transformerResult -> LOGGER.info("\t\t TWT Result: {}", transformerResult.toString())); + }); + } + + private static void addLimitInLine1(Network network) { + network.getLine("NHV1_NHV2_1") + .getOrCreateSelectedOperationalLimitsGroup1("DEFAULT") + .newCurrentLimits() + .setPermanentLimit(460).add(); + network.getLine("NHV1_NHV2_1").setSelectedOperationalLimitsGroup1("DEFAULT"); + } +} diff --git a/security-analysis/src/main/resources/config.yml b/security-analysis/src/main/resources/config.yml new file mode 100644 index 0000000..0379aef --- /dev/null +++ b/security-analysis/src/main/resources/config.yml @@ -0,0 +1,3 @@ +# This file is loaded when `powsybl.config.dirs` points to this resources directory. +security-analysis: + default-impl-name: "OpenLoadFlow" \ No newline at end of file diff --git a/security-analysis/src/main/resources/network.xiidm b/security-analysis/src/main/resources/network.xiidm new file mode 100644 index 0000000..1349874 --- /dev/null +++ b/security-analysis/src/main/resources/network.xiidm @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/security-analysis/src/main/resources/simplelogger.properties b/security-analysis/src/main/resources/simplelogger.properties new file mode 100644 index 0000000..7456f15 --- /dev/null +++ b/security-analysis/src/main/resources/simplelogger.properties @@ -0,0 +1,2 @@ +# Hide INFO logs from openloadflow +org.slf4j.simpleLogger.log.com.powsybl.openloadflow=warn