diff --git a/pandapower/converter/powerfactory/pp_import_functions.py b/pandapower/converter/powerfactory/pp_import_functions.py index ab72e79ba4..7f527016b7 100644 --- a/pandapower/converter/powerfactory/pp_import_functions.py +++ b/pandapower/converter/powerfactory/pp_import_functions.py @@ -6,6 +6,7 @@ import numpy as np import pandapower as pp +import pandas as pd from pandapower.auxiliary import ADict from pandas import DataFrame, Series @@ -16,16 +17,301 @@ logger = logging.getLogger(__name__) - # make wrapper for GetAttribute def ga(element, attr): return element.GetAttribute(attr) +def create_buses(net, dict_net, flag_graphics): + import numpy as np + def descr(item): + substat_descr = '' + if item.HasAttribute('cpSubstat'): + substat = item.cpSubstat + if substat is not None: + logger.debug('adding substat %s to descr of bus %s' % (substat, item.loc_name)) + substat_descr = substat.loc_name + else: + logger.debug("bus has no substat description") + else: + logger.debug('bus %s is not part of any substation' % item.loc_name) + + if len(item.desc) > 0: + descr = ' \n '.join(item.desc) + elif item.fold_id: + descr = item.fold_id.loc_name + else: + descr = '' + return descr, substat_descr + + grf_map = dict_net.get('graphics', {}) + + bus_param_list = [] + for bid, item in enumerate(dict_net['ElmTerm']): + bus_dict[item] = bid + + d = {} + d["name"] = item.loc_name + d["in_service"] = item.outserv == 0 + d["type"] = ["b", "m", "n"][item.iUsage] + d["vn_kv"] = item.uknom + d["zone"] = (item.Grid.loc_name.split('.ElmNet')[0] if item.HasAttribute("Grid") else + item.cpGrid.loc_name.split('.ElmNet')[0]) + d["description"], d["substat"] = descr(item) + d["folder_id"] = item.fold_id.loc_name + for s in ["sernum", "for_name", "chr_name"]: + try: + d[s] = item.GetAttribute(s) + except: + pass + try: + d["cpSite.loc_name"] = item.cpSite.loc_name + except: + pass + bus_param_list.append(d) + + x, y = 0, 0 + if flag_graphics == 'GPS': + x = item.GetAttribute('e:GPSlon') + y = item.GetAttribute('e:GPSlat') + elif flag_graphics == 'graphic objects': + graphic_object = grf_map.get(item, None) + if graphic_object: + x = graphic_object.GetAttribute('rCenterX') + y = graphic_object.GetAttribute('rCenterY') + d["geodata"] = (x, y) + bus = pd.DataFrame(bus_param_list) + + pp.create_buses(net, nr_buses=len(bus), **bus.to_dict(orient="list")) # , index=range(1, len(bus) + 1) + + +def create_loads(net, dict_net, pf_variable_p_loads, is_unbalanced): + load_param_list = [] + asym_load_param_list = [] + for load_type in ["ElmLod", "ElmLodlv", "ElmLodmv", "ElmLodlvp"]: # addes ElmLodlv + for item in dict_net[load_type]: + try: + params = ADict() + params.name = item.loc_name + params.in_service = not bool(item.outserv) + params.type = "wye" + params['description'] = ' \n '.join(item.desc) if len(item.desc) > 0 else '' + load_class = item.GetClassName() + logger.debug('>> creating load <%s.%s>' % (params.name, load_class)) + is_unbalanced = item.i_sym + + ask = ask_unbalanced_load_params if is_unbalanced else ask_load_params + + if load_class == 'ElmLodlv': + try: + params.update(ask(item, pf_variable_p_loads, dict_net=dict_net, + variables=('p_mw', 'sn_mva'))) + except Exception as err: + logger.error("m:P:bus1 and m:Q:bus1 should be used with ElmLodlv") + logger.error('While creating load %s, error occurred for ' + 'calculation of q_mvar: %s, %s' % (item, params, err)) + raise err + # added + elif load_class == 'ElmLodlvp': + params.update(ask(item, pf_variable_p_loads, dict_net=dict_net, + variables=('p_mw','sn_mva'))) + parent = item.GetParent() + parent_class = parent.GetClassName() + logger.debug('parent class name of ElmLodlvp: %s' % parent_class) + if parent_class == 'ElmLodlv': + # set parent load out of service + # parent_load = next(it for it in load_param_list if + # it["chr_name"] == item.GetParent().chr_name) + parent_load = next(it for it in load_param_list if + (it["name"] == item.GetParent().loc_name) and + (it["chr_name"] == item.GetParent().chr_name)) + parent_load["in_service"] = False + #raise NotImplementedError('ElmLodlvp as not part of ElmLne not implemented') + elif parent_class == 'ElmLne': + logger.debug('creating load that is part of line %s' % parent) + params.update(ask(item, pf_variable_p_loads=pf_variable_p_loads, + dict_net=dict_net, variables=('p_mw', 'sn_mva'))) + params.name += '(%s)' % parent.loc_name + split_dict = make_split_dict(parent) # parent is a line + # todo remake this + params.bus = split_line_add_bus(net, split_dict) + bus_is_known = True + logger.debug('created bus <%d> in net and changed lines' % params.bus) + + + ###### + elif load_class == 'ElmLodmv': + params.update(ask(item, pf_variable_p_loads=pf_variable_p_loads, + dict_net=dict_net, variables=('p_mw', 'sn_mva'))) + elif load_class == 'ElmLod': + params.update(ask(item, pf_variable_p_loads=pf_variable_p_loads, + dict_net=dict_net, variables=('p_mw', 'q_mvar'))) + else: + logger.warning('item <%s> not imported - <%s> not implemented yet!' % + (item.loc_name, load_class)) + raise RuntimeError('Load <%s> of type <%s> not implemented!' % + (item.loc_name, load_class)) + + try: + params.buses = get_connection_nodes(net, item, 1) + except IndexError: + logger.warn("Cannot add Load '%s': not connected" % params.name) + continue + params.in_service = monopolar_in_service(item) + attr_list = ["sernum", "for_name", "chr_name", 'cpSite.loc_name'] + if load_class == 'ElmLodlv' or load_class == 'ElmLodlvp' : + attr_list.extend(['pnight', 'cNrCust', 'cPrCust', 'UtilFactor', 'cSmax', + 'cSav', 'coslini' ]) #'ccosphi' + add_additional_attributes_to_params(item, params, attr_list=attr_list) + + #TODO get_pf_load_results(net, item, ld, is_unbalanced) + + if is_unbalanced: + params.type = map_type_var(item.phtech) + asym_load_param_list.append(params) + else: + load_param_list.append(params) + logger.debug('parameters: %s' % params) + except RuntimeError as err: + logger.debug('load failed at import and was not imported: %s' % err) + loads = pd.DataFrame(load_param_list) + pp.create_loads(net, **loads.to_dict(orient="list")) + + # no pp.create_asymmetric_loads yet, so we have to do it in a loop + for param in asym_load_param_list: #TODO test it + pp.create_asymmetric_load(net, bus=param["buses"], **param) + + if len(loads) > 0: logger.info('imported %d loads' % len(loads)) + + +def create_sgens(net, dict_net, pv_as_slack, pf_variable_p_gen, is_unbalanced): +# logger.debug('sum loads: %.3f' % sum(net.load.loc[net.load.in_service, 'p_mw'])) + logger.debug('creating static generators') + # create static generators: + for gen_type in ["ElmGenstat", "ElmPvsys"]: + for item in dict_net[gen_type]: + try: + create_sgen_genstat(net=net, item=item, pv_as_slack=pv_as_slack, + pf_variable_p_gen=pf_variable_p_gen, dict_net=dict_net, + is_unbalanced=is_unbalanced) + except RuntimeError as err: + logger.debug('sgen failed at import and was not imported: %s' % err) + + if len(net.sgen) > 0: logger.info('imported %d sgens' % len(net.sgen)) + + +def create_lines(net, dict_net, flag_graphics, is_unbalanced): + lines_to_create = [] + for n, item in enumerate(dict_net['ElmLne']): + params = {'name': item.loc_name, + 'parallel': item.nlnum, + 'line_idx': n} + logger.debug('>> creating line <%s>' % params['name']) + logger.debug('line <%s> has <%d> parallel lines' % (params['name'], params['parallel'])) + # here: implement situation if line not connected + + try: + params['bus1'], params['bus2'] = get_connection_nodes(net, item, 2) + except IndexError: + logger.debug("Cannot add Line '%s': not connected" % params['name']) + continue + except: + logger.error("Error while exporting Line '%s'" % params['name']) + continue + + line_sections = item.GetContents('*.ElmLnesec') + # geodata + if flag_graphics == 'no geodata': + coords = [] + elif flag_graphics == 'GPS': + if len(item.GPScoords) > 0 and item.GPScoords[0] != []: + coords = get_coords_from_item(item) + else: + coords = get_coords_from_buses(net, params['bus1'], params['bus2']) + else: + coords = get_coords_from_grf_object(item) + + if len(line_sections) == 0: + logger.debug('line <%s> has no sections' % params['name']) + lines_to_create.append(create_line_normal(net=net, item=item, geodata=coords, + is_unbalanced=is_unbalanced, **params)) + else: + logger.debug('line <%s> has sections' % params['name']) + lines_to_create.extend(create_line_sections(net=net, item_list=line_sections, line=item, + geodata=coords, is_unbalanced=is_unbalanced, **params)) +# logger.debug('created <%d> line sections for line <%s>' % (len(sid_list), params['name'])) + + #TODO necessary ? +# net.line.loc[sid_list, "line_idx"] = n + # create_connection_switches(net, item, 2, 'l', (params['bus1'], params['bus2']), + # (sid_list[0], sid_list[-1])) + +# logger.debug('line <%s> created' % params['name']) + + lines_by_stdtype = pd.DataFrame(l for l in lines_to_create if "r_ohm_per_km" not in l) + if len(lines_by_stdtype): + lines_by_stdtype.rename(columns={"from_bus": "from_buses", "to_bus": "to_buses"}, inplace=True) + pp.create_lines(net, **lines_by_stdtype.to_dict(orient="list")) + lines_by_parameter = pd.DataFrame(l for l in lines_to_create if "r_ohm_per_km" in l) + if len(lines_by_parameter): + lines_by_parameter.rename(columns={"from_bus": "from_buses", "to_bus": "to_buses"}, + inplace=True) + pp.create_lines_from_parameters(net, **lines_by_parameter.to_dict(orient="list")) + + # net.line_geodata = net.line[["coords"]] #TODO: adjust to new format + # net.line.drop(columns=["coords"], inplace=True) + + +def create_switches(net, dict_net): + switches_to_create = [] + switch_types = {"cbk": "CB", "sdc": "LBS", "swt": "LS", "dct": "DS", "fus": "FUSE"} + + for items, is_fuse in [[dict_net['ElmCoup'], False], + [dict_net['RelFuse'], True]]: + for item in items: + try: + bus1, bus2 = get_connection_nodes(net, item, 2) + except IndexError: + logger.debug("Cannot add Coup '%s': not connected" % item.loc_name) + continue + except IndexError: + logger.error("Error while exporting Coup '%s'!" % item.loc_name) + continue + + if not item.HasAttribute('isclosed') and not is_fuse: + logger.error('switch %s does not have the attribute isclosed!' % item) + switch_is_closed = bool(item.on_off) \ + and (bool(item.isclosed) if item.HasAttribute('isclosed') else True) + in_service = not bool(item.outserv) if item.HasAttribute('outserv') else True + + try: + in_ka = np.nan + if is_fuse: + in_ka = item.GetAttribute("typ_id").GetAttribute("irat") / 1000. + except: + pass + + switches_to_create.append({"name": item.loc_name, + "buses": bus1, + "elements": bus2, + "et": "b", + "closed": switch_is_closed and in_service, + "type": switch_types.get(item.aUsage, 'unknown'), + "in_ka": in_ka}) + + switches = pd.DataFrame(switches_to_create) + pp.create_switches(net, **switches.to_dict(orient='list')) + + + # net.res_switch.loc[cd, ['pf_closed', 'pf_in_service']] = bool(item.on_off) and ( + # bool(item.isclosed) if item.HasAttribute('isclosed') else True), in_service + + # import network to pandapower: def from_pf(dict_net, pv_as_slack=True, pf_variable_p_loads='plini', pf_variable_p_gen='pgini', flag_graphics='GPS', tap_opt="nntap", export_controller=True, handle_us="Deactivate", max_iter=None, is_unbalanced=False, create_sections=True): + logger.debug("__name__: %s" % __name__) logger.debug('started from_pf') logger.info(logger.__dict__) @@ -44,7 +330,7 @@ def from_pf(dict_net, pv_as_slack=True, pf_variable_p_loads='plini', pf_variable pp.set_user_pf_options(net, max_iteration=max_iter) logger.info('creating grid %s' % grid_name) if 'res_switch' not in net.keys(): - net['res_switch'] = DataFrame(columns=['pf_closed', 'pf_in_service'], dtype='bool') + net['res_switch'] = pd.DataFrame(columns=['pf_closed', 'pf_in_service'], dtype='bool') logger.debug('creating buses') # create buses: @@ -54,11 +340,7 @@ def from_pf(dict_net, pv_as_slack=True, pf_variable_p_loads='plini', pf_variable grf_map = dict_net.get('graphics', {}) logger.debug('the graphic mapping is: %s' % grf_map) - # ist leider notwendig - n = 0 - for n, bus in enumerate(dict_net['ElmTerm'], 1): - create_bus(net=net, item=bus, flag_graphics=flag_graphics, is_unbalanced=is_unbalanced) - if n > 0: logger.info('imported %d buses' % n) + create_buses(net, dict_net, flag_graphics) logger.debug('creating external grids') # create external networks: @@ -68,58 +350,8 @@ def from_pf(dict_net, pv_as_slack=True, pf_variable_p_loads='plini', pf_variable if n > 0: logger.info('imported %d external grids' % n) logger.debug('creating loads') - # create loads: - n = 0 - for n, load in enumerate(dict_net['ElmLod'], 1): - try: - create_load(net=net, item=load, pf_variable_p_loads=pf_variable_p_loads, - dict_net=dict_net, is_unbalanced=is_unbalanced) - except RuntimeError as err: - logger.debug('load failed at import and was not imported: %s' % err) - if n > 0: logger.info('imported %d loads' % n) - - logger.debug('creating lv loads') - # create loads: - n = 0 - for n, load in enumerate(dict_net['ElmLodlv'], 1): - try: - create_load(net=net, item=load, pf_variable_p_loads=pf_variable_p_loads, - dict_net=dict_net, is_unbalanced=is_unbalanced) - except RuntimeError as err: - logger.warning('load failed at import and was not imported: %s' % err) - if n > 0: logger.info('imported %d lv loads' % n), - - logger.debug('creating mv loads') - # create loads: - n = 0 - for n, load in enumerate(dict_net['ElmLodmv'], 1): - try: - create_load(net=net, item=load, pf_variable_p_loads=pf_variable_p_loads, - dict_net=dict_net, is_unbalanced=is_unbalanced) - except RuntimeError as err: - logger.error('load failed at import and was not imported: %s' % err) - if n > 0: logger.info('imported %d mv loads' % n) - -# logger.debug('sum loads: %.3f' % sum(net.load.loc[net.load.in_service, 'p_mw'])) - - logger.debug('creating static generators') - # create static generators: - n = 0 - for n, gen in enumerate(dict_net['ElmGenstat'], 1): - try: - create_sgen_genstat(net=net, item=gen, pv_as_slack=pv_as_slack, - pf_variable_p_gen=pf_variable_p_gen, dict_net=dict_net, is_unbalanced=is_unbalanced) - except RuntimeError as err: - logger.debug('sgen failed at import and was not imported: %s' % err) - if n > 0: logger.info('imported %d static generators' % n) - - logger.debug('creating pv generators as static generators') - # create pv generators: - n = 0 - for n, pv in enumerate(dict_net['ElmPvsys'], 1): - create_sgen_genstat(net=net, item=pv, pv_as_slack=pv_as_slack, - pf_variable_p_gen=pf_variable_p_gen, dict_net=dict_net, is_unbalanced=is_unbalanced) - if n > 0: logger.info('imported %d pv generators' % n) + create_loads(net, dict_net, pf_variable_p_loads, is_unbalanced) + create_sgens(net, dict_net, pv_as_slack, pf_variable_p_gen, is_unbalanced) logger.debug('creating asynchronous machines') # create asynchronous machines: @@ -141,7 +373,7 @@ def from_pf(dict_net, pv_as_slack=True, pf_variable_p_loads='plini', pf_variable n = 0 for n, trafo in enumerate(dict_net['ElmTr2'], 1): create_trafo(net=net, item=trafo, tap_opt=tap_opt, export_controller=export_controller, - is_unbalanced=is_unbalanced) + is_unbalanced=is_unbalanced) if n > 0: logger.info('imported %d trafos' % n) logger.debug('creating 3W-transformers') @@ -153,19 +385,7 @@ def from_pf(dict_net, pv_as_slack=True, pf_variable_p_loads='plini', pf_variable logger.info('imported %d 3w-trafos' % n) pp.set_user_pf_options(net, trafo3w_losses='star') - logger.debug('creating switches (couplings)') - # create switches (ElmCoup): - n = 0 - for n, coup in enumerate(dict_net['ElmCoup'], 1): - create_coup(net=net, item=coup) - if n > 0: logger.info('imported %d coups' % n) - - logger.debug('creating fuses (as couplings)') - # create fuses (RelFuse): - n = 0 - for n, fuse in enumerate(dict_net['RelFuse'], 1): - create_coup(net=net, item=fuse, is_fuse=True) - if n > 0: logger.info('imported %d fuses' % n) + create_switches(net, dict_net) # create shunts (ElmShnt): n = 0 @@ -191,31 +411,21 @@ def from_pf(dict_net, pv_as_slack=True, pf_variable_p_loads='plini', pf_variable create_vac(net=net, item=vac) if n > 0: logger.info('imported %d VAC' % n) - # logger.debug('creating switches') - # # create switches (StaSwitch): - # n = 0 - # for switch in dict_net['StaSwitch']: - # create_switch(net=net, item=switch) - # n += 1 - # logger.info('imported %d switches' % n) - - for idx, row in net.trafo.iterrows(): - propagate_bus_coords(net, row.lv_bus, row.hv_bus) - - for idx, row in net.switch[net.switch.et == 'b'].iterrows(): - propagate_bus_coords(net, row.bus, row.element) - - # we do lines last because of propagation of coordinates logger.debug('creating lines') - # create lines: global line_dict line_dict = {} - n = 0 - for n, line in enumerate(dict_net['ElmLne'], 0): - create_line(net=net, item=line, flag_graphics=flag_graphics, create_sections=create_sections, - is_unbalanced=is_unbalanced) - logger.info('imported %d lines' % (len(net.line.line_idx.unique())) if len(net.line) else 0) - net.line['section_idx'] = 0 + +#TODO why net.line['section_idx'] = 0 +# TODO check how create_lines function is beneficial + create_lines(net, dict_net, flag_graphics=flag_graphics, is_unbalanced=is_unbalanced) + + # n = 0 + # for n, line in enumerate(dict_net['ElmLne'], 0): + # create_line(net=net, item=line, flag_graphics=flag_graphics, create_sections=create_sections, + # is_unbalanced=is_unbalanced) + # logger.info('imported %d lines' % (len(net.line.line_idx.unique())) if len(net.line) else 0) + # net.line['section_idx'] = 0 + if dict_net['global_parameters']["iopt_tem"] == 1: pp.set_user_pf_options(net, consider_line_temperature=True) if dict_net['global_parameters']["global_load_voltage_dependency"] == 1: @@ -223,43 +433,14 @@ def from_pf(dict_net, pv_as_slack=True, pf_variable_p_loads='plini', pf_variable else: pp.set_user_pf_options(net, voltage_depend_loads=False) - if len(dict_net['ElmLodlvp']) > 0: - lvp_dict = get_lvp_for_lines(dict_net) - logger.debug(lvp_dict) - split_all_lines(net, lvp_dict) - - remove_folder_of_std_types(net) - - ### don't import the ElmLodlvp for now... - # logger.debug('creating lv partial loads') - # # create loads: - # n = 0 - # for n, load in enumerate(dict_net['ElmLodlvp'], 1): - # create_load(net=net, item=load, pf_variable_p_loads=pf_variable_p_loads) - # if n > 0: logger.info('imported %d lv partial loads' % n) - - # # here we import the partial LV loads that are part of lines because of line section - # coordinates - # logger.debug('creating lv partial loads') - # # create loads: - # n = 0 - # for n, load in enumerate(dict_net['ElmLodlvp'], 1): - # try: - # create_load(net=net, item=load, pf_variable_p_loads=pf_variable_p_loads) - # except NotImplementedError: - # logger.debug('load %s not imported because it is not contained in ElmLod' % load) - # if n > 0: logger.info('imported %d lv partial loads' % n) - # if len(dict_net['ElmLodlvp']) > 0: - # n = 0 - # for line in dict_net['ElmLne']: - # partial_loads = line.GetContents('*.ElmLodlvp') - # partial_loads.sort(key=lambda x: x.lneposkm) - # for load in partial_loads: - # create_load(net=net, item=load, pf_variable_p_loads=pf_variable_p_loads) - # n += 1 - # logger.info('imported %d lv partial loads' % n) + # lvp_dict = get_lvp_for_lines(dict_net) + # logger.debug(lvp_dict) + # split_all_lines(net, lvp_dict) + +# remove_folder_of_std_types(net) + #TODO if handle_us == "Deactivate": logger.info('deactivating unsupplied elements') pp.set_isolated_areas_out_of_service(net) @@ -268,7 +449,7 @@ def from_pf(dict_net, pv_as_slack=True, pf_variable_p_loads='plini', pf_variable pp.drop_inactive_elements(net) elif handle_us != "Nothing": raise ValueError("handle_us should be 'Deactivate', 'Drop' or 'Nothing', " - "received: %s" % handle_us) + "received: %s" % handle_us) if is_unbalanced: pp.add_zero_impedance_parameters(net) @@ -325,6 +506,37 @@ def add_additional_attributes(item, net, element, element_id, attr_list=None, at net[element].loc[element_id, attr_dict[attr]] = chr_name[0] +def add_additional_attributes_to_params(item, params, attr_list=None, attr_dict=None): + """ + Adds additonal atributes from powerfactory such as sernum or for_name + + @param item: powerfactory item + @param net: pp net + @param element: pp element namme (str). e.g. bus, load, sgen + @param element_id: element index in pp net + @param attr_list: list of attribtues to add. e.g. ["sernum", "for_name"] + @param attr_dict: names of an attribute in powerfactory and in pandapower + @return: + """ + if attr_dict is None: + attr_dict = {k: k for k in attr_list} + + for attr in attr_dict.keys(): + if '.' in attr: + # go in the object chain of a.b.c.d until finally get the chr_name + obj = item + for a in attr.split('.'): + if hasattr(obj, 'HasAttribute') and obj.HasAttribute(a): + obj = ga(obj, a) + if obj is not None and isinstance(obj, str): + params[attr_dict[attr]] = obj + + elif item.HasAttribute(attr): + chr_name = ga(item, attr) + if chr_name is not None: + params[attr_dict[attr]] = chr_name + + def create_bus(net, item, flag_graphics, is_unbalanced): # add geo data if flag_graphics == 'GPS': @@ -444,55 +656,6 @@ def get_pf_bus_results(net, item, bid, is_unbalanced): net[bus_type].at[bid, res_var_pp] = res -# # This one deletes all the results :( -# # Don't use it -# def find_bus_index_in_net(item, net=None): -# foreign_key = int(ga(item, 'for_name')) -# return foreign_key - - -# Is unfortunately not that safe :( -# Don't use it -# def find_bus_index_in_net(item, net): -# usage = ["b", "m", "n"] -# # to be sure that the bus is the correct one -# name = ga(item, 'loc_name') -# bus_type = usage[ga(item, 'iUsage')] -# logger.debug('looking for bus <%s> in net' % name) -# -# if item.HasAttribute('cpSubstat'): -# substat = ga(item, 'cpSubstat') -# if substat is not None: -# descr = ga(substat, 'loc_name') -# logger.debug('bus <%s> has substat, descr is <%s>' % (name, descr)) -# else: -# # omg so ugly :( -# descr = ga(item, 'desc') -# descr = descr[0] if len(descr) > 0 else "" -# logger.debug('substat is none, descr of bus <%s> is <%s>' % (name, descr)) -# else: -# descr = ga(item, 'desc') -# descr = descr[0] if len(descr) > 0 else "" -# logger.debug('no attribute "substat", descr of bus <%s> is <%s>' % (name, descr)) -# -# try: -# zone = ga(item, 'Grid') -# zone_name = ga(zone, 'loc_name').split('.ElmNet')[0] -# logger.debug('zone "Grid" found: <%s>' % zone_name) -# except: -# zone = ga(item, 'cpGrid') -# zone_name = ga(zone, 'loc_name').split('.ElmNet')[0] -# logger.debug('zone "cpGrid" found: <%s>' % zone_name) -# -# temp_df_a = net.bus[net.bus.zone == zone_name] -# temp_df_b = temp_df_a[temp_df_a.type == bus_type] -# temp_df_c = temp_df_b[temp_df_a.description == descr] -# bus_index = temp_df_c[temp_df_b.name == name].index.values[0] -# logger.debug('bus index in net of bus <%s> is <%d>' % (name, bus_index)) -# -# return bus_index - - def find_bus_index_in_net(pf_bus, net): # bid = bus_dict.get(pf_bus, -1) # i want key error @@ -504,7 +667,12 @@ def get_connection_nodes(net, item, num_nodes): buses = [] for i in range(num_nodes): try: - pf_bus = item.GetNode(i) + # wenn elmlodlvp, dann bus von elmlodlv parent + if item.GetClassName() == 'ElmLodlvp': + parent = item.GetParent() + pf_bus = parent.GetNode(0) + else: + pf_bus = item.GetNode(i) except Exception as err: logger.error('GetNode failed for %s' % item) logger.error(err) @@ -701,6 +869,9 @@ def create_line(net, item, flag_graphics, create_sections, is_unbalanced): lid = create_line_normal(net=net, item=item, is_unbalanced=is_unbalanced, **params) sid_list = [lid] logger.debug('created line <%s> with index <%d>' % (params['name'], lid)) + # if coords: + # net.line_geodata.loc[lid, 'coords'] = coords + else: logger.debug('line <%s> has sections' % params['name']) @@ -823,20 +994,9 @@ def get_section_coords(coords, sec_len, start_len, scale_factor): return sec_coords -def segment_buses(net, bus1, bus2, num_sections, line_name): # , sec_len, start_len, coords): +def segment_buses(net, bus1, bus2, num_sections, line_name): yield bus1 m = 1 - # if coords: - # if bus1 not in net.bus_geodata.index: - # logger.warning('bus1 not in coords, bus: %d, line: %s' % (bus1, line_name)) - # if bus2 not in net.bus_geodata.index: - # logger.warning('bus2 not in coords, bus: %d, line: %s' % (bus2, line_name)) - # x1, y1 = net.bus_geodata.loc[bus1, ['x', 'y']] - # x2, y2 = net.bus_geodata.loc[bus2, ['x', 'y']] - # # tot_len = ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5 - # tot_len = calc_len_coords(coords) - # scale_factor = tot_len / sum(sec_len) - # split_len = 0 while m < num_sections: bus_name = "%s (Muff %u)" % (line_name, m) @@ -844,14 +1004,6 @@ def segment_buses(net, bus1, bus2, num_sections, line_name): # , sec_len, start zone = net.bus.at[bus1, "zone"] k = pp.create_bus(net, name=bus_name, type='ls', vn_kv=vn_kv, zone=zone) - # if coords: - # split_len += sec_len[m - 1] * scale_factor - # - # x_k, y_k = cut_coords_segment([x1, y1], [x2, y2], split_len) - # net.bus_geodata.loc[k, ['x', 'y']] = x_k, y_k - # if x_k == 0 or y_k == 0: - # logger.warning('bus %d has 0 coords, bus1: %d, bus2: %d' % k, bus1, bus2) - if "description" in net.bus: net.bus.at[k, "description"] = u"" yield k @@ -861,8 +1013,8 @@ def segment_buses(net, bus1, bus2, num_sections, line_name): # , sec_len, start yield bus2 -def create_line_sections(net, item_list, line, bus1, bus2, coords, parallel, is_unbalanced, **kwargs): - sid_list = [] +def create_line_sections(net, item_list, line, bus1, bus2, coords, parallel, is_unbalanced, line_idx, **kwargs): + lines_to_create = [] line_name = line.loc_name item_list.sort(key=lambda x: x.index) # to ensure they are in correct order @@ -872,7 +1024,6 @@ def create_line_sections(net, item_list, line, bus1, bus2, coords, parallel, is_ line_loading = np.nan sec_len = [sec.dline for sec in item_list] - # start_len = [sec.rellen for sec in item_list] buses_gen = segment_buses(net, bus1=bus1, bus2=bus2, num_sections=len(item_list), line_name=line_name) @@ -882,25 +1033,29 @@ def create_line_sections(net, item_list, line, bus1, bus2, coords, parallel, is_ section_name = item.loc_name bus1 = next(buses_gen) bus2 = next(buses_gen) - sid = create_line_normal(net=net, item=item, bus1=bus1, bus2=bus2, name=name, - parallel=parallel, is_unbalanced=is_unbalanced) - sid_list.append(sid) - net.line.at[sid, "section"] = section_name - net.res_line.at[sid, "pf_loading"] = line_loading + #items_nogeo = []# + sec_coords = None if coords: try: scaling_factor = sum(sec_len) / calc_len_coords(coords) sec_coords = get_section_coords(coords, sec_len=item.dline, start_len=item.rellen, scale_factor=scaling_factor) - net.line_geodata.loc[sid, 'coords'] = sec_coords - # p1 = sec_coords[0] - # p2 = sec_coords[-1] - net.bus_geodata.loc[bus2, ['x', 'y']] = sec_coords[-1] - except ZeroDivisionError: + # lines_to_create[-1]['coords'] = sec_coords + # net.bus_geodata.loc[bus2, ['x', 'y']] = sec_coords[-1] + except: + #items_nogeo.append(item) logger.warning("Could not generate geodata for line !!") - return sid_list + lines_to_create.append(create_line_normal(net=net, item=item, bus1=bus1, bus2=bus2, + name=name, parallel=parallel, + is_unbalanced=is_unbalanced, coords=sec_coords, + line_idx=line_idx)) + lines_to_create[-1]["section"] = section_name + lines_to_create[-1]["pf_loading"] = line_loading + lines_to_create[-1]["folder_id"] = line.fold_id.loc_name + + return lines_to_create def create_line_no_sections(net, main_item, item_list, bus1, bus2, coords, is_unbalanced, **kwargs): @@ -962,12 +1117,11 @@ def create_line_no_sections(net, main_item, item_list, bus1, bus2, coords, is_un return lid -def create_line_normal(net, item, bus1, bus2, name, parallel, is_unbalanced, geodata=None): +def create_line_normal(net, item, bus1, bus2, name, line_idx, parallel, is_unbalanced, geodata=None): pf_type = item.typ_id - std_type, type_created = create_line_type(net=net, item=pf_type, - cable_in_air=item.inAir if item.HasAttribute( - 'inAir') else False) - + std_type, _ = create_line_type(net=net, item=pf_type, + cable_in_air=item.inAir if item.HasAttribute('inAir') + else False) params = { 'from_bus': bus1, 'to_bus': bus2, @@ -976,21 +1130,26 @@ def create_line_normal(net, item, bus1, bus2, name, parallel, is_unbalanced, geo 'length_km': item.dline, 'df': item.fline, 'parallel': parallel, - 'alpha': pf_type.alpha if pf_type is not None else None, - 'temperature_degree_celsius': pf_type.tmax if pf_type is not None else None, + # 'alpha': pf_type.alpha if pf_type is not None else None, #TODO: make this possible again + # 'temperature_degree_celsius': pf_type.tmax if pf_type is not None else None, #TODO: make this possible again + 'description': ' \n '.join(item.desc) if len(item.desc) > 0 else '', + 'line_idx': line_idx, + "folder_id": item.fold_id.loc_name, + 'section_idx': int(0), # TODO how should this be correct?, + # "coords": coords, 'geodata': geodata } if std_type is not None: #and not is_unbalanced:delete later params["std_type"] = std_type logger.debug('creating normal line with type <%s>' % std_type) - lid = pp.create_line(net, **params) +# lines_to_create.append(params) +# lid = pp.create_line(net, **params) else: logger.debug('creating normal line <%s> from parameters' % name) r_ohm, x_ohm, c_nf = item.R1, item.X1, item.C1 r0_ohm, x0_ohm, c0_nf = item.R0, item.X0, item.C0 - if r_ohm == 0 and x_ohm == 0 and c_nf == 0: logger.error('Incomplete data for line "%s": missing type and ' 'missing parameters R, X, C' % name) @@ -1005,7 +1164,7 @@ def create_line_normal(net, item, bus1, bus2, name, parallel, is_unbalanced, geo 'x0_ohm_per_km': x0_ohm / params['length_km'], 'c0_nf_per_km': c0_nf / params['length_km'] * 1e3, # internal unit for C in PF is uF, 'max_i_ka': item.Inom if item.Inom != 0 else 1e-3, - 'alpha': pf_type.alpha if pf_type is not None else None + # 'alpha': pf_type.alpha if pf_type is not None else None #TODO: make this possible again }) coupling = item.c_ptow @@ -1021,17 +1180,20 @@ def create_line_normal(net, item, bus1, bus2, name, parallel, is_unbalanced, geo else: params['type'] = None - lid = pp.create_line_from_parameters(net=net, **params) +# lines_to_create.append(params) + return params - net.line.loc[lid, 'description'] = ' \n '.join(item.desc) if len(item.desc) > 0 else '' - if hasattr(item, "cimRdfId"): - chr_name = item.cimRdfId - if chr_name is not None and len(chr_name) > 0: - net["line"].loc[lid, 'origin_id'] = chr_name[0] +# lid = pp.create_line_from_parameters(net=net, **params) - get_pf_line_results(net, item, lid, is_unbalanced) +#TODO get_pf_line_results(net, item, lid, is_unbalanced) + # net.line.loc[lid, 'description'] = ' \n '.join(item.desc) if len(item.desc) > 0 else '' + # if hasattr(item, "cimRdfId"): + # chr_name = item.cimRdfId + # if chr_name is not None and len(chr_name) > 0: + # net["line"].loc[lid, 'origin_id'] = chr_name[0] + + # get_pf_line_results(net, item, lid, is_unbalanced) - return lid def get_pf_line_results(net, item, lid, is_unbalanced): @@ -1066,7 +1228,6 @@ def get_pf_line_results(net, item, lid, is_unbalanced): def create_line_type(net, item, cable_in_air=False): # return False if no line type has been created # return True if a new line type has been created - if item is None: logger.warning('create_line_type: no item given! Be sure you can deal with None!') return None, False @@ -1096,7 +1257,7 @@ def create_line_type(net, item, cable_in_air=False): "alpha": item.alpha } pp.create_std_type(net, type_data, name, "line") - logger.debug('>> created line type <%s>' % name) + logger.info('>> created line type <%s>' % name) return name, True @@ -1304,7 +1465,7 @@ def ask_load_params(item, pf_variable_p_loads, dict_net, variables): multiplier = get_power_multiplier(item, pf_variable_p_loads) params = ADict() if pf_variable_p_loads == 'm:P:bus1' and not item.HasResults(0): - raise RuntimeError('load %s does not have results and is ignored' % item.loc_name) + raise RuntimeError('load %s does not have results and is ignored' % item.loc_name) if 'p_mw' in variables: params.p_mw = ga(item, pf_variable_p_loads) * multiplier if 'q_mvar' in variables: @@ -1323,7 +1484,12 @@ def ask_load_params(item, pf_variable_p_loads, dict_net, variables): logger.debug('load parameters: %s' % params) global_scaling = dict_net['global_parameters']['global_load_scaling'] - params.scaling = global_scaling * item.scale0 \ + # loadlvp no scale0, scaling immer 1 + if item.HasAttribute('scale0'): + scale = item.scale0 + else: + scale = 1 + params.scaling = global_scaling * scale \ if pf_variable_p_loads == 'plini' else 1 if item.HasAttribute('zonefact'): params.scaling *= item.zonefact @@ -1395,7 +1561,7 @@ def make_split_dict(line): section = find_section(load, sections) split_dict[section] = split_dict.get(section, []).append(load) - else: + else:#รถ. for load in loads: split_dict[line] = split_dict.get(line, []).append(load) return split_dict @@ -1456,134 +1622,6 @@ def split_line_add_bus(net, split_dict): pass -def split_line_add_bus_old(net, item, parent): - # get position at line - # find line section - previous_sec_len = 0 - sections = parent.GetContents('*.ElmLnesec') - sections.sort(key=lambda x: x.index) # to ensure they are in correct order - if len(sections) == 0: - # cool! no sections - split the line - sec = parent - has_sections = False - logger.debug('line has no sections') - else: - has_sections = True - logger.debug('line has %d sections' % len(sections)) - for s in sections: - logger.debug('section start: %s, load pos: %s, section end: %s' % ( - s.rellen, item.lneposkm, s.dline)) - if s.rellen <= item.lneposkm <= s.rellen + s.dline: - sec = s - logger.debug('found section: %s' % sec) - break - else: - previous_sec_len += s.dline - else: - raise RuntimeError("could not find section where ElmLodlvp %s belongs" % item.loc_name) - - # found section in powerfactory - # at this point the section can be split by other loads and its length can vary - # now find section in pandapower net - if has_sections: - sid = net.line.loc[ - (net.line.name == parent.loc_name) & (net.line.section == sec.loc_name)].index - logger.debug('index of section in net: %s' % sid) - else: - sid = net.line.loc[(net.line.name == parent.loc_name)].index - logger.debug('index of line in net: %s' % sid) - # check - if len(sid) > 1: - # section_idx is 0, 1, ... - for m in range(len(sid)): - # find the correct section for lodlvp - temp_lines = net.line.loc[sid] - logger.debug('temp_lines: %s' % temp_lines) - temp_sec_len = float(temp_lines.loc[temp_lines.section_idx == m, 'length_km']) - logger.debug('temp_sec_len of sec nr. %d: %.3f' % (m, temp_sec_len)) - if (temp_sec_len + previous_sec_len) >= item.lneposkm: - # temp_section = temp_lines.query('section_idx == @m') - # sid = temp_lines[temp_lines.section_idx == m].index.values[0] - sid = sid[m] - logger.debug('found section for creating lodlvp: %d' % sid) - break - else: - previous_sec_len += temp_sec_len - else: - raise RuntimeError( - "could not find line or section where ElmLodlvp %s belongs: multiple indices " - "found in net and none of them is good" % item.loc_name) - elif len(sid) == 0: - raise RuntimeError( - "could not find line or section where ElmLodlvp %s belongs: no index found in net" % - item.loc_name) - else: - sid = sid.values[0] - logger.debug('index is unique: %d' % sid) - - # new line lengths - tot_len = net.line.at[sid, 'length_km'] - sec_len_a = item.lneposkm - previous_sec_len - sec_len_b = tot_len - sec_len_a - logger.debug('total length: %.3f, a: %.3f, b:%.3f' % (tot_len, sec_len_a, sec_len_b)) - if sec_len_b < 0: - raise RuntimeError('incorrect length for section %s: %.3f' % (sec, sec_len_b)) - - # get coords - if sid in net.line_geodata.index.values: - logger.debug('line has coords') - coords = net.line_geodata.at[sid, 'coords'] - logger.debug('old geodata of line %d: %s' % (sid, coords)) - - # get coords for 2 split lines - coords_len = calc_len_coords(coords) - scale_factor = parent.dline / coords_len # scale = real_len / coords_len - coords_a = get_section_coords(coords, sec_len_a, 0, scale_factor) - coords_b = get_section_coords(coords, sec_len_b, sec_len_a, scale_factor) - logger.debug('new coords: %s; %s' % (coords_a, coords_b)) - - # get bus coords - bus_coords = tuple(coords_b[0]) - logger.debug('new bus coords: %.3f, %.3f' % bus_coords) - else: - logger.debug('line has no coords') - bus_coords = None - coords_a = None - coords_b = None - - if sec_len_b > 0: - # create new bus - vn_kv = net.bus.at[net.line.at[sid, 'from_bus'], 'vn_kv'] - name = 'LodLV-%s' % item.loc_name - bus = pp.create_bus(net, vn_kv=vn_kv, name=name, geodata=bus_coords, type='n') - net.bus.loc[bus, 'description'] = 'Partial load %s = %.3f kW' % (item.loc_name, item.plini) - logger.debug('created new bus in net: %s' % net.bus.loc[bus]) - - # create new line - lid = pp.create_line(net, from_bus=bus, to_bus=net.line.at[sid, 'to_bus'], - length_km=sec_len_b, - std_type=net.line.at[sid, 'std_type'], - name=net.line.at[sid, 'name'], df=net.line.at[sid, 'df']) - net.line.at[lid, 'section'] = net.line.at[sid, 'section'] - net.line_geodata.loc[lid, 'coords'] = coords_b - if not net.line.loc[sid, 'section_idx']: - net.line.loc[sid, 'section_idx'] = 0 - - net.line.loc[lid, 'section_idx'] = net.line.at[sid, 'section_idx'] + 1 - - logger.debug('old line: %s' % net.line.loc[sid]) - logger.debug('new line: %s' % net.line.loc[lid]) - - net.line.at[sid, 'to_bus'] = bus - net.line.at[sid, 'length_km'] = sec_len_a - net.line_geodata.loc[sid, 'coords'] = coords_a - logger.debug('changed: %s' % net.line.loc[sid]) - else: - # no new bus/line are created: take the to_bus - bus = net.line.at[sid, 'to_bus'] - return bus - - def create_load(net, item, pf_variable_p_loads, dict_net, is_unbalanced): # params collects the input parameters for the create function params = ADict() @@ -1849,9 +1887,7 @@ def create_sgen_genstat(net, item, pv_as_slack, pf_variable_p_gen, dict_net, is_ for param in params.keys(): if any(param.startswith(prefix) for prefix in ["p_", "q_", "sn_"]): params[param] *= ngnum -# params.p_mw *= ngnum -# params.q_mvar *= ngnum -# params.sn_mva *= ngnum + if is_unbalanced: pf_sgen_type = item.phtech params.type = map_sgen_type_var(pf_sgen_type) @@ -1883,43 +1919,6 @@ def create_sgen_genstat(net, item, pv_as_slack, pf_variable_p_gen, dict_net, is_ logger.debug('created genstat <%s> as element <%s> at index <%d>' % (params.name, element, sg)) - ########################### - -# if is_unbalanced: -# pf_sgen_type = item.phtech -# params.type = map_type_var(pf_sgen_type) -# # create... -# try: -# # net, bus, p_mw, q_mvar=0, sn_mva=np.nan, name=None, scaling=1., index=None, -# # in_service=True, type=None -# if is_unbalanced: -# sg = pp.create_asymmetric_sgen(net, **params) -# logger.info("CREATING UNBALANCED SGEN") -# else: -# logger.info("CREATING BALANCED SGEN") -# sg = pp.create_sgen_genstat(net, **params) -# logger.debug('created sgen with index <%d>' % sg) -# except Exception as err: -# logger.error('While creating %s.%s, error occured: %s' % (params.name, sgen_class, err)) -# raise err -# -# sgen_type = None -# -# if is_unbalanced: -# sgen_type = "asymmetric_sgen" -# else: -# sgen_type = "sgen" -# -# net[sgen_type].loc[sg, 'description'] = ' \n '.join(item.desc) if len(item.desc) > 0 else '' -# attr_list = ["sernum", "for_name", "chr_name", 'cpSite.loc_name'] -# if sgen_class == 'ElmGenstat': -# attr_list.extend(['pnight', 'cNrCust', 'cPrCust', 'UtilFactor', 'cSmax', 'cSav', 'ccosphi']) -# add_additional_attributes(item, net, sgen_type, sg, attr_list=attr_list) -# get_pf_sgen_results(net, item, sg, is_unbalanced) -# -# -# logger.debug('created sgen <%s> at index <%d>' % (params.name, sg)) - def get_pf_sgen_results(net, item, sg, is_unbalanced, element='sgen'): result_variables = None @@ -2221,8 +2220,6 @@ def create_trafo(net, item, export_controller=True, tap_opt="nntap", is_unbalanc logger.error("Cannot add Trafo '%s': not connected" % name) return - propagate_bus_coords(net, bus1, bus2) - if not net.bus.vn_kv[bus1] >= net.bus.vn_kv[bus2]: logger.error('trafo <%s>: violated condition of HV >= LV!' % name) # assert net.bus.vn_kv[bus1] >= net.bus.vn_kv[bus2] @@ -2499,14 +2496,6 @@ def create_trafo3w(net, item, tap_opt='nntap'): y_points=(vkr_min, vkr_neutral, vkr_max)) -def propagate_bus_coords(net, bus1, bus2): - pass - # if bus1 in net.bus_geodata.index and bus2 not in net.bus_geodata.index: - # net.bus_geodata.loc[bus2, ['x', 'y']] = net.bus_geodata.loc[bus1, ['x', 'y']] - # elif bus2 in net.bus_geodata.index and bus1 not in net.bus_geodata.index: - # net.bus_geodata.loc[bus1, ['x', 'y']] = net.bus_geodata.loc[bus2, ['x', 'y']] - - def create_coup(net, item, is_fuse=False): switch_types = {"cbk": "CB", "sdc": "LBS", "swt": "LS", "dct": "DS"} name = item.loc_name @@ -2518,7 +2507,6 @@ def create_coup(net, item, is_fuse=False): logger.error("Cannot add Coup '%s': not connected" % name) return - propagate_bus_coords(net, bus1, bus2) if not item.HasAttribute('isclosed') and not is_fuse: logger.error('switch %s does not have the attribute isclosed!' % item) switch_is_closed = bool(item.on_off) \ @@ -2541,33 +2529,6 @@ def create_coup(net, item, is_fuse=False): bool(item.isclosed) if item.HasAttribute('isclosed') else True), in_service -# # false approach, completely irrelevant -# def create_switch(net, item): -# switch_types = {"cbk": "CB", "sdc": "LBS", "swt": "LS", "dct": "DS"} -# name = ga(item, 'loc_name') -# logger.debug('>> creating switch <%s>' % name) -# -# pf_bus1 = item.GetNode(0) -# pf_bus2 = item.GetNode(1) -# -# # here: implement situation if line not connected -# if pf_bus1 is None or pf_bus2 is None: -# logger.error("Cannot add Switch '%s': not connected" % name) -# return -# -# bus1 = find_bus_index_in_net(pf_bus1, net) -# bus2 = find_bus_index_in_net(pf_bus2, net) -# logger.debug('switch %s connects buses <%d> and <%d>' % (name, bus1, bus2)) -# -# switch_is_closed = bool(ga(item, 'on_off')) -# switch_usage = switch_types[ga(item, 'aUsage')] -# -# cd = pp.create_switch(net, name=name, bus=bus1, element=bus2, et='b', -# closed=switch_is_closed, type=switch_usage) -# logger.debug('created switch at index <%d>, closed = %s, usage = %s' % (cd, -# switch_is_closed, switch_usage)) - - def create_shunt(net, item): try: bus = get_connection_nodes(net, item, 1) diff --git a/pandapower/create.py b/pandapower/create.py index 32263db515..1048485dc7 100644 --- a/pandapower/create.py +++ b/pandapower/create.py @@ -2262,6 +2262,12 @@ def create_lines(net, from_buses, to_buses, length_km, std_type, name=None, inde entries["g_us_per_km"] = [line_param_dict.get("g_us_per_km", 0) for line_param_dict in \ lineparam] entries["type"] = [line_param_dict.get("type", None) for line_param_dict in lineparam] + try: + entries["r0_ohm_per_km"] = list(map(itemgetter("r0_ohm_per_km"), lineparam)) + entries["x0_ohm_per_km"] = list(map(itemgetter("x0_ohm_per_km"), lineparam)) + entries["c0_nf_per_km"] = list(map(itemgetter("c0_nf_per_km"), lineparam)) + except: + pass _add_to_entries_if_not_nan(net, "line", entries, index, "max_loading_percent", max_loading_percent)