gdm-flow 0.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.
- gdm_flow-0.1.0/PKG-INFO +389 -0
- gdm_flow-0.1.0/README.md +363 -0
- gdm_flow-0.1.0/pyproject.toml +46 -0
- gdm_flow-0.1.0/setup.cfg +4 -0
- gdm_flow-0.1.0/src/gdm_flow/__init__.py +102 -0
- gdm_flow-0.1.0/src/gdm_flow/_utils.py +20 -0
- gdm_flow-0.1.0/src/gdm_flow/ac_opf.py +994 -0
- gdm_flow-0.1.0/src/gdm_flow/ac_pf.py +414 -0
- gdm_flow-0.1.0/src/gdm_flow/cli.py +1895 -0
- gdm_flow-0.1.0/src/gdm_flow/dashboard.py +1916 -0
- gdm_flow-0.1.0/src/gdm_flow/dc_opf.py +532 -0
- gdm_flow-0.1.0/src/gdm_flow/export_cli.py +241 -0
- gdm_flow-0.1.0/src/gdm_flow/lindistflow.py +540 -0
- gdm_flow-0.1.0/src/gdm_flow/mcp/__init__.py +9 -0
- gdm_flow-0.1.0/src/gdm_flow/mcp/server.py +844 -0
- gdm_flow-0.1.0/src/gdm_flow/multiperiod.py +1017 -0
- gdm_flow-0.1.0/src/gdm_flow/sqlite_export.py +1065 -0
- gdm_flow-0.1.0/src/gdm_flow/time_series.py +1033 -0
- gdm_flow-0.1.0/src/gdm_flow/ybus.py +432 -0
- gdm_flow-0.1.0/src/gdm_flow.egg-info/PKG-INFO +389 -0
- gdm_flow-0.1.0/src/gdm_flow.egg-info/SOURCES.txt +40 -0
- gdm_flow-0.1.0/src/gdm_flow.egg-info/dependency_links.txt +1 -0
- gdm_flow-0.1.0/src/gdm_flow.egg-info/entry_points.txt +4 -0
- gdm_flow-0.1.0/src/gdm_flow.egg-info/requires.txt +22 -0
- gdm_flow-0.1.0/src/gdm_flow.egg-info/top_level.txt +1 -0
- gdm_flow-0.1.0/tests/test_ac_opf_additional.py +238 -0
- gdm_flow-0.1.0/tests/test_cli.py +1350 -0
- gdm_flow-0.1.0/tests/test_component_specs.py +152 -0
- gdm_flow-0.1.0/tests/test_dc_opf.py +68 -0
- gdm_flow-0.1.0/tests/test_dc_opf_additional.py +244 -0
- gdm_flow-0.1.0/tests/test_export_cli.py +101 -0
- gdm_flow-0.1.0/tests/test_export_cli_additional.py +154 -0
- gdm_flow-0.1.0/tests/test_gdmloader_integration.py +27 -0
- gdm_flow-0.1.0/tests/test_lindistflow.py +60 -0
- gdm_flow-0.1.0/tests/test_lindistflow_additional.py +296 -0
- gdm_flow-0.1.0/tests/test_mcp_server.py +100 -0
- gdm_flow-0.1.0/tests/test_multiperiod.py +262 -0
- gdm_flow-0.1.0/tests/test_optimization.py +38 -0
- gdm_flow-0.1.0/tests/test_sqlite_export.py +311 -0
- gdm_flow-0.1.0/tests/test_time_series.py +271 -0
- gdm_flow-0.1.0/tests/test_ybus.py +221 -0
- gdm_flow-0.1.0/tests/test_ybus_additional.py +168 -0
gdm_flow-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gdm-flow
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Power flow utilities for grid-data-models distribution systems
|
|
5
|
+
Author: NLR Distribution Suite
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: numpy>=1.24
|
|
10
|
+
Requires-Dist: grid-data-models>=2.3.6
|
|
11
|
+
Requires-Dist: typer>=0.9
|
|
12
|
+
Requires-Dist: rich>=13.0
|
|
13
|
+
Provides-Extra: sparse
|
|
14
|
+
Requires-Dist: scipy>=1.10; extra == "sparse"
|
|
15
|
+
Provides-Extra: optimization
|
|
16
|
+
Requires-Dist: scipy>=1.10; extra == "optimization"
|
|
17
|
+
Provides-Extra: plotting
|
|
18
|
+
Requires-Dist: plotly>=5.0; extra == "plotting"
|
|
19
|
+
Provides-Extra: mcp
|
|
20
|
+
Requires-Dist: mcp>=1.0.0; extra == "mcp"
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
24
|
+
Requires-Dist: pre-commit>=3.7; extra == "dev"
|
|
25
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
26
|
+
|
|
27
|
+
# gdm-flow
|
|
28
|
+
|
|
29
|
+
[](https://github.com/NLR-Distribution-Suite/gdm_flow/actions/workflows/ci.yml) • [](https://github.com/NLR-Distribution-Suite/gdm_flow/actions/workflows/docs.yml) • [](https://github.com/NLR-Distribution-Suite/gdm_flow/actions/workflows/publish.yml) • [](https://codecov.io/github/NLR-Distribution-Suite/gdm_flow) •  •  • [](https://github.com/NLR-Distribution-Suite/gdm_flow/issues)
|
|
30
|
+
|
|
31
|
+
Utilities for OPF and power-flow style preprocessing on top of
|
|
32
|
+
`grid-data-models` distribution systems.
|
|
33
|
+
|
|
34
|
+
## Current features
|
|
35
|
+
|
|
36
|
+
- **Y-Bus Construction** — Phase-domain admittance matrices from GDM components (branches, transformers, switches) with matrix and sequence impedance support
|
|
37
|
+
- **Four Solvers** — AC OPF (nonlinear least-squares), AC PF (Newton-Raphson power flow), DC OPF (quadratic programming), and LinDistFlow (backward/forward sweep)
|
|
38
|
+
- **Multi-Phase Support** — Full three-phase and split-phase (center-tapped transformer) modeling
|
|
39
|
+
- **Component Integration** — Direct integration with GDM loads, solar PV, batteries, capacitors, and regulators
|
|
40
|
+
- **Interactive Dashboards** — Plotly-based HTML dashboards with voltage profiles, power flow, branch loading, losses, and equipment state
|
|
41
|
+
- **QSTS Simulation** — Quasi-static time series with warm-starting across timesteps for any solver
|
|
42
|
+
- **Multi-Period OPF** — Joint optimization across a time horizon with battery SOC coupling and ramp constraints (DC OPF and LinDistFlow)
|
|
43
|
+
- **Time Series Discovery** — Inspect available load, solar, and battery time series profiles in GDM models
|
|
44
|
+
- **Modern CLI** — Rich terminal interface with `info`, `run`, `compare`, `plot`, `export`, `ts-info`, `qsts`, `multiperiod`, `plot-ts`, and reporting commands
|
|
45
|
+
- **SQLite Export** — Structured database output with voltage/loading limit tracking, violation reporting, and streaming time series results
|
|
46
|
+
- **MCP Server** — Model Context Protocol server exposing all solvers as AI-callable tools
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install -e .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If you also want sparse matrix return support:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install -e .[sparse]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
If you want optimization support:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install -e '.[optimization]'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If you want interactive plotting dashboards:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install -e '.[plotting]'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
If you want to run the MCP server:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install -e '.[mcp,optimization]'
|
|
76
|
+
gdm-flow-mcp-server
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Development checks
|
|
80
|
+
|
|
81
|
+
Install development dependencies and enable pre-commit hooks:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install -e '.[dev]'
|
|
85
|
+
pre-commit install
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run the same lint checks used in CI:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pre-commit run --all-files
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Examples
|
|
95
|
+
|
|
96
|
+
An end-to-end examples folder is included at `examples/` with a downloaded demo
|
|
97
|
+
distribution model and runnable scripts for each optimization flavor:
|
|
98
|
+
|
|
99
|
+
- `examples/models/p5r.json`
|
|
100
|
+
- `examples/run_ac_opf_example.py`
|
|
101
|
+
- `examples/run_dc_opf_example.py`
|
|
102
|
+
- `examples/run_lindistflow_example.py`
|
|
103
|
+
- `examples/compare_plotly_results.py` — interactive Plotly comparison across all solvers
|
|
104
|
+
|
|
105
|
+
Run from the repository root:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
python examples/run_ac_opf_example.py
|
|
109
|
+
python examples/run_dc_opf_example.py
|
|
110
|
+
python examples/run_lindistflow_example.py
|
|
111
|
+
python examples/compare_plotly_results.py
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Usage
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from gdm.distribution import DistributionSystem
|
|
118
|
+
from gdm_flow import calculate_ybus
|
|
119
|
+
|
|
120
|
+
system = DistributionSystem.from_json("path/to/system.json")
|
|
121
|
+
result = calculate_ybus(system, include_shunt=False)
|
|
122
|
+
|
|
123
|
+
Y = result.ybus
|
|
124
|
+
labels = result.index_to_label
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`labels[k]` is a `(bus_name, phase)` tuple for row/column `k` of `Y`.
|
|
128
|
+
|
|
129
|
+
## Optimization usage
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from gdm.distribution import DistributionSystem
|
|
133
|
+
from gdm_flow import optimize_ac_power_flow
|
|
134
|
+
|
|
135
|
+
system = DistributionSystem.from_json("path/to/system.json")
|
|
136
|
+
|
|
137
|
+
# Specify net nodal injections in SI units: +generation, -load
|
|
138
|
+
p_spec_w = {("bus_5", "A"): -20_000.0, ("bus_5", "B"): -20_000.0, ("bus_5", "C"): -20_000.0}
|
|
139
|
+
q_spec_var = {("bus_5", "A"): -5_000.0, ("bus_5", "B"): -5_000.0, ("bus_5", "C"): -5_000.0}
|
|
140
|
+
|
|
141
|
+
result = optimize_ac_power_flow(
|
|
142
|
+
system,
|
|
143
|
+
p_spec_w=p_spec_w,
|
|
144
|
+
q_spec_var=q_spec_var,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
print(result.success, result.final_objective)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Auto-build specs from components
|
|
151
|
+
|
|
152
|
+
If your model already contains `DistributionLoad` and `DistributionSolar` components,
|
|
153
|
+
you can build nodal injections automatically:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from gdm.distribution import DistributionSystem
|
|
157
|
+
from gdm_flow import optimize_ac_power_flow_from_components
|
|
158
|
+
|
|
159
|
+
system = DistributionSystem.from_json("path/to/system.json")
|
|
160
|
+
|
|
161
|
+
result = optimize_ac_power_flow_from_components(
|
|
162
|
+
system,
|
|
163
|
+
include_loads=True,
|
|
164
|
+
include_solar=True,
|
|
165
|
+
include_capacitor=True,
|
|
166
|
+
include_battery=False,
|
|
167
|
+
include_regulator_targets=True,
|
|
168
|
+
include_regulator_limits=True,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
print(result.success, result.final_objective)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Notes:
|
|
175
|
+
|
|
176
|
+
- `include_capacitor=True` adds capacitor reactive injections into nodal `Q` specs.
|
|
177
|
+
- `include_regulator_targets=True` adds soft voltage-target constraints derived from regulator controllers.
|
|
178
|
+
- `include_regulator_limits=True` adds hard voltage magnitude bounds from regulator min/max limits.
|
|
179
|
+
|
|
180
|
+
## AC Power Flow (Newton-Raphson)
|
|
181
|
+
|
|
182
|
+
`gdm-flow` includes a classical Newton-Raphson AC power flow solver. Unlike the AC OPF
|
|
183
|
+
which *optimises* voltage magnitudes within bounds, the AC PF solves for exact bus
|
|
184
|
+
voltages given fixed P/Q injections and a slack bus:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from gdm.distribution import DistributionSystem
|
|
188
|
+
from gdm_flow import solve_ac_power_flow_from_components
|
|
189
|
+
|
|
190
|
+
system = DistributionSystem.from_json("path/to/system.json")
|
|
191
|
+
|
|
192
|
+
result = solve_ac_power_flow_from_components(
|
|
193
|
+
system,
|
|
194
|
+
include_loads=True,
|
|
195
|
+
include_solar=True,
|
|
196
|
+
include_capacitor=True,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
print(result.success, result.iterations)
|
|
200
|
+
print(result.max_mismatch_pu) # convergence metric
|
|
201
|
+
print(result.voltage_pu) # per-unit voltage magnitudes
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The solver uses sparse LU factorisation with a damped update and LinDistFlow warm-start
|
|
205
|
+
for robust convergence on heavily loaded feeders.
|
|
206
|
+
|
|
207
|
+
## DC OPF module
|
|
208
|
+
|
|
209
|
+
`gdm-flow` also includes a separate DC OPF module:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from gdm.distribution import DistributionSystem
|
|
213
|
+
from gdm_flow import solve_dc_opf_from_components
|
|
214
|
+
|
|
215
|
+
system = DistributionSystem.from_json("path/to/system.json")
|
|
216
|
+
|
|
217
|
+
result = solve_dc_opf_from_components(
|
|
218
|
+
system,
|
|
219
|
+
include_solar_generators=True,
|
|
220
|
+
include_battery_generators=True,
|
|
221
|
+
include_loads=True,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
print(result.success, result.objective)
|
|
225
|
+
print(result.generator_dispatch_w)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## LinDistFlow module
|
|
229
|
+
|
|
230
|
+
`gdm-flow` includes a separate radial LinDistFlow approximation module:
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from gdm.distribution import DistributionSystem
|
|
234
|
+
from gdm_flow import solve_lindistflow
|
|
235
|
+
|
|
236
|
+
system = DistributionSystem.from_json("path/to/system.json")
|
|
237
|
+
|
|
238
|
+
result = solve_lindistflow(system)
|
|
239
|
+
|
|
240
|
+
print(result.success)
|
|
241
|
+
print(result.voltage_v) # {(bus, phase): voltage_in_volts}
|
|
242
|
+
print(result.p_flow_w) # {(branch_name, phase): active_flow_w}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## SQLite export module
|
|
246
|
+
|
|
247
|
+
You can export results from AC OPF, DC OPF, and LinDistFlow into one SQLite database:
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
from gdm_flow import export_all_results_to_sqlite
|
|
251
|
+
|
|
252
|
+
run_ids = export_all_results_to_sqlite(
|
|
253
|
+
"results.sqlite",
|
|
254
|
+
ac_result=ac_result,
|
|
255
|
+
dc_result=dc_result,
|
|
256
|
+
lindistflow_result=lindistflow_result,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
print(run_ids)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Or export each result type separately:
|
|
263
|
+
|
|
264
|
+
- `export_ac_opf_result_to_sqlite(...)`
|
|
265
|
+
- `export_dc_opf_result_to_sqlite(...)`
|
|
266
|
+
- `export_lindistflow_result_to_sqlite(...)`
|
|
267
|
+
|
|
268
|
+
## Interactive dashboards
|
|
269
|
+
|
|
270
|
+
Generate interactive Plotly HTML dashboards from the CLI:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
# Generate dashboard with all four solvers
|
|
274
|
+
gdm-flow plot examples/models/p5r.json
|
|
275
|
+
|
|
276
|
+
# Select specific solvers
|
|
277
|
+
gdm-flow plot examples/models/p5r.json -s ac -s pf
|
|
278
|
+
|
|
279
|
+
# Custom output path
|
|
280
|
+
gdm-flow plot examples/models/p5r.json -o my_dashboard.html
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Time series simulation
|
|
284
|
+
|
|
285
|
+
GDM-Flow supports time series simulation for models with time-varying load and solar
|
|
286
|
+
profiles. Two modes are available:
|
|
287
|
+
|
|
288
|
+
### QSTS (Quasi-Static Time Series)
|
|
289
|
+
|
|
290
|
+
Sequential snapshot solves at each timestep with automatic warm-starting:
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
from gdm.distribution import DistributionSystem
|
|
294
|
+
from gdm_flow import run_qsts
|
|
295
|
+
|
|
296
|
+
system = DistributionSystem.from_json("model.json")
|
|
297
|
+
|
|
298
|
+
summary = run_qsts(
|
|
299
|
+
system,
|
|
300
|
+
solver="ldf", # any solver: ac, pf, dc, ldf
|
|
301
|
+
timestep_range=range(96), # 24 hours at 15-min resolution
|
|
302
|
+
db_path="qsts_results.db", # stream results to SQLite
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
print(f"Converged: {summary.num_converged}/{summary.num_timesteps}")
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Multi-Period OPF
|
|
309
|
+
|
|
310
|
+
Joint optimization across the time horizon with battery SOC coupling:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from gdm_flow import solve_multiperiod_dc_opf
|
|
314
|
+
from gdm_flow.dc_opf import build_dc_generators_from_components
|
|
315
|
+
|
|
316
|
+
generators = build_dc_generators_from_components(system)
|
|
317
|
+
|
|
318
|
+
result = solve_multiperiod_dc_opf(
|
|
319
|
+
system,
|
|
320
|
+
generators=generators,
|
|
321
|
+
timestep_range=range(96),
|
|
322
|
+
ramp_limit_w=5000.0,
|
|
323
|
+
db_path="multiperiod.db",
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
print(f"Objective: {result.objective:.2f}")
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### CLI commands
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
# Check available time series
|
|
333
|
+
gdm-flow ts-info model.json
|
|
334
|
+
|
|
335
|
+
# QSTS simulation
|
|
336
|
+
gdm-flow qsts model.json --solver ldf --end 96 --db results.db
|
|
337
|
+
|
|
338
|
+
# Multi-period DC OPF
|
|
339
|
+
gdm-flow multiperiod model.json --solver dc --end 96 --ramp 5000 --db results.db
|
|
340
|
+
|
|
341
|
+
# Plot time series results
|
|
342
|
+
gdm-flow plot-ts results.db -o timeseries.html
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The dashboard includes:
|
|
346
|
+
- Voltage-distance profiles along feeder paths
|
|
347
|
+
- Per-phase voltage comparison across solvers
|
|
348
|
+
- Branch power flow and loading analysis
|
|
349
|
+
- Line loss breakdown
|
|
350
|
+
- Equipment state (capacitors, regulators, transformers)
|
|
351
|
+
|
|
352
|
+
Requires the `plotting` extra: `pip install -e '.[plotting]'`
|
|
353
|
+
|
|
354
|
+
### CLI export from JSON
|
|
355
|
+
|
|
356
|
+
Use the CLI to load saved result JSON files and export to SQLite in one command:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
gdm-flow-export --db results.sqlite --ac-json ac_result.json --dc-json dc_result.json --lindistflow-json ldf_result.json
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Generate starter JSON templates:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
gdm-flow-export --write-templates ./templates
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
This creates:
|
|
369
|
+
|
|
370
|
+
- `templates/ac_result.template.json`
|
|
371
|
+
- `templates/dc_result.template.json`
|
|
372
|
+
- `templates/lindistflow_result.template.json`
|
|
373
|
+
|
|
374
|
+
Expected JSON fields:
|
|
375
|
+
|
|
376
|
+
- AC JSON:
|
|
377
|
+
- `success`, `message`, `iterations`, `initial_objective`, `final_objective`
|
|
378
|
+
- `index_to_label`: `[["bus", "phase"], ...]`
|
|
379
|
+
- `voltage`: `[{"real": ..., "imag": ...}, ...]`
|
|
380
|
+
- `power_injection`: `[{"real": ..., "imag": ...}, ...]`
|
|
381
|
+
- DC JSON:
|
|
382
|
+
- `success`, `message`, `objective`, `iterations`, `slack_injection_w`
|
|
383
|
+
- `generator_dispatch_w`: `{name: value}`
|
|
384
|
+
- `theta_rad`: `{"bus|phase": value, ...}`
|
|
385
|
+
- `nodal_balance_w`: `{"bus|phase": value, ...}`
|
|
386
|
+
- LinDistFlow JSON:
|
|
387
|
+
- `success`, `message`, `source_bus`
|
|
388
|
+
- `voltage_v`, `p_flow_w`, `q_flow_var`, `p_net_w`, `q_net_var`
|
|
389
|
+
- All series use `{"name|phase": value, ...}` format.
|