Skip to content

Commit a0db765

Browse files
authored
Merge pull request #9 from DAI-Lab/node_naming
Add node naming functionality
2 parents 2d6c914 + c180b8b commit a0db765

File tree

4 files changed

+100
-12
lines changed

4 files changed

+100
-12
lines changed

pygridsim/core.py

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from altdss import altdss
33

44
from pygridsim.configs import NAME_TO_CONFIG
5+
from pygridsim.defaults import RESERVED_PREFIXES
56
from pygridsim.lines import _make_line
67
from pygridsim.parameters import _make_generator, _make_load_node, _make_pv, _make_source_node
78
from pygridsim.results import _export_results, _query_solution
@@ -17,23 +18,34 @@ def __init__(self):
1718
Stores numbers of circuit components to ensure unique naming of repeat circuit components.
1819
1920
Attributes:
20-
num_generators (int): Number of generators created so far.
21-
num_lines (int): Number of lines created so far.
22-
num_loads (int): Number of load nodes created so far.
23-
num_pv (int): Number of PVSystems create so far.
21+
num_loads (int): Number of loads in circuit so far.
22+
num_lines (int): Number of lines in circuit so far.
23+
num_transformers (int): Number of transformers in circuit so far.
24+
num_pv (int): Number of PV systems in circuit so far.
25+
num_generators (int): Number generators in circuit so far.
26+
nickname_to_name (dict[str, str]): Map from nicknames to their internal names.
2427
"""
2528
self.num_generators = 0
2629
self.num_lines = 0
2730
self.num_loads = 0
2831
self.num_pv = 0
32+
self.nickname_to_name = {}
2933

3034
altdss.ClearAll()
3135
altdss('new circuit.MyCircuit')
3236

37+
def _check_naming(self, name):
38+
if name in self.nickname_to_name:
39+
raise ValueError("Provided name already assigned to a node")
40+
if any(name.startswith(prefix) for prefix in RESERVED_PREFIXES):
41+
raise ValueError(
42+
"Cannot name nodes of the format 'component + __', ambiguity with internal names")
43+
3344
def add_load_nodes(self,
3445
load_type: str = "house",
3546
params: dict[str, int] = None,
36-
num: int = 1):
47+
num: int = 1,
48+
names: list[str] = None):
3749
"""Adds Load Node(s) to circuit.
3850
3951
Allows the user to add num load nodes,
@@ -47,15 +59,26 @@ def add_load_nodes(self,
4759
Load parameters for these manual additions. Defaults to empty dictionary.
4860
num (int, optional):
4961
The number of loads to create with these parameters. Defaults to 1.
62+
names (list(str), optional):
63+
Up to num names to assign as shortcuts to the loads
5064
5165
Returns:
5266
list[OpenDSS object]:
5367
A list of OpenDSS objects representing the load nodes created.
5468
"""
5569

5670
params = params or dict()
71+
names = names or list()
72+
if len(names) > num:
73+
raise ValueError("Specified more names of loads than number of nodes")
74+
5775
load_nodes = []
58-
for _ in range(num):
76+
for i in range(num):
77+
if (len(names) > i):
78+
self._check_naming(names[i])
79+
internal_name = "load" + str(self.num_loads)
80+
self.nickname_to_name[names[i]] = internal_name
81+
5982
_make_load_node(params, load_type, self.num_loads)
6083
self.num_loads += 1
6184

@@ -107,12 +130,19 @@ def add_PVSystems(self, load_nodes: list[str],
107130

108131
PV_nodes = []
109132
for load in load_nodes:
133+
if (load in self.nickname_to_name):
134+
load = self.nickname_to_name[load]
135+
110136
PV_nodes.append(_make_pv(load, params, num_panels, self.num_pv))
111137
self.num_pv += 1
112138

113139
return PV_nodes
114140

115-
def add_generators(self, num: int = 1, gen_type: str = "small", params: dict[str, int] = None):
141+
def add_generators(self,
142+
num: int = 1,
143+
gen_type: str = "small",
144+
params: dict[str, int] = None,
145+
names: list[str] = None):
116146
"""Adds generator(s) to the system.
117147
118148
Args:
@@ -122,14 +152,25 @@ def add_generators(self, num: int = 1, gen_type: str = "small", params: dict[str
122152
The type of generator (one of "small", "large", "industrial"). Defaults to "small".
123153
params (dict[str, int], optional):
124154
A dictionary of parameters to configure the generator. Defaults to None.
155+
names (list(str), optional):
156+
Up to num names to assign as shortcuts to the generators
125157
126158
Returns:
127159
list[DSS objects]:
128160
A list of OpenDSS objects representing the generators created.
129161
"""
130162
params = params or dict()
163+
names = names or list()
164+
if len(names) > num:
165+
raise ValueError("Specified more names of generators than number of nodes")
166+
131167
generators = []
132-
for _ in range(num):
168+
for i in range(num):
169+
if (len(names) > i):
170+
self._check_naming(names[i])
171+
internal_name = "generator" + str(self.num_generators)
172+
self.nickname_to_name[names[i]] = internal_name
173+
133174
generators.append(_make_generator(params, gen_type, count=self.num_generators))
134175
self.num_generators += 1
135176

@@ -160,6 +201,13 @@ def add_lines(self,
160201
"""
161202
params = params or dict()
162203
for src, dst in connections:
204+
if (src in self.nickname_to_name):
205+
src = self.nickname_to_name[src]
206+
if (dst in self.nickname_to_name):
207+
dst = self.nickname_to_name[dst]
208+
if (src == dst):
209+
raise ValueError("Tried to make a line between equivalent src and dst")
210+
163211
_make_line(src, dst, line_type, self.num_lines, params, transformer)
164212
self.num_lines += 1
165213

@@ -173,6 +221,9 @@ def solve(self):
173221
"""
174222
altdss.Solution.Solve()
175223

224+
def _get_name_to_nickname(self):
225+
return {v: k for k, v in self.nickname_to_name.items()}
226+
176227
def results(self, queries: list[str], export_path=""):
177228
"""Gets simulation results based on specified queries.
178229
@@ -193,7 +244,7 @@ def results(self, queries: list[str], export_path=""):
193244
"""
194245
results = {}
195246
for query in queries:
196-
results[query] = _query_solution(query)
247+
results[query] = _query_solution(query, self._get_name_to_nickname())
197248

198249
if (export_path):
199250
_export_results(results, export_path)

pygridsim/defaults.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,5 @@
7575
VALID_LINE_TRANSFORMER_PARAMS = ["length", "XHL", "Conns"]
7676
VALID_PV_PARAMS = ["kV", "phases"]
7777
VALID_GENERATOR_PARAMS = ["kV", "kW", "phases"]
78+
79+
RESERVED_PREFIXES = ["load", "generator", "pv", "source"]

pygridsim/results.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
from altdss import altdss
88

99

10-
def _query_solution(query):
10+
def _query_solution(query, name_to_nickname):
1111
query_fix = query.lower().replace(" ", "")
1212
vector_losses = altdss.Losses()
1313
vector_power = altdss.TotalPower()
1414
match query_fix:
1515
case "voltages":
1616
bus_vmags = {}
1717
for bus_name, bus_vmag in zip(altdss.BusNames(), altdss.BusVMag()):
18-
bus_vmags[bus_name] = float(bus_vmag)
18+
return_name = bus_name
19+
if bus_name in name_to_nickname:
20+
nickname = name_to_nickname[bus_name]
21+
return_name += "/" + nickname
22+
bus_vmags[return_name] = float(bus_vmag)
1923
return bus_vmags
2024
case "losses" | "loss":
2125
losses = {}

tests/test_circuit.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,38 @@ def test_011_configs(self):
162162
with self.assertRaises(Exception):
163163
circuit.update_source(source_type=SourceType.TURBINE)
164164

165-
def test_012_all_results(self):
165+
def test_012_node_naming(self):
166+
circuit = PyGridSim()
167+
circuit.update_source()
168+
# Create 4 nodes, only name 2 of them
169+
circuit.add_load_nodes(num=4, load_type="house", names=["0", "1"])
170+
circuit.add_generators(num=2, gen_type="small", names=["G0", "G1"])
171+
172+
# Add PVSystems to one of the nodes with shortcut
173+
circuit.add_PVSystems(load_nodes=["load2", "0"])
174+
# Can use original or abbreviated name
175+
circuit.add_lines(connections=[("G0", "0"), ("generator1", "load1")])
176+
177+
with self.assertRaises(ValueError):
178+
# Tries to assign an already assigned name
179+
circuit.add_load_nodes(num=1, names=["G1"])
180+
181+
with self.assertRaises(ValueError):
182+
# Tries to assign name to internal name load1, errors because adds ambiguity
183+
circuit.add_load_nodes(num=1, names=["load1"])
184+
185+
with self.assertRaises(ValueError):
186+
# Attempt to name 2 load nodes, but only initiating 1
187+
circuit.add_load_nodes(names=["640", "641"])
188+
189+
with self.assertRaises(ValueError):
190+
# Trying to create a line between a node and itself (nickname)
191+
circuit.add_lines(connections=[("load0", "0")])
192+
193+
circuit.solve()
194+
print(circuit.results(["Voltages", "Losses"]))
195+
196+
def test_013_all_results(self):
166197
circuit = PyGridSim()
167198
circuit.update_source()
168199
circuit.add_load_nodes()

0 commit comments

Comments
 (0)