User manual — chapter list

Loading networks

GridSim loads a network through one of four doors, and the solver neither knows nor cares which: built-in named cases (embedded in GridSim.Core), MATPOWER .m files (data/matpower/ or any path), JSON case directories (data/uk, data/gb-synthetic, and anything you write), and GDA-materialised cases (the real GB network, assembled outside the repo). This chapter shows how to select each on the CLI and in the desktop app, and ends with the case-library reference table.

Built-in cases

Nine standard systems are compiled into GridSim.Core as IeeeCases factories, so they load with no files on disk. Select them by name:

dotnet run --project src/GridSim.Cli case9        # single solve, prints the report
dotnet run --project src/GridSim.Cli case118      # runs live (add --once for one report)
CLI name Case Buses Gens Branches Notes
case9 (or ieee9) WSCC 9-bus 9 3 9 hand-coded, carries the published inertia constants; the physics ground truth
case14 IEEE 14-bus 14 5 20
case30 IEEE 30-bus 30 6 41
case57 IEEE 57-bus 57 7 80
case118 IEEE 118-bus 118 54 186
case300 IEEE 300-bus 300 69 411
case145 IEEE 145-bus (dynamic) 145 50 453
case24 (or rts) IEEE RTS-79 24-bus (case24_ieee_rts) 24 33 38
cigre CIGRE MV benchmark (cigre_mv) 15 1 14 radial MV feeder; small enough that Newton still converges

An unrecognised name fails loud with usage and exit code 2 - it is never silently treated as the default model. With no case argument at all, the CLI loads the UK reduced 10-zone model from data/uk.

Only the hand-coded case9 carries generator inertia (H); MATPOWER files have no H column, so every parsed case has H = 0 and the frequency verbs have nothing to work with there.

MATPOWER .m files

Pass any .m file path (or use --mpc <file>.m):

dotnet run --project src/GridSim.Cli data/matpower/pegase/case9241pegase.m
dotnet run --project src/GridSim.Cli --mpc path/to/mycase.m --once

The parser handles Inf limits, transformer-by-tap-ratio, non-contiguous bus ids, and drops isolated (type-4) and out-of-service elements. Files loaded by path are additionally run through a unit normaliser that auto-fixes raw ohm/kW distribution files into per-unit-on-base form.

The committed archive lives under data/matpower/; tools/fetch-cases.sh expands it into the full PGLib-OPF + MATPOWER universe (~161 cases, up to ~78,000 buses) into data/matpower/pglib/ and data/matpower/matpower-all/. Anything under data/matpower/ is auto-discovered recursively by the desktop app's source picker, and analysis verbs (transient, bench, ...) accept a path relative to data/matpower/ (for example pegase/case1354pegase.m).

JSON case directories

A JSON case is a folder of human-editable files - the format both GB models ship in and the format GDA materialises to. Load one with --dir:

dotnet run --project src/GridSim.Cli --dir data/gb-synthetic --once
dotnet run --project src/GridSim.Cli --dir ./gda-out/zone-nged

A case directory contains:

File Required Contents
system.json recommended (identifies the folder as a case) name, base MVA, nominal frequency
buses.json yes id, name, nominal kV, type, shunts, 0-1 map coordinates x/y
generation.json yes (at least one "slack" role, unless a bus is typed slack) per-generator capacity, output, voltage setpoint, Q limits, inertia H, type, role (slack/pv/pq)
demand.json optional per-point MW (+ MVAr or power factor), aggregated onto buses
transmission.json optional lines: per-unit R/X/B, rating, parallel-circuit count
transformer.json optional transformers: per-unit R/X, tap ratio, phase shift, rating
onloadtapchanger.json optional OLTCs: controlled bus, target p.u., tap range/step/deadband
boundaries.json optional named transmission-boundary cuts with MW limits (drives the boundary panels)

Generation is dispatched from capacities on load (merit order, marginal slack), so the model arrives balanced and ready to solve. Multiple slack generators are allowed - a multi-island model needs one reference per island.

