|
1 | 1 | # -*- coding: utf-8 -*- |
2 | 2 | from altdss import altdss |
3 | | -from altdss import AltDSS, Transformer, Vsource, Load, LoadModel, LoadShape |
4 | | -from dss.enums import LineUnits, SolveModes |
5 | | -from pygridsim.parameters import make_load_node, make_source_node |
6 | | -from pygridsim.results import query_solution |
7 | | -from pygridsim.lines import make_line |
8 | | -from pygridsim.transformers import make_transformer |
9 | | -from pygridsim.enums import LineType, SourceType, LoadType |
| 3 | +from pygridsim.parameters import _make_load_node, _make_source_node, _make_generator, _make_pv |
| 4 | +from pygridsim.results import _query_solution, _export_results |
| 5 | +from pygridsim.lines import _make_line |
10 | 6 |
|
11 | 7 | """Main module.""" |
12 | 8 |
|
13 | 9 | class PyGridSim: |
14 | 10 | def __init__(self): |
15 | | - """ |
16 | | - Initialize OpenDSS/AltDSS engine. Creates an Empty Circuit |
| 11 | + """Initialize OpenDSS engine. |
| 12 | +
|
| 13 | + Instantiate an OpenDSS circuit that user can build circuit components on. Stores numbers of circuit components |
| 14 | + to ensure unique naming of repeat circuit components |
| 15 | + |
| 16 | + Attributes: |
| 17 | + num_loads (int): Number of loads in circuit so far. |
| 18 | + num_lines (int): Number of lines in circuit so far. |
| 19 | + num_transformers (int): Number of transformers in circuit so far. |
| 20 | + num_pv (int): Number of PV systems in circuit so far. |
| 21 | + num_generators (int): Number generators in circuit so far. |
17 | 22 | """ |
18 | 23 | self.num_loads = 0 |
19 | | - self.num_sources = 0 |
20 | 24 | self.num_lines = 0 |
21 | 25 | self.num_transformers = 0 |
| 26 | + self.num_pv = 0 |
| 27 | + self.num_generators = 0 |
22 | 28 | altdss.ClearAll() |
23 | 29 | altdss('new circuit.MyCircuit') |
24 | 30 |
|
25 | | - def add_load_nodes(self, params = {}, load_type: LoadType = LoadType.HOUSE, num = 1): |
26 | | - """ |
27 | | - When the user wants to manually add nodes, or make nodes with varying parameters. |
| 31 | + def add_load_nodes(self, load_type: str = "house", params: dict[str, int] = None, num: int = 1): |
| 32 | + """Adds Load Node(s) to circuit. |
| 33 | + |
| 34 | + Allows the user to add num load nodes, either with customized parameters or using a default load_type. |
28 | 35 |
|
29 | 36 | Args: |
30 | | - params: load parameters for these manual additions |
31 | | - lines: which nodes these new loads are connected to |
32 | | - num (optional): number of loads to create with these parameters |
33 | | - Return: |
34 | | - List of load_nodes |
| 37 | + load_type (str, optional): |
| 38 | + Load type as a string, one of "house", "commercial", "industrial". Defaults to "house". |
| 39 | + params (dict[str, int], optional): |
| 40 | + Load parameters for these manual additions. Defaults to empty dictionary. |
| 41 | + num (int, optional): |
| 42 | + The number of loads to create with these parameters. Defaults to 1. |
| 43 | +
|
| 44 | + Returns: |
| 45 | + list[OpenDSS object]: |
| 46 | + A list of OpenDSS objects representing the load nodes created. |
35 | 47 | """ |
| 48 | + params = params or dict() |
36 | 49 | load_nodes = [] |
37 | | - for i in range(num): |
38 | | - make_load_node(params, load_type, self.num_loads) |
| 50 | + for _ in range(num): |
| 51 | + _make_load_node(params, load_type, self.num_loads) |
39 | 52 | self.num_loads += 1 |
| 53 | + |
40 | 54 | return load_nodes |
41 | 55 |
|
42 | | - def add_source_nodes(self, params = {}, source_type: SourceType = SourceType.TURBINE, num_in_batch = 1, num=1): |
43 | | - """ |
44 | | - When the user wants to manually add nodes, or make nodes with varying parameters. |
| 56 | + def update_source(self, source_type: str = "turbine", params: dict[str, int] = None): |
| 57 | + """Adds or updates source node in system. |
| 58 | +
|
| 59 | + If a Vsource node does not exist, it is created. Otherwise, its parameters are updated based on the provided values. |
45 | 60 |
|
46 | 61 | Args: |
47 | | - params: load parameters for these manual additions |
48 | | - lines: which nodes these new sources are connected to |
49 | | - num (optional): number of sources to create with these parameters (removed for now) |
50 | | - num_in_batch: how many to batch together directly (so they can't be connected to lines separately, etc. |
51 | | - most common use case is if a house has 20 solar panels it's more useful to group them together) |
52 | | - Return: |
53 | | - List of source_nodes |
54 | | - """ |
55 | | - source_nodes = [] |
56 | | - for i in range(num): |
57 | | - make_source_node(params, source_type, count=self.num_sources, num_in_batch=num_in_batch) |
58 | | - self.num_sources += 1 |
59 | | - return source_nodes |
| 62 | + source_type (str, optional): |
| 63 | + The type of the source (one of "turbine", "powerplant", "lvsub", "mvsub", "hvsub", "shvsub"). Defaults to "turbine". |
| 64 | + params (dict[str, int], optional): |
| 65 | + A dictionary of parameters to configure the source node. Defaults to None. |
60 | 66 |
|
61 | | - def add_lines(self, connections, line_type: LineType = LineType.LV_LINE, params = {}, transformer = True): |
| 67 | + Returns: |
| 68 | + OpenDSS object: |
| 69 | + The OpenDSS object representing the source node. |
62 | 70 | """ |
63 | | - Specify all lines that the user wants to add. If redundant lines, doesn't add anything |
| 71 | + params = params or dict() |
| 72 | + return _make_source_node(params, source_type) |
64 | 73 |
|
65 | | - Args: |
66 | | - connections: a list of new connections to add. Each item of the list follows the form (source1, load1) |
67 | | - TODO: allow the input to also contain optional parameters |
68 | | - """ |
69 | | - for src, dst in connections: |
70 | | - make_line(src, dst, line_type, self.num_lines, params, transformer) |
71 | | - self.num_lines += 1 |
| 74 | + def add_PVSystem(self, load_nodes: list[str], params: dict[str, int] = None, num_panels: int = 1): |
| 75 | + """Adds a photovoltaic (PV) system to the specified load nodes. |
72 | 76 |
|
73 | | - def add_transformers(self, connections, params = {}): |
74 | | - """ |
75 | | - Specify all transformers that the user wants to add, same input style as lines. |
| 77 | + Adds PV system with num_panels to each of the listed load nodes. Can be customized with parameters. |
76 | 78 |
|
77 | 79 | Args: |
78 | | - connections: a list of new transformers to add (where to add them), with these params |
79 | | - TODO: remove |
80 | | - """ |
81 | | - for src, dst in connections: |
82 | | - make_transformer(src, dst, self.num_transformers, params) |
83 | | - self.num_transformers += 1 |
84 | | - |
| 80 | + load_nodes (list[str]): |
| 81 | + A list of node names where the PV system will be connected. |
| 82 | + params (dict[str, int], optional): |
| 83 | + A dictionary of additional parameters for the PV system. Defaults to None. |
| 84 | + num_panels (int, optional): |
| 85 | + The number of PV panels in the system. Defaults to 1. |
85 | 86 |
|
86 | | - def view_load_nodes(self, indices = []): |
| 87 | + Returns: |
| 88 | + list[DSS objects]: |
| 89 | + A list of OpenDSS objects representing the PV systems created. |
87 | 90 | """ |
88 | | - View load nodes (what their parameters are) at the given indices. |
| 91 | + params = params or dict() |
| 92 | + if not load_nodes: |
| 93 | + raise ValueError("Need to enter load nodes to add PVSystem to") |
89 | 94 |
|
| 95 | + PV_nodes = [] |
| 96 | + for load in load_nodes: |
| 97 | + PV_nodes.append(_make_pv(load, params, num_panels, self.num_pv)) |
| 98 | + self.num_pv += 1 |
| 99 | + |
| 100 | + return PV_nodes |
| 101 | + |
| 102 | + def add_generator(self, num: int = 1, gen_type: str = "small", params: dict[str, int] = None): |
| 103 | + """Adds generator(s) to the system. |
| 104 | + |
90 | 105 | Args: |
91 | | - indices (optional): Which indices to view the nodes at. |
92 | | - If none given, display all |
| 106 | + num (int, optional): |
| 107 | + The number of generator units to add. Defaults to 1. |
| 108 | + gen_type (str, optional): |
| 109 | + The type of generator (one of "small", "large", "industrial"). Defaults to "small". |
| 110 | + params (dict[str, int], optional): |
| 111 | + A dictionary of parameters to configure the generator. Defaults to None. |
| 112 | +
|
| 113 | + Returns: |
| 114 | + list[DSS objects]: |
| 115 | + A list of OpenDSS objects representing the generators created. |
93 | 116 | """ |
94 | | - load_nodes = [] |
95 | | - if not indices: |
96 | | - indices = [i for i in range(self.num_loads)] |
97 | | - |
98 | | - for idx in indices: |
99 | | - load_obj = altdss.Load["load" + str(idx)] |
100 | | - load_info = {} |
101 | | - load_info["name"] = "load" + str(idx) |
102 | | - load_info["kV"] = load_obj.kV |
103 | | - load_info["kW"] = load_obj.kW |
104 | | - load_info["kVar"] = load_obj.kvar |
105 | | - load_nodes.append(load_info) |
106 | | - return load_nodes |
| 117 | + params = params or dict() |
| 118 | + generators = [] |
| 119 | + for _ in range(num): |
| 120 | + generators.append(_make_generator(params, gen_type, count=self.num_generators)) |
| 121 | + self.num_generators += 1 |
| 122 | + |
| 123 | + return generators |
107 | 124 |
|
108 | 125 |
|
109 | | - def view_source_node(self): |
110 | | - """ |
111 | | - View source nodes (what their parameters are) at the given indices. |
| 126 | + def add_lines(self, connections: list[tuple], line_type: str = "lv", params: dict[str, int] = None, transformer: bool = True): |
| 127 | + """Adds lines to the system. |
| 128 | +
|
| 129 | + Adds electrical lines according to the given connections. Users can specify the parameters of the lines or otherwise use given line type options. |
112 | 130 |
|
113 | 131 | Args: |
114 | | - indices (optional): Which indices to view the nodes at. |
115 | | - If none given, display all |
116 | | - |
117 | | - TODO once capability for more source nodes is initialized |
| 132 | + connections (list[tuple]): |
| 133 | + A list of tuples defining the connections between nodes. |
| 134 | + line_type (str, optional): |
| 135 | + The type of line (one of "lv", "mv", "hv"). Defaults to "lv". |
| 136 | + params (dict[str, int], optional): |
| 137 | + A dictionary of parameters to configure the lines. Defaults to None. |
| 138 | + transformer (bool, optional): |
| 139 | + Whether to include a transformer in the connection. Defaults to True. |
| 140 | +
|
| 141 | + Returns: |
| 142 | + None |
118 | 143 | """ |
119 | | - source_obj = altdss.Vsource["source"] |
120 | | - source_info = {} |
121 | | - source_info["name"] = "source" |
122 | | - source_info["kV"] = source_obj.BasekV |
123 | | - return source_info |
| 144 | + params = params or dict() |
| 145 | + for src, dst in connections: |
| 146 | + _make_line(src, dst, line_type, self.num_lines, params, transformer) |
| 147 | + self.num_lines += 1 |
124 | 148 |
|
125 | 149 | def solve(self): |
126 | | - """ |
127 | | - Initialize "solve" mode in AltDSS, then allowing the user to query various results on the circuit |
| 150 | + """Solves the OpenDSS circuit. |
| 151 | +
|
| 152 | + Initializes "solve" mode in OpenDSS, which then allows the user to query results on the circuit. |
128 | 153 |
|
129 | | - TODO: error handling here |
| 154 | + Returns: |
| 155 | + None |
130 | 156 | """ |
131 | 157 | altdss.Solution.Solve() |
132 | 158 |
|
133 | | - def results(self, queries): |
134 | | - """ |
135 | | - Allow the user to query for many results at once instead of learning how to manually query |
| 159 | + def results(self, queries: list[str], export_path = ""): |
| 160 | + """Gets simulation results based on specified queries. |
| 161 | + |
| 162 | + Allows the user to query for many results at once by providing a list of desired queries. |
| 163 | + |
| 164 | + Args: |
| 165 | + queries (list[str]): |
| 166 | + A list of queries to the circuit ("Voltages", "Losses", "TotalPower") |
| 167 | + export_path (str, optional): |
| 168 | + The file path to export results. If empty, results are not exported. Defaults to "". |
136 | 169 |
|
137 | 170 | Returns: |
138 | | - Results for each query, in a dictionary |
| 171 | + dict: |
| 172 | + A dictionary containing the fetched simulation results. |
139 | 173 | """ |
140 | 174 | results = {} |
141 | 175 | for query in queries: |
142 | | - results[query] = query_solution(query) |
| 176 | + results[query] = _query_solution(query) |
| 177 | + |
| 178 | + if (export_path): |
| 179 | + _export_results(results, export_path) |
| 180 | + |
143 | 181 | return results |
144 | 182 |
|
145 | 183 | def clear(self): |
146 | | - """ |
147 | | - Must call after we are done using the circuit, or will cause re-creation errors. |
| 184 | + """Clears the OpenDSS circuit. |
148 | 185 |
|
149 | | - We only work with one circuit at a time, can only have one PyGridSim object at a time. |
150 | | - TODO: maybe this isn't necessary because it's done in the beginning |
| 186 | + Returns: |
| 187 | + None |
151 | 188 | """ |
152 | 189 | altdss.ClearAll() |
153 | 190 | self.num_loads = 0 |
|
0 commit comments