def runpp(net, algorithm='nr', calculate_voltage_angles="auto", init="auto", max_iteration="auto", tolerance_mva=1e-8, trafo_model="t", trafo_loading="current", enforce_q_lims=False, check_connectivity=True, voltage_depend_loads=True, consider_line_temperature=False, run_control=False, distributed_slack=False, **kwargs): """ Runs a power 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) - "iwamoto_nr" Newton-Raphson with Iwamoto multiplier (maybe slower than NR but more robust) - "bfsw" backward/forward sweep (specially suited for radial and weakly-meshed networks) - "gs" gauss-seidel (pypower implementation) - "fdbx" fast-decoupled (pypower implementation) - "fdxb" fast-decoupled (pypower implementation) **calculate_voltage_angles** (str or 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. **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, the slack bus is initialized with the values provided in net["ext_grid"] - "dc" - initial DC loadflow before the AC loadflow. The results of the DC loadflow are used as initial solution for the AC loadflow. Note that the DC loadflow only calculates voltage angles at PQ and PV buses, voltage magnitudes are still flat started. - "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_mva** (float, 1e-8) - loadflow termination condition referring to P / Q mismatch of node power in MVA **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_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) - 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) - 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 **distributed_slack** (bool, False) - Distribute slack power according to contribution factor weights for external grids and generators. **KWARGS**: **lightsim2grid** ((bool,str), "auto") - whether to use the package lightsim2grid for power flow backend **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) - 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** - Reactive power tolerance for option "enforce_q_lims" in kvar - helps convergence in some cases. **trafo3w_losses** - 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) - if True, voltage values in each newton-raphson iteration are logged in the ppc **init_vm_pu** (string/float/array/Series, None) - 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) - 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 trafo: If True trafo relevant variables, e.g., the Ybus matrix, is recalculated gen: If True Sbus and the gen table in the ppc are recalculated **neglect_open_switch_branches** (bool, False) - If True no auxiliary buses are created for branches when switches are opened at the branch. Instead branches are set out of service """ # if dict 'user_pf_options' is present in net, these options overrule the net._options # except for parameters that are passed by user if isinstance(kwargs.get("recycle", None), dict) and _internal_stored(net): _recycled_powerflow(net, **kwargs) return if run_control and net.controller.in_service.any(): from pandapower.control import run_control parameters = {**locals(), **kwargs} # disable run control for inner loop to avoid infinite loop parameters["run_control"] = False run_control(**parameters) else: passed_parameters = _passed_runpp_parameters(locals()) _init_runpp_options( net, algorithm=algorithm, calculate_voltage_angles=calculate_voltage_angles, init=init, max_iteration=max_iteration, tolerance_mva=tolerance_mva, trafo_model=trafo_model, trafo_loading=trafo_loading, enforce_q_lims=enforce_q_lims, check_connectivity=check_connectivity, voltage_depend_loads=voltage_depend_loads, consider_line_temperature=consider_line_temperature, distributed_slack=distributed_slack, passed_parameters=passed_parameters, **kwargs) _check_bus_index_and_print_warning_if_high(net) _check_gen_index_and_print_warning_if_high(net) _powerflow(net, **kwargs)
def validate_pf_conversion(net, is_unbalanced=False, **kwargs): """ Trys to run a Loadflow with the converted pandapower network. If the loadflow converges, \ PowerFactory and pandapower loadflow results will be compared. Note that a pf validation can \ only be done if there are pf results available. INPUT: - **net** (PandapowerNetwork) - converted pandapower network OUTPUT: - **all_diffs** (list) - returns a list with the difference in all validated values. If all values are zero -> results are equal """ logger.debug('starting verification') toolbox.replace_zero_branches_with_switches(net) pf_results = _get_pf_results(net, is_unbalanced=is_unbalanced) if "controller" not in net.keys() or len(net.controller) == 0: try: for arg in 'trafo_model check_connectivity'.split(): if arg in kwargs: kwargs.pop(arg) if is_unbalanced: logger.info("running pandapower 3ph loadflow") pp.runpp_3ph(net, trafo_model="t", check_connectivity=True, **kwargs) else: logger.info("running pandapower loadflow") pp.runpp(net, trafo_model="t", check_connectivity=True, **kwargs) except Exception as err: logger.error('pandapower load flow failed: %s' % err, exc_info=True) diagnostic(net) raise err else: import pandapower.control as control try: control.run_control(net, max_iter=50, trafo_model="t", check_connectivity=True, **kwargs) except Exception as err: logger.error('pandapower load flow with converter failed: %s' % err) diagnostic(net) raise err all_diffs = dict() logger.info('pandapower net converged: %s' % net.converged) _set_pf_results(net, pf_results, is_unbalanced=is_unbalanced) net.bus.name.fillna("", inplace=True) only_in_pandapower = set(net.bus[net.bus.name.str.endswith("_aux")].index) | \ set(net.bus[net.bus.type == "ls"].index) in_both = set(net.bus.index) - only_in_pandapower pf_closed = pf_results['pf_switch_status'] wrong_switches = net.res_switch.loc[pf_closed != net.switch.loc[ pf_closed.index, 'closed']].index.values if 'res_switch' in net.keys() else [] if len(net.switch) > 0: logger.info('%d switches are wrong: %s' % (len(wrong_switches), wrong_switches)) if len(net.trafo3w[net.trafo3w.in_service]) > 0: trafo3w_idx = net.trafo3w.query('in_service').index trafo3w_diff = net.res_trafo3w.loc[ trafo3w_idx].pf_loading - net.res_trafo3w.loc[ trafo3w_idx].loading_percent trafo3w_id = abs(trafo3w_diff).idxmax() logger.info( "Maximum trafo3w loading difference between pandapower and powerfactory: %.1f " "percent at trafo3w %d (%s)" % (max(abs(trafo3w_diff)), trafo3w_id, net.trafo3w.at[trafo3w_id, 'name'])) all_diffs["trafo3w_diff"] = trafo3w_diff if len(net.sgen[net.sgen.in_service]) > 0: logger.debug('verifying sgen') sgen_p_diff = net.res_sgen.pf_p.replace(np.nan, 0) - net.res_sgen.p_mw sgen_q_diff = net.res_sgen.pf_q.replace(np.nan, 0) - net.res_sgen.q_mvar sgen_p_diff_is = net.res_sgen.pf_p.replace(np.nan, 0) * net.sgen.loc[ net.res_sgen.index, 'in_service'] - net.res_sgen.p_mw sgen_q_diff_is = net.res_sgen.pf_q.replace(np.nan, 0) * net.sgen.loc[ net.res_sgen.index, 'in_service'] - net.res_sgen.q_mvar logger.info( "Maximum sgen active power difference between pandapower and powerfactory: " "%.1f MW, in service only: %.1f MW" % (max(abs(sgen_p_diff)), max(abs(sgen_p_diff_is)))) logger.info( "Maximum sgen reactive power difference between pandapower and powerfactory: " "%.1f Mvar, in service only: %.1f Mvar" % (max(abs(sgen_q_diff)), max(abs(sgen_q_diff_is)))) all_diffs["sgen_p_diff_is"] = sgen_p_diff_is all_diffs["sgen_q_diff_is"] = sgen_q_diff_is if len(net.gen[net.gen.in_service]) > 0: logger.debug('verifying gen') gen_p_diff = net.res_gen.pf_p.replace(np.nan, 0) - net.res_gen.p_mw gen_q_diff = net.res_gen.pf_q.replace(np.nan, 0) - net.res_gen.q_mvar gen_p_diff_is = net.res_gen.pf_p.replace(np.nan, 0) * net.gen.loc[ net.res_gen.index, 'in_service'] - net.res_gen.p_mw gen_q_diff_is = net.res_gen.pf_q.replace(np.nan, 0) * net.gen.loc[ net.res_gen.index, 'in_service'] - net.res_gen.q_mvar logger.info( "Maximum gen active power difference between pandapower and powerfactory: " "%.1f MW, in service only: %.1f MW" % (max(abs(gen_p_diff)), max(abs(gen_p_diff_is)))) logger.info( "Maximum gen reactive power difference between pandapower and powerfactory: " "%.1f Mvar, in service only: %.1f Mvar" % (max(abs(gen_q_diff)), max(abs(gen_q_diff_is)))) all_diffs["gen_p_diff_is"] = gen_p_diff_is all_diffs["gen_q_diff_is"] = gen_q_diff_is if len(net.ward[net.ward.in_service]) > 0: logger.debug('verifying ward') ward_p_diff = net.res_ward.pf_p.replace(np.nan, 0) - net.res_ward.p_mw ward_q_diff = net.res_ward.pf_q.replace(np.nan, 0) - net.res_ward.q_mvar ward_p_diff_is = net.res_ward.pf_p.replace(np.nan, 0) * net.ward.loc[ net.res_ward.index, 'in_service'] - net.res_ward.p_mw ward_q_diff_is = net.res_ward.pf_q.replace(np.nan, 0) * net.ward.loc[ net.res_ward.index, 'in_service'] - net.res_ward.q_mvar logger.info( "Maximum ward active power difference between pandapower and powerfactory: " "%.1f MW, in service only: %.1f MW" % (max(abs(ward_p_diff)), max(abs(ward_p_diff_is)))) logger.info( "Maximum ward reactive power difference between pandapower and powerfactory: " "%.1f Mvar, in service only: %.1f Mvar" % (max(abs(ward_q_diff)), max(abs(ward_q_diff_is)))) all_diffs["ward_p_diff_is"] = ward_p_diff_is all_diffs["ward_q_diff_is"] = ward_q_diff_is if is_unbalanced: _validate_pf_conversion_unbalanced(net, in_both, all_diffs) else: _validate_pf_conversion_balanced(net, in_both, all_diffs) return all_diffs