TopoStateGrid 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,315 @@
1
+ Metadata-Version: 2.2
2
+ Name: TopoStateGrid
3
+ Version: 1.1.0
4
+ Summary: TopoStateGrid is a physically informed graph construction method that converts power-grid topology, component attributes, and operating-state variables into machine-learning-ready graph datasets.
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: numpy
8
+ Requires-Dist: pandas
9
+ Requires-Dist: torch
10
+ Requires-Dist: torch-geometric
11
+ Provides-Extra: test
12
+ Requires-Dist: pytest; extra == "test"
13
+ Provides-Extra: pandapower
14
+ Requires-Dist: pandapower; extra == "pandapower"
15
+ Provides-Extra: visual
16
+ Requires-Dist: matplotlib; extra == "visual"
17
+ Requires-Dist: networkx; extra == "visual"
18
+ Requires-Dist: pillow; extra == "visual"
19
+
20
+ # TopoStateGrid
21
+
22
+ TopoStateGrid is a physically informed graph construction method that converts power-grid topology, component attributes, and operating-state variables into machine-learning-ready graph datasets.
23
+
24
+ The Python package import name is `topostategrid`.
25
+
26
+ ## Scope
27
+
28
+ TopoStateGrid focuses on physically grounded, state-dependent, and optionally time-indexed graph dataset construction for power-system machine learning. PowerGraph can be used as a reference dataset, and pandapower can be used as a parsing or simulation tool, but the main output is a reusable graph-construction pipeline.
29
+
30
+ This prototype does not build a GNN model, does not implement a cascading-failure simulator, and does not claim to be the first power-grid graph dataset tool.
31
+
32
+ ## Graph Definition
33
+
34
+ Each graph sample represents:
35
+
36
+ ```text
37
+ G_t = (V, E, X_t, A_t, y_t)
38
+ ```
39
+
40
+ where:
41
+
42
+ - `V` are bus nodes.
43
+ - `E` are physical line and transformer branches.
44
+ - `X_t` contains node features for scenario or time `t`.
45
+ - `A_t` contains edge features for scenario or time `t`.
46
+ - `y_t` is an optional label.
47
+
48
+ For the MVP, TopoStateGrid builds a homogeneous bus-branch graph and exports a PyTorch Geometric `Data` object with:
49
+
50
+ - `data.x`
51
+ - `data.edge_index`
52
+ - `data.edge_attr`
53
+ - `data.y`, optional label value; unlabeled graphs use `data.has_label=False` with placeholder label tensors for PyG batching
54
+ - `data.network_id`
55
+ - `data.sample_id`
56
+ - `data.timestamp`, optional
57
+ - `data.scenario_id`, optional
58
+ - `data.contingency_id`, optional
59
+ - `data.metadata`, a JSON string for source-specific metadata
60
+
61
+ Edges are stored bidirectionally so message passing can use both branch directions.
62
+
63
+ Source-specific metadata is stored as a JSON string rather than a Python dict so OPFData and MATPOWER graphs remain batchable together with PyTorch Geometric `DataLoader`.
64
+
65
+ ## Supported Inputs
66
+
67
+ Supported input sources in v1.1:
68
+
69
+ - OPFData JSON
70
+ - MATPOWER / PGLib `.m`
71
+ - pandapower `net` object
72
+ - pandas DataFrame tables
73
+ - CSV tables
74
+
75
+ Current working input paths:
76
+
77
+ - Extracted OPFData JSON samples under `data/opfdata/**/group_*/example_*.json`
78
+ - Static MATPOWER/PGLib `.m` files with `mpc.bus` and `mpc.branch` tables
79
+
80
+ The MATPOWER parser accepts common matrix syntax: comma-delimited or whitespace-delimited rows, semicolons, `%` comments, scientific notation, multi-line matrices, and explicit empty matrices such as `mpc.branch = [ ];`. Missing required `mpc.bus` or `mpc.branch` declarations raise `ValueError`; an explicitly present empty `mpc.branch` is allowed for isolated-bus fixtures.
81
+
82
+ The OPFData parser validates that JSON is well-formed and that `grid.nodes.bus` is present and non-empty. Malformed JSON and missing required fields raise `ValueError` with the source path included.
83
+
84
+ The local environment used for this prototype contains extracted OPFData samples for `pglib_opf_case14_ieee` and `pglib_opf_case30_ieee`, plus a static PGLib MATPOWER case for `pglib_opf_case118_ieee`.
85
+
86
+ pandapower support is optional. Install it with:
87
+
88
+ ```bash
89
+ python -m pip install -e ".[pandapower]"
90
+ ```
91
+
92
+ The pandapower converter supports bus nodes and line/transformer branch edges. For lines, `rate_a` uses `max_i_ka` as an approximate rating proxy when no direct MVA rating is available. The graph remains homogeneous bus-branch only.
93
+
94
+ Graph rendering support is also optional. Install it with:
95
+
96
+ ```bash
97
+ python -m pip install -e ".[visual]"
98
+ ```
99
+
100
+ The renderer writes GIF or MP4 files from existing graph samples for inspection. It does not simulate grid dynamics.
101
+
102
+ ## Features
103
+
104
+ Node features:
105
+
106
+ ```text
107
+ bus_status, bus_type, pd, qd, vm, va, vmax, vmin, normalized_demand
108
+ ```
109
+
110
+ For OPFData, `pd` and `qd` are aggregated from load nodes through `load_link` edges. `vm` and `va` are read from solved bus states when available. Missing values are filled with zero after NaN-safe conversion.
111
+
112
+ Edge features:
113
+
114
+ ```text
115
+ component_type, r, x, b_from, b_to, rate_a, pf, qf, pt, qt, loading_ratio, outage_flag
116
+ ```
117
+
118
+ `component_type` is `0` for AC lines and `1` for transformers. OPFData solution flows are used when present. Static MATPOWER/PGLib cases include physical branch attributes, but solved flow fields are set to zero unless supplied by another source.
119
+
120
+ ## Static Topology vs Operating State
121
+
122
+ Topology and component attributes come from buses, lines, transformers, and branch parameters. Operating state comes from scenario-dependent demand, solved bus voltage, solved branch flow, and derived loading ratio.
123
+
124
+ For the same network, `edge_index` can remain fixed across scenarios while `data.x` and `data.edge_attr` vary by sample. This supports later supervised GNNs, contrastive or masked-feature self-supervision, and temporal forecasting when ordered timestamps are available.
125
+
126
+ ## Labels
127
+
128
+ `topostategrid.labels.attach_stress_proxy_labels` can attach temporary proxy labels:
129
+
130
+ ```text
131
+ risk_score = max_line_loading_ratio
132
+ y_cls = 1 if max_line_loading_ratio > 1.0 else 0
133
+ y_reg = risk_score
134
+ ```
135
+
136
+ This is only a stress proxy for graph-construction experiments. It is not a real cascading-failure target.
137
+
138
+ Proxy label attachment is in-place and will not overwrite existing `data.y`, `data.y_cls`, `data.y_reg`, or `data.risk_score` by default. Pass `overwrite=True` only when replacing existing labels is intentional.
139
+
140
+ ## Splits
141
+
142
+ Implemented split strategies:
143
+
144
+ - Random split
145
+ - Time-based split when timestamps exist, otherwise input order
146
+ - Leave-One-Network-Out split with `create_lono_split(dataset, test_network="...")`
147
+
148
+ LONO is useful for cross-topology evaluation, for example training on `case14` and testing on `case30` or `case118`.
149
+
150
+ Random and time-based splits require each positive-ratio split to receive at least one graph by default. Tiny datasets raise `ValueError`; pass `allow_empty=True` to permit empty splits. LONO raises `ValueError` when the test network is absent, when graph objects lack `network_id`, or when train/test would be empty.
151
+
152
+ Time-based splitting treats `None`, empty strings, and NaN-like timestamps as missing. It sorts only when all timestamps are valid and comparable; otherwise it falls back to input order. Temporal windows use the same timestamp rule by default through `make_temporal_windows(..., sort_by_timestamp=True)`.
153
+
154
+ ## Normalization
155
+
156
+ `FeatureNormalizer` fits node and edge feature statistics only on the training split, then transforms train/validation/test graphs using the same statistics. This avoids data leakage from validation or test graphs.
157
+
158
+ ## Usage
159
+
160
+ Build one graph:
161
+
162
+ ```bash
163
+ python examples/01_build_single_graph.py
164
+ ```
165
+
166
+ Build multiple scenario graphs:
167
+
168
+ ```bash
169
+ python examples/02_build_multiple_state_graphs.py
170
+ ```
171
+
172
+ Create temporal windows over ordered samples:
173
+
174
+ ```bash
175
+ python examples/03_create_temporal_windows.py
176
+ ```
177
+
178
+ Create random, ordered, and LONO splits:
179
+
180
+ ```bash
181
+ python examples/04_create_splits.py
182
+ ```
183
+
184
+ Render a small graph-state sequence to GIF:
185
+
186
+ ```bash
187
+ python examples/07_render_graph_animation.py
188
+ ```
189
+
190
+ Render a 20-second GIF from pandapower's 300-bus benchmark:
191
+
192
+ ```bash
193
+ python examples/08_render_large_pandapower_gif.py
194
+ ```
195
+
196
+ Run tests:
197
+
198
+ ```bash
199
+ python -m unittest discover -s tests -q
200
+ ```
201
+
202
+ The tests are also compatible with `pytest` if it is installed.
203
+
204
+ Install optional test tooling with:
205
+
206
+ ```bash
207
+ python -m pip install -e ".[test]"
208
+ ```
209
+
210
+ On systems where the default matplotlib cache directory is not writable, use a writable cache directory for tests or rendering:
211
+
212
+ ```bash
213
+ MPLCONFIGDIR=/private/tmp/topostategrid-mpl python -m unittest discover -s tests -q
214
+ ```
215
+
216
+ On some macOS/conda environments, importing `torch`, `torch_geometric`, and numeric packages in one probe may expose an OpenMP runtime conflict from binary dependencies. Prefer a clean, consistent conda or virtualenv environment and avoid mixing package channels where possible.
217
+
218
+ pandapower may warn that `numba` is not installed. That warning only affects pandapower runtime speed; install `numba` separately if pandapower performance matters.
219
+
220
+ ## Output Files
221
+
222
+ The examples write to `outputs/`, including:
223
+
224
+ - `graphs.pt`
225
+ - `metadata.csv`
226
+ - `graphs_multi.pt`
227
+ - `metadata_multi.csv`
228
+ - `split_random.json`
229
+ - `split_time.json`
230
+ - `split_lono.json`
231
+ - `temporal_windows.pt`
232
+ - `graphs_tables.pt`
233
+ - `graphs_pandapower.pt`, when pandapower is installed
234
+ - `topostategrid_sequence.gif`, when visualization dependencies are installed
235
+ - `topostategrid_case300_20s.gif`, when pandapower and visualization dependencies are installed
236
+ - `README_generated.md`
237
+
238
+ Use `topostategrid.export.load_graphs` to load `.pt` files because it handles recent PyTorch `weights_only` defaults.
239
+
240
+ The example scripts assume the repository-local `data/` layout used by this prototype and overwrite their corresponding files in `outputs/` on repeated runs. Use the package functions directly when you need custom input paths or run-specific output directories.
241
+
242
+ ## v1.1 Table And pandapower Examples
243
+
244
+ Build from pandas DataFrames:
245
+
246
+ ```python
247
+ import pandas as pd
248
+ from topostategrid import build_graph_from_tables
249
+
250
+ bus_df = pd.DataFrame({
251
+ "bus_id": [1, 2, 3],
252
+ "bus_type": [3, 1, 1],
253
+ "pd": [0.0, 1.5, 0.8],
254
+ "qd": [0.0, 0.4, 0.2],
255
+ })
256
+ branch_df = pd.DataFrame({
257
+ "from_bus": [1, 2],
258
+ "to_bus": [2, 3],
259
+ "r": [0.01, 0.02],
260
+ "x": [0.05, 0.06],
261
+ })
262
+
263
+ data = build_graph_from_tables(
264
+ bus_df,
265
+ branch_df,
266
+ network_id="toy_3bus",
267
+ sample_id="sample_0",
268
+ )
269
+ ```
270
+
271
+ Build from CSV tables:
272
+
273
+ ```python
274
+ from topostategrid import build_graph_from_csv_tables
275
+
276
+ data = build_graph_from_csv_tables(
277
+ "bus.csv",
278
+ "branch.csv",
279
+ network_id="toy_3bus",
280
+ )
281
+ ```
282
+
283
+ Build from pandapower:
284
+
285
+ ```python
286
+ import pandapower as pp
287
+ from topostategrid import build_graph_from_pandapower
288
+
289
+ net = pp.create_empty_network()
290
+ b1 = pp.create_bus(net, vn_kv=110)
291
+ b2 = pp.create_bus(net, vn_kv=110)
292
+ b3 = pp.create_bus(net, vn_kv=110)
293
+ pp.create_ext_grid(net, b1)
294
+ pp.create_load(net, b2, p_mw=10.0, q_mvar=3.0)
295
+ pp.create_line_from_parameters(net, b1, b2, 1.0, 0.1, 0.2, 0.0, 0.4)
296
+ pp.create_line_from_parameters(net, b2, b3, 1.0, 0.1, 0.2, 0.0, 0.4)
297
+ pp.runpp(net)
298
+
299
+ data = build_graph_from_pandapower(net, network_id="pandapower_3bus")
300
+ ```
301
+
302
+ Render constructed graph samples to GIF:
303
+
304
+ ```python
305
+ from topostategrid import render_graph_sequence
306
+
307
+ render_graph_sequence(
308
+ [data],
309
+ "outputs/topostategrid_sequence.gif",
310
+ node_value="vm",
311
+ edge_value="loading_ratio",
312
+ )
313
+ ```
314
+
315
+ TopoStateGrid v1.1 still does not include a GNN model, cascading-failure simulator, `.mat` support, or heterogeneous graph construction.
@@ -0,0 +1,296 @@
1
+ # TopoStateGrid
2
+
3
+ TopoStateGrid is a physically informed graph construction method that converts power-grid topology, component attributes, and operating-state variables into machine-learning-ready graph datasets.
4
+
5
+ The Python package import name is `topostategrid`.
6
+
7
+ ## Scope
8
+
9
+ TopoStateGrid focuses on physically grounded, state-dependent, and optionally time-indexed graph dataset construction for power-system machine learning. PowerGraph can be used as a reference dataset, and pandapower can be used as a parsing or simulation tool, but the main output is a reusable graph-construction pipeline.
10
+
11
+ This prototype does not build a GNN model, does not implement a cascading-failure simulator, and does not claim to be the first power-grid graph dataset tool.
12
+
13
+ ## Graph Definition
14
+
15
+ Each graph sample represents:
16
+
17
+ ```text
18
+ G_t = (V, E, X_t, A_t, y_t)
19
+ ```
20
+
21
+ where:
22
+
23
+ - `V` are bus nodes.
24
+ - `E` are physical line and transformer branches.
25
+ - `X_t` contains node features for scenario or time `t`.
26
+ - `A_t` contains edge features for scenario or time `t`.
27
+ - `y_t` is an optional label.
28
+
29
+ For the MVP, TopoStateGrid builds a homogeneous bus-branch graph and exports a PyTorch Geometric `Data` object with:
30
+
31
+ - `data.x`
32
+ - `data.edge_index`
33
+ - `data.edge_attr`
34
+ - `data.y`, optional label value; unlabeled graphs use `data.has_label=False` with placeholder label tensors for PyG batching
35
+ - `data.network_id`
36
+ - `data.sample_id`
37
+ - `data.timestamp`, optional
38
+ - `data.scenario_id`, optional
39
+ - `data.contingency_id`, optional
40
+ - `data.metadata`, a JSON string for source-specific metadata
41
+
42
+ Edges are stored bidirectionally so message passing can use both branch directions.
43
+
44
+ Source-specific metadata is stored as a JSON string rather than a Python dict so OPFData and MATPOWER graphs remain batchable together with PyTorch Geometric `DataLoader`.
45
+
46
+ ## Supported Inputs
47
+
48
+ Supported input sources in v1.1:
49
+
50
+ - OPFData JSON
51
+ - MATPOWER / PGLib `.m`
52
+ - pandapower `net` object
53
+ - pandas DataFrame tables
54
+ - CSV tables
55
+
56
+ Current working input paths:
57
+
58
+ - Extracted OPFData JSON samples under `data/opfdata/**/group_*/example_*.json`
59
+ - Static MATPOWER/PGLib `.m` files with `mpc.bus` and `mpc.branch` tables
60
+
61
+ The MATPOWER parser accepts common matrix syntax: comma-delimited or whitespace-delimited rows, semicolons, `%` comments, scientific notation, multi-line matrices, and explicit empty matrices such as `mpc.branch = [ ];`. Missing required `mpc.bus` or `mpc.branch` declarations raise `ValueError`; an explicitly present empty `mpc.branch` is allowed for isolated-bus fixtures.
62
+
63
+ The OPFData parser validates that JSON is well-formed and that `grid.nodes.bus` is present and non-empty. Malformed JSON and missing required fields raise `ValueError` with the source path included.
64
+
65
+ The local environment used for this prototype contains extracted OPFData samples for `pglib_opf_case14_ieee` and `pglib_opf_case30_ieee`, plus a static PGLib MATPOWER case for `pglib_opf_case118_ieee`.
66
+
67
+ pandapower support is optional. Install it with:
68
+
69
+ ```bash
70
+ python -m pip install -e ".[pandapower]"
71
+ ```
72
+
73
+ The pandapower converter supports bus nodes and line/transformer branch edges. For lines, `rate_a` uses `max_i_ka` as an approximate rating proxy when no direct MVA rating is available. The graph remains homogeneous bus-branch only.
74
+
75
+ Graph rendering support is also optional. Install it with:
76
+
77
+ ```bash
78
+ python -m pip install -e ".[visual]"
79
+ ```
80
+
81
+ The renderer writes GIF or MP4 files from existing graph samples for inspection. It does not simulate grid dynamics.
82
+
83
+ ## Features
84
+
85
+ Node features:
86
+
87
+ ```text
88
+ bus_status, bus_type, pd, qd, vm, va, vmax, vmin, normalized_demand
89
+ ```
90
+
91
+ For OPFData, `pd` and `qd` are aggregated from load nodes through `load_link` edges. `vm` and `va` are read from solved bus states when available. Missing values are filled with zero after NaN-safe conversion.
92
+
93
+ Edge features:
94
+
95
+ ```text
96
+ component_type, r, x, b_from, b_to, rate_a, pf, qf, pt, qt, loading_ratio, outage_flag
97
+ ```
98
+
99
+ `component_type` is `0` for AC lines and `1` for transformers. OPFData solution flows are used when present. Static MATPOWER/PGLib cases include physical branch attributes, but solved flow fields are set to zero unless supplied by another source.
100
+
101
+ ## Static Topology vs Operating State
102
+
103
+ Topology and component attributes come from buses, lines, transformers, and branch parameters. Operating state comes from scenario-dependent demand, solved bus voltage, solved branch flow, and derived loading ratio.
104
+
105
+ For the same network, `edge_index` can remain fixed across scenarios while `data.x` and `data.edge_attr` vary by sample. This supports later supervised GNNs, contrastive or masked-feature self-supervision, and temporal forecasting when ordered timestamps are available.
106
+
107
+ ## Labels
108
+
109
+ `topostategrid.labels.attach_stress_proxy_labels` can attach temporary proxy labels:
110
+
111
+ ```text
112
+ risk_score = max_line_loading_ratio
113
+ y_cls = 1 if max_line_loading_ratio > 1.0 else 0
114
+ y_reg = risk_score
115
+ ```
116
+
117
+ This is only a stress proxy for graph-construction experiments. It is not a real cascading-failure target.
118
+
119
+ Proxy label attachment is in-place and will not overwrite existing `data.y`, `data.y_cls`, `data.y_reg`, or `data.risk_score` by default. Pass `overwrite=True` only when replacing existing labels is intentional.
120
+
121
+ ## Splits
122
+
123
+ Implemented split strategies:
124
+
125
+ - Random split
126
+ - Time-based split when timestamps exist, otherwise input order
127
+ - Leave-One-Network-Out split with `create_lono_split(dataset, test_network="...")`
128
+
129
+ LONO is useful for cross-topology evaluation, for example training on `case14` and testing on `case30` or `case118`.
130
+
131
+ Random and time-based splits require each positive-ratio split to receive at least one graph by default. Tiny datasets raise `ValueError`; pass `allow_empty=True` to permit empty splits. LONO raises `ValueError` when the test network is absent, when graph objects lack `network_id`, or when train/test would be empty.
132
+
133
+ Time-based splitting treats `None`, empty strings, and NaN-like timestamps as missing. It sorts only when all timestamps are valid and comparable; otherwise it falls back to input order. Temporal windows use the same timestamp rule by default through `make_temporal_windows(..., sort_by_timestamp=True)`.
134
+
135
+ ## Normalization
136
+
137
+ `FeatureNormalizer` fits node and edge feature statistics only on the training split, then transforms train/validation/test graphs using the same statistics. This avoids data leakage from validation or test graphs.
138
+
139
+ ## Usage
140
+
141
+ Build one graph:
142
+
143
+ ```bash
144
+ python examples/01_build_single_graph.py
145
+ ```
146
+
147
+ Build multiple scenario graphs:
148
+
149
+ ```bash
150
+ python examples/02_build_multiple_state_graphs.py
151
+ ```
152
+
153
+ Create temporal windows over ordered samples:
154
+
155
+ ```bash
156
+ python examples/03_create_temporal_windows.py
157
+ ```
158
+
159
+ Create random, ordered, and LONO splits:
160
+
161
+ ```bash
162
+ python examples/04_create_splits.py
163
+ ```
164
+
165
+ Render a small graph-state sequence to GIF:
166
+
167
+ ```bash
168
+ python examples/07_render_graph_animation.py
169
+ ```
170
+
171
+ Render a 20-second GIF from pandapower's 300-bus benchmark:
172
+
173
+ ```bash
174
+ python examples/08_render_large_pandapower_gif.py
175
+ ```
176
+
177
+ Run tests:
178
+
179
+ ```bash
180
+ python -m unittest discover -s tests -q
181
+ ```
182
+
183
+ The tests are also compatible with `pytest` if it is installed.
184
+
185
+ Install optional test tooling with:
186
+
187
+ ```bash
188
+ python -m pip install -e ".[test]"
189
+ ```
190
+
191
+ On systems where the default matplotlib cache directory is not writable, use a writable cache directory for tests or rendering:
192
+
193
+ ```bash
194
+ MPLCONFIGDIR=/private/tmp/topostategrid-mpl python -m unittest discover -s tests -q
195
+ ```
196
+
197
+ On some macOS/conda environments, importing `torch`, `torch_geometric`, and numeric packages in one probe may expose an OpenMP runtime conflict from binary dependencies. Prefer a clean, consistent conda or virtualenv environment and avoid mixing package channels where possible.
198
+
199
+ pandapower may warn that `numba` is not installed. That warning only affects pandapower runtime speed; install `numba` separately if pandapower performance matters.
200
+
201
+ ## Output Files
202
+
203
+ The examples write to `outputs/`, including:
204
+
205
+ - `graphs.pt`
206
+ - `metadata.csv`
207
+ - `graphs_multi.pt`
208
+ - `metadata_multi.csv`
209
+ - `split_random.json`
210
+ - `split_time.json`
211
+ - `split_lono.json`
212
+ - `temporal_windows.pt`
213
+ - `graphs_tables.pt`
214
+ - `graphs_pandapower.pt`, when pandapower is installed
215
+ - `topostategrid_sequence.gif`, when visualization dependencies are installed
216
+ - `topostategrid_case300_20s.gif`, when pandapower and visualization dependencies are installed
217
+ - `README_generated.md`
218
+
219
+ Use `topostategrid.export.load_graphs` to load `.pt` files because it handles recent PyTorch `weights_only` defaults.
220
+
221
+ The example scripts assume the repository-local `data/` layout used by this prototype and overwrite their corresponding files in `outputs/` on repeated runs. Use the package functions directly when you need custom input paths or run-specific output directories.
222
+
223
+ ## v1.1 Table And pandapower Examples
224
+
225
+ Build from pandas DataFrames:
226
+
227
+ ```python
228
+ import pandas as pd
229
+ from topostategrid import build_graph_from_tables
230
+
231
+ bus_df = pd.DataFrame({
232
+ "bus_id": [1, 2, 3],
233
+ "bus_type": [3, 1, 1],
234
+ "pd": [0.0, 1.5, 0.8],
235
+ "qd": [0.0, 0.4, 0.2],
236
+ })
237
+ branch_df = pd.DataFrame({
238
+ "from_bus": [1, 2],
239
+ "to_bus": [2, 3],
240
+ "r": [0.01, 0.02],
241
+ "x": [0.05, 0.06],
242
+ })
243
+
244
+ data = build_graph_from_tables(
245
+ bus_df,
246
+ branch_df,
247
+ network_id="toy_3bus",
248
+ sample_id="sample_0",
249
+ )
250
+ ```
251
+
252
+ Build from CSV tables:
253
+
254
+ ```python
255
+ from topostategrid import build_graph_from_csv_tables
256
+
257
+ data = build_graph_from_csv_tables(
258
+ "bus.csv",
259
+ "branch.csv",
260
+ network_id="toy_3bus",
261
+ )
262
+ ```
263
+
264
+ Build from pandapower:
265
+
266
+ ```python
267
+ import pandapower as pp
268
+ from topostategrid import build_graph_from_pandapower
269
+
270
+ net = pp.create_empty_network()
271
+ b1 = pp.create_bus(net, vn_kv=110)
272
+ b2 = pp.create_bus(net, vn_kv=110)
273
+ b3 = pp.create_bus(net, vn_kv=110)
274
+ pp.create_ext_grid(net, b1)
275
+ pp.create_load(net, b2, p_mw=10.0, q_mvar=3.0)
276
+ pp.create_line_from_parameters(net, b1, b2, 1.0, 0.1, 0.2, 0.0, 0.4)
277
+ pp.create_line_from_parameters(net, b2, b3, 1.0, 0.1, 0.2, 0.0, 0.4)
278
+ pp.runpp(net)
279
+
280
+ data = build_graph_from_pandapower(net, network_id="pandapower_3bus")
281
+ ```
282
+
283
+ Render constructed graph samples to GIF:
284
+
285
+ ```python
286
+ from topostategrid import render_graph_sequence
287
+
288
+ render_graph_sequence(
289
+ [data],
290
+ "outputs/topostategrid_sequence.gif",
291
+ node_value="vm",
292
+ edge_value="loading_ratio",
293
+ )
294
+ ```
295
+
296
+ TopoStateGrid v1.1 still does not include a GNN model, cascading-failure simulator, `.mat` support, or heterogeneous graph construction.