def runpm_storage_opf(net, calculate_voltage_angles=True, trafo_model="t", delta=1e-8, trafo3w_losses="hv", check_connectivity=True, n_timesteps=24, time_elapsed=1.0, correct_pm_network_data=True, pm_model="ACPPowerModel", pm_time_limits=None, pm_log_level=0): # pragma: no cover """ Runs a non-linear power system optimization with storages and time series using PowerModels.jl. INPUT: **net** - The pandapower format network OPTIONAL: **n_timesteps** (int, 24) - number of time steps to optimize **time_elapsed** (float, 1.0) - time elapsed between time steps (1.0 = 1 hour) **pm_time_limits** (Dict, None) - Time limits in seconds for power models interface. To be set as a dict like {"pm_time_limit": 300., "pm_nl_time_limit": 300., "pm_mip_time_limit": 300.} **pm_log_level** (int, 0) - solver log level in power models """ julia_file = os.path.join(pp_dir, "opf", 'run_powermodels_mn_storage.jl') ac = True if "DC" not in pm_model else False net._options = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode="opf", switch_rx_ratio=2, init_vm_pu="flat", init_va_degree="flat", enforce_q_lims=True, recycle=dict(_is_elements=False, ppc=False, Ybus=False), voltage_depend_loads=False, delta=delta, trafo3w_losses=trafo3w_losses) _add_opf_options(net, trafo_loading='power', ac=ac, init="flat", numba=True, pp_to_pm_callback=add_storage_opf_settings, julia_file=julia_file, correct_pm_network_data=correct_pm_network_data, pm_model=pm_model, pm_time_limits=pm_time_limits, pm_log_level=pm_log_level) net._options["n_time_steps"] = n_timesteps net._options["time_elapsed"] = time_elapsed _runpm(net) storage_results = read_pm_storage_results(net) return storage_results
def runpm_ac_opf(net, pp_to_pm_callback=None, calculate_voltage_angles=True, trafo_model="t", delta=1e-8, trafo3w_losses="hv", check_connectivity=True, pm_model="ACPPowerModel", pm_solver="ipopt", correct_pm_network_data=True, pm_time_limits=None, pm_log_level=0): # pragma: no cover """ Runs a non-linear power system optimization using PowerModels.jl. Flexibilities, constraints and cost parameters are defined in the pandapower element tables. Flexibilities can be defined in net.sgen / net.gen /net.load net.sgen.controllable if a static generator is controllable. If False, the active and reactive power are assigned as in a normal power flow. If True, the following flexibilities apply: - net.sgen.min_p_mw / net.sgen.max_p_mw - net.sgen.min_q_mvar / net.sgen.max_q_mvar - net.load.min_p_mw / net.load.max_p_mw - net.load.min_q_mvar / net.load.max_q_mvar - net.gen.min_p_mw / net.gen.max_p_mw - net.gen.min_q_mvar / net.gen.max_q_mvar - net.ext_grid.min_p_mw / net.ext_grid.max_p_mw - net.ext_grid.min_q_mvar / net.ext_grid.max_q_mvar - net.dcline.min_q_to_mvar / net.dcline.max_q_to_mvar / net.dcline.min_q_from_mvar / net.dcline.max_q_from_mvar Controllable loads behave just like controllable static generators. It must be stated if they are controllable. Otherwise, they are not respected as flexibilities. Dc lines are controllable per default Network constraints can be defined for buses, lines and transformers the elements in the following columns: - net.bus.min_vm_pu / net.bus.max_vm_pu - net.line.max_loading_percent - net.trafo.max_loading_percent - net.trafo3w.max_loading_percent How these costs are combined into a cost function depends on the cost_function parameter. INPUT: **net** - The pandapower format network OPTIONAL: **pp_to_pm_callback** (function, None) - callback function to add data to the PowerModels data structure **pm_model** (str, "ACPPowerModel") - model to use. Default is AC model **pm_solver** (str, "ipopt") - default solver to use. If ipopt is not available use Ipopt **correct_pm_network_data** (bool, True) - checks if network data is correct. If not tries to correct it **pm_time_limits** (Dict, None) - Time limits in seconds for power models interface. To be set as a dict like {"pm_time_limit": 300.} **pm_log_level** (int, 0) - solver log level in power models """ julia_file = os.path.join(pp_dir, "opf", 'run_powermodels.jl') ac = True if "DC" not in pm_model else False net._options = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode="opf", switch_rx_ratio=2, init_vm_pu="flat", init_va_degree="flat", enforce_q_lims=True, recycle=dict(_is_elements=False, ppc=False, Ybus=False), voltage_depend_loads=False, delta=delta, trafo3w_losses=trafo3w_losses) _add_opf_options(net, trafo_loading='power', ac=ac, init="flat", numba=True, pp_to_pm_callback=pp_to_pm_callback, julia_file=julia_file, pm_model=pm_model, pm_solver=pm_solver, correct_pm_network_data=correct_pm_network_data, pm_time_limits=pm_time_limits, pm_log_level=pm_log_level) _runpm(net)
def runpm_ots(net, pp_to_pm_callback=None, calculate_voltage_angles=True, trafo_model="t", delta=1e-8, trafo3w_losses="hv", check_connectivity=True, pm_model="DCPPowerModel", pm_solver="juniper", pm_nl_solver="ipopt", pm_mip_solver="cbc", correct_pm_network_data=True, pm_time_limits=None, pm_log_level=0): # pragma: no cover """ Runs a non-linear optimal transmission switching (OTS) optimization using PowerModels.jl. OPTIONAL: **julia_file** (str, None) - path to a custom julia optimization file **pp_to_pm_callback** (function, None) - callback function to add data to the PowerModels data structure **correct_pm_network_data** (bool, True) - checks if network data is correct. If not tries to correct it **pm_model** (str, "ACPPowerModel") - The PowerModels.jl model to use **pm_solver** (str, "juniper") - The "main" power models solver **pm_mip_solver** (str, "cbc") - The mixed integer solver (when "main" solver == juniper) **pm_nl_solver** (str, "ipopt") - The nonlinear solver (when "main" solver == juniper) **pm_time_limits** (Dict, None) - Time limits in seconds for power models interface. To be set as a dict like {"pm_time_limit": 300., "pm_nl_time_limit": 300., "pm_mip_time_limit": 300.} **pm_log_level** (int, 0) - solver log level in power models """ julia_file = os.path.join(pp_dir, "opf", 'run_powermodels_ots.jl') ac = True if "DC" not in pm_model else False if pm_solver is None: pm_solver = "juniper" net._options = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode="opf", switch_rx_ratio=2, init_vm_pu="flat", init_va_degree="flat", enforce_q_lims=True, recycle=dict(_is_elements=False, ppc=False, Ybus=False), voltage_depend_loads=False, delta=delta, trafo3w_losses=trafo3w_losses) _add_opf_options(net, trafo_loading='power', ac=ac, init="flat", numba=True, pp_to_pm_callback=pp_to_pm_callback, julia_file=julia_file, pm_model=pm_model, pm_solver=pm_solver, correct_pm_network_data=correct_pm_network_data, pm_mip_solver=pm_mip_solver, pm_nl_solver=pm_nl_solver, pm_time_limits=pm_time_limits, pm_log_level=pm_log_level) _runpm(net) read_ots_results(net)
if case not in ['max', 'min']: raise ValueError( 'case can only be "min" or "max" for minimal or maximal short "\ "circuit current') if topology not in ["meshed", "radial", "auto"]: raise ValueError( 'specify network structure as "meshed", "radial" or "auto"') kappa = ith or ip net["_options"] = {} _add_ppc_options(net, calculate_voltage_angles=False, trafo_model="pi", check_connectivity=False, mode="sc", copy_constraints_to_ppc=False, r_switch=0.0, init="flat", enforce_q_lims=False, recycle=None) _add_sc_options(net, fault=fault, case=case, lv_tol_percent=lv_tol_percent, tk_s=tk_s, topology=topology, r_fault_ohm=r_fault_ohm, kappa_method=kappa_method, x_fault_ohm=x_fault_ohm, kappa=kappa, ip=ip,
def runpp_3ph(net, calculate_voltage_angles=True, init="auto", max_iteration="auto", tolerance_mva=1e-8, trafo_model='t', trafo_loading="current", enforce_q_lims=False, numba=True, recycle=None, check_connectivity=True, switch_rx_ratio=2.0, delta_q=0, v_debug=False, **kwargs): """ runpp_3ph: Performs Unbalanced/Asymmetric/Three Phase Load flow INPUT: **net** - The pandapower format network OPTIONAL: **algorithm** (str, "nr") - algorithm that is used to solve the power flow problem. The following algorithms are available: - "nr" Newton-Raphson (pypower implementation with numba accelerations) Used only for positive sequence network Zero and Negative sequence networks use Current Injection method Vnew = Y.inv * Ispecified ( from s_abc/v_abc old) Icalculated = Y * Vnew **calculate_voltage_angles** (bool, "auto") - consider voltage angles in loadflow calculation If True, voltage angles of ext_grids and transformer shifts are considered in the loadflow calculation. Considering the voltage angles is only necessary in meshed networks that are usually found in higher voltage levels. calculate_voltage_angles in "auto" mode defaults to: - True, if the network voltage level is above 70 kV - False otherwise The network voltage level is defined as the maximum rated voltage of any bus in the network that is connected to a line. **max_iteration** (int, "auto") - maximum number of iterations carried out in the power flow algorithm. In "auto" mode, the default value depends on the power flow solver: - 10 for "nr" For three phase calculations, its extended to 3 * max_iteration **tolerance_mva** (float, 1e-8) - loadflow termination condition referring to P / Q mismatch of node power in MVA **trafo_model** - transformer equivalent models - "t" - transformer is modeled as equivalent with the T-model. - "pi" - This is not recommended, since it is less exact than the T-model. So, for three phase load flow, its not implemented **trafo_loading** (str, "current") - mode of calculation for transformer loading Transformer loading can be calculated relative to the rated current or the rated power. In both cases the overall transformer loading is defined as the maximum loading on the two sides of the transformer. - "current"- transformer loading is given as ratio of current flow and rated current of the transformer. This is the recommended setting, since thermal as well as magnetic effects in the transformer depend on the current. - "power" - transformer loading is given as ratio of apparent power flow to the rated apparent power of the transformer. **enforce_q_lims** (bool, False) (Not tested with 3 Phase load flow) - respect generator reactive power limits If True, the reactive power limits in net.gen.max_q_mvar/min_q_mvar are respected in the loadflow. This is done by running a second loadflow if reactive power limits are violated at any generator, so that the runtime for the loadflow will increase if reactive power has to be curtailed. Note: enforce_q_lims only works if algorithm="nr"! **check_connectivity** (bool, True) - Perform an extra connectivity test after the conversion from pandapower to PYPOWER. If True, an extra connectivity test based on SciPy Compressed Sparse Graph Routines is perfomed. If check finds unsupplied buses, they are set out of service in the ppc **voltage_depend_loads** (bool, True) (Not tested with 3 Phase load flow) - consideration of voltage-dependent loads. If False, ``net.load.const_z_percent`` and ``net.load.const_i_percent`` are not considered, i.e. ``net.load.p_mw`` and ``net.load.q_mvar`` are considered as constant-power loads. **consider_line_temperature** (bool, False) (Not tested with 3 Phase load flow) - adjustment of line impedance based on provided line temperature. If True, ``net.line`` must contain a column ``temperature_degree_celsius``. The temperature dependency coefficient alpha must be provided in the ``net.line.alpha`` column, otherwise the default value of 0.004 is used. **KWARGS**: **numba** (bool, True) - Activation of numba JIT compiler in the newton solver If set to True, the numba JIT compiler is used to generate matrices for the powerflow, which leads to significant speed improvements. **switch_rx_ratio** (float, 2) (Not tested with 3 Phase load flow) - rx_ratio of bus-bus-switches. If impedance is zero, buses connected by a closed bus-bus switch are fused to model an ideal bus. Otherwise, they are modelled as branches with resistance defined as z_ohm column in switch table and this parameter **delta_q** (Not tested with 3 Phase load flow) - Reactive power tolerance for option "enforce_q_lims" in kvar - helps convergence in some cases. **trafo3w_losses** (Not tested with 3 Phase load flow) - defines where open loop losses of three-winding transformers are considered. Valid options are "hv", "mv", "lv" for HV/MV/LV side or "star" for the star point. **v_debug** (bool, False) (Not tested with 3 Phase load flow) - if True, voltage values in each newton-raphson iteration are logged in the ppc. **init_vm_pu** (string/float/array/Series, None) (Not tested with 3 Phase load flow) - Allows to define initialization specifically for voltage magnitudes. Only works with ``init == "auto"``! - "auto": all buses are initialized with the mean value of all voltage controlled elements in the grid - "flat" for flat start from 1.0 - "results": voltage magnitude vector is taken from result table - a float with which all voltage magnitudes are initialized - an iterable with a voltage magnitude value for each bus (length and order has to match with the buses in net.bus) - a pandas Series with a voltage magnitude value for each bus (indexes have to match the indexes in net.bus) **init_va_degree** (string/float/array/Series, None) (Not tested with 3 Phase load flow) - Allows to define initialization specifically for voltage angles. Only works with ``init == "auto"``! - "auto": voltage angles are initialized from DC power flow if angles are calculated or as 0 otherwise - "dc": voltage angles are initialized from DC power flow - "flat" for flat start from 0 - "results": voltage angle vector is taken from result table - a float with which all voltage angles are initialized - an iterable with a voltage angle value for each bus (length and order has to match with the buses in net.bus) - a pandas Series with a voltage angle value for each bus (indexes have to match the indexes in net.bus) **recycle** (dict, none) - Reuse of internal powerflow variables for time series calculation. Contains a dict with the following parameters: bus_pq: If True PQ values of buses are updated gen: If True Sbus and the gen table in the ppc are recalculated Ybus: If True the admittance matrix (Ybus, Yf, Yt) is taken from ppc["internal"] and not reconstructed **neglect_open_switch_branches** (bool, False) (Not tested with 3 Phase load flow) - If True no auxiliary buses are created for branches when switches are opened at the branch. Instead branches are set out of service SEE ALSO: pp.add_zero_impedance_parameters(net): To add zero sequence parameters into network from the standard type EXAMPLES: Use this module like this: .. code-block:: python from pandapower.pf.runpp_3ph import runpp_3ph runpp_3ph(net) NOTES: - Three phase load flow uses Sequence Frame for power flow solution. - Three phase system is modelled with earth return. - PH-E load type is called as wye since Neutral and Earth are considered same - This solver has proved successful only for Earthed transformers (i.e Dyn,Yyn,YNyn & Yzn vector groups) """ # ============================================================================= # pandapower settings # ============================================================================= overrule_options = {} if "user_pf_options" in net.keys() and len(net.user_pf_options) > 0: passed_parameters = _passed_runpp_parameters(locals()) overrule_options = { key: val for key, val in net.user_pf_options.items() if key not in passed_parameters.keys() } if numba: numba = _check_if_numba_is_installed(numba) ac = True mode = "pf_3ph" # TODO: Make valid modes (pf, pf_3ph, se, etc.) available in seperate file (similar to idx_bus.py) # v_debug = kwargs.get("v_debug", False) copy_constraints_to_ppc = False if trafo_model == 'pi': raise Not_implemented("Three phase Power Flow doesnot support pi model\ because of lack of accuracy") # if calculate_voltage_angles == "auto": # calculate_voltage_angles = False # hv_buses = np.where(net.bus.vn_kv.values > 70)[0] # Todo: Where does that number come from? # if len(hv_buses) > 0: # line_buses = net.line[["from_bus", "to_bus"]].values.flatten() # if len(set(net.bus.index[hv_buses]) & set(line_buses)) > 0: # scipy spsolve options in NR power flow use_umfpack = kwargs.get("use_umfpack", True) permc_spec = kwargs.get("permc_spec", None) calculate_voltage_angles = True if init == "results" and net.res_bus_3ph.empty: init = "auto" if init == "auto": init = "dc" if calculate_voltage_angles else "flat" default_max_iteration = { "nr": 10, "bfsw": 10, "gs": 10000, "fdxb": 30, "fdbx": 30 } if max_iteration == "auto": max_iteration = default_max_iteration["nr"] neglect_open_switch_branches = kwargs.get("neglect_open_switch_branches", False) only_v_results = kwargs.get("only_v_results", False) net._options = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode=mode, switch_rx_ratio=switch_rx_ratio, init_vm_pu=init, init_va_degree=init, enforce_q_lims=enforce_q_lims, recycle=None, voltage_depend_loads=False, delta=delta_q,\ neglect_open_switch_branches=neglect_open_switch_branches ) _add_pf_options(net, tolerance_mva=tolerance_mva, trafo_loading=trafo_loading, numba=numba, ac=ac, algorithm="nr", max_iteration=max_iteration,\ only_v_results=only_v_results,v_debug=v_debug, use_umfpack=use_umfpack, permc_spec=permc_spec) net._options.update(overrule_options) _check_bus_index_and_print_warning_if_high(net) _check_gen_index_and_print_warning_if_high(net) # ========================================================================= # pd2ppc conversion # ========================================================================= _, ppci1 = _pd2ppc_recycle(net, 1, recycle=recycle) _, ppci2 = _pd2ppc_recycle(net, 2, recycle=recycle) gs_eg, bs_eg = _add_ext_grid_sc_impedance(net, ppci2) _, ppci0 = _pd2ppc_recycle(net, 0, recycle=recycle) _, bus0, gen0, branch0, _, _, _ = _get_pf_variables_from_ppci(ppci0) base_mva, bus1, gen1, branch1, sl_bus, _, pq_bus = _get_pf_variables_from_ppci( ppci1) _, bus2, gen2, branch2, _, _, _ = _get_pf_variables_from_ppci(ppci2) # initialize the results after the conversion to ppc is done, otherwise init=results does not work init_results(net, "pf_3ph") # ============================================================================= # P Q values aggragated and summed up for each bus to make s_abc matrix # s_abc for wye connections ; s_abc_delta for delta connection # ============================================================================= s_abc_delta, s_abc = _load_mapping(net, ppci1) # ========================================================================= # Construct Sequence Frame Bus admittance matrices Ybus # ========================================================================= ppci0, ppci1, ppci2, y_0_pu, y_1_pu, y_2_pu, y_0_f, y_1_f, _,\ y_0_t, y_1_t, _ = _get_y_bus(ppci0, ppci1, ppci2, recycle) # ========================================================================= # Initial voltage values # ========================================================================= nb = ppci1["bus"].shape[0] # make sure flat start is always respected, even with other voltage data in recycled ppc if init == "flat": v_012_it = np.zeros((3, nb), dtype=np.complex128) v_012_it[1, :] = 1.0 else: v_012_it = np.concatenate([ np.array(ppc["bus"][:, VM] * np.exp(1j * np.deg2rad(ppc["bus"][:, VA]))).reshape( (1, nb)) for ppc in (ppci0, ppci1, ppci2) ], axis=0).astype(np.complex128) # For Delta transformation: # Voltage changed from line-earth to line-line using V_T # s_abc/v_abc will now give line-line currents. This is converted to line-earth # current using I-T v_del_xfmn = np.array([[1, -1, 0], [0, 1, -1], [-1, 0, 1]]) i_del_xfmn = np.array([[1, 0, -1], [-1, 1, 0], [0, -1, 1]]) v_abc_it = sequence_to_phase(v_012_it) # ========================================================================= # Iteration using Power mismatch criterion # ========================================================================= outer_tolerance_mva = 3e-8 count = 0 s_mismatch = np.array([[True], [True]], dtype=bool) t0 = perf_counter() while (s_mismatch > outer_tolerance_mva).any() and count < 30 * max_iteration: # ===================================================================== # Voltages and Current transformation for PQ and Slack bus # ===================================================================== s_abc_pu = -np.divide(s_abc, ppci1["baseMVA"]) s_abc_delta_pu = -np.divide(s_abc_delta, ppci1["baseMVA"]) i_abc_it_wye = (np.divide(s_abc_pu, v_abc_it)).conjugate() i_abc_it_delta = np.matmul(i_del_xfmn, (np.divide( s_abc_delta_pu, np.matmul(v_del_xfmn, v_abc_it))).conjugate()) # For buses with both delta and wye loads we need to sum of their currents # to sum up the currents i_abc_it = i_abc_it_wye + i_abc_it_delta i012_it = phase_to_sequence(i_abc_it) v1_for_s1 = v_012_it[1, :] i1_for_s1 = -i012_it[1, :] v0_pu_it = X012_to_X0(v_012_it) v2_pu_it = X012_to_X2(v_012_it) i0_pu_it = X012_to_X0(i012_it) i2_pu_it = X012_to_X2(i012_it) s1 = np.multiply(v1_for_s1, i1_for_s1.conjugate()) # ============================================================================= # Current used to find S1 Positive sequence power # ============================================================================= ppci1["bus"][pq_bus, PD] = np.real(s1[pq_bus]) * ppci1["baseMVA"] ppci1["bus"][pq_bus, QD] = np.imag(s1[pq_bus]) * ppci1["baseMVA"] # ============================================================================= # Conduct Positive sequence power flow # ============================================================================= _run_newton_raphson_pf(ppci1, net._options) # ============================================================================= # Conduct Negative and Zero sequence power flow # ============================================================================= v0_pu_it = V_from_I(y_0_pu, i0_pu_it) v2_pu_it = V_from_I(y_2_pu, i2_pu_it) # ============================================================================= # Evaluate Positive Sequence Power Mismatch # ============================================================================= i1_from_v_it = I1_from_V012(v_012_it, y_1_pu).flatten() s_from_voltage = S_from_VI_elementwise(v1_for_s1, i1_from_v_it) v1_pu_it = V1_from_ppc(ppci1) v_012_new = combine_X012(v0_pu_it, v1_pu_it, v2_pu_it) s_mismatch = np.abs( np.abs(s1[pq_bus]) - np.abs(s_from_voltage[pq_bus])) v_012_it = v_012_new v_abc_it = sequence_to_phase(v_012_it) count += 1 et = perf_counter() - t0 success = (count < 30 * max_iteration) for ppc in [ppci0, ppci1, ppci2]: ppc["et"] = et ppc["success"] = success # TODO: Add reference to paper to explain the following steps # This is required since the ext_grid power results are not correct if its # not done ref, pv, pq = bustypes(ppci0["bus"], ppci0["gen"]) ref_gens = ppci0["internal"]["ref_gens"] ppci0["bus"][ref, GS] -= gs_eg ppci0["bus"][ref, BS] -= bs_eg y_0_pu, y_0_f, y_0_t = makeYbus(ppci0["baseMVA"], ppci0["bus"], ppci0["branch"]) # revert the change, otherwise repeated calculation with recycled elements will fail ppci0["bus"][ref, GS] += gs_eg ppci0["bus"][ref, BS] += bs_eg # Bus, Branch, and Gen power values bus0, gen0, branch0 = pfsoln(base_mva, bus0, gen0, branch0, y_0_pu, y_0_f, y_0_t, v_012_it[0, :].flatten(), sl_bus, ref_gens) bus1, gen1, branch1 = pfsoln(base_mva, bus1, gen1, branch1, y_1_pu, y_1_f, y_1_t, v_012_it[1, :].flatten(), sl_bus, ref_gens) bus2, gen2, branch2 = pfsoln(base_mva, bus2, gen2, branch2, y_1_pu, y_1_f, y_1_t, v_012_it[2, :].flatten(), sl_bus, ref_gens) ppci0 = _store_results_from_pf_in_ppci(ppci0, bus0, gen0, branch0) ppci1 = _store_results_from_pf_in_ppci(ppci1, bus1, gen1, branch1) ppci2 = _store_results_from_pf_in_ppci(ppci2, bus2, gen2, branch2) i_012_res = _current_from_voltage_results(y_0_pu, y_1_pu, v_012_it) s_012_res = S_from_VI_elementwise(v_012_it, i_012_res) * ppci1["baseMVA"] eg_is_mask = net["_is_elements"]['ext_grid'] ext_grid_lookup = net["_pd2ppc_lookups"]["ext_grid"] eg_is_idx = net["ext_grid"].index.values[eg_is_mask] eg_idx_ppc = ext_grid_lookup[eg_is_idx] """ # 2 ext_grids Fix: Instead of the generator index, bus indices of the generators are used""" eg_bus_idx_ppc = np.real(ppci1["gen"][eg_idx_ppc, GEN_BUS]).astype(int) ppci0["gen"][eg_idx_ppc, PG] = s_012_res[0, eg_bus_idx_ppc].real ppci1["gen"][eg_idx_ppc, PG] = s_012_res[1, eg_bus_idx_ppc].real ppci2["gen"][eg_idx_ppc, PG] = s_012_res[2, eg_bus_idx_ppc].real ppci0["gen"][eg_idx_ppc, QG] = s_012_res[0, eg_bus_idx_ppc].imag ppci1["gen"][eg_idx_ppc, QG] = s_012_res[1, eg_bus_idx_ppc].imag ppci2["gen"][eg_idx_ppc, QG] = s_012_res[2, eg_bus_idx_ppc].imag ppc0 = net["_ppc0"] ppc1 = net["_ppc1"] ppc2 = net["_ppc2"] # ppci doesn't contain out of service elements, but ppc does -> copy results accordingly ppc0 = _copy_results_ppci_to_ppc(ppci0, ppc0, mode=mode) ppc1 = _copy_results_ppci_to_ppc(ppci1, ppc1, mode=mode) ppc2 = _copy_results_ppci_to_ppc(ppci2, ppc2, mode=mode) _extract_results_3ph(net, ppc0, ppc1, ppc2) # Raise error if PF was not successful. If DC -> success is always 1 if not ppci0["success"]: net["converged"] = False _clean_up(net, res=False) raise LoadflowNotConverged("Power Flow {0} did not converge after\ {1} iterations!".format("nr", count)) else: net["converged"] = True _clean_up(net)
def to_ppc(net, calculate_voltage_angles=False, trafo_model="t", r_switch=0.0, check_connectivity=True, voltage_depend_loads=True, init="results"): """ This function converts a pandapower net to a pypower case file. INPUT: **net** - The pandapower net. OPTIONAL: **calculate_voltage_angles** (bool, False) - consider voltage angles in loadflow calculation If True, voltage angles of ext_grids and transformer shifts are considered in the loadflow calculation. Considering the voltage angles is only necessary in meshed networks that are usually found in higher networks. **trafo_model** (str, "t") - transformer equivalent circuit model pandapower provides two equivalent circuit models for the transformer: - "t" - transformer is modeled as equivalent with the T-model. - "pi" - transformer is modeled as equivalent PI-model. This is not recommended, since \ it is less exact than the T-model. It is only recommended for validation with other \ software that uses the pi-model. **r_switch** (float, 0.0) - resistance of bus-bus-switches. If impedance is zero, buses connected by a closed bus-bus switch are fused to model an ideal bus. Otherwise, they are modelled as branches with resistance r_switch. **check_connectivity** (bool, True) - Perform an extra connectivity test after the conversion from pandapower to PYPOWER If True, an extra connectivity test based on SciPy Compressed Sparse Graph Routines is perfomed. If check finds unsupplied buses, they are set out of service in the ppc **voltage_depend_loads** (bool, True) - consideration of voltage-dependent loads. If False, net.load.const_z_percent and net.load.const_i_percent are not considered, i.e. net.load.p_kw and net.load.q_kvar are considered as constant-power loads. **init** (str, "results") - initialization method of the converter pandapower ppc converter supports two methods for initializing the converter: - "flat"- flat start with voltage of 1.0pu and angle of 0° at all PQ-buses and 0° for PV buses as initial solution - "results" - voltage vector from net.res_bus is used as initial solution. OUTPUT: **ppc** - The Pypower casefile for usage with pypower EXAMPLE: import pandapower.converter as pc import pandapower.networks as pn net = pn.case9() ppc = pc.to_ppc(net) """ # select elements in service net["_options"] = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode="pf", copy_constraints_to_ppc=True, r_switch=r_switch, init=init, enforce_q_lims=True, recycle=None, voltage_depend_loads=voltage_depend_loads) # do the conversion ppc, _ = _pd2ppc(net) ppc['branch'] = ppc['branch'].real ppc.pop('internal') return ppc
def runpm(net, julia_file=None, pp_to_pm_callback=None, calculate_voltage_angles=True, trafo_model="t", delta=1e-8, trafo3w_losses="hv", check_connectivity=True, correct_pm_network_data=True, pm_model="ACPPowerModel", pm_solver="ipopt", pm_mip_solver="cbc", pm_nl_solver="ipopt", pm_time_limits=None, pm_log_level=0, delete_buffer_file=True, pm_file_path=None, opf_flow_lim="S", **kwargs): # pragma: no cover """ Runs a power system optimization using PowerModels.jl. with a custom julia file. Flexibilities, constraints and cost parameters are defined in the pandapower element tables. Flexibilities can be defined in net.sgen / net.gen /net.load net.sgen.controllable if a static generator is controllable. If False, the active and reactive power are assigned as in a normal power flow. If True, the following flexibilities apply: - net.sgen.min_p_mw / net.sgen.max_p_mw - net.sgen.min_q_mvar / net.sgen.max_q_mvar - net.load.min_p_mw / net.load.max_p_mw - net.load.min_q_mvar / net.load.max_q_mvar - net.gen.min_p_mw / net.gen.max_p_mw - net.gen.min_q_mvar / net.gen.max_q_mvar - net.ext_grid.min_p_mw / net.ext_grid.max_p_mw - net.ext_grid.min_q_mvar / net.ext_grid.max_q_mvar - net.dcline.min_q_to_mvar / net.dcline.max_q_to_mvar / net.dcline.min_q_from_mvar / net.dcline.max_q_from_mvar Controllable loads behave just like controllable static generators. It must be stated if they are controllable. Otherwise, they are not respected as flexibilities. Dc lines are controllable per default Network constraints can be defined for buses, lines and transformers the elements in the following columns: - net.bus.min_vm_pu / net.bus.max_vm_pu - net.line.max_loading_percent - net.trafo.max_loading_percent - net.trafo3w.max_loading_percent How these costs are combined into a cost function depends on the cost_function parameter. INPUT: **net** - The pandapower format network OPTIONAL: **julia_file** (str, None) - path to a custom julia optimization file **pp_to_pm_callback** (function, None) - callback function to add data to the PowerModels data structure **correct_pm_network_data** (bool, True) - checks if network data is correct. If not tries to correct it **pm_model** (str, "ACPPowerModel") - The PowerModels.jl model to use **pm_solver** (str, "ipopt") - The "main" power models solver **pm_mip_solver** (str, "cbc") - The mixed integer solver (when "main" solver == juniper) **pm_nl_solver** (str, "ipopt") - The nonlinear solver (when "main" solver == juniper) **pm_time_limits** (Dict, None) - Time limits in seconds for power models interface. To be set as a dict like {"pm_time_limit": 300., "pm_nl_time_limit": 300., "pm_mip_time_limit": 300.} **pm_log_level** (int, 0) - solver log level in power models **delete_buffer_file** (Bool, True) - If True, the .json file used by powermodels will be deleted after optimization. **pm_file_path** (str, None) - Specifiy the filename, under which the .json file for powermodels is stored. If you want to keep the file after optimization, you should also set delete_buffer_file to False! **opf_flow_lim** (str, "I") - Quantity to limit for branch flow constraints, in line with matpower's "opf.flowlim" parameter "S" - apparent power flow (limit in MVA), "I" - current magnitude (limit in MVA at 1 p.u. voltage) """ net._options = {} ac = True if "DC" not in pm_model else False julia_file = os.path.join( pp_dir, "opf", 'run_powermodels.jl') if julia_file is None else julia_file _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode="opf", switch_rx_ratio=2, init_vm_pu="flat", init_va_degree="flat", enforce_q_lims=True, recycle=dict(_is_elements=False, ppc=False, Ybus=False), voltage_depend_loads=False, delta=delta, trafo3w_losses=trafo3w_losses) _add_opf_options(net, trafo_loading='power', ac=ac, init="flat", numba=True, pp_to_pm_callback=pp_to_pm_callback, julia_file=julia_file, pm_solver=pm_solver, pm_model=pm_model, correct_pm_network_data=correct_pm_network_data, pm_mip_solver=pm_mip_solver, pm_nl_solver=pm_nl_solver, pm_time_limits=pm_time_limits, pm_log_level=pm_log_level, opf_flow_lim=opf_flow_lim) _runpm(net, delete_buffer_file=delete_buffer_file, pm_file_path=pm_file_path)
def to_ppc(net, calculate_voltage_angles=False, trafo_model="t", switch_rx_ratio=2, check_connectivity=True, voltage_depend_loads=True, init="results", mode=None): """ This function converts a pandapower net to a pypower case file. INPUT: **net** - The pandapower net. OPTIONAL: **calculate_voltage_angles** (bool, False) - consider voltage angles in loadflow calculation If True, voltage angles of ext_grids and transformer shifts are considered in the loadflow calculation. Considering the voltage angles is only necessary in meshed networks that are usually found in higher networks. **trafo_model** (str, "t") - transformer equivalent circuit model pandapower provides two equivalent circuit models for the transformer: - "t" - transformer is modeled as equivalent with the T-model. - "pi" - transformer is modeled as equivalent PI-model. This is not recommended, since \ it is less exact than the T-model. It is only recommended for validation with other \ software that uses the pi-model. **switch_rx_ratio** (float, 2) - rx_ratio of bus-bus-switches. If impedance is zero, \ buses connected by a closed bus-bus switch are fused to model an ideal bus. \ Otherwise, they are modelled as branches with resistance defined as z_ohm column in \ switch table and this parameter **check_connectivity** (bool, True) - Perform an extra connectivity test after the conversion from pandapower to PYPOWER If True, an extra connectivity test based on SciPy Compressed Sparse Graph Routines is perfomed. If check finds unsupplied buses, they are set out of service in the ppc **voltage_depend_loads** (bool, True) - consideration of voltage-dependent loads. \ If False, net.load.const_z_percent and net.load.const_i_percent are not considered, i.e. \ net.load.p_mw and net.load.q_mvar are considered as constant-power loads. **init** (str, "results") - initialization method of the converter pandapower ppc converter supports two methods for initializing the converter: - "flat"- flat start with voltage of 1.0pu and angle of 0° at all PQ-buses and 0° for \ PV buses as initial solution - "results" - voltage vector from net.res_bus is used as initial solution. **mode** (str, None) - mode of power flow calculation type ("pf" - power flow, "opf" - \ optimal power flow or "sc" - short circuit). "mode" influences for instance whether opf \ cost data will be converted or which slack bus voltage limits are respected. If "mode" \ is None, cost data will be respected via mode="opf" if cost data are existing. OUTPUT: **ppc** - The Pypower casefile for usage with pypower EXAMPLE: import pandapower.converter as pc import pandapower.networks as pn net = pn.case9() ppc = pc.to_ppc(net) """ if (not (net["poly_cost"].empty and net["pwl_cost"].empty) and mode is None) or mode == "opf": mode = "opf" _check_necessary_opf_parameters(net, logger) else: mode = "pf" # select elements in service net["_options"] = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode=mode, switch_rx_ratio=switch_rx_ratio, init_vm_pu=init, init_va_degree=init, enforce_q_lims=True, recycle=None, voltage_depend_loads=voltage_depend_loads) # do the conversion _, ppci = _pd2ppc(net) ppci['branch'] = ppci['branch'].real # ppci.pop('internal') return ppci
if case not in ['max', 'min']: raise ValueError('case can only be "min" or "max" for minimal or maximal short "\ "circuit current') if topology not in ["meshed", "radial", "auto"]: raise ValueError( 'specify network structure as "meshed", "radial" or "auto"') if branch_results: logger.warning("Branch results are in beta mode and might not always be reliable, " "especially for transformers") kappa = ith or ip net["_options"] = {} _add_ppc_options(net, calculate_voltage_angles=False, trafo_model="pi", check_connectivity=False, mode="sc", switch_rx_ratio=2, init_vm_pu="flat", init_va_degree="flat", enforce_q_lims=False, recycle=None) _add_sc_options(net, fault=fault, case=case, lv_tol_percent=lv_tol_percent, tk_s=tk_s, topology=topology, r_fault_ohm=r_fault_ohm, kappa_method=kappa_method, x_fault_ohm=x_fault_ohm, kappa=kappa, ip=ip, ith=ith, branch_results=branch_results) if fault == "3ph": _calc_sc(net) if fault == "2ph": _calc_sc(net) if fault == "1ph": if case == "min": raise NotImplementedError("Minimum 1ph short-circuits are not yet implemented") _calc_sc_1ph(net)
def rundcopp(net, verbose=False, check_connectivity=True, suppress_warnings=True, r_switch=0.0, delta=1e-10, **kwargs): """ Runs the pandapower Optimal Power Flow. Flexibilities, constraints and cost parameters are defined in the pandapower element tables. Flexibilities for generators can be defined in net.sgen / net.gen. net.sgen.controllable / net.gen.controllable signals if a generator is controllable. If False, the active and reactive power are assigned as in a normal power flow. If yes, the following flexibilities apply: - net.sgen.min_p_kw / net.sgen.max_p_kw - net.gen.min_p_kw / net.gen.max_p_kw - net.load.min_p_kw / net.load.max_p_kw Network constraints can be defined for buses, lines and transformers the elements in the following columns: - net.line.max_loading_percent - net.trafo.max_loading_percent - net.trafo3w.max_loading_percent INPUT: **net** - The pandapower format network OPTIONAL: **verbose** (bool, False) - If True, some basic information is printed **suppress_warnings** (bool, True) - suppress warnings in pypower If set to True, warnings are disabled during the loadflow. Because of the way data is processed in pypower, ComplexWarnings are raised during the loadflow. These warnings are suppressed by this option, however keep in mind all other pypower warnings are suppressed, too. """ mode = "opf" ac = False init = "flat" copy_constraints_to_ppc = True trafo_model = "t" trafo_loading = 'current' calculate_voltage_angles = True enforce_q_lims = True recycle = dict(_is_elements=False, ppc=False, Ybus=False) net._options = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode=mode, copy_constraints_to_ppc=copy_constraints_to_ppc, r_switch=r_switch, init=init, enforce_q_lims=enforce_q_lims, recycle=recycle, voltage_depend_loads=False, delta=delta) _add_opf_options(net, trafo_loading=trafo_loading, ac=ac) _optimal_powerflow(net, verbose, suppress_warnings, **kwargs)
def runpp(net, algorithm='nr', calculate_voltage_angles="auto", init="auto", max_iteration="auto", tolerance_kva=1e-5, trafo_model="t", trafo_loading="current", enforce_q_lims=False, numba=True, recycle=None, check_connectivity=True, r_switch=0.0, voltage_depend_loads=True, delta_q=1e-10, **kwargs): """ Runs PANDAPOWER AC Flow Note: May raise pandapower.api.run["load"]flowNotConverged INPUT: **net** - The pandapower format network OPTIONAL: **algorithm** (str, "nr") - algorithm that is used to solve the power flow problem. The following algorithms are available: - "nr" newton-raphson (pypower implementation with numba accelerations) - "bfsw" backward/forward sweep (specially suited for radial and weakly-meshed networks) - "gs" gauss-seidel (pypower implementation) - "fdbx" (pypower implementation) - "fdxb"(pypower implementation) **calculate_voltage_angles** (bool, "auto") - consider voltage angles in loadflow calculation If True, voltage angles of ext_grids and transformer shifts are considered in the loadflow calculation. Considering the voltage angles is only necessary in meshed networks that are usually found in higher networks. Thats why calculate_voltage_angles in "auto" mode defaults to: - True, if the network voltage level is above 70 kV - False otherwise The network voltage level is defined as the maximum rated voltage in the network that is connected to a line. **init** (str, "auto") - initialization method of the loadflow pandapower supports four methods for initializing the loadflow: - "auto" - init defaults to "dc" if calculate_voltage_angles is True or "flat" otherwise - "flat"- flat start with voltage of 1.0pu and angle of 0° at all PQ-buses and 0° for PV buses as initial solution - "dc" - initial DC loadflow before the AC loadflow. The results of the DC loadflow are used as initial solution for the AC loadflow. - "results" - voltage vector of last loadflow from net.res_bus is used as initial solution. This can be useful to accelerate convergence in iterative loadflows like time series calculations. Considering the voltage angles might lead to non-convergence of the power flow in flat start. That is why in "auto" mode, init defaults to "dc" if calculate_voltage_angles is True or "flat" otherwise **max_iteration** (int, "auto") - maximum number of iterations carried out in the power flow algorithm. In "auto" mode, the default value depends on the power flow solver: - 10 for "nr" - 100 for "bfsw" - 1000 for "gs" - 30 for "fdbx" - 30 for "fdxb" **tolerance_kva** (float, 1e-5) - loadflow termination condition referring to P / Q mismatch of node power in kva **trafo_model** (str, "t") - transformer equivalent circuit model pandapower provides two equivalent circuit models for the transformer: - "t" - transformer is modeled as equivalent with the T-model. - "pi" - transformer is modeled as equivalent PI-model. This is not recommended, since it is less exact than the T-model. It is only recommended for valdiation with other software that uses the pi-model. **trafo_loading** (str, "current") - mode of calculation for transformer loading Transformer loading can be calculated relative to the rated current or the rated power. In both cases the overall transformer loading is defined as the maximum loading on the two sides of the transformer. - "current"- transformer loading is given as ratio of current flow and rated current of the transformer. This is the recommended setting, since thermal as well as magnetic effects in the transformer depend on the current. - "power" - transformer loading is given as ratio of apparent power flow to the rated apparent power of the transformer. **enforce_q_lims** (bool, False) - respect generator reactive power limits If True, the reactive power limits in net.gen.max_q_kvar/min_q_kvar are respected in the loadflow. This is done by running a second loadflow if reactive power limits are violated at any generator, so that the runtime for the loadflow will increase if reactive power has to be curtailed. Note: enforce_q_lims only works if algorithm="nr"! **numba** (bool, True) - Activation of numba JIT compiler in the newton solver If set to True, the numba JIT compiler is used to generate matrices for the powerflow, which leads to significant speed improvements. **recycle** (dict, none) - Reuse of internal powerflow variables for time series calculation Contains a dict with the following parameters: _is_elements: If True in service elements are not filtered again and are taken from the last result in net["_is_elements"] ppc: If True the ppc is taken from net["_ppc"] and gets updated instead of reconstructed entirely Ybus: If True the admittance matrix (Ybus, Yf, Yt) is taken from ppc["internal"] and not reconstructed **check_connectivity** (bool, True) - Perform an extra connectivity test after the conversion from pandapower to PYPOWER If True, an extra connectivity test based on SciPy Compressed Sparse Graph Routines is perfomed. If check finds unsupplied buses, they are set out of service in the ppc **r_switch** (float, 0.0) - resistance of bus-bus-switches. If impedance is zero, buses connected by a closed bus-bus switch are fused to model an ideal bus. Otherwise, they are modelled as branches with resistance r_switch. **voltage_depend_loads** (bool, True) - consideration of voltage-dependent loads. If False, net.load.const_z_percent and net.load.const_i_percent are not considered, i.e. net.load.p_kw and net.load.q_kvar are considered as constant-power loads. **delta_q** - Reactive power tolerance for option "enforce_q_lims" in kvar - helps convergence in some cases. ****kwargs** - options to use for PYPOWER.runpf """ ## check if numba is available and the corresponding flag if numba: try: # get numba Version (in order to use it it must be > 0.25) nb_version = float(numba_version.version_version[:4]) if nb_version < 0.25: logger.warning( 'Warning: Numba version too old -> Upgrade to a version > 0.25. Numba is disabled\n' ) numba = False except: logger.warning( 'Warning: Numba cannot be imported.' ' Numba is disabled. Call runpp() with numba=False to avoid this warning!\n' ) numba = False if voltage_depend_loads: if not (np.any(net["load"]["const_z_percent"].values) or np.any(net["load"]["const_i_percent"].values)): voltage_depend_loads = False if algorithm not in ['nr', 'bfsw'] and voltage_depend_loads == True: logger.warning( "voltage-dependent loads not supported for {0} power flow algorithm -> " "loads will be considered as constant power".format(algorithm)) ac = True mode = "pf" copy_constraints_to_ppc = False if calculate_voltage_angles == "auto": calculate_voltage_angles = False hv_buses = np.where(net.bus.vn_kv.values > 70)[0] if len(hv_buses) > 0: line_buses = net.line[["from_bus", "to_bus"]].values.flatten() if len(set(net.bus.index[hv_buses]) & set(line_buses)) > 0: calculate_voltage_angles = True if init == "auto": init = "dc" if calculate_voltage_angles else "flat" default_max_iteration = { "nr": 10, "bfsw": 100, "gs": 10000, "fdxb": 30, "fdbx": 30 } if max_iteration == "auto": max_iteration = default_max_iteration[algorithm] # init options net._options = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode=mode, copy_constraints_to_ppc=copy_constraints_to_ppc, r_switch=r_switch, init=init, enforce_q_lims=enforce_q_lims, recycle=recycle, voltage_depend_loads=voltage_depend_loads, delta=delta_q) _add_pf_options(net, tolerance_kva=tolerance_kva, trafo_loading=trafo_loading, numba=numba, ac=ac, algorithm=algorithm, max_iteration=max_iteration) _powerflow(net, **kwargs)
def rundcpp(net, trafo_model="t", trafo_loading="current", recycle=None, check_connectivity=True, r_switch=0.0, **kwargs): """ Runs PANDAPOWER DC Flow INPUT: **net** - The pandapower format network OPTIONAL: **trafo_model** (str, "t") - transformer equivalent circuit model pandapower provides two equivalent circuit models for the transformer: - "t" - transformer is modeled as equivalent with the T-model. This is consistent with PowerFactory and is also more accurate than the PI-model. We recommend using this transformer model. - "pi" - transformer is modeled as equivalent PI-model. This is consistent with Sincal, but the method is questionable since the transformer is physically T-shaped. We therefore recommend the use of the T-model. **trafo_loading** (str, "current") - mode of calculation for transformer loading Transformer loading can be calculated relative to the rated current or the rated power. In both cases the overall transformer loading is defined as the maximum loading on the two sides of the transformer. - "current"- transformer loading is given as ratio of current flow and rated current of the transformer. This is the recommended setting, since thermal as well as magnetic effects in the transformer depend on the current. - "power" - transformer loading is given as ratio of apparent power flow to the rated apparent power of the transformer. **recycle** (dict, none) - Reuse of internal powerflow variables for time series calculation Contains a dict with the following parameters: _is_elements: If True in service elements are not filtered again and are taken from the last result in net["_is_elements"] ppc: If True the ppc (PYPOWER case file) is taken from net["_ppc"] and gets updated instead of reconstructed entirely Ybus: If True the admittance matrix (Ybus, Yf, Yt) is taken from ppc["internal"] and not reconstructed **check_connectivity** (bool, False) - Perform an extra connectivity test after the conversion from pandapower to PYPOWER If true, an extra connectivity test based on SciPy Compressed Sparse Graph Routines is perfomed. If check finds unsupplied buses, they are put out of service in the PYPOWER matrix **r_switch** (float, 0.0) - resistance of bus-bus-switches. If impedance is zero, buses connected by a closed bus-bus switch are fused to model an ideal bus. Otherwise, they are modelled as branches with resistance r_switch ****kwargs** - options to use for PYPOWER.runpf """ ac = False numba = True mode = "pf" init = 'flat' # the following parameters have no effect if ac = False calculate_voltage_angles = True copy_constraints_to_ppc = False enforce_q_lims = False algorithm = None max_iteration = None tolerance_kva = None net._options = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode=mode, copy_constraints_to_ppc=copy_constraints_to_ppc, r_switch=r_switch, init=init, enforce_q_lims=enforce_q_lims, recycle=recycle, voltage_depend_loads=False, delta=0) _add_pf_options(net, tolerance_kva=tolerance_kva, trafo_loading=trafo_loading, numba=numba, ac=ac, algorithm=algorithm, max_iteration=max_iteration) _powerflow(net, **kwargs)
def runpm(net, julia_file=None, pp_to_pm_callback=None, calculate_voltage_angles=True, trafo_model="t", delta=1e-8, trafo3w_losses="hv", check_connectivity=True, correct_pm_network_data=True, pm_model="ACPPowerModel", pm_solver="ipopt", pm_mip_solver="cbc", pm_nl_solver="ipopt", pm_time_limits=None, pm_log_level=0, report_duals=False, branch_limits="hard", objective="cost"): # pragma: no cover """ Runs a power system optimization using PowerModels.jl. with a custom julia file. Flexibilities, constraints and cost parameters are defined in the pandapower element tables. Flexibilities can be defined in net.sgen / net.gen /net.load net.sgen.controllable if a static generator is controllable. If False, the active and reactive power are assigned as in a normal power flow. If True, the following flexibilities apply: - net.sgen.min_p_mw / net.sgen.max_p_mw - net.sgen.min_q_mvar / net.sgen.max_q_mvar - net.load.min_p_mw / net.load.max_p_mw - net.load.min_q_mvar / net.load.max_q_mvar - net.gen.min_p_mw / net.gen.max_p_mw - net.gen.min_q_mvar / net.gen.max_q_mvar - net.ext_grid.min_p_mw / net.ext_grid.max_p_mw - net.ext_grid.min_q_mvar / net.ext_grid.max_q_mvar - net.dcline.min_q_to_mvar / net.dcline.max_q_to_mvar / net.dcline.min_q_from_mvar / net.dcline.max_q_from_mvar Controllable loads behave just like controllable static generators. It must be stated if they are controllable. Otherwise, they are not respected as flexibilities. Dc lines are controllable per default Network constraints can be defined for buses, lines and transformers the elements in the following columns: - net.bus.min_vm_pu / net.bus.max_vm_pu - net.line.max_loading_percent - net.trafo.max_loading_percent - net.trafo3w.max_loading_percent How these costs are combined into a cost function depends on the cost_function parameter. INPUT: **net** - The pandapower format network OPTIONAL: **julia_file** (str, None) - path to a custom julia optimization file **pp_to_pm_callback** (function, None) - callback function to add data to the PowerModels data structure **correct_pm_network_data** (bool, True) - checks if network data is correct. If not tries to correct it **pm_model** (str, "ACPPowerModel") - The PowerModels.jl model to use **pm_solver** (str, "ipopt") - The "main" power models solver **pm_mip_solver** (str, "cbc") - The mixed integer solver (when "main" solver == juniper) **pm_nl_solver** (str, "ipopt") - The nonlinear solver (when "main" solver == juniper) **pm_time_limits** (Dict, None) - Time limits in seconds for power models interface. To be set as a dict like {"pm_time_limit": 300., "pm_nl_time_limit": 300., "pm_mip_time_limit": 300.} **pm_log_level** (int, 0) - solver log level in power models ** report_duals ** (bool, False) - whether or not the dual variables should be reported ** branch_limits ** (str, "hard") - how the power flow of the branches should be imposed - "hard": impose hard limits on the branch flows. any violation means divergence - "soft": violates the power flow restrictions in the branches as little as possible - "none": impose no restrictions on the branch power flows ** objective ** (str, "cost") - the objective function to be used in the DC optimal power flow - "cost": minimize the overall generation costs - "flow": minimize the sum of squares of branch flows - "cost-flow": minimize "cost" + "flow" (no weights are added) - "cost-fuel": minimize PowerModels.objective_min_fuel_and_cost_polynomial """ net._options = {} ac = True if "DC" not in pm_model else False julia_file = os.path.join( pp_dir, "opf", 'run_powermodels.jl') if julia_file is None else julia_file _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode="opf", switch_rx_ratio=2, init_vm_pu="flat", init_va_degree="flat", enforce_q_lims=True, recycle=dict(_is_elements=False, ppc=False, Ybus=False), voltage_depend_loads=False, delta=delta, trafo3w_losses=trafo3w_losses) _add_opf_options(net, trafo_loading='power', ac=ac, init="flat", numba=True, pp_to_pm_callback=pp_to_pm_callback, julia_file=julia_file, pm_solver=pm_solver, pm_model=pm_model, correct_pm_network_data=correct_pm_network_data, pm_mip_solver=pm_mip_solver, pm_nl_solver=pm_nl_solver, pm_time_limits=pm_time_limits, pm_log_level=pm_log_level, report_duals=report_duals, branch_limits=branch_limits, objective=objective) _runpm(net)
def runopp(net, verbose=False, calculate_voltage_angles=False, check_connectivity=False, suppress_warnings=True, r_switch=0.0, delta=1e-10, init="flat", numba=True, trafo3w_losses="hv", **kwargs): """ Runs the pandapower Optimal Power Flow. Flexibilities, constraints and cost parameters are defined in the pandapower element tables. Flexibilities can be defined in net.sgen / net.gen /net.load net.sgen.controllable if a static generator is controllable. If False, the active and reactive power are assigned as in a normal power flow. If True, the following flexibilities apply: - net.sgen.min_p_kw / net.sgen.max_p_kw - net.sgen.min_q_kvar / net.sgen.max_q_kvar - net.load.min_p_kw / net.load.max_p_kw - net.load.min_q_kvar / net.load.max_q_kvar - net.gen.min_p_kw / net.gen.max_p_kw - net.gen.min_q_kvar / net.gen.max_q_kvar - net.ext_grid.min_p_kw / net.ext_grid.max_p_kw - net.ext_grid.min_q_kvar / net.ext_grid.max_q_kvar - net.dcline.min_q_to_kvar / net.dcline.max_q_to_kvar / net.dcline.min_q_from_kvar / net.dcline.max_q_from_kvar Controllable loads behave just like controllable static generators. It must be stated if they are controllable. Otherwise, they are not respected as flexibilities. Dc lines are controllable per default Network constraints can be defined for buses, lines and transformers the elements in the following columns: - net.bus.min_vm_pu / net.bus.max_vm_pu - net.line.max_loading_percent - net.trafo.max_loading_percent - net.trafo3w.max_loading_percent How these costs are combined into a cost function depends on the cost_function parameter. INPUT: **net** - The pandapower format network OPTIONAL: **verbose** (bool, False) - If True, some basic information is printed **suppress_warnings** (bool, True) - suppress warnings in pypower If set to True, warnings are disabled during the loadflow. Because of the way data is processed in pypower, ComplexWarnings are raised during the loadflow. These warnings are suppressed by this option, however keep in mind all other pypower warnings are suppressed, too. **init** (str, "flat") - init of starting opf vector. Options are "flat" or "pf" Starting solution vector (x0) for opf calculations is determined by this flag. Options are: "flat" (default): starting vector is (upper bound - lower bound) / 2 "pf": a power flow is executed prior to the opf and the pf solution is the starting vector. This may improve convergence, but takes a longer runtime (which are probably neglectible for opf calculations) """ _check_necessary_opf_parameters(net, logger) if numba: numba = _check_if_numba_is_installed(numba) mode = "opf" ac = True copy_constraints_to_ppc = True trafo_model = "t" trafo_loading = 'current' enforce_q_lims = True recycle = dict(_is_elements=False, ppc=False, Ybus=False) net._options = {} _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles, trafo_model=trafo_model, check_connectivity=check_connectivity, mode=mode, copy_constraints_to_ppc=copy_constraints_to_ppc, r_switch=r_switch, init=init, enforce_q_lims=enforce_q_lims, recycle=recycle, voltage_depend_loads=False, delta=delta, trafo3w_losses=trafo3w_losses) _add_opf_options(net, trafo_loading=trafo_loading, ac=ac, numba=numba) _check_bus_index_and_print_warning_if_high(net) _check_gen_index_and_print_warning_if_high(net) _optimal_powerflow(net, verbose, suppress_warnings, **kwargs)