def _calc_trafo_parameter(net, ppc, bus_lookup, calculate_voltage_angles, trafo_model, set_opf_constraints=False): ''' Calculates the transformer parameter in per unit. **INPUT**: **net** - The Pandapower format network **RETURN**: **temp_para** - Temporary transformer parameter. Which is a np.complex128 Numpy array. with the following order: 0:hv_bus; 1:lv_bus; 2:r_pu; 3:x_pu; 4:b_pu; 5:tab, 6:shift ''' temp_para = np.zeros(shape=(len(net["trafo"].index), 9), dtype=np.complex128) trafo = net["trafo"] temp_para[:, 0] = get_indices(trafo["hv_bus"].values, bus_lookup) temp_para[:, 1] = get_indices(trafo["lv_bus"].values, bus_lookup) temp_para[:, 2:6] = _calc_branch_values_from_trafo_df(net, ppc, bus_lookup, trafo_model) if calculate_voltage_angles: temp_para[:, 6] = trafo["shift_degree"].values else: temp_para[:, 6] = np.zeros(shape=(len(trafo.index),), dtype=np.complex128) temp_para[:, 7] = trafo["in_service"].values if set_opf_constraints: max_load = trafo.max_loading_percent if "max_loading_percent" in trafo else 1000 temp_para[:, 8] = max_load / 100 * trafo.sn_kva / 1000 return temp_para
def _calc_impedance_parameter(net, bus_lookup): t = np.zeros(shape=(len(net["impedance"].index), 5), dtype=np.complex128) t[:, 0] = get_indices(net["impedance"]["from_bus"].values, bus_lookup) t[:, 1] = get_indices(net["impedance"]["to_bus"].values, bus_lookup) t[:, 2] = net["impedance"]["r_pu"] / net["impedance"]["sn_kva"] * 1000. t[:, 3] = net["impedance"]["x_pu"] / net["impedance"]["sn_kva"] * 1000. t[:, 4] = net["impedance"]["in_service"].values return t
def _calc_xward_parameter(net, ppc, is_elems, bus_lookup): bus_is = is_elems['bus'] baseR = np.square(get_values(ppc["bus"][:, BASE_KV], net["xward"]["bus"].values, bus_lookup)) t = np.zeros(shape=(len(net["xward"].index), 5), dtype=np.complex128) xw_is = np.in1d(net["xward"].bus.values, bus_is.index) \ & net["xward"].in_service.values.astype(bool) t[:, 0] = get_indices(net["xward"]["bus"].values, bus_lookup) t[:, 1] = get_indices(net["xward"]["ad_bus"].values, bus_lookup) t[:, 2] = net["xward"]["r_ohm"] / baseR t[:, 3] = net["xward"]["x_ohm"] / baseR t[:, 4] = xw_is return t
def _calc_trafo3w_parameter(net, ppc, bus_lookup, calculate_voltage_angles, trafo_model): trafo_df = _trafo_df_from_trafo3w(net) temp_para = np.zeros(shape=(len(trafo_df), 8), dtype=np.complex128) temp_para[:, 0] = get_indices(trafo_df["hv_bus"].values, bus_lookup) temp_para[:, 1] = get_indices(trafo_df["lv_bus"].values, bus_lookup) temp_para[:, 2:6] = _calc_branch_values_from_trafo_df( net, ppc, bus_lookup, trafo_model, trafo_df) if calculate_voltage_angles: temp_para[:, 6] = trafo_df["shift_degree"].values else: temp_para[:, 6] = np.zeros(shape=(len(trafo_df.index),), dtype=np.complex128) temp_para[:, 7] = trafo_df["in_service"].values return temp_para
def _calc_shunts_and_add_on_ppc(net, ppc, is_elems, bus_lookup): # get in service elements bus_is = is_elems['bus'] s = net["shunt"] shunt_is = np.in1d(s.bus.values, bus_is.index) \ & s.in_service.values.astype(bool) vl = shunt_is / np.float64(1000.) sp = s["p_kw"].values * vl sq = s["q_kvar"].values * vl w = net["ward"] ward_is = np.in1d(w.bus.values, bus_is.index) \ & w.in_service.values.astype(bool) vl = ward_is / np.float64(1000.) wp = w["pz_kw"].values * vl wq = w["qz_kvar"].values * vl xw = net["xward"] xward_is = np.in1d(xw.bus.values, bus_is.index) \ & xw.in_service.values.astype(bool) vl = xward_is / np.float64(1000.) xwp = xw["pz_kw"].values * vl xwq = xw["qz_kvar"].values * vl b = get_indices(np.hstack([s["bus"].values, w["bus"].values, xw["bus"].values]), bus_lookup) b, vp, vq = _sum_by_group(b, np.hstack([sp, wp, xwp]), np.hstack([sq, wq, xwq])) ppc["bus"][b, GS] = vp ppc["bus"][b, BS] = -vq
def reindex_junctions(net, junction_lookup): """ Changes the index of net.junction and considers the new junction indices in all other pandapipes element tables. :param net: pandapipes network :type net: pandapipesNet :param junction_lookup: the keys are the old junction indices, the values the new junction \ indices :type junction_lookup: dict :return: junction_lookup - the finally reindexed junction lookup (with corrections if necessary) :rtype: dict """ not_fitting_junction_lookup_keys = set(junction_lookup.keys()) - set( net.junction.index) if len(not_fitting_junction_lookup_keys): logger.error( "These junction indices are unknown to net. Thus, they cannot be reindexed: " + str(not_fitting_junction_lookup_keys)) missing_junction_indices = sorted( set(net.junction.index) - set(junction_lookup.keys())) if len(missing_junction_indices): duplicates = set(missing_junction_indices).intersection( set(junction_lookup.values())) if len(duplicates): logger.error( "The junctions %s are not listed in junction_lookup but their index is " "used as a new index. This would result in duplicated junction indices." % (str(duplicates))) else: junction_lookup.update({j: j for j in missing_junction_indices}) net.junction.index = get_indices(net.junction.index, junction_lookup) if hasattr(net, "res_junction"): net.res_junction.index = get_indices(net.res_junction.index, junction_lookup) for element, value in element_junction_tuples(net=net): if element in net.keys(): net[element][value] = get_indices(net[element][value], junction_lookup) net["junction_geodata"].set_index(get_indices( net["junction_geodata"].index, junction_lookup), inplace=True) return junction_lookup
def reindex_elements(net, element, new_indices, old_indices=None): """ Changes the index of net[element]. :param net: pandapipes network :type net: pandapipesNet :param element: name of the element table :type element: str :param new_indices: list of new indices :type new_indices: iterable :param old_indices: list of old/previous indices which will be replaced. If None, all indices\ are considered. :type old_indices: iterable, default None :return: No output. """ old_indices = old_indices if old_indices is not None else net[element].index if not len(new_indices) or not net[element].shape[0]: return if len(new_indices) != len(old_indices): raise UserWarning( "The length of new indices to replace existing ones for %s does not " "match: %d (new) vs. %d (old)." % (element, len(new_indices), len(old_indices))) lookup = dict(zip(old_indices, new_indices)) if element == "junction": reindex_junctions(net, lookup) return # --- reindex net[element]["index"] = net[element].index net[element].loc[old_indices, "index"] = get_indices(old_indices, lookup) net[element].set_index("index", inplace=True) # --- adapt geodata index geotable = element + "_geodata" if geotable in net and net[geotable].shape[0]: net[geotable]["index"] = net[geotable].index net[geotable].loc[old_indices, "index"] = get_indices(old_indices, lookup) net[geotable].set_index("index", inplace=True)
def _get_xward_branch_results(net, ppc, bus_lookup, pq_buses, f, t, ac=True): p_branch_xward = ppc["branch"][f:t, PF].real * 1e3 net["res_xward"]["p_kw"] += p_branch_xward if ac: q_branch_xward = ppc["branch"][f:t, QF].real * 1e3 net["res_xward"]["q_kvar"] += q_branch_xward else: q_branch_xward = np.zeros(len(p_branch_xward)) b_pp, p, q = _sum_by_group(net["xward"]["bus"].values, p_branch_xward, q_branch_xward) b_ppc = get_indices(b_pp, bus_lookup, fused_indices=False) pq_buses[b_ppc, 0] += p pq_buses[b_ppc, 1] += q
def _calc_line_parameter(net, ppc, bus_lookup, set_opf_constraints=False): """ calculates the line parameter in per unit. **INPUT**: **net** -The Pandapower format network **RETURN**: **t** - Temporary line parameter. Which is a complex128 Nunmpy array. with the following order: 0:bus_a; 1:bus_b; 2:r_pu; 3:x_pu; 4:b_pu """ # baseR converts Ohm to p.u. Formula is U^2/Sref. Sref is 1 MVA and vn_kv is # in kV U^2* ((10^3 V)^2/10^6 VA) = U^2 # Therefore division by 1 MVA is not necessary. line = net["line"] fb = get_indices(line["from_bus"], bus_lookup) tb = get_indices(line["to_bus"], bus_lookup) length = line["length_km"].values parallel = line["parallel"] baseR = np.square(ppc["bus"][fb, BASE_KV]) t = np.zeros(shape=(len(line.index), 7), dtype=np.complex128) t[:, 0] = fb t[:, 1] = tb t[:, 2] = line["r_ohm_per_km"] * length / baseR / parallel t[:, 3] = line["x_ohm_per_km"] * length / baseR / parallel t[:, 4] = 2 * net.f_hz * math.pi * line["c_nf_per_km"] * 1e-9 * baseR * length * parallel t[:, 5] = line["in_service"] if set_opf_constraints: max_load = line.max_loading_percent if "max_loading_percent" in line else 1000 vr = net.bus.vn_kv[fb].values * np.sqrt(3) t[:, 6] = max_load / 100 * line.imax_ka * line.df * parallel * vr return t
def test_get_indices(): a = [i + 100 for i in range(10)] lookup = {idx: pos for pos, idx in enumerate(a)} lookup["before_fuse"] = a # First without fused buses no magic here # after fuse result = get_indices([102, 107], lookup, fused_indices=True) assert np.array_equal(result, [2, 7]) # before fuse result = get_indices([2, 7], lookup, fused_indices=False) assert np.array_equal(result, [102, 107]) # Same setup EXCEPT we have fused buses now (bus 102 and 107 are fused) lookup[107] = lookup[102] # after fuse result = get_indices([102, 107], lookup, fused_indices=True) assert np.array_equal(result, [2, 2]) # before fuse result = get_indices([2, 7], lookup, fused_indices=False) assert np.array_equal(result, [102, 107])
def create_continuous_bus_index(net): """ Creates a continuous bus index starting at zero and replaces all references of old indices by the new ones. """ new_bus_idxs = np.arange(len(net.bus)) bus_lookup = dict(zip(net["bus"].index.values, new_bus_idxs)) net.bus.index = new_bus_idxs for element, value in [("line", "from_bus"), ("line", "to_bus"), ("trafo", "hv_bus"), ("trafo", "lv_bus"), ("sgen", "bus"), ("load", "bus"), ("switch", "bus"), ("ward", "bus"), ("xward", "bus"), ("impedance", "from_bus"), ("impedance", "to_bus"), ("shunt", "bus"), ("ext_grid", "bus")]: net[element][value] = get_indices(net[element][value], bus_lookup) return net
def _get_bus_results(net, ppc, bus_lookup, bus_pq, return_voltage_angles, ac=True): net["res_bus"]["p_kw"] = bus_pq[:, 0] if ac: net["res_bus"]["q_kvar"] = bus_pq[:, 1] ppi = net["bus"].index.values bus_idx = get_indices(ppi, bus_lookup) if ac: net["res_bus"]["vm_pu"] = ppc["bus"][bus_idx][:, VM] net["res_bus"].index = net["bus"].index if return_voltage_angles or not ac: net["res_bus"]["va_degree"] = ppc["bus"][bus_idx][:, VA]
def _calc_loads_and_add_on_ppc(net, ppc, is_elems, bus_lookup): # get in service elements bus_is = is_elems['bus'] l = net["load"] # element_is = check if element is at a bus in service & element is in service load_is = np.in1d(l.bus.values, bus_is.index) \ & l.in_service.values.astype(bool) vl = load_is * l["scaling"].values.T / np.float64(1000.) lp = l["p_kw"].values * vl lq = l["q_kvar"].values * vl s = net["sgen"] sgen_is = np.in1d(s.bus.values, bus_is.index) \ & s.in_service.values.astype(bool) vl = sgen_is * s["scaling"].values.T / np.float64(1000.) sp = s["p_kw"].values * vl sq = s["q_kvar"].values * vl w = net["ward"] ward_is = np.in1d(w.bus.values, bus_is.index) \ & w.in_service.values.astype(bool) vl = ward_is / np.float64(1000.) wp = w["ps_kw"].values * vl wq = w["qs_kvar"].values * vl xw = net["xward"] xward_is = np.in1d(xw.bus.values, bus_is.index) \ & xw.in_service.values.astype(bool) vl = xward_is / np.float64(1000.) xwp = xw["ps_kw"].values * vl xwq = xw["qs_kvar"].values * vl b = get_indices(np.hstack([l["bus"].values, s["bus"].values, w["bus"].values, xw["bus"].values] ), bus_lookup) b, vp, vq = _sum_by_group(b, np.hstack([lp, sp, wp, xwp]), np.hstack([lq, sq, wq, xwq])) ppc["bus"][b, PD] = vp ppc["bus"][b, QD] = vq
def _build_bus_ppc(net, ppc, is_elems, init_results=False, set_opf_constraints=False): """ """ if len(net["trafo3w"]) > 0: # TODO: include directly in pd2ppc so that buses are only in ppc, not in pandapower. LT _create_trafo3w_buses(net, init_results) if len(net["xward"]) > 0: # TODO: include directly in pd2ppc so that buses are only in ppc, not in pandapower. LT _create_xward_buses(net, init_results) # get buses as set bus_list = set(net["bus"].index.values) # get in service elements eg_is = is_elems['eg'] gen_is = is_elems['gen'] bus_is = is_elems['bus'] # create a mapping from arbitrary pp-index to a consecutive index starting at zero (ppc-index) # To sort the array first, so that PV comes first, three steps are necessary: # 1. Find PV / Slack nodes and place them first (necessary for fast generation of Jacobi-Matrix) # get indices of PV (and ref) buses if len(net["xward"]) > 0: # add xwards if available pv_ref = set((np.r_[eg_is["bus"].values, gen_is["bus"].values, net["xward"][ net["xward"].in_service == 1]["ad_bus"].values]).flatten()) else: pv_ref = set(np.r_[eg_is["bus"].values, gen_is["bus"].values].flatten()) # 2. Add PQ buses without switches slidx = (net["switch"]["closed"].values == 1) & (net["switch"]["et"].values == "b") &\ (net["switch"]["bus"].isin(bus_is.index).values) & ( net["switch"]["element"].isin(bus_is.index).values) # get buses with switches switch_buses = set((np.r_[net["switch"]["bus"].values[slidx], net[ "switch"]["element"].values[slidx]]).flatten()) pq_buses_without_switches = (bus_list - switch_buses) - pv_ref # consecutive values for pv, ref, and non switch pq buses npArange = np.arange(len(pq_buses_without_switches) + len(pv_ref)) # buses in PandaPower PandaBusses = sorted(pv_ref) + sorted(pq_buses_without_switches) # generate bus_lookup PandaPower -> [PV, PQ(without switches)] bus_lookup = dict(zip(PandaBusses, npArange)) # 3. Add PQ buses with switches and fuse them v = defaultdict(set) # get the pp-indices of the buses for those switches fbus = net["switch"]["bus"].values[slidx] tbus = net["switch"]["element"].values[slidx] # create a mapping to map each bus to itself at frist ... ds = DisjointSet({e: e for e in chain(fbus, tbus)}) for f, t in zip(fbus, tbus): ds.union(f, t) for a in ds: v[ds.find(a)].add(a) disjoint_sets = [e for e in v.values() if len(e) > 1] i = npArange[-1] # check if PV buses need to be fused # if yes: the sets with PV buses must be found (which is slow) # if no: the check can be omitted if any(i in fbus or i in tbus for i in pv_ref): for dj in disjoint_sets: pv_buses_in_set = pv_ref & dj nr_pv_bus = len(pv_buses_in_set) if nr_pv_bus == 0: i += 1 map_to = i bus = dj.pop() PandaBusses.append(bus) bus_lookup[bus] = map_to elif nr_pv_bus == 1: map_to = bus_lookup[pv_buses_in_set.pop()] else: raise UserWarning("Can't fuse two PV buses") for bus in dj: bus_lookup[bus] = map_to else: for dj in disjoint_sets: # new bus to map to i += 1 map_to = i # get bus ID and append to Panda bus list bus = dj.pop() PandaBusses.append(bus) bus_lookup[bus] = map_to for bus in dj: bus_lookup[bus] = map_to # init ppc with zeros ppc["bus"] = np.zeros(shape=(i + 1, 13), dtype=float) # fill ppc with init values ppc["bus"][:] = np.array([0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1.1, 0.9]) ppc["bus"][:, BUS_I] = np.arange(i + 1) # change the voltages of the buses to the values in net ppc["bus"][:, BASE_KV] = net["bus"].vn_kv.ix[PandaBusses] if init_results is True and len(net["res_bus"]) > 0: int_index = get_indices(net["bus"].index.values, bus_lookup) ppc["bus"][int_index, 7] = net["res_bus"]["vm_pu"].values ppc["bus"][int_index, 8] = net["res_bus"].va_degree.values if set_opf_constraints: if "max_vm_pu" in net.bus: ppc["bus"][:, VMAX] = net["bus"].max_vm_pu.loc[PandaBusses] else: ppc["bus"][:, VMAX] = 10 if "min_vm_pu" in net.bus: ppc["bus"][:, VMIN] = net["bus"].min_vm_pu.loc[PandaBusses] else: ppc["bus"][:, VMIN] = 0 return bus_lookup
def _build_gen_ppc(net, ppc, is_elems, bus_lookup, enforce_q_lims, calculate_voltage_angles): ''' Takes the empty ppc network and fills it with the gen values. The gen datatype will be float afterwards. **INPUT**: **net** -The Pandapower format network **ppc** - The PYPOWER format network to fill in values ''' # get in service elements eg_is = is_elems['eg'] gen_is = is_elems['gen'] eg_end = len(eg_is) gen_end = eg_end + len(gen_is) xw_end = gen_end + len(net["xward"]) q_lim_default = 1e9 # which is 1000 TW - should be enough for distribution grids. # initialize generator matrix ppc["gen"] = np.zeros(shape=(xw_end, 21), dtype=float) ppc["gen"][:] = np.array([0, 0, 0, q_lim_default, -q_lim_default, 1., 1., 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) # add ext grid / slack data ppc["gen"][:eg_end, GEN_BUS] = get_indices(eg_is["bus"].values, bus_lookup) ppc["gen"][:eg_end, VG] = eg_is["vm_pu"].values ppc["gen"][:eg_end, GEN_STATUS] = eg_is["in_service"].values # set bus values for external grid buses eg_buses = get_indices(eg_is["bus"].values, bus_lookup) if calculate_voltage_angles: ppc["bus"][eg_buses, VA] = eg_is["va_degree"].values ppc["bus"][eg_buses, BUS_TYPE] = REF # add generator / pv data if gen_end > eg_end: ppc["gen"][eg_end:gen_end, GEN_BUS] = get_indices(gen_is["bus"].values, bus_lookup) ppc["gen"][eg_end:gen_end, PG] = - gen_is["p_kw"].values * 1e-3 * gen_is["scaling"].values ppc["gen"][eg_end:gen_end, VG] = gen_is["vm_pu"].values # set bus values for generator buses gen_buses = get_indices(gen_is["bus"].values, bus_lookup) ppc["bus"][gen_buses, BUS_TYPE] = PV ppc["bus"][gen_buses, VM] = gen_is["vm_pu"].values if enforce_q_lims: ppc["gen"][eg_end:gen_end, QMIN] = -gen_is["max_q_kvar"].values * 1e-3 ppc["gen"][eg_end:gen_end, QMAX] = -gen_is["min_q_kvar"].values * 1e-3 qmax = ppc["gen"][eg_end:gen_end, [QMIN]] ncn.copyto(qmax, -q_lim_default, where=np.isnan(qmax)) ppc["gen"][eg_end:gen_end, [QMIN]] = qmax qmin = ppc["gen"][eg_end:gen_end, [QMAX]] ncn.copyto(qmin, q_lim_default, where=np.isnan(qmin)) ppc["gen"][eg_end:gen_end, [QMAX]] = qmin # add extended ward pv node data if xw_end > gen_end: xw = net["xward"] bus_is = is_elems['bus'] xw_is = np.in1d(xw.bus.values, bus_is.index) \ & xw.in_service.values.astype(bool) ppc["gen"][gen_end:xw_end, GEN_BUS] = get_indices(xw["ad_bus"].values, bus_lookup) ppc["gen"][gen_end:xw_end, VG] = xw["vm_pu"].values ppc["gen"][gen_end:xw_end, GEN_STATUS] = xw_is ppc["gen"][gen_end:xw_end, QMIN] = -q_lim_default ppc["gen"][gen_end:xw_end, QMAX] = q_lim_default xward_buses = get_indices(net["xward"]["ad_bus"].values, bus_lookup) ppc["bus"][xward_buses[xw_is], BUS_TYPE] = PV ppc["bus"][xward_buses[~xw_is], BUS_TYPE] = NONE ppc["bus"][xward_buses, VM] = net["xward"]["vm_pu"].values
def _get_shunt_results(net, ppc, bus_lookup, bus_pq, bus_is, ac=True): b, p, q = np.array([]), np.array([]), np.array([]) s = net["shunt"] if len(s) > 0: sidx = get_indices(s["bus"], bus_lookup) shunt_is = np.in1d(s.bus.values, bus_is.index) \ & s.in_service.values.astype(bool) u_shunt = ppc["bus"][sidx, VM] u_shunt = np.nan_to_num(u_shunt) p_shunt = u_shunt**2 * net["shunt"]["p_kw"].values * shunt_is net["res_shunt"]["p_kw"] = p_shunt p = np.hstack([p, p_shunt]) if ac: net["res_shunt"]["vm_pu"] = u_shunt q_shunt = u_shunt**2 * net["shunt"]["q_kvar"].values * shunt_is net["res_shunt"]["q_kvar"] = q_shunt q = np.hstack([q, q_shunt]) b = np.hstack([b, s["bus"].values]) net["res_shunt"].index = net["shunt"].index w = net["ward"] if len(w) > 0: widx = get_indices(w["bus"], bus_lookup) ward_is = np.in1d(w.bus.values, bus_is.index) \ & w.in_service.values.astype(bool) u_ward = ppc["bus"][widx, VM] u_ward = np.nan_to_num(u_ward) p_ward = u_ward**2 * net["ward"]["pz_kw"].values * ward_is net["res_ward"]["p_kw"] += p_ward p = np.hstack([p, p_ward]) if ac: net["res_ward"]["vm_pu"] = u_ward q_ward = u_ward**2 * net["ward"]["qz_kvar"].values * ward_is net["res_ward"]["q_kvar"] += q_ward q = np.hstack([q, q_ward]) b = np.hstack([b, w["bus"].values]) net["res_ward"].index = net["ward"].index xw = net["xward"] if len(xw) > 0: widx = get_indices(xw["bus"], bus_lookup) xward_is = np.in1d(xw.bus.values, bus_is.index) \ & xw.in_service.values.astype(bool) u_xward = ppc["bus"][widx, VM] u_xward = np.nan_to_num(u_xward) p_xward = u_xward**2 * net["xward"]["pz_kw"].values * xward_is net["res_xward"]["p_kw"] += p_xward p = np.hstack([p, p_xward]) if ac: net["res_xward"]["vm_pu"] = u_xward q_xward = u_xward**2 * net["xward"]["qz_kvar"].values * xward_is net["res_xward"]["q_kvar"] += q_xward q = np.hstack([q, q_xward]) b = np.hstack([b, xw["bus"].values]) net["res_xward"].index = net["xward"].index if not ac: q = np.zeros(len(p)) b_pp, vp, vq = _sum_by_group(b.astype(int), p, q) b_ppc = get_indices(b_pp, bus_lookup, fused_indices=False) bus_pq[b_ppc, 0] += vp if ac: bus_pq[b_ppc, 1] += vq
def _get_p_q_results(net, ppc, bus_lookup, bus_is, ac=True): bus_pq = np.zeros(shape=(len(net["bus"].index), 2), dtype=np.float) b, p, q = np.array([]), np.array([]), np.array([]) l = net["load"] if len(l) > 0: load_is = np.in1d(l.bus.values, bus_is.index) \ & l.in_service.values.astype(bool) scaling = l["scaling"].values pl = l["p_kw"].values * scaling * load_is net["res_load"]["p_kw"] = pl p = np.hstack([p, pl]) if ac: ql = l["q_kvar"].values * scaling * load_is net["res_load"]["q_kvar"] = ql q = np.hstack([q, ql]) b = np.hstack([b, l["bus"].values]) net["res_load"].index = net["load"].index sg = net["sgen"] if len(sg) > 0: sgen_is = np.in1d(sg.bus.values, bus_is.index) \ & sg.in_service.values.astype(bool) scaling = sg["scaling"].values psg = sg["p_kw"].values * scaling * sgen_is net["res_sgen"]["p_kw"] = psg p = np.hstack([p, psg]) if ac: qsg = sg["q_kvar"].values * scaling * sgen_is net["res_sgen"]["q_kvar"] = qsg q = np.hstack([q, qsg]) b = np.hstack([b, sg["bus"].values]) net["res_sgen"].index = net["sgen"].index w = net["ward"] if len(w) > 0: ward_is = np.in1d(w.bus.values, bus_is.index) \ & w.in_service.values.astype(bool) pw = w["ps_kw"].values * ward_is net["res_ward"]["p_kw"] = pw p = np.hstack([p, pw]) if ac: qw = w["qs_kvar"].values * ward_is q = np.hstack([q, qw]) net["res_ward"]["q_kvar"] = qw b = np.hstack([b, w["bus"].values]) xw = net["xward"] if len(xw) > 0: xward_is = np.in1d(xw.bus.values, bus_is.index) \ & xw.in_service.values.astype(bool) pxw = xw["ps_kw"].values * xward_is p = np.hstack([p, pxw]) net["res_xward"]["p_kw"] = pxw if ac: qxw = xw["qs_kvar"].values * xward_is net["res_xward"]["q_kvar"] = qxw q = np.hstack([q, qxw]) b = np.hstack([b, xw["bus"].values]) if not ac: q = np.zeros(len(p)) b_pp, vp, vq = _sum_by_group(b.astype(int), p, q) b_ppc = get_indices(b_pp, bus_lookup, fused_indices=False) bus_pq[b_ppc, 0] = vp bus_pq[b_ppc, 1] = vq return bus_pq
def _switch_branches(n, ppc, is_elems, bus_lookup): """ Updates the ppc["branch"] matrix with the changed from or to values according of the status of switches **INPUT**: **pd_net** - The Pandapower format network **ppc** - The PYPOWER format network to fill in values """ # get in service elements lines_is = is_elems['line'] bus_is = is_elems['bus'] # opened bus line switches slidx = (n["switch"]["closed"].values == 0) \ & (n["switch"]["et"].values == "l") # check if there are multiple opened switches at a line (-> set line out of service) sw_elem = n['switch'].ix[slidx].element m = np.zeros_like(sw_elem, dtype=bool) m[np.unique(sw_elem, return_index=True)[1]] = True # if non unique elements are in sw_elem (= multiple opened bus line switches) if np.count_nonzero(m) < len(sw_elem): # set branch in ppc out of service # get from and to buses of these branches ppc_from = get_indices(lines_is.ix[sw_elem[~m]].from_bus, bus_lookup) ppc_to = get_indices(lines_is.ix[sw_elem[~m]].to_bus, bus_lookup) ppc_idx = np.in1d(ppc['branch'][:, 0], ppc_from)\ & np.in1d(ppc['branch'][:, 1], ppc_to) ppc["branch"][ppc_idx, BR_STATUS] = 0 # drop from in service lines as well lines_is = lines_is.drop(sw_elem[~m]) # opened switches at in service lines slidx = slidx\ & (np.in1d(n["switch"]["element"].values, lines_is.index)) \ & (np.in1d(n["switch"]["bus"].values, bus_is.index)) nlo = np.count_nonzero(slidx) stidx = (n.switch["closed"].values == 0) & (n.switch["et"].values == "t") nto = np.count_nonzero(stidx) if (nlo + nto) > 0: n_bus = len(ppc["bus"]) if nlo: future_buses = [ppc["bus"]] line_switches = n["switch"].loc[slidx] # determine on which side the switch is located mapfunc = partial(_gather_branch_switch_info, branch_type="l", net=n) ls_info = list(map(mapfunc, line_switches["bus"].values, line_switches["element"].values)) # we now have the following matrix # 0: 1 if switch is at to_bus, 0 else # 1: bus of the switch # 2: position of the line a switch is connected to ls_info = np.array(ls_info, dtype=int) # build new buses new_ls_buses = np.zeros(shape=(nlo, 13), dtype=float) new_indices = np.arange(n_bus, n_bus + nlo) # the newly created buses new_ls_buses[:] = np.array([0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1.1, 0.9]) new_ls_buses[:, 0] = new_indices new_ls_buses[:, 9] = get_values(ppc["bus"][:, BASE_KV], ls_info[:, 1], bus_lookup) # set voltage of new buses to voltage on other branch end to_buses = ppc["branch"][ls_info[ls_info[:, 0].astype(bool), 2], 1].real.astype(int) from_buses = ppc["branch"][ls_info[np.logical_not(ls_info[:, 0]), 2], 0].real\ .astype(int) if len(to_buses): ix = ls_info[:, 0] == 1 new_ls_buses[ix, 7] = ppc["bus"][to_buses, 7] new_ls_buses[ix, 8] = ppc["bus"][to_buses, 8] if len(from_buses): ix = ls_info[:, 0] == 0 new_ls_buses[ix, 7] = ppc["bus"][from_buses, 7] new_ls_buses[ix, 8] = ppc["bus"][from_buses, 8] future_buses.append(new_ls_buses) # re-route the end of lines to a new bus ppc["branch"][ls_info[ls_info[:, 0].astype(bool), 2], 1] = \ new_indices[ls_info[:, 0].astype(bool)] ppc["branch"][ls_info[np.logical_not(ls_info[:, 0]), 2], 0] = \ new_indices[np.logical_not(ls_info[:, 0])] ppc["bus"] = np.vstack(future_buses) if nto: future_buses = [ppc["bus"]] trafo_switches = n["switch"].loc[stidx] # determine on which side the switch is located mapfunc = partial(_gather_branch_switch_info, branch_type="t", net=n) ts_info = list(map(mapfunc, trafo_switches["bus"].values, trafo_switches["element"].values)) # we now have the following matrix # 0: 1 if switch is at lv_bus, 0 else # 1: bus of the switch # 2: position of the trafo a switch is connected to ts_info = np.array(ts_info, dtype=int) # build new buses new_ts_buses = np.zeros(shape=(nto, 13), dtype=float) new_indices = np.arange(n_bus + nlo, n_bus + nlo + nto) new_ts_buses[:] = np.array([0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1.1, 0.9]) new_ts_buses[:, 0] = new_indices new_ts_buses[:, 9] = get_values(ppc["bus"][:, BASE_KV], ts_info[:, 1], bus_lookup) # set voltage of new buses to voltage on other branch end to_buses = ppc["branch"][ts_info[ts_info[:, 0].astype(bool), 2], 1].real.astype(int) from_buses = ppc["branch"][ts_info[np.logical_not(ts_info[:, 0]), 2], 0].real\ .astype(int) # set newly created buses to voltage on other side of if len(to_buses): ix = ts_info[:, 0] == 1 taps = ppc["branch"][ts_info[ts_info[:, 0].astype(bool), 2], 8].real shift = ppc["branch"][ts_info[ts_info[:, 0].astype(bool), 2], 9].real new_ts_buses[ix, 7] = ppc["bus"][to_buses, 7] * taps new_ts_buses[ix, 8] = ppc["bus"][to_buses, 8] + shift if len(from_buses): ix = ts_info[:, 0] == 0 taps = ppc["branch"][ts_info[np.logical_not(ts_info[:, 0]), 2], 8].real shift = ppc["branch"][ts_info[np.logical_not(ts_info[:, 0]), 2], 9].real new_ts_buses[ix, 7] = ppc["bus"][from_buses, 7] * taps new_ts_buses[ix, 8] = ppc["bus"][from_buses, 8] + shift future_buses.append(new_ts_buses) # re-route the hv/lv-side of the trafo to a new bus # (trafo entries follow line entries) at_lv_bus = ts_info[:, 0].astype(bool) at_hv_bus = ~at_lv_bus ppc["branch"][len(n.line) + ts_info[at_lv_bus, 2], 1] = \ new_indices[at_lv_bus] ppc["branch"][len(n.line) + ts_info[at_hv_bus, 2], 0] = \ new_indices[at_hv_bus] ppc["bus"] = np.vstack(future_buses)
def _get_gen_results(net, ppc, is_elems, bus_lookup, pq_bus, return_voltage_angles, ac=True): # get in service elements gen_is = is_elems['gen'] eg_is = is_elems['eg'] eg_end = len(net['ext_grid']) gen_end = eg_end + len(net['gen']) # get results for external grids # bus index of in service egs gidx = eg_is.bus.values n_res_eg = len(net['ext_grid']) # indices of in service gens in the ppc gidx_ppc = np.searchsorted(ppc['gen'][:, GEN_BUS], get_indices(eg_is["bus"], bus_lookup)) # mask for indices of in service gens in net['res_gen'] idx_eg = np.in1d(net['ext_grid'].bus, gidx) # read results from ppc for these buses p = np.zeros(n_res_eg) p[idx_eg] = -ppc["gen"][gidx_ppc, PG] * 1e3 # store result in net['res'] net["res_ext_grid"]["p_kw"] = p # if ac PF q results are also available if ac: q = np.zeros(n_res_eg) q[idx_eg] = -ppc["gen"][gidx_ppc, QG] * 1e3 net["res_ext_grid"]["q_kvar"] = q # get bus values for pq_bus b = net['ext_grid'].bus.values # copy index for results net["res_ext_grid"].index = net['ext_grid'].index # get results for gens if gen_end > eg_end: # bus index of in service gens gidx = gen_is.bus.values n_res_gen = len(net['gen']) b = np.hstack([b, net['gen'].bus.values]) # indices of in service gens in the ppc gidx_ppc = np.searchsorted(ppc['gen'][:, GEN_BUS], get_indices(gen_is["bus"], bus_lookup)) # mask for indices of in service gens in net['res_gen'] idx_gen = np.in1d(net['gen'].bus, gidx) # read results from ppc for these buses p_gen = np.zeros(n_res_gen) p_gen[idx_gen] = -ppc["gen"][gidx_ppc, PG] * 1e3 if ac: q_gen = np.zeros(n_res_gen) q_gen[idx_gen] = -ppc["gen"][gidx_ppc, QG] * 1e3 v_pu = np.zeros(n_res_gen) v_pu[idx_gen] = ppc["bus"][gidx_ppc][:, VM] net["res_gen"]["vm_pu"] = v_pu if return_voltage_angles: v_a = np.zeros(n_res_gen) v_a[idx_gen] = ppc["bus"][gidx_ppc][:, VA] net["res_gen"]["va_degree"] = v_a net["res_gen"].index = net['gen'].index # store result in net['res'] p = np.hstack([p, p_gen]) net["res_gen"]["p_kw"] = p_gen if ac: q = np.hstack([q, q_gen]) net["res_gen"]["q_kvar"] = q_gen if not ac: q = np.zeros(len(p)) b_sum, p_sum, q_sum = _sum_by_group(b, p, q) b = get_indices(b_sum, bus_lookup, fused_indices=False) pq_bus[b, 0] += p_sum pq_bus[b, 1] += q_sum