The GDA bridge
The bridge is a small server in the GDA repo that streams reconstructed GB network state - and answers on-demand physics queries - over one WebSocket. GDA computes; GridSim renders. This chapter covers starting the server, connecting from the desktop and web apps, the stream controls, and the faults you will actually meet.
Running the server
cd D:\Work\GDA\v1
python Applications/GridSim-Bridge/bridge_server.py
It binds 127.0.0.1:8770 by default; override with the BRIDGE_HOST and
BRIDGE_PORT environment variables. It is a FastAPI/uvicorn app and needs the
GDA Python environment (pandas, numpy, scipy for the solver).
Sanity-check it with a browser or curl: GET http://127.0.0.1:8770/ returns
the app name, the WebSocket path (/ws/bridge), the available cases and the
current horizon cutoff; GET /meta adds the settled-scalar time bounds.
Cases are discovered at startup: gb-full, gb-spine, and every
NetworkModel/zones/zone-* directory (e.g. zone-nged, zone-ukpn).
Connecting
Desktop app - set BridgeEndpoint in
%LOCALAPPDATA%\GridSim\settings.json (default
ws://127.0.0.1:8770/ws/bridge) and pick the Live bridge model source.
The zone/start/SRC pickers then drive the stream - see
12-replaying-estimated-states.md.
Web console - set the GridSim:BridgeEndpoint configuration key to the
same ws://.../ws/bridge URI. The home screen then shows the bridge cards, and
/dashboard/bridge:<case>[@<source>] opens a bridge session.
Stream actions
The client sends JSON actions on /ws/bridge:
| Action | Fields | Effect |
|---|---|---|
start |
case, start (ISO timestamp), fps, source |
begin streaming the case from the historical start; fps clamped to 1-144 |
stop |
- | stop the active stream |
set_speed |
fps |
change playback speed without restarting |
seek |
start (and optionally case, source) |
restart the stream at a new instant |
query |
op, at, params, id |
on-demand physics; runs alongside the stream |
The server replies with a topology message once (the network model), then
one state message per settlement-period tick: the slim result block (buses
and line flows), the system scalars (frequency, RoCoF, generation, demand,
inertia, condition, RoCoF headroom), map overlays, operator/market series, and
- on estimated streams - a per-frame
estimatequality block. Stream-level failures arrive astype:"error"messages; the C# client surfaces them as a fault rather than dropping them silently.
Sources
source on start/seek selects the reconstruction. Each has a lake
prerequisite:
| Source | What each tick is | Requires |
|---|---|---|
live |
the case solved once; only the system scalars vary per tick | the case directory under NetworkModel/, plus the reconstructed scalar datasets (Derived/inertia_reconstructed, Derived/system_condition_index) |
presolved |
a stored demand-scaled re-solve of each period - per-bus state varies tick to tick | Derived/presolved_state/<case>/topology.json + the window's year=/week= partitions (built by NetworkModel/presolve_lake.py) |
estimated |
a stored WLS estimate fitted to each period's real telemetry, with its quality block | Derived/estimated_state/<case>/... (built by NetworkModel/build_estimated_state.py) |
If a stored source is requested but the case's lake (or the requested window)
does not exist, the server sends an info message naming the builder to run
and falls back to the live stream - the view is never left blank, it just
loses per-bus frame-by-frame replay until the window is built.
On-demand queries
{action:"query", op:..., at:"<iso>", params:{...}, id:"..."} runs physics at a
historical instant without stopping the stream. The ops are:
| Op | Returns |
|---|---|
inertia |
the reconstructed system-inertia scalars at at |
condition |
the system-condition index at at |
scenario |
a what-if solve (trip/scale) via the GDA sparse-NR + OLTC engine |
feasibility |
a feasibility/limit check of a proposed state |
post_event |
post-event replay around a disturbance |
asset |
per-asset drill-down (envelopes, BESS, tap changers, consumers) for a clicked bus |
propagation |
corridor propagation delays (tau map) from the lake product |
Every reply echoes the request's op and id, so out-of-order completion is
safe. An unknown op returns verdict:"ERROR" listing the available ops; a
query at past the horizon returns verdict:"INFEASIBLE_HORIZON". The C#
client (GridBridgeClient.Query) times a query out after 60 s by default.
The forward-inference horizon
The bridge is a replay tool and will not nowcast. Every emitted tick is masked
to the horizon cutoff - now minus 169 hours (7 days + 1 hour) - and a
start past the cutoff is rejected outright:
{"type":"error","message":"start 2026-07-13T00:00:00+00:00 is past the
no-forward-inference horizon (cutoff 2026-07-07T07:00:00+00:00, now - 7d 1h).
Rejected."}
Pick starts at least 8 days back. In the desktop app use the 2 weeks ago
or 30 days ago start presets (the shorter presets predate the horizon's
widening and are rejected); if you drive the socket yourself, compute the
cutoff from GET /meta. The GridSim client enforces the same 169-hour horizon
independently on every received tick (with a small clock-skew allowance) - a
too-recent frame is a protocol violation and faults the stream.
Common faults
| Symptom | Message | Cause and fix |
|---|---|---|
| Unknown case | Unknown case 'x'. Available: ['gb-full', 'gb-spine', 'zone-enwl', ...] |
typo, or the case directory is not materialised under NetworkModel/. Use a listed name |
| Horizon rejection | start ... is past the no-forward-inference horizon (cutoff ..., now - 7d 1h). Rejected. |
the requested start is too recent - pick one >= 8 days back |
| No settled data | No settled scalar data at/after ... |
the reconstructed scalar datasets do not cover the window; the settled data lags "now" - start further back, or refresh the GDA derived datasets |
| Missing stored lake | No estimated lake for 'zone-nged'; streaming the live on-demand solve instead (build with NetworkModel/build_estimated_state.py ...) |
the requested source lake does not exist for that case/window; the stream falls back to live. Build the window with the named script |
| Invalid start | Invalid start date |
the start string did not parse as a timestamp - send ISO-8601 |
In the desktop app these surface on the bridge status pill: a stream error
or socket fault shows BRIDGE FAULT * disconnected, a client-side horizon
violation shows HORIZON REJECTION * stream stopped, and a failed initial
connection shows CONNECT FAILED * ... - with the server's message logged in
the event log in each case.
See also
- 12-replaying-estimated-states.md - the SRC picker, chi-square pill and web bridge URLs
- 11-estimating-gb-history.md - building the presolved and estimated lakes
- 16-configuration.md - every endpoint and environment variable
- 17-troubleshooting.md - bridge faults in the wider fault catalogue