diff --git a/cpp/powsybl-cpp/powsybl-cpp.cpp b/cpp/powsybl-cpp/powsybl-cpp.cpp index cada99a548..8e0ac3a451 100644 --- a/cpp/powsybl-cpp/powsybl-cpp.cpp +++ b/cpp/powsybl-cpp/powsybl-cpp.cpp @@ -1337,8 +1337,8 @@ JavaHandle createEventMapping() { return PowsyblCaller::get()->callJava(::createEventMapping); } -JavaHandle runDynamicModel(JavaHandle dynamicModelContext, JavaHandle network, JavaHandle dynamicMapping, JavaHandle eventMapping, JavaHandle timeSeriesMapping, int start, int stop, JavaHandle reportNode) { - return PowsyblCaller::get()->callJava(::runDynamicModel, dynamicModelContext, network, dynamicMapping, eventMapping, timeSeriesMapping, start, stop, reportNode); +JavaHandle runDynamicModel(JavaHandle dynamicModelContext, JavaHandle network, JavaHandle dynamicMapping, JavaHandle eventMapping, JavaHandle timeSeriesMapping, int start, int stop, JavaHandle *reportNode) { + return PowsyblCaller::get()->callJava(::runDynamicModel, dynamicModelContext, network, dynamicMapping, eventMapping, timeSeriesMapping, start, stop, (reportNode == nullptr) ? nullptr : *reportNode); } void addDynamicMappings(JavaHandle dynamicMappingHandle, DynamicMappingType mappingType, dataframe_array* dataframes) { @@ -1354,8 +1354,12 @@ void addOutputVariables(JavaHandle outputVariablesHandle, std::string dynamicId, PowsyblCaller::get()->callJava<>(::addOutputVariables, outputVariablesHandle, (char*) dynamicId.c_str(), variablesPtr.get(), variables.size(), isDynamic, variableType); } -std::string getDynamicSimulationResultsStatus(JavaHandle dynamicSimulationResultsHandle) { - return PowsyblCaller::get()->callJava(::getDynamicSimulationResultsStatus, dynamicSimulationResultsHandle); +DynamicSimulationStatus getDynamicSimulationResultsStatus(JavaHandle resultsHandle) { + return PowsyblCaller::get()->callJava(::getDynamicSimulationResultsStatus, resultsHandle); +} + +std::string getDynamicSimulationResultsStatusText(JavaHandle resultsHandle) { + return PowsyblCaller::get()->callJava(::getDynamicSimulationResultsStatusText, resultsHandle); } SeriesArray* getDynamicCurve(JavaHandle resultHandle, std::string curveName) { @@ -1371,6 +1375,10 @@ SeriesArray* getFinalStateValues(JavaHandle resultHandle) { return new SeriesArray(PowsyblCaller::get()->callJava(::getFinalStateValues, resultHandle)); } +SeriesArray* getTimeline(JavaHandle resultHandle) { + return new SeriesArray(PowsyblCaller::get()->callJava(::getTimeline, resultHandle)); +} + std::vector getSupportedModels(DynamicMappingType mappingType) { ToStringVector vector(PowsyblCaller::get()->callJava(::getSupportedModels, mappingType)); return vector.get(); diff --git a/cpp/powsybl-cpp/powsybl-cpp.h b/cpp/powsybl-cpp/powsybl-cpp.h index daaecfb6af..ce1fe47b6b 100644 --- a/cpp/powsybl-cpp/powsybl-cpp.h +++ b/cpp/powsybl-cpp/powsybl-cpp.h @@ -766,7 +766,7 @@ JavaHandle createDynamicModelMapping(); JavaHandle createTimeseriesMapping(); JavaHandle createEventMapping(); -JavaHandle runDynamicModel(JavaHandle dynamicModelContext, JavaHandle network, JavaHandle dynamicMapping, JavaHandle eventMapping, JavaHandle timeSeriesMapping, int start, int stop, JavaHandle reportNode); +JavaHandle runDynamicModel(JavaHandle dynamicModelContext, JavaHandle network, JavaHandle dynamicMapping, JavaHandle eventMapping, JavaHandle timeSeriesMapping, int start, int stop, JavaHandle* reportNode); // timeseries mapping void addOutputVariables(JavaHandle outputVariablesHandle, std::string dynamicId, std::vector& variables, bool isDynamic, OutputVariableType variableType); @@ -781,10 +781,12 @@ std::vector> getDynamicMappingsMetaData(DynamicMappi std::vector getSupportedModels(DynamicMappingType mappingType); // results -std::string getDynamicSimulationResultsStatus(JavaHandle dynamicSimulationResultsHandle); +DynamicSimulationStatus getDynamicSimulationResultsStatus(JavaHandle resultsHandle); +std::string getDynamicSimulationResultsStatusText(JavaHandle resultsHandle); SeriesArray* getDynamicCurve(JavaHandle resultHandle, std::string curveName); std::vector getAllDynamicCurvesIds(JavaHandle resultHandle); SeriesArray* getFinalStateValues(JavaHandle resultHandle); +SeriesArray* getTimeline(JavaHandle resultHandle); //=======END OF dynamic modeling for dynawo package========== diff --git a/cpp/pypowsybl-cpp/bindings.cpp b/cpp/pypowsybl-cpp/bindings.cpp index 17b0a5da22..4c2914442c 100644 --- a/cpp/pypowsybl-cpp/bindings.cpp +++ b/cpp/pypowsybl-cpp/bindings.cpp @@ -188,9 +188,13 @@ void dynamicSimulationBindings(py::module_& m) { .value("NODE_FAULT", EventMappingType::NODE_FAULT) .value("ACTIVE_POWER_VARIATION", EventMappingType::ACTIVE_POWER_VARIATION); - py::enum_(m, "OutputVariableType") - .value("CURVE", OutputVariableType::CURVE) - .value("FINAL_STATE", OutputVariableType::FINAL_STATE); + py::enum_(m, "OutputVariableType") + .value("CURVE", OutputVariableType::CURVE) + .value("FINAL_STATE", OutputVariableType::FINAL_STATE); + + py::enum_(m, "DynamicSimulationStatus") + .value("SUCCESS", DynamicSimulationStatus::DYNAMIC_SIMULATION_SUCCESS) + .value("FAILURE", DynamicSimulationStatus::DYNAMIC_SIMULATION_FAILURE); //entrypoints for constructors m.def("create_dynamic_simulation_context", &pypowsybl::createDynamicSimulationContext); @@ -216,9 +220,11 @@ void dynamicSimulationBindings(py::module_& m) { // Simulation results m.def("get_dynamic_simulation_results_status", &pypowsybl::getDynamicSimulationResultsStatus, py::arg("result_handle")); + m.def("get_dynamic_simulation_results_status_text", &pypowsybl::getDynamicSimulationResultsStatusText, py::arg("result_handle")); m.def("get_dynamic_curve", &pypowsybl::getDynamicCurve, py::arg("report_handle"), py::arg("curve_name")); m.def("get_all_dynamic_curves_ids", &pypowsybl::getAllDynamicCurvesIds, py::arg("report_handle")); m.def("get_final_state_values", &pypowsybl::getFinalStateValues, py::arg("result_handle")); + m.def("get_timeline", &pypowsybl::getTimeline, py::arg("result_handle")); } void voltageInitializerBinding(py::module_& m) { diff --git a/cpp/pypowsybl-java/powsybl-api.h b/cpp/pypowsybl-java/powsybl-api.h index 3612aee5b5..65683e040f 100644 --- a/cpp/pypowsybl-java/powsybl-api.h +++ b/cpp/pypowsybl-java/powsybl-api.h @@ -411,6 +411,11 @@ typedef enum { FINAL_STATE, } OutputVariableType; +typedef enum { + DYNAMIC_SIMULATION_SUCCESS = 0, + DYNAMIC_SIMULATION_FAILURE, +} DynamicSimulationStatus; + typedef enum { UNDEFINED = -1, ONE, diff --git a/docs/reference/dynamic.rst b/docs/reference/dynamic.rst index bae5c4fb07..d298dced75 100644 --- a/docs/reference/dynamic.rst +++ b/docs/reference/dynamic.rst @@ -73,5 +73,7 @@ Results SimulationResult SimulationResult.status + SimulationResult.status_text SimulationResult.curves SimulationResult.final_state_values + SimulationResult.timeline diff --git a/docs/user_guide/dynamic.rst b/docs/user_guide/dynamic.rst index 7e51d07c80..a324781344 100644 --- a/docs/user_guide/dynamic.rst +++ b/docs/user_guide/dynamic.rst @@ -93,5 +93,7 @@ To run a Dynawo simulation: results = sim.run(network, model_mapping, event_mapping, variables_mapping, start_time, end_time) # getting the results results.status() + results.status_text() # error description if the simulation fails results.curves() # dataframe containing the mapped curves - results.final_state_values() # dataframe containing the mapped final state values \ No newline at end of file + results.final_state_values() # dataframe containing the mapped final state values + results.timeline() # dataframe containing the simulation timeline \ No newline at end of file diff --git a/integration_tests/test_dynawo.py b/integration_tests/test_dynawo.py index 47b98ea33b..251257984a 100644 --- a/integration_tests/test_dynawo.py +++ b/integration_tests/test_dynawo.py @@ -8,6 +8,7 @@ import pypowsybl as pp import pypowsybl.dynamic as dyn import pypowsybl.report as rp +from pypowsybl._pypowsybl import DynamicSimulationStatus import pandas as pd def test_simulation(): @@ -44,8 +45,10 @@ def test_simulation(): res = sim.run(network, model_mapping, event_mapping, variables_mapping, 0, 100, report_node) assert report_node - assert 'Ok' == res.status() + assert DynamicSimulationStatus.SUCCESS == res.status() + assert "" == res.status_text() assert 'BBM_GEN6_generator_PGen' in res.curves() assert 'BBM_GEN6_generator_QGen' in res.curves() assert 'BBM_GEN6_generator_UStatorPu' in res.curves() - assert res.final_state_values().loc['NETWORK_B3_Upu_value'].empty == False + assert False == res.final_state_values().loc['NETWORK_B3_Upu_value'].empty + assert False == res.timeline().empty diff --git a/java/src/main/java/com/powsybl/dataframe/dynamic/OutputVariablesSeries.java b/java/src/main/java/com/powsybl/dataframe/dynamic/DynamicSimulationDataframeMappersUtils.java similarity index 58% rename from java/src/main/java/com/powsybl/dataframe/dynamic/OutputVariablesSeries.java rename to java/src/main/java/com/powsybl/dataframe/dynamic/DynamicSimulationDataframeMappersUtils.java index a3d2875cff..91b697f2ec 100644 --- a/java/src/main/java/com/powsybl/dataframe/dynamic/OutputVariablesSeries.java +++ b/java/src/main/java/com/powsybl/dataframe/dynamic/DynamicSimulationDataframeMappersUtils.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2024, RTE (http://www.rte-france.com) + * Copyright (c) 2020-2022, 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/. @@ -9,18 +9,22 @@ import com.powsybl.dataframe.DataframeMapper; import com.powsybl.dataframe.DataframeMapperBuilder; +import com.powsybl.dynamicsimulation.TimelineEvent; import com.powsybl.timeseries.DoublePoint; import com.powsybl.timeseries.DoubleTimeSeries; import com.powsybl.timeseries.TimeSeries; +import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; /** - * @author Laurent Issertial {@literal } + * @author Nicolas Pierre {@literal } */ -public final class OutputVariablesSeries { +public final class DynamicSimulationDataframeMappersUtils { - private OutputVariablesSeries() { + private DynamicSimulationDataframeMappersUtils() { } public static DataframeMapper curvesDataFrameMapper(String colName) { @@ -38,4 +42,15 @@ public static DataframeMapper, Void> fsvDataFrameMapper() { .doubles("values", Map.Entry::getValue) .build(); } + + public static DataframeMapper, Void> timelineEventDataFrameMapper() { + AtomicInteger index = new AtomicInteger(); + return new DataframeMapperBuilder, TimelineEvent, Void>() + .itemsProvider(Function.identity()) + .intsIndex("index", e -> index.getAndIncrement()) + .doubles("time", TimelineEvent::time) + .strings("model", TimelineEvent::modelName) + .strings("message", TimelineEvent::message) + .build(); + } } 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 82c4b52f65..7288fa51d4 100644 --- a/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java +++ b/java/src/main/java/com/powsybl/python/commons/PyPowsyblApiHeader.java @@ -1245,6 +1245,18 @@ public enum OutputVariableType { public static native OutputVariableType fromCValue(int value); } + @CEnum("DynamicSimulationStatus") + public enum DynamicSimulationStatus { + DYNAMIC_SIMULATION_SUCCESS, + DYNAMIC_SIMULATION_FAILURE; + + @CEnumValue + public native int getCValue(); + + @CEnumLookup + public static native DynamicSimulationStatus fromCValue(int value); + } + @CEnum("ThreeSide") public enum ThreeSideType { UNDEFINED, 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 f5ffbc6018..1de0a7c533 100644 --- a/java/src/main/java/com/powsybl/python/commons/Util.java +++ b/java/src/main/java/com/powsybl/python/commons/Util.java @@ -12,6 +12,7 @@ import com.powsybl.dataframe.DataframeElementType; import com.powsybl.dataframe.SeriesDataType; import com.powsybl.dataframe.network.modifications.DataframeNetworkModificationType; +import com.powsybl.dynamicsimulation.DynamicSimulationResult; import com.powsybl.dynamicsimulation.OutputVariable; import com.powsybl.iidm.network.ThreeSides; import com.powsybl.iidm.network.ValidationLevel; @@ -21,6 +22,7 @@ import com.powsybl.openreac.parameters.input.algo.ReactiveSlackBusesMode; import com.powsybl.openreac.parameters.output.OpenReacStatus; import com.powsybl.python.commons.PyPowsyblApiHeader.ArrayPointer; +import com.powsybl.python.commons.PyPowsyblApiHeader.DynamicSimulationStatus; import com.powsybl.python.commons.PyPowsyblApiHeader.VoltageInitializerObjective; import com.powsybl.python.commons.PyPowsyblApiHeader.VoltageInitializerStatus; import com.powsybl.python.commons.PyPowsyblApiHeader.VoltageInitializerLogLevelAmpl; @@ -385,6 +387,13 @@ public static ReactiveSlackBusesMode convert(VoltageInitializerReactiveSlackBuse }; } + public static DynamicSimulationStatus convert(DynamicSimulationResult.Status obj) { + return switch (obj) { + case SUCCESS -> DynamicSimulationStatus.DYNAMIC_SIMULATION_SUCCESS; + case FAILURE -> DynamicSimulationStatus.DYNAMIC_SIMULATION_FAILURE; + }; + } + public static byte[] binaryBufferToBytes(ByteBuffer buffer) { if (buffer.hasArray()) { return buffer.array(); diff --git a/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationCFunctions.java b/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationCFunctions.java index b1b43cb073..2b3ee21d94 100644 --- a/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationCFunctions.java +++ b/java/src/main/java/com/powsybl/python/dynamic/DynamicSimulationCFunctions.java @@ -8,6 +8,7 @@ package com.powsybl.python.dynamic; import static com.powsybl.python.commons.CTypeUtil.toStringList; +import static com.powsybl.python.commons.Util.convert; import static com.powsybl.python.commons.Util.doCatch; import static com.powsybl.python.network.NetworkCFunctions.createDataframe; @@ -16,7 +17,7 @@ import com.powsybl.commons.report.ReportNode; import com.powsybl.dataframe.SeriesMetadata; -import com.powsybl.dataframe.dynamic.OutputVariablesSeries; +import com.powsybl.dataframe.dynamic.DynamicSimulationDataframeMappersUtils; import com.powsybl.python.network.Dataframes; import com.powsybl.python.report.ReportCUtils; import com.powsybl.timeseries.DoubleTimeSeries; @@ -107,6 +108,9 @@ public static ObjectHandle runDynamicModel(IsolateThread thread, EventModelsSupplier eventModelsSupplier = ObjectHandles.getGlobal().get(eventModelsSupplierHandle); OutputVariablesSupplier outputVariablesSupplier = ObjectHandles.getGlobal().get(outputVariablesSupplierHandle); ReportNode reportNode = ReportCUtils.getReportNode(reportNodeHandle); + if (reportNode == null) { + reportNode = ReportNode.NO_OP; + } DynamicSimulationParameters dynamicSimulationParameters = new DynamicSimulationParameters(startTime, stopTime); DynamicSimulationResult result = dynamicContext.run(network, @@ -193,17 +197,27 @@ public static void addOutputVariables(IsolateThread thread, String dynamicId = CTypeUtil.toString(dynamicIdPtr); List variables = toStringList(variablesPtrPtr, variableCount); PythonOutputVariablesSupplier outputVariablesSupplier = ObjectHandles.getGlobal().get(outputVariablesHandle); - outputVariablesSupplier.addOutputVariables(dynamicId, variables, isDynamic, Util.convert(variableType)); + outputVariablesSupplier.addOutputVariables(dynamicId, variables, isDynamic, convert(variableType)); }); } @CEntryPoint(name = "getDynamicSimulationResultsStatus") - public static CCharPointer getDynamicSimulationResultsStatus(IsolateThread thread, - ObjectHandle dynamicSimulationResultsHandle, + public static DynamicSimulationStatus getDynamicSimulationResultsStatus(IsolateThread thread, + ObjectHandle resultsHandle, ExceptionHandlerPointer exceptionHandlerPtr) { return doCatch(exceptionHandlerPtr, () -> { - DynamicSimulationResult simulationResult = ObjectHandles.getGlobal().get(dynamicSimulationResultsHandle); - return CTypeUtil.toCharPtr(simulationResult.isOk() ? "Ok" : "Not OK"); + DynamicSimulationResult simulationResult = ObjectHandles.getGlobal().get(resultsHandle); + return convert(simulationResult.getStatus()); + }); + } + + @CEntryPoint(name = "getDynamicSimulationResultsStatusText") + public static CCharPointer getDynamicSimulationResultsStatusText(IsolateThread thread, + ObjectHandle resultsHandle, + ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + DynamicSimulationResult simulationResult = ObjectHandles.getGlobal().get(resultsHandle); + return CTypeUtil.toCharPtr(simulationResult.getStatusText()); }); } @@ -216,7 +230,7 @@ public static ArrayPointer getDynamicCurve(IsolateThread thread, DynamicSimulationResult result = ObjectHandles.getGlobal().get(resultHandle); String curveName = CTypeUtil.toString(curveNamePtr); DoubleTimeSeries curve = result.getCurve(curveName); - return Dataframes.createCDataframe(OutputVariablesSeries.curvesDataFrameMapper(curveName), curve); + return Dataframes.createCDataframe(DynamicSimulationDataframeMappersUtils.curvesDataFrameMapper(curveName), curve); }); } @@ -234,6 +248,16 @@ public static ArrayPointer getAllDynamicCurvesIds(IsolateTh public static ArrayPointer getFinalStateValues(IsolateThread thread, ObjectHandle resultHandle, ExceptionHandlerPointer exceptionHandlerPtr) { DynamicSimulationResult result = ObjectHandles.getGlobal().get(resultHandle); - return Dataframes.createCDataframe(OutputVariablesSeries.fsvDataFrameMapper(), result.getFinalStateValues()); + return Dataframes.createCDataframe(DynamicSimulationDataframeMappersUtils.fsvDataFrameMapper(), result.getFinalStateValues()); + } + + @CEntryPoint(name = "getTimeline") + public static ArrayPointer getTimeline(IsolateThread thread, + ObjectHandle resultsHandle, + ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + DynamicSimulationResult simulationResult = ObjectHandles.getGlobal().get(resultsHandle); + return Dataframes.createCDataframe(DynamicSimulationDataframeMappersUtils.timelineEventDataFrameMapper(), simulationResult.getTimeLine()); + }); } } diff --git a/java/src/test/java/com/powsybl/dataframe/dynamic/DynamicSimulationDataframeMappersTest.java b/java/src/test/java/com/powsybl/dataframe/dynamic/DynamicSimulationDataframeMappersTest.java new file mode 100644 index 0000000000..e55a663854 --- /dev/null +++ b/java/src/test/java/com/powsybl/dataframe/dynamic/DynamicSimulationDataframeMappersTest.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.dataframe.dynamic; + +import com.powsybl.dataframe.impl.Series; +import com.powsybl.dynamicsimulation.TimelineEvent; +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static com.powsybl.dataframe.dynamic.DynamicSimulationDataframeMappersUtils.fsvDataFrameMapper; +import static com.powsybl.dataframe.dynamic.DynamicSimulationDataframeMappersUtils.timelineEventDataFrameMapper; +import static com.powsybl.python.network.Dataframes.createSeries; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Laurent Issertial {@literal } + */ +class DynamicSimulationDataframeMappersTest { + + @Test + void testFsvDataframesMapper() { + Map fsv = new LinkedHashMap<>(); + fsv.put("GEN_Upu_value", 45.8); + fsv.put("LOAD_load_PPu", 22.1); + List series = createSeries(fsvDataFrameMapper(), fsv); + assertThat(series) + .extracting(Series::getName) + .containsExactly("variables", "values"); + assertThat(series).satisfiesExactly( + col1 -> assertThat(col1.getStrings()).containsExactly("GEN_Upu_value", "LOAD_load_PPu"), + col2 -> assertThat(col2.getDoubles()).containsExactly(45.8, 22.1)); + } + + @Test + void testTimelineDataframesMapper() { + List timelineEvents = List.of( + new TimelineEvent(0.0, "BBM_GEN6", "PMIN : activation"), + new TimelineEvent(0.0, "BBM_GEN8", "PMIN : activation"), + new TimelineEvent(2.2, "BBM_GEN6", "PMIN : deactivation")); + List series = createSeries(timelineEventDataFrameMapper(), timelineEvents); + assertThat(series) + .extracting(Series::getName) + .containsExactly("index", "time", "model", "message"); + assertThat(series).satisfiesExactly( + index -> assertThat(index.getInts()).containsExactly(0, 1, 2), + col1 -> assertThat(col1.getDoubles()).containsExactly(0.0, 0.0, 2.2), + col2 -> assertThat(col2.getStrings()).containsExactly("BBM_GEN6", "BBM_GEN8", "BBM_GEN6"), + col3 -> assertThat(col3.getStrings()).containsExactly("PMIN : activation", "PMIN : activation", "PMIN : deactivation")); + } +} diff --git a/java/src/test/java/com/powsybl/dataframe/dynamic/adders/OutputVariablesSupplierTest.java b/java/src/test/java/com/powsybl/dataframe/dynamic/adders/OutputVariablesSupplierTest.java index 97125f9a03..175b32841e 100644 --- a/java/src/test/java/com/powsybl/dataframe/dynamic/adders/OutputVariablesSupplierTest.java +++ b/java/src/test/java/com/powsybl/dataframe/dynamic/adders/OutputVariablesSupplierTest.java @@ -7,19 +7,14 @@ */ package com.powsybl.dataframe.dynamic.adders; -import com.powsybl.dataframe.impl.Series; import com.powsybl.dynamicsimulation.OutputVariable; import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; import com.powsybl.python.dynamic.PythonOutputVariablesSupplier; import org.junit.jupiter.api.Test; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import static com.powsybl.dataframe.dynamic.OutputVariablesSeries.fsvDataFrameMapper; import static com.powsybl.dynamicsimulation.OutputVariable.OutputType.*; -import static com.powsybl.python.network.Dataframes.createSeries; import static org.assertj.core.api.Assertions.assertThat; /** @@ -45,18 +40,4 @@ void testPythonSupplier() { .hasFieldOrPropertyWithValue("outputType", FINAL_STATE) ); } - - @Test - void testFsvDataframesMapper() { - Map fsv = new LinkedHashMap<>(); - fsv.put("GEN_Upu_value", 45.8); - fsv.put("LOAD_load_PPu", 22.1); - List series = createSeries(fsvDataFrameMapper(), fsv); - assertThat(series) - .extracting(Series::getName) - .containsExactly("variables", "values"); - assertThat(series).satisfiesExactly( - col1 -> assertThat(col1.getStrings()).containsExactly("GEN_Upu_value", "LOAD_load_PPu"), - col2 -> assertThat(col2.getDoubles()).containsExactly(45.8, 22.1)); - } } diff --git a/pypowsybl/_pypowsybl.pyi b/pypowsybl/_pypowsybl.pyi index 5f54724156..cbf0c8fb7a 100644 --- a/pypowsybl/_pypowsybl.pyi +++ b/pypowsybl/_pypowsybl.pyi @@ -660,6 +660,11 @@ class OutputVariableType: CURVE: ClassVar[OutputVariableType] = ... FINAL_STATE: ClassVar[OutputVariableType] = ... +class DynamicSimulationStatus: + __members__: ClassVar[Dict[str, DynamicSimulationStatus]] = ... # read-only + SUCCESS: ClassVar[DynamicSimulationStatus] = ... + FAILURE: ClassVar[DynamicSimulationStatus] = ... + class VoltageInitializerStatus: __members__: ClassVar[Dict[str, VoltageInitializerStatus]] = ... # read-only @@ -930,10 +935,12 @@ def add_output_variables(output_variables_handle: JavaHandle, dynamic_id: str, v def add_all_event_mappings(event_mapping_handle: JavaHandle, mapping_type: EventMappingType, mapping_df: Dataframe) -> None: ... def get_event_mappings_meta_data(mapping_type: EventMappingType) -> List[SeriesMetadata]: ... def set_powsybl_config_location(absolute_path_to_config:str, config_file_name: str) -> None: ... -def get_dynamic_simulation_results_status(result_handle: JavaHandle) -> str: ... +def get_dynamic_simulation_results_status(result_handle: JavaHandle) -> DynamicSimulationStatus: ... +def get_dynamic_simulation_results_status_text(result_handle: JavaHandle) -> str: ... def get_dynamic_curve(report_handle: JavaHandle, curve_name: str) -> SeriesArray: ... def get_all_dynamic_curves_ids(report_handle: JavaHandle) -> List[str]: ... def get_final_state_values(result_handle: JavaHandle) -> SeriesArray: ... +def get_timeline(result_handle: JavaHandle) -> SeriesArray: ... def remove_elements_modification(network: JavaHandle, connectable_ids: List[str], dataframe: Optional[Dataframe], remove_modification_type: RemoveModificationType, raise_exception: Optional[bool], report_node: Optional[JavaHandle]) -> None: ... def get_network_modification_metadata(network_modification_type: NetworkModificationType) -> List[SeriesMetadata]: ... def get_network_modification_metadata_with_element_type(network_modification_type: NetworkModificationType, element_type: ElementType) -> List[List[SeriesMetadata]]: ... diff --git a/pypowsybl/dynamic/impl/simulation_result.py b/pypowsybl/dynamic/impl/simulation_result.py index 71b96a8bab..bf41b57f7b 100644 --- a/pypowsybl/dynamic/impl/simulation_result.py +++ b/pypowsybl/dynamic/impl/simulation_result.py @@ -6,6 +6,7 @@ # import pandas as pd from pypowsybl import _pypowsybl as _pp +from pypowsybl._pypowsybl import DynamicSimulationStatus from pypowsybl.utils import create_data_frame_from_series_array @@ -15,17 +16,19 @@ class SimulationResult: def __init__(self, handle: _pp.JavaHandle) -> None: self._handle = handle self._status = _pp.get_dynamic_simulation_results_status(self._handle) + self._status_text = _pp.get_dynamic_simulation_results_status_text(self._handle) self._curves = self._get_all_curves() self._fsv = create_data_frame_from_series_array(_pp.get_final_state_values(self._handle)) + self._timeline = create_data_frame_from_series_array(_pp.get_timeline(self._handle)) - def status(self) -> str: - """ - status of the simulation - - :returns 'Ok' or 'Not OK' - """ + def status(self) -> DynamicSimulationStatus: + """Status of the simulation (SUCCESS or FAILURE)""" return self._status + def status_text(self) -> str: + """Status text of the simulation (failure description or empty if success)""" + return self._status_text + def curves(self) -> pd.DataFrame: """Dataframe of the curves results, columns are the curves names and rows are timestep""" return self._curves @@ -43,3 +46,7 @@ def _get_all_curves(self) -> pd.DataFrame: def final_state_values(self) -> pd.DataFrame: """Dataframe of the final state values results, first column is the fsv names, second one the final state values""" return self._fsv + + def timeline(self) -> pd.DataFrame: + """Dataframe of the simulation timeline, first column is the event time, second one the model name and the third one the event message""" + return self._timeline