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.
- topostategrid-1.1.0/PKG-INFO +315 -0
- topostategrid-1.1.0/README.md +296 -0
- topostategrid-1.1.0/TopoStateGrid.egg-info/PKG-INFO +315 -0
- topostategrid-1.1.0/TopoStateGrid.egg-info/SOURCES.txt +25 -0
- topostategrid-1.1.0/TopoStateGrid.egg-info/dependency_links.txt +1 -0
- topostategrid-1.1.0/TopoStateGrid.egg-info/requires.txt +15 -0
- topostategrid-1.1.0/TopoStateGrid.egg-info/top_level.txt +1 -0
- topostategrid-1.1.0/pyproject.toml +28 -0
- topostategrid-1.1.0/setup.cfg +4 -0
- topostategrid-1.1.0/tests/test_builder.py +109 -0
- topostategrid-1.1.0/tests/test_cross_source.py +84 -0
- topostategrid-1.1.0/tests/test_pandapower.py +70 -0
- topostategrid-1.1.0/tests/test_parser.py +92 -0
- topostategrid-1.1.0/tests/test_splits_temporal_labels.py +190 -0
- topostategrid-1.1.0/tests/test_tables.py +196 -0
- topostategrid-1.1.0/tests/test_visualization.py +86 -0
- topostategrid-1.1.0/topostategrid/__init__.py +57 -0
- topostategrid-1.1.0/topostategrid/builder.py +379 -0
- topostategrid-1.1.0/topostategrid/export.py +142 -0
- topostategrid-1.1.0/topostategrid/labels.py +116 -0
- topostategrid-1.1.0/topostategrid/normalizer.py +69 -0
- topostategrid-1.1.0/topostategrid/pandapower.py +218 -0
- topostategrid-1.1.0/topostategrid/parser.py +241 -0
- topostategrid-1.1.0/topostategrid/splits.py +176 -0
- topostategrid-1.1.0/topostategrid/tables.py +315 -0
- topostategrid-1.1.0/topostategrid/temporal.py +79 -0
- topostategrid-1.1.0/topostategrid/visualization.py +286 -0
|
@@ -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.
|