Skip to content

Add operator strategies and remedial actions from a JSON file #978

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 72 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
bb6e8c3
[failed] added java and python impl for reading json contingency list…
Feb 26, 2025
5332784
new function for reading json files in contingency container impl
Mar 6, 2025
9014a4d
fix import
Mar 6, 2025
8cc01de
new add contingency from json file implementation
Mar 10, 2025
a8ba55c
fix add contingency from json file implementation
Mar 10, 2025
4bf1689
fixed add contingency from json file implementation
Mar 12, 2025
058e7d3
fixed add contingency from json file implementation
Mar 12, 2025
759dc0e
fixed venv
Mar 12, 2025
4bc688b
fixed error handling
Mar 12, 2025
c0c0d45
fixed error handling
Mar 12, 2025
2de47df
fixed add contingency impl and error handling
Mar 12, 2025
5a04d95
fix error in implementation
Mar 13, 2025
fdcb324
fix error in implementation
Mar 13, 2025
fdde7d4
fix error in test for json contingencies
Mar 13, 2025
6e312f7
update of add json contingencies function with error handling
Mar 13, 2025
00328dd
update of add json contingencies function with error handling
Mar 13, 2025
b22a043
fix checkstyle violation
Mar 13, 2025
2344c34
test with objects.requirenonnull
Mar 24, 2025
6b7b48a
test with try catch path initializer
Mar 24, 2025
599a5f7
test with try catch path initializer
Mar 24, 2025
21b666d
add test file contingencies.json
Mar 24, 2025
6939e17
deleted unused field
Mar 24, 2025
5fc4b63
deleted unused field
Mar 24, 2025
eeadfd4
fixed python impl of add_contingency_from_json_file function
Mar 24, 2025
c6cab0c
add python tests
Mar 24, 2025
a493ec8
rewrite of the python impl of the add_contingency_from_json_file func…
Mar 26, 2025
fbae2c7
rewrite python tests
Mar 27, 2025
42177ab
successful test to add contingencies from json file
Mar 31, 2025
ee6374a
data file contingencies.json
Mar 31, 2025
c22c268
add impl and test of addActionFromJsonFile and addOperatorStrategyFro…
Apr 2, 2025
6797cba
fix typo
Apr 2, 2025
9893a56
python impl of both function
Apr 2, 2025
3d72bd8
python tests
Apr 3, 2025
e9ad593
fix powsybl-cpp method
Apr 3, 2025
8b58fe8
clean unused imports
Apr 3, 2025
442a813
changing network
Apr 3, 2025
ab44563
add tests for operator strategies and documentation
Apr 7, 2025
3de29c2
changed test files
Apr 7, 2025
6a39902
Fix Network.create_operational_limits docstring (#956)
jeandemanged Feb 27, 2025
edb7490
Clean import config creation (#965)
geofjamg Mar 6, 2025
c15c626
Add supported extensions to documentation (#949)
HugoKulesza Mar 7, 2025
0a3119b
Fix NPE + clarify error message for wrong extension names (#952)
HugoKulesza Mar 7, 2025
01adbe9
Topovec is wrong when elements are lost by propagation (#966)
geofjamg Mar 10, 2025
39f2dac
Change minimum version of cmake to reflect use of cmake_path (#967)
HugoKulesza Mar 10, 2025
7091096
List supported actions in SA documentation (#957)
alicecaron Mar 10, 2025
aff268c
Add "Build the doc" section in the readme (#941)
alicecaron Mar 11, 2025
0bf8b42
Expose voltage angles to Grid2op backend (#968)
geofjamg Mar 14, 2025
d6be59f
Nullable specific parameters category key (#969)
Lisrte Mar 18, 2025
50f4823
adds a NadProfile param to the get NAD and write NAD methods (#942)
CBiasuzzi Mar 21, 2025
0718eb5
Add DiscreteMeasurements extension dataframe (#975)
geofjamg Mar 27, 2025
8373d33
Add documentation about RAO beta implementation (#958)
alicecaron Mar 31, 2025
eaaae6a
Switch snapshot ci to new app and rework notification design (#938)
rolnico Apr 3, 2025
bad6a9a
Adds more customization parameters to the NadProfile (#960)
CBiasuzzi Apr 3, 2025
36dcb8c
Fix subscripts in per unit documentation (#962)
feldmarshall Apr 3, 2025
c6f1ffc
Update to PowSyBl dependencies 2025.0.0 (#974)
geofjamg Apr 4, 2025
6d86170
Adding terminals connection action (#953)
HugoKulesza Apr 4, 2025
8605861
Add voltage_level_details parameter (#963)
So-Fras Apr 4, 2025
6e174c9
Fail early if crac is not valid (#979)
obrix Apr 7, 2025
8645d40
extends NadProfile with style customizations (#976)
CBiasuzzi Apr 7, 2025
84e46ab
Add JSON file content to ContingencyContainer (#959)
nao1345678 Apr 7, 2025
0019d17
Bump to v1.11.0
HugoKulesza Apr 7, 2025
0bf09d8
Bump to v1.12.0.dev1
HugoKulesza Apr 7, 2025
52fadb4
fix upload of wheels (force upgrade packaging) (#981)
HugoKulesza Apr 7, 2025
9bbad75
fix syntax error
Apr 9, 2025
6a50f50
changed test files and python test
Apr 10, 2025
28e9ee9
deleted tests for c functions
Apr 10, 2025
b7a0dbd
deleted unused imports
Apr 10, 2025
1f83779
Merge branch 'main' into json-inputs-for-operator-strategies-and-actions
nao1345678 Apr 10, 2025
457b12f
add deleted function impl add operator strategy from json fie
Apr 10, 2025
2873cbd
add files to gitignore
Apr 16, 2025
8b624c8
delete unused import
Apr 16, 2025
de83cb7
delete unused import
Apr 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,3 @@ build/*
tests/_trial_temp/*

*.orig

# Documentation generated files (autosummary)
docs/reference/api/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to remove this here

9 changes: 9 additions & 0 deletions cpp/powsybl-cpp/powsybl-cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,15 @@ void addOperatorStrategy(const JavaHandle& analysisContext, std::string operator
conditionType, subjectIdsPtr.get(), subjectIds.size(), violationTypesPtr.get(), violationTypesFilters.size());
}

void addActionFromJsonFile(const JavaHandle& analysisContext, const std::string& jsonFilePath) {
PowsyblCaller::get()->callJava(::addActionFromJsonFile, analysisContext, (char*) jsonFilePath.data());
}

void addOperatorStrategyFromJsonFile(const JavaHandle& analysisContext, const std::string& jsonFilePath) {
PowsyblCaller::get()->callJava(::addOperatorStrategyFromJsonFile, analysisContext, (char*) jsonFilePath.data());
}


::zone* createZone(const std::string& id, const std::vector<std::string>& injectionsIds, const std::vector<double>& injectionsShiftKeys) {
auto z = new ::zone;
z->id = copyStringToCharPtr(id);
Expand Down
4 changes: 4 additions & 0 deletions cpp/powsybl-cpp/powsybl-cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@ void addTerminalsConnectionAction(const JavaHandle& analysisContext, const std::
void addOperatorStrategy(const JavaHandle& analysisContext, std::string operatorStrategyId, std::string contingencyId, const std::vector<std::string>& actionsIds,
condition_type conditionType, const std::vector<std::string>& subjectIds, const std::vector<violation_type>& violationTypesFilters);

void addActionFromJsonFile(const JavaHandle& analysisContext, const std::string& jsonFilePath);

void addOperatorStrategyFromJsonFile(const JavaHandle& analysisContext, const std::string& jsonFilePath);

void setZones(const JavaHandle& sensitivityAnalysisContext, const std::vector<::zone*>& zones);

void addFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector<std::string>& branchesIds,
Expand Down
6 changes: 6 additions & 0 deletions cpp/pypowsybl-cpp/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,12 @@ PYBIND11_MODULE(_pypowsybl, m) {
py::arg("analysis_context"), py::arg("operator_strategy_id"), py::arg("contingency_id"), py::arg("action_ids"),
py::arg("condition_type"), py::arg("subject_ids"), py::arg("violation_types"));

m.def("add_action_from_json_file", &pypowsybl::addActionFromJsonFile, "Add actions from JSON file.",
py::arg("analysis_context"), py::arg("path_to_json_file"));

m.def("add_operator_strategy_from_json_file", &pypowsybl::addOperatorStrategyFromJsonFile, "Add operator strategies from JSON file.",
py::arg("analysis_context"), py::arg("path_to_json_file"));

py::enum_<pypowsybl::LimitType>(m, "LimitType")
.value("ACTIVE_POWER", pypowsybl::LimitType::ACTIVE_POWER)
.value("APPARENT_POWER", pypowsybl::LimitType::APPARENT_POWER)
Expand Down
14 changes: 14 additions & 0 deletions data/ActionFileTestV1.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"version" : "1.0",
"actions" : [ {
"type" : "SWITCH",
"id" : "id1",
"switchId" : "S1VL2_LCC1_BREAKER",
"open" : true
}, {
"type" : "SWITCH",
"id" : "id2",
"switchId" : "S1VL2_BBS2_COUPLER_DISCONNECTOR",
"open" : true
}]
}
15 changes: 15 additions & 0 deletions data/OperatorStrategyFileTestV1.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version" : "1.1",
"operatorStrategies" : [ {
"id" : "id1",
"contingencyContextType" : "SPECIFIC",
"contingencyId" : "contingency",
"conditionalActions" : [ {
"id" : "stage1",
"condition" : {
"type" : "TRUE_CONDITION"
},
"actionIds" : [ "id1", "id2" ]
} ]
}]
}
47 changes: 46 additions & 1 deletion docs/user_guide/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ Adding input data from JSON files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It is possible to add the input data of a security analysis using JSON files.
For now, only the contingencies can be added this way, using the `add_contingencies_from_json_file` method.
The contingencies can be added this way, using the `add_contingencies_from_json_file` method.

An example of a valid JSON contingency file is the following :

.. code-block:: JSON
Expand All @@ -175,3 +176,47 @@ An example of a valid JSON contingency file is the following :
} ]
} ]
}


From now on, it is possible to add the remedial actions using JSON files too, using the `add_actions_from_json_file` method.
The following example is a valid JSON file input for this method :

.. code-block:: JSON

{
"version" : "1.0",
"actions" : [ {
"type" : "SWITCH",
"id" : "id1",
"switchId" : "S1VL2_LCC1_BREAKER",
"open" : true
}, {
"type" : "SWITCH",
"id" : "id2",
"switchId" : "S1VL2_BBS2_COUPLER_DISCONNECTOR",
"open" : true
}]
}


Additionally, you can add operator strategies from JSON data, using the `add_operator_strategies_from_json_file` method.
The following example is a valid JSON file input for this method :

.. code-block:: JSON

{
"version" : "1.1",
"operatorStrategies" : [ {
"id" : "id1",
"contingencyContextType" : "SPECIFIC",
"contingencyId" : "contingency",
"conditionalActions" : [ {
"id" : "stage1",
"condition" : {
"type" : "TRUE_CONDITION"
},
"actionIds" : [ "id1", "id2" ]
} ]
}]
}

Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,17 @@ public static void addShuntCompensatorPositionAction(IsolateThread thread, Objec
});
}

@CEntryPoint(name = "addActionFromJsonFile")
public static void addActionFromJsonFile(IsolateThread thread, ObjectHandle securityAnalysisContextHandle,
CCharPointer jsonFilePath, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) {
doCatch(exceptionHandlerPtr, () -> {
SecurityAnalysisContext analysisContext = ObjectHandles.getGlobal().get(securityAnalysisContextHandle);
String stringPath = CTypeUtil.toString(jsonFilePath);
Path path = Paths.get(stringPath);
analysisContext.addActionFromJsonFile(path);
});
}

@CEntryPoint(name = "addTerminalsConnectionAction")
public static void addTerminalsConnectionAction(IsolateThread thread, ObjectHandle securityAnalysisContextHandle,
CCharPointer actionId, CCharPointer elementId, PyPowsyblApiHeader.ThreeSideType side,
Expand Down Expand Up @@ -500,6 +511,17 @@ public static void addOperatorStrategy(IsolateThread thread, ObjectHandle securi
});
}

@CEntryPoint(name = "addOperatorStrategyFromJsonFile")
public static void addOperatorStrategyFromJsonFile(IsolateThread thread, ObjectHandle securityAnalysisContextHandle,
CCharPointer jsonFilePath, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) {
doCatch(exceptionHandlerPtr, () -> {
SecurityAnalysisContext analysisContext = ObjectHandles.getGlobal().get(securityAnalysisContextHandle);
String stringPath = CTypeUtil.toString(jsonFilePath);
Path path = Paths.get(stringPath);
analysisContext.addOperatorStrategyFromJsonFile(path);
});
}

private static Condition buildCondition(PyPowsyblApiHeader.ConditionType conditionType,
CCharPointerPointer subjectIds, int subjectIdsCount,
CIntPointer violationTypes, int violationTypesCount) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.powsybl.python.security;

import com.powsybl.action.Action;
import com.powsybl.action.ActionList;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.contingency.ContingenciesProvider;
import com.powsybl.iidm.network.Network;
Expand All @@ -16,7 +17,10 @@
import com.powsybl.security.*;
import com.powsybl.security.monitor.StateMonitor;
import com.powsybl.security.strategy.OperatorStrategy;
import com.powsybl.security.strategy.OperatorStrategyList;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -45,6 +49,26 @@ SecurityAnalysisResult run(Network network, SecurityAnalysisParameters securityA
return report.getResult();
}

void addActionFromJsonFile(Path path) {
if (Files.exists(path)) {
ActionList actionList;
actionList = ActionList.readJsonFile(path);
actions.addAll(actionList.getActions());
} else {
throw new SecurityException("No actions found in " + path);
}
}

void addOperatorStrategyFromJsonFile(Path path) {
if (Files.exists(path)) {
OperatorStrategyList operatorStrategyList;
operatorStrategyList = OperatorStrategyList.read(path);
operatorStrategies.addAll(operatorStrategyList.getOperatorStrategies());
} else {
throw new SecurityException("No operatorStrategies found in " + path);
}
}

void addAction(Action action) {
actions.add(action);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.assertj.core.data.Offset;
import org.junit.jupiter.api.Test;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is useful ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't add these imports, and after a quick check they seem necessary.

import java.nio.file.FileSystem;
import java.util.Collections;
import java.util.List;

Expand All @@ -33,6 +34,8 @@
*/
class SecurityAnalysisTest {

protected FileSystem fileSystem;

@Test
void testStateMonitors() {
SecurityAnalysisContext analysisContext = new SecurityAnalysisContext();
Expand Down
14 changes: 14 additions & 0 deletions java/pypowsybl/src/test/resources/ActionFileTestV1.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"version" : "1.0",
"actions" : [ {
"type" : "SWITCH",
"id" : "id1",
"switchId" : "S1VL1_LD1_BREAKER",
"open" : true
}, {
"type" : "SWITCH",
"id" : "id2",
"switchId" : "S1VL2_BBS2_COUPLER_DISCONNECTOR",
"open" : true
}]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version" : "1.1",
"operatorStrategies" : [ {
"id" : "id1",
"contingencyContextType" : "SPECIFIC",
"contingencyId" : "First contingency",
"conditionalActions" : [ {
"id" : "stage1",
"condition" : {
"type" : "TRUE_CONDITION"
},
"actionIds" : [ "id1", "id2" ]
} ]
}]
}
3 changes: 2 additions & 1 deletion pypowsybl/_pypowsybl.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from logging import Logger
from typing import ClassVar, Dict, Iterator, List, Sequence, Optional, Union

from numpy import ndarray

class ArrayStruct:
Expand Down Expand Up @@ -807,6 +806,8 @@ def add_ratio_tap_changer_position_action(security_analysis_context: JavaHandle,
def add_shunt_compensator_position_action(security_analysis_context: JavaHandle, action_id: str, shunt_id: str, section: int) -> None: ...
def add_terminals_connection_action(security_analysis_context: JavaHandle, action_id: str, element_id: str, side: Side, opening: bool) -> None: ...
def add_operator_strategy(security_analysis_context: JavaHandle, operator_strategy_id: str, contingency_id: str, action_ids: List[str], condition_type: ConditionType, violation_subject_ids: List[str], violation_types: List[ViolationType]) -> None: ...
def add_action_from_json_file(security_analysis_context: JavaHandle, path_to_json_file: str) -> None: ...
def add_operator_strategy_from_json_file(security_analysis_context: JavaHandle, path_to_json_file: str) -> None: ...
def clone_variant(network: JavaHandle, src: str, variant: str, may_overwrite: bool) -> None: ...
def create_dataframe(columns_values: list, columns_names: List[str], columns_types: List[int], is_index: List[bool]) -> Dataframe: ...
def create_element(network: JavaHandle, dataframes: List[Optional[Dataframe]], element_type: ElementType) -> None: ...
Expand Down
20 changes: 20 additions & 0 deletions pypowsybl/security/impl/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,23 @@ def add_operator_strategy(self, operator_strategy_id: str, contingency_id: str,
if violation_subject_ids is None:
violation_subject_ids = []
_pypowsybl.add_operator_strategy(self._handle, operator_strategy_id, contingency_id, action_ids, condition_type, violation_subject_ids, violation_types)

def add_actions_from_json_file(self, path_to_json_file: str) -> None:
"""
Add any kinds of actions by reading them from a JSON file.

Args:
path_to_json_file: the path to the JSON file in which we extract the actions' data.
"""

_pypowsybl.add_action_from_json_file(self._handle, path_to_json_file)

def add_operator_strategies_from_json_file(self, path_to_json_file: str) -> None:
"""
Add operator strategies by reading them from a JSON file.

Args:
path_to_json_file: the path to the JSON file in which we extract the operator strategies' data.
"""

_pypowsybl.add_operator_strategy_from_json_file(self._handle, path_to_json_file)
9 changes: 9 additions & 0 deletions tests/test_security_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,15 @@ def test_add_contingencies_from_json_file():
assert 'contingency' in sa_result.post_contingency_results.keys()
assert 'contingency2' in sa_result.post_contingency_results.keys()

def test_add_operator_strategies_and_actions_from_json_file():
n = pp.network.create_four_substations_node_breaker_network()
sa = pp.security.create_analysis()
sa.add_single_element_contingency('LINE_S2S3', 'contingency')
sa.add_actions_from_json_file(str(DATA_DIR.joinpath('ActionFileTestV1.0.json')))
sa.add_operator_strategies_from_json_file(str(DATA_DIR.joinpath('OperatorStrategyFileTestV1.0.json')))
sa_result = sa.run_dc(n)
assert 'id1' in sa_result.operator_strategy_results.keys()

def test_terminal_connection_action():
n = pp.network.create_eurostag_tutorial_example1_network()
sa = pp.security.create_analysis()
Expand Down
Loading