diff --git a/cpp/src/bindings.cpp b/cpp/src/bindings.cpp index 0c97e26e8..9fe4d9a7f 100644 --- a/cpp/src/bindings.cpp +++ b/cpp/src/bindings.cpp @@ -766,4 +766,45 @@ PYBIND11_MODULE(_pypowsybl, m) { m.def("get_network_modification_metadata_with_element_type", &pypowsybl::getModificationMetadataWithElementType, "Get network modification metadata with element type", py::arg("network_modification_type"), py::arg("element_type")); m.def("create_network_modification", ::createNetworkModification, "Create and apply network modification", py::arg("network"), py::arg("dataframe"), py::arg("network_modification_type"), py::arg("raise_exception"), py::arg("reporter")); + + py::enum_(m, "ShortCircuitStudyType", "Indicates the type of short circuit study") + .value("SUB_TRANSIENT", pypowsybl::ShortCircuitStudyType::SUB_TRANSIENT, + "It is the first stage of the short circuit, right when the fault happens. The subtransient reactance of generators will be used.") + .value("TRANSIENT", pypowsybl::ShortCircuitStudyType::TRANSIENT, + "The second stage of the short circuit, before the system stabilizes. The transient reactance of generators will be used.") + .value("STEADY_STATE", pypowsybl::ShortCircuitStudyType::STEADY_STATE, + "The last stage of the short circuit, once all transient effects are gone."); + + py::class_(m, "ShortCircuitAnalysisParameters") + .def(py::init(&pypowsybl::createShortCircuitAnalysisParameters)) + .def_readwrite("with_voltage_result", &pypowsybl::ShortCircuitAnalysisParameters::with_voltage_result) + .def_readwrite("with_feeder_result", &pypowsybl::ShortCircuitAnalysisParameters::with_feeder_result) + .def_readwrite("with_limit_violations", &pypowsybl::ShortCircuitAnalysisParameters::with_limit_violations) + .def_readwrite("study_type", &pypowsybl::ShortCircuitAnalysisParameters::study_type) + .def_readwrite("with_fortescue_result", &pypowsybl::ShortCircuitAnalysisParameters::with_fortescue_result) + .def_readwrite("min_voltage_drop_proportional_threshold", &pypowsybl::ShortCircuitAnalysisParameters::min_voltage_drop_proportional_threshold) + .def_readwrite("provider_parameters_keys", &pypowsybl::ShortCircuitAnalysisParameters::provider_parameters_keys) + .def_readwrite("provider_parameters_values", &pypowsybl::ShortCircuitAnalysisParameters::provider_parameters_values); + + m.def("set_default_shortcircuit_analysis_provider", &pypowsybl::setDefaultShortCircuitAnalysisProvider, "Set default short-circuit analysis provider", py::arg("provider")); + m.def("get_default_shortcircuit_analysis_provider", &pypowsybl::getDefaultShortCircuitAnalysisProvider, "Get default short-circuit analysis provider"); + m.def("get_shortcircuit_provider_names", &pypowsybl::getShortCircuitAnalysisProviderNames, "Get supported short-circuit analysis providers"); + m.def("get_shortcircuit_provider_parameters_names", &pypowsybl::getShortCircuitAnalysisProviderParametersNames, "get provider parameters for a short-circuit analysis provider", py::arg("provider")); + m.def("create_shortcircuit_analysis", &pypowsybl::createShortCircuitAnalysis, "Create a short-circuit analysis"); + m.def("run_shortcircuit_analysis", &pypowsybl::runShortCircuitAnalysis, "Run a short-circuit analysis", py::call_guard(), + py::arg("shortcircuit_analysis_context"), py::arg("network"), py::arg("parameters"), + py::arg("provider"), py::arg("reporter")); + + py::enum_(m, "ShortCircuitFaultType") + .value("BUS_FAULT", ShortCircuitFaultType::BUS_FAULT) + .value("BRANCH_FAULT", ShortCircuitFaultType::BRANCH_FAULT); + + m.def("get_faults_dataframes_metadata", &pypowsybl::getFaultsMetaData, "Get faults metadata", py::arg("fault_type")); + m.def("set_faults", &pypowsybl::setFaults, "define faults for a short-circuit analysis", py::arg("analysisContext"), py::arg("dataframe"), py::arg("faultType")); + m.def("get_fault_results", &pypowsybl::getFaultResults, "gets the fault results computed after short-circuit analysis", + py::arg("result")); + m.def("get_feeder_results", &pypowsybl::getFeederResults, "gets the feeder results computed after short-circuit analysis", + py::arg("result")); + m.def("get_short_circuit_limit_violations", &pypowsybl::getShortCircuitLimitViolations, "gets the limit violations of a short-circuit analysis", py::arg("result")); + } diff --git a/cpp/src/pypowsybl-api.h b/cpp/src/pypowsybl-api.h index 9fb439dfd..608f0a009 100644 --- a/cpp/src/pypowsybl-api.h +++ b/cpp/src/pypowsybl-api.h @@ -291,3 +291,21 @@ typedef enum { ONE = 0, TWO, } BranchSide; + +typedef struct shortcircuit_analysis_parameters_struct { + unsigned char with_voltage_result; + unsigned char with_feeder_result; + unsigned char with_limit_violations; + int study_type; + unsigned char with_fortescue_result; + double min_voltage_drop_proportional_threshold; + char** provider_parameters_keys; + int provider_parameters_keys_count; + char** provider_parameters_values; + int provider_parameters_values_count; +} shortcircuit_analysis_parameters; + +typedef enum { + BUS_FAULT = 0, + BRANCH_FAULT, +} ShortCircuitFaultType; diff --git a/cpp/src/pypowsybl.cpp b/cpp/src/pypowsybl.cpp index 425724447..087ebcd5b 100644 --- a/cpp/src/pypowsybl.cpp +++ b/cpp/src/pypowsybl.cpp @@ -1339,4 +1339,106 @@ void createNetworkModification(pypowsybl::JavaHandle network, dataframe_array* d pypowsybl::callJava(::createNetworkModification, network, dataframes, networkModificationType, throwException, (reporter == nullptr) ? nullptr : *reporter); } +/*---------------------------------SHORT-CIRCUIT ANALYSIS---------------------------*/ + +void deleteShortCircuitAnalysisParameters(shortcircuit_analysis_parameters* ptr) { + pypowsybl::deleteCharPtrPtr(ptr->provider_parameters_keys, ptr->provider_parameters_keys_count); + pypowsybl::deleteCharPtrPtr(ptr->provider_parameters_values, ptr->provider_parameters_values_count); +} + +ShortCircuitAnalysisParameters::ShortCircuitAnalysisParameters(shortcircuit_analysis_parameters* src) +{ + with_voltage_result = (bool) src->with_voltage_result; + with_feeder_result = (bool) src->with_feeder_result; + with_limit_violations = (bool) src->with_limit_violations; + study_type = static_cast(src->study_type); + with_fortescue_result = (bool) src->with_fortescue_result; + with_voltage_result = (bool) src->with_voltage_result; + min_voltage_drop_proportional_threshold = (double) src->min_voltage_drop_proportional_threshold; + + copyCharPtrPtrToVector(src->provider_parameters_keys, src->provider_parameters_keys_count, provider_parameters_keys); + copyCharPtrPtrToVector(src->provider_parameters_values, src->provider_parameters_values_count, provider_parameters_values); +} + +std::shared_ptr ShortCircuitAnalysisParameters::to_c_struct() const { + shortcircuit_analysis_parameters* res = new shortcircuit_analysis_parameters(); + res->with_voltage_result = (bool) with_voltage_result; + res->with_feeder_result = (bool) with_feeder_result; + res->with_limit_violations = (bool) with_limit_violations; + res->study_type = study_type; + res->with_fortescue_result = (bool) with_fortescue_result; + + res->provider_parameters_keys = pypowsybl::copyVectorStringToCharPtrPtr(provider_parameters_keys); + res->provider_parameters_keys_count = provider_parameters_keys.size(); + res->provider_parameters_values = pypowsybl::copyVectorStringToCharPtrPtr(provider_parameters_values); + res->provider_parameters_values_count = provider_parameters_values.size(); + + //Memory has been allocated here on C side, we need to clean it up on C side (not java side) + return std::shared_ptr(res, [](shortcircuit_analysis_parameters* ptr){ + deleteShortCircuitAnalysisParameters(ptr); + delete ptr; + }); +} + +void setDefaultShortCircuitAnalysisProvider(const std::string& shortCircuitAnalysisProvider) { + callJava<>(::setDefaultShortCircuitAnalysisProvider, (char*) shortCircuitAnalysisProvider.data()); +} + +std::string getDefaultShortCircuitAnalysisProvider() { + return toString(callJava(::getDefaultShortCircuitAnalysisProvider)); +} + +std::vector getShortCircuitAnalysisProviderNames() { + auto formatsArrayPtr = callJava(::getShortCircuitAnalysisProviderNames); + ToStringVector formats(formatsArrayPtr); + return formats.get(); +} + +std::vector getShortCircuitAnalysisProviderParametersNames(const std::string& shortCircuitAnalysisProvider) { + auto providerParametersArrayPtr = pypowsybl::callJava(::getShortCircuitAnalysisProviderParametersNames, (char*) shortCircuitAnalysisProvider.c_str()); + ToStringVector providerParameters(providerParametersArrayPtr); + return providerParameters.get(); +} + +JavaHandle createShortCircuitAnalysis() { + return callJava(::createShortCircuitAnalysis); +} + +JavaHandle runShortCircuitAnalysis(const JavaHandle& shortCircuitAnalysisContext, const JavaHandle& network, const ShortCircuitAnalysisParameters& parameters, + const std::string& provider, JavaHandle* reporter) { + auto c_parameters = parameters.to_c_struct(); + return callJava(::runShortCircuitAnalysis, shortCircuitAnalysisContext, network, c_parameters.get(), (char *) provider.data(), (reporter == nullptr) ? nullptr : *reporter); +} + +ShortCircuitAnalysisParameters* createShortCircuitAnalysisParameters() { + shortcircuit_analysis_parameters* parameters_ptr = callJava(::createShortCircuitAnalysisParameters); + auto parameters = std::shared_ptr(parameters_ptr, [](shortcircuit_analysis_parameters* ptr){ + callJava(::freeShortCircuitAnalysisParameters, ptr); + }); + return new ShortCircuitAnalysisParameters(parameters.get()); +} + +std::vector getFaultsMetaData(ShortCircuitFaultType faultType) { + dataframe_metadata* metadata = pypowsybl::callJava(::getFaultsDataframeMetaData, faultType); + std::vector res = convertDataframeMetadata(metadata); + callJava(::freeDataframeMetadata, metadata); + return res; +} + +void setFaults(pypowsybl::JavaHandle analysisContext, dataframe* dataframe, ShortCircuitFaultType faultType) { + pypowsybl::callJava<>(::setFaults, analysisContext, faultType, dataframe); +} + +SeriesArray* getFaultResults(const JavaHandle& shortCircuitAnalysisResult) { + return new SeriesArray(callJava(::getFaultResults, shortCircuitAnalysisResult)); +} + +SeriesArray* getFeederResults(const JavaHandle& shortCircuitAnalysisResult) { + return new SeriesArray(callJava(::getMagnitudeFeederResults, shortCircuitAnalysisResult)); +} + +SeriesArray* getShortCircuitLimitViolations(const JavaHandle& shortCircuitAnalysisResult) { + return new SeriesArray(callJava(::getLimitViolationsResults, shortCircuitAnalysisResult)); +} + } diff --git a/cpp/src/pypowsybl.h b/cpp/src/pypowsybl.h index d980f673f..2862c20be 100644 --- a/cpp/src/pypowsybl.h +++ b/cpp/src/pypowsybl.h @@ -568,5 +568,42 @@ std::vector getModificationMetadata(network_modification_type ne std::vector> getModificationMetadataWithElementType(network_modification_type networkModificationType, element_type elementType); void createNetworkModification(pypowsybl::JavaHandle network, dataframe_array* dataframe, network_modification_type networkModificationType, bool throwException, JavaHandle* reporter); + +//=======short-circuit analysis========== +enum ShortCircuitStudyType { + SUB_TRANSIENT = 0, + TRANSIENT, + STEADY_STATE +}; + +class ShortCircuitAnalysisParameters { +public: + ShortCircuitAnalysisParameters(shortcircuit_analysis_parameters* src); + std::shared_ptr to_c_struct() const; + + bool with_voltage_result; + bool with_feeder_result; + bool with_limit_violations; + ShortCircuitStudyType study_type; + bool with_fortescue_result; + double min_voltage_drop_proportional_threshold; + + std::vector provider_parameters_keys; + std::vector provider_parameters_values; +}; + +void setDefaultShortCircuitAnalysisProvider(const std::string& shortCircuitAnalysisProvider); +std::string getDefaultShortCircuitAnalysisProvider(); +std::vector getShortCircuitAnalysisProviderNames(); +ShortCircuitAnalysisParameters* createShortCircuitAnalysisParameters(); +std::vector getShortCircuitAnalysisProviderParametersNames(const std::string& shortCircuitAnalysisProvider); +JavaHandle createShortCircuitAnalysis(); +JavaHandle runShortCircuitAnalysis(const JavaHandle& shortCircuitAnalysisContext, const JavaHandle& network, const ShortCircuitAnalysisParameters& parameters, const std::string& provider, JavaHandle* reporter); +std::vector getFaultsMetaData(ShortCircuitFaultType faultType); +void setFaults(pypowsybl::JavaHandle analysisContext, dataframe* dataframe, ShortCircuitFaultType faultType); +SeriesArray* getFaultResults(const JavaHandle& shortCircuitAnalysisResult); +SeriesArray* getFeederResults(const JavaHandle& shortCircuitAnalysisResult); +SeriesArray* getShortCircuitLimitViolations(const JavaHandle& shortCircuitAnalysisResult); + } #endif //PYPOWSYBL_H diff --git a/docs/reference/index.rst b/docs/reference/index.rst index fdea4e24d..3154d6bd5 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -13,3 +13,4 @@ of pypowsybl classes and methods. sensitivity flowdecomposition dynamic + shortcircuit diff --git a/docs/reference/shortcircuit.rst b/docs/reference/shortcircuit.rst new file mode 100644 index 000000000..1a5efedf5 --- /dev/null +++ b/docs/reference/shortcircuit.rst @@ -0,0 +1,59 @@ +Short-circuit analysis +====================== + +.. module:: pypowsybl.shortcircuit + + +Run a short-circuit analysis +---------------------------- + +You can run a short-circuit analysis using the following methods: + +.. autosummary:: + :nosignatures: + :toctree: api/ + + create_analysis + ShortCircuitAnalysis.run + set_default_provider + get_default_provider + get_provider_names + + +Parameters +---------- + +The execution of the short-circuit analysis can be customized using short-circuit analysis parameters. + +.. autosummary:: + :nosignatures: + :toctree: api/ + + Parameters + + +Define faults +------------- + +You can define faults to be simulated with the following methods: + +.. autosummary:: + :nosignatures: + :toctree: api/ + + ShortCircuitAnalysis.set_faults + + +Results +------- + +When the short-circuit analysis is completed, you can inspect its results: + +.. autosummary:: + :nosignatures: + :toctree: api/ + + ShortCircuitAnalysisResult + ShortCircuitAnalysisResult.fault_results + ShortCircuitAnalysisResult.feeder_results + ShortCircuitAnalysisResult.limit_violations diff --git a/docs/user_guide/index.rst b/docs/user_guide/index.rst index 3952b4f91..144145931 100644 --- a/docs/user_guide/index.rst +++ b/docs/user_guide/index.rst @@ -16,3 +16,4 @@ relying as much as possible on practical examples. logging flowdecomposition dynamic + shortcircuit diff --git a/docs/user_guide/shortcircuit.rst b/docs/user_guide/shortcircuit.rst new file mode 100644 index 000000000..ee3a53feb --- /dev/null +++ b/docs/user_guide/shortcircuit.rst @@ -0,0 +1,43 @@ +Running a short-circuit analysis +================================ + +You can use the module :mod:`pypowsybl.shortcircuit` in order to perform a shortcircuit analysis on a network. +Please check out the examples below. + +For detailed documentation of involved classes and methods, please refer to the :mod:`API reference `. + +Note that, currently, no simulator is integrated in pypowsybl to perform the short-circuit analysis. + +Short-circuit analysis +---------------------- + +The current APIs allow the simulation of three-phased bus faults, where the fault resistance and reactance, when specified, are connected to the ground in series. + +To perform a short-circuit analysis, you need a network and at least a fault to simulate on this network. +The results of the analysis contain the computed current and voltages on the network after the fault, in three-phased magnitude. +Optionally, depending on specific parameters for the simulation, the results contain also + + - the contributions of each feeder to the short circuit current (parameter with_feeder_result) + - a list of all the violations after the fault (parameter with_limit_violations) + + + .. code-block:: + + >>> import pypowsybl as pp + >>> import pypowsybl.network as pn + >>> import pandas as pd + >>> # create a network + >>> n = pn.create_four_substations_node_breaker_network() + >>> # sets some short-circuit parameters + >>> pars = pp.shortcircuit.Parameters(with_feeder_result = False, with_limit_violations = False, study_type = pp.shortcircuit.ShortCircuitStudyType.TRANSIENT) + >>> # create a short-circuit analysis context + >>> sc = pp.shortcircuit.create_analysis() + >>> # create a bus fault on the first two buses + >>> buses = n.get_buses() + >>> sc.set_faults(id = ['fault_1', 'fault_2'], element_id = [buses.index[0], buses.index[1]], r = [1, 1], x = [2, 2]) + >>> # perform the short-circuit analysis + >>> # results = sc.run(n, pars, 'sc_provider_1') + >>> # returns the analysis results + >>> # results.fault_results + + diff --git a/java/pom.xml b/java/pom.xml index 3a835bf49..ab564e437 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -263,6 +263,10 @@ com.powsybl powsybl-flow-decomposition + + com.powsybl + powsybl-shortcircuit-api + diff --git a/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/BusFaultDataframeAdder.java b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/BusFaultDataframeAdder.java new file mode 100644 index 000000000..fec4b3ef7 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/BusFaultDataframeAdder.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.dataframe.shortcircuit.adders; + +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.network.adders.SeriesUtils; +import com.powsybl.dataframe.update.DoubleSeries; +import com.powsybl.dataframe.update.StringSeries; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.python.shortcircuit.ShortCircuitAnalysisContext; +import com.powsybl.shortcircuit.BusFault; +import com.powsybl.shortcircuit.Fault; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Christian Biasuzzi + */ +public class BusFaultDataframeAdder implements ShortCircuitContextFaultAdder { + + private static final List METADATA = List.of( + SeriesMetadata.stringIndex("id"), + SeriesMetadata.strings("element_id"), + SeriesMetadata.doubles("r"), + SeriesMetadata.doubles("x") + ); + + @Override + public List getMetadata() { + return METADATA; + } + + private static final class BusFaultSeries { + private final StringSeries faultId; + private final StringSeries elementId; + private final DoubleSeries r; + private final DoubleSeries x; + + BusFaultSeries(UpdatingDataframe dataframe) { + this.faultId = SeriesUtils.getRequiredStrings(dataframe, "id"); + this.elementId = SeriesUtils.getRequiredStrings(dataframe, "element_id"); + this.r = dataframe.getDoubles("r"); + this.x = dataframe.getDoubles("x"); + } + + public StringSeries getFaultId() { + return faultId; + } + + public StringSeries getElementId() { + return elementId; + } + + public DoubleSeries getR() { + return r; + } + + public DoubleSeries getX() { + return x; + } + } + + @Override + public void addElements(ShortCircuitAnalysisContext context, UpdatingDataframe dataframe) { + List faults = new ArrayList<>(); + if (dataframe.getRowCount() > 0) { + BusFaultSeries series = new BusFaultSeries(dataframe); + for (int row = 0; row < dataframe.getRowCount(); row++) { + String faultId = series.getFaultId().get(row); + String elementId = series.getElementId().get(row); + double r = series.getR() != null ? series.getR().get(row) : Double.NaN; + double x = series.getX() != null ? series.getX().get(row) : Double.NaN; + faults.add(r == Double.NaN || x == Double.NaN ? + new BusFault(faultId, elementId) : + new BusFault(faultId, elementId, r, x)); + } + } + context.setFaults(faults); + } + +} diff --git a/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitContextFaultAdder.java b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitContextFaultAdder.java new file mode 100644 index 000000000..921b4b5d2 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitContextFaultAdder.java @@ -0,0 +1,25 @@ +/** + * 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.dataframe.shortcircuit.adders; + +import com.powsybl.dataframe.SeriesMetadata; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.python.shortcircuit.ShortCircuitAnalysisContext; + +import java.util.List; + +/** + * @author Christian Biasuzzi + */ +public interface ShortCircuitContextFaultAdder { + + List getMetadata(); + + void addElements(ShortCircuitAnalysisContext context, UpdatingDataframe dataframe); + +} diff --git a/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitFaultAdderFactory.java b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitFaultAdderFactory.java new file mode 100644 index 000000000..b7a4b57b9 --- /dev/null +++ b/java/src/main/java/com/powsybl/dataframe/shortcircuit/adders/ShortCircuitFaultAdderFactory.java @@ -0,0 +1,28 @@ +/** + * 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.dataframe.shortcircuit.adders; + +import com.powsybl.python.commons.PyPowsyblApiHeader.ShortCircuitFaultType; + +import java.util.Map; + +/** + * @author Christian Biasuzzi + */ +public final class ShortCircuitFaultAdderFactory { + + private static final Map ADDERS = Map.ofEntries( + Map.entry(ShortCircuitFaultType.BUS_FAULT, new BusFaultDataframeAdder())); + + public static ShortCircuitContextFaultAdder getAdder(ShortCircuitFaultType type) { + return ADDERS.get(type); + } + + private ShortCircuitFaultAdderFactory() { + } +} 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 cfab49d05..ea4d6b3d7 100644 --- a/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java +++ b/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java @@ -966,4 +966,82 @@ public enum BranchSide { @CEnumLookup public static native BranchSide fromCValue(int value); } + + @CStruct("shortcircuit_analysis_parameters") + public interface ShortCircuitAnalysisParametersPointer extends PointerBase { + + @CField("with_voltage_result") + boolean isWithVoltageResult(); + + @CField("with_voltage_result") + void setWithVoltageResult(boolean withVoltageResult); + + @CField("with_feeder_result") + boolean isWithFeederResult(); + + @CField("with_feeder_result") + void setWithFeederResult(boolean withFeederResult); + + @CField("with_limit_violations") + boolean isWithLimitViolations(); + + @CField("with_limit_violations") + void setWithLimitViolations(boolean withLimitViolations); + + @CField("study_type") + int getStudyType(); + + @CField("study_type") + void setStudyType(int studyType); + + @CField("with_fortescue_result") + boolean isWithFortescueResult(); + + @CField("with_fortescue_result") + void setWithFortescueResult(boolean withFortescueResult); + + @CField("min_voltage_drop_proportional_threshold") + double getMinVoltageDropProportionalThreshold(); + + @CField("min_voltage_drop_proportional_threshold") + void setMinVoltageDropProportionalThreshold(double minVoltageDropProportionalThreshold); + + @CField("provider_parameters_keys") + void setProviderParametersKeys(CCharPointerPointer providerParametersKeys); + + @CField("provider_parameters_keys") + CCharPointerPointer getProviderParametersKeys(); + + @CField("provider_parameters_keys_count") + int getProviderParametersKeysCount(); + + @CField("provider_parameters_keys_count") + void setProviderParametersKeysCount(int providerParametersKeysCount); + + @CField("provider_parameters_values") + void setProviderParametersValues(CCharPointerPointer providerParametersValues); + + @CField("provider_parameters_values") + CCharPointerPointer getProviderParametersValues(); + + @CField("provider_parameters_values_count") + int getProviderParametersValuesCount(); + + @CField("provider_parameters_values_count") + void setProviderParametersValuesCount(int providerParametersKeysCount); + } + + @CEnum("ShortCircuitFaultType") + public enum ShortCircuitFaultType { + BUS_FAULT, + BRANCH_FAULT; + + @CEnumValue + public native int getCValue(); + + @CEnumLookup + public static native ShortCircuitFaultType fromCValue(int value); + } + + } diff --git a/java/src/main/java/com/powsybl/python/commons/PyPowsyblConfiguration.java b/java/src/main/java/com/powsybl/python/commons/PyPowsyblConfiguration.java index a1fbde5a6..ea480cb54 100644 --- a/java/src/main/java/com/powsybl/python/commons/PyPowsyblConfiguration.java +++ b/java/src/main/java/com/powsybl/python/commons/PyPowsyblConfiguration.java @@ -11,6 +11,7 @@ public final class PyPowsyblConfiguration { private static String defaultLoadflowProvider = "OpenLoadFlow"; private static String defaultSecurityAnalysisProvider = "OpenLoadFlow"; private static String defaultSensitivityAnalysisProvider = "OpenLoadFlow"; + private static String defaultShortCircuitAnalysisProvider = ""; private PyPowsyblConfiguration() { } @@ -52,4 +53,12 @@ public static String getDefaultSensitivityAnalysisProvider() { public static void setDefaultSensitivityAnalysisProvider(String sensitivityAnalysisProvider) { defaultSensitivityAnalysisProvider = sensitivityAnalysisProvider; } + + public static String getDefaultShortCircuitAnalysisProvider() { + return defaultShortCircuitAnalysisProvider; + } + + public static void setDefaultShortCircuitAnalysisProvider(String shortCircuitAnalysisProvider) { + defaultShortCircuitAnalysisProvider = shortCircuitAnalysisProvider; + } } diff --git a/java/src/main/java/com/powsybl/python/network/Dataframes.java b/java/src/main/java/com/powsybl/python/network/Dataframes.java index a160621a8..b53917154 100644 --- a/java/src/main/java/com/powsybl/python/network/Dataframes.java +++ b/java/src/main/java/com/powsybl/python/network/Dataframes.java @@ -14,8 +14,6 @@ import com.powsybl.dataframe.impl.DefaultDataframeHandler; import com.powsybl.dataframe.impl.Series; import com.powsybl.flow_decomposition.FlowDecompositionResults; -import com.powsybl.iidm.network.Exporter; -import com.powsybl.iidm.network.Importer; import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.extensions.ConnectablePosition; import com.powsybl.python.commons.PyPowsyblApiHeader.ArrayPointer; @@ -26,9 +24,15 @@ import com.powsybl.python.security.BusResultContext; import com.powsybl.python.security.LimitViolationContext; import com.powsybl.python.security.ThreeWindingsTransformerResultContext; +import com.powsybl.python.shortcircuit.LimitViolationFaultContext; +import com.powsybl.python.shortcircuit.MagnitudeFeederResultContext; import com.powsybl.security.LimitViolation; import com.powsybl.security.LimitViolationType; import com.powsybl.security.SecurityAnalysisResult; +import com.powsybl.shortcircuit.FaultResult; +import com.powsybl.shortcircuit.MagnitudeFaultResult; +import com.powsybl.shortcircuit.MagnitudeFeederResult; +import com.powsybl.shortcircuit.ShortCircuitAnalysisResult; import org.apache.commons.collections4.IteratorUtils; import org.apache.commons.lang3.tuple.Pair; @@ -458,4 +462,85 @@ private static List getXnecWithDecompositions(Flow .sorted(Comparator.comparing(XnecWithDecompositionContext::getId)) .collect(Collectors.toList()); } + + // shortcircuit + public static DataframeMapper shortCircuitAnalysisFaultResultsMapper() { + return SHORT_CIRCUIT_MAGNITUDE_RESULTS_MAPPER; + } + + private static final DataframeMapper SHORT_CIRCUIT_MAGNITUDE_RESULTS_MAPPER = createMagnitudeFaultResultsMapper(); + + private static List getMagnitudeFaultResults(ShortCircuitAnalysisResult result) { + return result.getFaultResults().stream().filter(f -> f instanceof MagnitudeFaultResult) + .map(f -> (MagnitudeFaultResult) f) + .collect(Collectors.toList()); + } + + private static DataframeMapper createMagnitudeFaultResultsMapper() { + return new DataframeMapperBuilder() + .itemsProvider(Dataframes::getMagnitudeFaultResults) + .stringsIndex("id", f -> f.getFault().getId()) + .enums("status", FaultResult.Status.class, f -> f.getStatus()) + .doubles("short_circuit_power", MagnitudeFaultResult::getShortCircuitPower) + .strings("time_constant", f -> f.getTimeConstant() != null ? f.getTimeConstant().toString() : null) + .doubles("current", MagnitudeFaultResult::getCurrent) + .doubles("voltage", MagnitudeFaultResult::getVoltage) + .build(); + } + + public static DataframeMapper shortCircuitAnalysisLimitViolationsResultsMapper() { + return SHORT_CIRCUIT_LIMIT_VIOLATIONS_RESULTS_MAPPER; + } + + private static final DataframeMapper SHORT_CIRCUIT_LIMIT_VIOLATIONS_RESULTS_MAPPER = createLimitViolationsFaultMapper(); + + public static List getFaultLimitViolations(ShortCircuitAnalysisResult result) { + List limitViolations = result.getFaultResults().stream() + .flatMap(a -> a.getLimitViolations() + .stream() + .map(ss -> new LimitViolationFaultContext(a.getFault().getId(), ss))) + .collect(Collectors.toList()); + return limitViolations; + } + + private static DataframeMapper createLimitViolationsFaultMapper() { + return new DataframeMapperBuilder() + .itemsProvider(Dataframes::getFaultLimitViolations) + .stringsIndex("id", LimitViolationFaultContext::getFaultId) + .stringsIndex("subject_id", LimitViolation::getSubjectId) + .strings("subject_name", p -> Objects.toString(p.getSubjectName(), "")) + .enums("limit_type", LimitViolationType.class, LimitViolation::getLimitType) + .strings("limit_name", p -> Objects.toString(p.getLimitName(), "")) + .doubles("limit", LimitViolation::getLimit) + .ints("acceptable_duration", LimitViolation::getAcceptableDuration) + .doubles("limit_reduction", LimitViolation::getLimitReduction) + .doubles("value", LimitViolation::getValue) + .strings("side", p -> Objects.toString(p.getSide(), "")) + .build(); + } + + public static DataframeMapper shortCircuitAnalysisMagnitudeFeederResultsMapper() { + return SHORT_CIRCUIT_MAGNITUDE_FEEDER_RESULTS_MAPPER; + } + + private static final DataframeMapper SHORT_CIRCUIT_MAGNITUDE_FEEDER_RESULTS_MAPPER = createMagnitudeFeederMapper(); + + public static List getMagnitudeFeederResultContexts(ShortCircuitAnalysisResult result) { + List feederResults = result.getFaultResults().stream() + .flatMap(a -> a.getFeederResults() + .stream() + .map(ss -> new MagnitudeFeederResultContext(a.getFault().getId(), (MagnitudeFeederResult) ss))) + .collect(Collectors.toList()); + return feederResults; + } + + private static DataframeMapper createMagnitudeFeederMapper() { + return new DataframeMapperBuilder() + .itemsProvider(Dataframes::getMagnitudeFeederResultContexts) + .stringsIndex("id", MagnitudeFeederResultContext::getFaultId) + .stringsIndex("connectable_id", MagnitudeFeederResultContext::getConnectableId) + .doubles("current", MagnitudeFeederResultContext::getCurrent) + .build(); + } + } diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/LimitViolationFaultContext.java b/java/src/main/java/com/powsybl/python/shortcircuit/LimitViolationFaultContext.java new file mode 100644 index 000000000..6521a1210 --- /dev/null +++ b/java/src/main/java/com/powsybl/python/shortcircuit/LimitViolationFaultContext.java @@ -0,0 +1,32 @@ +/** + * 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.python.shortcircuit; + +import com.powsybl.security.LimitViolation; + +import java.util.Objects; + +/** + * @author Christian Biasuzzi + */ +public class LimitViolationFaultContext extends LimitViolation { + + private final String faultId; + + public LimitViolationFaultContext(String faultId, LimitViolation limitViolation) { + super(limitViolation.getSubjectId(), limitViolation.getSubjectName(), limitViolation.getLimitType(), + limitViolation.getLimitName(), limitViolation.getAcceptableDuration(), limitViolation.getLimit(), + limitViolation.getLimitReduction(), limitViolation.getValue(), limitViolation.getSide()); + this.faultId = Objects.requireNonNull(faultId); + } + + public String getFaultId() { + return faultId; + } + +} diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeFeederResultContext.java b/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeFeederResultContext.java new file mode 100644 index 000000000..6302c0990 --- /dev/null +++ b/java/src/main/java/com/powsybl/python/shortcircuit/MagnitudeFeederResultContext.java @@ -0,0 +1,28 @@ +/** + * 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.python.shortcircuit; + +import com.powsybl.shortcircuit.MagnitudeFeederResult; + +import java.util.Objects; + +/** + * @author Christian Biasuzzi + */ +public class MagnitudeFeederResultContext extends MagnitudeFeederResult { + private final String faultId; + + public MagnitudeFeederResultContext(String faultId, MagnitudeFeederResult result) { + super(result.getConnectableId(), result.getCurrent()); + this.faultId = Objects.requireNonNull(faultId); + } + + public String getFaultId() { + return faultId; + } +} diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCFunctions.java b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCFunctions.java new file mode 100644 index 000000000..10fb166e3 --- /dev/null +++ b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCFunctions.java @@ -0,0 +1,191 @@ +/** + * 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.python.shortcircuit; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.reporter.ReporterModel; +import com.powsybl.dataframe.shortcircuit.adders.ShortCircuitFaultAdderFactory; +import com.powsybl.dataframe.update.UpdatingDataframe; +import com.powsybl.iidm.network.Network; +import com.powsybl.python.commons.*; +import com.powsybl.python.commons.PyPowsyblApiHeader.DataframeMetadataPointer; +import com.powsybl.python.commons.PyPowsyblApiHeader.ShortCircuitAnalysisParametersPointer; +import com.powsybl.python.commons.PyPowsyblApiHeader.ShortCircuitFaultType; +import com.powsybl.python.network.Dataframes; +import com.powsybl.python.network.NetworkCFunctions; +import com.powsybl.shortcircuit.*; +import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.nativeimage.ObjectHandle; +import org.graalvm.nativeimage.ObjectHandles; +import org.graalvm.nativeimage.UnmanagedMemory; +import org.graalvm.nativeimage.c.CContext; +import org.graalvm.nativeimage.c.function.CEntryPoint; +import org.graalvm.nativeimage.c.struct.SizeOf; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CCharPointerPointer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static com.powsybl.python.commons.Util.createCharPtrArray; +import static com.powsybl.python.commons.Util.doCatch; + +/** + * C functions related to short-circuit analysis. + * + * @author Christian Biasuzzi + */ +@CContext(Directives.class) +public final class ShortCircuitAnalysisCFunctions { + + private ShortCircuitAnalysisCFunctions() { + } + + private static Logger logger() { + return LoggerFactory.getLogger(ShortCircuitAnalysisCFunctions.class); + } + + @CEntryPoint(name = "getShortCircuitAnalysisProviderNames") + public static PyPowsyblApiHeader.ArrayPointer getShortCircuitAnalysisProviderNames(IsolateThread thread, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> createCharPtrArray(ShortCircuitAnalysisProvider.findAll() + .stream().map(ShortCircuitAnalysisProvider::getName).collect(Collectors.toList()))); + } + + @CEntryPoint(name = "setDefaultShortCircuitAnalysisProvider") + public static void setDefaultShortCircuitAnalysisProvider(IsolateThread thread, CCharPointer provider, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + doCatch(exceptionHandlerPtr, () -> { + PyPowsyblConfiguration.setDefaultShortCircuitAnalysisProvider(CTypeUtil.toString(provider)); + }); + } + + @CEntryPoint(name = "getDefaultShortCircuitAnalysisProvider") + public static CCharPointer getDefaultShortCircuitAnalysisProvider(IsolateThread thread, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> CTypeUtil.toCharPtr(PyPowsyblConfiguration.getDefaultShortCircuitAnalysisProvider())); + } + + @CEntryPoint(name = "createShortCircuitAnalysis") + public static ObjectHandle createShortCircuitAnalysis(IsolateThread thread, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> ObjectHandles.getGlobal().create(new ShortCircuitAnalysisContext())); + } + + private static ShortCircuitAnalysisProvider getProvider(String name) { + String actualName = name.isEmpty() ? PyPowsyblConfiguration.getDefaultShortCircuitAnalysisProvider() : name; + return ShortCircuitAnalysisProvider.findAll().stream() + .filter(provider -> provider.getName().equals(actualName)) + .findFirst() + .orElseThrow(() -> new PowsyblException("No short-circuit analysis provider for name '" + actualName + "'")); + } + + @CEntryPoint(name = "runShortCircuitAnalysis") + public static ObjectHandle runShortCircuitAnalysis(IsolateThread thread, ObjectHandle shortCircuitAnalysisContextHandle, + ObjectHandle networkHandle, ShortCircuitAnalysisParametersPointer shortCircuitAnalysisParametersPointer, + CCharPointer providerName, ObjectHandle reporterHandle, + PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + ShortCircuitAnalysisContext analysisContext = ObjectHandles.getGlobal().get(shortCircuitAnalysisContextHandle); + + Network network = ObjectHandles.getGlobal().get(networkHandle); + + ShortCircuitAnalysisProvider provider = getProvider(CTypeUtil.toString(providerName)); + logger().info("Short-circuit analysis provider used for short-circuit analysis is : {}", provider.getName()); + ShortCircuitParameters shortCircuitAnalysisParameters = ShortCircuitAnalysisCUtils.createShortCircuitAnalysisParameters(shortCircuitAnalysisParametersPointer, provider); + + ReporterModel reporter = ObjectHandles.getGlobal().get(reporterHandle); + ShortCircuitAnalysisResult results = analysisContext.run(network, shortCircuitAnalysisParameters, provider.getName(), reporter); + return ObjectHandles.getGlobal().create(results); + }); + } + + @CEntryPoint(name = "freeShortCircuitAnalysisParameters") + public static void freeShortCircuitAnalysisParameters(IsolateThread thread, ShortCircuitAnalysisParametersPointer parameters, + PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + doCatch(exceptionHandlerPtr, () -> { + UnmanagedMemory.free(parameters); + }); + } + + @CEntryPoint(name = "createShortCircuitAnalysisParameters") + public static ShortCircuitAnalysisParametersPointer createShortCircuitAnalysisParameters(IsolateThread thread, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> convertToShortCircuitAnalysisParametersPointer(ShortCircuitAnalysisCUtils.createShortCircuitAnalysisParameters())); + } + + private static ShortCircuitAnalysisParametersPointer convertToShortCircuitAnalysisParametersPointer(ShortCircuitParameters parameters) { + ShortCircuitAnalysisParametersPointer paramsPtr = UnmanagedMemory.calloc(SizeOf.get(ShortCircuitAnalysisParametersPointer.class)); + paramsPtr.setWithVoltageResult(parameters.isWithVoltageResult()); + paramsPtr.setWithFeederResult(parameters.isWithFeederResult()); + paramsPtr.setWithLimitViolations(parameters.isWithLimitViolations()); + paramsPtr.setWithFortescueResult(parameters.isWithFortescueResult()); + paramsPtr.setMinVoltageDropProportionalThreshold(parameters.getMinVoltageDropProportionalThreshold()); + paramsPtr.setStudyType(parameters.getStudyType().ordinal()); + paramsPtr.setProviderParametersValuesCount(0); + paramsPtr.setProviderParametersKeysCount(0); + return paramsPtr; + } + + static List getSpecificParametersNames(ShortCircuitAnalysisProvider provider) { + // currently, the short-circuit APIs do not have this method. + // there is a List getSpecificParameters(), but its semantic must be checked + return Collections.emptyList(); + } + + @CEntryPoint(name = "getShortCircuitAnalysisProviderParametersNames") + public static PyPowsyblApiHeader.ArrayPointer getProviderParametersNames(IsolateThread thread, CCharPointer provider, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + String providerStr = CTypeUtil.toString(provider); + return Util.createCharPtrArray(getSpecificParametersNames(ShortCircuitAnalysisCUtils.getShortCircuitAnalysisProvider(providerStr))); + }); + } + + @CEntryPoint(name = "getFaultsDataframeMetaData") + public static DataframeMetadataPointer getFaultsDataframeMetaData(IsolateThread thread, + ShortCircuitFaultType mappingType, + PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + return CTypeUtil.createSeriesMetadata(ShortCircuitFaultAdderFactory.getAdder(mappingType).getMetadata()); + }); + } + + @CEntryPoint(name = "setFaults") + public static void setFaults(IsolateThread thread, ObjectHandle contextHandle, + ShortCircuitFaultType faultType, + PyPowsyblApiHeader.DataframePointer cDataframe, + PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + doCatch(exceptionHandlerPtr, () -> { + ShortCircuitAnalysisContext context = ObjectHandles.getGlobal().get(contextHandle); + UpdatingDataframe faultDataframe = NetworkCFunctions.createDataframe(cDataframe); + ShortCircuitFaultAdderFactory.getAdder(faultType).addElements(context, faultDataframe); + }); + } + + @CEntryPoint(name = "getFaultResults") + public static PyPowsyblApiHeader.ArrayPointer getFaultResults(IsolateThread thread, ObjectHandle shortCircuitAnalysisResult, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + ShortCircuitAnalysisResult result = ObjectHandles.getGlobal().get(shortCircuitAnalysisResult); + return Dataframes.createCDataframe(Dataframes.shortCircuitAnalysisFaultResultsMapper(), result); + }); + } + + @CEntryPoint(name = "getMagnitudeFeederResults") + public static PyPowsyblApiHeader.ArrayPointer getMagnitudeFeederResults(IsolateThread thread, ObjectHandle shortCircuitAnalysisResult, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + ShortCircuitAnalysisResult result = ObjectHandles.getGlobal().get(shortCircuitAnalysisResult); + return Dataframes.createCDataframe(Dataframes.shortCircuitAnalysisMagnitudeFeederResultsMapper(), result); + }); + } + + @CEntryPoint(name = "getLimitViolationsResults") + public static PyPowsyblApiHeader.ArrayPointer getLimitViolationsResults(IsolateThread thread, ObjectHandle shortCircuitAnalysisResult, PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + ShortCircuitAnalysisResult result = ObjectHandles.getGlobal().get(shortCircuitAnalysisResult); + return Dataframes.createCDataframe(Dataframes.shortCircuitAnalysisLimitViolationsResultsMapper(), result); + }); + } +} diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCUtils.java b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCUtils.java new file mode 100644 index 000000000..0402ab66d --- /dev/null +++ b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisCUtils.java @@ -0,0 +1,73 @@ +/** + * 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.python.shortcircuit; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.extensions.Extension; +import com.powsybl.python.commons.CTypeUtil; +import com.powsybl.python.commons.PyPowsyblApiHeader; +import com.powsybl.python.commons.PyPowsyblConfiguration; +import com.powsybl.shortcircuit.ShortCircuitAnalysisProvider; +import com.powsybl.shortcircuit.ShortCircuitParameters; +import com.powsybl.shortcircuit.StudyType; + +import java.util.Map; + +/** + * @author Christian Biasuzzi + */ +public final class ShortCircuitAnalysisCUtils { + + private ShortCircuitAnalysisCUtils() { + } + + public static ShortCircuitAnalysisProvider getShortCircuitAnalysisProvider(String name) { + String actualName = name.isEmpty() ? PyPowsyblConfiguration.getDefaultShortCircuitAnalysisProvider() : name; + return ShortCircuitAnalysisProvider.findAll().stream() + .filter(provider -> provider.getName().equals(actualName)) + .findFirst() + .orElseThrow(() -> new PowsyblException("No short-circuit analysis provider for name '" + actualName + "'")); + } + + public static ShortCircuitParameters createShortCircuitAnalysisParameters() { + return PyPowsyblConfiguration.isReadConfig() ? ShortCircuitParameters.load() : new ShortCircuitParameters(); + } + + public static ShortCircuitParameters createShortCircuitAnalysisParameters(PyPowsyblApiHeader.ShortCircuitAnalysisParametersPointer shortCircuitAnalysisParametersPointer, + ShortCircuitAnalysisProvider provider) { + ShortCircuitParameters parameters = createShortCircuitAnalysisParameters(shortCircuitAnalysisParametersPointer); + Map specificParametersProperties = getSpecificParameters(shortCircuitAnalysisParametersPointer); + provider.loadSpecificParameters(specificParametersProperties).ifPresent(ext -> { + // Dirty trick to get the class, and reload parameters if they exist. + Extension configured = parameters.getExtension(ext.getClass()); + if (configured != null) { + provider.updateSpecificParameters(configured, specificParametersProperties); + } else { + parameters.addExtension((Class) ext.getClass(), ext); + } + }); + return parameters; + } + + private static ShortCircuitParameters createShortCircuitAnalysisParameters(PyPowsyblApiHeader.ShortCircuitAnalysisParametersPointer shortCircuitAnalysisParametersPointer) { + return createShortCircuitAnalysisParameters().setWithLimitViolations(shortCircuitAnalysisParametersPointer.isWithLimitViolations()) + .setWithVoltageResult(shortCircuitAnalysisParametersPointer.isWithVoltageResult()) + .setWithFeederResult(shortCircuitAnalysisParametersPointer.isWithFeederResult()) + .setWithFortescueResult(shortCircuitAnalysisParametersPointer.isWithFortescueResult()) + .setWithLimitViolations(shortCircuitAnalysisParametersPointer.isWithLimitViolations()) + .setMinVoltageDropProportionalThreshold(shortCircuitAnalysisParametersPointer.getMinVoltageDropProportionalThreshold()) + .setStudyType(StudyType.values()[shortCircuitAnalysisParametersPointer.getStudyType()]); + } + + private static Map getSpecificParameters(PyPowsyblApiHeader.ShortCircuitAnalysisParametersPointer shortCircuitAnalysisParametersPointer) { + return CTypeUtil.toStringMap(shortCircuitAnalysisParametersPointer.getProviderParametersKeys(), + shortCircuitAnalysisParametersPointer.getProviderParametersKeysCount(), + shortCircuitAnalysisParametersPointer.getProviderParametersValues(), + shortCircuitAnalysisParametersPointer.getProviderParametersValuesCount()); + } +} diff --git a/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisContext.java b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisContext.java new file mode 100644 index 000000000..41bae3c31 --- /dev/null +++ b/java/src/main/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisContext.java @@ -0,0 +1,46 @@ +/** + * 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.python.shortcircuit; + +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.iidm.network.Network; +import com.powsybl.python.commons.CommonObjects; +import com.powsybl.shortcircuit.*; + +import java.util.Collections; +import java.util.List; + +/** + * @author Christian Biasuzzi + */ +public class ShortCircuitAnalysisContext { + + public List getFaults() { + return faults; + } + + public void setFaults(List faults) { + this.faults = faults; + } + + List faults = Collections.emptyList(); + + ShortCircuitAnalysisResult run(Network network, ShortCircuitParameters shortCircuitAnalysisParameters, String provider, Reporter reporter) { + List faultsParameters = Collections.emptyList(); + ShortCircuitAnalysisResult results = ShortCircuitAnalysis.find(provider) + .run( + network, + faults, + shortCircuitAnalysisParameters, + CommonObjects.getComputationManager(), + faultsParameters, + (reporter == null) ? Reporter.NO_OP : reporter + ); + return results; + } +} diff --git a/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java b/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java new file mode 100644 index 000000000..28f677a25 --- /dev/null +++ b/java/src/test/java/com/powsybl/python/shortcircuit/ShortCircuitAnalysisTest.java @@ -0,0 +1,109 @@ +/** + * 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.python.shortcircuit; + +import com.powsybl.dataframe.impl.Series; +import com.powsybl.python.network.Dataframes; +import com.powsybl.security.LimitViolation; +import com.powsybl.security.LimitViolationType; +import com.powsybl.shortcircuit.*; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static java.lang.Double.NaN; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Christian Biasuzzi + */ +class ShortCircuitAnalysisTest { + + @Test + void testShortCircuitAnalysis() { + ShortCircuitParameters parameters = new ShortCircuitParameters() + .setWithFortescueResult(false) + .setWithLimitViolations(false) + .setWithFeederResult(false) + .setStudyType(StudyType.TRANSIENT); + + assertThat(parameters).isNotNull(); + ShortCircuitAnalysisContext analysisContext = new ShortCircuitAnalysisContext(); + assertThat(analysisContext).isNotNull(); + List faults = new ArrayList<>(); + BusFault bf1 = new BusFault("F1", "B2", 0., 0., Fault.ConnectionType.SERIES, Fault.FaultType.THREE_PHASE); + BusFault bf2 = new BusFault("F2", "B3", 0., 0., Fault.ConnectionType.SERIES, Fault.FaultType.THREE_PHASE); + BusFault bf3 = new BusFault("F3", "B9", 0., 0., Fault.ConnectionType.SERIES, Fault.FaultType.THREE_PHASE); + faults.add(bf1); + faults.add(bf2); + faults.add(bf3); + assertThat(faults).hasSize(3); + analysisContext.setFaults(faults); + } + + @Test + void testShortCircuitAnalysisResults() { + Fault f1 = new BusFault("f1", "bus1"); + Fault f2 = new BusFault("f2", "bus2"); + + LimitViolation lv1 = new LimitViolation("subj1", LimitViolationType.HIGH_VOLTAGE, 1.0, 2.0f, 3.0); + LimitViolation lv2 = new LimitViolation("subj2", LimitViolationType.HIGH_VOLTAGE, 1.0, 2.0f, 3.0); + LimitViolation lv3 = new LimitViolation("subj3", LimitViolationType.HIGH_VOLTAGE, 1.0, 2.0f, 3.0); + LimitViolation lv4 = new LimitViolation("subj4", LimitViolationType.HIGH_VOLTAGE, 1.0, 2.0f, 3.0); + LimitViolation lv5 = new LimitViolation("subj5", LimitViolationType.HIGH_VOLTAGE, 1.0, 2.0f, 3.0); + + MagnitudeFeederResult mfr1 = new MagnitudeFeederResult("connect1", 1.1); + MagnitudeFeederResult mfr2 = new MagnitudeFeederResult("connect2", 1.2); + + FaultResult fr1 = new MagnitudeFaultResult(f1, 1.0, List.of(mfr1, mfr2), List.of(lv1, lv2), 5.0, FaultResult.Status.SUCCESS); + FaultResult fr2 = new MagnitudeFaultResult(f2, 2.0, Collections.emptyList(), List.of(lv3, lv4, lv5), 6.0, FaultResult.Status.SUCCESS); + + ShortCircuitAnalysisResult fakeResults = new ShortCircuitAnalysisResult(List.of(fr1, fr2)); + + List faultResultsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisFaultResultsMapper(), fakeResults); + Assertions.assertThat(faultResultsSeries) + .extracting(Series::getName) + .containsExactly("id", "status", "short_circuit_power", "time_constant", "current", "voltage"); + Assertions.assertThat(faultResultsSeries.get(0).getStrings()) + .containsExactly("f1", "f2"); + Assertions.assertThat(faultResultsSeries.get(1).getStrings()) + .containsExactly("SUCCESS", "SUCCESS"); + Assertions.assertThat(faultResultsSeries.get(2).getDoubles()) + .containsExactly(1.0, 2.0); + Assertions.assertThat(faultResultsSeries.get(3).getStrings()) + .containsExactly(null, null); + Assertions.assertThat(faultResultsSeries.get(4).getDoubles()) + .containsExactly(5.0, 6.0); + Assertions.assertThat(faultResultsSeries.get(5).getDoubles()) + .containsExactly(NaN, NaN); + + List feederResultsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisMagnitudeFeederResultsMapper(), fakeResults); + Assertions.assertThat(feederResultsSeries) + .extracting(Series::getName) + .containsExactly("id", "connectable_id", "current"); + Assertions.assertThat(feederResultsSeries.get(0).getStrings()) + .containsExactly("f1", "f1"); + Assertions.assertThat(feederResultsSeries.get(1).getStrings()) + .containsExactly("connect1", "connect2"); + Assertions.assertThat(feederResultsSeries.get(2).getDoubles()) + .containsExactly(1.1, 1.2); + + List limitViolationsSeries = Dataframes.createSeries(Dataframes.shortCircuitAnalysisLimitViolationsResultsMapper(), fakeResults); + Assertions.assertThat(limitViolationsSeries) + .extracting(Series::getName) + .containsExactly("id", "subject_id", "subject_name", "limit_type", "limit_name", "limit", + "acceptable_duration", "limit_reduction", "value", "side"); + Assertions.assertThat(limitViolationsSeries.get(0).getStrings()) + .containsExactly("f1", "f1", "f2", "f2", "f2"); + Assertions.assertThat(limitViolationsSeries.get(1).getStrings()) + .containsExactly("subj1", "subj2", "subj3", "subj4", "subj5"); + } +} diff --git a/pypowsybl/__init__.py b/pypowsybl/__init__.py index ca9c423ed..ec8b0ee61 100644 --- a/pypowsybl/__init__.py +++ b/pypowsybl/__init__.py @@ -17,7 +17,8 @@ sensitivity, glsk, flowdecomposition, - perunit + perunit, + shortcircuit ) __version__ = '0.25.0.dev1' @@ -36,7 +37,8 @@ "sensitivity", "glsk", "flowdecomposition", - "perunit" + "perunit", + "shortcircuit" ] diff --git a/pypowsybl/_pypowsybl.pyi b/pypowsybl/_pypowsybl.pyi index 57d01be54..d18ec031e 100644 --- a/pypowsybl/_pypowsybl.pyi +++ b/pypowsybl/_pypowsybl.pyi @@ -500,6 +500,29 @@ class DefaultXnecProvider: @property def name(self) -> str: ... +class ShortCircuitStudyType: + __members__: ClassVar[Dict[str, ShortCircuitStudyType]] = ... # read-only + SUB_TRANSIENT: ClassVar[ShortCircuitStudyType] = ... + TRANSIENT: ClassVar[ShortCircuitStudyType] = ... + STEADY_STATE: ClassVar[ShortCircuitStudyType] = ... + +class ShortCircuitFaultType: + __members__: ClassVar[Dict[str, ShortCircuitFaultType]] = ... # read-only + BUS_FAULT: ClassVar[ShortCircuitFaultType] = ... + BRANCH_FAULT: ClassVar[ShortCircuitFaultType] = ... + +class ShortCircuitAnalysisParameters: + with_voltage_result: bool + with_feeder_result: bool + with_limit_violations: bool + study_type: ShortCircuitStudyType + with_fortescue_result: bool + min_voltage_drop_proportional_threshold: float + provider_parameters_keys: List[str] + provider_parameters_values: List[str] + def __init__(self) -> None: ... + + def add_contingency(analysis_context: JavaHandle, contingency_id: str, elements_ids: List[str]) -> None: ... def add_monitored_elements(security_analysis_context: JavaHandle, contingency_context_type: ContingencyContextType, branch_ids: List[str], voltage_level_ids: List[str], three_windings_transformer_ids: List[str], contingency_ids: List[str]) -> None: ... def clone_variant(network: JavaHandle, src: str, variant: str, may_overwrite: bool) -> None: ... @@ -634,4 +657,15 @@ def remove_elements_modification(network: JavaHandle, connectable_ids: List[str] def get_network_modification_metadata(network_modification_type: NetworkModificationType) -> List[SeriesMetadata]: ... def get_network_modification_metadata_with_element_type(network_modification_type: NetworkModificationType, element_type: ElementType) -> List[List[SeriesMetadata]]: ... def create_network_modification(network: JavaHandle, dataframes: List[Optional[Dataframe]], network_modification_type: NetworkModificationType, raise_exception: Optional[bool], reporter: Optional[JavaHandle]) -> None: ... -def get_single_line_diagram_component_library_names() -> List[str]: ... \ No newline at end of file +def get_single_line_diagram_component_library_names() -> List[str]: ... +def set_faults(short_circuit_analysis: JavaHandle, dfs: Optional[Dataframe], fault_type: ShortCircuitFaultType) -> None: ... +def get_fault_results(result: JavaHandle) -> SeriesArray: ... +def get_feeder_results(result: JavaHandle) -> SeriesArray: ... +def get_short_circuit_limit_violations(result: JavaHandle) -> SeriesArray: ... +def get_faults_dataframes_metadata(faultType: ShortCircuitFaultType) -> List[SeriesMetadata]: ... +def run_shortcircuit_analysis(context: JavaHandle, network: JavaHandle, parameters: ShortCircuitAnalysisParameters, provider: str, reporter: Optional[JavaHandle]) -> JavaHandle: ... +def create_shortcircuit_analysis() -> JavaHandle: ... +def set_default_shortcircuit_analysis_provider(provider: str) -> None: ... +def get_default_shortcircuit_analysis_provider() -> str: ... +def get_shortcircuit_provider_names() -> List[str]: ... +def get_shortcircuit_provider_parameters_names(provider: str) -> List[str]: ... diff --git a/pypowsybl/shortcircuit.py b/pypowsybl/shortcircuit.py new file mode 100644 index 000000000..729b9b539 --- /dev/null +++ b/pypowsybl/shortcircuit.py @@ -0,0 +1,255 @@ +# +# Copyright (c) 2023, RTE (http://www.rte-france.com) +# This Source Code Form is subject to the terms of the Mozilla Public +# iicense, 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 +# +from __future__ import annotations # Necessary for type alias like _DataFrame to work with sphinx + +from typing import ( + List as _List, + Dict as _Dict, + Optional as _Optional, +) + +import pandas as _pd +from numpy.typing import ArrayLike as _ArrayLike +from pandas import DataFrame as _DataFrame +from pypowsybl import _pypowsybl +from pypowsybl._pypowsybl import ShortCircuitFaultType, ShortCircuitStudyType +from pypowsybl.network import Network as _Network +from pypowsybl.report import Reporter as _Reporter +from pypowsybl.util import ( + create_data_frame_from_series_array as _create_data_frame_from_series_array +) +from pypowsybl.utils.dataframes import ( + _get_c_dataframes +) + +ShortCircuitStudyType.__module__ = __name__ + +class Parameters: # pylint: disable=too-few-public-methods + """ + Parameters for a short-circuit analysis execution. + + Please check the Powsybl's short-circuit APIs documentation, for detailed information. + + .. currentmodule:: pypowsybl.shortcircuit + + Args: + with_feeder_result: indicates if the contributions of each feeder to the short circuit current at the fault node should be computed + with_limit_violations: indicates whether limit violations should be returned after the computation + study_type: indicates the type of short circuit study. It can be SUB_TRANSIENT, TRANSIENT or STEADY_STATE + """ + def __init__(self, + with_feeder_result: bool = None, + with_limit_violations: bool = None, + study_type: ShortCircuitStudyType = None, + provider_parameters: _Dict[str, str] = None): + self._init_with_default_values() + if with_feeder_result is not None: + self.with_feeder_result = with_feeder_result + if with_limit_violations is not None: + self.with_limit_violations = with_limit_violations + if study_type is not None: + self.study_type = study_type + if provider_parameters is not None: + self.provider_parameters = provider_parameters + + def _init_from_c(self, c_parameters: _pypowsybl.ShortCircuitAnalysisParameters) -> None: + self.with_feeder_result = c_parameters.with_feeder_result + self.with_limit_violations = c_parameters.with_limit_violations + self.study_type = c_parameters.study_type + self.provider_parameters = dict(zip(c_parameters.provider_parameters_keys, c_parameters.provider_parameters_values)) + + def _init_with_default_values(self) -> None: + self._init_from_c(_pypowsybl.ShortCircuitAnalysisParameters()) + self.with_feeder_result = False + self.with_limit_violations = False + self.study_type = ShortCircuitStudyType.TRANSIENT + + def _to_c_parameters(self) -> _pypowsybl.ShortCircuitAnalysisParameters: + c_parameters = _pypowsybl.ShortCircuitAnalysisParameters() + c_parameters.with_voltage_result = False + c_parameters.with_feeder_result = self.with_feeder_result + c_parameters.with_limit_violations = self.with_limit_violations + c_parameters.study_type = self.study_type + c_parameters.with_fortescue_result = False + c_parameters.min_voltage_drop_proportional_threshold = 0 + c_parameters.provider_parameters_keys = [] + c_parameters.provider_parameters_values = [] + return c_parameters + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(" \ + f"with_feeder_result={self.with_feeder_result!r}" \ + f", with_limit_violations={self.with_limit_violations!r}" \ + f", study_type={self.study_type!r}" \ + f")" + +class ShortCircuitAnalysisResult: + """ + The result of a short-circuit analysis. + """ + + def __init__(self, handle: _pypowsybl.JavaHandle): + self._handle = handle + + @property + def fault_results(self) -> _pd.DataFrame: + """ + contains the results, for each fault, in a dataframe representation. + """ + return _create_data_frame_from_series_array(_pypowsybl.get_fault_results(self._handle)) + + @property + def feeder_results(self) -> _pd.DataFrame: + """ + contains the contributions of each feeder to the short circuit current, in a dataframe representation. + """ + return _create_data_frame_from_series_array(_pypowsybl.get_feeder_results(self._handle)) + + @property + def limit_violations(self) -> _pd.DataFrame: + """ + contains a list of all the violations after the fault, in a dataframe representation. + """ + return _create_data_frame_from_series_array(_pypowsybl.get_short_circuit_limit_violations(self._handle)) + + +class ShortCircuitAnalysis(): + """ + Allows to run a short-circuit analysis on a network. + """ + + def __init__(self, handle: _pypowsybl.JavaHandle): + self._handle = handle + + + def _set_faults(self, fault_type: ShortCircuitFaultType, dfs: _List[_Optional[_DataFrame]], + **kwargs: _ArrayLike) -> None: + metadata = _pypowsybl.get_faults_dataframes_metadata(fault_type) + c_dfs = _get_c_dataframes(dfs, [metadata], **kwargs) + _pypowsybl.set_faults(self._handle, c_dfs[0], fault_type) + + + def set_faults(self, df: _DataFrame = None, **kwargs: _ArrayLike) -> None: + """ + Define faults to be analysed in the short-circuit simulation. + + Args: + df: Attributes as a dataframe. + kwargs: Attributes as keyword arguments. + + Notes: + The current implementation allows the simulation of three-phased bus faults, where + the fault resistance and reactance, when specified, are connected to the ground in series. + + Data may be provided as a dataframe or as keyword arguments. + In the latter case, all arguments must have the same length. + + Valid attributes are: + + - **id**: the id of the fault. + - **element_id**: the id of the bus on which the fault will be simulated (bus/view topology). + - **r**: The fault resistance to ground, in Ohm (optional). + - **x**: The fault reactance to ground, in Ohm (optional). + + Examples: + + .. code-block:: + + analysis = pypowsybl.shortcircuit.create_analysis() + + # define a single fault as keyword arguments + analysis.set_faults(id='F1', element_id='Bus1', r= 0, x= 0) + + # or, define multiple faults as keyword arguments + analysis.set_faults(id=['F1', 'F2'], element_id= [ 'Bus1', 'Bus2'], r= [0, 0], x= [0,0]) + + # or, define faults as a dataframe + analysis.set_faults(pd.DataFrame.from_records(index='id', data=[{'id': 'F1', 'element_id': buses.index[0], 'r': 1, 'x': 2}])) + + # or, since resistance and reactance are not mandatory parameters + analysis.set_faults(pd.DataFrame.from_records(index='id', data=[{'id': 'F1', 'element_id': buses.index[0]}])) + """ + self._set_faults(ShortCircuitFaultType.BUS_FAULT, [df], **kwargs) + + + def run(self, network: _Network, parameters: Parameters = None, + provider: str = '', reporter: _Reporter = None) -> ShortCircuitAnalysisResult: + """ Runs an short-circuit analysis. + + Args: + network: Network on which the short-circuit analysis will be computed + parameters: short-circuit analysis parameters + provider: Name of the short-circuit analysis implementation provider to be used. + + Returns: + A short-circuit analysis result. + """ + p = parameters._to_c_parameters() if parameters is not None else Parameters()._to_c_parameters() # pylint: disable=protected-access + + return ShortCircuitAnalysisResult( + _pypowsybl.run_shortcircuit_analysis(self._handle, network._handle, p, provider, + None if reporter is None else reporter._reporter_model) # pylint: disable=protected-access + # pylint: disable=protected-access + ) + +def create_analysis() -> ShortCircuitAnalysis: + """ Creates a short-circuit analysis object, which can be used to run a short-circuit analysis on a network + + Examples: + .. code-block:: + + >>> analysis = pypowsybl.shortcircuit.create_analysis() + >>> analysis.set_faults(id='F1', element_id='Bus1', r= 1, x= 2) + >>> res = analysis.run(network, parameters, provider_name) + + Returns: + A short-circuit analysis object. + """ + return ShortCircuitAnalysis(_pypowsybl.create_shortcircuit_analysis()) + + +def set_default_provider(provider: str) -> None: + """ + Set the default short-circuit analysis provider. + + Args: + provider: name of the default short-circuit analysis provider to set + """ + _pypowsybl.set_default_shortcircuit_analysis_provider(provider) + + +def get_default_provider() -> str: + """ + Get the current default short-circuit analysis provider. + + Returns: + the name of the current default short-circuit analysis provider + """ + return _pypowsybl.get_default_shortcircuit_analysis_provider() + + +def get_provider_names() -> _List[str]: + """ + Get list of supported provider names + + Returns: + the list of supported provider names + """ + return _pypowsybl.get_shortcircuit_provider_names() + + +def get_provider_parameters_names(provider: str = '') -> _List[str]: + """ + Get list of parameters for the specified short-circuit analysis provider. + + If not specified the provider will be the default one. + + Returns: + the list of provider's parameters + """ + return _pypowsybl.get_shortcircuit_provider_parameters_names(provider) diff --git a/tests/test_network.py b/tests/test_network.py index 7614f5a8c..161aa5ade 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -161,8 +161,8 @@ def test_get_export_parameters(): def test_get_export_format(): - formats = pp.network.get_export_formats() - assert ['AMPL', 'CGMES', 'MATPOWER', 'PSS/E', 'UCTE', 'XIIDM'] == formats + formats = set(pp.network.get_export_formats()) + assert set(['AMPL', 'CGMES', 'MATPOWER', 'PSS/E', 'UCTE', 'XIIDM']).intersection(formats) def test_load_network(): diff --git a/tests/test_shortcircuit_analysis.py b/tests/test_shortcircuit_analysis.py new file mode 100644 index 000000000..50cf2d466 --- /dev/null +++ b/tests/test_shortcircuit_analysis.py @@ -0,0 +1,63 @@ +# +# 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 +# +import pypowsybl as pp +import pypowsybl.network as pn +import pytest +import pathlib +import pandas as pd + +TEST_DIR = pathlib.Path(__file__).parent + +@pytest.fixture(autouse=True) +def no_config(): + pp.set_config_read(False) + +def _create_network_with_sc_extensions(): + # reads a network with short'circuit extensions + n = pn.create_four_substations_node_breaker_network() + + # create some short-circuit extensions + n.create_extensions('identifiableShortCircuit', id='S1VL1', ip_min=3.2, ip_max=5.1) + n.create_extensions('generatorShortCircuit', id='GH1', direct_sub_trans_x=9.2, direct_trans_x=2.1, + step_up_transformer_x=5) + return n + +def test_default_provider(): + assert '' == pp.shortcircuit.get_default_provider() + pp.shortcircuit.set_default_provider("provider_test") + assert 'provider_test' == pp.shortcircuit.get_default_provider() + + +def test_run_analysis(): + # reads a network with short'circuit extensions + n = _create_network_with_sc_extensions() + + # sets some short-circuit parameters + pars = pp.shortcircuit.Parameters(with_feeder_result = False, with_limit_violations = False, + study_type = pp.shortcircuit.ShortCircuitStudyType.TRANSIENT) + assert pars is not None + assert pars.with_feeder_result == False + assert pars.with_limit_violations == False + assert pars.study_type == pp.shortcircuit.ShortCircuitStudyType.TRANSIENT + + # create a short-circuit analysis context + sc = pp.shortcircuit.create_analysis() + assert sc is not None + + # define a couple of bus faults on the first two buses + buses = n.get_buses() + print(buses) + + sc.set_faults(pd.DataFrame.from_records(index='id', data=[ + {'id': 'F1', 'element_id': buses.index[0], 'r': 1, 'x': 2}, + {'id': 'F2', 'element_id': buses.index[1], 'r': 1, 'x': 2}, + ])) + + # run the short-circuit analysis using a nonexistent provider + with pytest.raises(Exception, match='No short-circuit analysis provider for name \'provider-unknown\''): + results = sc.run(n, pars, 'provider-unknown')