MATPOWER-compatible OPF#
The MATPOWER-compatible backend is the default OPF implementation in pwrs.
It follows the MATPOWER case-to-model pipeline, exposes the familiar
MP-Opt-Model result structures, and is the appropriate choice when behavioral
compatibility with MATPOWER is more important than selecting among many
alternative network formulations.
Run the default AC OPF#
No backend option is required. The following explicit assignment can be useful in applications that allow users to switch backends:
import pwrs as mp
mpopt = mp.MatpowerConfig()
mpopt.opf.backend = "MATPOWER"
mpopt.verbose = 0
mpopt.out.all = 0
result = mp.runopf(mp.case30(), mpopt)
if not result.success:
raise RuntimeError("MATPOWER-compatible AC OPF did not converge")
print(result.f)
print(result.gen)
For AC OPF, opf.ac.solver = "DEFAULT" resolves to MIPS. MIPS is the
project’s primal-dual interior-point solver and requires no external
optimization package.
Configure MIPS#
Use the mips section of MatpowerConfig to change its termination and
iteration controls:
import pwrs as mp
mpopt = mp.MatpowerConfig()
mpopt.opf.ac.solver = "MIPS"
mpopt.opf.violation = 5e-6
mpopt.mips.feastol = 5e-6
mpopt.mips.gradtol = 1e-6
mpopt.mips.comptol = 1e-6
mpopt.mips.costtol = 1e-6
mpopt.mips.max_it = 200
mpopt.verbose = 0
mpopt.out.all = 0
result = mp.runopf(mp.case118(), mpopt)
Leaving mips.feastol at zero derives it from opf.violation. Increase
max_it only after checking the model and the convergence trace. Relaxing
feasibility or optimality tolerances can make a marginal solve appear
successful without fixing the underlying modeling or scaling problem.
Set mips.linsolver to select the linear solver used by MIPS:
Option |
Backend |
Dependency |
Description |
|---|---|---|---|
|
SciPy/NumPy default |
|
Uses SciPy’s sparse solver for sparse matrices and NumPy’s dense solver otherwise. |
|
SuperLU |
|
Explicitly uses SciPy SuperLU with minimum-degree ordering. The MATPOWER-compatible aliases have the same behavior in pwrs. |
|
SuiteSparse UMFPACK |
|
Uses UMFPACK for sparse direct solves. pwrs intentionally does not distinguish the MATLAB four- and five-output LU forms. |
|
Intel MKL PARDISO |
|
Uses the multithreaded sparse direct solver exposed by PyPardiso. |
|
SuiteSparse KLU |
|
Uses KLU with block triangular form reordering disabled, matching the MIPS integration. |
The optional Python packages are included in the all dependency group and
can be installed with uv sync --group all. If a selected optional solver or
its native library is unavailable, pwrs emits a RuntimeWarning and falls
back to SciPy SuperLU.
Select the AC formulation#
The default MATPOWER-compatible AC formulation uses power-balance equations and polar voltage variables. Two options expose the other formulation choices:
Option |
Value |
Formulation |
|---|---|---|
|
|
Complex power balance (default). |
|
|
Complex current balance. |
|
|
Polar voltage variables (default). |
|
|
Cartesian voltage variables. |
For example:
mpopt = mp.MatpowerConfig()
mpopt.opf.current_balance = 1
mpopt.opf.v_cartesian = 1
mpopt.opf.ac.solver = "MIPS"
Other useful controls are:
opf.start: use the solver default (0), an interior initialization (1), the case state (2), or a preliminary power-flow solution (3).opf.flow_lim: enforce apparent power ("S"), active power ("P"), squared active power ("2"), or current magnitude ("I") at rated branches.opf.ignore_angle_lim: ignore branch angle-difference limits when set to1.opf.use_vg: control how generator voltage setpoints affect voltage bounds.opf.return_raw_der: include raw derivative data for detailed diagnostics.
The standard power-balance/polar formulation is the best starting point. Change one formulation option at a time when investigating numerical behavior.
Run a DC OPF#
rundcopf() is the clearest high-level entry point because it sets the model
to "DC" while preserving the remaining options:
import pwrs as mp
from pwrs.core.idx_bus import BUS_I, LAM_P, VA
from pwrs.core.idx_gen import GEN_BUS, PG
mpopt = mp.MatpowerConfig()
mpopt.opf.backend = "MATPOWER"
mpopt.verbose = 0
mpopt.out.all = 0
result = mp.rundcopf(mp.case30(), mpopt)
if not result.success:
raise RuntimeError("MATPOWER-compatible DC OPF failed")
print(result.bus[:, [BUS_I, VA, LAM_P]])
print(result.gen[:, [GEN_BUS, PG]])
MATPOWER user costs and constraints#
Choose this backend when the case uses MATPOWER’s generalized OPF extension
fields, including user linear constraints (A, l, and u) or generalized
cost fields (N, fparm, H, Cw). These fields are incorporated by the
MATPOWER-compatible setup pipeline and are retained in the structured result.
For reusable MATPOWER-style callbacks, use the userfcn mechanism and the
corresponding low-level OPF model APIs. The PowerModels-compatible backend has
a separate extension interface and does not currently consume these MATPOWER
user constraint or generalized cost fields.
Inspect optimization results#
The solved matrices contain the most portable economic and engineering results:
result.bus:LAM_P,LAM_Q,MU_VMIN, andMU_VMAX;result.gen:PG,QG, and generator bound multipliers;result.branch: terminal flows and branch flow/angle multipliers.
The MATPOWER path also exposes model-level structures through result.x,
result.mu, result.var, result.lin, result.nle, and result.nli.
These are useful for advanced analysis, but their grouping and ordering follow
the selected formulation. Prefer solved matrix columns for application-level
interfaces that must remain stable across formulations.
Backend diagnostics remain in result.raw. For example:
if result.raw is not None:
print(result.raw.get("info"))
print(result.raw.get("output"))
Low-level workflow#
Most applications should use runopf() or rundcopf(). For custom
MATPOWER-compatible models, the lower-level lifecycle is:
opf_setup()constructs the OPF model.opf_execute()dispatches the selected nonlinear or quadratic solver.int2ext()and the high-level runner restore external indexing and out-of-service rows.
The API Reference documents these functions and the objective, constraint, Jacobian, and Hessian helpers. Their mathematical roles are described in OPF solution process.
Troubleshooting#
When MIPS does not converge:
Set
verbose = 2and keep the default power-balance/polar formulation.Check generator, voltage, branch, and angle bounds for contradictions.
Confirm every active island has an online generator and a reference bus.
Inspect the initial
VM,VA,PG, andQG, or tryopf.start = 3.Check cost scaling and avoid very different coefficient magnitudes where possible.
Compare an AC failure with DC OPF only as a diagnostic; DC feasibility does not imply AC feasibility.
Inspect the solver status and iteration metadata in
result.rawbefore changing tolerances.