diff --git a/cpp/src/bindings.cpp b/cpp/src/bindings.cpp index abd3be9a4b..dff480e18b 100644 --- a/cpp/src/bindings.cpp +++ b/cpp/src/bindings.cpp @@ -174,12 +174,12 @@ void voltageInitializerBinding(py::module_& m) { .value("SPECIFIC_VOLTAGE_PROFILE", VoltageInitializerObjective::SPECIFIC_VOLTAGE_PROFILE); m.def("create_voltage_initializer_params", &pypowsybl::createVoltageInitializerParams); - m.def("create_voltage_limit_override", &pypowsybl::createVoltageLimitOverride, py::arg("min_voltage"), py::arg("max_voltage")); m.def("voltage_initializer_add_variable_shunt_compensators", &pypowsybl::voltageInitializerAddVariableShuntCompensators, py::arg("params_handle"), py::arg("id_ptr")); m.def("voltage_initializer_add_constant_q_generators", &pypowsybl::voltageInitializerAddConstantQGenerators, py::arg("params_handle"), py::arg("id_ptr")); m.def("voltage_initializer_add_variable_two_windings_transformers", &pypowsybl::voltageInitializerAddVariableTwoWindingsTransformers, py::arg("params_handle"), py::arg("id_ptr")); - m.def("voltage_initializer_add_specific_voltage_limits", &pypowsybl::voltageInitializerAddSpecificVoltageLimits, py::arg("id_ptr"), py::arg("min_voltage"), py::arg("params_handle"), py::arg("max_voltage")); + m.def("voltage_initializer_add_specific_low_voltage_limits", &pypowsybl::voltageInitializerAddSpecificLowVoltageLimits, py::arg("params_handle"), py::arg("voltage_level_id"), py::arg("is_relative"), py::arg("limit")); + m.def("voltage_initializer_add_specific_high_voltage_limits", &pypowsybl::voltageInitializerAddSpecificHighVoltageLimits, py::arg("params_handle"), py::arg("voltage_level_id"), py::arg("is_relative"), py::arg("limit")); m.def("voltage_initializer_add_algorithm_param", &pypowsybl::voltageInitializerAddAlgorithmParam, py::arg("params_handle"), py::arg("key_ptr"), py::arg("value_ptr")); m.def("voltage_initializer_set_objective", &pypowsybl::voltageInitializerSetObjective, py::arg("params_handle"), py::arg("c_objective")); @@ -326,10 +326,13 @@ PYBIND11_MODULE(_pypowsybl, m) { m.def("load_network_from_binary_buffers", &pypowsybl::loadNetworkFromBinaryBuffers, "Load a network from a list of binary buffer", py::call_guard(), py::arg("buffers"), py::arg("parameters"), py::arg("reporter")); - m.def("dump_network", &pypowsybl::dumpNetwork, "Dump network to a file in a given format", py::call_guard(), + m.def("save_network", &pypowsybl::saveNetwork, "Save network to a file in a given format", py::call_guard(), py::arg("network"), py::arg("file"),py::arg("format"), py::arg("parameters"), py::arg("reporter")); - m.def("dump_network_to_string", &pypowsybl::dumpNetworkToString, "Dump network in a given format", py::call_guard(), + m.def("save_network_to_string", &pypowsybl::saveNetworkToString, "Save network in a given format to a string", py::call_guard(), + py::arg("network"), py::arg("format"), py::arg("parameters"), py::arg("reporter")); + + m.def("save_network_to_binary_buffer", &pypowsybl::saveNetworkToBinaryBuffer, "Save network in a given format to a binary byffer", py::call_guard(), py::arg("network"), py::arg("format"), py::arg("parameters"), py::arg("reporter")); m.def("reduce_network", &pypowsybl::reduceNetwork, "Reduce network", py::call_guard(), @@ -586,17 +589,10 @@ PYBIND11_MODULE(_pypowsybl, m) { m.def("set_zones", &pypowsybl::setZones, "Add zones to sensitivity analysis", py::arg("sensitivity_analysis_context"), py::arg("zones")); - 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_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")); - - m.def("add_postcontingency_branch_flow_factor_matrix", &pypowsybl::addPostContingencyBranchFlowFactorMatrix, "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"), py::arg("contingencies_ids")); - - m.def("set_bus_voltage_factor_matrix", &pypowsybl::setBusVoltageFactorMatrix, "Add a bus_voltage factor matrix to a sensitivity analysis", - py::arg("sensitivity_analysis_context"), py::arg("bus_ids"), py::arg("target_voltage_ids")); + m.def("add_factor_matrix", &pypowsybl::addFactorMatrix, "Add a 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"), + py::arg("sensitivity_variable_type")); m.def("run_sensitivity_analysis", &pypowsybl::runSensitivityAnalysis, "Run a sensitivity analysis", py::call_guard(), py::arg("sensitivity_analysis_context"), py::arg("network"), py::arg("dc"), py::arg("parameters"), py::arg("provider"), py::arg("reporter")); @@ -611,18 +607,12 @@ PYBIND11_MODULE(_pypowsybl, m) { { sizeof(double) * m.column_count, sizeof(double) }); }); - m.def("get_branch_flows_sensitivity_matrix", &pypowsybl::getBranchFlowsSensitivityMatrix, "Get sensitivity analysis result matrix for a given contingency", + m.def("get_sensitivity_matrix", &pypowsybl::getSensitivityMatrix, "Get sensitivity analysis result matrix for a given contingency", py::arg("sensitivity_analysis_result_context"), py::arg("matrix_id"), py::arg("contingency_id")); - m.def("get_bus_voltages_sensitivity_matrix", &pypowsybl::getBusVoltagesSensitivityMatrix, "Get sensitivity analysis result matrix for a given contingency", - py::arg("sensitivity_analysis_result_context"), py::arg("contingency_id")); - - m.def("get_reference_flows", &pypowsybl::getReferenceFlows, "Get sensitivity analysis result reference flows for a given contingency", + m.def("get_reference_matrix", &pypowsybl::getReferenceMatrix, "Get sensitivity analysis result reference matrix for a given contingency", py::arg("sensitivity_analysis_result_context"), py::arg("matrix_id"), py::arg("contingency_id")); - m.def("get_reference_voltages", &pypowsybl::getReferenceVoltages, "Get sensitivity analysis result reference voltages for a given contingency", - py::arg("sensitivity_analysis_result_context"), py::arg("contingency_id")); - py::class_(m, "Series") .def_property_readonly("name", [](const series& s) { return s.name; @@ -691,6 +681,29 @@ PYBIND11_MODULE(_pypowsybl, m) { .value("NONE", contingency_context_type::NONE) .value("SPECIFIC", contingency_context_type::SPECIFIC); + py::enum_(m, "SensitivityFunctionType") + .value("BRANCH_ACTIVE_POWER_1", sensitivity_function_type::BRANCH_ACTIVE_POWER_1) + .value("BRANCH_CURRENT_1",sensitivity_function_type::BRANCH_CURRENT_1) + .value("BRANCH_REACTIVE_POWER_1",sensitivity_function_type::BRANCH_REACTIVE_POWER_1) + .value("BRANCH_ACTIVE_POWER_2",sensitivity_function_type::BRANCH_ACTIVE_POWER_2) + .value("BRANCH_CURRENT_2",sensitivity_function_type::BRANCH_CURRENT_2) + .value("BRANCH_REACTIVE_POWER_2",sensitivity_function_type::BRANCH_REACTIVE_POWER_2) + .value("BRANCH_ACTIVE_POWER_3",sensitivity_function_type::BRANCH_ACTIVE_POWER_3) + .value("BRANCH_CURRENT_3",sensitivity_function_type::BRANCH_CURRENT_3) + .value("BRANCH_REACTIVE_POWER_3",sensitivity_function_type::BRANCH_REACTIVE_POWER_3) + .value("BUS_VOLTAGE",sensitivity_function_type::BUS_VOLTAGE); + + py::enum_(m, "SensitivityVariableType") + .value("AUTO_DETECT", sensitivity_variable_type::AUTO_DETECT) + .value("INJECTION_ACTIVE_POWER", sensitivity_variable_type::INJECTION_ACTIVE_POWER) + .value("INJECTION_REACTIVE_POWER", sensitivity_variable_type::INJECTION_REACTIVE_POWER) + .value("TRANSFORMER_PHASE", sensitivity_variable_type::TRANSFORMER_PHASE) + .value("BUS_TARGET_VOLTAGE", sensitivity_variable_type::BUS_TARGET_VOLTAGE) + .value("HVDC_LINE_ACTIVE_POWER", sensitivity_variable_type::HVDC_LINE_ACTIVE_POWER) + .value("TRANSFORMER_PHASE_1", sensitivity_variable_type::TRANSFORMER_PHASE_1) + .value("TRANSFORMER_PHASE_2", sensitivity_variable_type::TRANSFORMER_PHASE_2) + .value("TRANSFORMER_PHASE_3", sensitivity_variable_type::TRANSFORMER_PHASE_3); + m.def("get_post_contingency_results", &pypowsybl::getPostContingencyResults, "get post contingency results of a security analysis", py::arg("result")); m.def("get_pre_contingency_result", &pypowsybl::getPreContingencyResult, "get pre contingency result of a security analysis", py::arg("result")); m.def("get_node_breaker_view_nodes", &pypowsybl::getNodeBreakerViewNodes, "get all nodes for a voltage level", py::arg("network"), py::arg("voltage_level")); diff --git a/cpp/src/pypowsybl-api.h b/cpp/src/pypowsybl-api.h index 4bdd9be063..2ab7075777 100644 --- a/cpp/src/pypowsybl-api.h +++ b/cpp/src/pypowsybl-api.h @@ -177,6 +177,31 @@ 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, + BUS_VOLTAGE, +} sensitivity_function_type; + +typedef enum { + AUTO_DETECT=0, + INJECTION_ACTIVE_POWER, + INJECTION_REACTIVE_POWER, + TRANSFORMER_PHASE, + BUS_TARGET_VOLTAGE, + HVDC_LINE_ACTIVE_POWER, + TRANSFORMER_PHASE_1, + TRANSFORMER_PHASE_2, + TRANSFORMER_PHASE_3, +} sensitivity_variable_type; + typedef enum { VOLTAGE_LEVEL_TOPOLOGY_CREATION = 0, CREATE_COUPLING_DEVICE, diff --git a/cpp/src/pypowsybl.cpp b/cpp/src/pypowsybl.cpp index e84d73fa48..1c72510e5f 100644 --- a/cpp/src/pypowsybl.cpp +++ b/cpp/src/pypowsybl.cpp @@ -664,7 +664,7 @@ JavaHandle loadNetworkFromBinaryBuffers(std::vector byteBuffers, con return networkHandle; } -void dumpNetwork(const JavaHandle& network, const std::string& file, const std::string& format, const std::map& parameters, JavaHandle* reporter) { +void saveNetwork(const JavaHandle& network, const std::string& file, const std::string& format, const std::map& parameters, JavaHandle* reporter) { std::vector parameterNames; std::vector parameterValues; parameterNames.reserve(parameters.size()); @@ -675,11 +675,11 @@ void dumpNetwork(const JavaHandle& network, const std::string& file, const std:: } ToCharPtrPtr parameterNamesPtr(parameterNames); ToCharPtrPtr parameterValuesPtr(parameterValues); - callJava(::dumpNetwork, network, (char*) file.data(), (char*) format.data(), parameterNamesPtr.get(), parameterNames.size(), + callJava(::saveNetwork, network, (char*) file.data(), (char*) format.data(), parameterNamesPtr.get(), parameterNames.size(), parameterValuesPtr.get(), parameterValues.size(), (reporter == nullptr) ? nullptr : *reporter); } -std::string dumpNetworkToString(const JavaHandle& network, const std::string& format, const std::map& parameters, JavaHandle* reporter) { +std::string saveNetworkToString(const JavaHandle& network, const std::string& format, const std::map& parameters, JavaHandle* reporter) { std::vector parameterNames; std::vector parameterValues; parameterNames.reserve(parameters.size()); @@ -690,10 +690,28 @@ std::string dumpNetworkToString(const JavaHandle& network, const std::string& fo } ToCharPtrPtr parameterNamesPtr(parameterNames); ToCharPtrPtr parameterValuesPtr(parameterValues); - return toString(callJava(::dumpNetworkToString, network, (char*) format.data(), parameterNamesPtr.get(), parameterNames.size(), + return toString(callJava(::saveNetworkToString, network, (char*) format.data(), parameterNamesPtr.get(), parameterNames.size(), parameterValuesPtr.get(), parameterValues.size(), (reporter == nullptr) ? nullptr : *reporter)); } +py::bytes saveNetworkToBinaryBuffer(const JavaHandle& network, const std::string& format, const std::map& parameters, JavaHandle* reporter) { + std::vector parameterNames; + std::vector parameterValues; + parameterNames.reserve(parameters.size()); + parameterValues.reserve(parameters.size()); + for (std::pair p : parameters) { + parameterNames.push_back(p.first); + parameterValues.push_back(p.second); + } + ToCharPtrPtr parameterNamesPtr(parameterNames); + ToCharPtrPtr parameterValuesPtr(parameterValues); + array* byteArray = callJava(::saveNetworkToBinaryBuffer, network, (char*) format.data(), parameterNamesPtr.get(), parameterNames.size(), + parameterValuesPtr.get(), parameterValues.size(), reporter == nullptr ? nullptr : *reporter); + py::bytes bytes((char*) byteArray->ptr, byteArray->length); + callJava<>(::freeNetworkBinaryBuffer, byteArray); + return bytes; +} + void reduceNetwork(const JavaHandle& network, double v_min, double v_max, const std::vector& ids, const std::vector& vls, const std::vector& depths, bool withDangLingLines) { ToCharPtrPtr elementIdPtr(ids); @@ -871,37 +889,15 @@ void setZones(const JavaHandle& sensitivityAnalysisContext, const std::vector<:: callJava(::setZones, sensitivityAnalysisContext, zonesPtr.get(), zones.size()); } -void addBranchFlowFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, - const std::vector& variablesIds) { - ToCharPtrPtr branchIdPtr(branchesIds); - ToCharPtrPtr variableIdPtr(variablesIds); - callJava(::addBranchFlowFactorMatrix, sensitivityAnalysisContext, branchIdPtr.get(), branchesIds.size(), - variableIdPtr.get(), variablesIds.size(), (char*) matrixId.c_str()); -} - -void addPreContingencyBranchFlowFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, - const std::vector& variablesIds) { - ToCharPtrPtr branchIdPtr(branchesIds); - ToCharPtrPtr variableIdPtr(variablesIds); - callJava(::addPreContingencyBranchFlowFactorMatrix, sensitivityAnalysisContext, branchIdPtr.get(), branchesIds.size(), - variableIdPtr.get(), variablesIds.size(), (char*) matrixId.c_str()); -} - -void addPostContingencyBranchFlowFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, - const std::vector& variablesIds, const std::vector& contingenciesIds) { +void addFactorMatrix(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, sensitivity_variable_type sensitivityVariableType) { ToCharPtrPtr branchIdPtr(branchesIds); ToCharPtrPtr variableIdPtr(variablesIds); ToCharPtrPtr contingenciesIdPtr(contingenciesIds); - callJava(::addPostContingencyBranchFlowFactorMatrix, sensitivityAnalysisContext, branchIdPtr.get(), branchesIds.size(), - variableIdPtr.get(), variablesIds.size(), contingenciesIdPtr.get(), contingenciesIds.size(), (char*) matrixId.c_str()); -} - -void setBusVoltageFactorMatrix(const JavaHandle& sensitivityAnalysisContext, const std::vector& busIds, - const std::vector& targetVoltageIds) { - ToCharPtrPtr busVoltageIdPtr(busIds); - ToCharPtrPtr targetVoltageIdPtr(targetVoltageIds); - callJava(::setBusVoltageFactorMatrix, sensitivityAnalysisContext, busVoltageIdPtr.get(), - busIds.size(), targetVoltageIdPtr.get(), targetVoltageIds.size()); + callJava(::addFactorMatrix, sensitivityAnalysisContext, branchIdPtr.get(), branchesIds.size(), + variableIdPtr.get(), variablesIds.size(), contingenciesIdPtr.get(), contingenciesIds.size(), + (char*) matrixId.c_str(), ContingencyContextType, sensitivityFunctionType, sensitivityVariableType); } JavaHandle runSensitivityAnalysis(const JavaHandle& sensitivityAnalysisContext, const JavaHandle& network, bool dc, SensitivityAnalysisParameters& parameters, const std::string& provider, JavaHandle* reporter) { @@ -909,26 +905,16 @@ JavaHandle runSensitivityAnalysis(const JavaHandle& sensitivityAnalysisContext, return callJava(::runSensitivityAnalysis, sensitivityAnalysisContext, network, dc, c_parameters.get(), (char *) provider.data(), (reporter == nullptr) ? nullptr : *reporter); } -matrix* getBranchFlowsSensitivityMatrix(const JavaHandle& sensitivityAnalysisResultContext, const std::string& matrixId, const std::string& contingencyId) { - return callJava(::getBranchFlowsSensitivityMatrix, sensitivityAnalysisResultContext, +matrix* getSensitivityMatrix(const JavaHandle& sensitivityAnalysisResultContext, const std::string& matrixId, const std::string& contingencyId) { + return callJava(::getSensitivityMatrix, sensitivityAnalysisResultContext, (char*) matrixId.c_str(), (char*) contingencyId.c_str()); } -matrix* getBusVoltagesSensitivityMatrix(const JavaHandle& sensitivityAnalysisResultContext, const std::string& contingencyId) { - return callJava(::getBusVoltagesSensitivityMatrix, sensitivityAnalysisResultContext, - (char*) contingencyId.c_str()); -} - -matrix* getReferenceFlows(const JavaHandle& sensitivityAnalysisResultContext, const std::string& matrixId, const std::string& contingencyId) { - return callJava(::getReferenceFlows, sensitivityAnalysisResultContext, +matrix* getReferenceMatrix(const JavaHandle& sensitivityAnalysisResultContext, const std::string& matrixId, const std::string& contingencyId) { + return callJava(::getReferenceMatrix, sensitivityAnalysisResultContext, (char*) matrixId.c_str(), (char*) contingencyId.c_str()); } -matrix* getReferenceVoltages(const JavaHandle& sensitivityAnalysisResultContext, const std::string& contingencyId) { - return callJava(::getReferenceVoltages, sensitivityAnalysisResultContext, - (char*) contingencyId.c_str()); -} - SeriesArray* createNetworkElementsSeriesArray(const JavaHandle& network, element_type elementType, filter_attributes_type filterAttributesType, const std::vector& attributes, dataframe* dataframe) { ToCharPtrPtr attributesPtr(attributes); return new SeriesArray(callJava(::createNetworkElementsSeriesArray, network, elementType, filterAttributesType, attributesPtr.get(), attributes.size(), dataframe)); @@ -1481,12 +1467,12 @@ JavaHandle createVoltageInitializerParams() { return pypowsybl::callJava(::createVoltageInitializerParams); } -JavaHandle createVoltageLimitOverride(double minVoltage, double maxVoltage) { - return pypowsybl::callJava(::createVoltageLimitOverride, minVoltage, maxVoltage); +void voltageInitializerAddSpecificLowVoltageLimits(const JavaHandle& paramsHandle, const std::string& voltageLevelId, bool isRelative, double limit) { + pypowsybl::callJava(::voltageInitializerAddSpecificLowVoltageLimits, paramsHandle, (char*) voltageLevelId.c_str(), isRelative, limit); } -void voltageInitializerAddSpecificVoltageLimits(const std::string& idPtr, double minVoltage, const JavaHandle& paramsHandle, double maxVoltage) { - pypowsybl::callJava(::voltageInitializerAddSpecificVoltageLimits, (char*) idPtr.c_str(), minVoltage, paramsHandle, maxVoltage); +void voltageInitializerAddSpecificHighVoltageLimits(const JavaHandle& paramsHandle, const std::string& voltageLevelId, bool isRelative, double limit) { + pypowsybl::callJava(::voltageInitializerAddSpecificHighVoltageLimits, paramsHandle, (char*) voltageLevelId.c_str(), isRelative, limit); } void voltageInitializerAddVariableShuntCompensators(const JavaHandle& paramsHandle, const std::string& idPtr) { diff --git a/cpp/src/pypowsybl.h b/cpp/src/pypowsybl.h index d6b6e535f1..0c423b1f6d 100644 --- a/cpp/src/pypowsybl.h +++ b/cpp/src/pypowsybl.h @@ -346,7 +346,7 @@ JavaHandle loadNetworkFromString(const std::string& fileName, const std::string& JavaHandle loadNetworkFromBinaryBuffers(std::vector byteBuffer, const std::map& parameters, JavaHandle* reporter); -void dumpNetwork(const JavaHandle& network, const std::string& file, const std::string& format, const std::map& parameters, JavaHandle* reporter); +void saveNetwork(const JavaHandle& network, const std::string& file, const std::string& format, const std::map& parameters, JavaHandle* reporter); LoadFlowParameters* createLoadFlowParameters(); @@ -364,7 +364,9 @@ SensitivityAnalysisParameters* createSensitivityAnalysisParameters(); std::vector getSensitivityAnalysisProviderParametersNames(const std::string& sensitivityAnalysisProvider); -std::string dumpNetworkToString(const JavaHandle& network, const std::string& format, const std::map& parameters, JavaHandle* reporter); +std::string saveNetworkToString(const JavaHandle& network, const std::string& format, const std::map& parameters, JavaHandle* reporter); + +py::bytes saveNetworkToBinaryBuffer(const JavaHandle& network, const std::string& format, const std::map& parameters, JavaHandle* reporter); void reduceNetwork(const JavaHandle& network, const double v_min, const double v_max, const std::vector& ids, const std::vector& vls, const std::vector& depths, bool withDangLingLines); @@ -396,26 +398,15 @@ JavaHandle createSensitivityAnalysis(); void setZones(const JavaHandle& sensitivityAnalysisContext, const std::vector<::zone*>& zones); -void addBranchFlowFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, - const std::vector& variablesIds); - -void addPreContingencyBranchFlowFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, - const std::vector& variablesIds); - -void addPostContingencyBranchFlowFactorMatrix(const JavaHandle& sensitivityAnalysisContext, std::string matrixId, const std::vector& branchesIds, - const std::vector& variablesIds, const std::vector& contingenciesIds); - -void setBusVoltageFactorMatrix(const JavaHandle& sensitivityAnalysisContext, const std::vector& busIds, const std::vector& targetVoltageIds); +void addFactorMatrix(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, sensitivity_variable_type sensitivityVariableType); JavaHandle runSensitivityAnalysis(const JavaHandle& sensitivityAnalysisContext, const JavaHandle& network, bool dc, SensitivityAnalysisParameters& parameters, const std::string& provider, JavaHandle* reporter); -matrix* getBranchFlowsSensitivityMatrix(const JavaHandle& sensitivityAnalysisResultContext, const std::string& matrixId, const std::string &contingencyId); - -matrix* getBusVoltagesSensitivityMatrix(const JavaHandle& sensitivityAnalysisResultContext, const std::string &contingencyId); - -matrix* getReferenceFlows(const JavaHandle& sensitivityAnalysisResultContext, const std::string& matrixId, const std::string& contingencyId); +matrix* getSensitivityMatrix(const JavaHandle& sensitivityAnalysisResultContext, const std::string& matrixId, const std::string &contingencyId); -matrix* getReferenceVoltages(const JavaHandle& sensitivityAnalysisResultContext, const std::string& contingencyId); +matrix* getReferenceMatrix(const JavaHandle& sensitivityAnalysisResultContext, const std::string& matrixId, const std::string& contingencyId); SeriesArray* createNetworkElementsSeriesArray(const JavaHandle& network, element_type elementType, filter_attributes_type filterAttributesType, const std::vector& attributes, dataframe* dataframe); @@ -576,8 +567,8 @@ std::vector getAllDynamicCurvesIds(JavaHandle resultHandle); //=======Voltage initializer mapping======== JavaHandle createVoltageInitializerParams(); -JavaHandle createVoltageLimitOverride(double minVoltage, double maxVoltage); -void voltageInitializerAddSpecificVoltageLimits(const std::string& idPtr, double minVoltage, const JavaHandle& paramsHandle, double maxVoltage); +void voltageInitializerAddSpecificLowVoltageLimits(const JavaHandle& paramsHandle, const std::string& voltageLevelId, bool isRelative, double limit); +void voltageInitializerAddSpecificHighVoltageLimits(const JavaHandle& paramsHandle, const std::string& voltageLevelId, bool isRelative, double limit); void voltageInitializerAddVariableShuntCompensators(const JavaHandle& paramsHandle, const std::string& idPtr); void voltageInitializerAddConstantQGenerators(const JavaHandle& paramsHandle, const std::string& idPtr); void voltageInitializerAddVariableTwoWindingsTransformers(const JavaHandle& paramsHandle, const std::string& idPtr); diff --git a/docs/reference/voltage_initializer.rst b/docs/reference/voltage_initializer.rst index 8a9554d149..fdf6f73442 100644 --- a/docs/reference/voltage_initializer.rst +++ b/docs/reference/voltage_initializer.rst @@ -22,7 +22,8 @@ VoltageInitializerParameters : How to parameterize the tool VoltageInitializerParameters.add_variable_shunt_compensators VoltageInitializerParameters.add_constant_q_generators VoltageInitializerParameters.add_variable_two_windings_transformers - VoltageInitializerParameters.add_specific_voltage_limits + VoltageInitializerParameters.add_specific_low_voltage_limits + VoltageInitializerParameters.add_specific_high_voltage_limits VoltageInitializerParameters.add_algorithm_param VoltageInitializerParameters.set_objective VoltageInitializerParameters.set_objective_distance diff --git a/docs/user_guide/network.rst b/docs/user_guide/network.rst index d78b62523a..ae54346d41 100644 --- a/docs/user_guide/network.rst +++ b/docs/user_guide/network.rst @@ -68,13 +68,19 @@ Networks can be written to the filesystem, using one of the available export for .. code-block:: python - network.dump('network.xiidm', format='XIIDM') + network.save('network.xiidm', format='XIIDM') You can also serialize networks to a string: .. code-block:: python - xiidm_str = network.dump_to_string('XIIDM') + xiidm_str = network.save_to_string('XIIDM') + +And also to a zip file as a (io.BytesIO) binary buffer. + +.. code-block:: python + + zipped_xiidm = network.save_to_binary_buffer('XIIDM') The supported formats are: diff --git a/docs/user_guide/sensitivity.rst b/docs/user_guide/sensitivity.rst index 975233736b..55e7896b34 100644 --- a/docs/user_guide/sensitivity.rst +++ b/docs/user_guide/sensitivity.rst @@ -21,10 +21,10 @@ as a result: >>> analysis = pp.sensitivity.create_dc_analysis() >>> analysis.add_branch_flow_factor_matrix(branches_ids=['NHV1_NHV2_1', 'NHV1_NHV2_2'], variables_ids=['LOAD']) >>> result = analysis.run(network) - >>> result.get_reference_flows() - NHV1_NHV2_1 NHV1_NHV2_2 - reference_flows 300.0 300.0 - >>> result.get_branch_flows_sensitivity_matrix() + >>> result.get_reference_matrix() + NHV1_NHV2_1 NHV1_NHV2_2 + reference_values 300.0 300.0 + >>> result.get_sensitivity_matrix() NHV1_NHV2_1 NHV1_NHV2_2 LOAD -0.5 -0.5 @@ -38,13 +38,13 @@ Several matrix of sensitivity factors can be specified, in that case you must na >>> analysis.add_branch_flow_factor_matrix(branches_ids=['NHV1_NHV2_1', 'NHV1_NHV2_2'], variables_ids=['LOAD'], matrix_id='m1') >>> analysis.add_branch_flow_factor_matrix(branches_ids=['NHV1_NHV2_1'], variables_ids=['GEN'], matrix_id='m2') >>> result = analysis.run(network) - >>> result.get_reference_flows('m1') - NHV1_NHV2_1 NHV1_NHV2_2 - reference_flows 300.0 300.0 - >>> result.get_branch_flows_sensitivity_matrix('m1') + >>> result.get_reference_matrix('m1') + NHV1_NHV2_1 NHV1_NHV2_2 + reference_values 300.0 300.0 + >>> result.get_sensitivity_matrix('m1') NHV1_NHV2_1 NHV1_NHV2_2 LOAD -0.5 -0.5 - >>> result.get_branch_flows_sensitivity_matrix('m2') + >>> result.get_sensitivity_matrix('m2') NHV1_NHV2_1 GEN -0.0 @@ -64,7 +64,7 @@ First, we create a zone containing all generators of DE network with a shift key >>> sa.set_zones([zone_de]) >>> sa.add_branch_flow_factor_matrix(['BBE2AA1 FFR3AA1 1'], ['DE'], 'm') >>> results = sa.run(n, params) - >>> m = results.get_branch_flows_sensitivity_matrix('m') + >>> m = results.get_sensitivity_matrix('m') BBE2AA1 FFR3AA1 1 DE -0.45182 @@ -87,7 +87,7 @@ In the following example, we compute the sensitivity of a active power transfer >>> sa.set_zones([zone_fr, zone_de]) >>> sa.add_branch_flow_factor_matrix(['BBE2AA1 FFR3AA1 1'], ['FR', 'DE'], 'm') >>> results = sa.run(n, params) - >>> m = results.get_branch_flows_sensitivity_matrix('m') + >>> m = results.get_sensitivity_matrix('m') BBE2AA1 FFR3AA1 1 FR -0.708461 DE -0.451820 @@ -107,7 +107,7 @@ Let's obtain that directly. In the following example, we create four zones based >>> sa.set_zones([zone_fr, zone_de, zone_be, zone_nl]) >>> sa.add_branch_flow_factor_matrix(['BBE2AA1 FFR3AA1 1', 'FFR2AA1 DDE3AA1 1'], ['FR', ('FR', 'DE'), ('DE', 'FR'), 'NL'], 'm') >>> result = sa.run(n, params) - >>> m = result.get_branch_flows_sensitivity_matrix('m') + >>> m = result.get_sensitivity_matrix('m') BBE2AA1 FFR3AA1 1 FFR2AA1 DDE3AA1 1 FR -0.708461 0.291539 FR -> DE -0.256641 0.743359 @@ -138,7 +138,7 @@ To calculate to sensitivity of X-Node XXXXXX11 on tie line 'BBE2AA1 FFR3AA1 1' >>> sa.set_zones([zone_x]) >>> sa.add_branch_flow_factor_matrix(['BBE2AA1 FFR3AA1 1'], ['X'], 'm') >>> result = sa.run(n) - >>> result.get_branch_flows_sensitivity_matrix('m') + >>> result.get_sensitivity_matrix('m') BBE2AA1 FFR3AA1 1 X 0.176618 @@ -271,9 +271,9 @@ the list of buses for which you want to compute the sensitivity, and a list of r .. doctest:: >>> analysis = pp.sensitivity.create_ac_analysis() - >>> analysis.set_bus_voltage_factor_matrix(bus_ids=['VLHV1_0', 'VLLOAD_0'], target_voltage_ids=['GEN']) + >>> analysis.add_bus_voltage_factor_matrix(bus_ids=['VLHV1_0', 'VLLOAD_0'], target_voltage_ids=['GEN']) >>> result = analysis.run(network) - >>> result.get_bus_voltages_sensitivity_matrix() + >>> result.get_sensitivity_matrix() VLHV1_0 VLLOAD_0 GEN 17.629602 7.89637 @@ -288,10 +288,10 @@ In previous paragraphs, sensitivities were only computed on pre-contingency situ >>> analysis.add_branch_flow_factor_matrix(branches_ids=['NHV1_NHV2_1', 'NHV1_NHV2_2'], variables_ids=['LOAD'], matrix_id='m') >>> analysis.add_single_element_contingency('NHV1_NHV2_1') >>> result = analysis.run(network) - >>> result.get_reference_flows('m', 'NHV1_NHV2_1') - NHV1_NHV2_1 NHV1_NHV2_2 - reference_flows NaN 600.0 - >>> result.get_branch_flows_sensitivity_matrix('m', 'NHV1_NHV2_1') + >>> result.get_reference_matrix('m', 'NHV1_NHV2_1') + NHV1_NHV2_1 NHV1_NHV2_2 + reference_values NaN 600.0 + >>> result.get_sensitivity_matrix('m', 'NHV1_NHV2_1') NHV1_NHV2_1 NHV1_NHV2_2 LOAD 0.0 -1.0 @@ -308,9 +308,135 @@ and postcontingency_branch_flow_factor_matrix methods. >>> analysis.add_postcontingency_branch_flow_factor_matrix(branches_ids=['NHV1_NHV2_1', 'NHV1_NHV2_2'], variables_ids=['GEN'], contingencies_ids=['NHV1_NHV2_1'], matrix_id='postcontingency') >>> analysis.add_single_element_contingency('NHV1_NHV2_1') >>> result = analysis.run(network) - >>> result.get_branch_flows_sensitivity_matrix('precontingency') + >>> result.get_sensitivity_matrix('precontingency') NHV1_NHV2_1 NHV1_NHV2_2 LOAD -0.5 -0.5 - >>> result.get_branch_flows_sensitivity_matrix('postcontingency', 'NHV1_NHV2_1') + >>> result.get_sensitivity_matrix('postcontingency', 'NHV1_NHV2_1') NHV1_NHV2_1 NHV1_NHV2_2 - GEN 0.0 0.0 \ No newline at end of file + GEN 0.0 0.0 + +Advanced sensitivity analysis factors configuration +--------------------------------------------------- + +For advanced users, a more generic way to create factors is available allowing to define the function and the variable type +(sensitivity is defined as the derivative of the function with respect to the variable). + +.. doctest:: + + >>> analysis = pp.sensitivity.create_ac_analysis() + >>> analysis.add_factor_matrix(functions_ids=['NHV1_NHV2_1'], variables_ids=['LOAD'], contingency_context_type=pp.sensitivity.ContingencyContextType.NONE, contingencies_ids=[], sensitivity_function_type=pp.sensitivity.SensitivityFunctionType.BRANCH_ACTIVE_POWER_2, sensitivity_variable_type=pp.sensitivity.SensitivityVariableType.INJECTION_ACTIVE_POWER) + >>> result = analysis.run(network) + >>> result.get_sensitivity_matrix() + NHV1_NHV2_1 + LOAD 0.501398 + +Here is a table summarizing the possible functions and variables and if it is supported in AC or DC analysis. + +.. list-table:: Supported functions and variables combination + :header-rows: 1 + :stub-columns: 1 + + * - Function \\ Variable + - INJECTION_ACTIVE_POWER + - INJECTION_REACTIVE_POWER + - TRANSFORMER_PHASE + - BUS_TARGET_VOLTAGE + - HVDC_LINE_ACTIVE_POWER + - TRANSFORMER_PHASE_1 + - TRANSFORMER_PHASE_2 + - TRANSFORMER_PHASE_3 + * - BRANCH_ACTIVE_POWER_1 + - AC + DC + - N/A + - AC + DC + - N/A + - AC + DC + - AC + DC + - AC + DC + - AC + DC + * - BRANCH_CURRENT_1 + - AC + DC + - AC + - AC + DC + - AC + - AC + DC + - AC + DC + - AC + DC + - AC + DC + * - BRANCH_REACTIVE_POWER_1 + - N/A + - AC + - N/A + - AC + - N/A + - N/A + - N/A + - N/A + * - BRANCH_ACTIVE_POWER_2 + - AC + DC + - N/A + - AC + DC + - N/A + - AC + DC + - AC + DC + - AC + DC + - AC + DC + * - BRANCH_CURRENT_2 + - AC + DC + - AC + - AC + DC + - AC + - AC + DC + - AC + DC + - AC + DC + - AC + DC + * - BRANCH_REACTIVE_POWER_2 + - N/A + - AC + - N/A + - AC + - N/A + - N/A + - N/A + - N/A + * - BRANCH_ACTIVE_POWER_3 + - AC + DC + - N/A + - AC + DC + - N/A + - AC + DC + - AC + DC + - AC + DC + - AC + DC + * - BRANCH_CURRENT_3 + - AC + DC + - AC + - AC + DC + - AC + - AC + DC + - AC + DC + - AC + DC + - AC + DC + * - BRANCH_REACTIVE_POWER_3 + - N/A + - AC + - N/A + - AC + - N/A + - N/A + - N/A + - N/A + * - BUS_VOLTAGE + - N/A + - AC + - N/A + - AC + - N/A + - N/A + - N/A + - N/A + +A special value of `SensitivityVariableType` `AUTO_DETECT` allows to auto detect each of the variable type using its ID. +It is important to notice that in this case, not all type of sensitivity variable are usable. For instance when an +ID of a busbar section is given as a variable and function is a current flow, the detected variable will be an active +power injection. This is an arbitrary choice because it could also have been a voltage. diff --git a/java/pom.xml b/java/pom.xml index b36647194f..87b09f7d9f 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -69,7 +69,7 @@ 3.0.8 3.6.0 2023.3.1 - 0.2.0 + 0.3.0 1.5.5-3 diff --git a/java/src/main/java/com/powsybl/python/commons/CTypeUtil.java b/java/src/main/java/com/powsybl/python/commons/CTypeUtil.java index 9a2a8572cb..eb67e1f6b9 100644 --- a/java/src/main/java/com/powsybl/python/commons/CTypeUtil.java +++ b/java/src/main/java/com/powsybl/python/commons/CTypeUtil.java @@ -38,6 +38,10 @@ public static CCharPointer toCharPtr(String str) { } // pybind11 convert std::string and char* to python utf-8 string byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + return toCharPtr(bytes); + } + + public static CCharPointer toCharPtr(byte[] bytes) { CCharPointer charPtr = UnmanagedMemory.calloc((bytes.length + 1) * SizeOf.get(CCharPointer.class)); for (int i = 0; i < bytes.length; ++i) { charPtr.write(i, bytes[i]); 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 a1d7888479..026a2a7779 100644 --- a/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java +++ b/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java @@ -636,6 +636,45 @@ 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, + BUS_VOLTAGE; + + @CEnumValue + public native int getCValue(); + + @CEnumLookup + public static native SensitivityFunctionType fromCValue(int value); + } + + @CEnum("sensitivity_variable_type") + public enum SensitivityVariableType { + AUTO_DETECT, + INJECTION_ACTIVE_POWER, + INJECTION_REACTIVE_POWER, + TRANSFORMER_PHASE, + BUS_TARGET_VOLTAGE, + HVDC_LINE_ACTIVE_POWER, + TRANSFORMER_PHASE_1, + TRANSFORMER_PHASE_2, + TRANSFORMER_PHASE_3; + + @CEnumValue + public native int getCValue(); + + @CEnumLookup + public static native SensitivityVariableType 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 9e73a6222e..19fbf97ce6 100644 --- a/java/src/main/java/com/powsybl/python/commons/Util.java +++ b/java/src/main/java/com/powsybl/python/commons/Util.java @@ -19,8 +19,11 @@ 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 com.powsybl.sensitivity.SensitivityVariableType; import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.struct.SizeOf; +import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CCharPointerPointer; import org.graalvm.nativeimage.c.type.CDoublePointer; import org.graalvm.nativeimage.c.type.CIntPointer; @@ -31,7 +34,6 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.function.BooleanSupplier; import java.util.function.IntSupplier; @@ -54,7 +56,8 @@ public static void setException(PyPowsyblApiHeader.ExceptionHandlerPointer excep // we need to create a non null message as on C++ side a null message is considered as non exception to rethrow // typically a NullPointerException has a null message and an empty string message need to be set in order to // correctly handle the exception on C++ side - String nonNullMessage = Objects.toString(t.getMessage(), ""); + String message = t.getMessage(); + String nonNullMessage = message == null || message.isEmpty() ? t.toString() : message; exceptionHandlerPtr.setMessage(CTypeUtil.toCharPtr(nonNullMessage)); } @@ -156,6 +159,10 @@ public static ArrayPointer createIntegerArray(List integer return allocArrayPointer(intListPtr, integerList.size()); } + public static ArrayPointer createByteArray(byte[] bytes) { + return allocArrayPointer(CTypeUtil.toCharPtr(bytes), bytes.length); + } + public static int convert(SeriesDataType type) { return switch (type) { case STRING -> CDataframeHandler.STRING_SERIES_TYPE; @@ -242,6 +249,35 @@ 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; + case BUS_VOLTAGE -> SensitivityFunctionType.BUS_VOLTAGE; + }; + } + + public static SensitivityVariableType convert(PyPowsyblApiHeader.SensitivityVariableType type) { + return switch (type) { + case AUTO_DETECT -> null; + case INJECTION_ACTIVE_POWER -> SensitivityVariableType.INJECTION_ACTIVE_POWER; + case INJECTION_REACTIVE_POWER -> SensitivityVariableType.INJECTION_REACTIVE_POWER; + case TRANSFORMER_PHASE -> SensitivityVariableType.TRANSFORMER_PHASE; + case BUS_TARGET_VOLTAGE -> SensitivityVariableType.BUS_TARGET_VOLTAGE; + case HVDC_LINE_ACTIVE_POWER -> SensitivityVariableType.HVDC_LINE_ACTIVE_POWER; + case TRANSFORMER_PHASE_1 -> SensitivityVariableType.TRANSFORMER_PHASE_1; + case TRANSFORMER_PHASE_2 -> SensitivityVariableType.TRANSFORMER_PHASE_2; + case TRANSFORMER_PHASE_3 -> SensitivityVariableType.TRANSFORMER_PHASE_3; + }; + } + public static ContingencyContextType convert(PyPowsyblApiHeader.RawContingencyContextType type) { return switch (type) { case ALL -> ContingencyContextType.ALL; diff --git a/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java b/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java index 232a989bd8..052bd53c9a 100644 --- a/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java +++ b/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java @@ -60,6 +60,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.util.*; +import java.util.zip.ZipOutputStream; import static com.powsybl.python.commons.CTypeUtil.toStringList; import static com.powsybl.python.commons.PyPowsyblApiHeader.*; @@ -210,8 +211,8 @@ public static ObjectHandle loadNetworkFromBinaryBuffers(IsolateThread thread, CC }); } - @CEntryPoint(name = "dumpNetwork") - public static void dumpNetwork(IsolateThread thread, ObjectHandle networkHandle, CCharPointer file, CCharPointer format, + @CEntryPoint(name = "saveNetwork") + public static void saveNetwork(IsolateThread thread, ObjectHandle networkHandle, CCharPointer file, CCharPointer format, CCharPointerPointer parameterNamesPtrPtr, int parameterNamesCount, CCharPointerPointer parameterValuesPtrPtr, int parameterValuesCount, ObjectHandle reporterHandle, ExceptionHandlerPointer exceptionHandlerPtr) { @@ -228,8 +229,8 @@ public static void dumpNetwork(IsolateThread thread, ObjectHandle networkHandle, }); } - @CEntryPoint(name = "dumpNetworkToString") - public static CCharPointer dumpNetworkToString(IsolateThread thread, ObjectHandle networkHandle, CCharPointer format, + @CEntryPoint(name = "saveNetworkToString") + public static CCharPointer saveNetworkToString(IsolateThread thread, ObjectHandle networkHandle, CCharPointer format, CCharPointerPointer parameterNamesPtrPtr, int parameterNamesCount, CCharPointerPointer parameterValuesPtrPtr, int parameterValuesCount, ObjectHandle reporterHandle, ExceptionHandlerPointer exceptionHandlerPtr) { @@ -260,6 +261,40 @@ public static CCharPointer dumpNetworkToString(IsolateThread thread, ObjectHandl }); } + @CEntryPoint(name = "saveNetworkToBinaryBuffer") + public static ArrayPointer saveNetworkToBinaryBuffer(IsolateThread thread, ObjectHandle networkHandle, CCharPointer format, + CCharPointerPointer parameterNamesPtrPtr, int parameterNamesCount, + CCharPointerPointer parameterValuesPtrPtr, int parameterValuesCount, + ObjectHandle reporterHandle, ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + Network network = ObjectHandles.getGlobal().get(networkHandle); + String formatStr = CTypeUtil.toString(format); + Properties parameters = createParameters(parameterNamesPtrPtr, parameterNamesCount, parameterValuesPtrPtr, parameterValuesCount); + var exporter = Exporter.find(formatStr); + if (exporter == null) { + throw new PowsyblException("No exporter found for '" + formatStr + "' to export as a string"); + } + Reporter reporter = ReportCUtils.getReporter(reporterHandle); + // to support all kind of export: simple file or multiple to an archive, + // best is to write to a zip file + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(bos)) { + DataSource dataSource = new ZipMemDataSource("file", zos); + exporter.export(network, parameters, dataSource, reporter); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + byte[] bytes = bos.toByteArray(); + return Util.createByteArray(bytes); + }); + } + + @CEntryPoint(name = "freeNetworkBinaryBuffer") + public static void freeNetworkBinaryBuffer(IsolateThread thread, PyPowsyblApiHeader.ArrayPointer byteArrayPtr, + PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + doCatch(exceptionHandlerPtr, () -> freeArrayPointer(byteArrayPtr)); + } + @CEntryPoint(name = "reduceNetwork") public static void reduceNetwork(IsolateThread thread, ObjectHandle networkHandle, double vMin, double vMax, diff --git a/java/src/main/java/com/powsybl/python/network/ZipMemDataSource.java b/java/src/main/java/com/powsybl/python/network/ZipMemDataSource.java new file mode 100644 index 0000000000..0533b2f025 --- /dev/null +++ b/java/src/main/java/com/powsybl/python/network/ZipMemDataSource.java @@ -0,0 +1,85 @@ +/** + * 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.network; + +import com.powsybl.commons.datasource.DataSource; +import com.powsybl.commons.datasource.DataSourceUtil; +import com.powsybl.commons.io.ForwardingOutputStream; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Objects; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * @author Geoffroy Jamgotchian {@literal } + */ +public class ZipMemDataSource implements DataSource { + + private final String baseName; + + private final ZipOutputStream zos; + + public ZipMemDataSource(String baseName, ZipOutputStream zos) { + this.baseName = Objects.requireNonNull(baseName); + this.zos = Objects.requireNonNull(zos); + } + + @Override + public OutputStream newOutputStream(String fileName, boolean append) throws IOException { + Objects.requireNonNull(fileName); + if (append) { + throw new UnsupportedOperationException("append not supported in zip file data source"); + } + zos.putNextEntry(new ZipEntry(fileName)); + return new ForwardingOutputStream(zos) { + @Override + public void close() throws IOException { + zos.closeEntry(); + } + }; + } + + @Override + public OutputStream newOutputStream(String suffix, String ext, boolean append) throws IOException { + return newOutputStream(DataSourceUtil.getFileName(baseName, suffix, ext), append); + } + + @Override + public String getBaseName() { + return baseName; + } + + @Override + public boolean exists(String suffix, String ext) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean exists(String fileName) { + throw new UnsupportedOperationException(); + } + + @Override + public InputStream newInputStream(String suffix, String ext) { + throw new UnsupportedOperationException(); + } + + @Override + public InputStream newInputStream(String fileName) { + throw new UnsupportedOperationException(); + } + + @Override + public Set listNames(String regex) { + throw new UnsupportedOperationException(); + } +} 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..3edad065a9 100644 --- a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java +++ b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisCFunctions.java @@ -99,63 +99,24 @@ public static void setZones(IsolateThread thread, ObjectHandle sensitivityAnalys }); } - @CEntryPoint(name = "addBranchFlowFactorMatrix") - public static void addBranchFlowFactorMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisContextHandle, - CCharPointerPointer branchIdPtrPtr, int branchIdCount, - CCharPointerPointer variableIdPtrPtr, int variableIdCount, - CCharPointer matrixIdPtr, - ExceptionHandlerPointer exceptionHandlerPtr) { + @CEntryPoint(name = "addFactorMatrix") + public static void addFactorMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisContextHandle, + CCharPointerPointer branchIdPtrPtr, int branchIdCount, + CCharPointerPointer variableIdPtrPtr, int variableIdCount, + CCharPointerPointer contingenciesIdPtrPtr, int contingenciesIdCount, + CCharPointer matrixIdPtr, + PyPowsyblApiHeader.RawContingencyContextType contingencyContextType, + PyPowsyblApiHeader.SensitivityFunctionType sensitivityFunctionType, + PyPowsyblApiHeader.SensitivityVariableType sensitivityVariableType, + 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); - analysisContext.addBranchFlowFactorMatrix(matrixId, branchesIds, variablesIds); - }); - } - - @CEntryPoint(name = "addPreContingencyBranchFlowFactorMatrix") - public static void addPreContingencyBranchFlowFactorMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisContextHandle, - CCharPointerPointer branchIdPtrPtr, int branchIdCount, - CCharPointerPointer variableIdPtrPtr, int variableIdCount, - CCharPointer matrixIdPtr, - 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); - analysisContext.addPreContingencyBranchFlowFactorMatrix(matrixId, branchesIds, variablesIds); - }); - } - - @CEntryPoint(name = "addPostContingencyBranchFlowFactorMatrix") - public static void addPostContingencyBranchFlowFactorMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisContextHandle, - CCharPointerPointer branchIdPtrPtr, int branchIdCount, - CCharPointerPointer variableIdPtrPtr, int variableIdCount, - CCharPointerPointer contingenciesIdPtrPtr, int contingenciesIdCount, - CCharPointer matrixIdPtr, - ExceptionHandlerPointer exceptionHandlerPtr) { - doCatch(exceptionHandlerPtr, () -> { - SensitivityAnalysisContext analysisContext = ObjectHandles.getGlobal().get(sensitivityAnalysisContextHandle); - List branchesIds = toStringList(branchIdPtrPtr, branchIdCount); - List variablesIds = toStringList(variableIdPtrPtr, variableIdCount); List contingencies = toStringList(contingenciesIdPtrPtr, contingenciesIdCount); - String matrixId = CTypeUtil.toString(matrixIdPtr); - analysisContext.addPostContingencyBranchFlowFactorMatrix(matrixId, branchesIds, variablesIds, contingencies); - }); - } - - @CEntryPoint(name = "setBusVoltageFactorMatrix") - public static void setBusVoltageFactorMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisContextHandle, - CCharPointerPointer busVoltageIdPtrPtr, int branchIdCount, - CCharPointerPointer targetVoltageIdPtrPtr, int injectionOrTransfoIdCount, - ExceptionHandlerPointer exceptionHandlerPtr) { - doCatch(exceptionHandlerPtr, () -> { - SensitivityAnalysisContext analysisContext = ObjectHandles.getGlobal().get(sensitivityAnalysisContextHandle); - List busVoltageIds = toStringList(busVoltageIdPtrPtr, branchIdCount); - List targetVoltageIds = toStringList(targetVoltageIdPtrPtr, injectionOrTransfoIdCount); - analysisContext.setBusVoltageFactorMatrix(busVoltageIds, targetVoltageIds); + analysisContext.addFactorMatrix(matrixId, branchesIds, variablesIds, contingencies, Util.convert(contingencyContextType), Util.convert(sensitivityFunctionType), + Util.convert(sensitivityVariableType)); }); } @@ -176,47 +137,27 @@ public static ObjectHandle runSensitivityAnalysis(IsolateThread thread, ObjectHa }); } - @CEntryPoint(name = "getBranchFlowsSensitivityMatrix") - public static PyPowsyblApiHeader.MatrixPointer getBranchFlowsSensitivityMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisResultContextHandle, - CCharPointer matrixIdPtr, CCharPointer contingencyIdPtr, - ExceptionHandlerPointer exceptionHandlerPtr) { + @CEntryPoint(name = "getSensitivityMatrix") + public static PyPowsyblApiHeader.MatrixPointer getSensitivityMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisResultContextHandle, + CCharPointer matrixIdPtr, CCharPointer contingencyIdPtr, + ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { SensitivityAnalysisResultContext resultContext = ObjectHandles.getGlobal().get(sensitivityAnalysisResultContextHandle); String contingencyId = CTypeUtil.toString(contingencyIdPtr); String matrixId = CTypeUtil.toString(matrixIdPtr); - return resultContext.createBranchFlowsSensitivityMatrix(matrixId, contingencyId); - }); - } - - @CEntryPoint(name = "getBusVoltagesSensitivityMatrix") - public static PyPowsyblApiHeader.MatrixPointer getBusVoltagesSensitivityMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisResultContextHandle, - CCharPointer contingencyIdPtr, ExceptionHandlerPointer exceptionHandlerPtr) { - return doCatch(exceptionHandlerPtr, () -> { - SensitivityAnalysisResultContext resultContext = ObjectHandles.getGlobal().get(sensitivityAnalysisResultContextHandle); - String contingencyId = CTypeUtil.toString(contingencyIdPtr); - return resultContext.createBusVoltagesSensitivityMatrix(contingencyId); + return resultContext.createSensitivityMatrix(matrixId, contingencyId); }); } - @CEntryPoint(name = "getReferenceFlows") - public static PyPowsyblApiHeader.MatrixPointer getReferenceFlows(IsolateThread thread, ObjectHandle sensitivityAnalysisResultContextHandle, - CCharPointer matrixIdPtr, CCharPointer contingencyIdPtr, - ExceptionHandlerPointer exceptionHandlerPtr) { + @CEntryPoint(name = "getReferenceMatrix") + public static PyPowsyblApiHeader.MatrixPointer getReferenceMatrix(IsolateThread thread, ObjectHandle sensitivityAnalysisResultContextHandle, + CCharPointer matrixIdPtr, CCharPointer contingencyIdPtr, + ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { SensitivityAnalysisResultContext resultContext = ObjectHandles.getGlobal().get(sensitivityAnalysisResultContextHandle); String contingencyId = CTypeUtil.toString(contingencyIdPtr); String matrixId = CTypeUtil.toString(matrixIdPtr); - return resultContext.createReferenceFlowsActivePower(matrixId, contingencyId); - }); - } - - @CEntryPoint(name = "getReferenceVoltages") - public static PyPowsyblApiHeader.MatrixPointer getReferenceVoltages(IsolateThread thread, ObjectHandle sensitivityAnalysisResultContextHandle, - CCharPointer contingencyIdPtr, ExceptionHandlerPointer exceptionHandlerPtr) { - return doCatch(exceptionHandlerPtr, () -> { - SensitivityAnalysisResultContext resultContext = ObjectHandles.getGlobal().get(sensitivityAnalysisResultContextHandle); - String contingencyId = CTypeUtil.toString(contingencyIdPtr); - return resultContext.createReferenceVoltages(contingencyId); + return resultContext.createReferenceMatrix(matrixId, contingencyId); }); } 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..d835cc5c62 100644 --- a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisContext.java +++ b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisContext.java @@ -26,11 +26,13 @@ class SensitivityAnalysisContext extends ContingencyContainerImpl { private List variableSets = Collections.emptyList(); - static class MatrixInfo { + public static class MatrixInfo { private final ContingencyContextType contingencyContextType; private final SensitivityFunctionType functionType; + private final SensitivityVariableType variableType; + private final List columnIds; private final List rowIds; @@ -41,9 +43,11 @@ static class MatrixInfo { private int offsetColumn; - MatrixInfo(ContingencyContextType context, SensitivityFunctionType functionType, List columnIds, List rowIds, List contingencyIds) { + MatrixInfo(ContingencyContextType context, SensitivityFunctionType functionType, SensitivityVariableType variableType, + List columnIds, List rowIds, List contingencyIds) { this.contingencyContextType = context; this.functionType = functionType; + this.variableType = variableType; this.columnIds = columnIds; this.rowIds = rowIds; this.contingencyIds = contingencyIds; @@ -57,6 +61,10 @@ SensitivityFunctionType getFunctionType() { return functionType; } + public SensitivityVariableType getVariableType() { + return variableType; + } + void setOffsetData(int offset) { this.offsetData = offset; } @@ -94,59 +102,28 @@ int getColumnCount() { } } - private final Map branchFlowFactorsMatrix = new HashMap<>(); - - private MatrixInfo busVoltageFactorsMatrix; + private final Map factorsMatrix = new HashMap<>(); - void addBranchFlowFactorMatrix(String matrixId, ContingencyContextType contingencyContextType, List branchesIds, - List variablesIds, List contingencyIds) { - if (branchFlowFactorsMatrix.containsKey(matrixId)) { + void addFactorMatrix(String matrixId, List branchesIds, List variablesIds, + List contingencies, ContingencyContextType contingencyContextType, + SensitivityFunctionType sensitivityFunctionType, SensitivityVariableType sensitivityVariableType) { + if (factorsMatrix.containsKey(matrixId)) { throw new PowsyblException("Matrix '" + matrixId + "' already exists."); } - MatrixInfo info = new MatrixInfo(contingencyContextType, SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, branchesIds, variablesIds, contingencyIds); - branchFlowFactorsMatrix.put(matrixId, info); - } - - void addBranchFlowFactorMatrix(String matrixId, List branchesIds, List variablesIds) { - addBranchFlowFactorMatrix(matrixId, ContingencyContextType.ALL, branchesIds, variablesIds, Collections.emptyList()); - } - - void addPreContingencyBranchFlowFactorMatrix(String matrixId, List branchesIds, List variablesIds) { - addBranchFlowFactorMatrix(matrixId, ContingencyContextType.NONE, branchesIds, variablesIds, Collections.emptyList()); - } - - void addPostContingencyBranchFlowFactorMatrix(String matrixId, List branchesIds, List variablesIds, List contingencies) { - addBranchFlowFactorMatrix(matrixId, ContingencyContextType.SPECIFIC, branchesIds, variablesIds, contingencies); + MatrixInfo info = new MatrixInfo(contingencyContextType, sensitivityFunctionType, sensitivityVariableType, branchesIds, variablesIds, contingencies); + factorsMatrix.put(matrixId, info); } public void setVariableSets(List variableSets) { this.variableSets = Objects.requireNonNull(variableSets); } - void setBusVoltageFactorMatrix(List busVoltageIds, List targetVoltageIds) { - busVoltageFactorsMatrix = new MatrixInfo(ContingencyContextType.ALL, SensitivityFunctionType.BUS_VOLTAGE, busVoltageIds, targetVoltageIds, Collections.emptyList()); - } - - private static Injection getInjection(Network network, String injectionId) { - Injection injection = network.getGenerator(injectionId); - if (injection == null) { - injection = network.getLoad(injectionId); - } - if (injection == null) { - injection = network.getLccConverterStation(injectionId); - } - if (injection == null) { - injection = network.getVscConverterStation(injectionId); - } - return injection; - } - List prepareMatrices() { List matrices = new ArrayList<>(); int offsetData = 0; int offsetColumns = 0; - for (MatrixInfo matrix : branchFlowFactorsMatrix.values()) { + for (MatrixInfo matrix : factorsMatrix.values()) { matrix.setOffsetData(offsetData); matrix.setOffsetColumn(offsetColumns); matrices.add(matrix); @@ -154,11 +131,6 @@ List prepareMatrices() { offsetColumns += matrix.getColumnCount(); } - if (busVoltageFactorsMatrix != null) { - busVoltageFactorsMatrix.setOffsetData(offsetData); - busVoltageFactorsMatrix.setOffsetColumn(offsetColumns); - matrices.add(busVoltageFactorsMatrix); - } return matrices; } @@ -178,6 +150,19 @@ int getTotalNumberOfMatrixFactorsColumns(List matrices) { return count; } + private SensitivityVariableType getVariableType(Network network, String variableId) { + Identifiable identifiable = network.getIdentifiable(variableId); + if (identifiable instanceof Injection) { + return SensitivityVariableType.INJECTION_ACTIVE_POWER; + } else if (identifiable instanceof TwoWindingsTransformer) { + return SensitivityVariableType.TRANSFORMER_PHASE; + } else if (identifiable instanceof HvdcLine) { + return SensitivityVariableType.HVDC_LINE_ACTIVE_POWER; + } else { + return null; + } + } + SensitivityAnalysisResultContext run(Network network, SensitivityAnalysisParameters sensitivityAnalysisParameters, String provider, Reporter reporter) { List contingencies = createContingencies(network); @@ -201,63 +186,37 @@ 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); - } - } else { - 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.TRANSFORMER_PHASE, variableId, - false, cCtx); - } - } else { - HvdcLine hvdcLine = network.getHvdcLine(variableId); - if (hvdcLine != null) { - for (ContingencyContext cCtx : contingencyContexts) { - handler.onFactor(SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, branchId, - SensitivityVariableType.HVDC_LINE_ACTIVE_POWER, variableId, - false, cCtx); - } - } else { + switch (matrix.getFunctionType()) { + case BRANCH_ACTIVE_POWER_1, BRANCH_ACTIVE_POWER_2, BRANCH_ACTIVE_POWER_3, + BRANCH_REACTIVE_POWER_1, BRANCH_REACTIVE_POWER_2, BRANCH_REACTIVE_POWER_3, + BRANCH_CURRENT_1, BRANCH_CURRENT_2, BRANCH_CURRENT_3 -> { + for (String variableId : rows) { + for (String branchId : columns) { + SensitivityVariableType variableType = matrix.getVariableType(); + boolean variableSet = false; + if (variableType == null) { + variableType = getVariableType(network, variableId); + if (variableType == null) { if (variableSetsById.containsKey(variableId)) { - for (ContingencyContext cCtx : contingencyContexts) { - handler.onFactor(SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, branchId, - SensitivityVariableType.INJECTION_ACTIVE_POWER, variableId, - true, cCtx); - } + variableSet = true; + variableType = SensitivityVariableType.INJECTION_ACTIVE_POWER; } else { throw new PowsyblException("Variable '" + variableId + "' not found"); } } } + for (ContingencyContext cCtx : contingencyContexts) { + handler.onFactor(matrix.getFunctionType(), branchId, variableType, variableId, variableSet, cCtx); + } } } } - } 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()); + } } } } @@ -295,7 +254,7 @@ public void writeSensitivityValue(int factorContext, int contingencyIndex, doubl @Override public void writeContingencyStatus(int i, SensitivityAnalysisResult.Status status) { - + // nothing to do } }; @@ -318,12 +277,11 @@ public void writeContingencyStatus(int i, SensitivityAnalysisResult.Status statu referencesByContingencyId.put(contingency.getId(), referencesByContingencyIndex[contingencyIndex]); } - return new SensitivityAnalysisResultContext(branchFlowFactorsMatrix, - busVoltageFactorsMatrix, - baseCaseValues, - valuesByContingencyId, - baseCaseReferences, - referencesByContingencyId); + return new SensitivityAnalysisResultContext(factorsMatrix, + baseCaseValues, + valuesByContingencyId, + baseCaseReferences, + referencesByContingencyId); } } diff --git a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisResultContext.java b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisResultContext.java index 2c102bb855..41ef5562f0 100644 --- a/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisResultContext.java +++ b/java/src/main/java/com/powsybl/python/sensitivity/SensitivityAnalysisResultContext.java @@ -21,9 +21,7 @@ */ public class SensitivityAnalysisResultContext { - private final Map branchFlowFactorsMatrix; - - private final SensitivityAnalysisContext.MatrixInfo busVoltageFactorsMatrix; + private final Map factorsMatrix; private final double[] baseCaseValues; @@ -33,12 +31,10 @@ public class SensitivityAnalysisResultContext { private final Map referencesByContingencyId; - public SensitivityAnalysisResultContext(Map branchFlowFactorsMatrix, - SensitivityAnalysisContext.MatrixInfo busVoltageFactorsMatrix, + public SensitivityAnalysisResultContext(Map factorsMatrix, double[] baseCaseValues, Map valuesByContingencyId, double[] baseCaseReferences, Map referencesByContingencyId) { - this.branchFlowFactorsMatrix = branchFlowFactorsMatrix; - this.busVoltageFactorsMatrix = busVoltageFactorsMatrix; + this.factorsMatrix = factorsMatrix; this.baseCaseValues = baseCaseValues; this.valuesByContingencyId = valuesByContingencyId; this.baseCaseReferences = baseCaseReferences; @@ -53,42 +49,24 @@ private double[] getReferences(String contingencyId) { return contingencyId.isEmpty() ? baseCaseReferences : referencesByContingencyId.get(contingencyId); } - private SensitivityAnalysisContext.MatrixInfo getBranchFlowFactorsMatrix(String matrixId) { - SensitivityAnalysisContext.MatrixInfo m = branchFlowFactorsMatrix.get(matrixId); + private SensitivityAnalysisContext.MatrixInfo getFactorsMatrix(String matrixId) { + SensitivityAnalysisContext.MatrixInfo m = factorsMatrix.get(matrixId); if (m == null) { throw new PowsyblException("Matrix '" + matrixId + "' not found"); } return m; } - private SensitivityAnalysisContext.MatrixInfo getBusVoltageFactoryMatrix() { - SensitivityAnalysisContext.MatrixInfo m = busVoltageFactorsMatrix; - if (m == null) { - throw new PowsyblException("bus voltage sensitivity matrix does not exist"); - } - return m; - } - - public PyPowsyblApiHeader.MatrixPointer createBranchFlowsSensitivityMatrix(String matrixId, String contingencyId) { - SensitivityAnalysisContext.MatrixInfo m = getBranchFlowFactorsMatrix(matrixId); + public PyPowsyblApiHeader.MatrixPointer createSensitivityMatrix(String matrixId, String contingencyId) { + SensitivityAnalysisContext.MatrixInfo m = getFactorsMatrix(matrixId); return createDoubleMatrix(() -> getValues(contingencyId), m.getOffsetData(), m.getRowCount(), m.getColumnCount()); } - public PyPowsyblApiHeader.MatrixPointer createBusVoltagesSensitivityMatrix(String contingencyId) { - SensitivityAnalysisContext.MatrixInfo m = getBusVoltageFactoryMatrix(); - return createDoubleMatrix(() -> getValues(contingencyId), m.getOffsetData(), m.getRowCount(), m.getColumnCount()); - } - - public PyPowsyblApiHeader.MatrixPointer createReferenceFlowsActivePower(String matrixId, String contingencyId) { - SensitivityAnalysisContext.MatrixInfo m = getBranchFlowFactorsMatrix(matrixId); + public PyPowsyblApiHeader.MatrixPointer createReferenceMatrix(String matrixId, String contingencyId) { + SensitivityAnalysisContext.MatrixInfo m = getFactorsMatrix(matrixId); return createDoubleMatrix(() -> getReferences(contingencyId), m.getOffsetColumn(), 1, m.getColumnCount()); } - public PyPowsyblApiHeader.MatrixPointer createReferenceVoltages(String contingencyId) { - return createDoubleMatrix(() -> getReferences(contingencyId), busVoltageFactorsMatrix.getOffsetData(), - busVoltageFactorsMatrix.getRowCount(), busVoltageFactorsMatrix.getColumnCount()); - } - private static PyPowsyblApiHeader.MatrixPointer createDoubleMatrix(Supplier srcSupplier, int srcPos, int matRow, int matCol) { final double[] sources = srcSupplier.get(); if (sources == null) { diff --git a/java/src/main/java/com/powsybl/python/voltageinit/VoltageInitializerCFunctions.java b/java/src/main/java/com/powsybl/python/voltageinit/VoltageInitializerCFunctions.java index b55d2f806c..69e2bed7e5 100644 --- a/java/src/main/java/com/powsybl/python/voltageinit/VoltageInitializerCFunctions.java +++ b/java/src/main/java/com/powsybl/python/voltageinit/VoltageInitializerCFunctions.java @@ -10,7 +10,6 @@ import static com.powsybl.python.commons.Util.doCatch; import java.util.List; -import java.util.Map; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.ObjectHandle; @@ -54,21 +53,34 @@ public static ObjectHandle createVoltageInitializerParams(IsolateThread thread, return doCatch(exceptionHandlerPtr, () -> ObjectHandles.getGlobal().create(new OpenReacParameters())); } - @CEntryPoint(name = "createVoltageLimitOverride") - public static ObjectHandle createVoltageLimitOverride(IsolateThread thread, double minVoltage, double maxVoltage, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { - return doCatch(exceptionHandlerPtr, - () -> ObjectHandles.getGlobal().create(new VoltageLimitOverride(minVoltage, maxVoltage))); + @CEntryPoint(name = "voltageInitializerAddSpecificLowVoltageLimits") + public static void addSpecificLowVoltageLimits(IsolateThread thread, ObjectHandle paramsHandle, + CCharPointer idPtr, boolean isRelative, double limit, + PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + OpenReacParameters params = ObjectHandles.getGlobal().get(paramsHandle); + String voltageLevelId = CTypeUtil.toString(idPtr); + doCatch(exceptionHandlerPtr, () -> params + .addSpecificVoltageLimits(List.of( + new VoltageLimitOverride(voltageLevelId, + VoltageLimitOverride.VoltageLimitType.LOW_VOLTAGE_LIMIT, + isRelative, + limit) + ))); } - @CEntryPoint(name = "voltageInitializerAddSpecificVoltageLimits") - public static void addSpecificVoltageLimits(IsolateThread thread, CCharPointer idPtr, double minVoltage, - ObjectHandle paramsHandle, double maxVoltage, - PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { + @CEntryPoint(name = "voltageInitializerAddSpecificHighVoltageLimits") + public static void addSpecificHighVoltageLimits(IsolateThread thread, ObjectHandle paramsHandle, + CCharPointer idPtr, boolean isRelative, double limit, + PyPowsyblApiHeader.ExceptionHandlerPointer exceptionHandlerPtr) { OpenReacParameters params = ObjectHandles.getGlobal().get(paramsHandle); - String voltageId = CTypeUtil.toString(idPtr); + String voltageLevelId = CTypeUtil.toString(idPtr); doCatch(exceptionHandlerPtr, () -> params - .addSpecificVoltageLimits(Map.of(voltageId, new VoltageLimitOverride(minVoltage, maxVoltage)))); + .addSpecificVoltageLimits(List.of( + new VoltageLimitOverride(voltageLevelId, + VoltageLimitOverride.VoltageLimitType.HIGH_VOLTAGE_LIMIT, + isRelative, + limit) + ))); } @CEntryPoint(name = "voltageInitializerAddVariableShuntCompensators") diff --git a/java/src/test/java/com/powsybl/python/sensitivity/SensitivityAnalysisTest.java b/java/src/test/java/com/powsybl/python/sensitivity/SensitivityAnalysisTest.java index e585daed7f..ab3a916192 100644 --- a/java/src/test/java/com/powsybl/python/sensitivity/SensitivityAnalysisTest.java +++ b/java/src/test/java/com/powsybl/python/sensitivity/SensitivityAnalysisTest.java @@ -33,13 +33,13 @@ void testNoOutputMatricesAvailableErrors() { SensitivityAnalysisResultContext result = sensitivityContext.run(network, parameters, "OpenLoadFlow", Reporter.NO_OP); try { - result.createBranchFlowsSensitivityMatrix("", ""); + result.createSensitivityMatrix("", ""); fail(); } catch (PowsyblException ignored) { } try { - result.createBusVoltagesSensitivityMatrix(""); + result.createSensitivityMatrix("m", ""); fail(); } catch (PowsyblException ignored) { } diff --git a/pypowsybl/_pypowsybl.pyi b/pypowsybl/_pypowsybl.pyi index 495e360913..9a0088b1c4 100644 --- a/pypowsybl/_pypowsybl.pyi +++ b/pypowsybl/_pypowsybl.pyi @@ -55,6 +55,51 @@ 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] = ... + BUS_VOLTAGE: 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 SensitivityVariableType: + __members__: ClassVar[Dict[str, SensitivityVariableType]] = ... # read-only + AUTO_DETECT: ClassVar[SensitivityVariableType] = ... + INJECTION_ACTIVE_POWER: ClassVar[SensitivityVariableType] = ... + INJECTION_REACTIVE_POWER: ClassVar[SensitivityVariableType] = ... + TRANSFORMER_PHASE: ClassVar[SensitivityVariableType] = ... + BUS_TARGET_VOLTAGE: ClassVar[SensitivityVariableType] = ... + HVDC_LINE_ACTIVE_POWER: ClassVar[SensitivityVariableType] = ... + TRANSFORMER_PHASE_1: ClassVar[SensitivityVariableType] = ... + TRANSFORMER_PHASE_2: ClassVar[SensitivityVariableType] = ... + TRANSFORMER_PHASE_3: ClassVar[SensitivityVariableType] = ... + 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: ... @@ -553,15 +598,15 @@ def get_extensions_names() -> List[str]: ... def get_extensions_information() -> SeriesArray: ... def create_security_analysis() -> JavaHandle: ... def create_sensitivity_analysis() -> JavaHandle: ... -def dump_network(network: JavaHandle, file: str, format: str, parameters: Dict[str,str], report: Optional[JavaHandle]) -> None: ... -def dump_network_to_string(network: JavaHandle, format: str, parameters: Dict[str,str], report: Optional[JavaHandle]) -> str: ... -def get_branch_flows_sensitivity_matrix(sensitivity_analysis_result_context: JavaHandle, matrix_id: str, contingency_id: str) -> Matrix: ... +def save_network(network: JavaHandle, file: str, format: str, parameters: Dict[str,str], report: Optional[JavaHandle]) -> None: ... +def save_network_to_string(network: JavaHandle, format: str, parameters: Dict[str,str], report: Optional[JavaHandle]) -> str: ... +def save_network_to_binary_buffer(network: JavaHandle, format: str, parameters: Dict[str,str], report: Optional[JavaHandle]) -> bytes: ... +def get_sensitivity_matrix(sensitivity_analysis_result_context: JavaHandle, matrix_id: str, contingency_id: str) -> Matrix: ... def get_branch_results(result: JavaHandle) -> SeriesArray: ... def get_bus_breaker_view_buses(network: JavaHandle, voltage_level: str) -> SeriesArray: ... def get_bus_breaker_view_elements(network: JavaHandle, voltage_level: str) -> SeriesArray: ... def get_bus_breaker_view_switches(network: JavaHandle, voltage_level: str) -> SeriesArray: ... def get_bus_results(result: JavaHandle) -> SeriesArray: ... -def get_bus_voltages_sensitivity_matrix(sensitivity_analysis_result_context: JavaHandle, contingency_id: str) -> Matrix: ... def get_loadflow_provider_parameters_names(provider: str) -> List[str]: ... def create_loadflow_provider_parameters_series_array(provider: str) -> SeriesArray: ... def get_security_analysis_provider_parameters_names(provider: str) -> List[str]: ... @@ -579,8 +624,7 @@ def get_network_metadata(network: JavaHandle) -> NetworkMetadata: ... def get_node_breaker_view_internal_connections(network: JavaHandle, voltage_level: str) -> SeriesArray: ... def get_node_breaker_view_nodes(network: JavaHandle, voltage_level: str) -> SeriesArray: ... def get_node_breaker_view_switches(network: JavaHandle, voltage_level: str) -> SeriesArray: ... -def get_reference_flows(sensitivity_analysis_result_context: JavaHandle, matrix_id: str, contingency_id: str) -> Matrix: ... -def get_reference_voltages(sensitivity_analysis_result_context: JavaHandle, contingency_id: str) -> Matrix: ... +def get_reference_matrix(sensitivity_analysis_result_context: JavaHandle, matrix_id: str, contingency_id: str) -> Matrix: ... def get_post_contingency_results(result: JavaHandle) -> PostContingencyResultArray: ... def get_pre_contingency_result(result: JavaHandle) -> PreContingencyResult: ... def get_network_elements_dataframe_metadata(element_type: ElementType) -> List[SeriesMetadata]: ... @@ -592,9 +636,7 @@ def get_validation_level(network: JavaHandle) -> ValidationLevel: ... def get_variant_ids(network: JavaHandle) -> List[str]: ... def get_version_table() -> str: ... 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_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, sensitivity_variable_type: Optional[SensitivityVariableType]) -> None: ... def is_config_read() -> bool: ... def get_default_loadflow_provider() -> str: ... def get_default_security_analysis_provider() -> str: ... @@ -612,8 +654,6 @@ def run_loadflow(network: JavaHandle, dc: bool, parameters: LoadFlowParameters, def run_loadflow_validation(network: JavaHandle, validation_type: ValidationType, validation_parameters: LoadFlowValidationParameters) -> SeriesArray: ... def run_security_analysis(security_analysis_context: JavaHandle, network: JavaHandle, parameters: SecurityAnalysisParameters, provider: str, dc: bool, report: Optional[JavaHandle]) -> JavaHandle: ... def run_sensitivity_analysis(sensitivity_analysis_context: JavaHandle, network: JavaHandle, dc: bool, parameters: SensitivityAnalysisParameters, provider: str, report: Optional[JavaHandle]) -> JavaHandle: ... -def set_branch_flow_factor_matrix(sensitivity_analysis_context: JavaHandle, branches_ids: List[str], variables_ids: List[str]) -> None: ... -def set_bus_voltage_factor_matrix(sensitivity_analysis_context: JavaHandle, bus_ids: List[str], target_voltage_ids: List[str]) -> None: ... def set_config_read(arg0: bool) -> None: ... def set_logger(logger: Logger) -> None: ... def set_default_loadflow_provider(provider: str) -> None: ... @@ -691,7 +731,6 @@ def get_shortcircuit_provider_names() -> List[str]: ... def get_shortcircuit_provider_parameters_names(provider: str) -> List[str]: ... def create_voltage_initializer_params() -> JavaHandle: ... -def create_voltage_limit_override(min_voltage: float, max_voltage: float) -> JavaHandle: ... def voltage_initializer_add_variable_shunt_compensators( @@ -706,9 +745,11 @@ def voltage_initializer_add_variable_two_windings_transformers( params_handle: JavaHandle, id_ptr: str) -> None: ... -def voltage_initializer_add_specific_voltage_limits( - id_ptr: str, min_voltage: float, params_handle: JavaHandle, max_voltage: float) -> None: ... +def voltage_initializer_add_specific_low_voltage_limits( + params_handle: JavaHandle, voltage_level_id: str, is_relative: bool, limit: float) -> None: ... +def voltage_initializer_add_specific_high_voltage_limits( + params_handle: JavaHandle, voltage_level_id: str, is_relative: bool, limit: float) -> None: ... def voltage_initializer_add_algorithm_param( params_handle: JavaHandle, key_ptr: str, value_ptr: str) -> None: ... diff --git a/pypowsybl/network/impl/network.py b/pypowsybl/network/impl/network.py index 182c893e0b..c254b86e3f 100644 --- a/pypowsybl/network/impl/network.py +++ b/pypowsybl/network/impl/network.py @@ -7,6 +7,7 @@ # from __future__ import annotations # Necessary for type alias like _DataFrame to work with sphinx +import io import sys import datetime @@ -93,7 +94,7 @@ def __repr__(self) -> str: return str(self) def __getstate__(self) -> Dict[str, str]: - return {'xml': self.dump_to_string()} + return {'xml': self.save_to_string()} def __setstate__(self, state: Dict[str, str]) -> None: xml = state['xml'] @@ -123,6 +124,13 @@ def disconnect(self, id: str) -> bool: def dump(self, file: PathOrStr, format: str = 'XIIDM', parameters: ParamsDict = None, reporter: Reporter = None) -> None: """ + Deprecated, use :meth:`save` instead. + """ + self.save(file, format, parameters, reporter) + + def save(self, file: PathOrStr, format: str = 'XIIDM', parameters: ParamsDict = None, + reporter: Reporter = None) -> None: + """ Save a network to a file using the specified format. Basic compression formats are also supported: @@ -139,17 +147,23 @@ def dump(self, file: PathOrStr, format: str = 'XIIDM', parameters: ParamsDict = .. code-block:: python - network.dump('network.xiidm') - network.dump('network.xiidm.gz') # produces a gzipped file - network.dump('/path/to/network.uct', format='UCTE') + network.save('network.xiidm') + network.save('network.xiidm.gz') # produces a gzipped file + network.save('/path/to/network.uct', format='UCTE') """ file = path_to_str(file) if parameters is None: parameters = {} - _pp.dump_network(self._handle, file, format, parameters, + _pp.save_network(self._handle, file, format, parameters, None if reporter is None else reporter._reporter_model) # pylint: disable=protected-access def dump_to_string(self, format: str = 'XIIDM', parameters: ParamsDict = None, reporter: Reporter = None) -> str: + """ + Deprecated, use :meth:`save_to_string` instead. + """ + return self.save_to_string(format, parameters, reporter) + + def save_to_string(self, format: str = 'XIIDM', parameters: ParamsDict = None, reporter: Reporter = None) -> str: """ Save a network to a string using a specified format. @@ -163,9 +177,29 @@ def dump_to_string(self, format: str = 'XIIDM', parameters: ParamsDict = None, r """ if parameters is None: parameters = {} - return _pp.dump_network_to_string(self._handle, format, parameters, + return _pp.save_network_to_string(self._handle, format, parameters, None if reporter is None else reporter._reporter_model) # pylint: disable=protected-access + def save_to_binary_buffer(self, format: str = 'XIIDM', parameters: ParamsDict = None, reporter: Reporter = None) -> io.BytesIO: + """ + Save a network to a binary buffer using a specified format. + In the current implementation, whatever the specified format is (so a format creating a single file or a format + creating multiple files), the created binary buffer is a zip file. + + Args: + format: format to export, only support mono file type, defaults to 'XIIDM' + parameters: a dictionary of export parameters + reporter: the reporter to be used to create an execution report, default is None (no report) + + Returns: + A BytesIO data buffer representing this network + """ + if parameters is None: + parameters = {} + return io.BytesIO(_pp.save_network_to_binary_buffer(self._handle, format, parameters, + None if reporter is None else reporter._reporter_model)) # pylint: disable=protected-access + + def reduce(self, v_min: float = 0, v_max: float = sys.float_info.max, ids: List[str] = None, vl_depths: tuple = (), with_dangling_lines: bool = False) -> None: if ids is None: diff --git a/pypowsybl/sensitivity/__init__.py b/pypowsybl/sensitivity/__init__.py index 5f974a2c07..16db94d9b2 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, SensitivityVariableType, ContingencyContextType from .impl.util import ( create_empty_zone, create_country_zone, diff --git a/pypowsybl/sensitivity/impl/ac_sensitivity_analysis.py b/pypowsybl/sensitivity/impl/ac_sensitivity_analysis.py index ecfd0810e2..fb09c2668f 100644 --- a/pypowsybl/sensitivity/impl/ac_sensitivity_analysis.py +++ b/pypowsybl/sensitivity/impl/ac_sensitivity_analysis.py @@ -9,7 +9,9 @@ from pypowsybl.network import Network from pypowsybl.report import Reporter from pypowsybl.loadflow import Parameters as LfParameters +from pypowsybl._pypowsybl import ContingencyContextType, SensitivityFunctionType, SensitivityVariableType from .ac_sensitivity_analysis_result import AcSensitivityAnalysisResult +from .sensitivity_analysis_result import DEFAULT_MATRIX_ID from .sensitivity import SensitivityAnalysis from .parameters import Parameters @@ -23,14 +25,29 @@ def __init__(self, handle: _pypowsybl.JavaHandle): self.target_voltage_ids: List[str] = [] def set_bus_voltage_factor_matrix(self, bus_ids: List[str], target_voltage_ids: List[str]) -> None: + """ + .. deprecated:: 1.1.0 + Use :meth:`add_bus_voltage_factor_matrix` instead. + + Defines buses voltage sensitivities to be computed. + + Args: + bus_ids: IDs of buses for which voltage sensitivities should be computed + target_voltage_ids: IDs of regulating equipments to which we should compute sensitivities + """ + self.add_bus_voltage_factor_matrix(bus_ids, target_voltage_ids) + + def add_bus_voltage_factor_matrix(self, bus_ids: List[str], target_voltage_ids: List[str], matrix_id: str = DEFAULT_MATRIX_ID) -> None: """ Defines buses voltage sensitivities to be computed. Args: bus_ids: IDs of buses for which voltage sensitivities should be computed target_voltage_ids: IDs of regulating equipments to which we should compute sensitivities + matrix_id: The matrix unique identifier, to be used to retrieve the sensibility value """ - _pypowsybl.set_bus_voltage_factor_matrix(self._handle, bus_ids, target_voltage_ids) + self.add_factor_matrix(bus_ids, target_voltage_ids, [], ContingencyContextType.ALL, + SensitivityFunctionType.BUS_VOLTAGE, SensitivityVariableType.BUS_TARGET_VOLTAGE, matrix_id) self.bus_voltage_ids = bus_ids self.target_voltage_ids = target_voltage_ids @@ -54,5 +71,4 @@ def run(self, network: Network, parameters: Union[Parameters, LfParameters] = No _pypowsybl.run_sensitivity_analysis(self._handle, network._handle, False, p, provider, None if reporter is None else reporter._reporter_model), # pylint: disable=protected-access - branches_ids=self.branches_ids, branch_data_frame_index=self.branch_data_frame_index, - bus_ids=self.bus_voltage_ids, target_voltage_ids=self.target_voltage_ids) + functions_ids=self.functions_ids, function_data_frame_index=self.function_data_frame_index) diff --git a/pypowsybl/sensitivity/impl/ac_sensitivity_analysis_result.py b/pypowsybl/sensitivity/impl/ac_sensitivity_analysis_result.py index f2251bd4d9..b59fa292d2 100644 --- a/pypowsybl/sensitivity/impl/ac_sensitivity_analysis_result.py +++ b/pypowsybl/sensitivity/impl/ac_sensitivity_analysis_result.py @@ -5,29 +5,31 @@ # SPDX-License-Identifier: MPL-2.0 # from typing import Dict, List, Optional -import numpy as np import pandas as pd from pypowsybl import _pypowsybl from .dc_sensitivity_analysis_result import DcSensitivityAnalysisResult +from .sensitivity_analysis_result import DEFAULT_MATRIX_ID 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. """ - def __init__(self, result_context_ptr: _pypowsybl.JavaHandle, branches_ids: Dict[str, List[str]], - branch_data_frame_index: Dict[str, List[str]], - bus_ids: List[str], target_voltage_ids: List[str]): - DcSensitivityAnalysisResult.__init__(self, result_context_ptr, branches_ids, branch_data_frame_index) - self.bus_ids = bus_ids - self.target_voltage_ids = target_voltage_ids + def __init__(self, + result_context_ptr: _pypowsybl.JavaHandle, + functions_ids: Dict[str, List[str]], + function_data_frame_index: Dict[str, List[str]]): + DcSensitivityAnalysisResult.__init__(self, result_context_ptr, functions_ids, function_data_frame_index) - def get_bus_voltages_sensitivity_matrix(self, contingency_id: str = None) -> Optional[pd.DataFrame]: + def get_bus_voltages_sensitivity_matrix(self, matrix_id: str = DEFAULT_MATRIX_ID, contingency_id: str = None) -> Optional[pd.DataFrame]: """ + .. deprecated:: 1.1.0 + Use :meth:`get_sensitivity_matrix` instead. + Get the matrix of bus voltages sensitivities on the base case or on post contingency state. Args: @@ -35,27 +37,19 @@ def get_bus_voltages_sensitivity_matrix(self, contingency_id: str = None) -> Opt Returns: the matrix of sensitivities """ - matrix = _pypowsybl.get_bus_voltages_sensitivity_matrix(self.result_context_ptr, - '' if contingency_id is None else contingency_id) - if matrix is None: - return None - - data = np.array(matrix, copy=False) - return pd.DataFrame(data=data, columns=self.bus_ids, index=self.target_voltage_ids) + return self.get_sensitivity_matrix(matrix_id, contingency_id) - def get_reference_voltages(self, contingency_id: str = None) -> Optional[pd.DataFrame]: + def get_reference_voltages(self, matrix_id: str = DEFAULT_MATRIX_ID, contingency_id: str = None) -> Optional[pd.DataFrame]: """ + .. deprecated:: 1.1.0 + Use :meth:`get_reference_matrix` instead. + The values of bus voltages on the base case or on post contingency state. Args: + matrix_id: ID of the matrix contingency_id: ID of the contingency Returns: the values of bus voltages """ - matrix = _pypowsybl.get_reference_voltages(self.result_context_ptr, - '' if contingency_id is None else contingency_id) - if matrix is None: - return None - - data = np.array(matrix, copy=False) - return pd.DataFrame(data=data, columns=self.bus_ids, index=['reference_voltages']) + return self.get_reference_matrix(matrix_id, contingency_id, 'reference_voltages') diff --git a/pypowsybl/sensitivity/impl/dc_sensitivity_analysis.py b/pypowsybl/sensitivity/impl/dc_sensitivity_analysis.py index 2a148bfb3b..6071b27bd1 100644 --- a/pypowsybl/sensitivity/impl/dc_sensitivity_analysis.py +++ b/pypowsybl/sensitivity/impl/dc_sensitivity_analysis.py @@ -39,4 +39,4 @@ def run(self, network: Network, parameters: Union[Parameters, lfParameters] = No return DcSensitivityAnalysisResult( _pypowsybl.run_sensitivity_analysis(self._handle, network._handle, True, p, provider, None if reporter is None else reporter._reporter_model), # pylint: disable=protected-access - branches_ids=self.branches_ids, branch_data_frame_index=self.branch_data_frame_index) + functions_ids=self.functions_ids, function_data_frame_index=self.function_data_frame_index) diff --git a/pypowsybl/sensitivity/impl/dc_sensitivity_analysis_result.py b/pypowsybl/sensitivity/impl/dc_sensitivity_analysis_result.py index 4998540025..5568e77cb6 100644 --- a/pypowsybl/sensitivity/impl/dc_sensitivity_analysis_result.py +++ b/pypowsybl/sensitivity/impl/dc_sensitivity_analysis_result.py @@ -5,14 +5,12 @@ # SPDX-License-Identifier: MPL-2.0 # from typing import Dict, List, Optional -import numpy as np import pandas as pd from pypowsybl import _pypowsybl +from .sensitivity_analysis_result import SensitivityAnalysisResult, DEFAULT_MATRIX_ID -TO_REMOVE = 'TO_REMOVE' - -class DcSensitivityAnalysisResult: +class DcSensitivityAnalysisResult(SensitivityAnalysisResult): """ Represents the result of a DC sensitivity analysis. @@ -22,16 +20,16 @@ class DcSensitivityAnalysisResult: def __init__(self, result_context_ptr: _pypowsybl.JavaHandle, - branches_ids: Dict[str, List[str]], - branch_data_frame_index: Dict[str, List[str]]): - self._handle = result_context_ptr - self.result_context_ptr = result_context_ptr - self.branches_ids = branches_ids - self.branch_data_frame_index = branch_data_frame_index + functions_ids: Dict[str, List[str]], + function_data_frame_index: Dict[str, List[str]]): + SensitivityAnalysisResult.__init__(self, result_context_ptr, functions_ids, function_data_frame_index) - def get_branch_flows_sensitivity_matrix(self, matrix_id: str = 'default', contingency_id: str = None) -> Optional[ + def get_branch_flows_sensitivity_matrix(self, matrix_id: str = DEFAULT_MATRIX_ID, contingency_id: str = None) -> Optional[ pd.DataFrame]: """ + .. deprecated:: 1.1.0 + Use :meth:`get_sensitivity_matrix` instead. + Get the matrix of branch flows sensitivities on the base case or on post contingency state. If contingency_id is None, returns the base case matrix. @@ -42,28 +40,13 @@ def get_branch_flows_sensitivity_matrix(self, matrix_id: str = 'default', contin Returns: the matrix of branch flows sensitivities """ - matrix = _pypowsybl.get_branch_flows_sensitivity_matrix(self.result_context_ptr, matrix_id, - '' if contingency_id is None else contingency_id) - if matrix is None: - return None - - data = np.array(matrix, copy=False) - - df = pd.DataFrame(data=data, columns=self.branches_ids[matrix_id], - index=self.branch_data_frame_index[matrix_id]) + return self.get_sensitivity_matrix(matrix_id, contingency_id) - # substract second power transfer zone to first one - i = 0 - while i < len(self.branch_data_frame_index[matrix_id]): - if self.branch_data_frame_index[matrix_id][i] == TO_REMOVE: - df.iloc[i - 1] = df.iloc[i - 1] - df.iloc[i] - i += 1 - - # remove rows corresponding to power transfer second zone - return df.drop([TO_REMOVE], errors='ignore') - - def get_reference_flows(self, matrix_id: str = 'default', contingency_id: str = None) -> Optional[pd.DataFrame]: + def get_reference_flows(self, matrix_id: str = DEFAULT_MATRIX_ID, contingency_id: str = None) -> Optional[pd.DataFrame]: """ + .. deprecated:: 1.1.0 + Use :meth:`get_reference_matrix` instead. + The branches active power flows on the base case or on post contingency state. Args: @@ -72,9 +55,4 @@ def get_reference_flows(self, matrix_id: str = 'default', contingency_id: str = Returns: the branches active power flows """ - matrix = _pypowsybl.get_reference_flows(self.result_context_ptr, matrix_id, - '' if contingency_id is None else contingency_id) - if matrix is None: - return None - data = np.array(matrix, copy=False) - return pd.DataFrame(data=data, columns=self.branches_ids[matrix_id], index=['reference_flows']) + return self.get_reference_matrix(matrix_id, contingency_id, 'reference_flows') diff --git a/pypowsybl/sensitivity/impl/sensitivity.py b/pypowsybl/sensitivity/impl/sensitivity.py index 7a89010128..6cb100c09e 100644 --- a/pypowsybl/sensitivity/impl/sensitivity.py +++ b/pypowsybl/sensitivity/impl/sensitivity.py @@ -6,21 +6,21 @@ # from __future__ import annotations from typing import List, Dict + from pypowsybl import _pypowsybl from pypowsybl.security import ContingencyContainer -from pypowsybl._pypowsybl import PyPowsyblError +from .sensitivity_analysis_result import DEFAULT_MATRIX_ID, TO_REMOVE +from pypowsybl._pypowsybl import PyPowsyblError, ContingencyContextType, SensitivityFunctionType, SensitivityVariableType from .zone import Zone -TO_REMOVE = 'TO_REMOVE' - class SensitivityAnalysis(ContingencyContainer): """ Base class for sensitivity analysis. Do not instantiate it directly!""" def __init__(self, handle: _pypowsybl.JavaHandle): ContingencyContainer.__init__(self, handle) - self.branches_ids: Dict[str, List[str]] = {} - self.branch_data_frame_index: Dict[str, List[str]] = {} + self.functions_ids: Dict[str, List[str]] = {} + self.function_data_frame_index: Dict[str, List[str]] = {} def set_zones(self, zones: List[Zone]) -> None: """ @@ -35,7 +35,8 @@ def set_zones(self, zones: List[Zone]) -> None: list(zone.shift_keys_by_injections_ids.values()))) _pypowsybl.set_zones(self._handle, _zones) - def _process_variable_ids(self, variables_ids: List) -> tuple: + @staticmethod + def _process_variable_ids(variables_ids: List) -> tuple: flatten_variables_ids = [] branch_data_frame_index = [] for variable_id in variables_ids: @@ -56,6 +57,7 @@ def _process_variable_ids(self, variables_ids: List) -> tuple: def set_branch_flow_factor_matrix(self, branches_ids: List[str], variables_ids: List[str]) -> None: """ .. deprecated:: 0.14.0 + Use :meth:`add_branch_flow_factor_matrix` instead. Defines branch active power flow factor matrix, with a list of branches IDs and a list of variables. @@ -72,7 +74,7 @@ def set_branch_flow_factor_matrix(self, branches_ids: List[str], variables_ids: self.add_branch_flow_factor_matrix(branches_ids, variables_ids) def add_branch_flow_factor_matrix(self, branches_ids: List[str], variables_ids: List[str], - matrix_id: str = 'default') -> None: + matrix_id: str = DEFAULT_MATRIX_ID) -> None: """ Defines branch active power flow factor matrix, with a list of branches IDs and a list of variables. @@ -86,13 +88,11 @@ def add_branch_flow_factor_matrix(self, branches_ids: List[str], variables_ids: variables_ids: variables which may impact branch flows,to which we should compute sensitivities 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_flow_factor_matrix(self._handle, matrix_id, branches_ids, flatten_variables_ids) - self.branches_ids[matrix_id] = branches_ids - self.branch_data_frame_index[matrix_id] = branch_data_frame_index + self.add_factor_matrix(branches_ids, variables_ids, [], ContingencyContextType.ALL, + SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, SensitivityVariableType.AUTO_DETECT, matrix_id) def add_precontingency_branch_flow_factor_matrix(self, branches_ids: List[str], variables_ids: List[str], - matrix_id: str = 'default') -> None: + matrix_id: str = DEFAULT_MATRIX_ID) -> None: """ Defines branch active power flow factor matrix for the base case, with a list of branches IDs and a list of variables. @@ -106,16 +106,12 @@ def add_precontingency_branch_flow_factor_matrix(self, branches_ids: List[str], variables_ids: variables which may impact branch flows,to which we should compute sensitivities 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_precontingency_branch_flow_factor_matrix(self._handle, matrix_id, branches_ids, - flatten_variables_ids) - self.branches_ids[matrix_id] = branches_ids - self.branch_data_frame_index[matrix_id] = branch_data_frame_index + self.add_factor_matrix(branches_ids, variables_ids, [], ContingencyContextType.NONE, + SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, SensitivityVariableType.AUTO_DETECT, matrix_id) def add_postcontingency_branch_flow_factor_matrix(self, branches_ids: List[str], variables_ids: List[str], contingencies_ids: List[str], - matrix_id: str = 'default') -> None: + matrix_id: str = DEFAULT_MATRIX_ID) -> None: """ Defines branch active power flow factor matrix for specific post contingencies states, with a list of branches IDs and a list of variables. @@ -130,9 +126,55 @@ def add_postcontingency_branch_flow_factor_matrix(self, branches_ids: List[str], contingencies_ids: List of the IDs of the contingencies to simulate 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) + self.add_factor_matrix(branches_ids, variables_ids, contingencies_ids, ContingencyContextType.SPECIFIC, + SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, SensitivityVariableType.AUTO_DETECT, matrix_id) + + def add_factor_matrix(self, functions_ids: List[str], variables_ids: List[str], contingencies_ids: List[str], + contingency_context_type: ContingencyContextType, + sensitivity_function_type: SensitivityFunctionType, + sensitivity_variable_type: SensitivityVariableType = SensitivityVariableType.AUTO_DETECT, + matrix_id: str = DEFAULT_MATRIX_ID) -> None: + """ + Defines branch active power factor matrix, 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 - _pypowsybl.add_postcontingency_branch_flow_factor_matrix(self._handle, matrix_id, branches_ids, - flatten_variables_ids, contingencies_ids) - self.branches_ids[matrix_id] = branches_ids - self.branch_data_frame_index[matrix_id] = branch_data_frame_index + sensitivity_function_type can be: + - 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 + + sensitivity_variable_type can be: + - INJECTION_ACTIVE_POWER + - INJECTION_REACTIVE_POWER + - TRANSFORMER_PHASE + - BUS_TARGET_VOLTAGE + - HVDC_LINE_ACTIVE_POWER + - TRANSFORMER_PHASE_1 + - TRANSFORMER_PHASE_2 + - TRANSFORMER_PHASE_3 + + Args: + functions_ids: functions for which the sensitivities for the sensitivity_function_type should be computed + variables_ids: variables which may impact functions,to which we should compute sensitivities + contingencies_ids: List of the IDs of the contingencies to simulate + contingency_context_type: the contingency context type it could be ALL, NONE or SPECIFIC + sensitivity_function_type: the function type of sensitivity to compute + sensitivity_variable_type: the variable type of sensitivity to compute, automatically guessed (best effort) if value is AUTO_DETECT + matrix_id: The matrix unique identifier, to be used to retrieve the sensibility value + """ + (flatten_variables_ids, function_data_frame_index) = self._process_variable_ids(variables_ids) + _pypowsybl.add_factor_matrix(self._handle, matrix_id, functions_ids, + flatten_variables_ids, contingencies_ids, contingency_context_type, + sensitivity_function_type, sensitivity_variable_type) + self.functions_ids[matrix_id] = functions_ids + self.function_data_frame_index[matrix_id] = function_data_frame_index diff --git a/pypowsybl/sensitivity/impl/sensitivity_analysis_result.py b/pypowsybl/sensitivity/impl/sensitivity_analysis_result.py new file mode 100644 index 0000000000..8ab4c0f452 --- /dev/null +++ b/pypowsybl/sensitivity/impl/sensitivity_analysis_result.py @@ -0,0 +1,89 @@ +# 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 +# +from typing import Dict, List, Optional +import numpy as np +import pandas as pd +from pypowsybl import _pypowsybl + +DEFAULT_REFERENCE_COLUMN_ID = 'reference_values' + +DEFAULT_MATRIX_ID = 'default' +TO_REMOVE = 'TO_REMOVE' + + +class SensitivityAnalysisResult: + """ + Represents the result of a sensitivity analysis. + + The result contains computed values (so called "reference" values) and sensitivity values + of requested factors, on the base case and on post contingency states. + """ + + def __init__(self, + result_context_ptr: _pypowsybl.JavaHandle, + functions_ids: Dict[str, List[str]], + function_data_frame_index: Dict[str, List[str]]): + self._handle = result_context_ptr + self.result_context_ptr = result_context_ptr + self.functions_ids = functions_ids + self.function_data_frame_index = function_data_frame_index + + @staticmethod + def clean_contingency_id(contingency_id: Optional[str]) -> str: + return '' if contingency_id is None else contingency_id + + def process_ptdf(self, df: pd.DataFrame, matrix_id: str) -> pd.DataFrame: + # substract second power transfer zone to first one + i = 0 + while i < len(self.function_data_frame_index[matrix_id]): + if self.function_data_frame_index[matrix_id][i] == TO_REMOVE: + df.iloc[i - 1] = df.iloc[i - 1] - df.iloc[i] + i += 1 + # remove rows corresponding to power transfer second zone + return df.drop([TO_REMOVE], errors='ignore') + + def get_sensitivity_matrix(self, matrix_id: str = DEFAULT_MATRIX_ID, contingency_id: str = None) -> Optional[ + pd.DataFrame]: + """ + Get the matrix of sensitivity values on the base case or on post contingency state. + + If contingency_id is None, returns the base case matrix. + + Args: + matrix_id: ID of the matrix + contingency_id: ID of the contingency + Returns: + the matrix of sensitivity values + """ + matrix = _pypowsybl.get_sensitivity_matrix(self.result_context_ptr, matrix_id, self.clean_contingency_id(contingency_id)) + if matrix is None: + return None + + data = np.array(matrix, copy=False) + + df = pd.DataFrame(data=data, columns=self.functions_ids[matrix_id], + index=self.function_data_frame_index[matrix_id]) + + return self.process_ptdf(df, matrix_id) # only used for PTDF + + def get_reference_matrix(self, matrix_id: str = DEFAULT_MATRIX_ID, contingency_id: str = None, reference_column_id: str = DEFAULT_REFERENCE_COLUMN_ID) -> Optional[pd.DataFrame]: + """ + The reference values on the base case or on post contingency state. + + Args: + matrix_id: ID of the matrix + contingency_id: ID of the contingency + Returns: + the reference values + """ + matrix = _pypowsybl.get_reference_matrix(self.result_context_ptr, matrix_id, self.clean_contingency_id(contingency_id)) + if matrix is None: + return None + + data = np.array(matrix, copy=False) + + return pd.DataFrame(data=data, columns=self.functions_ids[matrix_id], index=[reference_column_id]) diff --git a/pypowsybl/voltage_initializer/impl/voltage_initializer.py b/pypowsybl/voltage_initializer/impl/voltage_initializer.py index 3e0739b706..28d6a30881 100644 --- a/pypowsybl/voltage_initializer/impl/voltage_initializer.py +++ b/pypowsybl/voltage_initializer/impl/voltage_initializer.py @@ -10,7 +10,8 @@ voltage_initializer_add_variable_shunt_compensators, voltage_initializer_add_constant_q_generators, voltage_initializer_add_variable_two_windings_transformers, - voltage_initializer_add_specific_voltage_limits, + voltage_initializer_add_specific_low_voltage_limits, + voltage_initializer_add_specific_high_voltage_limits, voltage_initializer_add_algorithm_param, VoltageInitializerObjective, voltage_initializer_set_objective, @@ -63,16 +64,50 @@ def add_variable_two_windings_transformers(self, transformer_id_list: List[str]) for id in transformer_id_list: voltage_initializer_add_variable_two_windings_transformers(self._handle, id) + def add_specific_low_voltage_limits(self, low_limits: List[Tuple[str, bool, float]]) -> None: + ''' + Indicate to voltage initializer to override the network low voltages limits, + limit can be given relative to former limit or absolute. + High limits can be given for the same voltage level ids using + :func:`~VoltageInitializerParameters.add_specific_high_voltage_limits` + but it is not necessary to give a high limit as long as each voltage level has its limits + defined and consistent after overrides (low limit < high limit, low limit > 0...) + Use this if voltage initializer cannot converge because of infeasibility. + + Args: + low_limits: A List with elements as (voltage level id, is limit relative, limit value) + ''' + for voltage_level_id, is_relative, limit in low_limits: + voltage_initializer_add_specific_low_voltage_limits(self._handle, voltage_level_id, is_relative, limit) + + def add_specific_high_voltage_limits(self, high_limits: List[Tuple[str, bool, float]]) -> None: + ''' + Indicate to voltage initializer to override the network high voltages limits, + limit can be given relative to previous limit or absolute. + Low limits can be given for the same voltage level ids using + :func:`~VoltageInitializerParameters.add_specific_low_voltage_limits` + but it is not necessary to give a low limit as long as each voltage level has its limits + defined and consistent after overrides (low limit < high limit, low limit > 0...) + Use this if voltage initializer cannot converge because of infeasibility. + + Args: + high_limits: A List with elements as (voltage level id, is limit relative, limit value) + ''' + for voltage_level_id, is_relative, limit in high_limits: + voltage_initializer_add_specific_high_voltage_limits(self._handle, voltage_level_id, is_relative, limit) + def add_specific_voltage_limits(self, limits: Dict[str, Tuple[float, float]]) -> None: ''' Indicate to voltage initializer to override the network voltages limits. + Limits are given relative to previous limits. Use this if voltage initializer cannot converge because of infeasibility. Args: limits: A dictionary keys are voltage ids, values are (lower limit, upper limit) ''' for key in limits: - voltage_initializer_add_specific_voltage_limits(key, limits[key][0], self._handle, limits[key][1]) + self.add_specific_low_voltage_limits([(key, True, limits[key][0])]) + self.add_specific_high_voltage_limits([(key, True, limits[key][1])]) def add_algorithm_param(self, parameters_dict: Dict[str, str]) -> None: ''' diff --git a/tests/test_network.py b/tests/test_network.py index 9db04985df..9445ec84b9 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -12,6 +12,7 @@ import tempfile import unittest import io +import zipfile from os.path import exists import matplotlib.pyplot as plt @@ -60,25 +61,32 @@ def test_load_cgmes_two_zip(): assert 3 == len(n.get_substations()) +def test_save_cgmes_zip(): + n = pp.network.create_eurostag_tutorial_example1_network() + buffer = n.save_to_binary_buffer(format='CGMES') + with zipfile.ZipFile(buffer, 'r') as zip_file: + assert ['file_EQ.xml', 'file_TP.xml', 'file_SSH.xml', 'file_SV.xml'] == zip_file.namelist() + + def test_load_zipped_xiidm(): with open(DATA_DIR.joinpath('battery_xiidm.zip'), "rb") as fh: n = pp.network.load_from_binary_buffer(io.BytesIO(fh.read())) assert 2 == len(n.get_substations()) -def test_dump_to_string(): +def test_save_to_string(): bat_path = TEST_DIR.joinpath('battery.xiidm') xml = bat_path.read_text() n = pp.network.load(str(bat_path)) - assert xml == n.dump_to_string() + assert xml == n.save_to_string() -def test_dump_ampl(): +def test_save_ampl(): n = pp.network.create_eurostag_tutorial_example1_network() with tempfile.TemporaryDirectory() as tmp_dir_name: tmp_dir_path = pathlib.Path(tmp_dir_name) ampl_base_file = tmp_dir_path.joinpath('ampl') - n.dump(ampl_base_file, format='AMPL') + n.save(ampl_base_file, format='AMPL') file_names = os.listdir(tmp_dir_path) file_names_expected = ['ampl_network_vsc_converter_stations.txt', 'ampl_network_branches.txt', 'ampl_network_rtc.txt', 'ampl_network_generators.txt', @@ -91,41 +99,41 @@ def test_dump_ampl(): assert file_name in file_names_expected -def test_dump_import_iidm(): +def test_save_import_iidm(): n = pp.network.create_eurostag_tutorial_example1_network() with tempfile.TemporaryDirectory() as tmp_dir_name: tmp_dir_path = pathlib.Path(tmp_dir_name) iidm_file = tmp_dir_path.joinpath('test.xiidm') - n.dump(iidm_file, format='XIIDM') + n.save(iidm_file, format='XIIDM') file_names = os.listdir(tmp_dir_path) assert len(file_names) == 1 assert 'test.xiidm' in file_names n2 = pp.network.load(iidm_file) - assert n2.dump_to_string() == n.dump_to_string() + assert n2.save_to_string() == n.save_to_string() assert isinstance(n2, pp.network.Network) -def test_dump_matpower(): +def test_save_matpower(): n = pp.network.create_eurostag_tutorial_example1_network() with tempfile.TemporaryDirectory() as tmp_dir_name: tmp_dir_path = pathlib.Path(tmp_dir_name) mat_file = tmp_dir_path.joinpath('test.mat') - n.dump(mat_file, format='MATPOWER') + n.save(mat_file, format='MATPOWER') file_names = os.listdir(tmp_dir_path) assert len(file_names) == 1 assert 'test.mat' in file_names n2 = pp.network.load(mat_file) assert isinstance(n2, pp.network.Network) - # assert n2.dump_to_string() == n.dump_to_string() # problem import/export matpower + # assert n2.save_to_string() == n.save_to_string() # problem import/export matpower -def test_dump_ucte(): +def test_save_ucte(): ucte_local_path = TEST_DIR.joinpath('test.uct') n = pp.network.load(str(ucte_local_path)) with tempfile.TemporaryDirectory() as tmp_dir_name: tmp_dir_path = pathlib.Path(tmp_dir_name) ucte_temporary_path = tmp_dir_path.joinpath('test.uct') - n.dump(ucte_temporary_path, format='UCTE') + n.save(ucte_temporary_path, format='UCTE') file_names = os.listdir(tmp_dir_path) assert len(file_names) == 1 assert 'test.uct' in file_names @@ -1708,15 +1716,15 @@ def test_properties(): assert 'dataframe can not contain NaN values' in str(exc) -def test_pathlib_load_dump(tmpdir): +def test_pathlib_load_save(tmpdir): bat_path = TEST_DIR.joinpath('battery.xiidm') n_path = pp.network.load(bat_path) n_str = pp.network.load(str(bat_path)) - assert n_path.dump_to_string() == n_str.dump_to_string() + assert n_path.save_to_string() == n_str.save_to_string() data = tmpdir.mkdir('data') - n_path.dump(data.join('test.xiidm')) + n_path.save(data.join('test.xiidm')) n_path = pp.network.load(data.join('test.xiidm')) - assert n_path.dump_to_string() == n_str.dump_to_string() + assert n_path.save_to_string() == n_str.save_to_string() def test_write_svg_file(tmpdir): @@ -1771,7 +1779,7 @@ def test_load_network_from_string_with_report(): assert len(report2) > len(report1) -def test_dump_to_string_with_report(): +def test_save_to_string_with_report(): bat_path = TEST_DIR.joinpath('battery.xiidm') reporter = rp.Reporter() report1 = str(reporter) diff --git a/tests/test_sensitivity_analysis.py b/tests/test_sensitivity_analysis.py index ab55424368..37e39f2b33 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, SensitivityVariableType, ContingencyContextType TEST_DIR = pathlib.Path(__file__).parent DATA_DIR = TEST_DIR.parent.joinpath('data') @@ -34,7 +35,7 @@ def test_config(): with pytest.raises(PyPowsyblError, match='No sensitivity analysis provider for name \'provider\''): sa.run(n) r = sa.run(n, provider='OpenLoadFlow') - assert 6 == r.get_branch_flows_sensitivity_matrix().size + assert 6 == r.get_sensitivity_matrix().size assert 'provider' == pp.sensitivity.get_default_provider() pp.sensitivity.set_default_provider('OpenLoadFlow') assert 'OpenLoadFlow' == pp.sensitivity.get_default_provider() @@ -54,7 +55,7 @@ def test_sensitivity_analysis(): sa.add_postcontingency_branch_flow_factor_matrix(['L1-5-1', 'L2-3-1'], ['B1-G'], ['L1-2-1'], 'postContingency') r = sa.run(n) - df = r.get_branch_flows_sensitivity_matrix('m') + df = r.get_sensitivity_matrix('m') assert (3, 2) == df.shape assert df['L1-5-1']['B1-G'] == pytest.approx(0.080991, abs=1e-6) assert df['L1-5-1']['B2-G'] == pytest.approx(-0.080991, abs=1e-6) @@ -63,12 +64,12 @@ def test_sensitivity_analysis(): assert df['L2-3-1']['B2-G'] == pytest.approx(0.013675, abs=1e-6) assert df['L2-3-1']['B3-G'] == pytest.approx(-0.545682, abs=1e-6) - df = r.get_reference_flows('m') + df = r.get_reference_matrix('m') assert df.shape == (1, 2) - assert df['L1-5-1']['reference_flows'] == pytest.approx(72.246, abs=1e-3) - assert df['L2-3-1']['reference_flows'] == pytest.approx(69.831, abs=1e-3) + assert df['L1-5-1']['reference_values'] == pytest.approx(72.246, abs=1e-3) + assert df['L2-3-1']['reference_values'] == pytest.approx(69.831, abs=1e-3) - df = r.get_branch_flows_sensitivity_matrix('m', 'L1-2-1') + df = r.get_sensitivity_matrix('m', 'L1-2-1') assert df.shape == (3, 2) assert df['L1-5-1']['B1-G'] == pytest.approx(0.5, abs=1e-6) assert df['L1-5-1']['B2-G'] == pytest.approx(-0.5, abs=1e-6) @@ -77,18 +78,18 @@ def test_sensitivity_analysis(): assert df['L2-3-1']['B2-G'] == pytest.approx(0.084423, abs=1e-6) assert df['L2-3-1']['B3-G'] == pytest.approx(-0.490385, abs=1e-6) - df = r.get_reference_flows('m', 'L1-2-1') + df = r.get_reference_matrix('m', 'L1-2-1') assert df.shape == (1, 2) - assert df['L1-5-1']['reference_flows'] == pytest.approx(225.7, abs=1e-3) - assert df['L2-3-1']['reference_flows'] == pytest.approx(43.921, abs=1e-3) + assert df['L1-5-1']['reference_values'] == pytest.approx(225.7, abs=1e-3) + assert df['L2-3-1']['reference_values'] == pytest.approx(43.921, abs=1e-3) - assert r.get_branch_flows_sensitivity_matrix('m', 'aaa') is None + assert r.get_sensitivity_matrix('m', 'aaa') is None - df = r.get_branch_flows_sensitivity_matrix('preContingency') + df = r.get_sensitivity_matrix('preContingency') assert df.shape == (1, 2) assert df['L1-5-1']['B1-G'] == pytest.approx(0.080991, abs=1e-6) assert df['L2-3-1']['B1-G'] == pytest.approx(-0.013675, abs=1e-6) - df = r.get_branch_flows_sensitivity_matrix('postContingency', 'L1-2-1') + df = r.get_sensitivity_matrix('postContingency', 'L1-2-1') assert df.shape == (1, 2) assert df['L1-5-1']['B1-G'] == pytest.approx(0.5, abs=1e-6) assert df['L2-3-1']['B1-G'] == pytest.approx(-0.084423, abs=1e-6) @@ -97,9 +98,9 @@ def test_sensitivity_analysis(): def test_voltage_sensitivities(): n = pp.network.create_eurostag_tutorial_example1_network() sa = pp.sensitivity.create_ac_analysis() - sa.set_bus_voltage_factor_matrix(['VLGEN_0'], ['GEN']) + sa.add_bus_voltage_factor_matrix(['VLGEN_0'], ['GEN']) r = sa.run(n) - df = r.get_bus_voltages_sensitivity_matrix() + df = r.get_sensitivity_matrix() assert df.shape == (1, 1) assert df['VLGEN_0']['GEN'] == pytest.approx(1.0, abs=1e-6) @@ -151,16 +152,16 @@ def test_sensi_zone(): sa.set_zones([zone_fr, zone_be]) sa.add_branch_flow_factor_matrix(['BBE2AA1 FFR3AA1 1', 'FFR2AA1 DDE3AA1 1'], ['FR', 'BE'], 'm') result = sa.run(n) - s = result.get_branch_flows_sensitivity_matrix('m') + s = result.get_sensitivity_matrix('m') assert s.shape == (2, 2) assert s['BBE2AA1 FFR3AA1 1']['FR'] == pytest.approx(-0.379829, abs=1e-6) assert s['FFR2AA1 DDE3AA1 1']['FR'] == pytest.approx(0.370171, abs=1e-6) assert s['BBE2AA1 FFR3AA1 1']['BE'] == pytest.approx(0.378423, abs=1e-6) assert s['FFR2AA1 DDE3AA1 1']['BE'] == pytest.approx(0.128423, abs=1e-6) - r = result.get_reference_flows('m') + r = result.get_reference_matrix('m') assert r.shape == (1, 2) - assert r['BBE2AA1 FFR3AA1 1']['reference_flows'] == pytest.approx(324.666, abs=1e-3) - assert r['FFR2AA1 DDE3AA1 1']['reference_flows'] == pytest.approx(1324.666, abs=1e-3) + assert r['BBE2AA1 FFR3AA1 1']['reference_values'] == pytest.approx(324.666, abs=1e-3) + assert r['FFR2AA1 DDE3AA1 1']['reference_values'] == pytest.approx(1324.666, abs=1e-3) def test_sensi_power_transfer(): @@ -174,7 +175,7 @@ def test_sensi_power_transfer(): sa.add_branch_flow_factor_matrix(['BBE2AA1 FFR3AA1 1', 'FFR2AA1 DDE3AA1 1'], ['FR', ('FR', 'DE'), ('DE', 'FR'), 'NL'], 'm') result = sa.run(n) - s = result.get_branch_flows_sensitivity_matrix('m') + s = result.get_sensitivity_matrix('m') assert s.shape == (4, 2) assert s['BBE2AA1 FFR3AA1 1']['FR'] == pytest.approx(-0.379829, abs=1e-6) assert s['BBE2AA1 FFR3AA1 1']['FR -> DE'] == pytest.approx(-0.256641, abs=1e-6) @@ -195,7 +196,7 @@ def test_xnode_sensi(): sa.set_zones([zone_x]) sa.add_branch_flow_factor_matrix(['BBE2AA1 FFR3AA1 1'], ['X'], 'm') result = sa.run(n) - s = result.get_branch_flows_sensitivity_matrix('m') + s = result.get_sensitivity_matrix('m') assert s.shape == (1, 1) assert s['BBE2AA1 FFR3AA1 1']['X'] == pytest.approx(0.176618, abs=1e-6) @@ -211,7 +212,7 @@ def test_variant(): sa.add_branch_flow_factor_matrix(['L1-5-1'], ['B1-G'], 'm') r = sa.run(n) - df = r.get_branch_flows_sensitivity_matrix('m') + df = r.get_sensitivity_matrix('m') assert (1, 1) == df.shape assert df['L1-5-1']['B1-G'] == pytest.approx(0.080991, abs=1e-6) @@ -220,7 +221,7 @@ def test_variant(): n.update_lines(id='L2-3-1', connected1=False) r = sa.run(n) - df = r.get_branch_flows_sensitivity_matrix('m') + df = r.get_sensitivity_matrix('m') assert (1, 1) == df.shape assert df['L1-5-1']['B1-G'] == pytest.approx(0.078151, abs=1e-6) @@ -235,15 +236,11 @@ def test_no_output_matrices_available(): analysis.set_branch_flow_factor_matrix(network.get_lines().index.to_list(), network.get_generators().index.to_list()) result = analysis.run(network) - df = result.get_branch_flows_sensitivity_matrix('default') + df = result.get_sensitivity_matrix('default') assert (2, 2) == df.shape with pytest.raises(pp.PyPowsyblError) as errorContext: - result.get_bus_voltages_sensitivity_matrix() - assert 'bus voltage sensitivity matrix does not exist' == str(errorContext.value) - - with pytest.raises(pp.PyPowsyblError) as errorContext: - result.get_branch_flows_sensitivity_matrix('') + result.get_sensitivity_matrix('') assert 'Matrix \'\' not found' == str(errorContext.value) @@ -258,7 +255,7 @@ def test_provider_parameters(): analysis.run(n, parameters) # does not throw result = analysis.run(n) - assert result.get_reference_flows().loc['reference_flows', 'NHV1_NHV2_1'] == pytest.approx(302.45, abs=0.01) + assert result.get_reference_matrix().loc['reference_values', 'NHV1_NHV2_1'] == pytest.approx(302.45, abs=0.01) def test_voltage_sensitivities_with_report(): @@ -267,7 +264,7 @@ def test_voltage_sensitivities_with_report(): assert len(report1) > 0 n = pp.network.create_eurostag_tutorial_example1_network() sa = pp.sensitivity.create_ac_analysis() - sa.set_bus_voltage_factor_matrix(['VLGEN_0'], ['GEN']) + sa.add_bus_voltage_factor_matrix(['VLGEN_0'], ['GEN']) r = sa.run(n, reporter=reporter) report2 = str(reporter) assert len(report2) > len(report1) @@ -283,14 +280,14 @@ def test_sensitivity_parameters(): parameters.load_flow_parameters.distributed_slack = True parameters.load_flow_parameters.balance_type = pp.loadflow.BalanceType.PROPORTIONAL_TO_GENERATION_P result = analysis.run(n, parameters) - assert result.get_reference_flows().loc['reference_flows', 'NHV1_NHV2_1'] == pytest.approx(302.45, abs=0.01) - assert result.get_branch_flows_sensitivity_matrix().loc['GEN', 'NHV1_NHV2_1'] == 0 + assert result.get_reference_matrix().loc['reference_values', 'NHV1_NHV2_1'] == pytest.approx(302.45, abs=0.01) + assert result.get_sensitivity_matrix().loc['GEN', 'NHV1_NHV2_1'] == 0 # 2. distributing on loads parameters.load_flow_parameters.balance_type = pp.loadflow.BalanceType.PROPORTIONAL_TO_LOAD result = analysis.run(n, parameters) - assert result.get_reference_flows().loc['reference_flows', 'NHV1_NHV2_1'] == pytest.approx(605.35, abs=0.01) - assert result.get_branch_flows_sensitivity_matrix().loc['GEN', 'NHV1_NHV2_1'] == pytest.approx(0.53, abs=0.01) + assert result.get_reference_matrix().loc['reference_values', 'NHV1_NHV2_1'] == pytest.approx(605.35, abs=0.01) + assert result.get_sensitivity_matrix().loc['GEN', 'NHV1_NHV2_1'] == pytest.approx(0.53, abs=0.01) def test_provider_parameters_names(): @@ -305,6 +302,34 @@ def test_hvdc(): analysis = pp.sensitivity.create_dc_analysis() analysis.set_branch_flow_factor_matrix(["LINE_S2S3"], ["HVDC1"]) results = analysis.run(network) - 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) + assert {'default': ['HVDC1']} == results.function_data_frame_index + assert {'default': ['LINE_S2S3']} == results.functions_ids + pytest.approx(results.get_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_ac_analysis() + + analysis.add_factor_matrix(['LINE_S3S4'], ['GTH2'], [], ContingencyContextType.NONE, + SensitivityFunctionType.BRANCH_REACTIVE_POWER_1, SensitivityVariableType.BUS_TARGET_VOLTAGE, 'test') + analysis.add_factor_matrix(['LINE_S2S3'], ['GTH1'], [], ContingencyContextType.NONE, + SensitivityFunctionType.BRANCH_ACTIVE_POWER_1, SensitivityVariableType.INJECTION_ACTIVE_POWER, 'test1') + analysis.add_factor_matrix(['LINE_S3S4'], ['GTH2'], [], ContingencyContextType.NONE, + SensitivityFunctionType.BRANCH_CURRENT_1, SensitivityVariableType.BUS_TARGET_VOLTAGE, 'test2') + result = analysis.run(network) + assert 30.5280 == pytest.approx(result.get_sensitivity_matrix('test').loc['GTH2']['LINE_S3S4'], 1e-4) + assert 0.8 == result.get_sensitivity_matrix('test1').loc['GTH1']['LINE_S2S3'] + assert -0.4668 == pytest.approx(result.get_sensitivity_matrix('test2').loc['GTH2']['LINE_S3S4'], 1e-4) + + +def test_busbar_section_sensi(): + network = pp.network.create_four_substations_node_breaker_network() + analysis = pp.sensitivity.create_ac_analysis() + print(network.get_busbar_sections()) + print(network.get_lines()) + print(network.get_generators()) + analysis.add_factor_matrix(['LINE_S2S3'], ['S2VL1_BBS'], [], ContingencyContextType.NONE, + SensitivityFunctionType.BRANCH_ACTIVE_POWER_2, SensitivityVariableType.INJECTION_ACTIVE_POWER, 'm1') + result = analysis.run(network) + assert -0.8 == pytest.approx(result.get_sensitivity_matrix('m1').loc['S2VL1_BBS']['LINE_S2S3'], 1e-4) diff --git a/tests/test_voltage_initializer.py b/tests/test_voltage_initializer.py index c3c8c503c7..a610233f48 100644 --- a/tests/test_voltage_initializer.py +++ b/tests/test_voltage_initializer.py @@ -21,6 +21,13 @@ def test_parameters(): params.add_algorithm_param({"foo": "bar", "bar": "bar2"}) params.add_specific_voltage_limits({"vl_id": (0.5, 1.2)}) + params.add_specific_low_voltage_limits([("vl_id", True, 0.5)]) + params.add_specific_high_voltage_limits([("vl_id", True, 1.2)]) + params.add_specific_low_voltage_limits([("vl_id_2", False, 380)]) + params.add_specific_high_voltage_limits([("vl_id_3", False, 420)]) + params.add_specific_low_voltage_limits([("vl_id_4", False, 380)]) + params.add_specific_high_voltage_limits([("vl_id_4", True, 2.3)]) + params.set_objective(va.VoltageInitializerObjective.SPECIFIC_VOLTAGE_PROFILE) params.set_objective_distance(1.3)