From c42b4daefb20e1faacac93c283b8405bd0bbd675 Mon Sep 17 00:00:00 2001 From: Etienne LESOT Date: Fri, 13 Oct 2023 10:12:41 +0200 Subject: [PATCH] add target v sensitivity on current and reactive power Signed-off-by: Etienne LESOT --- cpp/src/bindings.cpp | 4 + cpp/src/pypowsybl-api.h | 12 +++ cpp/src/pypowsybl.cpp | 10 ++ cpp/src/pypowsybl.h | 3 + .../python/commons/PyPowsyblApiHeader.java | 19 ++++ .../java/com/powsybl/python/commons/Util.java | 42 +++++---- .../SensitivityAnalysisCFunctions.java | 19 ++++ .../SensitivityAnalysisContext.java | 93 ++++++++++++------- pypowsybl/_pypowsybl.pyi | 23 +++++ pypowsybl/sensitivity/__init__.py | 1 + .../impl/ac_sensitivity_analysis_result.py | 4 +- pypowsybl/sensitivity/impl/sensitivity.py | 29 ++++++ tests/test_sensitivity_analysis.py | 9 ++ 13 files changed, 214 insertions(+), 54 deletions(-) diff --git a/cpp/src/bindings.cpp b/cpp/src/bindings.cpp index b69e153f92..8901aa2329 100644 --- a/cpp/src/bindings.cpp +++ b/cpp/src/bindings.cpp @@ -581,6 +581,10 @@ PYBIND11_MODULE(_pypowsybl, m) { m.def("add_branch_flow_factor_matrix", &pypowsybl::addBranchFlowFactorMatrix, "Add a branch_flow factor matrix to a sensitivity analysis", py::arg("sensitivity_analysis_context"), py::arg("matrix_id"), py::arg("branches_ids"), py::arg("variables_ids")); + m.def("add_branch_factor_matrix", &pypowsybl::addBranchFactorMatrix, "Add a branch factor matrix to a sensitivity analysis", + py::arg("sensitivity_analysis_context"), py::arg("matrix_id"), py::arg("branches_ids"), py::arg("variables_ids"), + py::arg("contingencies_ids"), py::arg("contingency_context_type"), py::arg("sensitivity_function_type")); + m.def("add_precontingency_branch_flow_factor_matrix", &pypowsybl::addPreContingencyBranchFlowFactorMatrix, "Add a branch_flow factor matrix to a sensitivity analysis", py::arg("sensitivity_analysis_context"), py::arg("matrix_id"), py::arg("branches_ids"), py::arg("variables_ids")); diff --git a/cpp/src/pypowsybl-api.h b/cpp/src/pypowsybl-api.h index 8ff93cae89..a61d88988f 100644 --- a/cpp/src/pypowsybl-api.h +++ b/cpp/src/pypowsybl-api.h @@ -176,6 +176,18 @@ typedef enum { SPECIFIC, } contingency_context_type; +typedef enum { + BRANCH_ACTIVE_POWER_1=0, + BRANCH_CURRENT_1, + BRANCH_REACTIVE_POWER_1, + BRANCH_ACTIVE_POWER_2, + BRANCH_CURRENT_2, + BRANCH_REACTIVE_POWER_2, + BRANCH_ACTIVE_POWER_3, + BRANCH_CURRENT_3, + BRANCH_REACTIVE_POWER_3, +} sensitivity_function_type; + typedef enum { VOLTAGE_LEVEL_TOPOLOGY_CREATION = 0, CREATE_COUPLING_DEVICE, diff --git a/cpp/src/pypowsybl.cpp b/cpp/src/pypowsybl.cpp index ee265a7e2a..9b9f94c596 100644 --- a/cpp/src/pypowsybl.cpp +++ b/cpp/src/pypowsybl.cpp @@ -876,6 +876,16 @@ void addPostContingencyBranchFlowFactorMatrix(const JavaHandle& sensitivityAnaly variableIdPtr.get(), variablesIds.size(), contingenciesIdPtr.get(), contingenciesIds.size(), (char*) matrixId.c_str()); } +void addBranchFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, + const std::vector& variablesIds, const std::vector& contingenciesIds, contingency_context_type ContingencyContextType, sensitivity_function_type sensitivityFunctionType) { + ToCharPtrPtr branchIdPtr(branchesIds); + ToCharPtrPtr variableIdPtr(variablesIds); + ToCharPtrPtr contingenciesIdPtr(contingenciesIds); + callJava(::addBranchFactorMatrix, sensitivityAnalysisContext, branchIdPtr.get(), branchesIds.size(), + variableIdPtr.get(), variablesIds.size(), contingenciesIdPtr.get(), contingenciesIds.size(), + (char*) matrixId.c_str(), ContingencyContextType, sensitivityFunctionType); +} + void setBusVoltageFactorMatrix(const JavaHandle& sensitivityAnalysisContext, const std::vector& busIds, const std::vector& targetVoltageIds) { ToCharPtrPtr busVoltageIdPtr(busIds); diff --git a/cpp/src/pypowsybl.h b/cpp/src/pypowsybl.h index d738bcbf32..401b6a7051 100644 --- a/cpp/src/pypowsybl.h +++ b/cpp/src/pypowsybl.h @@ -393,6 +393,9 @@ void setZones(const JavaHandle& sensitivityAnalysisContext, const std::vector<:: void addBranchFlowFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, const std::vector& variablesIds); +void addBranchFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, + const std::vector& variablesIds, const std::vector& contingenciesIds, contingency_context_type ContingencyContextType, sensitivity_function_type sensitivityFunctionType); + void addPreContingencyBranchFlowFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, const std::vector& variablesIds); diff --git a/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java b/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java index 6aafdc80cd..ebb3374129 100644 --- a/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java +++ b/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java @@ -635,6 +635,25 @@ public enum RemoveModificationType { public static native RemoveModificationType fromCValue(int value); } + @CEnum("sensitivity_function_type") + public enum SensitivityFunctionType { + BRANCH_ACTIVE_POWER_1, + BRANCH_CURRENT_1, + BRANCH_REACTIVE_POWER_1, + BRANCH_ACTIVE_POWER_2, + BRANCH_CURRENT_2, + BRANCH_REACTIVE_POWER_2, + BRANCH_ACTIVE_POWER_3, + BRANCH_CURRENT_3, + BRANCH_REACTIVE_POWER_3; + + @CEnumValue + public native int getCValue(); + + @CEnumLookup + public static native SensitivityFunctionType fromCValue(int value); + } + @CStruct("matrix") public interface MatrixPointer extends PointerBase { diff --git a/java/src/main/java/com/powsybl/python/commons/Util.java b/java/src/main/java/com/powsybl/python/commons/Util.java index 2a1e9a615c..bb8d9ac457 100644 --- a/java/src/main/java/com/powsybl/python/commons/Util.java +++ b/java/src/main/java/com/powsybl/python/commons/Util.java @@ -20,6 +20,7 @@ import com.powsybl.python.commons.PyPowsyblApiHeader.VoltageInitializerObjective; import com.powsybl.python.commons.PyPowsyblApiHeader.VoltageInitializerStatus; import com.powsybl.python.dataframe.CDataframeHandler; +import com.powsybl.sensitivity.SensitivityFunctionType; import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointerPointer; @@ -99,7 +100,7 @@ public static long doCatch(PyPowsyblApiHeader.ExceptionHandlerPointer exceptionH } public static > T doCatch(PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr, - Supplier supplier) { + Supplier supplier) { exceptionHandlerPtr.setMessage(WordFactory.nullPointer()); try { return supplier.get(); @@ -314,17 +315,26 @@ public static DataframeElementType convert(PyPowsyblApiHeader.ElementType type) } } + public static SensitivityFunctionType convert(PyPowsyblApiHeader.SensitivityFunctionType type) { + return switch (type) { + case BRANCH_ACTIVE_POWER_1 -> SensitivityFunctionType.BRANCH_ACTIVE_POWER_1; + case BRANCH_CURRENT_1 -> SensitivityFunctionType.BRANCH_CURRENT_1; + case BRANCH_REACTIVE_POWER_1 -> SensitivityFunctionType.BRANCH_REACTIVE_POWER_1; + case BRANCH_ACTIVE_POWER_2 -> SensitivityFunctionType.BRANCH_ACTIVE_POWER_2; + case BRANCH_CURRENT_2 -> SensitivityFunctionType.BRANCH_CURRENT_2; + case BRANCH_REACTIVE_POWER_2 -> SensitivityFunctionType.BRANCH_REACTIVE_POWER_2; + case BRANCH_ACTIVE_POWER_3 -> SensitivityFunctionType.BRANCH_ACTIVE_POWER_3; + case BRANCH_CURRENT_3 -> SensitivityFunctionType.BRANCH_CURRENT_3; + case BRANCH_REACTIVE_POWER_3 -> SensitivityFunctionType.BRANCH_REACTIVE_POWER_3; + }; + } + public static ContingencyContextType convert(PyPowsyblApiHeader.RawContingencyContextType type) { - switch (type) { - case ALL: - return ContingencyContextType.ALL; - case NONE: - return ContingencyContextType.NONE; - case SPECIFIC: - return ContingencyContextType.SPECIFIC; - default: - throw new PowsyblException("Unknown contingency context type : " + type); - } + return switch (type) { + case ALL -> ContingencyContextType.ALL; + case NONE -> ContingencyContextType.NONE; + case SPECIFIC -> ContingencyContextType.SPECIFIC; + }; } public static PyPowsyblApiHeader.ValidationLevelType convert(ValidationLevel level) { @@ -409,11 +419,11 @@ public static byte[] binaryBufferToBytes(ByteBuffer buffer) { } } - private static final byte[] ZIP_SIGNATURE = new byte[] {0x50, 0x4B, 0x03, 0x04}; - private static final byte[] GZIP_SIGNATURE = new byte[] {0x1F, (byte) 0x8B}; - private static final byte[] XZ_SIGNATURE = new byte[] {(byte) 0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}; - private static final byte[] BZIP2_SIGNATURE = new byte[] {0x42, 0x5A, 0x68}; - private static final byte[] ZSTD_SIGNATURE = new byte[] {0x28, (byte) 0xB5, 0x2F, (byte) 0xFD}; + private static final byte[] ZIP_SIGNATURE = new byte[]{0x50, 0x4B, 0x03, 0x04}; + private static final byte[] GZIP_SIGNATURE = new byte[]{0x1F, (byte) 0x8B}; + private static final byte[] XZ_SIGNATURE = new byte[]{(byte) 0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}; + private static final byte[] BZIP2_SIGNATURE = new byte[]{0x42, 0x5A, 0x68}; + private static final byte[] ZSTD_SIGNATURE = new byte[]{0x28, (byte) 0xB5, 0x2F, (byte) 0xFD}; private static boolean compareSignature(ByteBuffer buffer, byte[] signature) { byte[] header = new byte[signature.length]; diff --git a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java index dcc1d436c8..e8e16d8474 100644 --- a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java +++ b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java @@ -99,6 +99,25 @@ public static void setZones(IsolateThread thread, ObjectHandle sensitivityAnalys }); } + @CEntryPoint(name = "addBranchFactorMatrix") + public static void addBranchFactorMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisContextHandle, + CCharPointerPointer branchIdPtrPtr, int branchIdCount, + CCharPointerPointer variableIdPtrPtr, int variableIdCount, + CCharPointerPointer contingenciesIdPtrPtr, int contingenciesIdCount, + CCharPointer matrixIdPtr, + PyPowsyblApiHeader.RawContingencyContextType contingencyContextType, + PyPowsyblApiHeader.SensitivityFunctionType sensitivityFunctionType, + ExceptionHandlerPointer exceptionHandlerPtr) { + doCatch(exceptionHandlerPtr, () -> { + SensitivityAnalysisContext analysisContext = ObjectHandles.getGlobal().get(sensitivityAnalysisContextHandle); + List branchesIds = toStringList(branchIdPtrPtr, branchIdCount); + List variablesIds = toStringList(variableIdPtrPtr, variableIdCount); + String matrixId = CTypeUtil.toString(matrixIdPtr); + List contingencies = toStringList(contingenciesIdPtrPtr, contingenciesIdCount); + analysisContext.addBranchFactorMatrix(matrixId, branchesIds, variablesIds, contingencies, Util.convert(contingencyContextType), Util.convert(sensitivityFunctionType)); + }); + } + @CEntryPoint(name = "addBranchFlowFactorMatrix") public static void addBranchFlowFactorMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisContextHandle, CCharPointerPointer branchIdPtrPtr, int branchIdCount, diff --git a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisContext.java b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisContext.java index 1277dbe444..2e1eba9cca 100644 --- a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisContext.java +++ b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisContext.java @@ -119,6 +119,16 @@ void addPostContingencyBranchFlowFactorMatrix(String matrixId, List bran addBranchFlowFactorMatrix(matrixId, ContingencyContextType.SPECIFIC, branchesIds, variablesIds, contingencies); } + void addBranchFactorMatrix(String matrixId, List branchesIds, List variablesIds, + List contingencies, ContingencyContextType contingencyContextType, + SensitivityFunctionType sensitivityFunctionType) { + if (branchFlowFactorsMatrix.containsKey(matrixId)) { + throw new PowsyblException("Matrix '" + matrixId + "' already exists."); + } + MatrixInfo info = new MatrixInfo(contingencyContextType, sensitivityFunctionType, branchesIds, variablesIds, contingencies); + branchFlowFactorsMatrix.put(matrixId, info); + } + public void setVariableSets(List variableSets) { this.variableSets = Objects.requireNonNull(variableSets); } @@ -201,63 +211,74 @@ SensitivityAnalysisResultContext run(Network network, SensitivityAnalysisParamet } } - if (matrix.getFunctionType() == SensitivityFunctionType.BRANCH_ACTIVE_POWER_1) { - for (int row = 0; row < rows.size(); row++) { - String variableId = rows.get(row); - Injection injection = getInjection(network, variableId); - for (int column = 0; column < columns.size(); column++) { - String branchId = columns.get(column); - Branch branch = network.getBranch(branchId); - if (branch == null) { - throw new PowsyblException("Branch '" + branchId + "' not found"); - } - if (injection != null) { - for (ContingencyContext cCtx : contingencyContexts) { - handler.onFactor(SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, branchId, - SensitivityVariableType.INJECTION_ACTIVE_POWER, variableId, - false, cCtx); + switch (matrix.getFunctionType()) { + case BRANCH_ACTIVE_POWER_1 -> { + for (String variableId : rows) { + Injection injection = getInjection(network, variableId); + for (String branchId : columns) { + Branch branch = network.getBranch(branchId); + if (branch == null) { + throw new PowsyblException("Branch '" + branchId + "' not found"); } - } else { - TwoWindingsTransformer twt = network.getTwoWindingsTransformer(variableId); - if (twt != null) { - if (twt.getPhaseTapChanger() == null) { - throw new PowsyblException("Transformer '" + variableId + "' is not a phase shifter"); - } + if (injection != null) { for (ContingencyContext cCtx : contingencyContexts) { handler.onFactor(SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, branchId, - SensitivityVariableType.TRANSFORMER_PHASE, variableId, + SensitivityVariableType.INJECTION_ACTIVE_POWER, variableId, false, cCtx); } } else { - HvdcLine hvdcLine = network.getHvdcLine(variableId); - if (hvdcLine != null) { + TwoWindingsTransformer twt = network.getTwoWindingsTransformer(variableId); + if (twt != null) { + if (twt.getPhaseTapChanger() == null) { + throw new PowsyblException("Transformer '" + variableId + "' is not a phase shifter"); + } for (ContingencyContext cCtx : contingencyContexts) { handler.onFactor(SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, branchId, - SensitivityVariableType.HVDC_LINE_ACTIVE_POWER, variableId, + SensitivityVariableType.TRANSFORMER_PHASE, variableId, false, cCtx); } } else { - if (variableSetsById.containsKey(variableId)) { + HvdcLine hvdcLine = network.getHvdcLine(variableId); + if (hvdcLine != null) { for (ContingencyContext cCtx : contingencyContexts) { handler.onFactor(SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, branchId, - SensitivityVariableType.INJECTION_ACTIVE_POWER, variableId, - true, cCtx); + SensitivityVariableType.HVDC_LINE_ACTIVE_POWER, variableId, + false, cCtx); } } else { - throw new PowsyblException("Variable '" + variableId + "' not found"); + if (variableSetsById.containsKey(variableId)) { + for (ContingencyContext cCtx : contingencyContexts) { + handler.onFactor(SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, branchId, + SensitivityVariableType.INJECTION_ACTIVE_POWER, variableId, + true, cCtx); + } + } else { + throw new PowsyblException("Variable '" + variableId + "' not found"); + } } } } } } } - } else if (matrix.getFunctionType() == SensitivityFunctionType.BUS_VOLTAGE) { - for (int row = 0; row < rows.size(); row++) { - String targetVoltageId = rows.get(row); - for (int column = 0; column < columns.size(); column++) { - String busVoltageId = columns.get(column); - handler.onFactor(SensitivityFunctionType.BUS_VOLTAGE, busVoltageId, - SensitivityVariableType.BUS_TARGET_VOLTAGE, targetVoltageId, false, ContingencyContext.all()); + case BUS_VOLTAGE -> { + for (String targetVoltageId : rows) { + for (String busVoltageId : columns) { + handler.onFactor(SensitivityFunctionType.BUS_VOLTAGE, busVoltageId, + SensitivityVariableType.BUS_TARGET_VOLTAGE, targetVoltageId, false, ContingencyContext.all()); + } + } + } + case BRANCH_REACTIVE_POWER_1, BRANCH_REACTIVE_POWER_2, BRANCH_REACTIVE_POWER_3, BRANCH_CURRENT_1, BRANCH_CURRENT_2, BRANCH_CURRENT_3 -> { + for (String targetVoltageId : rows) { + for (String branchId : columns) { + Branch branch = network.getBranch(branchId); + if (branch == null) { + throw new PowsyblException("Branch '" + branchId + "' not found"); + } + handler.onFactor(matrix.getFunctionType(), branchId, + SensitivityVariableType.BUS_TARGET_VOLTAGE, targetVoltageId, false, ContingencyContext.all()); + } } } } diff --git a/pypowsybl/_pypowsybl.pyi b/pypowsybl/_pypowsybl.pyi index 90a3d52590..f7d1030e3c 100644 --- a/pypowsybl/_pypowsybl.pyi +++ b/pypowsybl/_pypowsybl.pyi @@ -55,6 +55,28 @@ class ContingencyContextType: @property def name(self) -> str: ... +class SensitivityFunctionType: + __members__: ClassVar[Dict[str, SensitivityFunctionType]] = ... # read-only + BRANCH_ACTIVE_POWER_1: ClassVar[SensitivityFunctionType] = ... + BRANCH_CURRENT_1: ClassVar[SensitivityFunctionType] = ... + BRANCH_REACTIVE_POWER_1: ClassVar[SensitivityFunctionType] = ... + BRANCH_ACTIVE_POWER_2: ClassVar[SensitivityFunctionType] = ... + BRANCH_CURRENT_2: ClassVar[SensitivityFunctionType] = ... + BRANCH_REACTIVE_POWER_2: ClassVar[SensitivityFunctionType] = ... + BRANCH_ACTIVE_POWER_3: ClassVar[SensitivityFunctionType] = ... + BRANCH_CURRENT_3: ClassVar[SensitivityFunctionType] = ... + BRANCH_REACTIVE_POWER_3: ClassVar[SensitivityFunctionType] = ... + def __init__(self, arg0: int) -> None: ... + def __eq__(self, arg0: object) -> bool: ... + def __getstate__(self) -> int: ... + def __hash__(self) -> int: ... + def __index__(self) -> int: ... + def __int__(self) -> int: ... + def __ne__(self, arg0: object) -> bool: ... + def __setstate__(self, arg0: int) -> None: ... + @property + def name(self) -> str: ... + class PostContingencyResult: @property def contingency_id(self) -> str: ... @@ -593,6 +615,7 @@ def get_working_variant_id(network: JavaHandle) -> str: ... def add_branch_flow_factor_matrix(sensitivity_analysis_context: JavaHandle, matrix_id: str, branches_ids: List[str], variables_ids: List[str]) -> None: ... def add_precontingency_branch_flow_factor_matrix(sensitivity_analysis_context: JavaHandle, matrix_id: str, branches_ids: List[str], variables_ids: List[str]) -> None: ... def add_postcontingency_branch_flow_factor_matrix(sensitivity_analysis_context: JavaHandle, matrix_id: str, branches_ids: List[str], variables_ids: List[str], contingencies_ids: List[str]) -> None: ... +def add_branch_factor_matrix(sensitivity_analysis_context: JavaHandle, matrix_id: str, branches_ids: List[str], variables_ids: List[str], contingencies_ids: List[str], contingency_context_type: ContingencyContextType, sensitivity_function_type: SensitivityFunctionType) -> None: ... def is_config_read() -> bool: ... def get_default_loadflow_provider() -> str: ... def get_default_security_analysis_provider() -> str: ... diff --git a/pypowsybl/sensitivity/__init__.py b/pypowsybl/sensitivity/__init__.py index 5f974a2c07..a7897b90e3 100644 --- a/pypowsybl/sensitivity/__init__.py +++ b/pypowsybl/sensitivity/__init__.py @@ -4,6 +4,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # SPDX-License-Identifier: MPL-2.0 # +from pypowsybl._pypowsybl import SensitivityFunctionType, ContingencyContextType from .impl.util import ( create_empty_zone, create_country_zone, diff --git a/pypowsybl/sensitivity/impl/ac_sensitivity_analysis_result.py b/pypowsybl/sensitivity/impl/ac_sensitivity_analysis_result.py index f2251bd4d9..caa92f6e9f 100644 --- a/pypowsybl/sensitivity/impl/ac_sensitivity_analysis_result.py +++ b/pypowsybl/sensitivity/impl/ac_sensitivity_analysis_result.py @@ -13,9 +13,9 @@ class AcSensitivityAnalysisResult(DcSensitivityAnalysisResult): """ - Represents the result of a AC sensitivity analysis. + Represents the result of an AC sensitivity analysis. - The result contains computed values (so called "reference" values) and sensitivity values + The result contains computed values (so-called "reference" values) and sensitivity values of requested factors, on the base case and on post contingency states. """ diff --git a/pypowsybl/sensitivity/impl/sensitivity.py b/pypowsybl/sensitivity/impl/sensitivity.py index 7a89010128..962ca2cff4 100644 --- a/pypowsybl/sensitivity/impl/sensitivity.py +++ b/pypowsybl/sensitivity/impl/sensitivity.py @@ -6,6 +6,8 @@ # from __future__ import annotations from typing import List, Dict + +from _pypowsybl import ContingencyContextType, SensitivityFunctionType from pypowsybl import _pypowsybl from pypowsybl.security import ContingencyContainer from pypowsybl._pypowsybl import PyPowsyblError @@ -136,3 +138,30 @@ def add_postcontingency_branch_flow_factor_matrix(self, branches_ids: List[str], flatten_variables_ids, contingencies_ids) self.branches_ids[matrix_id] = branches_ids self.branch_data_frame_index[matrix_id] = branch_data_frame_index + + def add_branch_factor_matrix(self, branches_ids: List[str], variables_ids: List[str], contingencies_ids: List[str], + contingency_context_type: ContingencyContextType, + sensitivity_function_type: SensitivityFunctionType, + matrix_id: str = 'default', ) -> None: + """ + Defines branch active power flow factor matrix for specific post contingencies states, with a list of branches IDs and a list of variables. + + A variable could be: + - a network element ID: injections, PSTs, dangling lines and HVDC lines are supported + - a zone ID + - a couple of zone ID to define a transfer between 2 zones + + Args: + branches_ids: IDs of branches for which active power flow sensitivities should be computed + variables_ids: variables which may impact branch flows,to which we should compute sensitivities + contingencies_ids: List of the IDs of the contingencies to simulate + contingency_context_type: + sensitivity_function_type: + matrix_id: The matrix unique identifier, to be used to retrieve the sensibility value + """ + (flatten_variables_ids, branch_data_frame_index) = self._process_variable_ids(variables_ids) + _pypowsybl.add_branch_factor_matrix(self._handle, matrix_id, branches_ids, + flatten_variables_ids, contingencies_ids, contingency_context_type, + sensitivity_function_type) + self.branches_ids[matrix_id] = branches_ids + self.branch_data_frame_index[matrix_id] = branch_data_frame_index diff --git a/tests/test_sensitivity_analysis.py b/tests/test_sensitivity_analysis.py index ab55424368..f14a7c3fe1 100644 --- a/tests/test_sensitivity_analysis.py +++ b/tests/test_sensitivity_analysis.py @@ -10,6 +10,7 @@ import pandas as pd from pypowsybl import PyPowsyblError import pypowsybl.report as rp +from pypowsybl.sensitivity import SensitivityFunctionType, ContingencyContextType TEST_DIR = pathlib.Path(__file__).parent DATA_DIR = TEST_DIR.parent.joinpath('data') @@ -308,3 +309,11 @@ def test_hvdc(): assert {'default': ['HVDC1']} == results.branch_data_frame_index assert {'default': ['LINE_S2S3']} == results.branches_ids pytest.approx(results.get_branch_flows_sensitivity_matrix().loc['HVDC1']['LINE_S2S3'], 0.7824, 0.001) + +def test_add_branch_factor_matrix(): + network = pp.network.create_four_substations_node_breaker_network() + analysis = pp.sensitivity.create_dc_analysis() + analysis.add_branch_factor_matrix(['VLGEN_0'], ['GEN'], [], + ContingencyContextType.NONE, SensitivityFunctionType.BRANCH_CURRENT_1) + result = analysis.run(network) + print(result)