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..45d9d8bcb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,12 +25,14 @@ 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 - [ADDED] matplotlib v3.8.0 support (fixed :code:`plotting_colormaps.ipynb`) - [CHANGED] PowerFactory converter - name :code:`for_name` as :code:`equipment` for all elements; also add to line - [ADDED] option to use a second tap changer for the trafo element +- [CHANGED] parameters of function merge_internal_net_and_equivalent_external_net() - [FIXED] :code:`convert_format.py`: update the attributes of the characteristic objects to match the new characteristic 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/grid_equivalents/get_equivalent.py b/pandapower/grid_equivalents/get_equivalent.py index db3395602..67faac8c6 100644 --- a/pandapower/grid_equivalents/get_equivalent.py +++ b/pandapower/grid_equivalents/get_equivalent.py @@ -260,7 +260,7 @@ def get_equivalent(net, eq_type, boundary_buses, internal_buses, if return_internal: logger.debug("Merging of internal and equivalent network begins.") net_eq = merge_internal_net_and_equivalent_external_net( - net_eq, net_internal, eq_type, show_computing_time, + net_eq, net_internal, show_computing_time=show_computing_time, calc_volt_angles=calculate_voltage_angles) if len(orig_slack_gens): net_eq.gen.slack.loc[net_eq.gen.index.intersection(orig_slack_gens)] = True @@ -327,8 +327,7 @@ def get_equivalent(net, eq_type, boundary_buses, internal_buses, def merge_internal_net_and_equivalent_external_net( - net_eq, net_internal, eq_type, show_computing_time=False, - calc_volt_angles=False, **kwargs): + net_eq, net_internal, fuse_bus_column="auto", show_computing_time=False, **kwargs): """ Merges the internal network and the equivalent external network. It is expected that the boundaries occur in both, equivalent net and @@ -340,9 +339,11 @@ def merge_internal_net_and_equivalent_external_net( **net_internal** - internal area - **eq_type** (str) - equivalent type, such as "rei", "ward" or "xward" - OPTIONAL: + **fuse_bus_column** (str, "auto) - the function expects boundary buses to be in net_eq and + in net_internal. These duplicate buses get fused. To identify these buses, the given column is used. Option "auto" provides backward compatibility which is: use "name_equivalent" if + existing and "name" otherwise + **show_computing_time** (bool, False) ****kwargs** - key word arguments for pp.merge_nets() @@ -368,17 +369,24 @@ def merge_internal_net_and_equivalent_external_net( merged_net = pp.merge_nets( net_internal, net_eq, validate=kwargs.pop("validate", False), net2_reindex_log_level=kwargs.pop("net2_reindex_log_level", "debug"), **kwargs) - try: - merged_net.gen.max_p_mw[-len(net_eq.gen.max_p_mw):] = net_eq.gen.max_p_mw.values - merged_net.gen.min_p_mw[-len(net_eq.gen.max_p_mw):] = net_eq.gen.min_p_mw.values - except: - pass # --- fuse or combine the boundary buses in external and internal nets - busname_col = "name_equivalent" if "name_equivalent" in merged_net.bus.columns.tolist() else "name" + if fuse_bus_column == "auto": + if fuse_bus_column in merged_net.bus.columns: + raise ValueError( + f"{fuse_bus_column=} is ambiguous since the column 'auto' exists in net.bus") + if "name_equivalent" in merged_net.bus.columns: + fuse_bus_column = "name_equivalent" + else: + fuse_bus_column = "name" for bus in boundary_buses_inclusive_bswitch: - name = merged_net.bus[busname_col].loc[bus] - target_buses = merged_net.bus.index[merged_net.bus[busname_col] == name] + try: + name = merged_net.bus[fuse_bus_column].loc[bus] + except: + print(fuse_bus_column) + print(merged_net.bus.columns) + print() + target_buses = merged_net.bus.index[merged_net.bus[fuse_bus_column] == name] if len(target_buses) != 2: raise ValueError( "The code expects all boundary buses to occur double. One because " diff --git a/pandapower/grid_equivalents/rei_generation.py b/pandapower/grid_equivalents/rei_generation.py index 999fe190c..53ccc1fd4 100644 --- a/pandapower/grid_equivalents/rei_generation.py +++ b/pandapower/grid_equivalents/rei_generation.py @@ -673,11 +673,15 @@ def _replace_ext_area_by_impedances_and_shunts( "p_mw": shunt_params.parameter.values.real * net_eq.sn_mva }, index=range(max_idx+1, max_idx+1+shunt_params.shape[0])) new_shunts["name"] = "eq_shunt" - new_shunts["vn_kv"] = net_eq.bus.vn_kv.loc[new_shunts.bus.values].values + isin_sh = new_shunts.bus.isin(net_eq.bus.index) + new_shunts.loc[isin_sh, "vn_kv"] = net_eq.bus.vn_kv.loc[new_shunts.bus.loc[isin_sh]].values new_shunts["step"] = 1 new_shunts["max_step"] = 1 new_shunts["in_service"] = True net_eq["shunt"] = pd.concat([net_eq["shunt"], new_shunts]) + if n_disconnected_new_eq_shunts := sum(~isin_sh): + msg = f"{n_disconnected_new_eq_shunts=}, missing buses: {new_shunts.bus.loc[~isin_sh]}" + raise ValueError(msg) runpp_fct(net_eq, calculate_voltage_angles=calc_volt_angles, tolerance_mva=1e-6, max_iteration=100) 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(): diff --git a/pandapower/test/grid_equivalents/test_get_equivalent.py b/pandapower/test/grid_equivalents/test_get_equivalent.py index 43a9df637..f17b27d02 100644 --- a/pandapower/test/grid_equivalents/test_get_equivalent.py +++ b/pandapower/test/grid_equivalents/test_get_equivalent.py @@ -90,7 +90,7 @@ def run_basic_usecases(eq_type, net=None): # UC3: merge eq_net3 with subnet_rest eq_net3 = pp.grid_equivalents.merge_internal_net_and_equivalent_external_net( - eq_net3a, subnet_rest, eq_type) + eq_net3a, subnet_rest) pp.runpp(eq_net3, calculate_voltage_angles=True) assert pandapower.toolbox.nets_equal(net, create_test_net()) return eq_net1, eq_net2, eq_net3 @@ -315,7 +315,8 @@ def test_case9_with_slack_generator_in_external_net(): "impedance": 6}, check_all_pp_elements=True) # merge eq_net with internal net to get a power flow runable net to check the results eq_net3.gen.slack = True - eq_net4 = pp.grid_equivalents.merge_internal_net_and_equivalent_external_net(eq_net3, ib_net, eq_type) + eq_net4 = pp.grid_equivalents.merge_internal_net_and_equivalent_external_net( + eq_net3, ib_net) pp.runpp(eq_net4) check_res_bus(net, eq_net4) elif "ward" in eq_type: @@ -457,7 +458,7 @@ def test_retain_original_internal_indices(): net_eq = pp.grid_equivalents.get_equivalent(net, eq_type, boundary_buses, internal_buses, calculate_voltage_angles=True, retain_original_internal_indices=True) - + assert net_eq.sgen.index.tolist()[:3] == sgen_idxs[:3] assert set(net_eq.line.index.tolist()) - set(line_idxs) == set() assert set(net_eq.bus.index.tolist()[:-2]) - set(bus_idxs) == set() @@ -471,14 +472,14 @@ def test_switch_sgens(): pp.create_switch(net, 9, 1, "b") pp.create_sgen(net, 9, 10, 10) pp.runpp(net) - net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0]) + net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0]) assert max(net.res_bus.vm_pu[[0, 3, 4, 8]].values - net_eq.res_bus.vm_pu[[0, 3, 4, 8]].values) < 1e-6 assert max(net.res_bus.va_degree[[0, 3, 4, 8]].values - net_eq.res_bus.va_degree[[0, 3, 4, 8]].values) < 1e-6 def test_characteristic(): net = pp.networks.example_multivoltage() - pp.control.create_trafo_characteristics(net, "trafo", [1], 'vk_percent', + pp.control.create_trafo_characteristics(net, "trafo", [1], 'vk_percent', [[-2,-1,0,1,2]], [[2,3,4,5,6]]) pp.runpp(net) net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [41], [0]) @@ -490,12 +491,12 @@ def test_controller(): pp.replace_gen_by_sgen(net) pp.create_load(net, 5, 10, 10) pp.create_sgen(net, 3, 1, 1) - + net.sgen.loc[:, "type"] = "wind" net.load.loc[:, "type"] = "residential" net.sgen.name = ["sgen0", "sgen1", "sgen3"] net.load.name = ["load0", "load1", "load2", "load3"] - + # load time series json_path = os.path.join(pp_dir, "test", "opf", "cigre_timeseries_15min.json") time_series = pd.read_json(json_path) @@ -515,12 +516,12 @@ def test_controller(): ConstControl(net, element="sgen", variable="p_mw", element_index=net.sgen.index.tolist(), profile_name=net.sgen.index.tolist(), data_source=DFData(sgen_ts)) - + pp.runpp(net) # getting equivalent net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0]) - + assert net_eq.controller.object[0].__dict__["element_index"] == [0, 2] assert net_eq.controller.object[0].__dict__["matching_params"]["element_index"] == [0, 2] for i in net.controller.index: @@ -529,7 +530,7 @@ def test_controller(): assert set(net_eq.controller.object[i].__dict__["profile_name"]) - \ set(net.controller.object[i].__dict__["profile_name"]) == set([]) - net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0], + net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0], retain_original_internal_indices=True) assert net_eq.controller.object[0].__dict__["element_index"] == [0, 2] assert net_eq.controller.object[0].__dict__["matching_params"]["element_index"] == [0, 2] @@ -540,7 +541,7 @@ def test_controller(): ConstControl(net, element='load', variable='p_mw', element_index=[li], data_source=DFData(load_ts), profile_name=[li]) assert len(net.controller) == 4 - net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0], + net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0], retain_original_internal_indices=True) assert net_eq.controller.index.tolist() == [0, 2] @@ -556,14 +557,14 @@ def test_motor(): pp.runpp(net) values1 = net.res_bus.vm_pu.values.copy() - for eq in ["rei", "ward", "xward"]: - net_eq = pp.grid_equivalents.get_equivalent(net, eq, [4, 8], [0], + for eq in ["rei", "ward", "xward"]: + net_eq = pp.grid_equivalents.get_equivalent(net, eq, [4, 8], [0], retain_original_internal_indices=True, show_computing_time=True) - + assert max(net_eq.res_bus.vm_pu[[0,3,4,8]].values - net.res_bus.vm_pu[[0,3,4,8]].values) < 1e-8 assert net_eq.motor.bus.values.tolist() == [3, 4] - + replace_motor_by_load(net, net.bus.index.tolist()) assert len(net.motor) == 0 assert len(net.res_motor) == 0 @@ -572,7 +573,7 @@ def test_motor(): assert net.res_load.loc[4].values.tolist() == [0, 0] pp.runpp(net) values2 = net.res_bus.vm_pu.values.copy() - assert max(values1 - values2) < 1e-10 + assert max(values1 - values2) < 1e-10 def test_sgen_bswitch(): @@ -582,11 +583,11 @@ def test_sgen_bswitch(): pp.create_sgen(net, 1, 5, in_service=False) pp.runpp(net) net.sgen.name = ["aa", "bb", "cc", "dd"] - net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0], + net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0], retain_original_internal_indices=True) assert net_eq.sgen.name[0] == 'aa//cc//dd-sgen_separate_rei_1' assert net_eq.sgen.p_mw[0] == 173 - + net = pp.networks.case9() pp.replace_gen_by_sgen(net) pp.create_bus(net, 345) @@ -597,12 +598,12 @@ def test_sgen_bswitch(): pp.create_switch(net, 1, 10, "b") net.sgen.name = ["aa", "bb", "cc", "dd"] pp.runpp(net) - net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0], + net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0], retain_original_internal_indices=True) - + assert net_eq.sgen.name[0] == 'aa//cc-sgen_separate_rei_1' assert net_eq.sgen.p_mw[0] == 173 - + # add some columns for test net.bus["voltLvl"]=1 net.sgen["col_mixed"] = ["1", 2, None, True] @@ -610,11 +611,11 @@ def test_sgen_bswitch(): net.sgen["col_different_str"] = ["str_1", "str_2", "str_3", "str_4"] net.sgen["bool"] = [False, True, False, False] net.sgen["voltLvl"] = [1, 1, 1, 1] - net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0]) + net_eq = pp.grid_equivalents.get_equivalent(net, "rei", [4, 8], [0]) assert net_eq.sgen["col_mixed"][0] == "mixed data type" assert net_eq.sgen["col_same_str"][0] == "str_test" assert net_eq.sgen["col_different_str"][0] == "str_3//str_1" - assert net_eq.sgen["col_different_str"][1] == "str_2" + assert net_eq.sgen["col_different_str"][1] == "str_2" assert net_eq.sgen["bool"][0] == False assert net_eq.sgen["bool"][1] == True assert net_eq.sgen["voltLvl"].values.tolist() == [1, 1] @@ -624,12 +625,12 @@ def test_ward_admittance(): net = pp.networks.case9() pp.runpp(net) res_bus = net.res_bus.copy() - create_passive_external_net_for_ward_admittance(net, [1, 2, 5, 6, 7], + create_passive_external_net_for_ward_admittance(net, [1, 2, 5, 6, 7], [4,8], True, _runpp_except_voltage_angles) assert len(net.shunt)==3 assert np.allclose(net.res_bus.vm_pu.values, res_bus.vm_pu.values) - - + + if __name__ == "__main__": pytest.main(['-x', __file__]) \ No newline at end of file diff --git a/pandapower/toolbox/grid_modification.py b/pandapower/toolbox/grid_modification.py index 872d8d2cb..c6fc5f911 100644 --- a/pandapower/toolbox/grid_modification.py +++ b/pandapower/toolbox/grid_modification.py @@ -141,7 +141,7 @@ def select_subnet(net, buses, include_switch_buses=False, include_results=False, def merge_nets(net1, net2, validate=True, merge_results=True, tol=1e-9, **kwargs): """Function to concatenate two nets into one data structure. The elements keep their indices - unless both nets have the same indices. In that case, net2 elements get reindex. The reindex + unless both nets have the same indices. In that case, net2 elements get reindexed. The reindex lookup of net2 elements can be retrieved by passing return_net2_reindex_lookup=True. Parameters diff --git a/setup.py b/setup.py index a5e52a879..8fa971a01 100644 --- a/setup.py +++ b/setup.py @@ -52,7 +52,7 @@ "deepdiff"], extras_require={ "docs": ["numpydoc", "sphinx", "sphinx_rtd_theme"], - "plotting": ["plotly", "matplotlib", "igraph", "geopandas", "geojson"], + "plotting": ["plotly", "matplotlib", "python-igraph", "geopandas", "geojson"], # "shapely", "pyproj" are dependencies of geopandas and so already available; # "base64", "hashlib", "zlib" produce installing problems, so they are not included "test": ["pytest<=7.0", "pytest-xdist"], @@ -62,7 +62,7 @@ # "fiona" is a depedency of geopandas and so already available "converter": ["matpowercaseframes"], "all": ["numpydoc", "sphinx", "sphinx_rtd_theme", - "plotly>=3.1.1", "matplotlib", "igraph", "geopandas", "geojson", + "plotly>=3.1.1", "matplotlib", "python-igraph", "geopandas", "geojson", "pytest<=7.0", "pytest-xdist", "ortools", # lightsim2grid, "xlsxwriter", "openpyxl", "cryptography",