Test-case libraries and the MATPOWER parser¶
How GridSim gets its networks. Every standard test system - from the WSCC 9-bus toy up to
~78k-bus continental models - enters the solver through one of two doors: a small set of
built-in factories (IeeeCases) or the MATPOWER .m parser reading files committed
under data/matpower/. This document describes both, the committed archive, how cases are
validated, how to add one, and the known non-convergence / parse-gap classes.
- Parser:
src/GridSim.Core/IO/Matpower/MatpowerParser.cs - Parse result record:
src/GridSim.Core/IO/Matpower/MatpowerCase.cs - Built-in factories:
src/GridSim.Core/IO/IeeeCases.cs - Embedding config:
src/GridSim.Core/GridSim.Core.csproj - Committed archive:
data/matpower/(provenance indata/matpower/README.md) - Fetch script:
tools/fetch-cases.sh - Tests:
tests/GridSim.Tests/IeeeCasesTests.cs,tests/GridSim.Tests/MatpowerParserTests.cs - Server run findings:
docs/developer/benchmarks/server-results/README-section.md
1. The MATPOWER .m parser¶
MatpowerParser (src/GridSim.Core/IO/Matpower/MatpowerParser.cs:24) is a static class and the
single import path for the PEGASE, RTS, NESTA/PGLib and wider MATPOWER libraries, all of
which ship natively in MATPOWER .m format
(src/GridSim.Core/IO/Matpower/MatpowerParser.cs:8-13). It preserves the source operating point
verbatim - mpc.gen Pg/Qg/Vg and the stored bus voltages are kept as-is, no dispatch
reinterpretation - so the parsed model reproduces the file's snapshot.
Public API¶
| Member | Signature | Behaviour |
|---|---|---|
Parse |
Parse(string text, string? name = null, double frequencyHz = 50.0) : GridModel |
Parse case text; thin wrapper over ParseWithDiagnostics(...).Model. MatpowerParser.cs:28 |
ParseFile |
ParseFile(string path) : GridModel |
File.ReadAllText then Parse; case name defaults to the file stem (Path.GetFileNameWithoutExtension). MatpowerParser.cs:32 |
ParseWithDiagnostics |
ParseWithDiagnostics(string text, string? name = null, double frequencyHz = 50.0) : MatpowerParseResult |
The real workhorse; returns the model plus a list of informational notes. MatpowerParser.cs:36 |
MatpowerParseResult |
record (GridModel Model, IReadOnlyList<string> Notes) |
Model + diagnostics. Notes are informational only - a case that produced notes is still a valid, solvable model. MatpowerCase.cs:9 |
MatpowerUnitHandling |
optional last parameter of Parse, ParseFile and ParseWithDiagnostics (N165, October 2026) |
Default NormaliseRawFeeders: a raw ohm / kW distribution feeder (case22, case33bw, case69, case85) is converted to per-unit / MW by MatpowerUnitNormalizer.Normalize, with a parse note and MatpowerParseResult.RawUnitsConverted = true; every other file is unaffected. AsWritten keeps the numbers exactly as the file writes them (the behaviour before N165). |
The case name resolves in priority order: the name argument, else the function mpc = <name>
declaration, else the literal "matpower" (MatpowerParser.cs:26-27, :45,
ExtractFunctionName at :209-213). baseMVA defaults to 100.0 when absent
(MatpowerParser.cs:44). Default frequency is 50 Hz (GB). Empty/whitespace text throws
InvalidDataException (MatpowerParser.cs:38-39).
Tokenizing¶
Parsing happens in two stages: Clean normalises the text, then regex-driven extractors pull out
the scalar and each matrix.
- Comment strip + continuation join (
Clean,MatpowerParser.cs:176-194). The text is split on\n; each line has its trailing%comment removed byStripComment(MatpowerParser.cs:197-207), which respects single-quoted strings so a%inside a bus name is not treated as a comment. A line whose trimmed content ends with...is a MATLAB line continuation: its content (minus the...) is buffered and joined onto the next line before emitting (MatpowerParser.cs:184-192). - Scalar extraction (
ExtractScalar,MatpowerParser.cs:215-219).mpc.baseMVAis matched with a regex accepting an optional sign, digits/decimal, and scientifice/Eexponent. - Matrix extraction (
ExtractMatrix,MatpowerParser.cs:222-246). Finds the firstmpc.<field> = [(whole-word\bmatch sompc.busdoes not also matchmpc.bus_name,MatpowerParser.cs:224-225), takes everything up to the next], splits rows on;, and splits each row on spaces/tabs/CR/LF/commas. Each token is parsed byParseNum. - Number parsing (
ParseNum,MatpowerParser.cs:164-173) reads withInvariantCultureand maps MATLAB infinity/NaN spellings:Inf/inf/+Inf/Infinity->+inf,-Inf/-inf/-Infinity->-inf,NaN/nan-> NaN. These infinities are how MATPOWER encodes open generator limits.
At(row, i, dflt) (MatpowerParser.cs:159-160) reads column i safely, returning dflt when
the row is shorter than expected - so cases that omit trailing optional columns still parse.
Column mappings¶
MATPOWER columns are 1-based in the file spec; the parser uses 0-based indices. The mappings below are exactly what the code reads.
mpc.bus (MatpowerParser.cs:58-84)
| Col (1-based) | Index | Field | Maps to |
|---|---|---|---|
| 1 | 0 | bus_i |
Bus.Id (id preserved verbatim; not required to be contiguous/sorted) |
| 2 | 1 | type |
Bus.Type (see rules below) |
| 3 | 2 | Pd |
Bus.Pd |
| 4 | 3 | Qd |
Bus.Qd |
| 5 | 4 | Gs |
Bus.Gs |
| 6 | 5 | Bs |
Bus.Bs |
| 8 | 7 | Vm |
Bus.Vm (default 1.0) |
| 9 | 8 | Va |
Bus.VaDeg |
| 10 | 9 | baseKV |
Bus.BaseKv |
mpc.gen (MatpowerParser.cs:96-115)
| Col (1-based) | Index | Field | Maps to |
|---|---|---|---|
| 1 | 0 | bus |
Generator.Bus |
| 2 | 1 | Pg |
Generator.Pg |
| 3 | 2 | Qg |
Generator.Qg |
| 4 | 3 | Qmax |
Generator.Qmax (default +inf) |
| 5 | 4 | Qmin |
Generator.Qmin (default -inf) |
| 6 | 5 | Vg |
Generator.Vg (default 1.0) |
| 7 | 6 | mBase |
Generator.MBase if > 0, else the system baseMVA (MatpowerParser.cs:112) |
| 8 | 7 | status |
dropped if <= 0 (MatpowerParser.cs:99) |
| 9 | 8 | Pmax |
Generator.Pmax (default +inf) |
| 10 | 9 | Pmin |
Generator.Pmin |
Inertia H is not present in MATPOWER data and is set to 0 (MatpowerParser.cs:113).
mpc.branch (MatpowerParser.cs:121-141)
| Col (1-based) | Index | Field | Maps to |
|---|---|---|---|
| 1 | 0 | fbus |
Branch.From |
| 2 | 1 | tbus |
Branch.To |
| 3 | 2 | r |
Branch.R |
| 4 | 3 | x |
Branch.X |
| 5 | 4 | b |
Branch.B |
| 6 | 5 | rateA |
Branch.RateAMva |
| 9 | 8 | ratio |
tap ratio - drives transformer detection (below) |
| 10 | 9 | angle |
Branch.PhaseShiftDeg |
| 11 | 10 | status |
dropped if <= 0 (MatpowerParser.cs:123) |
Mapping rules¶
- Bus type (
MatpowerParser.cs:62-69):3 -> Slack,2 -> Pv,1 -> Pq; anything else falls through the_arm toPq. Type 4 (isolated) buses are detected first and dropped (added to anisolatedset,continued;MatpowerParser.cs:62). Infhandling: open Q/P limits written asInf/-Infbecomedouble.PositiveInfinity/double.NegativeInfinity(ParseNum,MatpowerParser.cs:164-173); this is asserted byMaps_Inf_generator_limits_to_infinity(MatpowerParserTests.cs:48-55).- Transformer detection by tap ratio (
MatpowerParser.cs:128-140): a branch is a transformer iff its ratio column is non-zero (ratio != 0.0). If so,IsTransformer = trueandTapRatio = ratio; otherwise it is a line withTapRatio = 1.0. (MATPOWER's convention:0means "not a transformer / nominal ratio".) Verified byLine_vs_transformer_by_ratio(MatpowerParserTests.cs:57-75). - Dropping isolated / out-of-service / dangling elements:
- Type-4 buses are removed and their ids never enter the
liveIdsset (MatpowerParser.cs:62,:83). - Generators with
status <= 0are skipped (genOffcounter,MatpowerParser.cs:99); so are generators on a bus that isn't inliveIds, i.e. on a dropped/isolated bus (MatpowerParser.cs:100). - Branches with
status <= 0are skipped (brOff,MatpowerParser.cs:123); branches touching a dropped bus on either end are skipped (brDangling,MatpowerParser.cs:126). This guarantees no dangling reference can reach the solver (MatpowerParser.cs:20-22). Covered byDrops_isolated_buses_and_incident_elements(MatpowerParserTests.cs:77-90) andSkips_out_of_service_elements(MatpowerParserTests.cs:92-103). - Non-contiguous bus ids preserved (
MatpowerParser.cs:18-22): buses need not be contiguous or sorted. The solvers key on the bus id, and result rowimaps tomodel.Buses[i], so file order is preserved.Handles_noncontiguous_bus_idsparses ids{100, 205, 9999}, solves, and checks bothm.Busesandr.BusIdscome back in that exact order (MatpowerParserTests.cs:105-118).
Reference-bus check¶
After building the bus list the parser counts slack (type-3) buses (slackCount,
MatpowerParser.cs:70) and enforces:
- Zero slack -> throw.
InvalidDataException("... has no reference (type 3) bus - the power flow would be singular.")(MatpowerParser.cs:86-87). Tested byThrows_when_no_reference_bus(MatpowerParserTests.cs:120-129). - Multiple slack -> note, not error. A diagnostic "N reference (slack) buses - treating as a
multi-island system." is added (
MatpowerParser.cs:88-89).
Other diagnostics appended to Notes: dropped isolated buses (:90-91), skipped out-of-service
generators (:116), skipped out-of-service branches (:142), and skipped dangling branches
(:143).
Known limitation: no arithmetic-expression evaluation¶
The parser reads each token as a single number via double.Parse (ParseNum,
MatpowerParser.cs:171). It does not evaluate arithmetic expressions. Some ACTIVSg cases embed
literals like 135/sqrt(3) in the data matrices; these fail to load. This is a current gap,
not a corruption - on the server run the two case533mt files were the only load failures:
docs/developer/benchmarks/server-results/README-section.md:93-94, docs/developer/benchmarks/server-results/20260701-101835/regime.log:110-111).
Closing it would require a tiny expression evaluator in ParseNum.
2. Built-in cases (IeeeCases)¶
IeeeCases (src/GridSim.Core/IO/IeeeCases.cs:13) exposes standard systems as plain factory
methods so callers use them identically regardless of origin - the solver neither knows nor
cares where a GridModel came from (IeeeCases.cs:6-12).
Case9 - the MATPOWER file plus the WSCC machine data¶
Case9() is the WSCC 9-bus system (MATPOWER case9), 3 generators, 3 loads, 100 MVA base, 345 kV. Since N199
(October 2026) it reads the embedded case9.m itself, so its buses (with the file's 0.9-1.1 pu limits), branches
(RATE_A, RATE_B, RATE_C), generators (Pmin 10 MW each) and cost curves are the file's, and lays over each unit only
the published WSCC machine data from Anderson and Fouad: the machine rating as MBase (247.5 / 192 / 128 MVA) and the
inertia constant H on that base (23.64 / 6.40 / 3.01 s), which no .m file carries. Before N199 it was typed out by
hand without the limits, Pmin, RATE_B / RATE_C and costs. By project convention it is the physics ground truth.
Embedded .m factories¶
The remaining factories parse a .m file embedded in the assembly via the private helper Mpc
(IeeeCases.cs:90-97), which loads the manifest resource GridSim.Core.cases.<res>.m and calls
MatpowerParser.Parse.
| Factory | .m resource |
Display name | Source |
|---|---|---|---|
Case14() :60 |
case14 |
IEEE 14-bus | MATPOWER 8.0 |
Case30() :63 |
case30 |
IEEE 30-bus | MATPOWER 8.0 |
Case57() :66 |
case57 |
IEEE 57-bus | MATPOWER 8.0 |
Case118() :69 |
case118 |
IEEE 118-bus | MATPOWER 8.0 |
Case300() :72 |
case300 |
IEEE 300-bus | MATPOWER 8.0 |
Case145() :75 |
case145 |
IEEE 145-bus (dynamic) | MATPOWER 8.0 |
Case24Rts() :81 |
case24_ieee_rts |
IEEE RTS-79 24-bus | MATPOWER 8.0 |
CigreMv() :87 |
cigre_mv |
CIGRE MV benchmark (TF C6.04.02) | authored |
How embedding works¶
GridSim.Core.csproj marks the nine .m files as <EmbeddedResource> with an explicit
LogicalName, e.g.:
<EmbeddedResource Include="..\..\data\matpower\lib\case14.m"
LogicalName="GridSim.Core.cases.case14.m" />
src/GridSim.Core/GridSim.Core.csproj, the <ItemGroup> of embedded cases). The LogicalName
gives a stable resource id GridSim.Core.cases.<name>.m independent of the on-disk folder, which is
exactly the string Mpc reconstructs (IeeeCases.cs:92). The single source of truth is the
committed .m files under data/matpower; the classic IEEE cases + CIGRE MV are pulled from
lib/ and benchmarks/ at build time. case9.m is embedded too: Case9() reads it (N199).
RTS-79 (24-bus) vs RTS-96 (73-bus)¶
These are distinct systems, easily confused:
- RTS-79 is the single-area 24-bus IEEE Reliability Test System, exposed as
Case24Rts()/case24_ieee_rtsand embedded (IeeeCases.cs:77-81). - RTS-96 is the three-area 73-bus system, a separate and larger case shipped as
data/matpower/rts/pglib_opf_case73_ieee_rts.m(RTS-96, 73-bus 3-area) and not embedded - it is loaded from disk (IeeeCases.cs:78-80,data/matpower/README.md:15).
CIGRE MV¶
CigreMv() / cigre_mv (IeeeCases.cs:83-87) is a ~15-node, 20 kV radial distribution feeder
(not a transmission system), authored to the CIGRE Task Force C6.04.02 European MV distribution
benchmark spec ("Benchmark Systems for Network Integration of Renewable and Distributed Energy
Resources", 2014). The .m header documents the derivation: one 110 kV source (Bus 0) feeding two
20 kV feeders through two 25 MVA 110/20 kV transformers, base (no-DER) configuration with the three
normally-open tie switches S1/S2/S3 open (so the network is a tree), line/transformer/load data
reproducing pandapower's create_cigre_network_mv, converted to per-unit on a 100 MVA system base
(data/matpower/benchmarks/cigre_mv.m:1-20). The test expects 15 buses / 14 branches and
convergence (MatpowerParserTests.cs:181-189).
3. The committed archive (data/matpower/)¶
The repo commits a curated subset so a lean checkout can build, embed, and test without any network
fetch. Layout (provenance table in data/matpower/README.md:11-17):
| Folder | Committed contents | Source | Version | Licence |
|---|---|---|---|---|
lib/ |
case9/14/30/57/118/145/300, case24_ieee_rts |
MATPOWER data/ |
8.0 | BSD-3-Clause (LICENSE-MATPOWER.txt) |
pegase/ |
case1354pegase, case2869pegase, case9241pegase |
MATPOWER data/ |
8.0 | BSD-3-Clause + citation |
rts/ |
pglib_opf_case73_ieee_rts (RTS-96, 73-bus 3-area) |
PGLib-OPF | master | CC BY 4.0 |
nesta/ |
pglib_opf_case{3_lmbd,5_pjm,30_as,162_ieee_dtc,240_pserc,588_sdet} |
PGLib-OPF | master | CC BY 4.0 |
benchmarks/ |
cigre_mv (authored, not upstream) |
CIGRE TF C6.04.02 / pandapower ref | - | see README |
The lib/ files double as the embedded-resource source for IeeeCases. Provenance, required
academic citations (PEGASE, PGLib-OPF/RTS-96/NESTA-lineage, CIGRE MV), the NESTA-retirement note,
and the CIGRE MV derivation are all maintained in data/matpower/README.md - see there
rather than duplicating. The BSD-3-Clause text for the MATPOWER case data is in
data/matpower/LICENSE-MATPOWER.txt.
After the server fetch (tools/fetch-cases.sh)¶
tools/fetch-cases.sh (tools/fetch-cases.sh:1-35) expands the committed archive into the full
standard-case universe on a build/benchmark host. It is idempotent (skips files already present,
fetch-cases.sh:17, :28) and drops files into two new folders under data/matpower:
pglib/(fetch-cases.sh:11-20): the entire PGLib-OPF archive. It hits the GitHub trees API forpower-grid-lib/pglib-opf(master), greps out everypglib_opf_case*.mpath, and downloads each fromraw.githubusercontent.com/.../master/.matpower-all/(fetch-cases.sh:22-31): everydata/case*.mfrom the MATPOWER repo, pinned to tag8.0, via the same trees-API-then-raw pattern.
Nothing is embedded from these; they are disk-discovered only (see below). After the fetch the
tree holds roughly ~161 .m cases spanning up to ~78k buses (e.g. the ACTIVSg25k / 70k
networks), the union of PGLib-OPF and MATPOWER's data/. The server run confirmed clean scaling and
reached 10,000 buses (case_ACTIVSg10k) in its partial snapshot
(docs/developer/benchmarks/server-results/README-section.md:57-63).
Auto-discovery¶
Both the CLI and the WPF picker enumerate data/matpower recursively for *.m, so whatever
fetch-cases.sh drops in is picked up automatically:
- CLI registry
BuildCases(src/GridSim.Cli/Program.cs:793-826): starts with the nine embedded classics, thenDirectory.EnumerateFiles(mp, "*.m", SearchOption.AllDirectories)(Program.cs:814), skipping any file whose stem duplicates an embedded classic so disk copies ofcase9/case14/... don't double-register (Program.cs:798-799,:817-818). A rough bus-count hint is parsed from digits in the filename for ordering (Program.cs:819-821). - WPF
ModelCatalog.Discover(src/GridSim.Wpf/ModelCatalog.cs:38-44): adds oneModelSourceper discovered.mlabelled"MATPOWER <stem>", alongside the GB reduced/synthetic/GDA sources.
4. Validation¶
IeeeCasesTests (tests/GridSim.Tests/IeeeCasesTests.cs)¶
Validates the built-in systems on three axes:
- Dimensions (
Has_expected_dimensions,IeeeCasesTests.cs:48-64): a[Theory]asserting the exact(buses, gens, branches)for each case (14/30/57/118/300/24/145),BaseMva == 100, and exactly one slack bus. The counts are post-parse (i.e. after dropped isolated/out-of-service elements): e.g.("118", 118, 54, 186),("300", 300, 69, 411),("24", 24, 33, 38)(IeeeCasesTests.cs:49-55). - Convergence + balance (
Converges_and_balances,IeeeCasesTests.cs:66-86): solves with the defaultNewtonRaphsonPowerFlow(generator Q-limits on, like the case9 tests), assertsConverged,MaxMismatch < 1e-6, that net injections sum to total losses (r.TotalLossMw == r.PInjMw.Sum()), and that losses are non-negative. - Published Vm/Va with slack re-anchoring (
AssertPublished,IeeeCasesTests.cs:97-126). Each case has MATPOWER's stored solvedVm/Vaarrays inlined (IeeeCasesTests.cs:16-33). The test solves, maps results by bus id, then compares. Because the source's angle reference need not be the slack bus, both sides are re-referenced to the slack before comparing angles:dva = (va[id] - solvedSlack) - (expVa[i] - offset)(IeeeCasesTests.cs:113-124). Per-case tolerances are ~2x the observed deviation:
| Case | Vm tol | Va tol (deg) | Ref |
|---|---|---|---|
| 14 | 0.005 | 0.2 | :91 |
| 57 | 0.02 | 1.5 | :92 |
| 118 | 0.03 | 1.0 | :93 |
| 300 | 0.02 | 12.0 | :94 |
| 145 | 0.01 | 0.1 | :95 |
The case300 angle bound is deliberately loose because its stored classic angles are only roughly
converged; its voltages still match tightly (IeeeCasesTests.cs:88-90).
MatpowerParserTests (tests/GridSim.Tests/MatpowerParserTests.cs)¶
Two layers:
- Tokenizer / mapping units over inline
.mfragments (MatpowerParserTests.cs:17-129): scalar - matrix parse,
Inf-> infinity, line-vs-transformer by ratio, isolated-bus dropping, out-of-service skipping, non-contiguous ids, and the no-reference-bus throw (all cited in section 1 above). - Round-trip + library integration.
Case9_from_file_matches_handcoded_structure_and_solution(MatpowerParserTests.cs:133-158) parseslib/case9.mand asserts it matchesIeeeCases.Case9()in structure and, after solving both, in Vm (6 dp) / Va (4 dp) - proving the parser reproduces the ground truth (since N199N199AlignedCase9Testsproves the two bit-identical).Large_case_parses_and_converges(MatpowerParserTests.cs:162-179) is a[Theory]over the three PEGASE cases + RTS-96, guarded on file existence (if (!File.Exists(path)) return;,:170) so a lean checkout stays green; it solves with the sparse solver (Q-limits off, 100 iters) and requiresMaxMismatch < 1e-4.CigreMv_parses_and_converges(:181-189) checks the 15-bus/14-branch radial feeder solves.
Dense-vs-sparse agreement¶
The trust signal for the large cases is that the dense (NewtonRaphsonPowerFlow) and sparse
(SparseNewtonRaphsonPowerFlow, direct sparse LU with an ILU0/BiCGSTAB fallback) solvers agree case-for-case on
iteration count and final mismatch. Since 1.95.001 they are bit-identical on the CPU (same Newton step;
DenseSparseParityTests), so this cross-check now tests the two assemblies, not two numerical answers. The server regime runs both sweeps to cross-check. In the
partial snapshot the dense reference sweep had not yet started, so its column is marked
pending and the cross-check is explicitly not yet available
(docs/developer/benchmarks/server-results/README-section.md:6-11, :32-35, :123-126). The sparse half alone landed
every converged case at mismatch <= ~1e-9 (mostly 1e-11/1e-12).
5. How to add a case¶
Two paths, depending on whether the case should be built into GridSim.Core or just available to
the tools.
A. Disk case (usual - no rebuild of Core needed)¶
- Drop a valid MATPOWER
.mfile anywhere underdata/matpower/(any subfolder; discovery is recursive). - It is auto-discovered: the CLI
bench/sweep/N-1/stress verbs and the WPF source picker enumeratedata/matpower/**/*.m(src/GridSim.Cli/Program.cs:814,src/GridSim.Wpf/ModelCatalog.cs:41). No code change. - Constraints: the file must have an
mpc.busmatrix (MatpowerParser.cs:47-48) and at least one type-3 slack bus (MatpowerParser.cs:86-87). If the filename stem duplicates an embedded classic (case9,case14, ...,cigre_mv), the CLI skips the disk copy in favour of the embedded one (Program.cs:798-799,:817-818). - If the case has real provenance/licence terms, add a row to
data/matpower/README.md.
B. Embedded factory (built into GridSim.Core)¶
Use this only when the case must ship inside the Core assembly (as the classics do).
- Place the
.mfile underdata/matpower/(e.g.lib/orbenchmarks/). - Add an
<EmbeddedResource>line tosrc/GridSim.Core/GridSim.Core.csprojwith aLogicalName="GridSim.Core.cases.<stem>.m"(match the existing pattern). - Add a factory to
IeeeCasesreturningMpc("<stem>", "<display name>")(IeeeCases.cs:90-97). - Add it to the embedded-classic skip set in the CLI (
Program.cs:798-799) so the disk copy isn't also registered. - Add a test: dimensions + convergence (and published Vm/Va if a solved snapshot exists), following
IeeeCasesTests.
6. Known non-convergence classes (from the server run)¶
Findings below are from the partial server sweep, docs/developer/benchmarks/server-results/README-section.md:65-94
(mirrored in README.md:240-245). These are expected outcomes, not parser bugs or solver
failures.
PGLib-OPF operating points (flat-start power flow)¶
PGLib-OPF cases ship an optimisation (OPF) benchmark operating point, not a solved
power-flow snapshot. Run as a plain flat-start Newton-Raphson power flow they hit the 100-iteration
cap with large residuals, while their non-OPF twins converge trivially
(README-section.md:66-84):
| PGLib OPF case | Buses | OPF result (conv / iters / mismatch) | Non-OPF twin |
|---|---|---|---|
..._case1888_rte |
1888 | NO / 100 / 2.35E+02 | case1888rte yes / 2 / 118 ms |
..._case1951_rte |
1951 | NO / 100 / 9.97E+01 | case1951rte yes / 3 / 132 ms |
..._case2848_rte |
2848 | NO / 100 / 2.12E+02 | case2848rte yes / 2 / 440 ms |
..._case2868_rte |
2868 | NO / 100 / 2.98E+01 | case2868rte yes / 5 / 448 ms |
..._case6470_rte |
6470 | NO / 100 / 1.15E+02 | case6470rte yes / 3 / 2687 ms |
Plus ..._case2000_goc, ..._case3022_goc, ..._case4917_goc, ..._case6495_rte,
..._case6515_rte, ..._case2853_sdet, ..._case240_pserc, ..._case300_ieee,
..._case179_goc, ..._case162_ieee_dtc, ..._case1803_snem, ..._case39_epri,
..._case3_lmbd (README-section.md:82-84). The NESTA note in data/matpower/README.md:19-25
flags the same: heavily-loaded OPF benchmarks may not converge under a plain NR power flow.
Radial distribution feeders¶
Small single-generator distribution feeders don't converge under the transmission-oriented polar
Newton-Raphson solver: case12da, case15da, case16am, case18nbr, case22, case28da,
case33bw, case33mg, case34sa, case38si, case51he/51ga, case69, case74ds, case85,
case94pi, case118zh, case136ma, case141, case10ba
(README-section.md:86-89). These motivate a dedicated backward/forward-sweep distribution
solver (tracked). Note CIGRE MV is a radial feeder too, but small enough that the transmission NR
still converges on it (data/matpower/benchmarks/cigre_mv.m:19-20).
Parse-gap load failures (not a convergence class)¶
case533mt_hi / case533mt_lo fail to load (not to converge) because of the unevaluated
135/sqrt(3) arithmetic literal - the section-1 parser gap (README-section.md:93-94).
Bottom line: on the 28-core EPYC 7763 the sparse solver converged the full IEEE/PEGASE line-up
to machine-tight mismatches (<= ~1e-9) from 9 to 10,000 buses; the only non-convergences are the two
expected classes above, and the only load failures are the two sqrt(3) cases
(README-section.md:114-126).