vp.power.runpf
Solve the AC or DC power flow of a MATPOWER case: bus voltages, generator outputs and branch flows.
Part of the distribution vipyr; it runs from a checkout of the
repository. Planned on PyPI as pip install vipyr.
vp.power.runpf is MATPOWER's runpf: give it a case and it returns the
steady state of the network, every bus voltage (magnitude and angle), every
generator's real and reactive output and the power entering each end of
every branch, in MATPOWER's units and column order.
The case is a MATPOWER case file, by name for the ones shipped with vipyr
("case9", "case14", "case30", "case118", "case300",
"case2383wp", from the MATPOWER repository), by path for any other .m
file in MATPOWER's case format, or as a dict of MATPOWER's arrays
(baseMVA, bus, gen, branch), the form PYPOWER and pandapower call
ppc.
Three methods, as in MATPOWER:
"nr": Newton-Raphson in polar coordinates, the default. Converges in 2 to 6 iterations on the shipped cases."fdxb": the fast-decoupled method, XB version. Two constant matrices, factored once; more iterations, each much cheaper."dc": the linear DC model (flat voltage magnitudes, lossless branches), one sparse solve.
enforce_q_lims=True holds every generator that would exceed its reactive
limits at the limit and solves again, as MATPOWER's ENFORCE_Q_LIMS option
does; a slack generator held at a limit hands the reference role to the
first remaining PV bus, and the angles are shifted back afterwards.
The whole computation runs in compiled Viv: the case's admittance matrix and Jacobian are assembled sparse, and each Newton step is solved by the sparse LU of lib/power/splu.viv (left-looking, threshold partial pivoting, a minimum-degree column order). The first call compiles the solver once (a few seconds); later calls load it from vipyr's cache.
Signature
vp.power.runpf(case, *, method='nr', enforce_q_lims=False, tol=1e-08, max_it=None) Parameters
- case : str | dict, required
- A shipped MATPOWER case by name ('case9', 'case14', 'case30', 'case118', 'case300', 'case2383wp'), a path to a MATPOWER .m file, or a dict with baseMVA, bus, gen and branch (MATPOWER's columns, as in PYPOWER's ppc).
- method : str, default
'nr' - 'nr' (Newton-Raphson, polar), 'fdxb' (fast-decoupled, XB) or 'dc'.
- enforce_q_lims : bool, default
False - Hold generators at their reactive limits, turning their buses into PQ buses, and solve again until no limit is violated (AC methods only).
- tol : float, default
1e-08 - The largest power mismatch allowed, per unit (MATPOWER's pf.tol).
- max_it : int, default
None - The most iterations: None for MATPOWER's defaults, 10 for 'nr' and 30 for 'fdxb'.
Returns
A frozen PowerFlowResult. Iterating it gives the fields below in order; it also has
params, tests, log and check().
- success : bool
- Whether the flow converged.
- iterations : int
- Newton or fast-decoupled iterations (summed over the Q-limit passes).
- method : str
- The method used.
- mismatch : float
- The largest power mismatch left, per unit (0 for 'dc').
- vm : ndarray[float64]
- Bus voltage magnitudes, per unit, in the case's bus order.
- va : ndarray[float64]
- Bus voltage angles, degrees.
- pg : ndarray[float64]
- Generator real outputs, MW, in the case's gen order.
- qg : ndarray[float64]
- Generator reactive outputs, MVAr.
- pf : ndarray[float64]
- Real power entering each branch at its from end, MW.
- qf : ndarray[float64]
- Reactive power entering each branch at its from end, MVAr.
- pt : ndarray[float64]
- Real power entering each branch at its to end, MW.
- qt : ndarray[float64]
- Reactive power entering each branch at its to end, MVAr.
- bus : ndarray[float64]
- The bus matrix with VM and VA filled, MATPOWER's columns.
- gen : ndarray[float64]
- The gen matrix with PG and QG filled.
- branch : ndarray[float64]
- The branch matrix widened to 17 columns, PF, QF, PT, QT filled.
- baseMVA : float
- The system base, MVA.
Raises
- ValueError
- An unknown method, a case name that is not shipped, or a case dict without baseMVA, bus, gen and branch.
- module.VivStop
- The case has no bus that can be the reference, or every generator is at a reactive limit.
- TypeError
- An argument of the wrong type, or an unknown keyword.
- vipyr.VivBuildError
- An ImportError: no C compiler, or the program's C does not build.
Notes
The equations. With bus voltages and the bus admittance matrix , the power injected at bus is . The power flow solves : the real part at every PV and PQ bus, the imaginary part at every PQ bus, for the angles of the PV and PQ buses and the magnitudes of the PQ buses.
Newton-Raphson. The Jacobian has the closed forms and , assembled entry by entry from 's sparsity. The minimum-degree order is found once, from the first Jacobian; every iteration factors anew, with row pivoting decided on the values.
Fast-decoupled. is of the network with no shunts, no line charging, unit tap ratios and no series resistance; is with no phase shifts. An iteration solves , then .
DC. with , the reference angle held fixed; branch flows are plus the phase shifter's term.
Generator outputs. After the solve the reference bus's first generator
takes the slack in P, and Q at a bus with several generators is shared in
proportion to their Q ranges, as MATPOWER's pfsoln does.
References
- R. D. Zimmerman, C. E. Murillo-Sanchez, R. J. Thomas, MATPOWER: steady-state operations, planning and analysis tools for power systems research and education, IEEE Trans. Power Systems 26 (2011) 12-19 doi:10.1109/TPWRS.2010.2051168
- MATPOWER (cases and the reference solution soln9_pf.mat) https://github.com/MATPOWER/matpower
- B. Stott, O. Alsac, Fast decoupled load flow, IEEE Trans. PAS 93 (1974) 859-869 doi:10.1109/TPAS.1974.293985
- R. A. M. van Amerongen, A general-purpose version of the fast decoupled load flow, IEEE Trans. Power Systems 4 (1989) 760-770 doi:10.1109/59.193851
- J. R. Gilbert, T. Peierls, Sparse partial pivoting in time proportional to arithmetic operations, SIAM Journal on Scientific and Statistical Computing 9 (1988) 862-874 doi:10.1137/0909058
Examples
import vipyr as vp
r = vp.power.runpf("case118") # Newton-Raphson
print(r.success, r.iterations, r.vm.min(), r.va.max())
d = vp.power.runpf("case118", method="dc") # the linear model
print(abs(d.va - r.va).max()) # degrees between the two
assert r.check() True 3 0.9429999999999998 39.74834338937072
5.309803267993082Each example ran as written when these pages were generated, and the output shown is what it printed.
Checked against
- Reference
- MATPOWER's own solution of its test case t_case9_pf (lib/t/soln9_pf.mat and soln9_dcpf.mat): every voltage, generator and branch result to 10−12 by NR and DC, and by FDXB to its convergence tolerance; and an independent NumPy/SciPy power flow (lib/power/oracle/pf_scipy.py) on case9 to case2383wp, where the largest differences are 2 × 10−13 per unit in Vm and 4 × 10−11 degrees in Va by NR (lib/power/compare.py, lib/power/COMPARISON.md).
Credits
- The methods follow MATPOWER's (BSD-3-Clause). The test cases are MATPOWER's case files, each with its source named in its header.
Source
- Viv: lib/power/api.viv
- Viv: lib/power/pf.viv
- Viv: lib/power/ybus.viv
- Viv: lib/power/splu.viv
- Viv: lib/power/case.viv