From f039d239147bd5dc5ee04d79c66f866d9b9672e3 Mon Sep 17 00:00:00 2001 From: David Heck Date: Tue, 7 Nov 2023 14:40:59 +0100 Subject: [PATCH] add option to opt out of powerflow within the cim2pp converter (#2158) * add option to opt out of internal powerflow * fix docstring * fix tap_side nan error, now init with None * add tap_side nan/na => None fix for trafo3w * add parameter for runpowerflow internal in cim2pp, add paramter to ignore raised exceptions * change ignore errors default to true because of unhelpfull error message * fix cim2pp doc * github action try julia 1.10 release candidate * github action try julia 1.10 release candidate * github action try julia latest stable --------- Co-authored-by: Roman Bolgaryn --- .github/workflows/github_test_action.yml | 4 +-- CHANGELOG.rst | 1 + .../converter/cim/cim2pp/build_pp_net.py | 34 ++++++++++++------- .../transformers/powerTransformersCim16.py | 2 ++ pandapower/converter/cim/cim2pp/from_cim.py | 20 ++++++----- pandapower/test/converter/test_from_cim.py | 21 ++++++++++-- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/.github/workflows/github_test_action.yml b/.github/workflows/github_test_action.yml index 4e0fd33ea..4977c255a 100644 --- a/.github/workflows/github_test_action.yml +++ b/.github/workflows/github_test_action.yml @@ -42,7 +42,7 @@ jobs: - name: Install Julia if: ${{ matrix.python-version == '3.9' }} run: | - ./.install_julia.sh 1.8 + ./.install_julia.sh 1.9.3 pip install julia python ./.install_pycall.py - name: List of installed packages @@ -181,7 +181,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install pytest nbmake pytest-xdist igraph numba seaborn - ./.install_julia.sh 1.8 + ./.install_julia.sh 1.9.3 python -m pip install julia python ./.install_pycall.py if [ -f requirements.txt ]; then pip install -r requirements.txt; fi diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6eb76b3f1..55be5742e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,6 +25,7 @@ Change Log - [FIXED] bug in coords conversion in cim2pp, small fixes - [CHANGED] cim2pp: added support for multi diagram usage for DL profiles - [CHANGED] cim2pp: made build_pp_net modular by introducing classes +- [ADDED] cim2pp: added option to opt out of internal powerflow calculation - [FIXED] error handling in :code:`plotly/mapbox_plot.py` not raising :code`ImportError` if :code:`geopy` or :code:`pyproj` are missing - [FIXED] powerfactory2pandapower-converter error if a line has two identical coordinates - [ADDED] logger messages about the probabilistic load flow calculation (simultaneities) in the powerfactory2pandapower-converter for low voltage loads diff --git a/pandapower/converter/cim/cim2pp/build_pp_net.py b/pandapower/converter/cim/cim2pp/build_pp_net.py index fda9af7c6..f0e50de73 100644 --- a/pandapower/converter/cim/cim2pp/build_pp_net.py +++ b/pandapower/converter/cim/cim2pp/build_pp_net.py @@ -170,19 +170,22 @@ def convert_to_pp(self, convert_line_to_switch: bool = False, line_r_limit: floa self.logger.info("Running a power flow.") self.report_container.add_log(Report( level=LogLevel.INFO, code=ReportCode.INFO, message="Running a power flow.")) - try: - pp.runpp(self.net) - except Exception as e: - self.logger.error("Failed running a powerflow.") - self.logger.exception(e) - self.report_container.add_log(Report( - level=LogLevel.ERROR, code=ReportCode.ERROR, message="Failed running a powerflow.")) - self.report_container.add_log(Report(level=LogLevel.EXCEPTION, code=ReportCode.EXCEPTION, - message=traceback.format_exc())) - else: - self.logger.info("Power flow solved normal.") - self.report_container.add_log(Report( - level=LogLevel.INFO, code=ReportCode.INFO, message="Power flow solved normal.")) + if kwargs.get('run_powerflow', False): + try: + pp.runpp(self.net) + except Exception as e: + self.logger.error("Failed running a powerflow.") + self.logger.exception(e) + self.report_container.add_log(Report( + level=LogLevel.ERROR, code=ReportCode.ERROR, message="Failed running a powerflow.")) + self.report_container.add_log(Report(level=LogLevel.EXCEPTION, code=ReportCode.EXCEPTION, + message=traceback.format_exc())) + if not kwargs.get('ignore_errors', True): + raise e + else: + self.logger.info("Power flow solved normal.") + self.report_container.add_log(Report( + level=LogLevel.INFO, code=ReportCode.INFO, message="Power flow solved normal.")) try: create_measurements = kwargs.get('create_measurements', None) if create_measurements is not None and create_measurements.lower() == 'sv': @@ -206,7 +209,10 @@ def convert_to_pp(self, convert_line_to_switch: bool = False, line_r_limit: floa level=LogLevel.EXCEPTION, code=ReportCode.EXCEPTION_CONVERTING, message=traceback.format_exc())) self.net.measurement = self.net.measurement[0:0] + if not kwargs.get('ignore_errors', True): + raise e try: + # TODO: think on whether to remove whole function if kwargs.get('update_assets_from_sv', False): CreateMeasurements(self.net, self.cim).update_assets_from_sv() except Exception as e: @@ -218,6 +224,8 @@ def convert_to_pp(self, convert_line_to_switch: bool = False, line_r_limit: floa self.report_container.add_log(Report( level=LogLevel.EXCEPTION, code=ReportCode.EXCEPTION_CONVERTING, message=traceback.format_exc())) + if not kwargs.get('ignore_errors', True): + raise e # a special fix for BB and NB mixed networks: # fuse boundary ConnectivityNodes with their TopologicalNodes bus_t = self.net.bus.reset_index(level=0, drop=False) diff --git a/pandapower/converter/cim/cim2pp/converter_classes/transformers/powerTransformersCim16.py b/pandapower/converter/cim/cim2pp/converter_classes/transformers/powerTransformersCim16.py index 535120fe2..b4ca19517 100644 --- a/pandapower/converter/cim/cim2pp/converter_classes/transformers/powerTransformersCim16.py +++ b/pandapower/converter/cim/cim2pp/converter_classes/transformers/powerTransformersCim16.py @@ -413,6 +413,7 @@ def _prepare_trafos_cim16(self, power_trafo2w: pd.DataFrame) -> pd.DataFrame: one_item + '_lv'] del copy_list, one_item # detect on which winding a tap changer is attached + power_trafo2w['tap_side'] = None power_trafo2w.loc[power_trafo2w['step_lv'].notna(), 'tap_side'] = 'lv' power_trafo2w.loc[power_trafo2w['step'].notna(), 'tap_side'] = 'hv' fillna_list = ['neutralStep', 'lowStep', 'highStep', 'stepVoltageIncrement', 'stepPhaseShiftIncrement', 'step', @@ -516,6 +517,7 @@ def _prepare_trafo3w_cim16(self, power_trafo3w: pd.DataFrame) -> pd.DataFrame: del copy_list, one_item # detect on which winding a tap changer is attached + power_trafo3w['tap_side'] = None power_trafo3w.loc[power_trafo3w['step_lv'].notna(), 'tap_side'] = 'lv' power_trafo3w.loc[power_trafo3w['step_mv'].notna(), 'tap_side'] = 'mv' power_trafo3w.loc[power_trafo3w['step'].notna(), 'tap_side'] = 'hv' diff --git a/pandapower/converter/cim/cim2pp/from_cim.py b/pandapower/converter/cim/cim2pp/from_cim.py index 89c59a8b1..9ec7cfe8b 100644 --- a/pandapower/converter/cim/cim2pp/from_cim.py +++ b/pandapower/converter/cim/cim2pp/from_cim.py @@ -95,22 +95,26 @@ def from_cim(file_list: List[str] = None, encoding: str = 'utf-8', convert_line_ """ Convert a CIM net to a pandapower net from XML files. Additional parameters for kwargs: - create_measurements (str): Set this parameter to 'SV' to create measurements for the pandapower net from the SV + - create_measurements (str): Set this parameter to 'SV' to create measurements for the pandapower net from the SV profile. Set it to 'Analog' to create measurements from Analogs. If the parameter is not set or is set to None, no measurements will be created. - update_assets_from_sv (bool): Set this parameter to True to update the assets (sgens, loads, wards, ...) with values - from the SV profile. Default: False. - use_GL_or_DL_profile (str): Choose the profile to use for converting coordinates. Set it to 'GL' to use the GL + - update_assets_from_sv (bool): Set this parameter to True to update the assets (sgens, loads, wards, ...) with + values from the SV profile. Default: False. + - use_GL_or_DL_profile (str): Choose the profile to use for converting coordinates. Set it to 'GL' to use the GL profile (Usually lat and long coordinates). Set it to 'DL' to use the DL profile (Usually x, y coordinates for displaying control room schema). Set it to 'both' to let the converter choose the profile. The converter will choose the GL profile first if available, otherwise the DL profile. Optional, default: both. - diagram_name (str): The name from the Diagram from the diagram layout profile for the geo coordinates. Default: The - first diagram sorted ascending by name. Set the parameter to "all" to use available diagrams for creating the + - diagram_name (str): The name from the Diagram from the diagram layout profile for the geo coordinates. Default: + The first diagram sorted ascending by name. Set the parameter to "all" to use available diagrams for creating the coordinates. - create_tap_controller (bool): If True, create pandapower controllers for transformer tap changers. If False, skip + - create_tap_controller (bool): If True, create pandapower controllers for transformer tap changers. If False, skip creating them. Default: True - sn_mva (float): Set the sn_mva from the pandapower net to a specific value. This value is not given in CGMES. + - sn_mva (float): Set the sn_mva from the pandapower net to a specific value. This value is not given in CGMES. Default: None (pandapower default will be chosen) + - run_powerflow (bool): Option to run to powerflow inside the converter to create res tables directly. + Default: False. + - ignore_errors (bool): Option to disable raising of internal errors. Useful if you need to get a network not matter + if there are errors in the conversion. Default: True. :param file_list: The path to the CGMES files as a list. :param encoding: The encoding from the files. Optional, default: utf-8 diff --git a/pandapower/test/converter/test_from_cim.py b/pandapower/test/converter/test_from_cim.py index 0f4c5e8f5..8f17c8811 100644 --- a/pandapower/test/converter/test_from_cim.py +++ b/pandapower/test/converter/test_from_cim.py @@ -54,7 +54,7 @@ def SimBench_1_HVMVmixed_1_105_0_sw_modified(): cgmes_files = [os.path.join(folder_path, 'SimBench_1-HVMV-mixed-1.105-0-sw_modified.zip')] - return cim2pp.from_cim(file_list=cgmes_files) + return cim2pp.from_cim(file_list=cgmes_files, run_powerflow=True) @pytest.fixture(scope="session") @@ -63,7 +63,7 @@ def Simbench_1_EHV_mixed__2_no_sw(): cgmes_files = [os.path.join(folder_path, 'Simbench_1-EHV-mixed--2-no_sw.zip')] - return cim2pp.from_cim(file_list=cgmes_files, create_measurements='SV') + return cim2pp.from_cim(file_list=cgmes_files, create_measurements='SV', run_powerflow=True) @pytest.fixture(scope="session") @@ -77,6 +77,20 @@ def example_multivoltage(): return net +@pytest.fixture(scope="session") +def SimBench_1_HVMVmixed_1_105_0_sw_modified_no_load_flow(): + folder_path = os.path.join(test_path, "test_files", "example_cim") + + cgmes_files = [os.path.join(folder_path, 'SimBench_1-HVMV-mixed-1.105-0-sw_modified.zip')] + + return cim2pp.from_cim(file_list=cgmes_files) + + +def test_SimBench_1_HVMVmixed_1_105_0_sw_modified_no_load_flow_res_bus( + SimBench_1_HVMVmixed_1_105_0_sw_modified_no_load_flow): + assert 0 == len(SimBench_1_HVMVmixed_1_105_0_sw_modified_no_load_flow.res_bus.index) + + def test_example_multivoltage_res_xward(example_multivoltage): assert 2 == len(example_multivoltage.res_xward.index) element_0 = example_multivoltage.res_xward.iloc[example_multivoltage.xward[ @@ -715,7 +729,7 @@ def test_fullgrid_trafo(fullgrid): assert 0.0 == element_1['pfe_kw'].item() assert 0.0 == element_1['i0_percent'].item() assert 0.0 == element_1['shift_degree'].item() - assert math.isnan(element_1['tap_side'].item()) + assert None is element_1['tap_side'].item() assert pd.isna(element_1['tap_neutral'].item()) assert pd.isna(element_1['tap_min'].item()) assert pd.isna(element_1['tap_max'].item()) @@ -1054,6 +1068,7 @@ def test_fullgrid_controller(fullgrid): def test_fullgrid_characteristic_temp(fullgrid): assert 8 == len(fullgrid.characteristic_temp.index) + def test_fullgrid_characteristic(fullgrid): assert 20 == len(fullgrid.characteristic.index) for _, obj in fullgrid.characteristic.iterrows():