kuzu-grid-schema 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.
Files changed (25) hide show
  1. kuzu_grid_schema-0.1.0/PKG-INFO +32 -0
  2. kuzu_grid_schema-0.1.0/README.md +22 -0
  3. kuzu_grid_schema-0.1.0/pyproject.toml +27 -0
  4. kuzu_grid_schema-0.1.0/setup.cfg +4 -0
  5. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/__init__.py +17 -0
  6. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/apply.py +56 -0
  7. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/cypher/__init__.py +5 -0
  8. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/cypher/tier0.py +413 -0
  9. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/cypher/tier1.py +171 -0
  10. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/cypher/tier2.py +258 -0
  11. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/cypher/tier3.py +209 -0
  12. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/effective_refresh.py +62 -0
  13. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/resources.py +31 -0
  14. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/schema/kuzu_grid_schema_composed.cypher +158 -0
  15. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/schema/schema_contract.json +227 -0
  16. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/schema/tiers/tier0.schema.json +69 -0
  17. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/schema/tiers/tier1.schema.json +26 -0
  18. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/schema/tiers/tier2.schema.json +61 -0
  19. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema/schema/tiers/tier3.schema.json +44 -0
  20. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema.egg-info/PKG-INFO +32 -0
  21. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema.egg-info/SOURCES.txt +23 -0
  22. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema.egg-info/dependency_links.txt +1 -0
  23. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema.egg-info/requires.txt +4 -0
  24. kuzu_grid_schema-0.1.0/src/kuzu_grid_schema.egg-info/top_level.txt +1 -0
  25. kuzu_grid_schema-0.1.0/tests/test_schema_and_crud.py +136 -0
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: kuzu-grid-schema
3
+ Version: 0.1.0
4
+ Summary: Shared Kuzu grid tier schemas, composed DDL, and per-tier Cypher CRUD
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: kuzu>=0.7.0
8
+ Provides-Extra: dev
9
+ Requires-Dist: pytest>=7.0; extra == "dev"
10
+
11
+ # kuzu-grid-schema
12
+
13
+ Pip-installable library that **owns** the Kuzu grid working-graph schema:
14
+
15
+ - Tier 0–3 JSON contracts
16
+ - One composed DDL for a **single** shared database
17
+ - Per-tier Cypher CRUD (write statements never cross tier ownership)
18
+ - `effective_*` refresh (`base → latest_actual → scenario → effective`)
19
+
20
+ ```bash
21
+ pip install -e .
22
+ ```
23
+
24
+ ```python
25
+ from kuzu_grid_schema import apply_schema, open_database, refresh_effective
26
+ from kuzu_grid_schema.cypher import tier0, tier1, tier2, tier3
27
+
28
+ db, conn = open_database(":memory:")
29
+ apply_schema(conn)
30
+ ```
31
+
32
+ Other packages (including `kuzu-mcp`) should depend on this library via pip — do not copy DDL.
@@ -0,0 +1,22 @@
1
+ # kuzu-grid-schema
2
+
3
+ Pip-installable library that **owns** the Kuzu grid working-graph schema:
4
+
5
+ - Tier 0–3 JSON contracts
6
+ - One composed DDL for a **single** shared database
7
+ - Per-tier Cypher CRUD (write statements never cross tier ownership)
8
+ - `effective_*` refresh (`base → latest_actual → scenario → effective`)
9
+
10
+ ```bash
11
+ pip install -e .
12
+ ```
13
+
14
+ ```python
15
+ from kuzu_grid_schema import apply_schema, open_database, refresh_effective
16
+ from kuzu_grid_schema.cypher import tier0, tier1, tier2, tier3
17
+
18
+ db, conn = open_database(":memory:")
19
+ apply_schema(conn)
20
+ ```
21
+
22
+ Other packages (including `kuzu-mcp`) should depend on this library via pip — do not copy DDL.
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "kuzu-grid-schema"
7
+ version = "0.1.0"
8
+ description = "Shared Kuzu grid tier schemas, composed DDL, and per-tier Cypher CRUD"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "kuzu>=0.7.0",
13
+ ]
14
+
15
+ [project.optional-dependencies]
16
+ dev = ["pytest>=7.0"]
17
+
18
+ [tool.setuptools.packages.find]
19
+ where = ["src"]
20
+ include = ["kuzu_grid_schema*"]
21
+
22
+ [tool.setuptools.package-data]
23
+ kuzu_grid_schema = ["schema/**/*.json", "schema/**/*.cypher"]
24
+
25
+ [tool.pytest.ini_options]
26
+ testpaths = ["tests"]
27
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,17 @@
1
+ """Shared Kuzu grid schema: contracts, DDL, per-tier Cypher, effective refresh."""
2
+
3
+ from kuzu_grid_schema.apply import apply_schema, open_database, query_rows
4
+ from kuzu_grid_schema.effective_refresh import refresh_effective
5
+ from kuzu_grid_schema.resources import load_composed_ddl, load_schema_contract, load_tier_schema
6
+
7
+ __all__ = [
8
+ "apply_schema",
9
+ "open_database",
10
+ "query_rows",
11
+ "refresh_effective",
12
+ "load_composed_ddl",
13
+ "load_schema_contract",
14
+ "load_tier_schema",
15
+ ]
16
+
17
+ __version__ = "0.1.0"
@@ -0,0 +1,56 @@
1
+ """Apply composed DDL and open a single shared Kuzu database."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ import kuzu
9
+
10
+ from kuzu_grid_schema.resources import load_composed_ddl
11
+
12
+
13
+ def open_database(path: str | Path = ":memory:") -> tuple[kuzu.Database, kuzu.Connection]:
14
+ """Open one Kuzu DB used by all tiers together."""
15
+ db = kuzu.Database(str(path))
16
+ conn = kuzu.Connection(db)
17
+ return db, conn
18
+
19
+
20
+ def apply_schema(conn: kuzu.Connection, ddl: str | None = None) -> None:
21
+ """Apply the composed DDL (tiers 0–3) to a single connection."""
22
+ text = ddl if ddl is not None else load_composed_ddl()
23
+ for statement in _split_cypher(text):
24
+ if statement.strip():
25
+ conn.execute(statement)
26
+
27
+
28
+ def query_rows(conn: kuzu.Connection, cypher: str, params: dict[str, Any] | None = None) -> list[dict[str, Any]]:
29
+ """Return rows as dicts without requiring pandas/numpy."""
30
+ result = conn.execute(cypher, params or {})
31
+ columns = list(result.get_column_names())
32
+ rows: list[dict[str, Any]] = []
33
+ while result.has_next():
34
+ values = result.get_next()
35
+ rows.append(dict(zip(columns, values, strict=True)))
36
+ return rows
37
+
38
+
39
+ def execute(conn: kuzu.Connection, cypher: str, params: dict[str, Any] | None = None) -> None:
40
+ conn.execute(cypher, params or {})
41
+
42
+
43
+ def _split_cypher(text: str) -> list[str]:
44
+ statements: list[str] = []
45
+ current: list[str] = []
46
+ for line in text.splitlines():
47
+ stripped = line.strip()
48
+ if stripped.startswith("//"):
49
+ continue
50
+ current.append(line)
51
+ if stripped.endswith(";"):
52
+ statements.append("\n".join(current))
53
+ current = []
54
+ if current:
55
+ statements.append("\n".join(current))
56
+ return statements
@@ -0,0 +1,5 @@
1
+ """Per-tier Cypher CRUD modules (distinct write statements; no cross-tier SET)."""
2
+
3
+ from kuzu_grid_schema.cypher import tier0, tier1, tier2, tier3
4
+
5
+ __all__ = ["tier0", "tier1", "tier2", "tier3"]
@@ -0,0 +1,413 @@
1
+ """Tier 0 Cypher CRUD: topology + base_* only (never latest_actual_/scenario_/effective_)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from kuzu_grid_schema.apply import execute, query_rows
8
+
9
+ # Forbidden prefixes in this module's write statements (enforced by tests).
10
+ FORBIDDEN_WRITE_PREFIXES = ("latest_actual_", "scenario_", "effective_", "dc_")
11
+
12
+ CREATE_BUS = """
13
+ CREATE (b:Bus {
14
+ bus_id: $bus_id,
15
+ name: $name,
16
+ base_kv: $base_kv,
17
+ bus_type: $bus_type,
18
+ zone: $zone,
19
+ in_service: $in_service,
20
+ is_slack: $is_slack,
21
+ base_in_service: $base_in_service,
22
+ geom: $geom
23
+ })
24
+ """
25
+
26
+ GET_BUS = """
27
+ MATCH (b:Bus {bus_id: $bus_id})
28
+ RETURN b.bus_id AS bus_id, b.name AS name, b.base_kv AS base_kv, b.bus_type AS bus_type,
29
+ b.zone AS zone, b.in_service AS in_service, b.is_slack AS is_slack,
30
+ b.base_in_service AS base_in_service, b.geom AS geom
31
+ """
32
+
33
+ LIST_BUS = """
34
+ MATCH (b:Bus)
35
+ RETURN b.bus_id AS bus_id, b.name AS name, b.base_kv AS base_kv, b.bus_type AS bus_type,
36
+ b.zone AS zone, b.in_service AS in_service, b.is_slack AS is_slack,
37
+ b.base_in_service AS base_in_service, b.geom AS geom
38
+ ORDER BY b.bus_id
39
+ """
40
+
41
+ UPDATE_BUS = """
42
+ MATCH (b:Bus {bus_id: $bus_id})
43
+ SET b.name = coalesce($name, b.name),
44
+ b.base_kv = coalesce($base_kv, b.base_kv),
45
+ b.bus_type = coalesce($bus_type, b.bus_type),
46
+ b.zone = coalesce($zone, b.zone),
47
+ b.in_service = coalesce($in_service, b.in_service),
48
+ b.is_slack = coalesce($is_slack, b.is_slack),
49
+ b.base_in_service = coalesce($base_in_service, b.base_in_service),
50
+ b.geom = coalesce($geom, b.geom)
51
+ """
52
+
53
+ DELETE_BUS = "MATCH (b:Bus {bus_id: $bus_id}) DETACH DELETE b"
54
+
55
+ CREATE_GENERATOR = """
56
+ CREATE (g:Generator {
57
+ gen_id: $gen_id,
58
+ fuel_type: $fuel_type,
59
+ control_mode: $control_mode,
60
+ pmax_mw: $pmax_mw,
61
+ pmin_mw: $pmin_mw,
62
+ base_p_mw: $base_p_mw,
63
+ q_mvar: $q_mvar,
64
+ vm_pu: $vm_pu,
65
+ base_in_service: $base_in_service
66
+ })
67
+ WITH g
68
+ MATCH (b:Bus {bus_id: $bus_id})
69
+ CREATE (g)-[:GENERATOR_AT_BUS]->(b)
70
+ """
71
+
72
+ GET_GENERATOR = """
73
+ MATCH (g:Generator {gen_id: $gen_id})
74
+ OPTIONAL MATCH (g)-[:GENERATOR_AT_BUS]->(b:Bus)
75
+ RETURN g.gen_id AS gen_id, b.bus_id AS bus_id, g.fuel_type AS fuel_type,
76
+ g.control_mode AS control_mode, g.pmax_mw AS pmax_mw, g.pmin_mw AS pmin_mw,
77
+ g.base_p_mw AS base_p_mw, g.q_mvar AS q_mvar, g.vm_pu AS vm_pu,
78
+ g.base_in_service AS base_in_service
79
+ """
80
+
81
+ LIST_GENERATOR = """
82
+ MATCH (g:Generator)
83
+ OPTIONAL MATCH (g)-[:GENERATOR_AT_BUS]->(b:Bus)
84
+ RETURN g.gen_id AS gen_id, b.bus_id AS bus_id, g.fuel_type AS fuel_type,
85
+ g.control_mode AS control_mode, g.pmax_mw AS pmax_mw, g.pmin_mw AS pmin_mw,
86
+ g.base_p_mw AS base_p_mw, g.q_mvar AS q_mvar, g.vm_pu AS vm_pu,
87
+ g.base_in_service AS base_in_service
88
+ ORDER BY g.gen_id
89
+ """
90
+
91
+ UPDATE_GENERATOR = """
92
+ MATCH (g:Generator {gen_id: $gen_id})
93
+ SET g.fuel_type = coalesce($fuel_type, g.fuel_type),
94
+ g.control_mode = coalesce($control_mode, g.control_mode),
95
+ g.pmax_mw = coalesce($pmax_mw, g.pmax_mw),
96
+ g.pmin_mw = coalesce($pmin_mw, g.pmin_mw),
97
+ g.base_p_mw = coalesce($base_p_mw, g.base_p_mw),
98
+ g.q_mvar = coalesce($q_mvar, g.q_mvar),
99
+ g.vm_pu = coalesce($vm_pu, g.vm_pu),
100
+ g.base_in_service = coalesce($base_in_service, g.base_in_service)
101
+ """
102
+
103
+ DELETE_GENERATOR = "MATCH (g:Generator {gen_id: $gen_id}) DETACH DELETE g"
104
+
105
+ CREATE_LOAD = """
106
+ CREATE (l:Load {
107
+ load_id: $load_id,
108
+ load_zone: $load_zone,
109
+ base_p_mw: $base_p_mw,
110
+ base_q_mvar: $base_q_mvar,
111
+ scaling: $scaling,
112
+ base_in_service: $base_in_service
113
+ })
114
+ WITH l
115
+ MATCH (b:Bus {bus_id: $bus_id})
116
+ CREATE (l)-[:LOAD_AT_BUS]->(b)
117
+ """
118
+
119
+ GET_LOAD = """
120
+ MATCH (l:Load {load_id: $load_id})
121
+ OPTIONAL MATCH (l)-[:LOAD_AT_BUS]->(b:Bus)
122
+ RETURN l.load_id AS load_id, b.bus_id AS bus_id, l.load_zone AS load_zone,
123
+ l.base_p_mw AS base_p_mw, l.base_q_mvar AS base_q_mvar, l.scaling AS scaling,
124
+ l.base_in_service AS base_in_service
125
+ """
126
+
127
+ LIST_LOAD = """
128
+ MATCH (l:Load)
129
+ OPTIONAL MATCH (l)-[:LOAD_AT_BUS]->(b:Bus)
130
+ RETURN l.load_id AS load_id, b.bus_id AS bus_id, l.load_zone AS load_zone,
131
+ l.base_p_mw AS base_p_mw, l.base_q_mvar AS base_q_mvar, l.scaling AS scaling,
132
+ l.base_in_service AS base_in_service
133
+ ORDER BY l.load_id
134
+ """
135
+
136
+ UPDATE_LOAD = """
137
+ MATCH (l:Load {load_id: $load_id})
138
+ SET l.load_zone = coalesce($load_zone, l.load_zone),
139
+ l.base_p_mw = coalesce($base_p_mw, l.base_p_mw),
140
+ l.base_q_mvar = coalesce($base_q_mvar, l.base_q_mvar),
141
+ l.scaling = coalesce($scaling, l.scaling),
142
+ l.base_in_service = coalesce($base_in_service, l.base_in_service)
143
+ """
144
+
145
+ DELETE_LOAD = "MATCH (l:Load {load_id: $load_id}) DETACH DELETE l"
146
+
147
+ CREATE_BRANCH = """
148
+ MATCH (a:Bus {bus_id: $from_bus_id}), (b:Bus {bus_id: $to_bus_id})
149
+ CREATE (a)-[e:CONNECTED_TO {
150
+ branch_id: $branch_id,
151
+ element_kind: $element_kind,
152
+ r_pu: $r_pu,
153
+ x_pu: $x_pu,
154
+ b_pu: $b_pu,
155
+ c_nf_per_km: $c_nf_per_km,
156
+ g_us_per_km: $g_us_per_km,
157
+ rate_a_mva: $rate_a_mva,
158
+ base_rate_a_mva: $base_rate_a_mva,
159
+ length_km: $length_km,
160
+ tap_ratio: $tap_ratio,
161
+ phase_shift_deg: $phase_shift_deg,
162
+ pfe_kw: $pfe_kw,
163
+ i0_percent: $i0_percent,
164
+ status: $status,
165
+ base_in_service: $base_in_service,
166
+ parallel: $parallel
167
+ }]->(b)
168
+ """
169
+
170
+ GET_BRANCH = """
171
+ MATCH (a:Bus)-[e:CONNECTED_TO {branch_id: $branch_id}]->(b:Bus)
172
+ RETURN e.branch_id AS branch_id, a.bus_id AS from_bus_id, b.bus_id AS to_bus_id,
173
+ e.element_kind AS element_kind, e.r_pu AS r_pu, e.x_pu AS x_pu, e.b_pu AS b_pu,
174
+ e.rate_a_mva AS rate_a_mva, e.base_rate_a_mva AS base_rate_a_mva,
175
+ e.length_km AS length_km, e.status AS status, e.base_in_service AS base_in_service,
176
+ e.parallel AS parallel
177
+ """
178
+
179
+ LIST_BRANCH = """
180
+ MATCH (a:Bus)-[e:CONNECTED_TO]->(b:Bus)
181
+ RETURN e.branch_id AS branch_id, a.bus_id AS from_bus_id, b.bus_id AS to_bus_id,
182
+ e.element_kind AS element_kind, e.r_pu AS r_pu, e.x_pu AS x_pu, e.b_pu AS b_pu,
183
+ e.rate_a_mva AS rate_a_mva, e.base_rate_a_mva AS base_rate_a_mva,
184
+ e.length_km AS length_km, e.status AS status, e.base_in_service AS base_in_service,
185
+ e.parallel AS parallel
186
+ ORDER BY e.branch_id
187
+ """
188
+
189
+ UPDATE_BRANCH = """
190
+ MATCH ()-[e:CONNECTED_TO {branch_id: $branch_id}]->()
191
+ SET e.element_kind = coalesce($element_kind, e.element_kind),
192
+ e.r_pu = coalesce($r_pu, e.r_pu),
193
+ e.x_pu = coalesce($x_pu, e.x_pu),
194
+ e.b_pu = coalesce($b_pu, e.b_pu),
195
+ e.rate_a_mva = coalesce($rate_a_mva, e.rate_a_mva),
196
+ e.base_rate_a_mva = coalesce($base_rate_a_mva, e.base_rate_a_mva),
197
+ e.length_km = coalesce($length_km, e.length_km),
198
+ e.status = coalesce($status, e.status),
199
+ e.base_in_service = coalesce($base_in_service, e.base_in_service),
200
+ e.parallel = coalesce($parallel, e.parallel)
201
+ """
202
+
203
+ DELETE_BRANCH = "MATCH ()-[e:CONNECTED_TO {branch_id: $branch_id}]->() DELETE e"
204
+
205
+ CREATE_BOUNDARY = """
206
+ CREATE (bi:BoundaryInjection {
207
+ boundary_id: $boundary_id,
208
+ p_mw: $p_mw,
209
+ q_mvar: $q_mvar,
210
+ source: $source
211
+ })
212
+ WITH bi
213
+ MATCH (b:Bus {bus_id: $bus_id})
214
+ CREATE (bi)-[:BOUNDARY_AT_BUS]->(b)
215
+ """
216
+
217
+ GET_BOUNDARY = """
218
+ MATCH (bi:BoundaryInjection {boundary_id: $boundary_id})
219
+ OPTIONAL MATCH (bi)-[:BOUNDARY_AT_BUS]->(b:Bus)
220
+ RETURN bi.boundary_id AS boundary_id, b.bus_id AS bus_id, bi.p_mw AS p_mw,
221
+ bi.q_mvar AS q_mvar, bi.source AS source
222
+ """
223
+
224
+ LIST_BOUNDARY = """
225
+ MATCH (bi:BoundaryInjection)
226
+ OPTIONAL MATCH (bi)-[:BOUNDARY_AT_BUS]->(b:Bus)
227
+ RETURN bi.boundary_id AS boundary_id, b.bus_id AS bus_id, bi.p_mw AS p_mw,
228
+ bi.q_mvar AS q_mvar, bi.source AS source
229
+ ORDER BY bi.boundary_id
230
+ """
231
+
232
+ UPDATE_BOUNDARY = """
233
+ MATCH (bi:BoundaryInjection {boundary_id: $boundary_id})
234
+ SET bi.p_mw = coalesce($p_mw, bi.p_mw),
235
+ bi.q_mvar = coalesce($q_mvar, bi.q_mvar),
236
+ bi.source = coalesce($source, bi.source)
237
+ """
238
+
239
+ DELETE_BOUNDARY = "MATCH (bi:BoundaryInjection {boundary_id: $boundary_id}) DETACH DELETE bi"
240
+
241
+
242
+ def create_bus(conn, **params: Any) -> None:
243
+ defaults = {
244
+ "name": params.get("bus_id"),
245
+ "bus_type": "b",
246
+ "zone": "",
247
+ "in_service": True,
248
+ "is_slack": False,
249
+ "base_in_service": True,
250
+ "geom": None,
251
+ "base_kv": 0.0,
252
+ }
253
+ defaults.update({k: v for k, v in params.items() if v is not None})
254
+ execute(conn, CREATE_BUS, defaults)
255
+
256
+
257
+ def get_bus(conn, bus_id: str) -> dict[str, Any] | None:
258
+ rows = query_rows(conn, GET_BUS, {"bus_id": bus_id})
259
+ return rows[0] if rows else None
260
+
261
+
262
+ def list_buses(conn) -> list[dict[str, Any]]:
263
+ return query_rows(conn, LIST_BUS)
264
+
265
+
266
+ def update_bus(conn, bus_id: str, **params: Any) -> None:
267
+ payload = {"bus_id": bus_id, "name": None, "base_kv": None, "bus_type": None, "zone": None,
268
+ "in_service": None, "is_slack": None, "base_in_service": None, "geom": None}
269
+ payload.update(params)
270
+ execute(conn, UPDATE_BUS, payload)
271
+
272
+
273
+ def delete_bus(conn, bus_id: str) -> None:
274
+ execute(conn, DELETE_BUS, {"bus_id": bus_id})
275
+
276
+
277
+ def create_generator(conn, **params: Any) -> None:
278
+ defaults = {
279
+ "fuel_type": "",
280
+ "control_mode": "PV",
281
+ "pmax_mw": 0.0,
282
+ "pmin_mw": 0.0,
283
+ "base_p_mw": 0.0,
284
+ "q_mvar": 0.0,
285
+ "vm_pu": 1.0,
286
+ "base_in_service": True,
287
+ }
288
+ defaults.update({k: v for k, v in params.items() if v is not None})
289
+ execute(conn, CREATE_GENERATOR, defaults)
290
+
291
+
292
+ def get_generator(conn, gen_id: str) -> dict[str, Any] | None:
293
+ rows = query_rows(conn, GET_GENERATOR, {"gen_id": gen_id})
294
+ return rows[0] if rows else None
295
+
296
+
297
+ def list_generators(conn) -> list[dict[str, Any]]:
298
+ return query_rows(conn, LIST_GENERATOR)
299
+
300
+
301
+ def update_generator(conn, gen_id: str, **params: Any) -> None:
302
+ payload = {"gen_id": gen_id, "fuel_type": None, "control_mode": None, "pmax_mw": None,
303
+ "pmin_mw": None, "base_p_mw": None, "q_mvar": None, "vm_pu": None,
304
+ "base_in_service": None}
305
+ payload.update(params)
306
+ execute(conn, UPDATE_GENERATOR, payload)
307
+
308
+
309
+ def delete_generator(conn, gen_id: str) -> None:
310
+ execute(conn, DELETE_GENERATOR, {"gen_id": gen_id})
311
+
312
+
313
+ def create_load(conn, **params: Any) -> None:
314
+ defaults = {
315
+ "load_zone": "",
316
+ "base_p_mw": 0.0,
317
+ "base_q_mvar": 0.0,
318
+ "scaling": 1.0,
319
+ "base_in_service": True,
320
+ }
321
+ defaults.update({k: v for k, v in params.items() if v is not None})
322
+ execute(conn, CREATE_LOAD, defaults)
323
+
324
+
325
+ def get_load(conn, load_id: str) -> dict[str, Any] | None:
326
+ rows = query_rows(conn, GET_LOAD, {"load_id": load_id})
327
+ return rows[0] if rows else None
328
+
329
+
330
+ def list_loads(conn) -> list[dict[str, Any]]:
331
+ return query_rows(conn, LIST_LOAD)
332
+
333
+
334
+ def update_load(conn, load_id: str, **params: Any) -> None:
335
+ payload = {"load_id": load_id, "load_zone": None, "base_p_mw": None, "base_q_mvar": None,
336
+ "scaling": None, "base_in_service": None}
337
+ payload.update(params)
338
+ execute(conn, UPDATE_LOAD, payload)
339
+
340
+
341
+ def delete_load(conn, load_id: str) -> None:
342
+ execute(conn, DELETE_LOAD, {"load_id": load_id})
343
+
344
+
345
+ def create_branch(conn, **params: Any) -> None:
346
+ defaults = {
347
+ "element_kind": "line",
348
+ "r_pu": 0.0,
349
+ "x_pu": 0.01,
350
+ "b_pu": 0.0,
351
+ "c_nf_per_km": 0.0,
352
+ "g_us_per_km": 0.0,
353
+ "rate_a_mva": None,
354
+ "base_rate_a_mva": None,
355
+ "length_km": 1.0,
356
+ "tap_ratio": 1.0,
357
+ "phase_shift_deg": 0.0,
358
+ "pfe_kw": 0.0,
359
+ "i0_percent": 0.0,
360
+ "status": True,
361
+ "base_in_service": True,
362
+ "parallel": 1,
363
+ }
364
+ defaults.update({k: v for k, v in params.items() if v is not None})
365
+ execute(conn, CREATE_BRANCH, defaults)
366
+
367
+
368
+ def get_branch(conn, branch_id: str) -> dict[str, Any] | None:
369
+ rows = query_rows(conn, GET_BRANCH, {"branch_id": branch_id})
370
+ return rows[0] if rows else None
371
+
372
+
373
+ def list_branches(conn) -> list[dict[str, Any]]:
374
+ return query_rows(conn, LIST_BRANCH)
375
+
376
+
377
+ def update_branch(conn, branch_id: str, **params: Any) -> None:
378
+ payload = {
379
+ "branch_id": branch_id, "element_kind": None, "r_pu": None, "x_pu": None, "b_pu": None,
380
+ "rate_a_mva": None, "base_rate_a_mva": None, "length_km": None, "status": None,
381
+ "base_in_service": None, "parallel": None,
382
+ }
383
+ payload.update(params)
384
+ execute(conn, UPDATE_BRANCH, payload)
385
+
386
+
387
+ def delete_branch(conn, branch_id: str) -> None:
388
+ execute(conn, DELETE_BRANCH, {"branch_id": branch_id})
389
+
390
+
391
+ def create_boundary(conn, **params: Any) -> None:
392
+ defaults = {"p_mw": 0.0, "q_mvar": 0.0, "source": "equivalent"}
393
+ defaults.update({k: v for k, v in params.items() if v is not None})
394
+ execute(conn, CREATE_BOUNDARY, defaults)
395
+
396
+
397
+ def get_boundary(conn, boundary_id: str) -> dict[str, Any] | None:
398
+ rows = query_rows(conn, GET_BOUNDARY, {"boundary_id": boundary_id})
399
+ return rows[0] if rows else None
400
+
401
+
402
+ def list_boundaries(conn) -> list[dict[str, Any]]:
403
+ return query_rows(conn, LIST_BOUNDARY)
404
+
405
+
406
+ def update_boundary(conn, boundary_id: str, **params: Any) -> None:
407
+ payload = {"boundary_id": boundary_id, "p_mw": None, "q_mvar": None, "source": None}
408
+ payload.update(params)
409
+ execute(conn, UPDATE_BOUNDARY, payload)
410
+
411
+
412
+ def delete_boundary(conn, boundary_id: str) -> None:
413
+ execute(conn, DELETE_BOUNDARY, {"boundary_id": boundary_id})