From 2a904aaad84dc0207573a017dad9039a3bad9513 Mon Sep 17 00:00:00 2001 From: Angela Zhao Date: Fri, 11 Apr 2025 13:05:57 -0400 Subject: [PATCH 1/6] naming nodes --- pygridsim/core.py | 52 +++++++++++++++++++++++++++++++++++++++---- tests/test_circuit.py | 31 ++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/pygridsim/core.py b/pygridsim/core.py index 780d393..e794314 100644 --- a/pygridsim/core.py +++ b/pygridsim/core.py @@ -27,13 +27,23 @@ def __init__(self): self.num_transformers = 0 self.num_pv = 0 self.num_generators = 0 + self.nickname_to_name = {} altdss.ClearAll() altdss('new circuit.MyCircuit') + def _check_naming(self, name): + if name in self.nickname_to_name: + raise NameError("Provided name already assigned to a node") + if name.startswith("load") or name.startswith( + "generator") or name == "source" or name.startswith("pv"): + raise NameError( + "Cannot name nodes of the format 'component + __', ambiguity with internal names") + def add_load_nodes(self, load_type: str = "house", params: dict[str, int] = None, - num: int = 1): + num: int = 1, + names: list[str] = None): """Adds Load Node(s) to circuit. Allows the user to add num load nodes, @@ -47,14 +57,24 @@ def add_load_nodes(self, Load parameters for these manual additions. Defaults to empty dictionary. num (int, optional): The number of loads to create with these parameters. Defaults to 1. + names (list(str), optional): + Up to num names to assign as shortcuts to the loads Returns: list[OpenDSS object]: A list of OpenDSS objects representing the load nodes created. """ params = params or dict() + names = names or list() + if len(names) > num: + raise ValueError("Specified more names of loads than number of nodes") + load_nodes = [] - for _ in range(num): + for i in range(num): + if (len(names) > i): + self._check_naming(names[i]) + self.nickname_to_name[names[i]] = "load" + str(self.num_loads) + _make_load_node(params, load_type, self.num_loads) self.num_loads += 1 @@ -106,12 +126,19 @@ def add_PVSystems(self, load_nodes: list[str], PV_nodes = [] for load in load_nodes: + if (load in self.nickname_to_name): + self.nickname_to_name[load] + PV_nodes.append(_make_pv(load, params, num_panels, self.num_pv)) self.num_pv += 1 return PV_nodes - def add_generators(self, num: int = 1, gen_type: str = "small", params: dict[str, int] = None): + def add_generators(self, + num: int = 1, + gen_type: str = "small", + params: dict[str, int] = None, + names: list[str] = None): """Adds generator(s) to the system. Args: @@ -121,14 +148,24 @@ def add_generators(self, num: int = 1, gen_type: str = "small", params: dict[str The type of generator (one of "small", "large", "industrial"). Defaults to "small". params (dict[str, int], optional): A dictionary of parameters to configure the generator. Defaults to None. + names (list(str), optional): + Up to num names to assign as shortcuts to the generators Returns: list[DSS objects]: A list of OpenDSS objects representing the generators created. """ params = params or dict() + names = names or list() + if len(names) > num: + raise ValueError("Specified more names of generators than number of nodes") + generators = [] - for _ in range(num): + for i in range(num): + if (len(names) > i): + self._check_naming(names[i]) + self.nickname_to_name[names[i]] = "generator" + str(self.num_generators) + generators.append(_make_generator(params, gen_type, count=self.num_generators)) self.num_generators += 1 @@ -159,6 +196,13 @@ def add_lines(self, """ params = params or dict() for src, dst in connections: + if (src in self.nickname_to_name): + src = self.nickname_to_name[src] + if (dst in self.nickname_to_name): + dst = self.nickname_to_name[dst] + if (src == dst): + raise ValueError("Tried to make a line between equivalent src and dst") + _make_line(src, dst, line_type, self.num_lines, params, transformer) self.num_lines += 1 diff --git a/tests/test_circuit.py b/tests/test_circuit.py index f67c00a..aa8ef1a 100644 --- a/tests/test_circuit.py +++ b/tests/test_circuit.py @@ -162,6 +162,37 @@ def test_011_configs(self): with self.assertRaises(Exception): circuit.update_source(source_type=SourceType.TURBINE) + def test_012_node_naming(self): + circuit = PyGridSim() + circuit.update_source() + # Create 4 nodes, only name 2 of them + circuit.add_load_nodes(num=4, load_type="house", names=["0", "1"]) + circuit.add_generators(num=2, gen_type="small", names=["G0", "G1"]) + + # Add PVSystems to one of the nodes with shortcut + circuit.add_PVSystems(load_nodes=["load2", "0"]) + # Can use original or abbreviated name + circuit.add_lines(connections=[("G0", "0"), ("generator1", "load1")]) + + with self.assertRaises(NameError): + # Tries to assign an already assigned name + circuit.add_load_nodes(num=1, names=["G1"]) + + with self.assertRaises(NameError): + # Tries to assign name to internal name load1, errors because adds ambiguity + circuit.add_load_nodes(num=1, names=["load1"]) + + with self.assertRaises(ValueError): + # Attempt to name 2 load nodes, but only initiating 1 + circuit.add_load_nodes(names=["640", "641"]) + + with self.assertRaises(ValueError): + # Trying to create a line between a node and itself (nickname) + circuit.add_lines(connections=[("load0", "0")]) + + circuit.solve() + print(circuit.results(["Voltages", "Losses"])) + class TestCustomizedCircuit(unittest.TestCase): """ From e733173cb0104566c9c797ae3ec6a7746bf56426 Mon Sep 17 00:00:00 2001 From: Angela Zhao Date: Wed, 7 May 2025 15:50:25 -0400 Subject: [PATCH 2/6] add prefixes to avoid hard coding, error naming changes, fixed bug in add_pvsystem --- pygridsim/core.py | 11 ++++++----- pygridsim/defaults.py | 2 ++ tests/test_circuit.py | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pygridsim/core.py b/pygridsim/core.py index e794314..c6e674d 100644 --- a/pygridsim/core.py +++ b/pygridsim/core.py @@ -4,6 +4,7 @@ from pygridsim.lines import _make_line from pygridsim.parameters import _make_generator, _make_load_node, _make_pv, _make_source_node from pygridsim.results import _export_results, _query_solution +from pygridsim.defaults import RESERVED_PREFIXES """Main module.""" @@ -21,6 +22,7 @@ def __init__(self): num_transformers (int): Number of transformers in circuit so far. num_pv (int): Number of PV systems in circuit so far. num_generators (int): Number generators in circuit so far. + nickname_to_name (dict[str, str]): Map containing nicknames to their internal names. """ self.num_loads = 0 self.num_lines = 0 @@ -33,10 +35,9 @@ def __init__(self): def _check_naming(self, name): if name in self.nickname_to_name: - raise NameError("Provided name already assigned to a node") - if name.startswith("load") or name.startswith( - "generator") or name == "source" or name.startswith("pv"): - raise NameError( + raise ValueError("Provided name already assigned to a node") + if any(name.startswith(prefix) for prefix in RESERVED_PREFIXES): + raise ValueError( "Cannot name nodes of the format 'component + __', ambiguity with internal names") def add_load_nodes(self, @@ -127,7 +128,7 @@ def add_PVSystems(self, load_nodes: list[str], PV_nodes = [] for load in load_nodes: if (load in self.nickname_to_name): - self.nickname_to_name[load] + load = self.nickname_to_name[load] PV_nodes.append(_make_pv(load, params, num_panels, self.num_pv)) self.num_pv += 1 diff --git a/pygridsim/defaults.py b/pygridsim/defaults.py index 9af1219..9e7cfcf 100644 --- a/pygridsim/defaults.py +++ b/pygridsim/defaults.py @@ -75,3 +75,5 @@ VALID_LINE_TRANSFORMER_PARAMS = ["length", "XHL", "Conns"] VALID_PV_PARAMS = ["kV", "phases"] VALID_GENERATOR_PARAMS = ["kV", "kW", "phases"] + +RESERVED_PREFIXES = ["load", "generator", "pv", "source"] \ No newline at end of file diff --git a/tests/test_circuit.py b/tests/test_circuit.py index aa8ef1a..274909a 100644 --- a/tests/test_circuit.py +++ b/tests/test_circuit.py @@ -174,11 +174,11 @@ def test_012_node_naming(self): # Can use original or abbreviated name circuit.add_lines(connections=[("G0", "0"), ("generator1", "load1")]) - with self.assertRaises(NameError): + with self.assertRaises(ValueError): # Tries to assign an already assigned name circuit.add_load_nodes(num=1, names=["G1"]) - with self.assertRaises(NameError): + with self.assertRaises(ValueError): # Tries to assign name to internal name load1, errors because adds ambiguity circuit.add_load_nodes(num=1, names=["load1"]) From 9ce8f7e343d0f1eff0b3327d88df381946316eeb Mon Sep 17 00:00:00 2001 From: Angela Zhao Date: Wed, 7 May 2025 15:57:09 -0400 Subject: [PATCH 3/6] lint --- pygridsim/core.py | 2 +- pygridsim/defaults.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pygridsim/core.py b/pygridsim/core.py index c6e674d..c530b15 100644 --- a/pygridsim/core.py +++ b/pygridsim/core.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- from altdss import altdss +from pygridsim.defaults import RESERVED_PREFIXES from pygridsim.lines import _make_line from pygridsim.parameters import _make_generator, _make_load_node, _make_pv, _make_source_node from pygridsim.results import _export_results, _query_solution -from pygridsim.defaults import RESERVED_PREFIXES """Main module.""" diff --git a/pygridsim/defaults.py b/pygridsim/defaults.py index 9e7cfcf..4e9a501 100644 --- a/pygridsim/defaults.py +++ b/pygridsim/defaults.py @@ -76,4 +76,4 @@ VALID_PV_PARAMS = ["kV", "phases"] VALID_GENERATOR_PARAMS = ["kV", "kW", "phases"] -RESERVED_PREFIXES = ["load", "generator", "pv", "source"] \ No newline at end of file +RESERVED_PREFIXES = ["load", "generator", "pv", "source"] From 22e5cc19278ecf1189563b73b4eb4dc6baeff1e3 Mon Sep 17 00:00:00 2001 From: Angela Zhao Date: Thu, 8 May 2025 11:10:45 -0400 Subject: [PATCH 4/6] change results and internally add name_to_nickname --- pygridsim/core.py | 14 ++++++++++---- pygridsim/results.py | 8 ++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/pygridsim/core.py b/pygridsim/core.py index c530b15..0b5d17a 100644 --- a/pygridsim/core.py +++ b/pygridsim/core.py @@ -22,7 +22,8 @@ def __init__(self): num_transformers (int): Number of transformers in circuit so far. num_pv (int): Number of PV systems in circuit so far. num_generators (int): Number generators in circuit so far. - nickname_to_name (dict[str, str]): Map containing nicknames to their internal names. + nickname_to_name (dict[str, str]): Map from nicknames to their internal names. + name_to_nickname (dict[str, str]): Map from internal names to nicknames. """ self.num_loads = 0 self.num_lines = 0 @@ -30,6 +31,7 @@ def __init__(self): self.num_pv = 0 self.num_generators = 0 self.nickname_to_name = {} + self.name_to_nickname = {} altdss.ClearAll() altdss('new circuit.MyCircuit') @@ -74,7 +76,9 @@ def add_load_nodes(self, for i in range(num): if (len(names) > i): self._check_naming(names[i]) - self.nickname_to_name[names[i]] = "load" + str(self.num_loads) + internal_name = "load" + str(self.num_loads) + self.nickname_to_name[names[i]] = internal_name + self.name_to_nickname[internal_name] = names[i] _make_load_node(params, load_type, self.num_loads) self.num_loads += 1 @@ -165,7 +169,9 @@ def add_generators(self, for i in range(num): if (len(names) > i): self._check_naming(names[i]) - self.nickname_to_name[names[i]] = "generator" + str(self.num_generators) + internal_name = "generator" + str(self.num_generators) + self.nickname_to_name[names[i]] = internal_name + self.name_to_nickname[internal_name] = names[i] generators.append(_make_generator(params, gen_type, count=self.num_generators)) self.num_generators += 1 @@ -235,7 +241,7 @@ def results(self, queries: list[str], export_path=""): """ results = {} for query in queries: - results[query] = _query_solution(query) + results[query] = _query_solution(query, self.name_to_nickname) if (export_path): _export_results(results, export_path) diff --git a/pygridsim/results.py b/pygridsim/results.py index a81d105..6bbd9fe 100644 --- a/pygridsim/results.py +++ b/pygridsim/results.py @@ -7,12 +7,16 @@ from altdss import altdss -def _query_solution(query): +def _query_solution(query, name_to_nickname): match query: case "Voltages": bus_vmags = {} for bus_name, bus_vmag in zip(altdss.BusNames(), altdss.BusVMag()): - bus_vmags[bus_name] = float(bus_vmag) + return_name = bus_name + if bus_name in name_to_nickname: + nickname = name_to_nickname[bus_name] + return_name += "/" + nickname + bus_vmags[return_name] = float(bus_vmag) return bus_vmags case "Losses": vector_losses = altdss.Losses() From 8594034dda3761cf4d9272dc3468c2356df8f2c4 Mon Sep 17 00:00:00 2001 From: Angela Zhao Date: Tue, 13 May 2025 10:14:54 -0400 Subject: [PATCH 5/6] name to nickname as helper not class var --- pygridsim/core.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pygridsim/core.py b/pygridsim/core.py index 0b5d17a..1608082 100644 --- a/pygridsim/core.py +++ b/pygridsim/core.py @@ -23,7 +23,6 @@ def __init__(self): num_pv (int): Number of PV systems in circuit so far. num_generators (int): Number generators in circuit so far. nickname_to_name (dict[str, str]): Map from nicknames to their internal names. - name_to_nickname (dict[str, str]): Map from internal names to nicknames. """ self.num_loads = 0 self.num_lines = 0 @@ -31,7 +30,6 @@ def __init__(self): self.num_pv = 0 self.num_generators = 0 self.nickname_to_name = {} - self.name_to_nickname = {} altdss.ClearAll() altdss('new circuit.MyCircuit') @@ -78,7 +76,6 @@ def add_load_nodes(self, self._check_naming(names[i]) internal_name = "load" + str(self.num_loads) self.nickname_to_name[names[i]] = internal_name - self.name_to_nickname[internal_name] = names[i] _make_load_node(params, load_type, self.num_loads) self.num_loads += 1 @@ -171,7 +168,6 @@ def add_generators(self, self._check_naming(names[i]) internal_name = "generator" + str(self.num_generators) self.nickname_to_name[names[i]] = internal_name - self.name_to_nickname[internal_name] = names[i] generators.append(_make_generator(params, gen_type, count=self.num_generators)) self.num_generators += 1 @@ -223,6 +219,9 @@ def solve(self): """ altdss.Solution.Solve() + def _get_name_to_nickname(self): + return {v: k for k, v in self.nickname_to_name.items()} + def results(self, queries: list[str], export_path=""): """Gets simulation results based on specified queries. @@ -241,7 +240,7 @@ def results(self, queries: list[str], export_path=""): """ results = {} for query in queries: - results[query] = _query_solution(query, self.name_to_nickname) + results[query] = _query_solution(query, self._get_name_to_nickname()) if (export_path): _export_results(results, export_path) From c180b8bf6a4a1d9ef23b262f845c7bc97c977a38 Mon Sep 17 00:00:00 2001 From: Angela Zhao Date: Tue, 20 May 2025 11:32:06 -0400 Subject: [PATCH 6/6] lint --- pygridsim/core.py | 2 +- pygridsim/results.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pygridsim/core.py b/pygridsim/core.py index 278b33d..1def45e 100644 --- a/pygridsim/core.py +++ b/pygridsim/core.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- from altdss import altdss -from pygridsim.defaults import RESERVED_PREFIXES from pygridsim.configs import NAME_TO_CONFIG +from pygridsim.defaults import RESERVED_PREFIXES from pygridsim.lines import _make_line from pygridsim.parameters import _make_generator, _make_load_node, _make_pv, _make_source_node from pygridsim.results import _export_results, _query_solution diff --git a/pygridsim/results.py b/pygridsim/results.py index 99eda1f..9e1334c 100644 --- a/pygridsim/results.py +++ b/pygridsim/results.py @@ -6,6 +6,7 @@ from altdss import altdss + def _query_solution(query, name_to_nickname): query_fix = query.lower().replace(" ", "") vector_losses = altdss.Losses()