Two JSON cases ship in the repo: data/uk (the reduced 10-zone GB model, the CLI and GUI default) and data/gb-synthetic (a procedurally generated, entirely synthetic GB-shaped network down the voltage ladder). Regenerate or rescale the synthetic case with gridsim synth --scale 1.5.

GDA-materialised cases: gb-full, gb-spine, zone-*

The real GB network is assembled from the GDA data lake outside the repository - the repo carries only the schema mapping, never the data. The materialised output is the same multi-file JSON case format, written to a git-ignored directory (conventionally gda-out/ at the repo root):

  • gb-full - the full GB transmission model (3,539 buses).
  • gb-spine - the GSP-level spine (316 buses, real ETYS Appendix B circuit impedances and ratings where the register resolves). Adds fold_map.json, recording which full-model buses folded into each spine bus.
  • zone-nged, zone-ukpn, zone-ssen, zone-spen, zone-npg, zone-enwl - per-DNO zone cuts of the full model.

These carry the standard files above plus, where emitted, fold_map.json and diagnostics.json (assembly provenance - read them, do not edit them). DNO distribution footprints can also be materialised directly from the lake on an authorised machine:

gridsim gda --root /gda --out ./gda-out       # whole DNO footprint -> git-ignored JSON
gridsim --dir ./gda-out/<case>                # then run it like any other case

Once materialised, a GDA case is just a case directory: --dir on the CLI, and verbs that take a case argument (estimate, crossval, bench --estimate) accept the directory path directly - any folder containing system.json is recognised as a case.

Selecting a case in the desktop app

The NETWORK picker in the header lists everything discovered at startup:

Picker label Source Discovered from
GB reduced 10-zone data/uk bundled next to the app
GB synthetic distribution depth data/gb-synthetic bundled next to the app
GDA <case> each materialised case directory any gda-out/ folder found walking up from the app directory
MATPOWER <stem> each .m file data/matpower/** recursively
Replay <run> pre-solved run directories any nearby results/ folder (see 04-the-desktop-app.md)
Live bridge the GDA bridge stream shown when a bridge endpoint is configured

Imported MATPOWER cases carry no coordinates, so the app synthesises a layout (force-directed, with a graph-derived voltage hierarchy); the electrical model and the solve are untouched.

Case library at a glance

Condensed from the committed archive, verified against data/matpower/. Class: A converges from flat start; B OPF operating point - expected non-convergence as a plain power flow; C radial feeder - needs the backward/forward sweep; D fails to load (unevaluated arithmetic literal - a known parser limitation).

Folder Cases Buses Class
lib/ case9, case14, case30, case57, case118, case145, case300, case24_ieee_rts 9-300 A
pegase/ case1354pegase, case2869pegase, case9241pegase 1,354-9,241 A1
rts/ pglib_opf_case73_ieee_rts (RTS-96) 73 B
nesta/ pglib_opf_case{3_lmbd, 5_pjm, 30_as, 162_ieee_dtc, 240_pserc, 588_sdet} 3-588 B
benchmarks/ cigre_mv 15 A
dist/ case18, case22, case33bw, case69, case85 18-85 C
dist/ case533mt_hi 533 D

1 case9241pegase solves on the sparse path; the dense path runs out of memory at that scale - use the automatic engine selection and it never arises.

A class-B or class-C outcome is a property of the input, not a solver bug: a pglib_opf_ file ships an optimisation snapshot, not a solved power-flow point (its non-OPF twin solves in a handful of iterations), and a radial feeder is routed to the sweep automatically when it is strictly radial after parsing. See 17-troubleshooting.md for the canonical non-convergence list.

To add your own case: drop a valid MATPOWER .m anywhere under data/matpower/ (discovery is recursive; a stem that duplicates an embedded classic is ignored - the embedded bytes win), or write a JSON case directory and load it with --dir.

See also