def test_to_ppc_and_mpc(): # pypower cases to validate functions = [ 'case4gs', 'case6ww', 'case14', 'case30', 'case24_ieee_rts', 'case39' ] for fn in functions: # get pypower results pypower_module = __import__('pypower.' + fn) pypower_submodule = getattr(pypower_module, fn) pypower_function = getattr(pypower_submodule, fn) ppc_net = pypower_function() # get net from pandapower res_pypower, status_pypower = runpf(ppc_net, ppopt=ppoption(VERBOSE=0, OUT_ALL=0)) pandapower_module = __import__('pandapower', fromlist=['networks']) pandapower_function = getattr(pandapower_module.networks, fn) net = pandapower_function() reset_results(net) # convert to ppc ppc = cv.to_ppc(net) # convert to mpc mpc = cv.to_mpc(net) # runpf from converted ppc res_converted_pp, status_converted_pp = runpf( ppc, ppopt=ppoption(VERBOSE=0, OUT_ALL=0)) if status_converted_pp and status_pypower: # get lookup pp2ppc bus_lookup = net["_pd2ppc_lookups"]["bus"] # check for equality in bus voltages pp_buses = bus_lookup[res_converted_pp['bus'][:, BUS_I].astype(int)] assert np.allclose(res_converted_pp['bus'][pp_buses, VM:VA + 1], res_pypower['bus'][:, VM:VA + 1]) # ToDo: check equality of branch and gen values # pp_gen = bus_lookup[res_converted_pp['bus'][:, BUS_I].astype(int)] # assert np.allclose(res_pypower['gen'][res_pypower['order']['gen']['e2i'], PG:QG+1] # , res_converted_pp['gen'][:, PG:QG+1]) else: raise LoadflowNotConverged("Loadflow did not converge!")
def rundcpf(casedata=None, ppopt=None, fname='', solvedcase=''): """Runs a DC power flow. @see: L{runpf} @author: Ray Zimmerman (PSERC Cornell) """ ## default arguments if casedata is None: casedata = join(dirname(__file__), 'case9') ppopt = ppoption(ppopt, PF_DC=True) return runpf(casedata, ppopt, fname, solvedcase)
def calc_power_flow(case): # we run an opf, but real power output is fixed everywhere except a single quasi-slack bus, # so it just adjusts the voltage setpoints to minimize losses (and hopefully get more # reasonable solutions than a raw power flow) #results = runopf(case) #results = runpf(case, ppopt=ppoption(ENFORCE_Q_LIMS=1))[0] results = runpf(case, ppopt=ppoption(OUT_ALL=0))[0] slack_bus = case["slack_bus"] slack_gens = case["slack_gens"] # add in the extra slack generation introduced by the model, so the results # show the operating state accurately (even if it differs from the proposed state) results["net_injection"][slack_bus] += ( np.sum(results["gen"][slack_gens, PG] - case["gen"][slack_gens, PG]) ) return results
def solve_pf(case, hour=None, results=None, ppopt=None, set_generator_status=False, fname='./runpf.log'): ppopt = ppoption(ppopt) fname = os.path.abspath(fname) baseMVA = case.baseMVA bus = case.bus.copy(deep=True) branch = case.branch.copy(deep=True) gen = case.gen.copy(deep=True) gencost = case.gen.copy(deep=True) if hour is not None: logger.debug("Setting bus load based on case.load") bus['PD'] = case.load.loc[hour] bus = bus.fillna(0) if hour is not None and results is not None: logger.debug("Setting GEN_STATUS and PG") if set_generator_status is True: gen['GEN_STATUS'] = results.unit_commitment.loc[hour].astype(int) gen['PG'] = results.power_generated.loc[hour] value = [i + 1 for i in range(0, len(bus.index))] bus_name = bus.index bus.index = value bus.index = bus.index.astype(int) branch['F_BUS'] = branch['F_BUS'].apply(lambda x: value[bus_name.get_loc(x)]).astype(int) branch['T_BUS'] = branch['T_BUS'].apply(lambda x: value[bus_name.get_loc(x)]).astype(int) gen['GEN_BUS'] = gen['GEN_BUS'].apply(lambda x: value[bus_name.get_loc(x)]).astype(int) bus = np.array(bus.reset_index()) branch = np.array(branch) gen = np.array(gen) gencost = np.array(gencost) casedata = {'baseMVA': baseMVA, 'gencost': gencost, 'gen': gen, 'branch': branch, 'bus': bus} return runpf(casedata, ppopt=ppopt, fname=fname)
def callrunpf(ppc_sol, verbose=0): if ppc_sol['branch'].shape[ 1] > 13: # check if more columns than expected are included branchcopy = ppc_sol['branch'] # create copy of the original branch branch = ppc_sol[ 'branch'][:, 0:13] # copy solution branch data except Pf Qf Pt Qt ppc_sol['branch'] = branch # replace the original branch opt = ppoption(VERBOSE=verbose, OUT_ALL=verbose) # set verbose value (0 by default) r = runpf(ppc_sol, opt) # execute power flow if 'branchcopy' in locals(): # check if branch copy was needed ppc_sol['branch'] = branchcopy # return the original branch # returns the power flow solution struct return r[0]
def calc_power_flow(self): """ Calculate power flow """ dialog = gui_pf_settings.Settings_ui(self) result = dialog.getSettings() if result: # Run power flow self.refresh_data() results, success = runpf(gui_globals.ppc, gui_globals.ppopt) bus = results["bus"] printpf(results, sys.stdout) if success: self.show_status_message("Power flow successfully converged...") else: self.show_status_message("Power flow did not converge...") gui_globals.ppc["bus"][:, VM] = np.round(bus[:, VM], 4) gui_globals.ppc["bus"][:, VA] = np.round(bus[:, VA], 4) self.refresh_data()
def monte_carlo_simulation(self, power_networks, ns=100, beta=0.05): # mpc = ext2int(power_networks) mpc = power_networks nb = shape(mpc['bus'])[0] ## number of buses result = zeros((ns, nb)) base_load_P = mpc["bus"][:, PD] base_load_Q = mpc["bus"][:, QD] for i in range(ns): load_variation = random.randn(nb) mpc["bus"][:, PD] = base_load_P * (1 + load_variation * 0.5) mpc["bus"][:, QD] = base_load_Q * (1 + load_variation * 0.5) power_result_temp = runpf.runpf(mpc, ppopt=self.opt) result[i, :] = power_result_temp[0]["bus"][:, VM] pyplot.hist(result) pyplot.show() return result
def validate_from_ppc( ppc_net, pp_net, max_diff_values={ "vm_pu": 1e-6, "va_degree": 1e-5, "p_branch_kw": 1e-3, "q_branch_kvar": 1e-3, "p_gen_kw": 1e-3, "q_gen_kvar": 1e-3 }): """ This function validates the pypower case files to pandapower net structure conversion via a \ comparison of loadflow calculations. INPUT: **ppc_net** - The pypower case file. **pp_net** - The pandapower network. OPTIONAL: **max_diff_values** - Dict of maximal allowed difference values. The keys must be 'vm_pu', 'va_degree', 'p_branch_kw', 'q_branch_kvar', 'p_gen_kw' and 'q_gen_kvar' and the values floats. OUTPUT: **conversion_success** - conversion_success is returned as False if pypower or pandapower cannot calculate a power flow or if the maximum difference values (max_diff_values ) cannot be hold. EXAMPLE: import pandapower.converter as pc from pypower import case4gs ppc_net = case4gs.case4gs() pp_net = cv.from_ppc(ppc_net, f_hz=60) cv.validate_from_ppc(ppc_net, pp_net) """ # --- run a pypower power flow without print output ppopt = ppoption.ppoption(VERBOSE=0, OUT_ALL=0) ppc_res = runpf.runpf(ppc_net, ppopt)[0] # --- store pypower power flow results ppc_res_branch = ppc_res['branch'][:, 13:17] ppc_res_bus = ppc_res['bus'][:, 7:9] ppc_res_gen = ppc_res['gen'][:, 1:3] # --- try to run a pandapower power flow try: pp.runpp(pp_net, init="dc", calculate_voltage_angles=True, trafo_model="pi") except: try: pp.runpp(pp_net, calculate_voltage_angles=True, init="flat", trafo_model="pi") except: try: pp.runpp(pp_net, trafo_model="pi") except: if (ppc_res['success'] == 1) & (~pp_net.converged): logger.error( 'The validation of ppc conversion fails because the pandapower net' ' power flow does not converge.') elif (ppc_res['success'] != 1) & (pp_net.converged): logger.error( 'The validation of ppc conversion fails because the power flow of ' 'the pypower case does not converge.') elif (ppc_res['success'] != 1) & (~pp_net.converged): logger.error( 'The power flow of both, the pypower case and the pandapower net, ' 'do not converge.') return False # --- prepare power flow result comparison by reordering pp results as they are in ppc results if (ppc_res['success'] == 1) & (pp_net.converged): # --- pandapower bus result table pp_res_bus = array(pp_net.res_bus[['vm_pu', 'va_degree']]) # --- pandapower gen result table pp_res_gen = zeros([1, 2]) # consideration of parallel generators via storing how much generators have been considered # each node already_used_gen = Series(zeros([pp_net.bus.shape[0]]), index=pp_net.bus.index).astype(int) GENS = DataFrame(ppc_res['gen'][:, [0]].astype(int)) change_q_compare = [] for i, j in GENS.iterrows(): current_bus_idx = pp.get_element_index(pp_net, 'bus', name=j[0]) current_bus_type = int(ppc_res['bus'][current_bus_idx, 1]) # ext_grid if current_bus_type == 3: if already_used_gen.at[current_bus_idx] == 0: pp_res_gen = append( pp_res_gen, array(pp_net.res_ext_grid[ pp_net.ext_grid.bus == current_bus_idx][[ 'p_kw', 'q_kvar' ]])[already_used_gen.at[current_bus_idx]].reshape( (1, 2)), 0) already_used_gen.at[current_bus_idx] += 1 else: pp_res_gen = append( pp_res_gen, array(pp_net.res_sgen[ pp_net.sgen.bus == current_bus_idx][[ 'p_kw', 'q_kvar' ]])[already_used_gen.at[current_bus_idx] - 1].reshape((1, 2)), 0) already_used_gen.at[current_bus_idx] += 1 change_q_compare += [j[0]] # gen elif current_bus_type == 2: pp_res_gen = append( pp_res_gen, array(pp_net.res_gen[pp_net.gen.bus == current_bus_idx][[ 'p_kw', 'q_kvar' ]])[already_used_gen.at[current_bus_idx]].reshape((1, 2)), 0) if already_used_gen.at[current_bus_idx] > 0: change_q_compare += [j[0]] already_used_gen.at[current_bus_idx] += 1 # sgen elif current_bus_type == 1: pp_res_gen = append( pp_res_gen, array(pp_net.res_sgen[pp_net.sgen.bus == current_bus_idx][[ 'p_kw', 'q_kvar' ]])[already_used_gen.at[current_bus_idx]].reshape((1, 2)), 0) already_used_gen.at[current_bus_idx] += 1 pp_res_gen = pp_res_gen[1:, :] # delete initial zero row # --- pandapower branch result table pp_res_branch = zeros([1, 4]) # consideration of parallel branches via storing how much branches have been considered # each node-to-node-connection init1 = concat([pp_net.line.from_bus, pp_net.line.to_bus], axis=1).drop_duplicates() init2 = concat([pp_net.trafo.hv_bus, pp_net.trafo.lv_bus], axis=1).drop_duplicates() init1['hv_bus'] = nan init1['lv_bus'] = nan init2['from_bus'] = nan init2['to_bus'] = nan already_used_branches = concat([init1, init2], axis=0) already_used_branches['number'] = zeros( [already_used_branches.shape[0], 1]).astype(int) BRANCHES = DataFrame(ppc_res['branch'][:, [0, 1, 8, 9]]) for i in BRANCHES.index: from_bus = pp.get_element_index(pp_net, 'bus', name=int(ppc_res['branch'][i, 0])) to_bus = pp.get_element_index(pp_net, 'bus', name=int(ppc_res['branch'][i, 1])) from_vn_kv = ppc_res['bus'][from_bus, 9] to_vn_kv = ppc_res['bus'][to_bus, 9] ratio = BRANCHES[2].at[i] angle = BRANCHES[3].at[i] # from line results if (from_vn_kv == to_vn_kv) & ((ratio == 0) | (ratio == 1)) & (angle == 0): pp_res_branch = append( pp_res_branch, array( pp_net.res_line[(pp_net.line.from_bus == from_bus) & (pp_net.line.to_bus == to_bus)] [['p_from_kw', 'q_from_kvar', 'p_to_kw', 'q_to_kvar']])[int(already_used_branches.number.loc[ (already_used_branches.from_bus == from_bus) & (already_used_branches.to_bus == to_bus)]. values)].reshape(1, 4), 0) already_used_branches.number.loc[ (already_used_branches.from_bus == from_bus) & (already_used_branches.to_bus == to_bus)] += 1 # from trafo results else: if from_vn_kv >= to_vn_kv: pp_res_branch = append( pp_res_branch, array(pp_net.res_trafo[ (pp_net.trafo.hv_bus == from_bus) & (pp_net.trafo.lv_bus == to_bus)][[ 'p_hv_kw', 'q_hv_kvar', 'p_lv_kw', 'q_lv_kvar' ]])[int(already_used_branches.number.loc[ (already_used_branches.hv_bus == from_bus) & (already_used_branches.lv_bus == to_bus)]. values)].reshape(1, 4), 0) already_used_branches.number.loc[ (already_used_branches.hv_bus == from_bus) & (already_used_branches.lv_bus == to_bus)] += 1 else: # switch hv-lv-connection of pypower connection buses pp_res_branch = append( pp_res_branch, array(pp_net.res_trafo[ (pp_net.trafo.hv_bus == to_bus) & (pp_net.trafo.lv_bus == from_bus)][[ 'p_lv_kw', 'q_lv_kvar', 'p_hv_kw', 'q_hv_kvar' ]])[int(already_used_branches.number.loc[ (already_used_branches.hv_bus == to_bus) & (already_used_branches.lv_bus == from_bus)]. values)].reshape(1, 4), 0) already_used_branches.number.loc[ (already_used_branches.hv_bus == to_bus) & (already_used_branches.lv_bus == from_bus)] += 1 pp_res_branch = pp_res_branch[1:, :] # delete initial zero row # --- do the power flow result comparison diff_res_bus = ppc_res_bus - pp_res_bus diff_res_branch = ppc_res_branch - pp_res_branch * 1e-3 diff_res_gen = ppc_res_gen + pp_res_gen * 1e-3 # comparison of buses with several generator units only as q sum GEN_uniq = GENS.drop_duplicates() for i in GEN_uniq.loc[GEN_uniq[0].isin(change_q_compare)].index: next_is = GEN_uniq.index[GEN_uniq.index > i] if len(next_is) > 0: next_i = next_is[0] else: next_i = GENS.index[-1] + 1 if (next_i - i) > 1: diff_res_gen[i:next_i, 1] = sum(diff_res_gen[i:next_i, 1]) # logger info logger.debug( "Maximum voltage magnitude difference between pypower and pandapower: " "%.2e pu" % max(abs(diff_res_bus[:, 0]))) logger.debug( "Maximum voltage angle difference between pypower and pandapower: " "%.2e degree" % max(abs(diff_res_bus[:, 1]))) logger.debug( "Maximum branch flow active power difference between pypower and pandapower: " "%.2e kW" % max(abs(diff_res_branch[:, [0, 2]] * 1e3))) logger.debug( "Maximum branch flow reactive power difference between pypower and " "pandapower: %.2e kVAr" % max(abs(diff_res_branch[:, [1, 3]] * 1e3))) logger.debug( "Maximum active power generation difference between pypower and pandapower: " "%.2e kW" % max(abs(diff_res_gen[:, 0] * 1e3))) logger.debug( "Maximum reactive power generation difference between pypower and pandapower: " "%.2e kVAr" % max(abs(diff_res_gen[:, 1] * 1e3))) if (max(abs(diff_res_bus[:, 0])) < 1e-3) & (max(abs(diff_res_bus[:, 1])) < 1e-3) & \ (max(abs(diff_res_branch[:, [0, 2]])) < 1e-3) & \ (max(abs(diff_res_branch[:, [1, 3]])) < 1e-3) & \ (max(abs(diff_res_gen)) > 1e-1).any(): logger.debug( "The active/reactive power generation difference possibly results " "because of a pypower error. Please validate " "the results via pypower loadflow." ) # this occurs e.g. at ppc case9 # give a return if type(max_diff_values) == dict: if Series([ 'q_gen_kvar', 'p_branch_kw', 'q_branch_kvar', 'p_gen_kw', 'va_degree', 'vm_pu' ]).isin(Series(list(max_diff_values.keys()))).all(): if (max(abs(diff_res_bus[:, 0])) < max_diff_values['vm_pu']) & \ (max(abs(diff_res_bus[:, 1])) < max_diff_values['va_degree']) & \ (max(abs(diff_res_branch[:, [0, 2]])) < max_diff_values['p_branch_kw'] / 1e3) & \ (max(abs(diff_res_branch[:, [1, 3]])) < max_diff_values['q_branch_kvar'] / 1e3) & \ (max(abs(diff_res_gen[:, 0])) < max_diff_values['p_gen_kw'] / 1e3) & \ (max(abs(diff_res_gen[:, 1])) < max_diff_values['q_gen_kvar'] / 1e3): return True else: return False else: logger.debug('Not all requried dict keys are provided.') else: logger.debug("'max_diff_values' must be a dict.")
def run_sim(ppc, elements, dynopt=None, events=None, recorder=None): """ Run a time-domain simulation Inputs: ppc PYPOWER load flow case elements Dictionary of dynamic model objects (machines, controllers, etc) with Object ID as key events Events object recorder Recorder object (empty) Outputs: recorder Recorder object (with data) """ ######### # SETUP # ######### # Get version information ver = pydyn_ver() print('PYPOWER-Dynamics ' + ver['Version'] + ', ' + ver['Date']) # Program options if dynopt: h = dynopt['h'] t_sim = dynopt['t_sim'] max_err = dynopt['max_err'] max_iter = dynopt['max_iter'] verbose = dynopt['verbose'] else: # Default program options h = 0.01 # step length (s) t_sim = 5 # simulation time (s) max_err = 0.0001 # Maximum error in network iteration (voltage mismatches) max_iter = 25 # Maximum number of network iterations verbose = False # Make lists of current injection sources (generators, external grids, etc) and controllers sources = [] controllers = [] for element in elements.values(): if element.__module__ in [ 'pydyn.sym_order6a', 'pydyn.sym_order6b', 'pydyn.sym_order4', 'pydyn.ext_grid', 'pydyn.vsc_average', 'pydyn.asym_1cage', 'pydyn.asym_2cage' ]: sources.append(element) if element.__module__ == 'pydyn.controller': controllers.append(element) # Set up interfaces interfaces = init_interfaces(elements) ################## # INITIALISATION # ################## print('Initialising models...') # Run power flow and update bus voltages and angles in PYPOWER case object results, success = runpf(ppc) ppc["bus"][:, VM] = results["bus"][:, VM] ppc["bus"][:, VA] = results["bus"][:, VA] # Build Ybus matrix ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int[ "branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Build modified Ybus matrix Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Calculate initial voltage phasors v0 = bus[:, VM] * (np.cos(np.radians(bus[:, VA])) + 1j * np.sin(np.radians(bus[:, VA]))) # Initialise sources from load flow for source in sources: if source.__module__ in ['pydyn.asym_1cage', 'pydyn.asym_2cage']: # Asynchronous machine source_bus = int(ppc_int['bus'][source.bus_no, 0]) v_source = v0[source_bus] source.initialise(v_source, 0) else: # Generator or VSC source_bus = int(ppc_int['gen'][source.gen_no, 0]) S_source = np.complex(results["gen"][source.gen_no, 1] / baseMVA, results["gen"][source.gen_no, 2] / baseMVA) v_source = v0[source_bus] source.initialise(v_source, S_source) # Interface controllers and machines (for initialisation) for intf in interfaces: int_type = intf[0] var_name = intf[1] if int_type == 'OUTPUT': # If an output, interface in the reverse direction for initialisation intf[2].signals[var_name] = intf[3].signals[var_name] else: # Inputs are interfaced in normal direction during initialisation intf[3].signals[var_name] = intf[2].signals[var_name] # Initialise controllers for controller in controllers: controller.initialise() ############# # MAIN LOOP # ############# if events == None: print('Warning: no events!') # Factorise Ybus matrix Ybus_inv = splu(Ybus) y1 = [] v_prev = v0 print('Simulating...') for t in range(int(t_sim / h) + 1): if np.mod(t, 1 / h) == 0: print('t=' + str(t * h) + 's') # Interface controllers and machines for intf in interfaces: var_name = intf[1] intf[3].signals[var_name] = intf[2].signals[var_name] # Solve differential equations for j in range(4): # Solve step of differential equations for element in elements.values(): element.solve_step(h, j) # Interface with network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) if recorder != None: # Record signals or states recorder.record_variables(t * h, elements) if events != None: # Check event stack ppc, refactorise = events.handle_events(np.round(t * h, 5), elements, ppc, baseMVA) if refactorise == True: # Rebuild Ybus from new ppc_int prev_ybus = Ybus.todense().copy() ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int[ "bus"], ppc_int["branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Rebuild modified Ybus Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) ybus_delta = np.array(Ybus - prev_ybus) # Refactorise Ybus Ybus_inv = splu(Ybus) # Solve network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) return recorder
def rundyn(casefile_pf, casefile_dyn, casefile_ev, pdopt=None): """ Runs dynamic simulation. @param casefile_pf: m-file with power flow data @param casefile_dyn: m-file with dynamic data @param casefile_ev: m-file with event data @param pdopt: options vector @rtype: tuple @return: (Angles = generator angles, Speeds = generator speeds, Eq_tr = q component of transient voltage behind reactance, Ed_tr = d component of transient voltage behind reactance, Efd = Excitation voltage, PM = mechanical power, Voltages = bus voltages, Stepsize = step size integration method, Errest = estimation of integration error, Failed = failed steps, Time = time points) @see: U{http://www.esat.kuleuven.be/electa/teaching/matdyn/} """ ## Begin timing t0 = time() if pdopt is None: pdopt = Pdoption() method = pdopt[0] tol = pdopt[1] minstepsize = pdopt[2] maxstepsize = pdopt[3] output = bool(pdopt[4]) plots = bool(pdopt[5]) ## Load all data # Load dynamic simulation data if output: print '> Loading dynamic simulation data...' global freq freq, stepsize, stoptime = Loaddyn(casefile_dyn) # Load generator data Pgen0 = Loadgen(casefile_dyn, output) # Load exciter data Pexc0 = Loadexc(casefile_dyn) # Load governor data Pgov0 = Loadgov(casefile_dyn) # Load event data if len(casefile_ev) > 0: event, buschange, linechange = Loadevents(casefile_ev) else: event = array([]) genmodel = Pgen0[:, 0] excmodel = Pgen0[:, 1] govmodel = Pgen0[:, 2] ## Initialization: Power Flow # Power flow options ppopt = ppoption() ppopt["VERBOSE"] = False ppopt["OUT_ALL"] = False # Run power flow baseMVA, bus, gen, branch, success = runpf(casefile_pf, ppopt) if not success: print '> Error: Power flow did not converge. Exiting...' return else: if output: print '\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b> Power flow converged\n' U0 = bus[:, VM] * (cos(bus[:, VA] * pi / 180) + 1j * sin(bus[:, VA] * pi / 180 )) U00 = copy(U0) # Get generator info on = find(gen[:, GEN_STATUS] > 0) ## which generators are on? gbus = gen[on, GEN_BUS] ## what buses are they at? ngen = len(gbus) nbus = len(U0) ## Construct augmented Ybus if output: print '> Constructing augmented admittance matrix...' Pl = bus[:, PD] / baseMVA ## load power Ql = bus[:, QD] / baseMVA xd_tr = zeros(ngen) xd_tr[genmodel == 2] = Pgen0[genmodel == 2, 7] # 4th order model: xd_tr column 7 xd_tr[genmodel == 1] = Pgen0[genmodel == 1, 6] # classical model: xd_tr column 6 invYbus = AugYbus(baseMVA, bus, branch, xd_tr, gbus, Pl, Ql, U0) ## Calculate Initial machine state if output: print '\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b> Calculating initial state...\n' Efd0, Xgen0 = GeneratorInit(Pgen0, U0(gbus), gen, baseMVA, genmodel) omega0 = Xgen0[:, 0] Id0, Iq0, Pe0 = MachineCurrents(Xgen0, Pgen0, U0[gbus], genmodel) Vgen0 = r_[Id0, Iq0, Pe0] ## Exciter initial conditions Vexc0 = abs(U0[gbus]) Xexc0, Pexc0 = ExciterInit(Efd0, Pexc0, Vexc0, excmodel) ## Governor initial conditions Pm0 = copy(Pe0) Xgov0, Pgov0 = GovernorInit(Pm0, Pgov0, omega0, govmodel) Vgov0 = copy(omega0) ## Check Steady-state Fexc0 = Exciter(Xexc0, Pexc0, Vexc0, excmodel) Fgov0 = Governor(Xgov0, Pgov0, Vgov0, govmodel) Fgen0 = Generator(Xgen0, Xexc0, Xgov0, Pgen0, Vgen0, genmodel) # Check Generator Steady-state if sum(sum(abs(Fgen0))) > 1e-6: print '> Error: Generator not in steady-state\n> Exiting...\n' return # Check Exciter Steady-state if sum(sum(abs(Fexc0))) > 1e-6: print '> Error: Exciter not in steady-state\n> Exiting...\n' return # Check Governor Steady-state if sum(sum(abs(Fgov0))) > 1e-6: print '> Error: Governor not in steady-state\n> Exiting...\n' return if output: print '\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b> System in steady-state\n' ## Initialization of main stability loop t = -0.02 # simulate 0.02s without applying events errest = 0 failed = 0 eulerfailed = 0 if method == 3 | method == 4: stepsize = minstepsize if not output: print ' ' ev = 1 eventhappened = False i = 0 ## Allocate memory for variables if output: print '> Allocate memory...\n' chunk = 5000 Time = zeros(chunk); Time[0, :] = t Errest = zeros(chunk); Errest[0, :] = errest Stepsize = zeros(chunk); Stepsize[0, :] = stepsize # System variables Voltages = zeros(chunk, len(U0)); Voltages[0, :] = U0 # Generator Angles = zeros(chunk, ngen); Angles[0, :] = Xgen0[:, 0] * 180 / pi Speeds = zeros(chunk, ngen); Speeds[0, :] = Xgen0[:, 2] / (2 * pi * freq) Eq_tr = zeros(chunk, ngen); Eq_tr[0, :] = Xgen0[:, 2] Ed_tr = zeros(chunk, ngen); Ed_tr[0, :] = Xgen0[:, 3] # Exciter and governor Efd = zeros(chunk,ngen); Efd[0, :] = Efd0[:, 0] PM = zeros(chunk, ngen); PM[0, :] = Pm0[:, 0] ## Main stability loop while t < stoptime + stepsize: ## Output i += 1 if i % 45 == 0 & output: print '\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b> %6.2f## completed' % t / stoptime * 100 ## Numerical Method if method == 1: Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, U0, t, newstepsize = \ ModifiedEuler(t, Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, invYbus, gbus, genmodel, excmodel, govmodel, stepsize) elif method == 2: Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, U0, t, newstepsize = \ RungeKutta(t, Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, invYbus, gbus, genmodel, excmodel, govmodel, stepsize) elif method == 3: Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, U0, errest, failed, t, newstepsize = \ RungeKuttaFehlberg(t, Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, U0, invYbus, gbus, genmodel, excmodel, govmodel, tol, maxstepsize, stepsize) elif method == 4: Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, U0, errest, failed, t, newstepsize = \ RungeKuttaHighamHall(t, Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, U0, invYbus, gbus, genmodel, excmodel, govmodel, tol, maxstepsize, stepsize) elif method == 5: Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, U0, t, eulerfailed, newstepsize = \ ModifiedEuler2(t, Xgen0, Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0, Vgov0, invYbus, gbus, genmodel, excmodel, govmodel, stepsize) if eulerfailed: print '\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b> Error: No solution found. Try lowering tolerance or increasing maximum number of iterations in ModifiedEuler2. Exiting... \n' return if failed: t -= stepsize # End exactly at stop time if t + newstepsize > stoptime: newstepsize = stoptime - t elif stepsize < minstepsize: print '\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b> Error: No solution found with minimum step size. Exiting... \n' return ## Allocate new memory chunk if matrices are full if i > Time.shape[0]: Stepsize = c_[Stepsize, zeros(chunk)]; Errest = c_[Errest, zeros(chunk)] Time = c_[Time, zeros(chunk)] Voltages = c_[Voltages, zeros(chunk, len(U0))] Efd = c_[Efd, zeros(chunk, ngen)] PM = c_[PM, zeros(chunk, ngen)] Angles = c_[Angles, zeros(chunk,ngen)] Speeds = c_[Speeds, zeros(chunk,ngen)] Eq_tr = c_[Eq_tr, zeros(chunk,ngen)] Ed_tr = c_[Ed_tr, zeros(chunk,ngen)] ## Save values Stepsize[i, :] = stepsize.T Errest[i, :] = errest.T Time[i, :] = t Voltages[i, :] = U0.T # exc Efd[i, :] = Xexc0[:, 0] * (genmodel > 1) # Set Efd to zero when using classical generator model # gov PM[i, :] = Xgov0[:, 0] # gen Angles[i, :] = Xgen0[:, 0] * 180 / pi Speeds[i, :] = Xgen0[:, 1] / (2 * pi * freq) Eq_tr[i, :] = Xgen0[:, 2] Ed_tr[i, :] = Xgen0[:, 3] ## Adapt step size if event will occur in next step if len(event) > 0 & ev <= event.shape[0] & (method == 3 | method == 4): if t + newstepsize >= event[ev, 0]: if event[ev, 0] - t < newstepsize: newstepsize = event[ev, 0] - t ## Check for events if len(event) > 0 & ev <= event.shape[0]: for k in range(ev, event.shape[0]): # cycle through all events .. if abs(t - event[ev, 0]) > 10 * EPS | ev > event.shape[0]: #.. that happen on time t break else: eventhappened = True if event[ev, 1] == 1: bus[buschange[ev, 1], buschange[ev, 2]] = buschange[ev, 3] if event[ev, 1] == 2: branch[linechange[ev, 1], linechange[ev, 2]] = linechange[ev, 3] ev += 1 if eventhappened: # Refactorise invYbus = AugYbus(baseMVA, bus, branch, xd_tr, gbus, bus[:, PD] / baseMVA, bus[:, QD] / baseMVA, U00) U0 = SolveNetwork(Xgen0, Pgen0, invYbus, gbus, genmodel) Id0, Iq0, Pe0 = MachineCurrents(Xgen0, Pgen0, U0(gbus), genmodel) Vgen0 = Id0,Iq0,Pe0 Vexc0 = abs(U0[gbus]) # decrease stepsize after event occured if method == 3 | method == 4: newstepsize = minstepsize i += 1 # if event occurs, save values at t- and t+ ## Save values Stepsize[i, :] = stepsize.T Errest[i, :] = errest.T Time[i, :] = t Voltages[i, :] = U0.T # exc Efd[i, :] = Xexc0[:, 0] * (genmodel > 1) # Set Efd to zero when using classical generator model # gov PM[i, :] = Xgov0[:, 0] # gen Angles[i, :] = Xgen0[:, 0] * 180 / pi Speeds[i, :] = Xgen0[:, 1] / (2 * pi * freq) Eq_tr[i, :] = Xgen0[:, 2] Ed_tr[i, :] = Xgen0[:, 3] eventhappened = False ## Advance time stepsize = newstepsize t += stepsize # end of main stability loop ## Output if output: print '\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b> 100## completed' else: print '\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b' simulationtime = t0 - time() if output: print '\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b> Simulation completed in %5.2f seconds\n' % simulationtime # Save only the first i elements Angles = Angles[:i, :] Speeds = Speeds[:i, :] Eq_tr = Eq_tr[:i, :] Ed_tr = Ed_tr[:i, :] Efd = Efd[:i, :] PM = PM[:i, :] Voltages = Voltages[:i, :] Stepsize = Stepsize[:i, :] Errest = Errest[:i, :] Time = Time[:i, :] ## Plot if plots: from pylab import close, figure, xlabel, ylabel, hold, plot, axis close('all') figure xlabel('Time [s]') ylabel('Angle [deg]') hold(True) plot(Time,Angles) axis([0, Time(-1), -1, 1]) axis('auto y') figure xlabel('Time [s]') ylabel('Speed [pu]') hold(True) plot(Time,Speeds) axis([0, Time(-1), -1, 1]) axis('auto y') figure xlabel('Time [s]') ylabel('Voltage [pu]') hold(True) plot(Time, abs(Voltages)) axis([0, Time(-1), -1, 1]) axis('auto y') figure xlabel('Time [s]') ylabel('Excitation voltage [pu]') hold(True) plot(Time, Efd) axis([0, Time(-1), -1, 1]) axis('auto y') figure xlabel('Time [s]') ylabel('Turbine Power [pu]') hold(True) plot(Time, PM) axis([0, Time(-1), -1, 1]) axis('auto y') figure hold(True) xlabel('Time [s]') ylabel('Step size') plot(Time, Stepsize, '-o') axis([0, Time(-1), -1, 1]) axis('auto y') return Angles, Speeds, Eq_tr, Ed_tr, Efd, PM, Voltages, Stepsize, Errest, Time
def t_dcline(quiet=False): """Tests for DC line extension in L{{toggle_dcline}. @author: Ray Zimmerman (PSERC Cornell) """ num_tests = 50 t_begin(num_tests, quiet) tdir = dirname(__file__) casefile = join(tdir, 't_case9_dcline') if quiet: verbose = False else: verbose = False t0 = '' ppopt = ppoption(OPF_VIOLATION=1e-6, PDIPM_GRADTOL=1e-8, PDIPM_COMPTOL=1e-8, PDIPM_COSTTOL=1e-9) ppopt = ppoption(ppopt, OPF_ALG=560, OPF_ALG_DC=200) ppopt = ppoption(ppopt, OUT_ALL=0, VERBOSE=verbose) ## set up indices ib_data = r_[arange(BUS_AREA + 1), arange(BASE_KV, VMIN + 1)] ib_voltage = arange(VM, VA + 1) ib_lam = arange(LAM_P, LAM_Q + 1) ib_mu = arange(MU_VMAX, MU_VMIN + 1) ig_data = r_[[GEN_BUS, QMAX, QMIN], arange(MBASE, APF + 1)] ig_disp = array([PG, QG, VG]) ig_mu = arange(MU_PMAX, MU_QMIN + 1) ibr_data = arange(ANGMAX + 1) ibr_flow = arange(PF, QT + 1) ibr_mu = array([MU_SF, MU_ST]) ibr_angmu = array([MU_ANGMIN, MU_ANGMAX]) ## load case ppc0 = loadcase(casefile) del ppc0['dclinecost'] ppc = ppc0 ppc = toggle_dcline(ppc, 'on') ppc = toggle_dcline(ppc, 'off') ndc = ppc['dcline'].shape[0] ## run AC OPF w/o DC lines t = ''.join([t0, 'AC OPF (no DC lines) : ']) r0 = runopf(ppc0, ppopt) success = r0['success'] t_ok(success, [t, 'success']) r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) t_is(r['f'], r0['f'], 8, [t, 'f']) t_is(r['bus'][:, ib_data], r0['bus'][:, ib_data], 10, [t, 'bus data']) t_is(r['bus'][:, ib_voltage], r0['bus'][:, ib_voltage], 3, [t, 'bus voltage']) t_is(r['bus'][:, ib_lam], r0['bus'][:, ib_lam], 3, [t, 'bus lambda']) t_is(r['bus'][:, ib_mu], r0['bus'][:, ib_mu], 2, [t, 'bus mu']) t_is(r['gen'][:, ig_data], r0['gen'][:, ig_data], 10, [t, 'gen data']) t_is(r['gen'][:, ig_disp], r0['gen'][:, ig_disp], 3, [t, 'gen dispatch']) t_is(r['gen'][:, ig_mu], r0['gen'][:, ig_mu], 3, [t, 'gen mu']) t_is(r['branch'][:, ibr_data], r0['branch'][:, ibr_data], 10, [t, 'branch data']) t_is(r['branch'][:, ibr_flow], r0['branch'][:, ibr_flow], 3, [t, 'branch flow']) t_is(r['branch'][:, ibr_mu], r0['branch'][:, ibr_mu], 2, [t, 'branch mu']) t = ''.join([t0, 'AC PF (no DC lines) : ']) ppc1 = { 'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy() } ppc1['bus'][:, VM] = 1 ppc1['bus'][:, VA] = 0 rp = runpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is(rp['bus'][:, ib_voltage], r['bus'][:, ib_voltage], 3, [t, 'bus voltage']) t_is(rp['gen'][:, ig_disp], r['gen'][:, ig_disp], 3, [t, 'gen dispatch']) t_is(rp['branch'][:, ibr_flow], r['branch'][:, ibr_flow], 3, [t, 'branch flow']) ## run with DC lines t = ''.join([t0, 'AC OPF (with DC lines) : ']) ppc = toggle_dcline(ppc, 'on') r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) expected = array([[10, 8.9, -10, 10, 1.0674, 1.0935], [2.2776, 2.2776, 0, 0, 1.0818, 1.0665], [0, 0, 0, 0, 1.0000, 1.0000], [10, 9.5, 0.0563, -10, 1.0778, 1.0665]]) t_is(r['dcline'][:, c.PF:c.VT + 1], expected, 4, [t, 'P Q V']) expected = array([[0, 0.8490, 0.6165, 0, 0, 0.2938], [0, 0, 0, 0.4290, 0.0739, 0], [0, 0, 0, 0, 0, 0], [0, 7.2209, 0, 0, 0.0739, 0]]) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected, 3, [t, 'mu']) t = ''.join([t0, 'AC PF (with DC lines) : ']) ppc1 = { 'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy() } ppc1 = toggle_dcline(ppc1, 'on') ppc1['bus'][:, VM] = 1 ppc1['bus'][:, VA] = 0 rp = runpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is(rp['bus'][:, ib_voltage], r['bus'][:, ib_voltage], 3, [t, 'bus voltage']) #t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is(rp['gen'][:2, ig_disp], r['gen'][:2, ig_disp], 3, [t, 'gen dispatch']) t_is(rp['gen'][2, PG], r['gen'][2, PG], 3, [t, 'gen dispatch']) t_is(rp['gen'][2, QG] + rp['dcline'][0, c.QF], r['gen'][2, QG] + r['dcline'][0, c.QF], 3, [t, 'gen dispatch']) t_is(rp['branch'][:, ibr_flow], r['branch'][:, ibr_flow], 3, [t, 'branch flow']) ## add appropriate P and Q injections and check angles and generation when running PF t = ''.join([t0, 'AC PF (with equivalent injections) : ']) ppc1 = { 'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy() } ppc1['bus'][:, VM] = 1 ppc1['bus'][:, VA] = 0 for k in range(ndc): if ppc1['dcline'][k, c.BR_STATUS]: ff = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.F_BUS]) tt = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.T_BUS]) ppc1['bus'][ff, PD] = ppc1['bus'][ff, PD] + r['dcline'][k, c.PF] ppc1['bus'][ff, QD] = ppc1['bus'][ff, QD] - r['dcline'][k, c.QF] ppc1['bus'][tt, PD] = ppc1['bus'][tt, PD] - r['dcline'][k, c.PT] ppc1['bus'][tt, QD] = ppc1['bus'][tt, QD] - r['dcline'][k, c.QT] ppc1['bus'][ff, VM] = r['dcline'][k, c.VF] ppc1['bus'][tt, VM] = r['dcline'][k, c.VT] ppc1['bus'][ff, BUS_TYPE] = PV ppc1['bus'][tt, BUS_TYPE] = PV rp = runpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is(rp['bus'][:, ib_voltage], r['bus'][:, ib_voltage], 3, [t, 'bus voltage']) t_is(rp['gen'][:, ig_disp], r['gen'][:, ig_disp], 3, [t, 'gen dispatch']) t_is(rp['branch'][:, ibr_flow], r['branch'][:, ibr_flow], 3, [t, 'branch flow']) ## test DC OPF t = ''.join([t0, 'DC OPF (with DC lines) : ']) ppc = ppc0.copy() ppc['gen'][0, PMIN] = 10 ppc['branch'][4, RATE_A] = 100 ppc = toggle_dcline(ppc, 'on') r = rundcopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) expected = array([[10, 8.9, 0, 0, 1.01, 1], [2, 2, 0, 0, 1, 1], [0, 0, 0, 0, 1, 1], [10, 9.5, 0, 0, 1, 0.98]]) t_is(r['dcline'][:, c.PF:c.VT + 1], expected, 4, [t, 'P Q V']) expected = array([[0, 1.8602, 0, 0, 0, 0], [1.8507, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0.2681, 0, 0, 0, 0]]) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected, 3, [t, 'mu']) t = ''.join([t0, 'DC PF (with DC lines) : ']) ppc1 = { 'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy() } ppc1 = toggle_dcline(ppc1, 'on') ppc1['bus'][:, VA] = 0 rp = rundcpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is(rp['bus'][:, ib_voltage], r['bus'][:, ib_voltage], 3, [t, 'bus voltage']) t_is(rp['gen'][:, ig_disp], r['gen'][:, ig_disp], 3, [t, 'gen dispatch']) t_is(rp['branch'][:, ibr_flow], r['branch'][:, ibr_flow], 3, [t, 'branch flow']) ## add appropriate P injections and check angles and generation when running PF t = ''.join([t0, 'DC PF (with equivalent injections) : ']) ppc1 = { 'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy() } ppc1['bus'][:, VA] = 0 for k in range(ndc): if ppc1['dcline'][k, c.BR_STATUS]: ff = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.F_BUS]) tt = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.T_BUS]) ppc1['bus'][ff, PD] = ppc1['bus'][ff, PD] + r['dcline'][k, c.PF] ppc1['bus'][tt, PD] = ppc1['bus'][tt, PD] - r['dcline'][k, c.PT] ppc1['bus'][ff, BUS_TYPE] = PV ppc1['bus'][tt, BUS_TYPE] = PV rp = rundcpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is(rp['bus'][:, ib_voltage], r['bus'][:, ib_voltage], 3, [t, 'bus voltage']) t_is(rp['gen'][:, ig_disp], r['gen'][:, ig_disp], 3, [t, 'gen dispatch']) t_is(rp['branch'][:, ibr_flow], r['branch'][:, ibr_flow], 3, [t, 'branch flow']) ## run with DC lines t = ''.join([t0, 'AC OPF (with DC lines + poly cost) : ']) ppc = loadcase(casefile) ppc = toggle_dcline(ppc, 'on') r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) expected1 = array([[10, 8.9, -10, 10, 1.0663, 1.0936], [7.8429, 7.8429, 0, 0, 1.0809, 1.0667], [0, 0, 0, 0, 1.0000, 1.0000], [6.0549, 5.7522, -0.5897, -10, 1.0778, 1.0667]]) t_is(r['dcline'][:, c.PF:c.VT + 1], expected1, 4, [t, 'P Q V']) expected2 = array([[0, 0.7605, 0.6226, 0, 0, 0.2980], [0, 0, 0, 0.4275, 0.0792, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0.0792, 0]]) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected2, 3, [t, 'mu']) ppc['dclinecost'][3, :8] = array([2, 0, 0, 4, 0, 0, 7.3, 0]) r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) t_is(r['dcline'][:, c.PF:c.VT + 1], expected1, 4, [t, 'P Q V']) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected2, 3, [t, 'mu']) t = ''.join([t0, 'AC OPF (with DC lines + pwl cost) : ']) ppc['dclinecost'][3, :8] = array([1, 0, 0, 2, 0, 0, 10, 73]) r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) t_is(r['dcline'][:, c.PF:c.VT + 1], expected1, 4, [t, 'P Q V']) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected2, 3, [t, 'mu']) t_end()
def t_loadcase(quiet=False): """Test that C{loadcase} works with an object as well as case file. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ t_begin(240, quiet) ## compare result of loading from M-file file to result of using data matrices tdir = dirname(__file__) casefile = join(tdir, 't_case9_opf') matfile = join(tdir, 't_mat9_opf') pfcasefile = join(tdir, 't_case9_pf') pfmatfile = join(tdir, 't_mat9_pf') casefilev2 = join(tdir, 't_case9_opfv2') matfilev2 = join(tdir, 't_mat9_opfv2') pfcasefilev2 = join(tdir, 't_case9_pfv2') pfmatfilev2 = join(tdir, 't_mat9_pfv2') ## read version 1 OPF data matrices baseMVA, bus, gen, branch, areas, gencost = t_case9_opf() ## save as .mat file savemat(matfile + '.mat', {'baseMVA': baseMVA, 'bus': bus, 'gen': gen, 'branch': branch, 'areas': areas, 'gencost': gencost}, oned_as='row') ## read version 2 OPF data matrices ppc = t_case9_opfv2() ## save as .mat file savemat(matfilev2 + '.mat', {'ppc': ppc}, oned_as='column') ## prepare expected matrices for v1 load ## (missing gen cap curve & branch ang diff lims) tmp1 = (ppc['baseMVA'], ppc['bus'].copy(), ppc['gen'].copy(), ppc['branch'].copy(), ppc['areas'].copy(), ppc['gencost'].copy()) tmp2 = (ppc['baseMVA'], ppc['bus'].copy(), ppc['gen'].copy(), ppc['branch'].copy(), ppc['areas'].copy(), ppc['gencost'].copy()) ## remove capability curves, angle difference limits tmp1[2][1:3, [PC1, PC2, QC1MIN, QC1MAX, QC2MIN, QC2MAX]] = zeros((2,6)) tmp1[3][0, ANGMAX] = 360 tmp1[3][8, ANGMIN] = -360 baseMVA, bus, gen, branch, areas, gencost = tmp1 ##----- load OPF data into individual matrices ----- t = 'loadcase(opf_PY_file_v1) without .py extension : ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = \ loadcase(casefile, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t_is(areas1, areas, 12, [t, 'areas']) t_is(gencost1, gencost, 12, [t, 'gencost']) t = 'loadcase(opf_PY_file_v1) with .py extension : ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = \ loadcase(casefile + '.py', False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t_is(areas1, areas, 12, [t, 'areas']) t_is(gencost1, gencost, 12, [t, 'gencost']) t = 'loadcase(opf_MAT_file_v1) without .mat extension : ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = \ loadcase(matfile, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t_is(areas1, areas, 12, [t, 'areas']) t_is(gencost1, gencost, 12, [t, 'gencost']) t = 'loadcase(opf_MAT_file_v1) with .mat extension : ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = \ loadcase(matfile + '.mat', False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t_is(areas1, areas, 12, [t, 'areas']) t_is(gencost1, gencost, 12, [t, 'gencost']) ## prepare expected matrices for v2 load baseMVA, bus, gen, branch, areas, gencost = tmp2 t = 'loadcase(opf_PY_file_v2) without .py extension : ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = \ loadcase(casefilev2, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t_is(areas1, areas, 12, [t, 'areas']) t_is(gencost1, gencost, 12, [t, 'gencost']) t = 'loadcase(opf_PY_file_v2) with .py extension : ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = \ loadcase(casefilev2 + '.py', False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t_is(areas1, areas, 12, [t, 'areas']) t_is(gencost1, gencost, 12, [t, 'gencost']) t = 'loadcase(opf_MAT_file_v2) without .mat extension : ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = \ loadcase(matfilev2, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t_is(areas1, areas, 12, [t, 'areas']) t_is(gencost1, gencost, 12, [t, 'gencost']) t = 'loadcase(opf_MAT_file_v2) with .mat extension : ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = \ loadcase(matfilev2 + '.mat', False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t_is(areas1, areas, 12, [t, 'areas']) t_is(gencost1, gencost, 12, [t, 'gencost']) ## prepare expected matrices for v1 load baseMVA, bus, gen, branch, areas, gencost = tmp1 t = 'loadcase(opf_struct_v1) (no version): ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = t_case9_opf() c = {} c['baseMVA'] = baseMVA1 c['bus'] = bus1.copy() c['gen'] = gen1.copy() c['branch'] = branch1.copy() c['areas'] = areas1.copy() c['gencost'] = gencost1.copy() baseMVA2, bus2, gen2, branch2, areas2, gencost2 = loadcase(c, False) t_is(baseMVA2, baseMVA, 12, [t, 'baseMVA']) t_is(bus2, bus, 12, [t, 'bus']) t_is(gen2, gen, 12, [t, 'gen']) t_is(branch2, branch, 12, [t, 'branch']) t_is(areas2, areas, 12, [t, 'areas']) t_is(gencost2, gencost, 12, [t, 'gencost']) t = 'loadcase(opf_struct_v1) (version=\'1\'): ' c['version'] = '1' baseMVA2, bus2, gen2, branch2, areas2, gencost2 = loadcase(c, False) t_is(baseMVA2, baseMVA, 12, [t, 'baseMVA']) t_is(bus2, bus, 12, [t, 'bus']) t_is(gen2, gen, 12, [t, 'gen']) t_is(branch2, branch, 12, [t, 'branch']) t_is(areas2, areas, 12, [t, 'areas']) t_is(gencost2, gencost, 12, [t, 'gencost']) ## prepare expected matrices for v2 load baseMVA, bus, gen, branch, areas, gencost = tmp2 t = 'loadcase(opf_struct_v2) (no version): ' c = {} c['baseMVA'] = baseMVA c['bus'] = bus.copy() c['gen'] = gen.copy() c['branch'] = branch.copy() c['areas'] = areas.copy() c['gencost'] = gencost.copy() baseMVA2, bus2, gen2, branch2, areas2, gencost2 = loadcase(c, False) t_is(baseMVA2, baseMVA, 12, [t, 'baseMVA']) t_is(bus2, bus, 12, [t, 'bus']) t_is(gen2, gen, 12, [t, 'gen']) t_is(branch2, branch, 12, [t, 'branch']) t_is(areas2, areas, 12, [t, 'areas']) t_is(gencost2, gencost, 12, [t, 'gencost']) t = 'loadcase(opf_struct_v2) (version=''2''): ' c = {} c['baseMVA'] = baseMVA c['bus'] = bus.copy() c['gen'] = gen.copy() c['branch'] = branch.copy() c['areas'] = areas.copy() c['gencost'] = gencost.copy() c['version'] = '2' baseMVA2, bus2, gen2, branch2, areas2, gencost2 = loadcase(c, False) t_is(baseMVA2, baseMVA, 12, [t, 'baseMVA']) t_is(bus2, bus, 12, [t, 'bus']) t_is(gen2, gen, 12, [t, 'gen']) t_is(branch2, branch, 12, [t, 'branch']) t_is(areas2, areas, 12, [t, 'areas']) t_is(gencost2, gencost, 12, [t, 'gencost']) ##----- load OPF data into struct ----- ## prepare expected matrices for v1 load baseMVA, bus, gen, branch, areas, gencost = tmp1 t = 'ppc = loadcase(opf_PY_file_v1) without .py extension : ' ppc1 = loadcase(casefile) t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t_is(ppc1['areas'], areas, 12, [t, 'areas']) t_is(ppc1['gencost'], gencost, 12, [t, 'gencost']) t = 'ppc = loadcase(opf_PY_file_v1) with .py extension : ' ppc1 = loadcase(casefile + '.py') t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t_is(ppc1['areas'], areas, 12, [t, 'areas']) t_is(ppc1['gencost'], gencost, 12, [t, 'gencost']) t = 'ppc = loadcase(opf_MAT_file_v1) without .mat extension : ' ppc1 = loadcase(matfile) t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t_is(ppc1['areas'], areas, 12, [t, 'areas']) t_is(ppc1['gencost'], gencost, 12, [t, 'gencost']) t = 'ppc = loadcase(opf_MAT_file_v1) with .mat extension : ' ppc1 = loadcase(matfile + '.mat') t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t_is(ppc1['areas'], areas, 12, [t, 'areas']) t_is(ppc1['gencost'], gencost, 12, [t, 'gencost']) ## prepare expected matrices for v2 load baseMVA, bus, gen, branch, areas, gencost = tmp2 t = 'ppc = loadcase(opf_PY_file_v2) without .m extension : ' ppc1 = loadcase(casefilev2) t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t_is(ppc1['areas'], areas, 12, [t, 'areas']) t_is(ppc1['gencost'], gencost, 12, [t, 'gencost']) t = 'ppc = loadcase(opf_PY_file_v2) with .py extension : ' ppc1 = loadcase(casefilev2 + '.py') t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t_is(ppc1['areas'], areas, 12, [t, 'areas']) t_is(ppc1['gencost'], gencost, 12, [t, 'gencost']) t = 'ppc = loadcase(opf_MAT_file_v2) without .mat extension : ' ppc1 = loadcase(matfilev2) t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t_is(ppc1['areas'], areas, 12, [t, 'areas']) t_is(ppc1['gencost'], gencost, 12, [t, 'gencost']) t = 'ppc = loadcase(opf_MAT_file_v2) with .mat extension : ' ppc1 = loadcase(matfilev2 + '.mat') t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t_is(ppc1['areas'], areas, 12, [t, 'areas']) t_is(ppc1['gencost'], gencost, 12, [t, 'gencost']) ## prepare expected matrices for v1 load baseMVA, bus, gen, branch, areas, gencost = tmp1 t = 'ppc = loadcase(opf_struct_v1) (no version): ' baseMVA1, bus1, gen1, branch1, areas1, gencost1 = t_case9_opf() c = {} c['baseMVA'] = baseMVA1 c['bus'] = bus1.copy() c['gen'] = gen1.copy() c['branch'] = branch1.copy() c['areas'] = areas1.copy() c['gencost'] = gencost1.copy() ppc2 = loadcase(c) t_is(ppc2['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc2['bus'], bus, 12, [t, 'bus']) t_is(ppc2['gen'], gen, 12, [t, 'gen']) t_is(ppc2['branch'], branch, 12, [t, 'branch']) t_is(ppc2['areas'], areas, 12, [t, 'areas']) t_is(ppc2['gencost'], gencost, 12, [t, 'gencost']) t = 'ppc = loadcase(opf_struct_v1) (version=''1''): ' c['version'] = '1' ppc2 = loadcase(c) t_is(ppc2['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc2['bus'], bus, 12, [t, 'bus']) t_is(ppc2['gen'], gen, 12, [t, 'gen']) t_is(ppc2['branch'], branch, 12, [t, 'branch']) t_is(ppc2['areas'], areas, 12, [t, 'areas']) t_is(ppc2['gencost'], gencost, 12, [t, 'gencost']) ## prepare expected matrices for v2 load baseMVA, bus, gen, branch, areas, gencost = tmp2 t = 'ppc = loadcase(opf_struct_v2) (no version): ' c = {} c['baseMVA'] = baseMVA c['bus'] = bus.copy() c['gen'] = gen.copy() c['branch'] = branch.copy() c['areas'] = areas.copy() c['gencost'] = gencost.copy() ppc2 = loadcase(c) t_is(ppc2['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc2['bus'], bus, 12, [t, 'bus']) t_is(ppc2['gen'], gen, 12, [t, 'gen']) t_is(ppc2['branch'], branch, 12, [t, 'branch']) t_is(ppc2['areas'], areas, 12, [t, 'areas']) t_is(ppc2['gencost'], gencost, 12, [t, 'gencost']) t_ok(ppc2['version'] == '2', [t, 'version']) t = 'ppc = loadcase(opf_struct_v2) (version=''2''): ' c = {} c['baseMVA'] = baseMVA c['bus'] = bus.copy() c['gen'] = gen.copy() c['branch'] = branch.copy() c['areas'] = areas.copy() c['gencost'] = gencost.copy() c['version'] = '2' ppc2 = loadcase(c) t_is(ppc2['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc2['bus'], bus, 12, [t, 'bus']) t_is(ppc2['gen'], gen, 12, [t, 'gen']) t_is(ppc2['branch'], branch, 12, [t, 'branch']) t_is(ppc2['areas'], areas, 12, [t, 'areas']) t_is(ppc2['gencost'], gencost, 12, [t, 'gencost']) ## read version 1 PF data matrices baseMVA, bus, gen, branch = t_case9_pf() savemat(pfmatfile + '.mat', {'baseMVA': baseMVA, 'bus': bus, 'gen': gen, 'branch': branch}, oned_as='column') ## read version 2 PF data matrices ppc = t_case9_pfv2() tmp = (ppc['baseMVA'], ppc['bus'].copy(), ppc['gen'].copy(), ppc['branch'].copy()) baseMVA, bus, gen, branch = tmp ## save as .mat file savemat(pfmatfilev2 + '.mat', {'ppc': ppc}, oned_as='column') ##----- load PF data into individual matrices ----- t = 'loadcase(pf_PY_file_v1) without .py extension : ' baseMVA1, bus1, gen1, branch1 = \ loadcase(pfcasefile, False, False, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t = 'loadcase(pf_PY_file_v1) with .py extension : ' baseMVA1, bus1, gen1, branch1 = \ loadcase(pfcasefile + '.py', False, False, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t = 'loadcase(pf_MAT_file_v1) without .mat extension : ' baseMVA1, bus1, gen1, branch1 = \ loadcase(pfmatfile, False, False, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t = 'loadcase(pf_MAT_file_v1) with .mat extension : ' baseMVA1, bus1, gen1, branch1 = \ loadcase(pfmatfile + '.mat', False, False, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t = 'loadcase(pf_PY_file_v2) without .py extension : ' baseMVA1, bus1, gen1, branch1 = \ loadcase(pfcasefilev2, False, False, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t = 'loadcase(pf_PY_file_v2) with .py extension : ' baseMVA1, bus1, gen1, branch1 = \ loadcase(pfcasefilev2 + '.py', False, False, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t = 'loadcase(pf_MAT_file_v2) without .mat extension : ' baseMVA1, bus1, gen1, branch1 = \ loadcase(pfmatfilev2, False, False, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t = 'loadcase(pf_MAT_file_v2) with .mat extension : ' baseMVA1, bus1, gen1, branch1 = \ loadcase(pfmatfilev2 + '.mat', False, False, False) t_is(baseMVA1, baseMVA, 12, [t, 'baseMVA']) t_is(bus1, bus, 12, [t, 'bus']) t_is(gen1, gen, 12, [t, 'gen']) t_is(branch1, branch, 12, [t, 'branch']) t = 'loadcase(pf_struct_v1) (no version): ' baseMVA1, bus1, gen1, branch1 = t_case9_pf() c = {} c['baseMVA'] = baseMVA1 c['bus'] = bus1.copy() c['gen'] = gen1.copy() c['branch'] = branch1.copy() baseMVA2, bus2, gen2, branch2 = loadcase(c, False, False, False) t_is(baseMVA2, baseMVA, 12, [t, 'baseMVA']) t_is(bus2, bus, 12, [t, 'bus']) t_is(gen2, gen, 12, [t, 'gen']) t_is(branch2, branch, 12, [t, 'branch']) t = 'loadcase(pf_struct_v1) (version=''1''): ' c['version'] = '1' baseMVA2, bus2, gen2, branch2 = loadcase(c, False, False, False) t_is(baseMVA2, baseMVA, 12, [t, 'baseMVA']) t_is(bus2, bus, 12, [t, 'bus']) t_is(gen2, gen, 12, [t, 'gen']) t_is(branch2, branch, 12, [t, 'branch']) t = 'loadcase(pf_struct_v2) : ' c = {} c['baseMVA'] = baseMVA c['bus'] = bus.copy() c['gen'] = gen.copy() c['branch'] = branch.copy() c['version'] = '2' baseMVA2, bus2, gen2, branch2 = loadcase(c, False, False, False) t_is(baseMVA2, baseMVA, 12, [t, 'baseMVA']) t_is(bus2, bus, 12, [t, 'bus']) t_is(gen2, gen, 12, [t, 'gen']) t_is(branch2, branch, 12, [t, 'branch']) ##----- load PF data into struct ----- t = 'ppc = loadcase(pf_PY_file_v1) without .py extension : ' ppc1 = loadcase(pfcasefile) t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_PY_file_v1) with .py extension : ' ppc1 = loadcase(pfcasefile + '.py') t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_MAT_file_v1) without .mat extension : ' ppc1 = loadcase(pfmatfile) t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_MAT_file_v1) with .mat extension : ' ppc1 = loadcase(pfmatfile + '.mat') t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_PY_file_v2) without .py extension : ' ppc1 = loadcase(pfcasefilev2) t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_PY_file_v2) with .py extension : ' ppc1 = loadcase(pfcasefilev2 + '.py') t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_MAT_file_v2) without .mat extension : ' ppc1 = loadcase(pfmatfilev2) t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_MAT_file_v2) with .mat extension : ' ppc1 = loadcase(pfmatfilev2 + '.mat') t_is(ppc1['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc1['bus'], bus, 12, [t, 'bus']) t_is(ppc1['gen'], gen, 12, [t, 'gen']) t_is(ppc1['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_struct_v1) (no version): ' baseMVA1, bus1, gen1, branch1 = t_case9_pf() c = {} c['baseMVA'] = baseMVA1 c['bus'] = bus1.copy() c['gen'] = gen1.copy() c['branch'] = branch1.copy() ppc2 = loadcase(c) t_is(ppc2['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc2['bus'], bus, 12, [t, 'bus']) t_is(ppc2['gen'], gen, 12, [t, 'gen']) t_is(ppc2['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_struct_v1) (version=''1''): ' c['version'] = '1' ppc2 = loadcase(c) t_is(ppc2['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc2['bus'], bus, 12, [t, 'bus']) t_is(ppc2['gen'], gen, 12, [t, 'gen']) t_is(ppc2['branch'], branch, 12, [t, 'branch']) t = 'ppc = loadcase(pf_struct_v2) : ' c = {} c['baseMVA'] = baseMVA c['bus'] = bus.copy() c['gen'] = gen.copy() c['branch'] = branch.copy() c['version'] = '2' ppc2 = loadcase(c) t_is(ppc2['baseMVA'], baseMVA, 12, [t, 'baseMVA']) t_is(ppc2['bus'], bus, 12, [t, 'bus']) t_is(ppc2['gen'], gen, 12, [t, 'gen']) t_is(ppc2['branch'], branch, 12, [t, 'branch']) ## cleanup os.remove(matfile + '.mat') os.remove(pfmatfile + '.mat') os.remove(matfilev2 + '.mat') os.remove(pfmatfilev2 + '.mat') t = 'runpf(my_PY_file)' ppopt = ppoption(VERBOSE=0, OUT_ALL=0) results3, success = runpf(pfcasefile, ppopt) baseMVA3, bus3, gen3, branch3 = results3['baseMVA'], results3['bus'], \ results3['gen'], results3['branch'] t_ok( success, t ) t = 'runpf(my_object)' results4, success = runpf(c, ppopt) baseMVA4, bus4, gen4, branch4 = results4['baseMVA'], results4['bus'], \ results4['gen'], results4['branch'] t_ok( success, t ) t = 'runpf result comparison : ' t_is(baseMVA3, baseMVA4, 12, [t, 'baseMVA']) t_is(bus3, bus4, 12, [t, 'bus']) t_is(gen3, gen4, 12, [t, 'gen']) t_is(branch3, branch4, 12, [t, 'branch']) t = 'runpf(modified_struct)' c['gen'][2, 1] = c['gen'][2, 1] + 1 ## increase gen 3 output by 1 results5, success = runpf(c, ppopt) gen5 = results5['gen'] t_is(gen5[0, 1], gen4[0, 1] - 1, 1, t) ## slack bus output should decrease by 1 t_end()
def t_hessian(quiet=False): """Numerical tests of 2nd derivative code. @author: Ray Zimmerman (PSERC Cornell) """ t_begin(44, quiet) ## run powerflow to get solved case ppopt = ppoption(VERBOSE=0, OUT_ALL=0) results, _ = runpf(case30(), ppopt) baseMVA, bus, gen, branch = \ results['baseMVA'], results['bus'], results['gen'], results['branch'] ## switch to internal bus numbering and build admittance matrices _, bus, gen, branch = ext2int1(bus, gen, branch) Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) Vm = bus[:, VM] Va = bus[:, VA] * (pi / 180) V = Vm * exp(1j * Va) f = branch[:, F_BUS] ## list of "from" buses t = branch[:, T_BUS] ## list of "to" buses nl = len(f) nb = len(V) Cf = sparse((ones(nl), (range(nl), f)), (nl, nb)) ## connection matrix for line & from buses Ct = sparse((ones(nl), (range(nl), t)), (nl, nb)) ## connection matrix for line & to buses pert = 1e-8 ##----- check d2Sbus_dV2 code ----- t = ' - d2Sbus_dV2 (complex power injections)' lam = 10 * random.rand(nb) num_Haa = zeros((nb, nb), complex) num_Hav = zeros((nb, nb), complex) num_Hva = zeros((nb, nb), complex) num_Hvv = zeros((nb, nb), complex) dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) Haa, Hav, Hva, Hvv = d2Sbus_dV2(Ybus, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSbus_dVm_ap, dSbus_dVa_ap = dSbus_dV(Ybus, Vap) num_Haa[:, i] = (dSbus_dVa_ap - dSbus_dVa).T * lam / pert num_Hva[:, i] = (dSbus_dVm_ap - dSbus_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSbus_dVm_mp, dSbus_dVa_mp = dSbus_dV(Ybus, Vmp) num_Hav[:, i] = (dSbus_dVa_mp - dSbus_dVa).T * lam / pert num_Hvv[:, i] = (dSbus_dVm_mp - dSbus_dVm).T * lam / pert t_is(Haa.todense(), num_Haa, 4, ['Haa', t]) t_is(Hav.todense(), num_Hav, 4, ['Hav', t]) t_is(Hva.todense(), num_Hva, 4, ['Hva', t]) t_is(Hvv.todense(), num_Hvv, 4, ['Hvv', t]) ##----- check d2Sbr_dV2 code ----- t = ' - d2Sbr_dV2 (complex power flows)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, _, _ = dSbr_dV(branch, Yf, Yt, V) Gfaa, Gfav, Gfva, Gfvv = d2Sbr_dV2(Cf, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2Sbr_dV2(Ct, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap = \ dSbr_dV(branch, Yf, Yt, Vap) num_Gfaa[:, i] = (dSf_dVa_ap - dSf_dVa).T * lam / pert num_Gfva[:, i] = (dSf_dVm_ap - dSf_dVm).T * lam / pert num_Gtaa[:, i] = (dSt_dVa_ap - dSt_dVa).T * lam / pert num_Gtva[:, i] = (dSt_dVm_ap - dSt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp = \ dSbr_dV(branch, Yf, Yt, Vmp) num_Gfav[:, i] = (dSf_dVa_mp - dSf_dVa).T * lam / pert num_Gfvv[:, i] = (dSf_dVm_mp - dSf_dVm).T * lam / pert num_Gtav[:, i] = (dSt_dVa_mp - dSt_dVa).T * lam / pert num_Gtvv[:, i] = (dSt_dVm_mp - dSt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 4, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 4, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 4, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 4, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 4, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 4, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 4, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 4, ['Gtvv', t]) ##----- check d2Ibr_dV2 code ----- t = ' - d2Ibr_dV2 (complex currents)' lam = 10 * random.rand(nl) # lam = [1, zeros(nl-1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, _, _ = dIbr_dV(branch, Yf, Yt, V) Gfaa, Gfav, Gfva, Gfvv = d2Ibr_dV2(Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2Ibr_dV2(Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dIf_dVa_ap, dIf_dVm_ap, dIt_dVa_ap, dIt_dVm_ap, If_ap, It_ap = \ dIbr_dV(branch, Yf, Yt, Vap) num_Gfaa[:, i] = (dIf_dVa_ap - dIf_dVa).T * lam / pert num_Gfva[:, i] = (dIf_dVm_ap - dIf_dVm).T * lam / pert num_Gtaa[:, i] = (dIt_dVa_ap - dIt_dVa).T * lam / pert num_Gtva[:, i] = (dIt_dVm_ap - dIt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dIf_dVa_mp, dIf_dVm_mp, dIt_dVa_mp, dIt_dVm_mp, If_mp, It_mp = \ dIbr_dV(branch, Yf, Yt, Vmp) num_Gfav[:, i] = (dIf_dVa_mp - dIf_dVa).T * lam / pert num_Gfvv[:, i] = (dIf_dVm_mp - dIf_dVm).T * lam / pert num_Gtav[:, i] = (dIt_dVa_mp - dIt_dVa).T * lam / pert num_Gtvv[:, i] = (dIt_dVm_mp - dIt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 4, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 4, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 4, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 4, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 4, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 4, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 4, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 4, ['Gtvv', t]) ##----- check d2ASbr_dV2 code ----- t = ' - d2ASbr_dV2 (squared apparent power flows)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(branch, Yf, Yt, V) dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St) Gfaa, Gfav, Gfva, Gfvv = d2ASbr_dV2(dSf_dVa, dSf_dVm, Sf, Cf, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2ASbr_dV2(dSt_dVa, dSt_dVm, St, Ct, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap = \ dSbr_dV(branch, Yf, Yt, Vap) dAf_dVa_ap, dAf_dVm_ap, dAt_dVa_ap, dAt_dVm_ap = \ dAbr_dV(dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap) num_Gfaa[:, i] = (dAf_dVa_ap - dAf_dVa).T * lam / pert num_Gfva[:, i] = (dAf_dVm_ap - dAf_dVm).T * lam / pert num_Gtaa[:, i] = (dAt_dVa_ap - dAt_dVa).T * lam / pert num_Gtva[:, i] = (dAt_dVm_ap - dAt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp = \ dSbr_dV(branch, Yf, Yt, Vmp) dAf_dVa_mp, dAf_dVm_mp, dAt_dVa_mp, dAt_dVm_mp = \ dAbr_dV(dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp) num_Gfav[:, i] = (dAf_dVa_mp - dAf_dVa).T * lam / pert num_Gfvv[:, i] = (dAf_dVm_mp - dAf_dVm).T * lam / pert num_Gtav[:, i] = (dAt_dVa_mp - dAt_dVa).T * lam / pert num_Gtvv[:, i] = (dAt_dVm_mp - dAt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 2, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 2, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 2, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 2, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 2, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 2, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 2, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 2, ['Gtvv', t]) ##----- check d2ASbr_dV2 code ----- t = ' - d2ASbr_dV2 (squared real power flows)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(branch, Yf, Yt, V) dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dSf_dVa.real, dSf_dVm.real, dSt_dVa.real, dSt_dVm.real, Sf.real, St.real) Gfaa, Gfav, Gfva, Gfvv = d2ASbr_dV2(dSf_dVa.real, dSf_dVm.real, Sf.real, Cf, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2ASbr_dV2(dSt_dVa.real, dSt_dVm.real, St.real, Ct, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dSf_dVa_ap, dSf_dVm_ap, dSt_dVa_ap, dSt_dVm_ap, Sf_ap, St_ap = \ dSbr_dV(branch, Yf, Yt, Vap) dAf_dVa_ap, dAf_dVm_ap, dAt_dVa_ap, dAt_dVm_ap = \ dAbr_dV(dSf_dVa_ap.real, dSf_dVm_ap.real, dSt_dVa_ap.real, dSt_dVm_ap.real, Sf_ap.real, St_ap.real) num_Gfaa[:, i] = (dAf_dVa_ap - dAf_dVa).T * lam / pert num_Gfva[:, i] = (dAf_dVm_ap - dAf_dVm).T * lam / pert num_Gtaa[:, i] = (dAt_dVa_ap - dAt_dVa).T * lam / pert num_Gtva[:, i] = (dAt_dVm_ap - dAt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dSf_dVa_mp, dSf_dVm_mp, dSt_dVa_mp, dSt_dVm_mp, Sf_mp, St_mp = \ dSbr_dV(branch, Yf, Yt, Vmp) dAf_dVa_mp, dAf_dVm_mp, dAt_dVa_mp, dAt_dVm_mp = \ dAbr_dV(dSf_dVa_mp.real, dSf_dVm_mp.real, dSt_dVa_mp.real, dSt_dVm_mp.real, Sf_mp.real, St_mp.real) num_Gfav[:, i] = (dAf_dVa_mp - dAf_dVa).T * lam / pert num_Gfvv[:, i] = (dAf_dVm_mp - dAf_dVm).T * lam / pert num_Gtav[:, i] = (dAt_dVa_mp - dAt_dVa).T * lam / pert num_Gtvv[:, i] = (dAt_dVm_mp - dAt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 2, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 2, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 2, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 2, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 2, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 2, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 2, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 2, ['Gtvv', t]) ##----- check d2AIbr_dV2 code ----- t = ' - d2AIbr_dV2 (squared current magnitudes)' lam = 10 * random.rand(nl) # lam = [1 zeros(nl-1, 1)] num_Gfaa = zeros((nb, nb), complex) num_Gfav = zeros((nb, nb), complex) num_Gfva = zeros((nb, nb), complex) num_Gfvv = zeros((nb, nb), complex) num_Gtaa = zeros((nb, nb), complex) num_Gtav = zeros((nb, nb), complex) num_Gtva = zeros((nb, nb), complex) num_Gtvv = zeros((nb, nb), complex) dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, If, It = dIbr_dV(branch, Yf, Yt, V) dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, If, It) Gfaa, Gfav, Gfva, Gfvv = d2AIbr_dV2(dIf_dVa, dIf_dVm, If, Yf, V, lam) Gtaa, Gtav, Gtva, Gtvv = d2AIbr_dV2(dIt_dVa, dIt_dVm, It, Yt, V, lam) for i in range(nb): Vap = V.copy() Vap[i] = Vm[i] * exp(1j * (Va[i] + pert)) dIf_dVa_ap, dIf_dVm_ap, dIt_dVa_ap, dIt_dVm_ap, If_ap, It_ap = \ dIbr_dV(branch, Yf, Yt, Vap) dAf_dVa_ap, dAf_dVm_ap, dAt_dVa_ap, dAt_dVm_ap = \ dAbr_dV(dIf_dVa_ap, dIf_dVm_ap, dIt_dVa_ap, dIt_dVm_ap, If_ap, It_ap) num_Gfaa[:, i] = (dAf_dVa_ap - dAf_dVa).T * lam / pert num_Gfva[:, i] = (dAf_dVm_ap - dAf_dVm).T * lam / pert num_Gtaa[:, i] = (dAt_dVa_ap - dAt_dVa).T * lam / pert num_Gtva[:, i] = (dAt_dVm_ap - dAt_dVm).T * lam / pert Vmp = V.copy() Vmp[i] = (Vm[i] + pert) * exp(1j * Va[i]) dIf_dVa_mp, dIf_dVm_mp, dIt_dVa_mp, dIt_dVm_mp, If_mp, It_mp = \ dIbr_dV(branch, Yf, Yt, Vmp) dAf_dVa_mp, dAf_dVm_mp, dAt_dVa_mp, dAt_dVm_mp = \ dAbr_dV(dIf_dVa_mp, dIf_dVm_mp, dIt_dVa_mp, dIt_dVm_mp, If_mp, It_mp) num_Gfav[:, i] = (dAf_dVa_mp - dAf_dVa).T * lam / pert num_Gfvv[:, i] = (dAf_dVm_mp - dAf_dVm).T * lam / pert num_Gtav[:, i] = (dAt_dVa_mp - dAt_dVa).T * lam / pert num_Gtvv[:, i] = (dAt_dVm_mp - dAt_dVm).T * lam / pert t_is(Gfaa.todense(), num_Gfaa, 3, ['Gfaa', t]) t_is(Gfav.todense(), num_Gfav, 3, ['Gfav', t]) t_is(Gfva.todense(), num_Gfva, 3, ['Gfva', t]) t_is(Gfvv.todense(), num_Gfvv, 2, ['Gfvv', t]) t_is(Gtaa.todense(), num_Gtaa, 3, ['Gtaa', t]) t_is(Gtav.todense(), num_Gtav, 3, ['Gtav', t]) t_is(Gtva.todense(), num_Gtva, 3, ['Gtva', t]) t_is(Gtvv.todense(), num_Gtvv, 2, ['Gtvv', t]) t_end()
def runacdcpf(caseac=None, casedc=None, pacdcopt=None, ppopt=None): """ Runs a sequential AC/DC power flow, optionally returning the results, a convergence flag and the time. Inputs (optional): CASEAC : ac power flow data either a PYPOWER case struct or a string containing the name of the file with the data (default ac case is 'case5_stagg', only used when both ac and dc power flow data are not defined) (see also CASEFORMAT and LOADCASE and PYPOWER) CASEDC : dc power flow data either a PYACDCPF case struct or a string containing the name of the file with the data (default dc case is 'case5_stagg_MTDCslack') (see also LOADCASEDC) PACDCOPT : PYACDCPF options vector to override default ac/dc power flow options. Can be used to specify tolerances, inclusion of limits, plot options and more (see also MACDCOPTION). PPOPT : PYPOWER options vector to override default options can be used to specify the solution algorithm, output options termination tolerances, and more (see also MPOPTION). Outputs: RESULTSAC : results struct, with the following fields from the input PYPOWER case: baseMVA, bus, branch, gen (but with solved voltages, power flows, etc.) RESULTSDC : results struct, with the following fields: input PYACDCPF dc case: baseMVAac, baseMVAdc, pol, busdc, convdc, branchdc (but with solved voltages, power flows, etc.) CONVERGED : converge flag, can additionally be returned TIMECALC : elapsed time, can additionally be returned Examples of usage: [resultsac, resultsdc, converged, te] = runacdcpf('case5_stagg', \ 'case5_stagg_MTDCdroop'); @author:Jef Beerten (KU Leuven) @author:Roni Irnawan (Aalborg University) """ ## start of time calculation t0 = time() ## add subdirectories to path if caseac is None: dirac = join(dirname(__file__), 'Cases', 'PowerflowAC') caseac = join(dirac, 'case5_stagg') # caseac = join(dirac, 'case3_inf') # caseac = join(dirac, 'case24_ieee_rts1996_3zones') # caseac = join(dirac, 'case24_ieee_rts1996_3zones_inf') if casedc is None: dirdc = join(dirname(__file__), 'Cases', 'PowerflowDC') # casedc = join(dirdc, 'case5_stagg_HVDCptp') # casedc = join(dirdc, 'case5_stagg_MTDCslack') casedc = join(dirdc, 'case5_stagg_MTDCdroop') # casedc = join(dirdc, 'case24_ieee_rts1996_MTDC') ## default arguments ppopt = ppoption(ppopt) ppopt["VERBOSE"] = 0 ppopt["OUT_ALL"] = 0 pacdcopt = pacdcoption(pacdcopt) ## options tolacdc = pacdcopt["TOLACDC"] itmaxacdc = pacdcopt["ITMAXACDC"] toldc = pacdcopt["TOLDC"] itmaxdc = pacdcopt["ITMAXDC"] tolslackdroop = pacdcopt["TOLSLACKDROOP"] itmaxslackdroop = pacdcopt["ITMAXSLACKDROOP"] tolslackdroopint = pacdcopt["TOLSLACKDROOPINT"] itmaxslackdroopint = pacdcopt["ITMAXSLACKDROOPINT"] multslack = pacdcopt["MULTSLACK"] limac = pacdcopt["LIMAC"] limdc = pacdcopt["LIMDC"] tollim = pacdcopt["TOLLIM"] output = pacdcopt["OUTPUT"] convplotopt = pacdcopt["CONVPLOTOPT"] ## ----- initialise ----- ## read data pdc = loadcasedc(casedc) ppc = loadcase(caseac) ##----- Data preparation ----- ## converter outage are considered as stations without ac grid connection pdc, conv0busi, conv1, conv1i, conv0, conv0i = convout(pdc) pdc['convdc'] = conv1 # only use converters without outage ## dc branch outages (remove branches from input data) brchdc1, brchdc1i, brchdc0, brchdc0i = brchdcout(pdc) pdc['branchdc'] = brchdc1 # only include branches in operation ## ac branch outages (remove branches from input data) brch1, brch1i, brch0, brch0i = brchout(ppc) ppc['branch'] = brch1 # only include branches in operation ## generator outages (remove non-operational generators from input data) gon = where(ppc['gen'][:,GEN_STATUS] > 0)[0] # which gens are on? goff = where(ppc['gen'][:,GEN_STATUS] == 0)[0] # which gens are off? gen0 = ppc['gen'][goff,:] # non-operational gens data ppc['gen'] = ppc['gen'][gon,:] #keep operational generators" ##----- External to internal numbering ----- ## dc network external to internal bus numbering i2edcpmt, i2edc, pdc = ext2intdc(pdc) ## ac network external to internal bus numbering acdmbus, i2eac, pdc, ppc = ext2intac(pdc,ppc) ## sort matrices by new bus numbers i2ebus = ppc['bus'][:,0].argsort() i2egen = ppc['gen'][:,0].argsort() i2ebrch = ppc['branch'][:,0].argsort() i2ebusdc = pdc['busdc'][:,0].argsort() i2econvdc = pdc['convdc'][:,0].argsort() i2ebrchdc = pdc['branchdc'][:,0].argsort() ppc['bus'] = ppc['bus'][i2ebus,:] ppc['gen'] = ppc['gen'][i2egen,:] ppc['branch'] = ppc['branch'][i2ebrch,:] pdc['busdc'] = pdc['busdc'][i2ebusdc,:] pdc['convdc'] = pdc['convdc'][i2econvdc,:] pdc['branchdc'] = pdc['branchdc'][i2ebrchdc,:] ## Per unit external to internal data conversion pdc = ext2intpu(ppc['baseMVA'],pdc) ##----- Additional data preparation & index initialisation ----- ## zero rows addition to convdc matrix (dc buses without converter) convdc1 = zeros((pdc['busdc'].shape[0]-pdc['convdc'].shape[0], pdc['convdc'].shape[1])) pdc['convdc'] = r_[pdc['convdc'],convdc1] ## indices initialisation bdci = where(pdc['busdc'][:,BUSAC_I])[0].astype(int) cdci = where(pdc['convdc'][:,CONVSTATUS] == 1)[0].astype(int) slackdc = where(pdc['convdc'][:,CONVTYPE_DC] == DCSLACK)[0].astype(int) droopdc = where(pdc['convdc'][:,CONVTYPE_DC] == DCDROOP)[0].astype(int) ngriddc = pdc['busdc'][:,GRIDDC].max() ## convert to internal indexing baseMVA, bus, gen, branch = \ ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"] baseMVAac, baseMVAdc, pol, busdc, convdc, branchdc = \ pdc["baseMVAac"], pdc["baseMVAdc"], pdc["pol"], \ pdc["busdc"], pdc["convdc"], pdc["branchdc"] ##----- Violation check ----- ## dc slack bus and distributed voltage bus violation check gridviol = setdiff1d(arange(1,ngriddc+1),busdc[r_[slackdc, droopdc],GRIDDC]) if not gridviol.size == 0: stdout.write('\nMultiple dc slack busses defined in grid %d \n' % (gridviol)) stderr.write('No droop controlled bus or slack bus defined for every dc grid !\n') ## remove multiple slack buses if multslack == 0: for ii in arange(1,ngriddc+1): slackdcii = intersect1d(slackdc,where(busdc[:,GRIDDC] == ii)[0]) if slackdcii.size > 1: convdc[slackdcii[0].astype(int),CONVTYPE_DC ] = DCSLACK convdc[slackdcii[1:].astype(int),CONVTYPE_DC ] = DCNOSLACK slackdcii = slackdcii[0] ## printout changes stdout.write('\nMultiple dc slack busses defined in grid %d' %(ii)) stdout.write('\n Bus %d kept as the slack bus\n'%\ (i2edc[slackdcii.astype(int)+1])) ## redefine slack buses slackdc = where(convdc[:,CONVTYPE_DC] == DCSLACK)[0].astype(int) ## define indices of slack, droop and power controlled buses slackdroopdc = union1d(slackdc, droopdc) noslackbdc = setdiff1d(where(busdc[:,BUSDC_I]), slackdc) ## remove converter and generator V control violations vcontrvsc = where(convdc[:,CONVTYPE_AC] == PVC)[0] vcontrgen = r_[where(bus[:,BUS_TYPE] == PV)[0], \ where(bus[:,BUS_TYPE] == REF)[0]] ## buses with V control conflicts vconfl = intersect1d(vcontrvsc, vcontrgen).astype(int) convdc[vconfl,CONVTYPE_AC] = PQC convdc[vconfl,QCONV] = 0 convdc[:,QCONV] *= convdc[:,CONVTYPE_AC] == PQC if not vconfl.size == 0: stdout.write('Generator & VSC converter on the same bus') stdout.write('\n Conflicting voltage control on bus %d' %(i2eac[vconfl+1])) stdout.write('\n=> Corresponding VSC Converter set to PQ control without Q injections.\n') ##----- initialisation ac network ----- ## dummy generator initialisation Vcref = convdc[:,VCONV] # voltage setpoints busVSC = bus.copy() gendm = zeros((0,gen.shape[1])) genPQ = zeros((0,1)).astype(int) genPQi = zeros((0,1)).astype(int) Qcmin_dum = -99999 Qcmax_dum = 99999 Pcmin_dum = 0 Pcmax_dum = 99999 ## dummy generator addition for ii in arange(convdc.shape[0]): ## change control from PQ to PV for buses with converter in PV control if bus[ii,BUS_TYPE] == PQ and convdc[ii,CONVTYPE_AC] == PVC: busVSC[ii,BUS_TYPE] = PV ## add dummy generator to V controlling converter bus without generator if not any(gen[:,GEN_BUS] == bus[ii,BUS_I]): gendm = r_[gendm, zeros((1,gen.shape[1]))] gendm[-1,[GEN_BUS,PG,QG,QMAX,QMIN,VG,MBASE,GEN_STATUS,PMAX,PMIN]] = \ [ii+1,0,0,Qcmax_dum,Qcmin_dum,Vcref[ii],baseMVAac,1,Pcmax_dum,Pcmin_dum] else: genPQ = r_[genPQ,bus[ii,BUS_I]] genPQii = where(gen[:,GEN_BUS] == bus[ii,BUS_I])[0] genPQi = r_[genPQi,genPQii] ## define buses with dummy generator # gdmbus = where(gendm[[where(bus[:,BUS_I]==x)[0][0] for x in \ # gendm[:,GEN_BUS]],GEN_BUS])[0] if any(gendm[:,GEN_BUS]): gdmbus = where(gendm[:,GEN_BUS] == bus[:,BUS_I])[0] else: gdmbus = [] ## converter stations power injections into ac network Pvsc = convdc[:,PCONV]/baseMVA Qvsc = convdc[:,QCONV]/baseMVA ## dc voltage droop setpoints and parameters PVdroop = zeros(busdc.shape[0]) Pdcset = zeros(busdc.shape[0]) Vdcset = zeros(busdc.shape[0]) dVdcset = zeros(busdc.shape[0]) PVdroop[cdci] = convdc[cdci,DROOP]*baseMVA Pdcset[cdci] = convdc[cdci,PDCSET]/baseMVA Vdcset[cdci] = convdc[cdci,VDCSET] dVdcset[cdci] = convdc[cdci,DVDCSET] ## voltage droop converter power initialisation Pvsc[droopdc] = Pdcset[droopdc] ## assumption: operating in reference set-point & no converter losses ## dc slack converter power injection initialisation if slackdc.size != 0: for ii in arange(1,ngriddc+1): #slackdcii = where(convdc[busdc[:,GRIDDC]==ii,CONVTYPE_DC]==DCSLACK)[0] slackdcii = intersect1d(where(busdc[:,GRIDDC]==ii)[0],\ where(convdc[:,CONVTYPE_DC]==DCSLACK)[0]) if slackdcii.size != 0: #Pvscii = Pvsc*(convdc[busdc[:,GRIDDC]==1,CONVTYPE_DC]!=DCSLACK) Pvscii = Pvsc*(equal(busdc[:,GRIDDC],ii)* \ not_equal(convdc[:,CONVTYPE_DC],DCSLACK)) Pvsc[slackdcii] = -Pvscii.sum()/slackdcii.shape[0] ## Inclusion of converters as loads busVSC[cdci,PD] = bus[cdci,PD] - Pvsc[cdci]*baseMVA busVSC[cdci,QD] = bus[cdci,QD] - Qvsc[cdci]*baseMVA ##----- initialisation of converter quantities ----- ## per unit converter loss coefficients values basekA = baseMVA/(sqrt(3)*convdc[:,BASEKVC]) lossa = convdc[:,LOSSA]/baseMVA lossb = convdc[:,LOSSB]*basekA/baseMVA losscr = convdc[:,LOSSCR]*basekA**2/baseMVA lossci = convdc[:,LOSSCI]*basekA**2/baseMVA ## converter reactor parameters Rc = convdc[:,RCONV] Xc = convdc[:,XCONV] Zc = Rc+j*Xc ## converter limits data Icmax = convdc[:,ICMAX] Vcmax = convdc[:,VCMAX] Vcmin = convdc[:,VCMIN] ## filter reactance Bf = convdc[:,BF] ## transformer parameters Rtf = convdc[:,RTF] Xtf = convdc[:,XTF] Ztf = Rtf+j*Xtf ##----- initialisation of dc network quantities ----- ## build dc bus matrix Ybusdc, Yfdc, Ytdc = makeYbusdc( busdc, branchdc ) ## detect ac islands errors (non-synchronised zones => to be solved independently) zonecheck(bus, gen, branch, i2eac, output) aczones = sort(unique(bus[:,ZONE])).astype(int) ##----- main iteration loop ----- ## initialise Vdc = busdc[:,VDC] #dc bus voltages genVSC = r_[gen, gendm] #inclusion of dummy generators for ac solution gendmidx = where(genVSC[:,GEN_BUS] == setdiff1d(genVSC[:,GEN_BUS], gen[:, GEN_BUS]))[0] # index of dummy generators in genVSC matrix Ps = Pvsc #grid side converter power initialisation Pdc = zeros(busdc.shape[0]) Ifdc = zeros(branchdc.shape[0]) Pfdc = zeros(branchdc.shape[0]) Ptdc = zeros(branchdc.shape[0]) ## iteration options it = 0 converged = 0 ## main loop while (not converged) and (it <= itmaxacdc): ## update iteration counter it += 1 ## reset grid side converter reactive power injection Qs = Qvsc Ss = Ps +j*Qs ##----- ac network power flow ----- ## ac power flow with converters as loads (PQ mode) or load+generator (PV mode) busVSCext = zeros((0,bus.shape[1])) genVSCext = zeros((0,gen.shape[1])) branchext = zeros((0,QT+1)) buszi = [] genVSCzi = [] brchzi = [] for i in arange(aczones.size): ## select buses, generators and branches in the specified ac zone buszi = where(bus[:,ZONE] == aczones[i])[0] genVSCzi = where(bus[[where(bus[:,BUS_I]==x)[0][0] for x in \ genVSC[:,GEN_BUS]],ZONE] == aczones[i])[0] brchzi = where(bus[[where(bus[:,BUS_I]==x)[0][0] for x in \ branch[:,F_BUS]],ZONE] == aczones[i])[0] busVSCz = busVSC[buszi,:] genVSCz = genVSC[genVSCzi,:] branchz = branch[brchzi,:] ## solve ac power flow for specified ac zone (if not infinite bus) if busVSCz.shape[0]>1: accaseVSCz = {} accaseVSCz['baseMVA'] = baseMVA accaseVSCz['bus'] = busVSCz accaseVSCz['gen'] = genVSCz accaseVSCz['branch'] = branchz resultsz,successz = runpf(accaseVSCz, ppopt) busVSCz = resultsz['bus'] genVSCz = resultsz['gen'] branchz = resultsz['branch'] ## store solutions for specified ac zone in extended matrices for k,idx in enumerate(buszi): if busVSCext.shape[0] <= idx: busVSCext = r_[busVSCext,zeros((idx-busVSCext.shape[0]+1, \ bus.shape[1]))] busVSCext[idx,:] = busVSCz[k,:] for k,idx in enumerate(genVSCzi): if genVSCext.shape[0] <= idx: genVSCext = r_[genVSCext,zeros((idx-genVSCext.shape[0]+1, \ gen.shape[1]))] genVSCext[idx,:] = genVSCz[k,:] for k,idx in enumerate(brchzi): if branchext.shape[0] <= idx: branchext = r_[branchext,zeros((idx-branchext.shape[0]+1, \ QT+1))] branchext[idx,:] = branchz[k,:] busVSC = busVSCext.copy() genVSC = genVSCext.copy() branch = branchext.copy() ## dummy generator update gendm = genVSC[gen.shape[0]:,:] ## dummy generator on converter V controlled bus Ss[gdmbus] = Ss[gdmbus] + j*gendm[:,QG]/baseMVA ## PQ generator on converter V controlled bus Ss[genPQ] = Ss[genPQ] + \ j*(genVSC[genPQi,QG] - gen[genPQi,QG])/baseMVA ## update grid side converter power injections Ps = real(Ss) Qs = imag(Ss) ## generator reset genVSC[gendmidx,QG] = 0 genVSC[genPQi,QG] = gen[genPQi,QG] ##----- Converter calculations ----- ## converter reactor voltages and power Vs = busVSC[bdci,VM]*exp(j*busVSC[bdci,VA]*pi/180) Itf = conj(Ss/Vs) ## transformer current Vf = Vs + Itf*Ztf ## filter side voltage Ssf = Vf*conj(Itf) ## filter side transformer complex power Qf = -Bf*abs(Vf)**2 ## filter reactive power Scf = Ssf + j*Qf ## filter side converter complex power Ic = conj(Scf/Vf) ## converter current Vc = Vf + Ic*Zc ## converter side voltage Sc = Vc*conj(Ic) ## converter side complex power ## converter active and reactive powers Pc = real(Sc) Qc = imag(Sc) Pcf = real(Scf) Qcf = imag(Scf) Psf = real(Ssf) Qsf = imag(Ssf) ## initialisation Ps_old = Ps.copy() if limac == 1: ##--- converter limit check --- ## initialisation limviol = zeros((busdc.shape[0])) SsL = zeros((busdc.shape[0]),dtype=complex) plotarg = zeros((busdc.shape[0],17),dtype=complex) for ii in arange(1,ngriddc+1): ## remove slack converters from limit check cdcii = where(busdc[:,GRIDDC] == ii)[0] ccdcslackii = where(intersect1d(cdcii,slackdc)==cdcii)[0] if not ccdcslackii.size == 0: cdcii = delete(cdcii,ccdcslackii) #remove slack converter cdci0 = where(convdc[cdcii,CONV_BUS]==0)[0] cdcii = delete(cdcii,cdci0) # remove zero elements (converter outages) ## converter limit check for jj in arange(cdcii.size): cvjj = cdcii[jj] limviol[cvjj],SsL[cvjj], plotarg[cvjj,:] = convlim(Ss[cvjj], \ Vs[cvjj], Vc[cvjj], Ztf[cvjj], Bf[cvjj], Zc[cvjj], \ Icmax[cvjj], Vcmax[cvjj], Vcmin[cvjj], i2edc[cvjj+1], \ tollim, convplotopt) ## converter limit violations (1 = Q limit, 2 = P limit) limviolii = limviol*(busdc[:,GRIDDC] == ii) dSii = (SsL-Ss)*(busdc[:,GRIDDC] == ii)*(convdc[:,CONVTYPE_DC] != DCSLACK) if (2 in limviolii) or (1 in limviolii): if (2 in limviolii): dSii = dSii*(limviolii==2) dSiimaxi = where(abs(real(dSii)).max())[0] stdout.write('\n Active power setpoint of converter %d changed from %.2f MW to %.2f MW.'%( \ i2edc[dSiimaxi+1], real(Ss[dSiimaxi])*baseMVA, real(SsL[dSiimaxi])*baseMVA)) stdout.write('\n Reactive power setpoint of converter %d changed from %.2f MVAr to %.2f MVAr.\n'%(\ i2edc[dSiimaxi+1], imag(Ss[dSiimaxi])*baseMVA, imag(SsL[dSiimaxi])*baseMVA)) else: ## if ismember(1, limviolii) dSii = dSii*(limviolii==1) dSiimaxi = argmax(abs(imag(dSii))) stdout.write('\n Reactive power setpoint of converter %d changed from %.2f MVAr to %.2f MVAr. \n'%(\ i2edc[dSiimaxi+1], imag(Ss[dSiimaxi])*baseMVA, imag(SsL[dSiimaxi])*baseMVA)) ## plot converter setpoint adaptation if convplotopt != 0 : convlimplot(plotarg[dSiimaxi,:], i2edc[dSiimaxi]) ## update converter powers Ss[dSiimaxi] = SsL[dSiimaxi] Pvsc[dSiimaxi] = real(Ss[dSiimaxi]) Qvsc[dSiimaxi] = imag(Ss[dSiimaxi]) busVSC[dSiimaxi,PD] = bus[dSiimaxi,PD] - \ Pvsc[dSiimaxi]*baseMVA ## converter P injection from input files included as load busVSC[dSiimaxi,QD] = bus[dSiimaxi,QD] - \ Qvsc[dSiimaxi]*baseMVA ## only Q from input files is included, not for V control else: dSiimaxi = [] ## Remove voltage control on violated converter if convdc[dSiimaxi, CONVTYPE_AC]==PVC: convdc[dSiimaxi, CONVTYPE_AC] = PQC stdout.write(' Voltage control at converter bus %d removed.\n'% i2edc[dSiimaxi+1]) busVSC[dSiimaxi, BUS_TYPE] = PQ ## Remove dummy generator (PV bus changed to PQ bus) if dSiimaxi in gdmbus: dSidx = where(gdmbus == dSiimaxi)[0] dSgenidx = gendmidx[dSidx] gendm = delete(gendm,dSidx) genVSC = delete(genVSC,dSgenidx) gdmbus = delete(gdmbus,dSidx) gendmidx = where(genVSC[:,GEN_BUS] == np.setdiff1d(genVSC[:, GEN_BUS],gen[:, GEN_BUS])) ## index of dummy generators in genVSC matrix ## Remove VSC voltage control at genPQ bus if dSiimaxi in genPQ: dSidx = where(genPQ == dSiimaxi)[0] genPQ = delete(genPQ,dSidx) genPQi = delete(genPQi,dSidx) ## Remove droop control on violated converter if convdc[dSiimaxi, CONVTYPE_DC]==DCDROOP: convdc[dSiimaxi, CONVTYPE_DC] = DCNOSLACK droopdc = setdiff1d(droopdc,dSiimaxi) ## remove converter from droop converters slackdroopdc = setdiff1d(slackdroopdc,dSiimaxi) ## remove converter from slack/droop converters (additional loss iteration) stdout.write(' Droop control at converter bus %d disabled.\n'%i2edc[dSiimaxi+1]) ## recalculate converter quantities after limit check Itf = conj(Ss/Vs) ## transformer current Vf = Vs + Itf*Ztf ## filter side voltage Ssf = Vf*conj(Itf) ## filter side transformer complex power Qf = -Bf*abs(Vf)**2 ## filter reactive power Scf = Ssf + j*Qf ## filter side converter complex power Ic = conj(Scf/Vf) ## converter current Vc = Vf + Ic*Zc ## converter side voltage Sc = Vc*conj(Ic) ## converter side complex power ## converter active and reactive powers after limit check Ps = real(Ss) Qs = imag(Ss) Pc = real(Sc) Qc = imag(Sc) Pcf = real(Scf) Qcf = imag(Scf) Psf = real(Ssf) Qsf = imag(Ssf) ## converter losses and dc side power Ploss = calclossac(Pc, Qc, Vc, lossa, lossb, losscr, lossci) Pdc[cdci] = Pc[cdci] + Ploss[cdci] ##----- dc networks power flow ----- ## calculate dc networks Vdc, Pdc = dcnetworkpf(Ybusdc, Vdc, Pdc,slackdc, noslackbdc,\ droopdc, PVdroop, Pdcset, Vdcset, dVdcset, pol, toldc, itmaxdc) ## calculate dc line powers Ifdc = Yfdc*Vdc ## current through dc lines Vdcf = Vdc[[where(busdc[:,BUSDC_I]==x)[0][0] for x in \ branchdc[:,F_BUSDC]]] Vdct = Vdc[[where(busdc[:,BUSDC_I]==x)[0][0] for x in \ branchdc[:,T_BUSDC]]] Pfdc = pol*Vdcf*Ifdc ## power at the "from" bus Ptdc = pol*Vdct*(-Ifdc) ## power at the "to" bus ##----- slack/droop bus voltage and converter loss ----- ## Initialisation Pc[slackdroopdc] = Pdc[slackdroopdc] - Ploss[slackdroopdc] ## Pc initialisation itslack = 0 convergedslackdroop = 0 ## dc slack bus loss calculation while not convergedslackdroop and itslack<=itmaxslackdroop: ## update iteration counter and convergence variable itslack += 1 Pcprev = Pc.copy() ## update slack bus powers Ps, Qc and voltage Vc Ps[slackdroopdc], Qc[slackdroopdc], Vc[slackdroopdc] = calcslackdroop( Pc[slackdroopdc], Qs[slackdroopdc], Vs[slackdroopdc], \ Vf[slackdroopdc], Vc[slackdroopdc], Ztf[slackdroopdc], \ Bf[slackdroopdc], Zc[slackdroopdc], \ tolslackdroopint, itmaxslackdroopint) ## update slack bus losses Ploss[slackdroopdc] = calclossac(Pc[slackdroopdc], Qc[slackdroopdc], \ Vc[slackdroopdc], lossa[slackdroopdc], lossb[slackdroopdc], \ losscr[slackdroopdc], lossci[slackdroopdc]) ## update slack bus converter side power Pc Pc[slackdroopdc] = Pdc[slackdroopdc] - Ploss[slackdroopdc] ## slack bus tolerance check if max(abs(Pcprev[slackdroopdc] - Pc[slackdroopdc])) < tolslackdroop: convergedslackdroop = 1 if not convergedslackdroop: stdout.write('\nSlackbus/Droop converter loss calculation of grid did NOT converge in %d iterations\n'% itslack) ## extended bus matrix update busVSC[cdci,PD] = bus[cdci,PD] - Ps[cdci]*baseMVA ## convergence check if abs(Ps_old - Ps).max() < tolacdc: converged = 1 ## end of iteration timecalc = time() - t0 ##----- Post processing ----- ## convergence if converged: if output: stdout.write('\nSequential solution method converged in %d iterations\n'%it) else: stdout.write('\nSequential solution method did NOT converge after %d iterations\n'%it) ## converter limit check if limac == 1: for ii in arange(cdci.size): cvii = cdci[ii] limviol, _, plotarg = convlim(Ss[cvii], Vs[cvii], Vc[cvii], Ztf[cvii], \ Bf[cvii], Zc[cvii], Icmax[cvii], Vcmax[cvii], Vcmin[cvii], i2edc[cvii+1], tollim, 1) if limviol != 0: ## limits are hit if (convdc[cvii,CONVTYPE_DC] == DCSLACK): stdout.write('\n Slackbus converter %d is operating outside its limits.\n'%i2edc[cvii+1]) elif (convdc[cvii,CONVTYPE_DC] == DCNOSLACK): stdout.write('\n Converter %d is operating outside its limits.\n'%i2edc[cvii+1]) if convplotopt == 2 : convlimplot(plotarg, i2edc[cvii]) ## update bus matrix bus[:,VM] = busVSC[:,VM] bus[:,VA] = busVSC[:,VA] ## dummy generators removal gen = genVSC[arange(gen.shape[0]),:] ## update busdc matrix busdc[:,PDC] = Pdc*baseMVA busdc[:,VDC] = Vdc ## update convdc matrix convdc[:,PCONV] = Ps*baseMVA convdc[:,QCONV] = Qs*baseMVA # new addition to convdc matrix convdc = c_[convdc,abs(Vc)] convdc = c_[convdc,angle(Vc)*180/pi] convdc = c_[convdc,Pc*baseMVA] convdc = c_[convdc,Qc*baseMVA] convdc = c_[convdc,Ploss*baseMVA] convdc = c_[convdc,abs(Vf)] convdc = c_[convdc,angle(Vf)*180/pi] convdc = c_[convdc,Psf*baseMVA] convdc = c_[convdc,Qsf*baseMVA] convdc = c_[convdc,Qcf*baseMVA] ## new addition to branchdc matrix branchdc = c_[branchdc,Pfdc*baseMVA] branchdc = c_[branchdc,Ptdc*baseMVA] #----- internal to external bus renumbering ----- # remove dummy converters convdc = convdc[cdci,:] ## convert to external indexing ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"] = \ baseMVA, bus, gen, branch pdc["baseMVAac"], pdc["baseMVAdc"], pdc["pol"], \ pdc["busdc"], pdc["convdc"], pdc["branchdc"] = \ baseMVAac, baseMVAdc, pol, busdc, convdc, branchdc ## Per unit internal to external data conversion pdc =int2extpu(ppc['baseMVA'],pdc); ## Undo the matrices sorting based on the bus numbers ppc['bus'] = ppc['bus'][i2ebus.argsort(),:] ppc['gen'] = ppc['gen'][i2egen.argsort(),:] ppc['branch'] = ppc['branch'][i2ebrch.argsort(),:] pdc['busdc'] = pdc['busdc'][i2ebusdc.argsort(),:] pdc['convdc'] = pdc['convdc'][i2econvdc.argsort(),:] pdc['branchdc'] = pdc['branchdc'][i2ebrchdc.argsort(),:] ## ac network internal to external bus numbering pdc, ppc = int2extac(i2eac, acdmbus, pdc, ppc) ## dc network internal to external bus numbering pdc = int2extdc(i2edcpmt, i2edc, pdc) ## generator outage inclusion gen1 = ppc['gen'] ## operational generators gen0[:,[PG, QG]] = 0 ## reset generator power injection ppc['gen'] = zeros((gon.shape[0]+goff.shape[0], gen1.shape[1])); ppc['gen'][gon,:] = gen1 ## include operational generators ppc['gen'][goff,:] = gen0 ## include non-operational generators ## converter with outages inclusion conv1 = pdc['convdc'] conv0 = c_[conv0, zeros((conv0.shape[0],conv1.shape[1] - conv0.shape[1]))] pdc['convdc'][conv0i, :] = conv0 pdc['convdc'][conv1i, :] = conv1 if conv0busi.shape[0]>0: pdc['busdc'][conv0busi[:,0], BUSAC_I] = conv0busi[:,1] ## dc branch outages inclusion brchdc1 = pdc['branchdc'] brchdc0 = c_[brchdc0, zeros((brchdc0.shape[0], brchdc1.shape[1] - brchdc0.shape[1]))] pdc['branchdc'][brchdc0i,:] = brchdc0 pdc['branchdc'][brchdc1i,:] = brchdc1 ## ac branch outages inclusion if ppc['branch'].shape[0] == 0: ## all infinite buses # python start the index at 0 brch0 = c_[brch0, zeros((brch0.shape[0], QT + 1 - brch0.shape[1]))] # not necessary anymore after rewriting the code ppc['branch'] = brch0; else: brch1 = ppc['branch'] brch0 = c_[brch0, zeros((brch0.shape[0], brch1.shape[1] - brch0.shape[1]))]; ppc['branch'][brch0i,:] = brch0 ppc['branch'][brch1i,:] = brch1 ##----- output results ----- ## print results if output: printpf(ppc['baseMVA'], ppc['bus'], ppc['gen'], ppc['branch'],None,converged,timecalc) printdcpf(pdc['busdc'], pdc['convdc'], pdc['branchdc']) ##----- output results ----- # as dict resultsac = {} resultsac['baseMVA'] = baseMVA resultsac['bus'] = bus resultsac['gen'] = gen resultsac['branch'] = branch resultsdc = {} resultsdc['baseMVAac'] = baseMVAac resultsdc['baseMVAdc'] = baseMVAdc resultsdc['pol'] = pol resultsdc['busdc'] = busdc resultsdc['convdc'] = convdc resultsdc['branchdc'] = branchdc # if nargout == 2 || nargout == 3 || nargout == 4 # baseMVA = resultsac; # bus = resultsdc; # gen = converged; # branch = timecalc; # end input() return resultsac, resultsdc, converged, timecalc
def t_dcline(quiet=False): """Tests for DC line extension in L{{toggle_dcline}. @author: Ray Zimmerman (PSERC Cornell) @author: Richard Lincoln """ num_tests = 50 t_begin(num_tests, quiet) tdir = dirname(__file__) casefile = join(tdir, 't_case9_dcline') if quiet: verbose = False else: verbose = False t0 = '' ppopt = ppoption(OPF_VIOLATION=1e-6, PDIPM_GRADTOL=1e-8, PDIPM_COMPTOL=1e-8, PDIPM_COSTTOL=1e-9) ppopt = ppoption(ppopt, OPF_ALG=560, OPF_ALG_DC=200) ppopt = ppoption(ppopt, OUT_ALL=0, VERBOSE=verbose) ## set up indices ib_data = r_[arange(BUS_AREA + 1), arange(BASE_KV, VMIN + 1)] ib_voltage = arange(VM, VA + 1) ib_lam = arange(LAM_P, LAM_Q + 1) ib_mu = arange(MU_VMAX, MU_VMIN + 1) ig_data = r_[[GEN_BUS, QMAX, QMIN], arange(MBASE, APF + 1)] ig_disp = array([PG, QG, VG]) ig_mu = arange(MU_PMAX, MU_QMIN + 1) ibr_data = arange(ANGMAX + 1) ibr_flow = arange(PF, QT + 1) ibr_mu = array([MU_SF, MU_ST]) ibr_angmu = array([MU_ANGMIN, MU_ANGMAX]) ## load case ppc0 = loadcase(casefile) del ppc0['dclinecost'] ppc = ppc0 ppc = toggle_dcline(ppc, 'on') ppc = toggle_dcline(ppc, 'off') ndc = ppc['dcline'].shape[0] ## run AC OPF w/o DC lines t = ''.join([t0, 'AC OPF (no DC lines) : ']) r0 = runopf(ppc0, ppopt) success = r0['success'] t_ok(success, [t, 'success']) r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) t_is(r['f'], r0['f'], 8, [t, 'f']) t_is( r['bus'][:,ib_data ], r0['bus'][:,ib_data ], 10, [t, 'bus data']) t_is( r['bus'][:,ib_voltage], r0['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( r['bus'][:,ib_lam ], r0['bus'][:,ib_lam ], 3, [t, 'bus lambda']) t_is( r['bus'][:,ib_mu ], r0['bus'][:,ib_mu ], 2, [t, 'bus mu']) t_is( r['gen'][:,ig_data ], r0['gen'][:,ig_data ], 10, [t, 'gen data']) t_is( r['gen'][:,ig_disp ], r0['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is( r['gen'][:,ig_mu ], r0['gen'][:,ig_mu ], 3, [t, 'gen mu']) t_is(r['branch'][:,ibr_data ], r0['branch'][:,ibr_data ], 10, [t, 'branch data']) t_is(r['branch'][:,ibr_flow ], r0['branch'][:,ibr_flow ], 3, [t, 'branch flow']) t_is(r['branch'][:,ibr_mu ], r0['branch'][:,ibr_mu ], 2, [t, 'branch mu']) t = ''.join([t0, 'AC PF (no DC lines) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1['bus'][:, VM] = 1 ppc1['bus'][:, VA] = 0 rp = runpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## run with DC lines t = ''.join([t0, 'AC OPF (with DC lines) : ']) ppc = toggle_dcline(ppc, 'on') r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) expected = array([ [10, 8.9, -10, 10, 1.0674, 1.0935], [2.2776, 2.2776, 0, 0, 1.0818, 1.0665], [0, 0, 0, 0, 1.0000, 1.0000], [10, 9.5, 0.0563, -10, 1.0778, 1.0665] ]) t_is(r['dcline'][:, c.PF:c.VT + 1], expected, 4, [t, 'P Q V']) expected = array([ [0, 0.8490, 0.6165, 0, 0, 0.2938], [0, 0, 0, 0.4290, 0.0739, 0], [0, 0, 0, 0, 0, 0], [0, 7.2209, 0, 0, 0.0739, 0] ]) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected, 3, [t, 'mu']) t = ''.join([t0, 'AC PF (with DC lines) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1 = toggle_dcline(ppc1, 'on') ppc1['bus'][:, VM] = 1 ppc1['bus'][:, VA] = 0 rp = runpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) #t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is( rp['gen'][:2,ig_disp ], r['gen'][:2,ig_disp ], 3, [t, 'gen dispatch']) t_is( rp['gen'][2,PG ], r['gen'][2,PG ], 3, [t, 'gen dispatch']) t_is( rp['gen'][2,QG]+rp['dcline'][0,c.QF], r['gen'][2,QG]+r['dcline'][0,c.QF], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## add appropriate P and Q injections and check angles and generation when running PF t = ''.join([t0, 'AC PF (with equivalent injections) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1['bus'][:, VM] = 1 ppc1['bus'][:, VA] = 0 for k in range(ndc): if ppc1['dcline'][k, c.BR_STATUS]: ff = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.F_BUS]) tt = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.T_BUS]) ppc1['bus'][ff, PD] = ppc1['bus'][ff, PD] + r['dcline'][k, c.PF] ppc1['bus'][ff, QD] = ppc1['bus'][ff, QD] - r['dcline'][k, c.QF] ppc1['bus'][tt, PD] = ppc1['bus'][tt, PD] - r['dcline'][k, c.PT] ppc1['bus'][tt, QD] = ppc1['bus'][tt, QD] - r['dcline'][k, c.QT] ppc1['bus'][ff, VM] = r['dcline'][k, c.VF] ppc1['bus'][tt, VM] = r['dcline'][k, c.VT] ppc1['bus'][ff, BUS_TYPE] = PV ppc1['bus'][tt, BUS_TYPE] = PV rp = runpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## test DC OPF t = ''.join([t0, 'DC OPF (with DC lines) : ']) ppc = ppc0.copy() ppc['gen'][0, PMIN] = 10 ppc['branch'][4, RATE_A] = 100 ppc = toggle_dcline(ppc, 'on') r = rundcopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) expected = array([ [10, 8.9, 0, 0, 1.01, 1], [2, 2, 0, 0, 1, 1], [0, 0, 0, 0, 1, 1], [10, 9.5, 0, 0, 1, 0.98] ]) t_is(r['dcline'][:, c.PF:c.VT + 1], expected, 4, [t, 'P Q V']) expected = array([ [0, 1.8602, 0, 0, 0, 0], [1.8507, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0.2681, 0, 0, 0, 0] ]) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected, 3, [t, 'mu']) t = ''.join([t0, 'DC PF (with DC lines) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1 = toggle_dcline(ppc1, 'on') ppc1['bus'][:, VA] = 0 rp = rundcpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## add appropriate P injections and check angles and generation when running PF t = ''.join([t0, 'DC PF (with equivalent injections) : ']) ppc1 = {'baseMVA': r['baseMVA'], 'bus': r['bus'][:, :VMIN + 1].copy(), 'gen': r['gen'][:, :APF + 1].copy(), 'branch': r['branch'][:, :ANGMAX + 1].copy(), 'gencost': r['gencost'].copy(), 'dcline': r['dcline'][:, :c.LOSS1 + 1].copy()} ppc1['bus'][:, VA] = 0 for k in range(ndc): if ppc1['dcline'][k, c.BR_STATUS]: ff = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.F_BUS]) tt = find(ppc1['bus'][:, BUS_I] == ppc1['dcline'][k, c.T_BUS]) ppc1['bus'][ff, PD] = ppc1['bus'][ff, PD] + r['dcline'][k, c.PF] ppc1['bus'][tt, PD] = ppc1['bus'][tt, PD] - r['dcline'][k, c.PT] ppc1['bus'][ff, BUS_TYPE] = PV ppc1['bus'][tt, BUS_TYPE] = PV rp = rundcpf(ppc1, ppopt) success = rp['success'] t_ok(success, [t, 'success']) t_is( rp['bus'][:,ib_voltage], r['bus'][:,ib_voltage], 3, [t, 'bus voltage']) t_is( rp['gen'][:,ig_disp ], r['gen'][:,ig_disp ], 3, [t, 'gen dispatch']) t_is(rp['branch'][:,ibr_flow ], r['branch'][:,ibr_flow ], 3, [t, 'branch flow']) ## run with DC lines t = ''.join([t0, 'AC OPF (with DC lines + poly cost) : ']) ppc = loadcase(casefile) ppc = toggle_dcline(ppc, 'on') r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) expected1 = array([ [10, 8.9, -10, 10, 1.0663, 1.0936], [7.8429, 7.8429, 0, 0, 1.0809, 1.0667], [0, 0, 0, 0, 1.0000, 1.0000], [6.0549, 5.7522, -0.5897, -10, 1.0778, 1.0667] ]) t_is(r['dcline'][:, c.PF:c.VT + 1], expected1, 4, [t, 'P Q V']) expected2 = array([ [0, 0.7605, 0.6226, 0, 0, 0.2980], [0, 0, 0, 0.4275, 0.0792, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0.0792, 0] ]) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected2, 3, [t, 'mu']) ppc['dclinecost'][3, :8] = array([2, 0, 0, 4, 0, 0, 7.3, 0]) r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) t_is(r['dcline'][:, c.PF:c.VT + 1], expected1, 4, [t, 'P Q V']) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected2, 3, [t, 'mu']) t = ''.join([t0, 'AC OPF (with DC lines + pwl cost) : ']) ppc['dclinecost'][3, :8] = array([1, 0, 0, 2, 0, 0, 10, 73]) r = runopf(ppc, ppopt) success = r['success'] t_ok(success, [t, 'success']) t_is(r['dcline'][:, c.PF:c.VT + 1], expected1, 4, [t, 'P Q V']) t_is(r['dcline'][:, c.MU_PMIN:c.MU_QMAXT + 1], expected2, 3, [t, 'mu']) t_end()
def t_pf(quiet=False): """Tests for power flow solvers. @author: Ray Zimmerman (PSERC Cornell) """ t_begin(33, quiet) tdir = dirname(__file__) casefile = join(tdir, 't_case9_pf') verbose = not quiet ppopt = ppoption(VERBOSE=verbose, OUT_ALL=0) ## get solved AC power flow case from MAT-file ## defines bus_soln, gen_soln, branch_soln soln9_pf = loadmat(join(tdir, 'soln9_pf.mat'), struct_as_record=False) bus_soln = soln9_pf['bus_soln'] gen_soln = soln9_pf['gen_soln'] branch_soln = soln9_pf['branch_soln'] ## run Newton PF t = 'Newton PF : ' ppopt = ppoption(ppopt, PF_ALG=1) results, success = runpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 6, [t, 'bus']) t_is(gen, gen_soln, 6, [t, 'gen']) t_is(branch, branch_soln, 6, [t, 'branch']) ## run fast-decoupled PF (XB version) t = 'Fast Decoupled (XB) PF : ' ppopt = ppoption(ppopt, PF_ALG=2) results, success = runpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 6, [t, 'bus']) t_is(gen, gen_soln, 6, [t, 'gen']) t_is(branch, branch_soln, 6, [t, 'branch']) ## run fast-decoupled PF (BX version) t = 'Fast Decoupled (BX) PF : ' ppopt = ppoption(ppopt, PF_ALG=3) results, success = runpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 6, [t, 'bus']) t_is(gen, gen_soln, 6, [t, 'gen']) t_is(branch, branch_soln, 6, [t, 'branch']) ## run Gauss-Seidel PF t = 'Gauss-Seidel PF : ' ppopt = ppoption(ppopt, PF_ALG=4) results, success = runpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 5, [t, 'bus']) t_is(gen, gen_soln, 5, [t, 'gen']) t_is(branch, branch_soln, 5, [t, 'branch']) ## get solved AC power flow case from MAT-file ## defines bus_soln, gen_soln, branch_soln soln9_dcpf = loadmat(join(tdir, 'soln9_dcpf.mat'), struct_as_record=False) bus_soln = soln9_dcpf['bus_soln'] gen_soln = soln9_dcpf['gen_soln'] branch_soln = soln9_dcpf['branch_soln'] ## run DC PF t = 'DC PF : ' results, success = rundcpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 6, [t, 'bus']) t_is(gen, gen_soln, 6, [t, 'gen']) t_is(branch, branch_soln, 6, [t, 'branch']) ## check Qg distribution, when Qmin = Qmax t = 'check Qg : ' ppopt = ppoption(ppopt, PF_ALG=1, VERBOSE=0) ppc = loadcase(casefile) ppc['gen'][0, [QMIN, QMAX]] = [20, 20] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0, QG], 24.07, 2, [t, 'single gen, Qmin = Qmax']) ppc['gen'] = r_[array([ppc['gen'][0, :]]), ppc['gen']] ppc['gen'][0, [QMIN, QMAX]] = [10, 10] ppc['gen'][1, [QMIN, QMAX]] = [0, 50] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0:2, QG], [10, 14.07], 2, [t, '2 gens, Qmin = Qmax for one']) ppc['gen'][0, [QMIN, QMAX]] = [10, 10] ppc['gen'][1, [QMIN, QMAX]] = [-50, -50] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0:2, QG], [12.03, 12.03], 2, [t, '2 gens, Qmin = Qmax for both']) ppc['gen'][0, [QMIN, QMAX]] = [0, 50] ppc['gen'][1, [QMIN, QMAX]] = [0, 100] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0:2, QG], [8.02, 16.05], 2, [t, '2 gens, proportional']) ppc['gen'][0, [QMIN, QMAX]] = [-50, 0] ppc['gen'][1, [QMIN, QMAX]] = [50, 150] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0:2, QG], [-50 + 8.02, 50 + 16.05], 2, [t, '2 gens, proportional']) ## network with islands t = 'network w/islands : DC PF : ' ppc0 = loadcase(casefile) ppc0['gen'][0, PG] = 60 ppc0['gen'][0, [PMIN, PMAX, QMIN, QMAX, PG, QG]] = \ ppc0['gen'][0, [PMIN, PMAX, QMIN, QMAX, PG, QG]] / 2 ppc0['gen'] = r_[array([ppc0['gen'][0, :]]), ppc0['gen']] ppc1 = ppc0.copy() ppc = ppc0.copy() nb = ppc['bus'].shape[0] ppc1['bus'][:, BUS_I] = ppc1['bus'][:, BUS_I] + nb ppc1['branch'][:, F_BUS] = ppc1['branch'][:, F_BUS] + nb ppc1['branch'][:, T_BUS] = ppc1['branch'][:, T_BUS] + nb ppc1['gen'][:, GEN_BUS] = ppc1['gen'][:, GEN_BUS] + nb ppc['bus'] = r_[ppc['bus'], ppc1['bus']] ppc['branch'] = r_[ppc['branch'], ppc1['branch']] ppc['gen'] = r_[ppc['gen'], ppc1['gen']] #ppopt = ppoption(ppopt, OUT_BUS=1, OUT_GEN=1, OUT_ALL=-1, VERBOSE=2) ppopt = ppoption(ppopt, VERBOSE=verbose) r = rundcpf(ppc, ppopt) t_is(r['bus'][:9, VA], bus_soln[:, VA], 8, [t, 'voltage angles 1']) t_is(r['bus'][10:18, VA], bus_soln[:, VA], 8, [t, 'voltage angles 2']) Pg = r_[gen_soln[0, PG] - 30, 30, gen_soln[1:3, PG]] t_is(r['gen'][:4, PG], Pg, 8, [t, 'active power generation 1']) t_is(r['gen'][4:8, PG], Pg, 8, [t, 'active power generation 1']) t = 'network w/islands : AC PF : ' ## get solved AC power flow case from MAT-file soln9_pf = loadmat(join(tdir, 'soln9_pf.mat'), struct_as_record=False) bus_soln = soln9_pf['bus_soln'] gen_soln = soln9_pf['gen_soln'] branch_soln = soln9_pf['branch_soln'] r = runpf(ppc, ppopt) t_is(r['bus'][:9, VA], bus_soln[:, VA], 8, [t, 'voltage angles 1']) t_is(r['bus'][9:18, VA], bus_soln[:, VA], 8, [t, 'voltage angles 2']) Pg = r_[gen_soln[0, PG] - 30, 30, gen_soln[1:3, PG]] t_is(r['gen'][:4, PG], Pg, 8, [t, 'active power generation 1']) t_is(r['gen'][4:8, PG], Pg, 8, [t, 'active power generation 1']) t_end()
def main(train_case, storing_dir): # Define the parameters of the training. # More informations about the distributions used during training can be found # in the notebook Sampling random power networks. train_params = { 'num_samples': 1000, 'learning_rate': 3e-3, } # Define the parameters of the model. model_params = { 'num_updates': 30, #10, 'latent_dim': 10, 'latent_layers': 2 #3 } noise_params = { 'is_noise_active': 1., 'r_min_coeff': -0.1, 'r_max_coeff': +0.1, 'x_min_coeff': -0.1, 'x_max_coeff': +0.1, 'b_min_coeff': -0.1, 'b_max_coeff': +0.1, 'ratio_min_coeff': -0.2, 'ratio_max_coeff': +0.2, 'angle_min_offset': -0.2, 'angle_max_offset': +0.2, 'Vg_min': 0.95, 'Vg_max': 1.05, 'Pd_min_coeff': -0.5, 'Pd_max_coeff': +0.5, 'Qd_min_coeff': -0.5, 'Qd_max_coeff': +0.5, 'Gs_min_coeff': -0., 'Gs_max_coeff': +0., 'Bs_min_coeff': -0., 'Bs_max_coeff': +0., } TEST_SIZE = 100 LEARN_ITER = 10 config = tf.ConfigProto() config.gpu_options.allow_growth = True config.allow_soft_placement = True config.log_device_placement = False case_list = ['case9', 'case14', 'case30', 'case118'] error_NR_list = {} error_DC_list = {} error_GNS_list = {} time_NR_list = {} time_DC_list = {} time_GNS_list = {} timestr = time.strftime("%Y-%m-%d-%Hh%Mm%Ss") expe_dir = 'expe_' + train_case + '_' + timestr try: os.stat(os.path.join(path, expe_dir)) except: os.mkdir(os.path.join(path, expe_dir)) print("Building model that will be trained on " + train_case) tf.reset_default_graph() sess = tf.Session(config=config) expe_path = os.path.join(path, expe_dir) with tf.variable_scope('GNS', reuse=tf.AUTO_REUSE): model = GNS(train_case, train_params, model_params, noise_params, expe_path, sess) sess.run(tf.global_variables_initializer()) sess.run(tf.local_variables_initializer()) print(" Learning on " + train_case + "...") for learn_iter in tqdm.tqdm(range(LEARN_ITER)): # Apply a gradient descent sess.run(model.opt_op) # Store the loss model.store_summary(learn_iter) print(" The model trained on " + train_case + " is ready to be tested!") all_p_nr = {} all_p_dc = {} all_p_gns = {} time_NR_list = {} time_DC_list = {} time_GNS_list = {} for test_case in case_list: print(" Testing on " + test_case + "...") model.change_default_power_grid(case=test_case) # Sample all_p_nr[test_case] = None all_p_dc[test_case] = None all_p_gns[test_case] = None time_NR_list[test_case] = [] time_DC_list[test_case] = [] time_GNS_list[test_case] = [] i = 0 while i < TEST_SIZE: print(i) # Sample a power grid gens, buses, lines, p_gen, pft, ptf, qft, qtf = sess.run([ model.gens, model.buses, model.lines, model.p_gen, model.p_from_to[str(model_params['num_updates'])], model.p_to_from[str(model_params['num_updates'])], model.q_from_to[str(model_params['num_updates'])], model.q_to_from[str(model_params['num_updates'])], ]) sample_id = 0 model.input_data['gen'][:, 1] = p_gen[str( model_params['num_updates'])][sample_id] * 100 model.input_data['gen'][:, 5] = gens['Vg'][sample_id] model.input_data['bus'][:, 2] = buses['Pd'][sample_id] model.input_data['bus'][:, 3] = buses['Qd'][sample_id] model.input_data['bus'][:, 4] = buses['Gs'][sample_id] model.input_data['bus'][:, 5] = buses['Bs'][sample_id] model.input_data['branch'][:, 2] = lines['r'][sample_id] model.input_data['branch'][:, 3] = lines['x'][sample_id] model.input_data['branch'][:, 4] = lines['b'][sample_id] model.input_data['branch'][:, 8] = lines['ratio'][sample_id] model.input_data[ 'branch'][:, 9] = lines['angle'][sample_id] * 180 / np.pi i += 1 try: start = time.time() a_nr = runpf(model.input_data) time_NR_list[test_case].append(time.time() - start) except: print('did not converge') i -= 1 continue if a_nr[0]['success'] == 0: print('did not converge') i -= 1 continue # Now try with DC approx start = time.time() a_dc = rundcpf(model.input_data) time_DC_list[test_case].append(time.time() - start) # Get flows from GNS p_from_gns = pft[sample_id] * 100 p_to_gns = ptf[sample_id] * 100 q_from_gns = qft[sample_id] * 100 q_to_gns = qtf[sample_id] * 100 if all_p_gns[test_case] is None: all_p_gns[test_case] = np.r_[p_from_gns, p_to_gns] else: all_p_gns[test_case] = np.c_[all_p_gns[test_case], np.r_[p_from_gns, p_to_gns]] # Get flows from Newton-Raphson p_from_nr = a_nr[0]['branch'][:, 13] q_from_nr = a_nr[0]['branch'][:, 14] p_to_nr = a_nr[0]['branch'][:, 15] q_to_nr = a_nr[0]['branch'][:, 16] if all_p_nr[test_case] is None: all_p_nr[test_case] = np.r_[p_from_nr, p_to_nr] else: all_p_nr[test_case] = np.c_[all_p_nr[test_case], np.r_[p_from_nr, p_to_nr]] # Get flows from DC approx p_from_dc = a_dc[0]['branch'][:, 13] q_from_dc = a_dc[0]['branch'][:, 14] p_to_dc = a_dc[0]['branch'][:, 15] q_to_dc = a_dc[0]['branch'][:, 16] if all_p_dc[test_case] is None: all_p_dc[test_case] = np.r_[p_from_dc, p_to_dc] else: all_p_dc[test_case] = np.c_[all_p_dc[test_case], np.r_[p_from_dc, p_to_dc]] # Now try with GNS v_time, theta_time = sess.run([model.v, model.theta]) start = time.time() v_time, theta_time = sess.run([model.v, model.theta]) time_GNS_list[test_case].append( (time.time() - start) / train_params['num_samples']) np.save(os.path.join(storing_dir, expe_dir + "_all_p_nr.npy"), all_p_nr) np.save(os.path.join(storing_dir, expe_dir + "_all_p_gns.npy"), all_p_gns) np.save(os.path.join(storing_dir, expe_dir + "_all_p_dc.npy"), all_p_dc) np.save(os.path.join(storing_dir, expe_dir + "_time_nr.npy"), time_NR_list) np.save(os.path.join(storing_dir, expe_dir + "_time_gns.npy"), time_GNS_list) np.save(os.path.join(storing_dir, expe_dir + "_time_dc.npy"), time_DC_list)
def run_pf(self, power_factor, power): '''Run the power flow. This is repeated for each power bin contained in the power factor data structure. Args: power_factor (list) [-]: List of tuples, val1 = array power output in put, val2 = power factor. power (float) [W]: Rated power of oec. Attributes: ppopt (dict) [-]: PyPower options. ppc (dict) [-]: PyPower case definition. This contains all newtork information. result (dict) [-]: PyPower results. all_success (list) [int]: Binary flag indicating if power solved, 1 = successful solution, 0 = unsuccessful solution. all_active_powers (list) [MW]: Active power delivered onshore. all_reactive_powers (list) [MVAr]: Reactive power delivered onshore. all_busbar_voltage (list) [pu]: Busbar voltage magnitudes. all_busbar_angle (list) [deg]: Busbar voltage angles. Returns: Note: The power flow is repeated for each defined power level. The length of the lists above should equal this length. ''' ppopt = ppoption.ppoption(VERBOSE=0, OUT_ALL=0) ppc = {"version": '2'} ppc["baseMVA"] = 100.0 ppc["bus"] = self.bus_data ppc["branch"] = self.branch_data all_active_powers = [] all_reactive_powers = [] all_success = [] all_busbar_voltage = [] all_busbar_angle = [] gen_active_powers = [] gen_reactive_powers = [] # for each generator output for output_power in power_factor: gen_power = output_power[0] * power/1000000 # power in MW gen_reactive_power = gen_power * np.tan(np.arccos(output_power[1])) for gen in range(1, self.n_devices+1): self.gen_data[gen][1] = gen_power self.gen_data[gen][2] = gen_reactive_power ppc["gen"] = self.gen_data result, success = runpf.runpf(ppc, ppopt) all_active_powers.append(result['gen'][0][1]) all_reactive_powers.append(result['gen'][0][2]) all_success.append(success) all_busbar_voltage.append(result['bus'][:,7]) all_busbar_angle.append(result['bus'][:,8]) gen_active_powers.append(result['gen'][:,1][1:]) gen_reactive_powers.append(result['gen'][:,2][1:]) self.flag = all_success self.onshore_active_power = all_active_powers self.onshore_reactive_power = all_reactive_powers self.busbar_voltages = all_busbar_voltage self.busbar_angles = all_busbar_angle self.gen_active_powers = gen_active_powers self.gen_reactive_powers = gen_reactive_powers self.all_results = result return result
def validate_from_ppc( ppc_net, net, pf_type="runpp", max_diff_values={ "bus_vm_pu": 1e-6, "bus_va_degree": 1e-5, "branch_p_mw": 1e-6, "branch_q_mvar": 1e-6, "gen_p_mw": 1e-6, "gen_q_mvar": 1e-6 }, run=True): """ This function validates the pypower case files to pandapower net structure conversion via a \ comparison of loadflow calculation results. (Hence the opf cost conversion is not validated.) INPUT: **ppc_net** - The pypower case file, which must already contain the pypower powerflow results or pypower must be importable. **net** - The pandapower network. OPTIONAL: **pf_type** ("runpp", string) - Type of validated power flow. Possible are ("runpp", "rundcpp", "runopp", "rundcopp") **max_diff_values** - Dict of maximal allowed difference values. The keys must be 'vm_pu', 'va_degree', 'p_branch_mw', 'q_branch_mvar', 'p_gen_mw' and 'q_gen_mvar' and the values floats. **run** (True, bool or list of two bools) - changing the value to False avoids trying to run (optimal) loadflows. Giving a list of two bools addresses first pypower and second pandapower. OUTPUT: **conversion_success** - conversion_success is returned as False if pypower or pandapower cannot calculate a powerflow or if the maximum difference values (max_diff_values ) cannot be hold. EXAMPLE: import pandapower.converter as pc net = cv.from_ppc(ppc_net, f_hz=50) conversion_success = cv.validate_from_ppc(ppc_net, net) NOTE: The user has to take care that the loadflow results already are included in the provided \ ppc_net or pypower is importable. """ # check in case of optimal powerflow comparison whether cost information exist if "opp" in pf_type: if not (len(net.polynomial_cost) | len(net.piecewise_linear_cost)): if "gencost" in ppc_net: if not len(ppc_net["gencost"]): logger.debug( 'ppc and pandapower net do not include cost information.' ) return True else: logger.error( 'The pandapower net does not include cost information.' ) return False else: logger.debug( 'ppc and pandapower net do not include cost information.') return True # guarantee run parameter as list, for pypower and pandapower (optimal) powerflow run run = [run, run] if isinstance(run, bool) else run # --- check pypower powerflow success, if possible if pypower_import and run[0]: try: if pf_type == "runpp": ppc_net = runpf.runpf(ppc_net, ppopt)[0] elif pf_type == "rundcpp": ppc_net = rundcpf.rundcpf(ppc_net, ppopt)[0] elif pf_type == "runopp": ppc_net = runopf.runopf(ppc_net, ppopt) elif pf_type == "rundcopp": ppc_net = rundcopf.rundcopf(ppc_net, ppopt) else: raise ValueError("The pf_type %s is unknown" % pf_type) except: logger.debug("The pypower run did not work.") ppc_success = True if 'success' in ppc_net.keys(): if ppc_net['success'] != 1: ppc_success = False logger.error( "The given ppc data indicates an unsuccessful pypower powerflow: " + "'ppc_net['success'] != 1'") if (ppc_net['branch'].shape[1] < 17): ppc_success = False logger.error( "The shape of given ppc data indicates missing pypower powerflow results." ) # --- try to run a pandapower powerflow if run[1]: if pf_type == "runpp": try: pp.runpp(net, init="dc", calculate_voltage_angles=True, trafo_model="pi") except pp.LoadflowNotConverged: try: pp.runpp(net, calculate_voltage_angles=True, init="flat", trafo_model="pi") except pp.LoadflowNotConverged: try: pp.runpp(net, trafo_model="pi", calculate_voltage_angles=False) if "bus_va_degree" in max_diff_values.keys(): max_diff_values[ "bus_va_degree"] = 1e2 if max_diff_values[ "bus_va_degree"] < 1e2 else max_diff_values[ "bus_va_degree"] logger.info("voltage_angles could be calculated.") except pp.LoadflowNotConverged: logger.error( 'The pandapower powerflow does not converge.') elif pf_type == "rundcpp": try: pp.rundcpp(net, trafo_model="pi") except pp.LoadflowNotConverged: logger.error('The pandapower dc powerflow does not converge.') elif pf_type == "runopp": try: pp.runopp(net, init="flat", calculate_voltage_angles=True) except pp.OPFNotConverged: try: pp.runopp(net, init="pf", calculate_voltage_angles=True) except (pp.OPFNotConverged, pp.LoadflowNotConverged, KeyError): try: pp.runopp(net, init="flat", calculate_voltage_angles=False) logger.info("voltage_angles could be calculated.") if "bus_va_degree" in max_diff_values.keys(): max_diff_values[ "bus_va_degree"] = 1e2 if max_diff_values[ "bus_va_degree"] < 1e2 else max_diff_values[ "bus_va_degree"] except pp.OPFNotConverged: try: pp.runopp(net, init="pf", calculate_voltage_angles=False) if "bus_va_degree" in max_diff_values.keys(): max_diff_values[ "bus_va_degree"] = 1e2 if max_diff_values[ "bus_va_degree"] < 1e2 else max_diff_values[ "bus_va_degree"] logger.info("voltage_angles could be calculated.") except (pp.OPFNotConverged, pp.LoadflowNotConverged, KeyError): logger.error( 'The pandapower optimal powerflow does not converge.' ) elif pf_type == "rundcopp": try: pp.rundcopp(net) except pp.LoadflowNotConverged: logger.error( 'The pandapower dc optimal powerflow does not converge.') else: raise ValueError("The pf_type %s is unknown" % pf_type) # --- prepare powerflow result comparison by reordering pp results as they are in ppc results if not ppc_success: return False if "opp" in pf_type: if not net.OPF_converged: return elif not net.converged: return False # --- store pypower powerflow results ppc_res = dict.fromkeys(ppc_elms) ppc_res["branch"] = ppc_net['branch'][:, 13:17] ppc_res["bus"] = ppc_net['bus'][:, 7:9] ppc_res["gen"] = ppc_net['gen'][:, 1:3] # --- pandapower bus result table pp_res = dict.fromkeys(ppc_elms) pp_res["bus"] = array(net.res_bus.sort_index()[['vm_pu', 'va_degree']]) # --- pandapower gen result table pp_res["gen"] = zeros([1, 2]) # consideration of parallel generators via storing how much generators have been considered # each node # if in ppc is only one gen -> numpy initially uses one dim array -> change to two dim array if len(ppc_net["gen"].shape) == 1: ppc_net["gen"] = array(ppc_net["gen"], ndmin=2) GENS = DataFrame(ppc_net['gen'][:, [0]].astype(int)) GEN_uniq = GENS.drop_duplicates() already_used_gen = Series(zeros(GEN_uniq.shape[0]).astype(int), index=[int(v) for v in GEN_uniq.values]) change_q_compare = [] for i, j in GENS.iterrows(): current_bus_type, current_bus_idx, same_bus_gen_idx, first_same_bus_in_service_gen_idx, \ last_same_bus_in_service_gen_idx = _gen_bus_info(ppc_net, i) if current_bus_type == 3 and i == first_same_bus_in_service_gen_idx: pp_res["gen"] = append( pp_res["gen"], array(net.res_ext_grid[net.ext_grid.bus == current_bus_idx][[ 'p_mw', 'q_mvar' ]]).reshape((1, 2)), 0) elif current_bus_type == 2 and i == first_same_bus_in_service_gen_idx: pp_res["gen"] = append( pp_res["gen"], array(net.res_gen[net.gen.bus == current_bus_idx][[ 'p_mw', 'q_mvar' ]]).reshape((1, 2)), 0) else: pp_res["gen"] = append( pp_res["gen"], array(net.res_sgen[net.sgen.bus == current_bus_idx][[ 'p_mw', 'q_mvar' ]])[already_used_gen.at[int(j)]].reshape((1, 2)), 0) already_used_gen.at[int(j)] += 1 change_q_compare += [int(j)] pp_res["gen"] = pp_res["gen"][1:, :] # delete initial zero row # --- pandapower branch result table pp_res["branch"] = zeros([1, 4]) # consideration of parallel branches via storing how often branches were considered # each node-to-node-connection try: init1 = concat([net.line.from_bus, net.line.to_bus], axis=1, sort=True).drop_duplicates() init2 = concat([net.trafo.hv_bus, net.trafo.lv_bus], axis=1, sort=True).drop_duplicates() except TypeError: # legacy pandas < 0.21 init1 = concat([net.line.from_bus, net.line.to_bus], axis=1).drop_duplicates() init2 = concat([net.trafo.hv_bus, net.trafo.lv_bus], axis=1).drop_duplicates() init1['hv_bus'] = nan init1['lv_bus'] = nan init2['from_bus'] = nan init2['to_bus'] = nan try: already_used_branches = concat([init1, init2], axis=0, sort=True) except TypeError: # pandas < 0.21 legacy already_used_branches = concat([init1, init2], axis=0) already_used_branches['number'] = zeros( [already_used_branches.shape[0], 1]).astype(int) BRANCHES = DataFrame(ppc_net['branch'][:, [0, 1, 8, 9]]) for i in BRANCHES.index: from_bus = pp.get_element_index(net, 'bus', name=int(ppc_net['branch'][i, 0])) to_bus = pp.get_element_index(net, 'bus', name=int(ppc_net['branch'][i, 1])) from_vn_kv = ppc_net['bus'][from_bus, 9] to_vn_kv = ppc_net['bus'][to_bus, 9] ratio = BRANCHES[2].at[i] angle = BRANCHES[3].at[i] # from line results if (from_vn_kv == to_vn_kv) & ((ratio == 0) | (ratio == 1)) & (angle == 0): pp_res["branch"] = append( pp_res["branch"], array(net.res_line[(net.line.from_bus == from_bus) & (net.line.to_bus == to_bus)][[ 'p_from_mw', 'q_from_mvar', 'p_to_mw', 'q_to_mvar' ]]) [int(already_used_branches.number.loc[ (already_used_branches.from_bus == from_bus) & (already_used_branches.to_bus == to_bus)].values)].reshape( 1, 4), 0) already_used_branches.number.loc[ (already_used_branches.from_bus == from_bus) & (already_used_branches.to_bus == to_bus)] += 1 # from trafo results else: if from_vn_kv >= to_vn_kv: pp_res["branch"] = append( pp_res["branch"], array(net.res_trafo[(net.trafo.hv_bus == from_bus) & (net.trafo.lv_bus == to_bus)] [['p_hv_mw', 'q_hv_mvar', 'p_lv_mw', 'q_lv_mvar' ]])[int(already_used_branches.number.loc[ (already_used_branches.hv_bus == from_bus) & (already_used_branches.lv_bus == to_bus)]. values)].reshape(1, 4), 0) already_used_branches.number.loc[ (already_used_branches.hv_bus == from_bus) & (already_used_branches.lv_bus == to_bus)] += 1 else: # switch hv-lv-connection of pypower connection buses pp_res["branch"] = append( pp_res["branch"], array(net.res_trafo[(net.trafo.hv_bus == to_bus) & (net.trafo.lv_bus == from_bus)] [['p_lv_mw', 'q_lv_mvar', 'p_hv_mw', 'q_hv_mvar' ]])[int(already_used_branches.number.loc[ (already_used_branches.hv_bus == to_bus) & (already_used_branches.lv_bus == from_bus)]. values)].reshape(1, 4), 0) already_used_branches.number.loc[ (already_used_branches.hv_bus == to_bus) & (already_used_branches.lv_bus == from_bus)] += 1 pp_res["branch"] = pp_res["branch"][1:, :] # delete initial zero row # --- do the powerflow result comparison diff_res = dict.fromkeys(ppc_elms) diff_res["bus"] = ppc_res["bus"] - pp_res["bus"] diff_res["bus"][:, 1] -= diff_res["bus"][0, 1] # remove va_degree offset diff_res["branch"] = ppc_res["branch"] - pp_res["branch"] diff_res["gen"] = ppc_res["gen"] - pp_res["gen"] # comparison of buses with several generator units only as q sum for i in GEN_uniq.loc[GEN_uniq[0].isin(change_q_compare)].index: next_is = GEN_uniq.index[GEN_uniq.index > i] if len(next_is) > 0: next_i = next_is[0] else: next_i = GENS.index[-1] + 1 if (next_i - i) > 1: diff_res["gen"][i:next_i, 1] = sum(diff_res["gen"][i:next_i, 1]) # logger info logger.debug( "Maximum voltage magnitude difference between pypower and pandapower: " "%.2e pu" % max_(abs(diff_res["bus"][:, 0]))) logger.debug( "Maximum voltage angle difference between pypower and pandapower: " "%.2e degree" % max_(abs(diff_res["bus"][:, 1]))) logger.debug( "Maximum branch flow active power difference between pypower and pandapower: " "%.2e MW" % max_(abs(diff_res["branch"][:, [0, 2]]))) logger.debug( "Maximum branch flow reactive power difference between pypower and " "pandapower: %.2e MVAr" % max_(abs(diff_res["branch"][:, [1, 3]]))) logger.debug( "Maximum active power generation difference between pypower and pandapower: " "%.2e MW" % max_(abs(diff_res["gen"][:, 0]))) logger.debug( "Maximum reactive power generation difference between pypower and pandapower: " "%.2e MVAr" % max_(abs(diff_res["gen"][:, 1]))) if _validate_diff_res(diff_res, {"bus_vm_pu": 1e-3, "bus_va_degree": 1e-3, "branch_p_mw": 1e-6, "branch_q_mvar": 1e-6}) and \ (max_(abs(diff_res["gen"])) > 1e-1).any(): logger.debug( "The active/reactive power generation difference possibly results " "because of a pypower error. Please validate " "the results via pypower loadflow." ) # this occurs e.g. at ppc case9 # give a return if isinstance(max_diff_values, dict): return _validate_diff_res(diff_res, max_diff_values) else: logger.debug("'max_diff_values' must be a dict.")
def run_sim(ppc, elements, dynopt=None, events=None, recorder=None): """ Run a time-domain simulation Inputs: ppc PYPOWER load flow case elements Dictionary of dynamic model objects (machines, controllers, etc) with Object ID as key events Events object recorder Recorder object (empty) Outputs: recorder Recorder object (with data) """ ######### # SETUP # ######### # Get version information ver = pydyn_ver() print('PYPOWER-Dynamics ' + ver['Version'] + ', ' + ver['Date']) # Program options if dynopt: h = dynopt['h'] t_sim = dynopt['t_sim'] max_err = dynopt['max_err'] max_iter = dynopt['max_iter'] verbose = dynopt['verbose'] else: # Default program options h = 0.01 # step length (s) t_sim = 5 # simulation time (s) max_err = 0.0001 # Maximum error in network iteration (voltage mismatches) max_iter = 25 # Maximum number of network iterations verbose = False if dynopt['sample_period']: sample_rate = max(int(dynopt['sample_period'] / h) - 1, 0) else: sample_rate = 0 # Make lists of current injection sources (generators, external grids, etc) and controllers sources = [] controllers = [] for element in elements.values(): if element.__module__ in [ 'pydyn.sym_order6a', 'pydyn.sym_order6b', 'pydyn.sym_order4', 'pydyn.ext_grid', 'pydyn.vsc_average', 'pydyn.asym_1cage', 'pydyn.asym_2cage' ]: sources.append(element) if element.__module__ == 'pydyn.controller': controllers.append(element) # Set up interfaces interfaces = init_interfaces(elements) interfaces0 = init_interfaces0(elements) # find events events_controllers = [] # find blocks that create events in controllers for element_id in elements.keys(): element = elements[element_id] if element.__module__ == 'pydyn.controller': for line in element.equations: if line[1] == 'EVENT': new_event = [element_id, line[0], line[2]] + line[3:] events_controllers.append(new_event) ################## # INITIALISATION # ################## print('Initialising models...') if not verbose: ppopt = ppoption(VERBOSE=0, OUT_ALL=0) else: ppopt = ppoption() #print('not verbose') # Run power flow and update bus voltages and angles in PYPOWER case object results, success = runpf(ppc, ppopt) ppc["bus"][:, VM] = results["bus"][:, VM] ppc["bus"][:, VA] = results["bus"][:, VA] # Build Ybus matrix ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int[ "branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Build modified Ybus matrix try: Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) except: bp() Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Calculate initial voltage phasors v0 = bus[:, VM] * (np.cos(np.radians(bus[:, VA])) + 1j * np.sin(np.radians(bus[:, VA]))) # Initialise sources from load flow for source in sources: if source.__module__ in ['pydyn.asym_1cage', 'pydyn.asym_2cage']: # Asynchronous machine source_bus = ppc_int['bus'][source.bus_no, 0].astype(np.int64) v_source = v0[source_bus] source.initialise(v_source, 0) else: # Generator or VSC source_bus = ppc_int['gen'][source.gen_no, 0].astype(np.int64) S_source = np.complex(results["gen"][source.gen_no, 1] / baseMVA, results["gen"][source.gen_no, 2] / baseMVA) v_source = v0[source_bus] source.initialise(v_source, S_source) # initialise bus elements['bus'] = bus_int(ppc) elements['sys_matrices'] = sys_matrices_int(ppc) #elements['branch'] = ppc['branch'] # Do we need interfaces0? # Interface controllers and machines (for initialisation) #for intf in interfaces: for k in range(len(interfaces)): intf = interfaces[k] intf0 = interfaces0[k] int_type = intf[0] var_name = intf0[1] source_var = intf[1] source_id = intf[2] dest_var = intf[3] dest_id = intf[4] if int_type == 'OUTPUT': # If an output, interface in the reverse direction for initialisation #intf[2].signals[var_name] = intf[3].signals[var_name] #if (intf0[2] != source_id) or (var_name != source_var) or (var_name != dest_var): # bp() elements[source_id].signals[source_var] = elements[ dest_id].signals[dest_var] else: # Inputs are interfaced in normal direction during initialisation #intf[3].signals[var_name] = intf[2].signals[var_name] #if (intf0[3] != dest_id) or (var_name != source_var) or (var_name != dest_var): # bp() elements[dest_id].signals[dest_var] = elements[source_id].signals[ source_var] #try: # element_source.signals[ var_name_source ] = element_dest.signals[ var_name_dest ] #except: # bp() # Initialise controllers for controller in controllers: controller.initialise() ############# # MAIN LOOP # ############# sample_age = 0 if events == None: print('Warning: no events!') # Factorise Ybus matrix Ybus_inv = splu(Ybus) y1 = [] v_prev = v0 print('Simulating...') for t in range(int(t_sim / h) + 1): if np.mod(t, 1 / h) == 0 and verbose: print('t=' + str(t * h) + 's') # Interface controllers and machines #for intf in interfaces: for k in range(len(interfaces)): intf = interfaces[k] intf0 = interfaces0[k] var_name = intf0[1] source_var = intf[1] source_id = intf[2] dest_var = intf[3] dest_id = intf[4] #if var_name_dest not in element_dest.signals.keys(): #bp() #element_dest.signals[ var_name_dest ] = element_source.signals[ var_name_source ] #if (intf0[2] != source_id) or (var_name != source_var) or (var_name != dest_var) or (intf0[3] != dest_id): # bp() elements[dest_id].signals[dest_var] = elements[source_id].signals[ source_var] #intf[3].signals[var_name] = intf[2].signals[var_name] # Solve differential equations for j in range(4): # Solve step of differential equations for element in elements.values(): try: element.solve_step(h, j) except: bp() element.solve_step(h, j) # Interface with network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) # check for events for event_c in events_controllers: new_event = None ctrl = event_c[0] ctrl_var = event_c[2] var_result = event_c[1] condition = elements[ctrl].signals[ctrl_var] if condition >= 1: #event_type = event_c[0] #node = event_c[1] new_event = [np.round(t * h, 5)] + event_c[3:] #print(new_event) try: events.event_stack.append(new_event) elements[ctrl].signals[var_result] = 1.0 #bp() except: bp() else: elements[ctrl].signals[var_result] = 0.0 if sample_age < sample_rate: sample_age += 1 else: sample_age = 0 if recorder != None: # Record signals or states recorder.record_variables(t * h, elements) if events != None: #if new_event != None: # bp() # Check event stack ppc, refactorise = events.handle_events(np.round(t * h, 5), elements, ppc, baseMVA) if refactorise == True: # Rebuild Ybus from new ppc_int ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int[ "bus"], ppc_int["branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Rebuild modified Ybus Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Refactorise Ybus Ybus_inv = splu(Ybus) # Solve network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) # update the voltage in 'bus' matrix ppc['bus'][:, VM] = abs(v_prev) ppc['bus'][:, VA] = 2 * np.arctan(v_prev.imag / (abs(v_prev) + v_prev.real)) # update the system matrices elements['bus'].update(ppc) elements['sys_matrices'].update(ppc) #bp() return recorder
def t_pf(quiet=False): """Tests for power flow solvers. @author: Ray Zimmerman (PSERC Cornell) """ t_begin(33, quiet) tdir = dirname(__file__) casefile = join(tdir, 't_case9_pf') verbose = not quiet ppopt = ppoption(VERBOSE=verbose, OUT_ALL=0) ## get solved AC power flow case from MAT-file ## defines bus_soln, gen_soln, branch_soln soln9_pf = loadmat(join(tdir, 'soln9_pf.mat'), struct_as_record=False) bus_soln = soln9_pf['bus_soln'] gen_soln = soln9_pf['gen_soln'] branch_soln = soln9_pf['branch_soln'] ## run Newton PF t = 'Newton PF : '; ppopt = ppoption(ppopt, PF_ALG=1) results, success = runpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 6, [t, 'bus']) t_is(gen, gen_soln, 6, [t, 'gen']) t_is(branch, branch_soln, 6, [t, 'branch']) ## run fast-decoupled PF (XB version) t = 'Fast Decoupled (XB) PF : '; ppopt = ppoption(ppopt, PF_ALG=2) results, success = runpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 6, [t, 'bus']) t_is(gen, gen_soln, 6, [t, 'gen']) t_is(branch, branch_soln, 6, [t, 'branch']) ## run fast-decoupled PF (BX version) t = 'Fast Decoupled (BX) PF : '; ppopt = ppoption(ppopt, PF_ALG=3) results, success = runpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 6, [t, 'bus']) t_is(gen, gen_soln, 6, [t, 'gen']) t_is(branch, branch_soln, 6, [t, 'branch']) ## run Gauss-Seidel PF t = 'Gauss-Seidel PF : '; ppopt = ppoption(ppopt, PF_ALG=4) results, success = runpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 5, [t, 'bus']) t_is(gen, gen_soln, 5, [t, 'gen']) t_is(branch, branch_soln, 5, [t, 'branch']) ## get solved AC power flow case from MAT-file ## defines bus_soln, gen_soln, branch_soln soln9_dcpf = loadmat(join(tdir, 'soln9_dcpf.mat'), struct_as_record=False) bus_soln = soln9_dcpf['bus_soln'] gen_soln = soln9_dcpf['gen_soln'] branch_soln = soln9_dcpf['branch_soln'] ## run DC PF t = 'DC PF : ' results, success = rundcpf(casefile, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_ok(success, [t, 'success']) t_is(bus, bus_soln, 6, [t, 'bus']) t_is(gen, gen_soln, 6, [t, 'gen']) t_is(branch, branch_soln, 6, [t, 'branch']) ## check Qg distribution, when Qmin = Qmax t = 'check Qg : ' ppopt = ppoption(ppopt, PF_ALG=1, VERBOSE=0) ppc = loadcase(casefile) ppc['gen'][0, [QMIN, QMAX]] = [20, 20] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0, QG], 24.07, 2, [t, 'single gen, Qmin = Qmax']) ppc['gen'] = r_[array([ ppc['gen'][0, :] ]), ppc['gen']] ppc['gen'][0, [QMIN, QMAX]] = [10, 10] ppc['gen'][1, [QMIN, QMAX]] = [ 0, 50] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0:2, QG], [10, 14.07], 2, [t, '2 gens, Qmin = Qmax for one']) ppc['gen'][0, [QMIN, QMAX]] = [10, 10] ppc['gen'][1, [QMIN, QMAX]] = [-50, -50] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0:2, QG], [12.03, 12.03], 2, [t, '2 gens, Qmin = Qmax for both']) ppc['gen'][0, [QMIN, QMAX]] = [0, 50] ppc['gen'][1, [QMIN, QMAX]] = [0, 100] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0:2, QG], [8.02, 16.05], 2, [t, '2 gens, proportional']) ppc['gen'][0, [QMIN, QMAX]] = [-50, 0] ppc['gen'][1, [QMIN, QMAX]] = [50, 150] results, success = runpf(ppc, ppopt) bus, gen, branch = results['bus'], results['gen'], results['branch'] t_is(gen[0:2, QG], [-50 + 8.02, 50 + 16.05], 2, [t, '2 gens, proportional']) ## network with islands t = 'network w/islands : DC PF : ' ppc0 = loadcase(casefile) ppc0['gen'][0, PG] = 60 ppc0['gen'][0, [PMIN, PMAX, QMIN, QMAX, PG, QG]] = \ ppc0['gen'][0, [PMIN, PMAX, QMIN, QMAX, PG, QG]] / 2 ppc0['gen'] = r_[array([ ppc0['gen'][0, :] ]), ppc0['gen']] ppc1 = ppc0.copy() ppc = ppc0.copy() nb = ppc['bus'].shape[0] ppc1['bus'][:, BUS_I] = ppc1['bus'][:, BUS_I] + nb ppc1['branch'][:, F_BUS] = ppc1['branch'][:, F_BUS] + nb ppc1['branch'][:, T_BUS] = ppc1['branch'][:, T_BUS] + nb ppc1['gen'][:, GEN_BUS] = ppc1['gen'][:, GEN_BUS] + nb ppc['bus'] = r_[ppc['bus'], ppc1['bus']] ppc['branch'] = r_[ppc['branch'], ppc1['branch']] ppc['gen'] = r_[ppc['gen'], ppc1['gen']] #ppopt = ppoption(ppopt, OUT_BUS=1, OUT_GEN=1, OUT_ALL=-1, VERBOSE=2) ppopt = ppoption(ppopt, VERBOSE=verbose) r = rundcpf(ppc, ppopt) t_is(r['bus'][ :9, VA], bus_soln[:, VA], 8, [t, 'voltage angles 1']) t_is(r['bus'][10:18, VA], bus_soln[:, VA], 8, [t, 'voltage angles 2']) Pg = r_[gen_soln[0, PG] - 30, 30, gen_soln[1:3, PG]] t_is(r['gen'][ :4, PG], Pg, 8, [t, 'active power generation 1']) t_is(r['gen'][4:8, PG], Pg, 8, [t, 'active power generation 1']) t = 'network w/islands : AC PF : ' ## get solved AC power flow case from MAT-file soln9_pf = loadmat(join(tdir, 'soln9_pf.mat'), struct_as_record=False) bus_soln = soln9_pf['bus_soln'] gen_soln = soln9_pf['gen_soln'] branch_soln = soln9_pf['branch_soln'] r = runpf(ppc, ppopt) t_is(r['bus'][ :9, VA], bus_soln[:, VA], 8, [t, 'voltage angles 1']) t_is(r['bus'][9:18, VA], bus_soln[:, VA], 8, [t, 'voltage angles 2']) Pg = r_[gen_soln[0, PG] - 30, 30, gen_soln[1:3, PG]] t_is(r['gen'][ :4, PG], Pg, 8, [t, 'active power generation 1']) t_is(r['gen'][4:8, PG], Pg, 8, [t, 'active power generation 1']) t_end()
bus.index = value bus.index = bus.index.astype(int) branch['F_BUS'] = branch['F_BUS'].apply( lambda x: value[bus_name.get_loc(x)]).astype(int) branch['T_BUS'] = branch['T_BUS'].apply( lambda x: value[bus_name.get_loc(x)]).astype(int) gen['GEN_BUS'] = gen['GEN_BUS'].apply( lambda x: value[bus_name.get_loc(x)]).astype(int) bus = np.array(bus.reset_index()) branch = np.array(branch) gen = np.array(gen) gencost = np.array(gencost) casedata = { 'baseMVA': baseMVA, 'gencost': gencost, 'gen': gen, 'branch': branch, 'bus': bus } return runpf(casedata, ppopt=ppopt, fname=fname) def find_violated_lines(original_case_branch, results_case_branch): s = (pd.Series(abs(results_case_branch[:, 13])) > original_case_branch['RATE_A']) & (original_case_branch['RATE_A'] != 0) return s[s == True].index
def FlujosAC(self, Sbase, t_index): for PVAC in self.PVAC: # Actualizar matriz de generación con valores interpolados self.genMatrix[PVAC.indice - 1, 1] = PVAC.P[t_index] / (Sbase * 1000000) self.genMatrix[PVAC.indice - 1, 2] = PVAC.Q[t_index] / (Sbase * 1000000) self.busMatrix = numpy.array([]) self.branchMatrix = numpy.array([]) for bus in self.Terminales: bus.actualizar(t_index, Sbase) self.busMatrix = numpy.concatenate((self.busMatrix, [ bus.ID, bus.Tipo, bus.P, bus.Q, 0, 0, 1, 1, 0, bus.Vnom, 1, 1.1, 0.9 ]), 0) i = 0 for branch in self.Branch: self.branchMatrix = numpy.concatenate((self.branchMatrix, [ branch.Term1.ID, branch.Term2.ID, branch.R, branch.X, branch.Y, branch.Snom, branch.Snom, branch.Snom, branch.Turns, 0, 1, -360, 360 ]), 0) branch.branchindex = i i = i + 1 # Crear caso para cargalo usando PYPOWER y correr flujos AC RedACdict = dict() RedACdict['baseMVA'] = Sbase RedACdict['bus'] = self.busMatrix.reshape((len(self.Terminales), 13)) RedACdict['branch'] = self.branchMatrix.reshape((len(self.Branch), 13)) RedACdict['gen'] = self.genMatrix.reshape((len(self.PVAC) + 1, 21)) # Cargar caso de pypower con matrices actualizadas caseRedAC = loadcase.loadcase(RedACdict) # Correr flujo de potencia con PYPOWER self.ACresults = runpf.runpf(caseRedAC) fecha = datetime.strftime(self.fechas[t_index], '%Y-%m-%d %H:%M:%S') # Guardar resultados de flujos para terminales for bus in self.Terminales: # Recuperar índice de bus en matriz de buses de pypower con resultados busindex = numpy.where( self.ACresults[0]['bus'][:, 0] == bus.ID)[0].item() # Guardar resultados respectivos bus.Results[fecha] = { 'V': self.ACresults[0]['bus'][busindex, 7].item(), 'delta': self.ACresults[0]['bus'][busindex, 8].item(), 'P': self.ACresults[0]['bus'][busindex, 2].item(), 'Q': self.ACresults[0]['bus'][busindex, 3].item() } # Guardar resultados de flujos para líneas y trafos for branch in self.Branch: # Recuperar potencias de entrada y salida de la rama Pf = self.ACresults[0]['branch'][branch.branchindex, 13].item() Qf = self.ACresults[0]['branch'][branch.branchindex, 14].item() Pt = self.ACresults[0]['branch'][branch.branchindex, 15].item() Qt = self.ACresults[0]['branch'][branch.branchindex, 16].item() # Calcular pérdidas y cargabilidad de cada rama Ploss = math.fabs(Pf - Pt) Qloss = math.fabs(Qf - Qt) Loading = math.sqrt(math.pow(Pf, 2) + math.pow(Qf, 2)) * 100 / branch.Snom # Guardar resultados respectivos branch.Results[fecha] = { 'Pf': Pf, 'Qf': Qf, 'Ploss': Ploss, 'Qloss': Qloss, 'Loading': Loading } self.CDC.Results[fecha] = { 'P': self.ACresults[0]['gen'][0, 1].item(), 'Q': self.ACresults[0]['gen'][0, 2].item() }
def t_jacobian(quiet=False): """Numerical tests of partial derivative code. @author: Ray Zimmerman (PSERC Cornell) """ t_begin(28, quiet) ## run powerflow to get solved case ppopt = ppoption(VERBOSE=0, OUT_ALL=0) ppc = loadcase(case30()) results, _ = runpf(ppc, ppopt) baseMVA, bus, gen, branch = \ results['baseMVA'], results['bus'], results['gen'], results['branch'] ## switch to internal bus numbering and build admittance matrices _, bus, gen, branch = ext2int1(bus, gen, branch) Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) Ybus_full = Ybus.todense() Yf_full = Yf.todense() Yt_full = Yt.todense() Vm = bus[:, VM] Va = bus[:, VA] * (pi / 180) V = Vm * exp(1j * Va) f = branch[:, F_BUS].astype(int) ## list of "from" buses t = branch[:, T_BUS].astype(int) ## list of "to" buses #nl = len(f) nb = len(V) pert = 1e-8 Vm = array([Vm]).T # column array Va = array([Va]).T # column array Vc = array([V]).T # column array ##----- check dSbus_dV code ----- ## full matrices dSbus_dVm_full, dSbus_dVa_full = dSbus_dV(Ybus_full, V) ## sparse matrices dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V) dSbus_dVm_sp = dSbus_dVm.todense() dSbus_dVa_sp = dSbus_dVa.todense() ## compute numerically to compare Vmp = (Vm * ones((1, nb)) + pert*eye(nb)) * (exp(1j * Va) * ones((1, nb))) Vap = (Vm * ones((1, nb))) * (exp(1j * (Va*ones((1, nb)) + pert*eye(nb)))) num_dSbus_dVm = (Vmp * conj(Ybus * Vmp) - Vc * ones((1, nb)) * conj(Ybus * Vc * ones((1, nb)))) / pert num_dSbus_dVa = (Vap * conj(Ybus * Vap) - Vc * ones((1, nb)) * conj(Ybus * Vc * ones((1, nb)))) / pert t_is(dSbus_dVm_sp, num_dSbus_dVm, 5, 'dSbus_dVm (sparse)') t_is(dSbus_dVa_sp, num_dSbus_dVa, 5, 'dSbus_dVa (sparse)') t_is(dSbus_dVm_full, num_dSbus_dVm, 5, 'dSbus_dVm (full)') t_is(dSbus_dVa_full, num_dSbus_dVa, 5, 'dSbus_dVa (full)') ##----- check dSbr_dV code ----- ## full matrices dSf_dVa_full, dSf_dVm_full, dSt_dVa_full, dSt_dVm_full, _, _ = \ dSbr_dV(branch, Yf_full, Yt_full, V) ## sparse matrices dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St = dSbr_dV(branch, Yf, Yt, V) dSf_dVa_sp = dSf_dVa.todense() dSf_dVm_sp = dSf_dVm.todense() dSt_dVa_sp = dSt_dVa.todense() dSt_dVm_sp = dSt_dVm.todense() ## compute numerically to compare Vmpf = Vmp[f, :] Vapf = Vap[f, :] Vmpt = Vmp[t, :] Vapt = Vap[t, :] Sf2 = (Vc[f] * ones((1, nb))) * conj(Yf * Vc * ones((1, nb))) St2 = (Vc[t] * ones((1, nb))) * conj(Yt * Vc * ones((1, nb))) Smpf = Vmpf * conj(Yf * Vmp) Sapf = Vapf * conj(Yf * Vap) Smpt = Vmpt * conj(Yt * Vmp) Sapt = Vapt * conj(Yt * Vap) num_dSf_dVm = (Smpf - Sf2) / pert num_dSf_dVa = (Sapf - Sf2) / pert num_dSt_dVm = (Smpt - St2) / pert num_dSt_dVa = (Sapt - St2) / pert t_is(dSf_dVm_sp, num_dSf_dVm, 5, 'dSf_dVm (sparse)') t_is(dSf_dVa_sp, num_dSf_dVa, 5, 'dSf_dVa (sparse)') t_is(dSt_dVm_sp, num_dSt_dVm, 5, 'dSt_dVm (sparse)') t_is(dSt_dVa_sp, num_dSt_dVa, 5, 'dSt_dVa (sparse)') t_is(dSf_dVm_full, num_dSf_dVm, 5, 'dSf_dVm (full)') t_is(dSf_dVa_full, num_dSf_dVa, 5, 'dSf_dVa (full)') t_is(dSt_dVm_full, num_dSt_dVm, 5, 'dSt_dVm (full)') t_is(dSt_dVa_full, num_dSt_dVa, 5, 'dSt_dVa (full)') ##----- check dAbr_dV code ----- ## full matrices dAf_dVa_full, dAf_dVm_full, dAt_dVa_full, dAt_dVm_full = \ dAbr_dV(dSf_dVa_full, dSf_dVm_full, dSt_dVa_full, dSt_dVm_full, Sf, St) ## sparse matrices dAf_dVa, dAf_dVm, dAt_dVa, dAt_dVm = \ dAbr_dV(dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St) dAf_dVa_sp = dAf_dVa.todense() dAf_dVm_sp = dAf_dVm.todense() dAt_dVa_sp = dAt_dVa.todense() dAt_dVm_sp = dAt_dVm.todense() ## compute numerically to compare num_dAf_dVm = (abs(Smpf)**2 - abs(Sf2)**2) / pert num_dAf_dVa = (abs(Sapf)**2 - abs(Sf2)**2) / pert num_dAt_dVm = (abs(Smpt)**2 - abs(St2)**2) / pert num_dAt_dVa = (abs(Sapt)**2 - abs(St2)**2) / pert t_is(dAf_dVm_sp, num_dAf_dVm, 4, 'dAf_dVm (sparse)') t_is(dAf_dVa_sp, num_dAf_dVa, 4, 'dAf_dVa (sparse)') t_is(dAt_dVm_sp, num_dAt_dVm, 4, 'dAt_dVm (sparse)') t_is(dAt_dVa_sp, num_dAt_dVa, 4, 'dAt_dVa (sparse)') t_is(dAf_dVm_full, num_dAf_dVm, 4, 'dAf_dVm (full)') t_is(dAf_dVa_full, num_dAf_dVa, 4, 'dAf_dVa (full)') t_is(dAt_dVm_full, num_dAt_dVm, 4, 'dAt_dVm (full)') t_is(dAt_dVa_full, num_dAt_dVa, 4, 'dAt_dVa (full)') ##----- check dIbr_dV code ----- ## full matrices dIf_dVa_full, dIf_dVm_full, dIt_dVa_full, dIt_dVm_full, _, _ = \ dIbr_dV(branch, Yf_full, Yt_full, V) ## sparse matrices dIf_dVa, dIf_dVm, dIt_dVa, dIt_dVm, _, _ = dIbr_dV(branch, Yf, Yt, V) dIf_dVa_sp = dIf_dVa.todense() dIf_dVm_sp = dIf_dVm.todense() dIt_dVa_sp = dIt_dVa.todense() dIt_dVm_sp = dIt_dVm.todense() ## compute numerically to compare num_dIf_dVm = (Yf * Vmp - Yf * Vc * ones((1, nb))) / pert num_dIf_dVa = (Yf * Vap - Yf * Vc * ones((1, nb))) / pert num_dIt_dVm = (Yt * Vmp - Yt * Vc * ones((1, nb))) / pert num_dIt_dVa = (Yt * Vap - Yt * Vc * ones((1, nb))) / pert t_is(dIf_dVm_sp, num_dIf_dVm, 5, 'dIf_dVm (sparse)') t_is(dIf_dVa_sp, num_dIf_dVa, 5, 'dIf_dVa (sparse)') t_is(dIt_dVm_sp, num_dIt_dVm, 5, 'dIt_dVm (sparse)') t_is(dIt_dVa_sp, num_dIt_dVa, 5, 'dIt_dVa (sparse)') t_is(dIf_dVm_full, num_dIf_dVm, 5, 'dIf_dVm (full)') t_is(dIf_dVa_full, num_dIf_dVa, 5, 'dIf_dVa (full)') t_is(dIt_dVm_full, num_dIt_dVm, 5, 'dIt_dVm (full)') t_is(dIt_dVa_full, num_dIt_dVa, 5, 'dIt_dVa (full)') t_end()
def run_sim(ppc, elements, dynopt = None, events = None, recorder = None): """ Run a time-domain simulation Inputs: ppc PYPOWER load flow case elements Dictionary of dynamic model objects (machines, controllers, etc) with Object ID as key events Events object recorder Recorder object (empty) Outputs: recorder Recorder object (with data) """ ######### # SETUP # ######### # Get version information ver = pydyn_ver() print('PYPOWER-Dynamics ' + ver['Version'] + ', ' + ver['Date']) # Program options if dynopt: h = dynopt['h'] t_sim = dynopt['t_sim'] max_err = dynopt['max_err'] max_iter = dynopt['max_iter'] verbose = dynopt['verbose'] else: # Default program options h = 0.01 # step length (s) t_sim = 5 # simulation time (s) max_err = 0.0001 # Maximum error in network iteration (voltage mismatches) max_iter = 25 # Maximum number of network iterations verbose = False # Make lists of current injection sources (generators, external grids, etc) and controllers sources = [] controllers = [] for element in elements.values(): if element.__module__ in ['pydyn.sym_order6a', 'pydyn.sym_order6b', 'pydyn.sym_order4', 'pydyn.ext_grid', 'pydyn.vsc_average', 'pydyn.asym_1cage', 'pydyn.asym_2cage']: sources.append(element) if element.__module__ == 'pydyn.controller': controllers.append(element) # Set up interfaces interfaces = init_interfaces(elements) ################## # INITIALISATION # ################## print('Initialising models...') # Run power flow and update bus voltages and angles in PYPOWER case object results, success = runpf(ppc) ppc["bus"][:, VM] = results["bus"][:, VM] ppc["bus"][:, VA] = results["bus"][:, VA] # Build Ybus matrix ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int["branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Build modified Ybus matrix Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Calculate initial voltage phasors v0 = bus[:, VM] * (np.cos(np.radians(bus[:, VA])) + 1j * np.sin(np.radians(bus[:, VA]))) # Initialise sources from load flow for source in sources: if source.__module__ in ['pydyn.asym_1cage', 'pydyn.asym_2cage']: # Asynchronous machine source_bus = ppc_int['bus'][source.bus_no,0] v_source = v0[source_bus] source.initialise(v_source,0) else: # Generator or VSC source_bus = ppc_int['gen'][source.gen_no,0] S_source = np.complex(results["gen"][source.gen_no, 1] / baseMVA, results["gen"][source.gen_no, 2] / baseMVA) v_source = v0[source_bus] source.initialise(v_source,S_source) # Interface controllers and machines (for initialisation) for intf in interfaces: int_type = intf[0] var_name = intf[1] if int_type == 'OUTPUT': # If an output, interface in the reverse direction for initialisation intf[2].signals[var_name] = intf[3].signals[var_name] else: # Inputs are interfaced in normal direction during initialisation intf[3].signals[var_name] = intf[2].signals[var_name] # Initialise controllers for controller in controllers: controller.initialise() ############# # MAIN LOOP # ############# if events == None: print('Warning: no events!') # Factorise Ybus matrix Ybus_inv = splu(Ybus) y1 = [] v_prev = v0 print('Simulating...') for t in range(int(t_sim / h) + 1): if np.mod(t,1/h) == 0: print('t=' + str(t*h) + 's') # Interface controllers and machines for intf in interfaces: var_name = intf[1] intf[3].signals[var_name] = intf[2].signals[var_name] # Solve differential equations for j in range(4): # Solve step of differential equations for element in elements.values(): element.solve_step(h,j) # Interface with network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) if recorder != None: # Record signals or states recorder.record_variables(t*h, elements) if events != None: # Check event stack ppc, refactorise = events.handle_events(np.round(t*h,5), elements, ppc, baseMVA) if refactorise == True: # Rebuild Ybus from new ppc_int ppc_int = ext2int(ppc) baseMVA, bus, branch = ppc_int["baseMVA"], ppc_int["bus"], ppc_int["branch"] Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch) # Rebuild modified Ybus Ybus = mod_Ybus(Ybus, elements, bus, ppc_int['gen'], baseMVA) # Refactorise Ybus Ybus_inv = splu(Ybus) # Solve network equations v_prev = solve_network(sources, v_prev, Ybus_inv, ppc_int, len(bus), max_err, max_iter) return recorder