MATPOWER .m¶
Reading a MATPOWER case file produces a PowerNetwork for the
power flow of the electrical application.
noodl.apps.electrical.power.read_matpower(path) reads the case format of MATPOWER version 2
(mpc.version = '2'), the format of MATPOWER's standard cases, within the limits listed
below (no DC lines, no indexed assignments, no
define_constants). A case file is a MATLAB function that fills a struct mpc; the reader
parses its text and never runs MATLAB or Octave. The network keeps the file's units (MW, MVAr,
degrees, p.u. impedances on baseMVA); the power-flow builders convert to per unit.
A four-bus case written for this page (the network of the power-flow example), saved and read back:
from pathlib import Path
from noodl.apps.electrical.power import build_power_flow, read_matpower, solve_power_flow
Path("four_bus.m").write_text("""function mpc = four_bus
%FOUR_BUS A 132 kV triangle feeding a 33 kV bus through a tap-changing transformer.
mpc.version = '2';
mpc.baseMVA = 100;
% bus_i type Pd Qd Gs Bs area Vm Va baseKV zone Vmax Vmin
mpc.bus = [
1 3 0 0 0 0 1 1 0 132 1 1.1 0.9;
2 2 20 5 0 0 1 1 0 132 1 1.1 0.9;
3 1 60 20 0 0 1 1 0 132 1 1.1 0.9;
4 1 45 15 0 8 1 1 0 33 1 1.1 0.9;
];
% bus Pg Qg Qmax Qmin Vg mBase status Pmax Pmin
mpc.gen = [
1 0 0 100 -100 1.02 100 1 200 0;
2 50 0 60 -60 1.01 100 1 80 0;
];
% fbus tbus r x b rateA rateB rateC ratio angle status angmin angmax
mpc.branch = [
1 2 0.01 0.06 0.03 0 0 0 0 0 1 -360 360;
1 3 0.02 0.08 0.04 0 0 0 0 0 1 -360 360;
2 3 0.015 0.07 0.02 0 0 0 0 0 1 -360 360;
3 4 0 0.1 0 0 0 0 0.975 0 1 -360 360;
];
% 2 startup shutdown n c2 c1 c0
mpc.gencost = [
2 0 0 3 0.01 20 0;
2 0 0 3 0.02 25 0;
];
""", encoding="utf-8")
net = read_matpower("four_bus.m")
print(net.name, len(net.buses), len(net.branches), net.notes["ignored_fields"])
print([(s.bus, s.g_mw, s.b_mvar) for s in net.shunts], net.branches[3].ratio)
model, state, drivers = build_power_flow(net)
solved = solve_power_flow(model, state, drivers)
print(model.power_index.bus_ids)
What is read, ignored and refused¶
| Fields and statements | |
|---|---|
| Read | mpc.version (must be exactly '2'; any other version is refused), mpc.baseMVA, mpc.bus (at least 13 columns), mpc.gen (at least 10 columns), mpc.branch (at least 11 columns). Extra columns (solved-case or optimal-power-flow results) are ignored. |
Ignored, because they do not change the power flow; recorded in file order in notes["ignored_fields"] |
gencost, bus_name, gen_name, branch_name, areas, gentype, genfuel |
| Refused by name, because skipping them would silently change the network | mpc.dcline (DC lines); isolated buses (type 4, every one named); any other bus type; any other mpc.<field>; any statement that is neither the function mpc = <name> header nor a whole-field assignment mpc.<field> = ... (an indexed assignment such as mpc.bus(:, 8) = 1;, define_constants, return, ...); a field assigned twice |
Syntax. The file is read as UTF-8 (a byte-order mark is skipped; a file that is not UTF-8
is refused, naming it). % starts a comment except inside a single-quoted string; block
comments %{ ... %} are not supported (a line holding only %{ or %} is refused with its
number). ... continues a line (the rest of the physical line is dropped). Matrices [ ... ] may span lines; a row ends at
; or at the end of a line; values are separated by whitespace or commas; Inf and -Inf
are numbers. Cell arrays { ... } are accepted only for ignored fields. A row shorter than its
table needs, a row of a different length from the table's first row, or a token that is not a
number, is refused with the line number. Bus ids must be
integers, and baseMVA must be positive (PowerNetwork refuses base_mva <= 0). The
network's name is the function's name (case9), or the file's stem if the file has no
function header.
Columns¶
Columns are named as in MATPOWER's idx_bus, idx_gen and idx_brch; a column not listed is
read past.
mpc.bus, one Bus per row, plus a Load and a FixedShunt where nonzero:
| Column | MATPOWER name | Unit | Becomes |
|---|---|---|---|
| 1 | BUS_I |
integer | Bus.id |
| 2 | TYPE |
1, 2, 3 | Bus.kind: 1 "pq" (load bus), 2 "pv" (voltage-controlled), 3 "slack" (reference); 4 (isolated) is refused |
| 3, 4 | PD, QD |
MW, MVAr | Load(bus, p_mw, q_mvar), absorbed, when either is nonzero |
| 5, 6 | GS, BS |
MW, MVAr at 1 p.u. | FixedShunt(bus, g_mw, b_mvar), when either is nonzero: GS consumed, BS injected |
| 8 | VM |
p.u. | Bus.vm (as stated; not used by the solve) |
| 9 | VA |
degrees | Bus.va_deg; the slack bus's angle setpoint |
| 10 | BASE_KV |
kV | Bus.base_kv (informative) |
mpc.gen, one Generator per row:
| Column | MATPOWER name | Unit | Becomes |
|---|---|---|---|
| 1 | GEN_BUS |
integer | Generator.bus |
| 2 | PG |
MW | Generator.p_mw |
| 3 | QG |
MVAr | Generator.q_mvar (used only on a bus solved as PQ) |
| 4, 5 | QMAX, QMIN |
MVAr | Generator.q_max_mvar, q_min_mvar (kept; limits are not enforced) |
| 6 | VG |
p.u. | Generator.vm_set, the voltage setpoint of its bus |
| 8 | GEN_STATUS |
Generator.in_service is GEN_STATUS > 0 |
mpc.branch, one Branch per row:
| Column | MATPOWER name | Unit | Becomes |
|---|---|---|---|
| 1, 2 | F_BUS, T_BUS |
integer | Branch.from_bus, to_bus |
| 3, 4, 5 | BR_R, BR_X, BR_B |
p.u. on baseMVA |
Branch.r, x, b (series resistance and reactance, total line charging) |
| 9 | TAP |
ratio | Branch.ratio, off-nominal turns ratio at the from end; 0 means 1 (a line) |
| 10 | SHIFT |
degrees | Branch.shift_deg, phase shift at the from end |
| 11 | BR_STATUS |
Branch.in_service is BR_STATUS > 0 |
How the network is solved¶
The reader keeps every element, out-of-service ones flagged; the power-flow builders apply
MATPOWER's rules (makeYbus, bustypes, makeSbus, makeBdc, dcpf):
- Status. Generators and branches with status
<= 0are dropped. - Generators per bus. Generators on one bus are summed. A voltage-controlled bus whose
in-service generators state different
VGvalues is refused (MATPOWER silently takes the last). - Bus types. A PV bus with no in-service generator is solved as PQ; the
PG,QGof generators on a PQ bus are a fixed injection, netted against the bus's load. - Slack. Exactly one slack bus, with at least one in-service generator. Its voltage
magnitude is its generators'
VG, its angle the busVAcolumn. A PV bus's magnitude is its generators'VG. - Per unit. Powers are divided by
baseMVA, angles converted to radians;BR_R,BR_X,BR_Bare already p.u. onbaseMVA, andVM,VGin p.u. - Start. A flat start: regulated buses at their setpoint, every other bus at 1 p.u., all at the slack angle.
Buses not connected to the slack through in-service branches, and branches of zero impedance, are refused at build time (see Refusals and limitations).
MATPOWER's standard cases¶
MATPOWER's code is BSD-licensed, but its licence states that the case files distributed with
it are not covered by that licence, so noodl physics does not redistribute them.
matpower_case(name, *, cache_dir=None) returns the path of one of MATPOWER 8.1's case9,
case14, case30 and case118, downloading it on first use from MATPOWER's GitHub
repository at the commit of the 8.1 release (MATPOWER_COMMIT), and checks its SHA-256
(MATPOWER_CASES, of the bytes with LF line endings) on every call. The file is only ever
parsed as text by read_matpower. It is cached in $NOODL_CACHE_DIR/matpower/8.1/, by
default ~/.cache/noodl/matpower/8.1/, or under cache_dir when given:
from noodl.apps.electrical.power import matpower_case, read_matpower
net = read_matpower(matpower_case("case118"))
Without a network connection matpower_case raises MatpowerCaseUnavailableError (an
OSError) naming the URL, the path to save the file at by hand and its expected SHA-256; a
downloaded or cached file with another digest raises MatpowerCaseDigestError (a
ValueError) and a download that fails the check is not cached. Any other name is refused.
matpower_case("case9_features") is the variant of case9 the
verification uses, derived from the
fetched case9 and checked the same way. tests/data/power_flow/ holds only the stored
reference results (*.json, part of noodl physics), each recording the SHA-256 of its case,
and tests/data/power_flow/NOTICE.md the cases' sources.