├── Agents ├── BaseAgent.py ├── DoNothingAgent.py └── __pycache__ │ ├── BaseAgent.cpython-37.pyc │ └── DoNothingAgent.cpython-37.pyc ├── Environment ├── Environment.py └── __pycache__ │ └── Environment.cpython-37.pyc ├── Examples └── demos.ipynb ├── Grids ├── BaseGrid.py ├── DoNothingGrid.py └── __pycache__ │ ├── BaseGrid.cpython-37.pyc │ └── DoNothingGrid.cpython-37.pyc ├── LICENSE ├── Markets ├── BaseMarket.py ├── DoNothingMarket.py └── __pycache__ │ ├── BaseMarket.cpython-37.pyc │ └── DoNothingMarket.cpython-37.pyc ├── README.md ├── dss_grid_examples └── 34Bus │ ├── IEEE34_BusXY.csv │ ├── IEEELineCodes.DSS │ ├── README.md │ └── ieee34Mod1.dss ├── dss_tools ├── __pycache__ │ └── opendss_tools.cpython-37.pyc └── opendss_tools.py ├── requirements.txt └── utils ├── __pycache__ ├── iterator.cpython-37.pyc └── method_update.cpython-37.pyc ├── iterator.py └── method_update.py /Agents/BaseAgent.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class BaseAgent(ABC): 4 | 5 | @abstractmethod 6 | def set_market_actions(self): 7 | ... 8 | 9 | @abstractmethod 10 | def set_grid_actions(self): 11 | ... 12 | 13 | def get_market_actions(self): 14 | return self.actions 15 | 16 | def get_grid_actions(self): 17 | return self.actions -------------------------------------------------------------------------------- /Agents/DoNothingAgent.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from BaseAgent import BaseAgent 3 | 4 | @dataclass 5 | class DoNothingAgent(BaseAgent): 6 | 7 | def __post_init__(self): 8 | self.actions = [] 9 | 10 | def set_market_actions(self): 11 | pass 12 | 13 | def set_grid_actions(self): 14 | pass 15 | 16 | 17 | if __name__ == '__main__': 18 | 19 | agent = DoNothingAgent() 20 | print(agent) 21 | print(isinstance(agent, BaseAgent)) -------------------------------------------------------------------------------- /Agents/__pycache__/BaseAgent.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/Agents/__pycache__/BaseAgent.cpython-37.pyc -------------------------------------------------------------------------------- /Agents/__pycache__/DoNothingAgent.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/Agents/__pycache__/DoNothingAgent.cpython-37.pyc -------------------------------------------------------------------------------- /Environment/Environment.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | from typing import List 3 | import os, sys 4 | from pathlib import Path 5 | import warnings 6 | from tqdm import trange 7 | 8 | path_to_OpenGridGym = Path(__file__).parents[1] 9 | sys.path.insert(0, os.path.join(path_to_OpenGridGym)) 10 | for folder in ('Grids', 'Markets', 'Agents', 'utils'): 11 | path = os.path.join(path_to_OpenGridGym, folder) 12 | sys.path.insert(0, path) 13 | 14 | from BaseGrid import BaseGrid 15 | from BaseMarket import BaseMarket 16 | from BaseAgent import BaseAgent 17 | 18 | from method_update import update_instance_method 19 | from iterator import episode_iterator 20 | 21 | def ensure_type(obj, desired_type): 22 | ''' 23 | Ensure that a given object is of desired type. 24 | 25 | Example 26 | 27 | >>> ensure_type(1, int) 28 | >>> ensure_type('1', int) 29 | TypeError: Expected object of type '' 30 | but observed instead type ''. 31 | 32 | ''' 33 | if not isinstance(obj, desired_type): 34 | actual_type = type(obj) 35 | raise TypeError(f"Expected object of type '{desired_type}'\n" 36 | f"but instead observed type '{actual_type}'.") 37 | 38 | @dataclass 39 | class Environment: 40 | ''' 41 | Stores all the objects relevant to the simulation 42 | of electricity markets, and provides the user with 43 | an interface to manipulate the simulation process. 44 | 45 | Some method names are inspired by OpenAI Gym's style 46 | of defining environments. We employ the dataclass 47 | decorator to improve the user's debugging experience. 48 | 49 | ''' 50 | grid: BaseGrid 51 | market: BaseMarket 52 | agents: List[BaseAgent] 53 | max_steps: int = field(default=24*4) 54 | 55 | def __post_init__(self): 56 | # Ensure correctness of object types 57 | ensure_type(self.grid, BaseGrid) 58 | ensure_type(self.market, BaseMarket) 59 | ensure_type(self.agents, list) 60 | for agent in self.agents: 61 | ensure_type(agent, BaseAgent) 62 | 63 | # Assign pointers to environment 64 | self.grid.env = self 65 | self.market.env = self 66 | for agent in self.agents: 67 | agent.env = self 68 | 69 | self.reset() 70 | 71 | def reset(self): 72 | self.grid.reset() 73 | self.market.reset() 74 | # Instruct agents to get their first observations 75 | ... 76 | 77 | def step(self): 78 | # Gym: action -> done, info, reward, obs 79 | # Here: Call agent to act & it internally grabs info, reward, obs 80 | ... 81 | 82 | def iterate(self, num_iter=None, grid_iterator=trange, market_iterator=range, warn=True): 83 | ''' 84 | Iterate through the episode starting at the current time 85 | self.grid.t and ending after num_iter iterations. 86 | 87 | Note: To reset start time, self.reset(). 88 | Any explicit edit of self.grid.t must be done cautiously. 89 | 90 | For explanation of this method's parameters, please refer to 91 | the docstring of episode_iterator. 92 | 93 | Example: 94 | 95 | >>> env.grid.t # check current time 96 | 50 97 | >>> for t in env.iterate(3): # progress for 3 steps only 98 | ... print(t) 99 | ... 100 | 51 101 | 52 102 | 53 103 | 104 | ''' 105 | # Progress and yield time 106 | for t in episode_iterator(t_prev=self.grid.t, max_steps=self.max_steps, 107 | num_iter=num_iter, iterator=grid_iterator, warn=warn): 108 | 109 | # Market interactions 110 | self.market.reset() 111 | for t_market in self.market.iterate(iterator=market_iterator, warn=warn): 112 | self.callback_market_step(t_market) 113 | 114 | # # Grid interactions 115 | for agent in self.agents: 116 | agent.set_grid_actions() 117 | 118 | self.grid.t = t 119 | self.grid.step() 120 | 121 | yield t 122 | 123 | def callback_market_step(self, t_market): 124 | pass 125 | 126 | def render(self): 127 | ... 128 | 129 | def close(self): 130 | ... -------------------------------------------------------------------------------- /Environment/__pycache__/Environment.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/Environment/__pycache__/Environment.cpython-37.pyc -------------------------------------------------------------------------------- /Examples/demos.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "heading_collapsed": "true", 7 | "tags": [] 8 | }, 9 | "source": [ 10 | "# Import modules" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "import os, sys\n", 20 | "import numpy as np\n", 21 | "import pandas as pd\n", 22 | "import matplotlib.pyplot as plt" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 2, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "for folder in ('dss_tools', 'dss_grid_examples', 'Environment', 'Grids', 'Markets', 'Agents', 'utils'):\n", 32 | " \n", 33 | " if folder not in sys.path:\n", 34 | " sys.path.insert(0, folder)" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 3, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "from opendss_tools import DSS, DSS_from_docstring, DSS_from_empty\n", 44 | "\n", 45 | "from Environment import Environment\n", 46 | "from DoNothingAgent import DoNothingAgent\n", 47 | "from DoNothingMarket import DoNothingMarket\n", 48 | "from DoNothingGrid import DoNothingGrid" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": { 54 | "heading_collapsed": "true", 55 | "tags": [] 56 | }, 57 | "source": [ 58 | "# Test DSS interface with IEEE 34 bus" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 4, 64 | "metadata": {}, 65 | "outputs": [ 66 | { 67 | "name": "stdout", 68 | "output_type": "stream", 69 | "text": [ 70 | "37 Buses\n", 71 | "95 Nodes\n", 72 | "Voltage Bases (kV L-L):\t 69.0, 24.9, 4.16, 0.48\n", 73 | "Circuit Elements:\n", 74 | "\t\t1 Vsource\n", 75 | "\t\t8 Transformers\n", 76 | "\t\t32 Lines\n", 77 | "\t\t2 Capacitors\n", 78 | "\t\t6 RegControls\n", 79 | "\t\t68 Loads\n" 80 | ] 81 | } 82 | ], 83 | "source": [ 84 | "dss_path = r'dss_grid_examples\\34Bus\\ieee34Mod1.dss'\n", 85 | "xy_path = r'dss_grid_examples\\34Bus\\IEEE34_BusXY.csv'\n", 86 | "\n", 87 | "dss = DSS.open_case_from_file(dss_path)\n", 88 | "\n", 89 | "dss.command(f\"BusCoords [{xy_path}]\")\n", 90 | "\n", 91 | "dss.summarize()" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 5, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "data": { 101 | "text/html": [ 102 | "
\n", 103 | "\n", 116 | "\n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | "
xyVmagPu
sourcebus-300.00.01.049996
8000.00.01.049994
802400.00.01.048111
806700.00.01.046870
8081000.00.01.024143
\n", 158 | "
" 159 | ], 160 | "text/plain": [ 161 | " x y VmagPu\n", 162 | "sourcebus -300.0 0.0 1.049996\n", 163 | "800 0.0 0.0 1.049994\n", 164 | "802 400.0 0.0 1.048111\n", 165 | "806 700.0 0.0 1.046870\n", 166 | "808 1000.0 0.0 1.024143" 167 | ] 168 | }, 169 | "execution_count": 5, 170 | "metadata": {}, 171 | "output_type": "execute_result" 172 | } 173 | ], 174 | "source": [ 175 | "dss.get_bus_df().head()" 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": { 181 | "heading_collapsed": "true", 182 | "tags": [] 183 | }, 184 | "source": [ 185 | "# DoNothing" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": 6, 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [ 194 | "grid = DoNothingGrid()\n", 195 | "\n", 196 | "market = DoNothingMarket()\n", 197 | "\n", 198 | "agents = [DoNothingAgent(), DoNothingAgent(), DoNothingAgent()]\n", 199 | "\n", 200 | "env = Environment(grid=grid, market=market, agents=agents)" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 7, 206 | "metadata": {}, 207 | "outputs": [ 208 | { 209 | "name": "stderr", 210 | "output_type": "stream", 211 | "text": [ 212 | "100%|██████████████████████████████████████████████████████████████████████████████████| 5/5 [00:00<00:00, 5009.92it/s]\n" 213 | ] 214 | } 215 | ], 216 | "source": [ 217 | "env.reset()\n", 218 | "for t in env.iterate(5):\n", 219 | " pass" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": { 225 | "heading_collapsed": "true", 226 | "tags": [] 227 | }, 228 | "source": [ 229 | "# Demo Grid only" 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "metadata": {}, 235 | "source": [ 236 | "In this demo, the grid randomly fluctuates load consumption and records bus voltages at each step." 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 8, 242 | "metadata": {}, 243 | "outputs": [], 244 | "source": [ 245 | "from BaseGrid import BaseGrid\n", 246 | "\n", 247 | "class DemoGrid(BaseGrid):\n", 248 | " def __init__(self, dss_path, xy_path=None, load_std=0.2):\n", 249 | " self.dss_path = dss_path\n", 250 | " self.xy_path = xy_path\n", 251 | " \n", 252 | " self.load_std = load_std\n", 253 | " \n", 254 | " self.reset()\n", 255 | " \n", 256 | " def reset(self):\n", 257 | " self.dss = DSS.open_case_from_file(self.dss_path)\n", 258 | " if self.xy_path is not None:\n", 259 | " self.dss.command(f\"BusCoords [{self.xy_path}]\")\n", 260 | " \n", 261 | " self.t = -1\n", 262 | " self.voltages = []\n", 263 | " \n", 264 | " def step(self):\n", 265 | " self.randomly_fluctuate_loads()\n", 266 | " self.dss.solve_case()\n", 267 | " self.record_voltages()\n", 268 | " \n", 269 | " def randomly_fluctuate_loads(self):\n", 270 | " for load in self.dss.Circuit.Loads:\n", 271 | " load.kW *= 1 + self.load_std * np.random.randn()\n", 272 | " \n", 273 | " def record_voltages(self):\n", 274 | " v = self.dss.get_bus_df().VmagPu.values\n", 275 | " self.voltages.append(v)\n", 276 | " \n", 277 | " def plot_voltages(self, *plot_args, **plot_kwargs):\n", 278 | " plt.plot(self.voltages, *plot_args, **plot_kwargs)\n", 279 | " xlim = plt.xlim(0, self.t)\n", 280 | " plt.hlines(1.0, *xlim, color='r')\n", 281 | " plt.hlines([0.95, 1.05], *xlim, color='r', linestyle='--')\n", 282 | " \n", 283 | "grid = DemoGrid(dss_path, xy_path)" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": 9, 289 | "metadata": {}, 290 | "outputs": [ 291 | { 292 | "name": "stderr", 293 | "output_type": "stream", 294 | "text": [ 295 | "100%|████████████████████████████████████████████████████████████████████████████████| 96/96 [00:00<00:00, 1007.37it/s]\n" 296 | ] 297 | } 298 | ], 299 | "source": [ 300 | "env = Environment(grid=grid, market=market, agents=agents)\n", 301 | "\n", 302 | "env.reset()\n", 303 | "for t in env.iterate():\n", 304 | " pass" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": 10, 310 | "metadata": {}, 311 | "outputs": [ 312 | { 313 | "data": { 314 | "image/png": "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\n", 315 | "text/plain": [ 316 | "
" 317 | ] 318 | }, 319 | "metadata": { 320 | "needs_background": "light" 321 | }, 322 | "output_type": "display_data" 323 | } 324 | ], 325 | "source": [ 326 | "env.grid.plot_voltages(color='gray', alpha=0.5)" 327 | ] 328 | }, 329 | { 330 | "cell_type": "markdown", 331 | "metadata": { 332 | "heading_collapsed": "true", 333 | "tags": [] 334 | }, 335 | "source": [ 336 | "# Demo Grid & Agent" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": {}, 342 | "source": [ 343 | "In this demo, the agent responds to local voltages by incrementing/decrementing load" 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 11, 349 | "metadata": {}, 350 | "outputs": [], 351 | "source": [ 352 | "from BaseAgent import BaseAgent\n", 353 | "\n", 354 | "class DemoAgentVoltageControl(BaseAgent):\n", 355 | "\n", 356 | " def __init__(self, load=None, bus=None):\n", 357 | " if load is None or bus is None:\n", 358 | " raise ValueError('load and bus must be str, not None.')\n", 359 | " \n", 360 | " self.load = load\n", 361 | " self.bus = bus\n", 362 | " self.voltages = []\n", 363 | " \n", 364 | " def get_voltage(self):\n", 365 | " return self.env.grid.dss.get_bus_df().loc[self.bus, 'VmagPu']\n", 366 | " \n", 367 | " def get_load(self):\n", 368 | " load_df = self.env.grid.dss.get_load_df().loc[self.load]\n", 369 | " kW, kvar = load_df.kW, load_df.kvar\n", 370 | " return kW, kvar\n", 371 | " \n", 372 | " def increment_load(self, delta_kW=0.0, delta_kvar=0.0):\n", 373 | " kW, kvar = self.get_load()\n", 374 | " \n", 375 | " new_kW = kW + delta_kW\n", 376 | " new_kvar = kvar + delta_kvar\n", 377 | " \n", 378 | " self.env.grid.dss.edit(f'Load.{self.load}', kW=new_kW, kvar=new_kvar)\n", 379 | " \n", 380 | " def set_market_actions(self):\n", 381 | " pass\n", 382 | " \n", 383 | " def set_grid_actions(self):\n", 384 | " \n", 385 | " # Simple feedback control\n", 386 | " v = self.get_voltage()\n", 387 | " self.voltages.append(v)\n", 388 | " delta_kW = 3 * (1 - v) / 0.05\n", 389 | " delta_kvar = 1 * (1 - v) / 0.05\n", 390 | " \n", 391 | " # Update load\n", 392 | " self.increment_load(delta_kW=delta_kW, delta_kvar=delta_kvar)\n", 393 | " \n", 394 | "agent_grid = DemoAgentVoltageControl(load='s890', bus='890')" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 12, 400 | "metadata": {}, 401 | "outputs": [ 402 | { 403 | "name": "stderr", 404 | "output_type": "stream", 405 | "text": [ 406 | "100%|█████████████████████████████████████████████████████████████████████████████████| 96/96 [00:00<00:00, 311.51it/s]\n" 407 | ] 408 | } 409 | ], 410 | "source": [ 411 | "agents = [agent_grid]\n", 412 | "grid = DemoGrid(dss_path, xy_path, load_std=0.01)\n", 413 | "\n", 414 | "env = Environment(grid=grid, market=market, agents=agents)\n", 415 | "\n", 416 | "env.reset()\n", 417 | "for t in env.iterate():\n", 418 | " pass" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": 13, 424 | "metadata": {}, 425 | "outputs": [ 426 | { 427 | "data": { 428 | "image/png": "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bNCfj3nz5nHJJZfw8ccfV7rshQZ9eQYPHkxSUhL79+8vmfbtt9/SvXt3WrVqVen69SHobdRNKSkZefRv18zTZZhaaOq/N7Ml5WSVbrNbq8Y8c333CudnZWWxcuVKli9fzvXXX8/UqVMBcDgcTJ48mWXLlhEdHY2fnx933nkn48ePJz4+noceeoisrCzCw8N59913admyJUOHDqV///4sX76cjIwM3n77bfr378/TTz9Nbm4uK1euZMqUKQQGBtKoUSMAFi5cyNSpU/H19SU0NJQffjjzIXG7d+8mKyuLN954gxdeeIHf/e53JbXff//9rF27FhHhmWeeYc2aNeTm5hIbG0v37t154YUXGDVqFJs2bQJgxowZZGVl8eyzz/LWW28xa9YsCgoK6NixI3PnziUoqPxhzz4+Ptx8883Mnz+fxx57DID58+czadIkjh8/zp133smePXsICgpi1qxZxMTElKz7888/s2TJEr7//nuef/55PvroI5YtW1buvnfv3s2tt95KdnY2o0ePZubMmWRlZQEwffp0FixYQH5+PmPGjGHq1KlkZ2dz8803k5SURHFxMU899RQTJkw41/8mVcJa9C7FDuXwyTxa2YgbU0t8+umnjBw5ks6dOxMWFkZ8fDwAH3/8Mfv27WPLli3MnTuXVatWAc5x//fffz+LFi0iPj6eO++8kyeeeKJke0VFRfz666/MnDmTqVOn4u/vz7Rp05gwYQIJCQlMmDCBCRMm8MgjzucETZs2ja+++orExESWLFlSbo3z589n4sSJDB48mO3bt3PkyBEAnnvuOUJDQ9m4cSMbNmxg+PDhvPzyywQGBpKQkMAHH3xw1mMfO3Ysa9asITExka5du/L222+fdflJkyYxf/58APLz81m6dCnjxo3jmWeeoXfv3mzYsIEXX3yR3/72t6etN3DgQG644QamT59OQkICHTp0qHDfDzzwAA888AAbN24kKuo/j8X++uuv2blzJ7/++isJCQnEx8fzww8/8OWXX9KqVSsSExPZtGkTI0eOPOsxVCdr0bsczcyj2KEW9KZcZ2t5V5d58+bxwAMPADBx4kTmzZtH3759WblyJTfddBM+Pj60aNGCYcOGAbB9+3Y2bdrEiBEjACguLqZly5Yl2xs71vkQuL59+7Jv375K9z9o0CDuuOMObr755pJ1y6vxk08+wcfHh3Hj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\n", 429 | "text/plain": [ 430 | "
" 431 | ] 432 | }, 433 | "metadata": { 434 | "needs_background": "light" 435 | }, 436 | "output_type": "display_data" 437 | } 438 | ], 439 | "source": [ 440 | "plt.plot(agent_grid.voltages, label=\"Agent's Actual Voltages\")\n", 441 | "plt.hlines(1.0, *plt.xlim(), color='r', label=\"Agent's Reference Voltage\")\n", 442 | "plt.legend();" 443 | ] 444 | }, 445 | { 446 | "cell_type": "markdown", 447 | "metadata": { 448 | "heading_collapsed": "true", 449 | "tags": [] 450 | }, 451 | "source": [ 452 | "# Demo Grid & Market & Agents" 453 | ] 454 | }, 455 | { 456 | "cell_type": "markdown", 457 | "metadata": {}, 458 | "source": [ 459 | "In this demo, we assume the distribution market is faced with a new (incremental) demand at the beginning of each market clearning process. Its job is to dispatch all supply units with how much to supply and how much to get paid.\n", 460 | "\n", 461 | "For simplicity, we assume that the source bus (feeder) in the distribution grid provides an electricity price (1.0 for reference) which is exogenously determined (for example, by a transmission grid market).\n", 462 | "\n", 463 | "Then, we let an agent represent a load that is willing to sacrifice some consumption (acting like a reserve) and receive a reward for it. If the price accepted is lower than that of the source bus, the market will adjust dispatch accordingly." 464 | ] 465 | }, 466 | { 467 | "cell_type": "code", 468 | "execution_count": 14, 469 | "metadata": {}, 470 | "outputs": [], 471 | "source": [ 472 | "from BaseMarket import BaseMarket\n", 473 | "\n", 474 | "class DemoMarket(BaseMarket):\n", 475 | " def __init__(self, max_steps=1):\n", 476 | " self.max_steps = max_steps\n", 477 | " self.reset()\n", 478 | "\n", 479 | " def reset(self):\n", 480 | " self.t = -1\n", 481 | " self.new_demand = 100\n", 482 | " \n", 483 | " self.source_price = 1.0\n", 484 | " self.source_dispatch = self.new_demand * 1.0\n", 485 | " \n", 486 | " # To be changed by agent\n", 487 | " self.agent_price = 999 # large to represent inelastic\n", 488 | " self.agent_capacity = 0.0\n", 489 | " self.agent_dispatch = 0.0\n", 490 | "\n", 491 | " def step(self):\n", 492 | " if self.agent_capacity > 0.0 and self.agent_price < self.source_price:\n", 493 | " self.agent_dispatch = np.minimum(self.agent_capacity, self.new_demand)\n", 494 | " else:\n", 495 | " self.agent_dispatch = 0.0\n", 496 | " \n", 497 | " self.source_dispatch = self.new_demand - self.agent_dispatch\n", 498 | " \n", 499 | "market = DemoMarket()" 500 | ] 501 | }, 502 | { 503 | "cell_type": "code", 504 | "execution_count": 15, 505 | "metadata": {}, 506 | "outputs": [], 507 | "source": [ 508 | "class DemoAgentReserveSupply(BaseAgent):\n", 509 | "\n", 510 | " def __init__(self, load=None, minimum_kW=0.0):\n", 511 | " if load is None:\n", 512 | " raise ValueError('load must be str, not None.')\n", 513 | " \n", 514 | " self.load = load\n", 515 | " self.minimum_kW = minimum_kW\n", 516 | " \n", 517 | " self.dispatches = []\n", 518 | " self.kWs = []\n", 519 | " \n", 520 | " def set_market_actions(self):\n", 521 | " kW = self.get_load()\n", 522 | " \n", 523 | " # Do not be willing to sacrifice too much\n", 524 | " if kW < self.minimum_kW:\n", 525 | " self.env.market.agent_capacity = 0.0\n", 526 | " else:\n", 527 | " self.env.market.agent_capacity = kW - self.minimum_kW\n", 528 | " \n", 529 | " # Random offer\n", 530 | " self.env.market.agent_price = 1.1 + 0.2 * np.random.randn()\n", 531 | " \n", 532 | " def get_load(self):\n", 533 | " kW = self.env.grid.dss.get_load_df().loc[self.load, 'kW']\n", 534 | " return kW\n", 535 | " \n", 536 | " def increment_load(self, delta_kW=0.0):\n", 537 | " kW = self.get_load()\n", 538 | " \n", 539 | " new_kW = kW + delta_kW\n", 540 | " \n", 541 | " self.env.grid.dss.edit(f'Load.{self.load}', kW=new_kW)\n", 542 | " \n", 543 | " def set_grid_actions(self):\n", 544 | " kW_drop = self.env.market.agent_dispatch\n", 545 | " \n", 546 | " self.increment_load(kW_drop)\n", 547 | " \n", 548 | " self.dispatches.append(kW_drop)\n", 549 | " self.kWs.append(self.get_load())\n", 550 | " \n", 551 | "agent_market = DemoAgentReserveSupply(load='s844', minimum_kW=1300)" 552 | ] 553 | }, 554 | { 555 | "cell_type": "code", 556 | "execution_count": 16, 557 | "metadata": {}, 558 | "outputs": [ 559 | { 560 | "name": "stderr", 561 | "output_type": "stream", 562 | "text": [ 563 | "100%|█████████████████████████████████████████████████████████████████████████████████| 96/96 [00:00<00:00, 130.07it/s]\n" 564 | ] 565 | } 566 | ], 567 | "source": [ 568 | "agents = [agent_grid, agent_market]\n", 569 | "\n", 570 | "grid = DemoGrid(dss_path, xy_path, load_std=0.01)\n", 571 | "\n", 572 | "market = DemoMarket()\n", 573 | "\n", 574 | "env = Environment(grid=grid, market=market, agents=agents)\n", 575 | "\n", 576 | "env.reset()\n", 577 | "for t in env.iterate():\n", 578 | " pass" 579 | ] 580 | }, 581 | { 582 | "cell_type": "code", 583 | "execution_count": 17, 584 | "metadata": {}, 585 | "outputs": [ 586 | { 587 | "data": { 588 | "image/png": "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\n", 589 | "text/plain": [ 590 | "
" 591 | ] 592 | }, 593 | "metadata": { 594 | "needs_background": "light" 595 | }, 596 | "output_type": "display_data" 597 | } 598 | ], 599 | "source": [ 600 | "plt.plot(agent_market.dispatches, label=\"Market's dispatch Signal\")\n", 601 | "plt.plot(agent_market.kWs, label=\"Agent's Actual Load\")\n", 602 | "plt.hlines(agent_market.minimum_kW, *plt.xlim(), color='r', label=\"Agent's Minimum Acceptable Load\")\n", 603 | "plt.legend();" 604 | ] 605 | } 606 | ], 607 | "metadata": { 608 | "kernelspec": { 609 | "display_name": "Python 3 (ipykernel)", 610 | "language": "python", 611 | "name": "python3" 612 | }, 613 | "language_info": { 614 | "codemirror_mode": { 615 | "name": "ipython", 616 | "version": 3 617 | }, 618 | "file_extension": ".py", 619 | "mimetype": "text/x-python", 620 | "name": "python", 621 | "nbconvert_exporter": "python", 622 | "pygments_lexer": "ipython3", 623 | "version": "3.7.8" 624 | }, 625 | "toc-autonumbering": false, 626 | "toc-showtags": false 627 | }, 628 | "nbformat": 4, 629 | "nbformat_minor": 4 630 | } 631 | -------------------------------------------------------------------------------- /Grids/BaseGrid.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class BaseGrid(ABC): 4 | 5 | @abstractmethod 6 | def reset(self): 7 | self.t = -1 8 | 9 | @abstractmethod 10 | def step(self): 11 | pass -------------------------------------------------------------------------------- /Grids/DoNothingGrid.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | from BaseGrid import BaseGrid 3 | 4 | @dataclass 5 | class DoNothingGrid(BaseGrid): 6 | case_folder: str = field(default='') 7 | 8 | def __post_init__(self): 9 | self.reset() 10 | 11 | def reset(self): 12 | self.t = -1 13 | 14 | def step(self): 15 | pass 16 | 17 | 18 | if __name__ == '__main__': 19 | 20 | grid = DoNothingGrid() 21 | print(grid) 22 | print(isinstance(grid, BaseGrid)) -------------------------------------------------------------------------------- /Grids/__pycache__/BaseGrid.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/Grids/__pycache__/BaseGrid.cpython-37.pyc -------------------------------------------------------------------------------- /Grids/__pycache__/DoNothingGrid.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/Grids/__pycache__/DoNothingGrid.cpython-37.pyc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Texas A&M Engineering Research 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Markets/BaseMarket.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | import os, sys 3 | from pathlib import Path 4 | 5 | path_to_OpenEMS = Path(__file__).parents[1] 6 | sys.path.insert(0, os.path.join(path_to_OpenEMS)) 7 | for folder in ('utils',): 8 | path = os.path.join(path_to_OpenEMS, folder) 9 | sys.path.insert(0, path) 10 | 11 | from iterator import episode_iterator 12 | 13 | class BaseMarket(ABC): 14 | 15 | @abstractmethod 16 | def reset(self): 17 | self.t = -1 18 | 19 | @abstractmethod 20 | def step(self): 21 | ... 22 | 23 | def iterate(self, num_iter=None, iterator=range, warn=True): 24 | ''' 25 | Iterate through the market episode starting at the current step 26 | self.t and ending after num_iter iterations. 27 | 28 | Note: To reset start time, self.reset(). 29 | Any explicit edit of self.t must be done cautiously. 30 | 31 | For explanation of this method's parameters, please refer to 32 | the docstring of episode_iterator. 33 | 34 | Example: 35 | 36 | >>> market.t # check current time 37 | 50 38 | >>> for t in market.iterate(3): # progress for 3 steps only 39 | ... print(t) 40 | ... 41 | 51 42 | 52 43 | 53 44 | ''' 45 | # Progress and yield time 46 | for t in episode_iterator(t_prev=self.t, 47 | max_steps=self.max_steps, 48 | num_iter=num_iter, 49 | iterator=iterator, 50 | warn=warn): 51 | 52 | for agent in self.env.agents: 53 | agent.set_market_actions() 54 | 55 | self.step() 56 | 57 | self.t = t 58 | yield t -------------------------------------------------------------------------------- /Markets/DoNothingMarket.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | from BaseMarket import BaseMarket 3 | 4 | @dataclass 5 | class DoNothingMarket(BaseMarket): 6 | max_steps: int = field(default=100) 7 | 8 | def __post_init__(self): 9 | self.reset() 10 | 11 | def reset(self): 12 | self.t = -1 13 | 14 | def step(self): 15 | pass 16 | 17 | 18 | if __name__ == '__main__': 19 | 20 | market = DoNothingMarket() 21 | print(market) 22 | print(isinstance(market, BaseMarket)) -------------------------------------------------------------------------------- /Markets/__pycache__/BaseMarket.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/Markets/__pycache__/BaseMarket.cpython-37.pyc -------------------------------------------------------------------------------- /Markets/__pycache__/DoNothingMarket.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/Markets/__pycache__/DoNothingMarket.cpython-37.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenGridGym 2 | 3 | OpenGridGym is an Open-Source AI-friendly toolkit for distribution market simulation, referenced and described here: https://arxiv.org/abs/2203.04410. Please refer to the paper for details on authors and contact information. 4 | 5 | 6 | 7 | As it is completely Python-based, OpenGridGym can be used on any operating system where Python 3 can be used, including Windows/Mac/Linux. It can also be run online using Google COLAB. 8 | 9 | 10 | 11 | It is currently in its initial stages. Feedback is welcomed, and the developers plan to continue to upload more examples, similar to the ones described in the paper. 12 | 13 | 14 | 15 | ### Getting Started 16 | 17 | 1. Please clone this repository to a local directory. 18 | 2. Make sure you have already installed all Python packages listed in `requirements.txt`. The most notable is `dss-python` (see [here](https://github.com/dss-extensions/dss_python) for more), which is used to interface with OpenDSS from Python on any operating system. 19 | 3. In the `Examples` folder, you will find a quick tutorial in `demos.ipynb`. Make sure to run the notebook section by section first to ensure no installation errors are encountered. 20 | 21 | 22 | 23 | ### Example usage 24 | 25 | Once you have created your own (or imported existing) grid, market and agent classes, you may run an environment episode as follows: 26 | 27 | 1. **Instantiate the objects** 28 | 29 | ```python 30 | grid = CustomGrid(...) 31 | 32 | market = CustomMaraket(...) 33 | 34 | agents = [CustomAgent(...), ..., CustomAgent(...)] 35 | 36 | env = Environment(grid=grid, market=market, agent=agents) 37 | ``` 38 | 39 | 2. **Run the environment** 40 | 41 | ```python 42 | env.reset() 43 | for t in env.iterate(): 44 | ... # customize as needed (e.g. observe measurements) 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /dss_grid_examples/34Bus/IEEE34_BusXY.csv: -------------------------------------------------------------------------------- 1 | SourceBus,-300,0 2 | 800,0,0 3 | 802,400,0 4 | 806,700,0 5 | 808,1000,0 6 | 810,1000,-800 7 | 812,1300,0 8 | 814,1600,0 9 | 850,2500,0 10 | 816,2900,0 11 | 818,2900,400 12 | 820,2900,800 13 | 822,2900,1100 14 | 824,3200,0 15 | 826,3500,0 16 | 828,3200,-1200 17 | 830,3700,-1200 18 | 854,4000,-1200 19 | 856,4400,-1200 20 | 852,4000,-900 21 | 832,4000,-400 22 | 888,4700,-400 23 | 890,5000,-400 24 | 858,4000,0 25 | 864,4000,400 26 | 834,4400,0 27 | 842,4400,400 28 | 844,4400,700 29 | 846,4400,1100 30 | 848,4400,1400 31 | 860,5000,0 32 | 836,5600,0 33 | 840,6000,0 34 | 862,5600,-400 35 | 838,5600,-800 36 | 814r,2100,0 37 | 852r,4000,-600 38 | -------------------------------------------------------------------------------- /dss_grid_examples/34Bus/IEEELineCodes.DSS: -------------------------------------------------------------------------------- 1 | ! this file was corrected 9/16/2010 to match the values in Kersting's files 2 | 3 | 4 | 5 | ! These line codes are used in the 123-bus circuit 6 | 7 | New linecode.1 nphases=3 BaseFreq=60 8 | !!!~ rmatrix = (0.088205 | 0.0312137 0.0901946 | 0.0306264 0.0316143 0.0889665 ) 9 | !!!~ xmatrix = (0.20744 | 0.0935314 0.200783 | 0.0760312 0.0855879 0.204877 ) 10 | !!!~ cmatrix = (2.90301 | -0.679335 3.15896 | -0.22313 -0.481416 2.8965 ) 11 | ~ rmatrix = [0.086666667 | 0.029545455 0.088371212 | 0.02907197 0.029924242 0.087405303] 12 | ~ xmatrix = [0.204166667 | 0.095018939 0.198522727 | 0.072897727 0.080227273 0.201723485] 13 | ~ cmatrix = [2.851710072 | -0.920293787 3.004631862 | -0.350755566 -0.585011253 2.71134756] 14 | 15 | New linecode.2 nphases=3 BaseFreq=60 16 | !!!~ rmatrix = (0.0901946 | 0.0316143 0.0889665 | 0.0312137 0.0306264 0.088205 ) 17 | !!!~ xmatrix = (0.200783 | 0.0855879 0.204877 | 0.0935314 0.0760312 0.20744 ) 18 | !!!~ cmatrix = (3.15896 | -0.481416 2.8965 | -0.679335 -0.22313 2.90301 ) 19 | ~ rmatrix = [0.088371212 | 0.02992424 0.087405303 | 0.029545455 0.02907197 0.086666667] 20 | ~ xmatrix = [0.198522727 | 0.080227273 0.201723485 | 0.095018939 0.072897727 0.204166667] 21 | ~ cmatrix = [3.004631862 | -0.585011253 2.71134756 | -0.920293787 -0.350755566 2.851710072] 22 | 23 | New linecode.3 nphases=3 BaseFreq=60 24 | !!!~ rmatrix = (0.0889665 | 0.0306264 0.088205 | 0.0316143 0.0312137 0.0901946 ) 25 | !!!~ xmatrix = (0.204877 | 0.0760312 0.20744 | 0.0855879 0.0935314 0.200783 ) 26 | !!!~ cmatrix = (2.8965 | -0.22313 2.90301 | -0.481416 -0.679335 3.15896 ) 27 | 28 | ~ rmatrix = [0.087405303 | 0.02907197 0.086666667 | 0.029924242 0.029545455 0.088371212] 29 | ~ xmatrix = [0.201723485 | 0.072897727 0.204166667 | 0.080227273 0.095018939 0.198522727] 30 | ~ cmatrix = [2.71134756 | -0.350755566 2.851710072 | -0.585011253 -0.920293787 3.004631862] 31 | 32 | New linecode.4 nphases=3 BaseFreq=60 33 | !!!~ rmatrix = (0.0889665 | 0.0316143 0.0901946 | 0.0306264 0.0312137 0.088205 ) 34 | !!!~ xmatrix = (0.204877 | 0.0855879 0.200783 | 0.0760312 0.0935314 0.20744 ) 35 | !!!~ cmatrix = (2.8965 | -0.481416 3.15896 | -0.22313 -0.679335 2.90301 ) 36 | ~ rmatrix = [0.087405303 | 0.029924242 0.088371212 | 0.02907197 0.029545455 0.086666667] 37 | ~ xmatrix = [0.201723485 | 0.080227273 0.198522727 | 0.072897727 0.095018939 0.204166667] 38 | ~ cmatrix = [2.71134756 | 0.585011253 3.004631862 | -0.350755566 -0.920293787 2.851710072] 39 | 40 | New linecode.5 nphases=3 BaseFreq=60 41 | !!!~ rmatrix = (0.0901946 | 0.0312137 0.088205 | 0.0316143 0.0306264 0.0889665 ) 42 | !!!~ xmatrix = (0.200783 | 0.0935314 0.20744 | 0.0855879 0.0760312 0.204877 ) 43 | !!!~ cmatrix = (3.15896 | -0.679335 2.90301 | -0.481416 -0.22313 2.8965 ) 44 | 45 | ~ rmatrix = [0.088371212 | 0.029545455 0.086666667 | 0.029924242 0.02907197 0.087405303] 46 | ~ xmatrix = [0.198522727 | 0.095018939 0.204166667 | 0.080227273 0.072897727 0.201723485] 47 | ~ cmatrix = [3.004631862 | -0.920293787 2.851710072 | -0.585011253 -0.350755566 2.71134756] 48 | 49 | New linecode.6 nphases=3 BaseFreq=60 50 | !!!~ rmatrix = (0.088205 | 0.0306264 0.0889665 | 0.0312137 0.0316143 0.0901946 ) 51 | !!!~ xmatrix = (0.20744 | 0.0760312 0.204877 | 0.0935314 0.0855879 0.200783 ) 52 | !!!~ cmatrix = (2.90301 | -0.22313 2.8965 | -0.679335 -0.481416 3.15896 ) 53 | ~ rmatrix = [0.086666667 | 0.02907197 0.087405303 | 0.029545455 0.029924242 0.088371212] 54 | ~ xmatrix = [0.204166667 | 0.072897727 0.201723485 | 0.095018939 0.080227273 0.198522727] 55 | ~ cmatrix = [2.851710072 | -0.350755566 2.71134756 | -0.920293787 -0.585011253 3.004631862] 56 | New linecode.7 nphases=2 BaseFreq=60 57 | !!!~ rmatrix = (0.088205 | 0.0306264 0.0889665 ) 58 | !!!~ xmatrix = (0.20744 | 0.0760312 0.204877 ) 59 | !!!~ cmatrix = (2.75692 | -0.326659 2.82313 ) 60 | ~ rmatrix = [0.086666667 | 0.02907197 0.087405303] 61 | ~ xmatrix = [0.204166667 | 0.072897727 0.201723485] 62 | ~ cmatrix = [2.569829596 | -0.52995137 2.597460011] 63 | New linecode.8 nphases=2 BaseFreq=60 64 | !!!~ rmatrix = (0.088205 | 0.0306264 0.0889665 ) 65 | !!!~ xmatrix = (0.20744 | 0.0760312 0.204877 ) 66 | !!!~ cmatrix = (2.75692 | -0.326659 2.82313 ) 67 | ~ rmatrix = [0.086666667 | 0.02907197 0.087405303] 68 | ~ xmatrix = [0.204166667 | 0.072897727 0.201723485] 69 | ~ cmatrix = [2.569829596 | -0.52995137 2.597460011] 70 | New linecode.9 nphases=1 BaseFreq=60 71 | !!!~ rmatrix = (0.254428 ) 72 | !!!~ xmatrix = (0.259546 ) 73 | !!!~ cmatrix = (2.50575 ) 74 | ~ rmatrix = [0.251742424] 75 | ~ xmatrix = [0.255208333] 76 | ~ cmatrix = [2.270366128] 77 | New linecode.10 nphases=1 BaseFreq=60 78 | !!!~ rmatrix = (0.254428 ) 79 | !!!~ xmatrix = (0.259546 ) 80 | !!!~ cmatrix = (2.50575 ) 81 | ~ rmatrix = [0.251742424] 82 | ~ xmatrix = [0.255208333] 83 | ~ cmatrix = [2.270366128] 84 | New linecode.11 nphases=1 BaseFreq=60 85 | !!!~ rmatrix = (0.254428 ) 86 | !!!~ xmatrix = (0.259546 ) 87 | !!!~ cmatrix = (2.50575 ) 88 | ~ rmatrix = [0.251742424] 89 | ~ xmatrix = [0.255208333] 90 | ~ cmatrix = [2.270366128] 91 | New linecode.12 nphases=3 BaseFreq=60 92 | !!!~ rmatrix = (0.291814 | 0.101656 0.294012 | 0.096494 0.101656 0.291814 ) 93 | !!!~ xmatrix = (0.141848 | 0.0517936 0.13483 | 0.0401881 0.0517936 0.141848 ) 94 | !!!~ cmatrix = (53.4924 | 0 53.4924 | 0 0 53.4924 ) 95 | ~ rmatrix = [0.288049242 | 0.09844697 0.29032197 | 0.093257576 0.09844697 0.288049242] 96 | ~ xmatrix = [0.142443182 | 0.052556818 0.135643939 | 0.040852273 0.052556818 0.142443182] 97 | ~ cmatrix = [33.77150149 | 0 33.77150149 | 0 0 33.77150149] 98 | 99 | ! These line codes are used in the 34-node test feeder 100 | 101 | New linecode.300 nphases=3 basefreq=60 units=kft ! ohms per 1000ft Corrected 11/30/05 102 | ~ rmatrix = [0.253181818 | 0.039791667 0.250719697 | 0.040340909 0.039128788 0.251780303] !ABC ORDER 103 | ~ xmatrix = [0.252708333 | 0.109450758 0.256988636 | 0.094981061 0.086950758 0.255132576] 104 | ~ CMATRIX = [2.680150309 | -0.769281006 2.5610381 | -0.499507676 -0.312072984 2.455590387] 105 | New linecode.301 nphases=3 basefreq=60 units=kft 106 | ~ rmatrix = [0.365530303 | 0.04407197 0.36282197 | 0.04467803 0.043333333 0.363996212] 107 | ~ xmatrix = [0.267329545 | 0.122007576 0.270473485 | 0.107784091 0.099204545 0.269109848] 108 | ~ cmatrix = [2.572492163 | -0.72160598 2.464381882 | -0.472329395 -0.298961096 2.368881119] 109 | New linecode.302 nphases=1 basefreq=60 units=kft 110 | ~ rmatrix = (0.530208 ) 111 | ~ xmatrix = (0.281345 ) 112 | ~ cmatrix = (2.12257 ) 113 | New linecode.303 nphases=1 basefreq=60 units=kft 114 | ~ rmatrix = (0.530208 ) 115 | ~ xmatrix = (0.281345 ) 116 | ~ cmatrix = (2.12257 ) 117 | New linecode.304 nphases=1 basefreq=60 units=kft 118 | ~ rmatrix = (0.363958 ) 119 | ~ xmatrix = (0.269167 ) 120 | ~ cmatrix = (2.1922 ) 121 | 122 | 123 | ! This may be for the 4-node test feeder, but is not actually referenced. 124 | ! instead, the 4Bus*.dss files all use the wiredata and linegeometry inputs 125 | ! to calculate these matrices from physical data. 126 | 127 | New linecode.400 nphases=3 BaseFreq=60 128 | ~ rmatrix = (0.088205 | 0.0312137 0.0901946 | 0.0306264 0.0316143 0.0889665 ) 129 | ~ xmatrix = (0.20744 | 0.0935314 0.200783 | 0.0760312 0.0855879 0.204877 ) 130 | ~ cmatrix = (2.90301 | -0.679335 3.15896 | -0.22313 -0.481416 2.8965 ) 131 | 132 | ! These are for the 13-node test feeder 133 | 134 | New linecode.601 nphases=3 BaseFreq=60 135 | !!!~ rmatrix = (0.0674673 | 0.0312137 0.0654777 | 0.0316143 0.0306264 0.0662392 ) 136 | !!!~ xmatrix = (0.195204 | 0.0935314 0.201861 | 0.0855879 0.0760312 0.199298 ) 137 | !!!~ cmatrix = (3.32591 | -0.743055 3.04217 | -0.525237 -0.238111 3.03116 ) 138 | ~ rmatrix = [0.065625 | 0.029545455 0.063920455 | 0.029924242 0.02907197 0.064659091] 139 | ~ xmatrix = [0.192784091 | 0.095018939 0.19844697 | 0.080227273 0.072897727 0.195984848] 140 | ~ cmatrix = [3.164838036 | -1.002632425 2.993981593 | -0.632736516 -0.372608713 2.832670203] 141 | New linecode.602 nphases=3 BaseFreq=60 142 | !!!~ rmatrix = (0.144361 | 0.0316143 0.143133 | 0.0312137 0.0306264 0.142372 ) 143 | !!!~ xmatrix = (0.226028 | 0.0855879 0.230122 | 0.0935314 0.0760312 0.232686 ) 144 | !!!~ cmatrix = (3.01091 | -0.443561 2.77543 | -0.624494 -0.209615 2.77847 ) 145 | ~ rmatrix = [0.142537879 | 0.029924242 0.14157197 | 0.029545455 0.02907197 0.140833333] 146 | ~ xmatrix = [0.22375 | 0.080227273 0.226950758 | 0.095018939 0.072897727 0.229393939] 147 | ~ cmatrix = [2.863013423 | -0.543414918 2.602031589 | -0.8492585 -0.330962141 2.725162768] 148 | New linecode.603 nphases=2 BaseFreq=60 149 | !!!~ rmatrix = (0.254472 | 0.0417943 0.253371 ) 150 | !!!~ xmatrix = (0.259467 | 0.0912376 0.261431 ) 151 | !!!~ cmatrix = (2.54676 | -0.28882 2.49502 ) 152 | ~ rmatrix = [0.251780303 | 0.039128788 0.250719697] 153 | ~ xmatrix = [0.255132576 | 0.086950758 0.256988636] 154 | ~ cmatrix = [2.366017603 | -0.452083836 2.343963508] 155 | New linecode.604 nphases=2 BaseFreq=60 156 | !!!~ rmatrix = (0.253371 | 0.0417943 0.254472 ) 157 | !!!~ xmatrix = (0.261431 | 0.0912376 0.259467 ) 158 | !!!~ cmatrix = (2.49502 | -0.28882 2.54676 ) 159 | ~ rmatrix = [0.250719697 | 0.039128788 0.251780303] 160 | ~ xmatrix = [0.256988636 | 0.086950758 0.255132576] 161 | ~ cmatrix = [2.343963508 | -0.452083836 2.366017603] 162 | New linecode.605 nphases=1 BaseFreq=60 163 | !!!~ rmatrix = (0.254428 ) 164 | !!!~ xmatrix = (0.259546 ) 165 | !!!~ cmatrix = (2.50575 ) 166 | ~ rmatrix = [0.251742424] 167 | ~ xmatrix = [0.255208333] 168 | ~ cmatrix = [2.270366128] 169 | New linecode.606 nphases=3 BaseFreq=60 170 | !!!~ rmatrix = (0.152193 | 0.0611362 0.15035 | 0.0546992 0.0611362 0.152193 ) 171 | !!!~ xmatrix = (0.0825685 | 0.00548281 0.0745027 | -0.00339824 0.00548281 0.0825685 ) 172 | !!!~ cmatrix = (72.7203 | 0 72.7203 | 0 0 72.7203 ) 173 | ~ rmatrix = [0.151174242 | 0.060454545 0.149450758 | 0.053958333 0.060454545 0.151174242] 174 | ~ xmatrix = [0.084526515 | 0.006212121 0.076534091 | -0.002708333 0.006212121 0.084526515] 175 | ~ cmatrix = [48.67459408 | 0 48.67459408 | 0 0 48.67459408] 176 | New linecode.607 nphases=1 BaseFreq=60 177 | !!!~ rmatrix = (0.255799 ) 178 | !!!~ xmatrix = (0.092284 ) 179 | !!!~ cmatrix = (50.7067 ) 180 | ~ rmatrix = [0.254261364] 181 | ~ xmatrix = [0.097045455] 182 | ~ cmatrix = [44.70661522] 183 | 184 | ! These are for the 37-node test feeder, all underground 185 | 186 | New linecode.721 nphases=3 BaseFreq=60 187 | !!!~ rmatrix = (0.0554906 | 0.0127467 0.0501597 | 0.00640446 0.0127467 0.0554906 ) 188 | !!!~ xmatrix = (0.0372331 | -0.00704588 0.0358645 | -0.00796424 -0.00704588 0.0372331 ) 189 | !!!~ cmatrix = (124.851 | 0 124.851 | 0 0 124.851 ) 190 | ~ rmatrix = [0.055416667 | 0.012746212 0.050113636 | 0.006382576 0.012746212 0.055416667] 191 | ~ xmatrix = [0.037367424 | -0.006969697 0.035984848 | -0.007897727 -0.006969697 0.037367424] 192 | ~ cmatrix = [80.27484728 | 0 80.27484728 | 0 0 80.27484728] 193 | New linecode.722 nphases=3 BaseFreq=60 194 | !!!~ rmatrix = (0.0902251 | 0.0309584 0.0851482 | 0.0234946 0.0309584 0.0902251 ) 195 | !!!~ xmatrix = (0.055991 | -0.00646552 0.0504025 | -0.0117669 -0.00646552 0.055991 ) 196 | !!!~ cmatrix = (93.4896 | 0 93.4896 | 0 0 93.4896 ) 197 | ~ rmatrix = [0.089981061 | 0.030852273 0.085 | 0.023371212 0.030852273 0.089981061] 198 | ~ xmatrix = [0.056306818 | -0.006174242 0.050719697 | -0.011496212 -0.006174242 0.056306818] 199 | ~ cmatrix = [64.2184109 | 0 64.2184109 | 0 0 64.2184109] 200 | New linecode.723 nphases=3 BaseFreq=60 201 | !!!~ rmatrix = (0.247572 | 0.0947678 0.249104 | 0.0893782 0.0947678 0.247572 ) 202 | !!!~ xmatrix = (0.126339 | 0.0390337 0.118816 | 0.0279344 0.0390337 0.126339 ) 203 | !!!~ cmatrix = (58.108 | 0 58.108 | 0 0 58.108 ) 204 | ~ rmatrix = [0.245 | 0.092253788 0.246628788 | 0.086837121 0.092253788 0.245] 205 | ~ xmatrix = [0.127140152 | 0.039981061 0.119810606 | 0.028806818 0.039981061 0.127140152] 206 | ~ cmatrix = [37.5977112 | 0 37.5977112 | 0 0 37.5977112] 207 | New linecode.724 nphases=3 BaseFreq=60 208 | !!!~ rmatrix = (0.399883 | 0.101765 0.402011 | 0.0965199 0.101765 0.399883 ) 209 | !!!~ xmatrix = (0.146325 | 0.0510963 0.139305 | 0.0395402 0.0510963 0.146325 ) 210 | !!!~ cmatrix = (46.9685 | 0 46.9685 | 0 0 46.9685 ) 211 | ~ rmatrix = [0.396818182 | 0.098560606 0.399015152 | 0.093295455 0.098560606 0.396818182] 212 | ~ xmatrix = [0.146931818 | 0.051856061 0.140113636 | 0.040208333 0.051856061 0.146931818] 213 | ~ cmatrix = [30.26701029 | 0 30.26701029 | 0 0 30.26701029] 214 | -------------------------------------------------------------------------------- /dss_grid_examples/34Bus/README.md: -------------------------------------------------------------------------------- 1 | Contents of this folder are taken directly from OpenDSS' IEEE test cases. 2 | 3 | 4 | 5 | For more details about OpenDSS, please refer to the source below: 6 | 7 | *R. C. Dugan and D. Montenegro, “The Open Distribution System Simulator™(OpenDSS), Reference Guide,” Electric Power Research Institute (EPRI), 2018.* -------------------------------------------------------------------------------- /dss_grid_examples/34Bus/ieee34Mod1.dss: -------------------------------------------------------------------------------- 1 | ! Standard (Mod 1) model of IEEE 34 Bus Test Feeder 2 | 3 | ! Note: Mod 2 better accounts for distributed load. 4 | 5 | Clear 6 | 7 | New object=circuit.ieee34-1 8 | ~ basekv=69 pu=1.05 angle=30 mvasc3=200000 !stiffen up a bit over DSS default 9 | 10 | ! Substation Transformer -- Modification: Make source very stiff by defining a tiny leakage Z 11 | New Transformer.SubXF Phases=3 Windings=2 Xhl=0.01 ! normally 8 12 | ~ wdg=1 bus=sourcebus conn=Delta kv=69 kva=25000 %r=0.0005 !reduce %r, too 13 | ~ wdg=2 bus=800 conn=wye kv=24.9 kva=25000 %r=0.0005 14 | 15 | ! import line codes with phase impedance matrices 16 | Redirect IEEELineCodes.dss ! revised according to Later test feeder doc 17 | 18 | ! Lines 19 | New Line.L1 Phases=3 Bus1=800.1.2.3 Bus2=802.1.2.3 LineCode=300 Length=2.58 units=kft 20 | New Line.L2 Phases=3 Bus1=802.1.2.3 Bus2=806.1.2.3 LineCode=300 Length=1.73 units=kft 21 | New Line.L3 Phases=3 Bus1=806.1.2.3 Bus2=808.1.2.3 LineCode=300 Length=32.23 units=kft 22 | New Line.L4 Phases=1 Bus1=808.2 Bus2=810.2 LineCode=303 Length=5.804 units=kft 23 | New Line.L5 Phases=3 Bus1=808.1.2.3 Bus2=812.1.2.3 LineCode=300 Length=37.5 units=kft 24 | New Line.L6 Phases=3 Bus1=812.1.2.3 Bus2=814.1.2.3 LineCode=300 Length=29.73 units=kft 25 | New Line.L7 Phases=3 Bus1=814r.1.2.3 Bus2=850.1.2.3 LineCode=301 Length=0.01 units=kft 26 | New Line.L8 Phases=1 Bus1=816.1 Bus2=818.1 LineCode=302 Length=1.71 units=kft 27 | New Line.L9 Phases=3 Bus1=816.1.2.3 Bus2=824.1.2.3 LineCode=301 Length=10.21 units=kft 28 | New Line.L10 Phases=1 Bus1=818.1 Bus2=820.1 LineCode=302 Length=48.15 units=kft 29 | New Line.L11 Phases=1 Bus1=820.1 Bus2=822.1 LineCode=302 Length=13.74 units=kft 30 | New Line.L12 Phases=1 Bus1=824.2 Bus2=826.2 LineCode=303 Length=3.03 units=kft 31 | New Line.L13 Phases=3 Bus1=824.1.2.3 Bus2=828.1.2.3 LineCode=301 Length=0.84 units=kft 32 | New Line.L14 Phases=3 Bus1=828.1.2.3 Bus2=830.1.2.3 LineCode=301 Length=20.44 units=kft 33 | New Line.L15 Phases=3 Bus1=830.1.2.3 Bus2=854.1.2.3 LineCode=301 Length=0.52 units=kft 34 | New Line.L16 Phases=3 Bus1=832.1.2.3 Bus2=858.1.2.3 LineCode=301 Length=4.9 units=kft 35 | New Line.L17 Phases=3 Bus1=834.1.2.3 Bus2=860.1.2.3 LineCode=301 Length=2.02 units=kft 36 | New Line.L18 Phases=3 Bus1=834.1.2.3 Bus2=842.1.2.3 LineCode=301 Length=0.28 units=kft 37 | New Line.L19 Phases=3 Bus1=836.1.2.3 Bus2=840.1.2.3 LineCode=301 Length=0.86 units=kft 38 | New Line.L20 Phases=3 Bus1=836.1.2.3 Bus2=862.1.2.3 LineCode=301 Length=0.28 units=kft 39 | New Line.L21 Phases=3 Bus1=842.1.2.3 Bus2=844.1.2.3 LineCode=301 Length=1.35 units=kft 40 | New Line.L22 Phases=3 Bus1=844.1.2.3 Bus2=846.1.2.3 LineCode=301 Length=3.64 units=kft 41 | New Line.L23 Phases=3 Bus1=846.1.2.3 Bus2=848.1.2.3 LineCode=301 Length=0.53 units=kft 42 | New Line.L24 Phases=3 Bus1=850.1.2.3 Bus2=816.1.2.3 LineCode=301 Length=0.31 units=kft 43 | New Line.L25 Phases=3 Bus1=852r.1.2.3 Bus2=832.1.2.3 LineCode=301 Length=0.01 units=kft 44 | 45 | ! 24.9/4.16 kV Transformer 46 | New Transformer.XFM1 Phases=3 Windings=2 Xhl=4.08 47 | ~ wdg=1 bus=832 conn=wye kv=24.9 kva=500 %r=0.95 48 | ~ wdg=2 bus=888 conn=Wye kv=4.16 kva=500 %r=0.95 49 | 50 | New Line.L26 Phases=1 Bus1=854.2 Bus2=856.2 LineCode=303 Length=23.33 units=kft 51 | New Line.L27 Phases=3 Bus1=854.1.2.3 Bus2=852.1.2.3 LineCode=301 Length=36.83 units=kft 52 | ! 9-17-10 858-864 changed to phase A per error report 53 | New Line.L28 Phases=1 Bus1=858.1 Bus2=864.1 LineCode=303 Length=1.62 units=kft 54 | New Line.L29 Phases=3 Bus1=858.1.2.3 Bus2=834.1.2.3 LineCode=301 Length=5.83 units=kft 55 | New Line.L30 Phases=3 Bus1=860.1.2.3 Bus2=836.1.2.3 LineCode=301 Length=2.68 units=kft 56 | New Line.L31 Phases=1 Bus1=862.2 Bus2=838.2 LineCode=304 Length=4.86 units=kft 57 | New Line.L32 Phases=3 Bus1=888.1.2.3 Bus2=890.1.2.3 LineCode=300 Length=10.56 units=kft 58 | 59 | ! Capacitors 60 | New Capacitor.C844 Bus1=844 Phases=3 kVAR=300 kV=24.9 61 | New Capacitor.C848 Bus1=848 Phases=3 kVAR=450 kV=24.9 62 | 63 | ! Regulators - three independent phases 64 | ! Regulator 1 65 | new transformer.reg1a phases=1 windings=2 buses=(814.1 814r.1) conns='wye wye' kvs="14.376 14.376" kvas="20000 20000" XHL=1 66 | new regcontrol.creg1a transformer=reg1a winding=2 vreg=122 band=2 ptratio=120 ctprim=100 R=2.7 X=1.6 67 | new transformer.reg1b phases=1 windings=2 buses=(814.2 814r.2) conns='wye wye' kvs="14.376 14.376" kvas="20000 20000" XHL=1 68 | new regcontrol.creg1b transformer=reg1b winding=2 vreg=122 band=2 ptratio=120 ctprim=100 R=2.7 X=1.6 69 | new transformer.reg1c phases=1 windings=2 buses=(814.3 814r.3) conns='wye wye' kvs="14.376 14.376" kvas="20000 20000" XHL=1 70 | new regcontrol.creg1c transformer=reg1c winding=2 vreg=122 band=2 ptratio=120 ctprim=100 R=2.7 X=1.6 71 | 72 | ! Regulator 2 73 | new transformer.reg2a phases=1 windings=2 buses=(852.1 852r.1) conns='wye wye' kvs="14.376 14.376" kvas="20000 20000" XHL=1 74 | new regcontrol.creg2a transformer=reg2a winding=2 vreg=124 band=2 ptratio=120 ctprim=100 R=2.5 X=1.5 75 | new transformer.reg2b phases=1 windings=2 buses=(852.2 852r.2) conns='wye wye' kvs="14.376 14.376" kvas="20000 20000" XHL=1 76 | new regcontrol.creg2b transformer=reg2b winding=2 vreg=124 band=2 ptratio=120 ctprim=100 R=2.5 X=1.5 77 | new transformer.reg2c phases=1 windings=2 buses=(852.3 852r.3) conns='wye wye' kvs="14.376 14.376" kvas="20000 20000" XHL=1 78 | new regcontrol.creg2c transformer=reg2c winding=2 vreg=124 band=2 ptratio=120 ctprim=100 R=2.5 X=1.5 79 | 80 | ! spot loads 81 | New Load.S860 Bus1=860 Phases=3 Conn=Wye Model=1 kV= 24.900 kW= 60.0 kVAR= 48.0 82 | New Load.S840 Bus1=840 Phases=3 Conn=Wye Model=5 kV= 24.900 kW= 27.0 kVAR= 21.0 83 | New Load.S844 Bus1=844 Phases=3 Conn=Wye Model=2 kV= 24.900 kW= 405.0 kVAR= 315.0 84 | 85 | New Load.S848 Bus1=848 Phases=3 Conn=Delta Model=1 kV= 24.900 kW= 60.0 kVAR= 48.0 86 | New Load.S830a Bus1=830.1.2 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 10.0 kVAR= 5.0 87 | New Load.S830b Bus1=830.2.3 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 10.0 kVAR= 5.0 88 | New Load.S830c Bus1=830.3.1 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 25.0 kVAR= 10.0 89 | New Load.S890 Bus1=890 Phases=3 Conn=Delta Model=5 kV= 4.160 kW= 450.0 kVAR= 225.0 90 | 91 | ! distributed loads 92 | New Load.D802_806sb Bus1=802.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 15.0 kVAR= 7.5 93 | New Load.D802_806rb Bus1=806.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 15.0 kVAR= 7.5 94 | New Load.D802_806sc Bus1=802.3 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 12.5 kVAR= 7.0 95 | New Load.D802_806rc Bus1=806.3 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 12.5 kVAR= 7.0 96 | 97 | New Load.D808_810sb Bus1=808.2 Phases=1 Conn=Wye Model=4 kV= 14.376 kW= 8.0 kVAR= 4.0 98 | New Load.D808_810rb Bus1=810.2 Phases=1 Conn=Wye Model=4 kV= 14.376 kW= 8.0 kVAR= 4.0 99 | 100 | New Load.D818_820sa Bus1=818.1 Phases=1 Conn=Wye Model=2 kV= 14.376 kW= 17.0 kVAR= 8.5 101 | New Load.D818_820ra Bus1=820.1 Phases=1 Conn=Wye Model=2 kV= 14.376 kW= 17.0 kVAR= 8.5 102 | 103 | New Load.D820_822sa Bus1=820.1 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 67.5 kVAR= 35.0 104 | New Load.D820_822ra Bus1=822.1 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 67.5 kVAR= 35.0 105 | 106 | New Load.D816_824sb Bus1=816.2 Phases=1 Conn=Delta Model=5 kV= 24.900 kW= 2.5 kVAR= 1.0 107 | New Load.D816_824rb Bus1=824.2 Phases=1 Conn=Delta Model=5 kV= 24.900 kW= 2.5 kVAR= 1.0 108 | 109 | New Load.D824_826sb Bus1=824.2 Phases=1 Conn=Wye Model=5 kV= 14.376 kW= 20.0 kVAR= 10.0 110 | New Load.D824_826rb Bus1=826.2 Phases=1 Conn=Wye Model=5 kV= 14.376 kW= 20.0 kVAR= 10.0 111 | New Load.D824_828sc Bus1=824.3 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 2.0 kVAR= 1.0 112 | New Load.D824_828rc Bus1=828.3 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 2.0 kVAR= 1.0 113 | 114 | New Load.D828_830sa Bus1=828.1 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 3.5 kVAR= 1.5 115 | New Load.D828_830ra Bus1=830.1 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 3.5 kVAR= 1.5 116 | 117 | New Load.D854_856sb Bus1=854.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 2.0 kVAR= 1.0 118 | New Load.D854_856rb Bus1=856.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 2.0 kVAR= 1.0 119 | 120 | New Load.D832_858sa Bus1=832.1 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 3.5 kVAR= 1.5 121 | New Load.D832_858ra Bus1=858.1 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 3.5 kVAR= 1.5 122 | New Load.D832_858sb Bus1=832.2 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 1.0 kVAR= 0.5 123 | New Load.D832_858rb Bus1=858.2 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 1.0 kVAR= 0.5 124 | New Load.D832_858sc Bus1=832.3 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 3.0 kVAR= 1.5 125 | New Load.D832_858rc Bus1=858.3 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 3.0 kVAR= 1.5 126 | 127 | ! 9-17-10 858-864 changed to phase A per error report 128 | New Load.D858_864sb Bus1=858.1 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 1.0 kVAR= 0.5 129 | New Load.D858_864rb Bus1=864.1 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 1.0 kVAR= 0.5 130 | 131 | New Load.D858_834sa Bus1=858.1.2 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 2.0 kVAR= 1.0 132 | New Load.D858_834ra Bus1=834.1.2 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 2.0 kVAR= 1.0 133 | New Load.D858_834sb Bus1=858.2.3 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 7.5 kVAR= 4.0 134 | New Load.D858_834rb Bus1=834.2.3 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 7.5 kVAR= 4.0 135 | New Load.D858_834sc Bus1=858.3.1 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 6.5 kVAR= 3.5 136 | New Load.D858_834rc Bus1=834.3.1 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 6.5 kVAR= 3.5 137 | 138 | New Load.D834_860sa Bus1=834.1.2 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 8.0 kVAR= 4.0 139 | New Load.D834_860ra Bus1=860.1.2 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 8.0 kVAR= 4.0 140 | New Load.D834_860sb Bus1=834.2.3 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 10.0 kVAR= 5.0 141 | New Load.D834_860rb Bus1=860.2.3 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 10.0 kVAR= 5.0 142 | New Load.D834_860sc Bus1=834.3.1 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 55.0 kVAR= 27.5 143 | New Load.D834_860rc Bus1=860.3.1 Phases=1 Conn=Delta Model=2 kV= 24.900 kW= 55.0 kVAR= 27.5 144 | 145 | New Load.D860_836sa Bus1=860.1.2 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 15.0 kVAR= 7.5 146 | New Load.D860_836ra Bus1=836.1.2 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 15.0 kVAR= 7.5 147 | New Load.D860_836sb Bus1=860.2.3 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 5.0 kVAR= 3.0 148 | New Load.D860_836rb Bus1=836.2.3 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 5.0 kVAR= 3.0 149 | New Load.D860_836sc Bus1=860.3.1 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 21.0 kVAR= 11.0 150 | New Load.D860_836rc Bus1=836.3.1 Phases=1 Conn=Delta Model=1 kV= 24.900 kW= 21.0 kVAR= 11.0 151 | 152 | New Load.D836_840sa Bus1=836.1.2 Phases=1 Conn=Delta Model=5 kV= 24.900 kW= 9.0 kVAR= 4.5 153 | New Load.D836_840ra Bus1=840.1.2 Phases=1 Conn=Delta Model=5 kV= 24.900 kW= 9.0 kVAR= 4.5 154 | New Load.D836_840sb Bus1=836.2.3 Phases=1 Conn=Delta Model=5 kV= 24.900 kW= 11.0 kVAR= 5.5 155 | New Load.D836_840rb Bus1=840.2.3 Phases=1 Conn=Delta Model=5 kV= 24.900 kW= 11.0 kVAR= 5.5 156 | 157 | New Load.D862_838sb Bus1=862.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 14.0 kVAR= 7.0 158 | New Load.D862_838rb Bus1=838.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 14.0 kVAR= 7.0 159 | 160 | New Load.D842_844sa Bus1=842.1 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 4.5 kVAR= 2.5 161 | New Load.D842_844ra Bus1=844.1 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 4.5 kVAR= 2.5 162 | 163 | New Load.D844_846sb Bus1=844.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 12.5 kVAR= 6.0 164 | New Load.D844_846rb Bus1=846.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 12.5 kVAR= 6.0 165 | New Load.D844_846sc Bus1=844.3 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 10.0 kVAR= 5.5 166 | New Load.D844_846rc Bus1=846.3 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 10.0 kVAR= 5.5 167 | 168 | New Load.D846_848sb Bus1=846.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 11.5 kVAR= 5.5 169 | New Load.D846_848rb Bus1=848.2 Phases=1 Conn=Wye Model=1 kV= 14.376 kW= 11.5 kVAR= 5.5 170 | 171 | ! Script to revise Vminpu property on all loads to allow voltage to sag to 85% without switching 172 | ! to constant Z model 173 | Load.s860.vminpu=.85 174 | Load.s840.vminpu=.85 175 | Load.s844.vminpu=.85 176 | Load.s848.vminpu=.85 177 | Load.s830a.vminpu=.85 178 | Load.s830b.vminpu=.85 179 | Load.s830c.vminpu=.85 180 | Load.s890.vminpu=.85 181 | Load.d802_806sb.vminpu=.85 182 | Load.d802_806rb.vminpu=.85 183 | Load.d802_806sc.vminpu=.85 184 | Load.d802_806rc.vminpu=.85 185 | Load.d808_810sb.vminpu=.85 186 | Load.d808_810rb.vminpu=.85 187 | Load.d818_820sa.vminpu=.85 188 | Load.d818_820ra.vminpu=.85 189 | Load.d820_822sa.vminpu=.85 190 | Load.d820_822ra.vminpu=.85 191 | Load.d816_824sb.vminpu=.85 192 | Load.d816_824rb.vminpu=.85 193 | Load.d824_826sb.vminpu=.85 194 | Load.d824_826rb.vminpu=.85 195 | Load.d824_828sc.vminpu=.85 196 | Load.d824_828rc.vminpu=.85 197 | Load.d828_830sa.vminpu=.85 198 | Load.d828_830ra.vminpu=.85 199 | Load.d854_856sb.vminpu=.85 200 | Load.d854_856rb.vminpu=.85 201 | Load.d832_858sa.vminpu=.85 202 | Load.d832_858ra.vminpu=.85 203 | Load.d832_858sb.vminpu=.85 204 | Load.d832_858rb.vminpu=.85 205 | Load.d832_858sc.vminpu=.85 206 | Load.d832_858rc.vminpu=.85 207 | Load.d858_864sb.vminpu=.85 208 | Load.d858_864rb.vminpu=.85 209 | Load.d858_834sa.vminpu=.85 210 | Load.d858_834ra.vminpu=.85 211 | Load.d858_834sb.vminpu=.85 212 | Load.d858_834rb.vminpu=.85 213 | Load.d858_834sc.vminpu=.85 214 | Load.d858_834rc.vminpu=.85 215 | Load.d834_860sa.vminpu=.85 216 | Load.d834_860ra.vminpu=.85 217 | Load.d834_860sb.vminpu=.85 218 | Load.d834_860rb.vminpu=.85 219 | Load.d834_860sc.vminpu=.85 220 | Load.d834_860rc.vminpu=.85 221 | Load.d860_836sa.vminpu=.85 222 | Load.d860_836ra.vminpu=.85 223 | Load.d860_836sb.vminpu=.85 224 | Load.d860_836rb.vminpu=.85 225 | Load.d860_836sc.vminpu=.85 226 | Load.d860_836rc.vminpu=.85 227 | Load.d836_840sa.vminpu=.85 228 | Load.d836_840ra.vminpu=.85 229 | Load.d836_840sb.vminpu=.85 230 | Load.d836_840rb.vminpu=.85 231 | Load.d862_838sb.vminpu=.85 232 | Load.d862_838rb.vminpu=.85 233 | Load.d842_844sa.vminpu=.85 234 | Load.d842_844ra.vminpu=.85 235 | Load.d844_846sb.vminpu=.85 236 | Load.d844_846rb.vminpu=.85 237 | Load.d844_846sc.vminpu=.85 238 | Load.d844_846rc.vminpu=.85 239 | Load.d846_848sb.vminpu=.85 240 | Load.d846_848rb.vminpu=.85 241 | 242 | 243 | ! let the DSS estimate voltage bases automatically 244 | Set VoltageBases = "69,24.9,4.16, .48" 245 | CalcVoltageBases 246 | -------------------------------------------------------------------------------- /dss_tools/__pycache__/opendss_tools.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/dss_tools/__pycache__/opendss_tools.cpython-37.pyc -------------------------------------------------------------------------------- /dss_tools/opendss_tools.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | import numpy as np 3 | import pandas as pd 4 | import matplotlib.pyplot as plt 5 | from datetime import datetime 6 | from dss import DSS as dssObj 7 | from ipywidgets import interact, Dropdown 8 | from scipy.sparse import csr_matrix 9 | import functools 10 | 11 | def get_measurable_fields(obj): 12 | ''' 13 | Returns list of fields accessible in active item in OpenDSS. 14 | For example, obj could be the active circuit element. 15 | 16 | Example: 17 | 18 | >>> get_measurable_fields(dss.Elem) 19 | ['AllPropertyNames', 'AllVariableNames', ..., 20 | 'VoltagesMagAng', 'Yprim'] 21 | 22 | ''' 23 | fields = [] 24 | for attr in dir(obj): 25 | value = getattr(obj, attr) 26 | if not callable(value) and not attr.startswith('_'): 27 | fields.append(attr) 28 | 29 | return fields 30 | 31 | def case_updated(instance_method): 32 | ''' 33 | A decorator for methods to trigger when a dss case 34 | gets updated. 35 | 36 | ''' 37 | @functools.wraps(instance_method) # to preserve method signature 38 | def new_instance_method(self, *a, **kw): 39 | output = instance_method(self, *a, **kw) 40 | 41 | self.needs_saving = True 42 | 43 | return output 44 | 45 | return new_instance_method 46 | 47 | def DSS_from_docstring(commands): 48 | ''' 49 | Return a dss object corresponding to multiline OpenDSS commands 50 | provided in the form of a docstring. 51 | 52 | commands: str 53 | Docstring containing all the OpenDSS commands to be run as if 54 | it were a .dss file. 55 | 56 | ''' 57 | with open('temp.dss', 'w') as file: 58 | file.write(commands) 59 | 60 | dss = DSS(file.name) 61 | 62 | os.remove(file.name) 63 | 64 | return dss 65 | 66 | def DSS_from_empty(circuit_name='default_circuit', basekv=115.0): 67 | ''' 68 | Return a dss object corresponding to an empty circuit with default 69 | parameters and no elements (other than default Vsource). 70 | 71 | circuit_name: str 72 | Circuit name used when creating empty circuit, without spaces. 73 | 74 | basekv: float 75 | Circuit's base voltage in kV. 76 | 77 | ''' 78 | assert ' ' not in circuit_name, 'circuit_name cannot contain spaces.' 79 | 80 | commands = f''' 81 | 82 | Clear 83 | 84 | New Circuit.{circuit_name} basekv={basekv} 85 | 86 | Set VoltageBases=[{basekv}] 87 | CalcVoltageBases 88 | 89 | Solve 90 | ''' 91 | 92 | return DSS_from_docstring(commands) 93 | 94 | class DSS: 95 | ''' 96 | A class to interface with OpenDSS. 97 | 98 | case_path: str 99 | Path to '.dss' case file. If name of file is provided without 100 | full path, current directory is assumed. 101 | 102 | Example: 103 | 104 | >>> case_path = 'some_case.dss' 105 | >>> dss = DSS(case_path) 106 | 107 | ''' 108 | def __init__(self, case_path): 109 | self.case_path = case_path 110 | self._check_case_path() 111 | 112 | starting_directory = os.getcwd() 113 | try: 114 | self._initialize_OpenDSS() 115 | self._compile_case() 116 | self._set_data_path(starting_directory) 117 | self.solve_case() 118 | self.update_all_element_names() 119 | self._avoid_exception_15013() 120 | os.chdir(starting_directory) 121 | except: 122 | # In case above fails, you don't want OpenDSS to send the 123 | # current working directory to your Documents folder. 124 | # Instead, you bring it back to where it was previously. 125 | os.chdir(starting_directory) 126 | raise NotImplementedError('Could not initialize OpenDSS case or '\ 127 | 'could not compile or solve. Needs further review.') 128 | 129 | def __repr__(self): 130 | NumBuses = self.Circuit.NumBuses 131 | NumNodes = self.Circuit.NumNodes 132 | return f'DSS(NumBuses={NumBuses}, NumNodes={NumNodes})' 133 | 134 | @classmethod 135 | def new_case_from_docstring(cls, commands): 136 | ''' 137 | Return a dss object corresponding to multiline OpenDSS commands 138 | provided in the form of a docstring. 139 | 140 | commands: str 141 | Docstring containing all the OpenDSS commands to be run as if 142 | it were a .dss file. 143 | 144 | ''' 145 | # Ensure all is first cleared 146 | commands = 'Clear\n' + commands 147 | 148 | with open('temp.dss', 'w') as file: 149 | file.write(commands) 150 | 151 | dss = cls.open_case_from_file(file.name) 152 | 153 | os.remove(file.name) 154 | 155 | return dss 156 | 157 | @classmethod 158 | def new_case_from_empty(cls, circuit_name='default_circuit', basekv=115.0): 159 | ''' 160 | Return a dss object corresponding to an empty circuit with default 161 | parameters and no elements (other than default Vsource). 162 | 163 | circuit_name: str 164 | Circuit name used when creating empty circuit, without spaces. 165 | 166 | basekv: float 167 | Circuit's base voltage in kV. 168 | 169 | ''' 170 | assert ' ' not in circuit_name, 'circuit_name cannot contain spaces.' 171 | 172 | commands = f''' 173 | 174 | Clear 175 | 176 | New Circuit.{circuit_name} basekv={basekv} 177 | 178 | Set VoltageBases=[{basekv}] 179 | CalcVoltageBases 180 | 181 | Solve 182 | ''' 183 | 184 | return cls.new_case_from_docstring(commands) 185 | 186 | @classmethod 187 | def open_case_from_file(cls, case_path): 188 | ''' 189 | Open an existing case file given path (.dss). 190 | 191 | ''' 192 | 193 | return cls(case_path) 194 | 195 | @classmethod 196 | def open_case_from_folder(cls, folder): 197 | ''' 198 | Open an existing case file given folder. 199 | This assumes Master.dss exists and is the main 200 | file in the folder. 201 | 202 | ''' 203 | master_path = os.path.join(folder, 'Master.dss') 204 | 205 | if os.path.isfile(master_path): 206 | 207 | return cls.open_case_from_file(master_path) 208 | 209 | else: 210 | 211 | raise ValueError(f'No Master.dss file found in {folder}. ' 212 | f'You may use {cls.__name__}.open_case_from_file instead.') 213 | 214 | def _check_case_path(self): 215 | ''' 216 | Ensure that self.case_path is a valid path 217 | to an OpenDSS case file. 218 | 219 | ''' 220 | # Force full path and check if it's a file 221 | self.case_path = os.path.abspath(self.case_path) 222 | if not os.path.isfile(self.case_path): 223 | raise ValueError(f'The following path does not lead to a file:\n{self.case_path}') 224 | 225 | # Check for the '.dss' extension (OpenDSS) 226 | if self.case_path[-4:] != '.dss': 227 | raise ValueError(f'Expected an OpenDSS file (.dss). Instead, got:\n{self.case_path}') 228 | 229 | def _set_data_path(self, data_path): 230 | ''' 231 | Sets data path so that OpenDSS knows where 232 | the defualt path is (e.g. for saving purposes). 233 | 234 | ''' 235 | self.Obj.DataPath = data_path 236 | if self.Obj.DataPath != data_path: 237 | return ValueError(f''' 238 | Requested the following as new data path: 239 | {data_path} 240 | but the following is still the data path: 241 | {self.Obj.DataPath} 242 | ''') 243 | 244 | def _initialize_OpenDSS(self): 245 | ''' 246 | Initialize useful interfaces with OpenDSS. 247 | 248 | ''' 249 | self.Obj = dssObj 250 | self.Text = self.Obj.Text 251 | self.Circuit = self.Obj.ActiveCircuit 252 | self.Solution = self.Circuit.Solution 253 | self.Elem = self.Circuit.ActiveCktElement 254 | self.Bus = self.Circuit.ActiveBus 255 | 256 | def _compile_case(self): 257 | ''' 258 | Instruct OpenDSS to compile case given in self.case_path. 259 | 260 | ''' 261 | result = self.command(f"compile [{self.case_path}]") 262 | 263 | self.NumBuses = self.Circuit.NumBuses 264 | self.NumNodes = self.Circuit.NumNodes 265 | 266 | self._create_node_to_bus_matrix() 267 | 268 | return result or None 269 | 270 | def solve_case(self): 271 | ''' 272 | Instruct OpenDSS to solve power flow. 273 | 274 | ''' 275 | # self.command('Solve') # alternative 276 | result = self.Solution.Solve() 277 | 278 | if (self.NumBuses != self.Circuit.NumBuses) or \ 279 | (self.NumNodes != self.Circuit.NumNodes): 280 | 281 | self._create_node_to_bus_matrix() 282 | 283 | self.NumBuses = self.Circuit.NumBuses 284 | self.NumNodes = self.Circuit.NumNodes 285 | 286 | return result or None 287 | 288 | def update_all_element_names(self): 289 | ''' 290 | Creates/updates self.element_names dict where 291 | each key is a type of element (e.g. 'Generator') and 292 | each value is a list of **full** element names. 293 | 294 | Example: 295 | 296 | >>> dss.update_all_element_names() 297 | >>> dss.element_names['Generator'] 298 | ['Generator.gen_1', 'Generator.gen_2'] 299 | 300 | ''' 301 | self.element_names = {} 302 | for elem_name in self.Circuit.AllElementNames: 303 | 304 | # Pull element class (e.g. Generator or Load) 305 | class_name = elem_name.split('.')[0] 306 | 307 | # If never seen this class before, initialize list 308 | if class_name not in self.element_names.keys(): 309 | self.element_names[class_name] = [] 310 | 311 | # Add element names to this class' list 312 | self.element_names[class_name].append(elem_name) 313 | 314 | def _avoid_exception_15013(self): 315 | ''' 316 | INTERNAL: Temporary solution to a problem experienced with 317 | dss_python exception: 318 | 319 | "DSSException: (#15013) Nodes are not initialized. Try solving the system first." 320 | 321 | ''' 322 | for elem_name in self.Circuit.AllElementNames: 323 | self.Circuit.SetActiveElement(elem_name) 324 | 325 | def _create_node_to_bus_matrix(self): 326 | ''' 327 | Creates a sparse matrix 'N2B' that's used to very quickly 328 | extract bus voltages from node voltages. 329 | 330 | Used internally. 331 | 332 | ''' 333 | N2B = np.zeros((self.Circuit.NumBuses, self.Circuit.NumNodes)) 334 | for i, elem in enumerate(self.iterate('Bus')): 335 | bus = elem.Name 336 | num_nodes = elem.NumNodes 337 | for j, node in enumerate(self.Circuit.AllNodeNames): 338 | 339 | bus_at_node, node_idx = node.split('.') 340 | if bus_at_node != bus: 341 | continue 342 | 343 | node_idx = int(node_idx) 344 | if num_nodes < 1: 345 | raise NotImplementedError('Unfamiliar with less than 1 node at bus.') 346 | elif num_nodes == 1: 347 | N2B[i, j] = 1.0 348 | elif num_nodes == 2: 349 | if node_idx < 1: 350 | raise NotImplementedError('') 351 | elif node_idx in (1, 2): 352 | N2B[i, j] = 1/2 353 | elif num_nodes >= 3: 354 | if node_idx < 1: 355 | raise NotImplementedError('') 356 | elif node_idx in (1, 2, 3): 357 | N2B[i, j] = 1/3 358 | 359 | self.N2B = csr_matrix(N2B) 360 | 361 | @case_updated 362 | def _new_element(self, class_name, elem_name, **prop_kwargs): 363 | ''' 364 | A helper method used to create new elements to avoid 365 | repeating redundant code. 366 | 367 | ''' 368 | command = f'New {class_name}.{elem_name}' 369 | for prop, val in prop_kwargs.items(): 370 | command += f' {prop}={val}' 371 | 372 | self.command(command) 373 | self.solve_case() 374 | self.update_all_element_names() 375 | 376 | def new_load(self, load_name, bus, **prop_kwargs): 377 | ''' 378 | Creates a new load given required load_name and bus. 379 | 380 | load_name: str 381 | Name of load (excluding 'Load.') 382 | 383 | bus: str 384 | Bus name where load is to be connected 385 | 386 | prop_kwargs: dict 387 | Properties to be passed directly to OpenDSS 388 | 389 | ''' 390 | prop_kwargs['bus1'] = bus 391 | 392 | for prop in ('kW', 'kvar'): 393 | if prop not in prop_kwargs: 394 | prop_kwargs[prop] = 0.0 395 | 396 | self._new_element('Load', load_name, **prop_kwargs) 397 | 398 | def new_generator(self, generator_name, bus, **prop_kwargs): 399 | ''' 400 | Creates a new generator given required generator_name and bus. 401 | 402 | generator_name: str 403 | Name of generator (excluding 'Generator.') 404 | 405 | bus: str 406 | Bus name where generator is to be connected 407 | 408 | prop_kwargs: dict 409 | Properties to be passed directly to OpenDSS 410 | 411 | ''' 412 | prop_kwargs['bus1'] = bus 413 | 414 | for prop in ('kW', 'kvar'): 415 | if prop not in prop_kwargs: 416 | prop_kwargs[prop] = 0.0 417 | 418 | self._new_element('Generator', generator_name, **prop_kwargs) 419 | 420 | def new_line(self, line_name, bus_from, bus_to, **prop_kwargs): 421 | ''' 422 | Creates a new line given required line_name, bus_from and bus_to. 423 | 424 | line_name: str 425 | Name of line (excluding 'Line.') 426 | 427 | bus_from: str 428 | Bus name where line is to be connected (FROM) 429 | 430 | bus_to: str 431 | Bus name where line is to be connected (TO) 432 | 433 | prop_kwargs: dict 434 | Properties to be passed directly to OpenDSS 435 | 436 | ''' 437 | prop_kwargs['bus1'] = bus_from 438 | prop_kwargs['bus2'] = bus_to 439 | 440 | self._new_element('Line', line_name, **prop_kwargs) 441 | 442 | def get_node_df(self): 443 | ''' 444 | Returns observation dataframe, where index is unique node names. 445 | 446 | ''' 447 | Vbase = (self.Circuit.AllBusVmag / self.Circuit.AllBusVmagPu).reshape(-1, 1) 448 | Vri = self.Circuit.AllBusVolts.reshape(-1,2) / Vbase 449 | VmagPu = self.Circuit.AllBusVmagPu.reshape(-1,1) 450 | node_df = np.concatenate((Vbase, Vri, VmagPu), axis=1) 451 | node_df = pd.DataFrame(node_df, index=self.Circuit.AllNodeNames, columns=['Vbase', 'Vr', 'Vi', 'VmagPu']) 452 | return node_df 453 | 454 | def get_bus_df(self): 455 | ''' 456 | Returns observation dataframe, where index is unique bus names. 457 | 458 | ''' 459 | VmagPu = (self.N2B @ self.Circuit.AllBusVmagPu).reshape(-1, 1) 460 | bus_xy = [[bus.x, bus.y] for bus in self.iterate('Bus')] 461 | bus_df = np.concatenate((bus_xy, VmagPu), axis=1) 462 | bus_df = pd.DataFrame(bus_df, index=self.Circuit.AllBusNames, columns=['x', 'y', 'VmagPu']) 463 | return bus_df 464 | 465 | def get_vsource_df(self): 466 | ''' 467 | Returns observation dataframe, where index is unique vsource names. 468 | 469 | ''' 470 | vsource_dict = {'enabled': [], 'bus': [], 'kW': [], 'kvar': []} 471 | 472 | if self.Circuit.Vsources.Count > 0: 473 | index = self.Circuit.Vsources.AllNames 474 | for vsource in self.iterate('Vsource'): 475 | 476 | vsource_dict['enabled'].append(vsource.Enabled) 477 | 478 | bus = vsource.BusNames[0].split('.')[0] 479 | vsource_dict['bus'].append(bus) 480 | 481 | kW, kvar = vsource.TotalPowers[:2] 482 | vsource_dict['kW'].append(kW) 483 | vsource_dict['kvar'].append(kvar) 484 | else: 485 | index = [] 486 | 487 | vsource_df = pd.DataFrame(vsource_dict, index=index) 488 | return vsource_df 489 | 490 | def _get_prosumer_group_df(self, class_name, circuit_obj, sign=+1.0): 491 | ''' 492 | Returns observation dataframe, where index is unique load/gen names. 493 | 494 | Used internally. Refer to 'get_load_df' for an example. 495 | 496 | sign: float/int 497 | +1 for default consumers (e.g. load) and 498 | -1 for default producers (e.g. generators) 499 | 500 | ''' 501 | elem_dict = {'enabled': [], 'bus': [], 'kW': [], 'kvar': []} 502 | 503 | if circuit_obj.Count > 0: 504 | index = circuit_obj.AllNames 505 | for elem in self.iterate(class_name): 506 | 507 | elem_dict['enabled'].append(elem.Enabled) 508 | 509 | bus = elem.BusNames[0].split('.')[0] 510 | elem_dict['bus'].append(bus) 511 | 512 | kW, kvar = sign * elem.TotalPowers 513 | elem_dict['kW'].append(kW) 514 | elem_dict['kvar'].append(kvar) 515 | else: 516 | index = [] 517 | 518 | elem_df = pd.DataFrame(elem_dict, index=index) 519 | return elem_df 520 | 521 | def get_load_df(self): 522 | ''' 523 | Returns observation dataframe, where index is unique load names. 524 | 525 | ''' 526 | return self._get_prosumer_group_df('Load', self.Circuit.Loads, sign=+1.0) 527 | 528 | def get_generator_df(self): 529 | ''' 530 | Returns observation dataframe, where index is unique gen names. 531 | 532 | ''' 533 | return self._get_prosumer_group_df('Generator', self.Circuit.Generators, sign=-1.0) 534 | 535 | def _get_branch_group_df(self, class_name, circuit_obj): 536 | ''' 537 | Returns observation dataframe, where index is unique branch names. 538 | 539 | ''' 540 | elem_dict = { 541 | 'enabled': [], 'phases': [], 542 | 'bus_from': [], 'bus_to': [], 543 | 'max_amps': [], 544 | 'kW_from': [], 'kvar_from': [], 545 | 'kW_to': [], 'kvar_to': [], 546 | 'kW_loss': [], 'kvar_loss': [], 547 | 'flow_direction': [], 548 | } 549 | 550 | if circuit_obj.Count > 0: 551 | index = circuit_obj.AllNames 552 | for elem in self.iterate(class_name): 553 | 554 | elem_dict['enabled'].append(elem.Enabled) 555 | elem_dict['phases'].append(elem.NumPhases) 556 | 557 | f = elem.BusNames[0].split('.')[0] 558 | t = elem.BusNames[1].split('.')[0] 559 | elem_dict['bus_from'].append(f) 560 | elem_dict['bus_to'].append(t) 561 | 562 | max_amps = elem.CurrentsMagAng[::2].max() 563 | elem_dict['max_amps'].append(max_amps) 564 | 565 | if elem.Enabled: 566 | kW_from, kvar_from, _kW_to, _kvar_to = elem.TotalPowers 567 | else: 568 | kW_from, kvar_from, _kW_to, _kvar_to = 0., 0., 0., 0. 569 | elem_dict['kW_from'].append(kW_from) 570 | elem_dict['kvar_from'].append(kvar_from) 571 | elem_dict['kW_to'].append(-_kW_to) 572 | elem_dict['kvar_to'].append(-_kvar_to) 573 | 574 | kW_loss, kvar_loss = elem.Losses / 1000 575 | elem_dict['kW_loss'].append(kW_loss) 576 | elem_dict['kvar_loss'].append(kvar_loss) 577 | 578 | sign = np.sign(np.abs(kW_from) - np.abs(_kW_to)) 579 | elem_dict['flow_direction'].append(sign) 580 | else: 581 | index = [] 582 | 583 | elem_df = pd.DataFrame(elem_dict, index=index) 584 | return elem_df 585 | 586 | def get_line_df(self): 587 | ''' 588 | Returns observation dataframe, where index is unique line names. 589 | 590 | ''' 591 | return self._get_branch_group_df('Line', self.Circuit.Lines) 592 | 593 | def get_transformer_df(self): 594 | ''' 595 | Returns observation dataframe, where index is unique transformer names. 596 | 597 | ''' 598 | return self._get_branch_group_df('Transformer', self.Circuit.Transformers) 599 | 600 | def get_all_branches_df(self): 601 | ''' 602 | Returns observation dataframe, where index is unique branch names. 603 | 604 | This combines all branches (e.g. lines & transformers) 605 | 606 | ''' 607 | line_df = self.get_line_df() 608 | trans_df = self.get_transformer_df() 609 | 610 | line_df.index = line_df.index.map(lambda name: f'Line.{name}') 611 | trans_df.index = trans_df.index.map(lambda name: f'Transformer.{name}') 612 | 613 | branch_df = pd.DataFrame() 614 | branch_df = branch_df.append([line_df, trans_df]) 615 | 616 | return branch_df 617 | 618 | def save_case_as(self, directory=None, overwrite=False): 619 | ''' 620 | NEEDS REVIEW! 621 | 622 | TEMPORARY NOTES: 623 | 624 | Given a ***directory**, save the entire case using 625 | OpenDSS' Save command (creates and organizes dss files 626 | in one folder). 627 | 628 | If user specifies a directory that already exists, either raise 629 | error and suggest that they should specify 'overwrite=True' or 630 | ignore and overwrite if 'overwrite=True'. 631 | 632 | If user does not specify directory, use circuit name followed 633 | by time now from year to microsecond (6 digits after second). 634 | 635 | Correspondingly, when "opening" an existing case, the user now 636 | has to specify a '.dss' file (e.g. Master) but for consistency, 637 | if the user specifies a folder instead, it should by default 638 | assume that 'Master.DSS' is the main one. For this, you need to 639 | ensure there exists such a file in the directory. 640 | 641 | PROBLEM: If you save into a directory that exists (and allow 642 | overwriting), and the directory already has some files in there 643 | from a previous case, they will stay there. Ideally, you'd like 644 | to erase the entire folder first. However, you might have some 645 | results that are useful to you. What if you just remove '.dss' 646 | files? Even then, you might have some useful files for you. I 647 | don't have a solution at the moment, but it seems like the way 648 | to go is to advise the user not overwrite to existing folder. 649 | 650 | ''' 651 | if directory is None: 652 | 653 | time_now = datetime.now().strftime('%Y-%m-%d-%H-%M-%S-%f') 654 | 655 | directory = f'{self.Circuit.Name}_{time_now}' 656 | 657 | if os.path.isdir(directory) and not overwrite: 658 | 659 | raise ValueError(f'Specified directory already exists. ' 660 | f'To overwrite, set keyword argument overwrite=True.') 661 | 662 | save_command = f'Save Circuit dir="{directory}"' 663 | 664 | result = self.command(save_command) 665 | 666 | self.needs_saving = False 667 | 668 | return result or None 669 | 670 | def delete_case(self, directory=None): 671 | ''' 672 | Placeholder to remind user not to automatically delete a case file! 673 | 674 | ''' 675 | raise ValueError(f'Please delete manually by deleting the directory ' 676 | f'containing the case, but first ensure that no other useful ' 677 | f'files or folders are contained within it!') 678 | 679 | def summarize(self): 680 | ''' 681 | Prints a detailed summary of the circuit. 682 | 683 | ''' 684 | # print('Case path:', self.case_path) 685 | 686 | print(self.Circuit.NumBuses, 'Buses') 687 | print(self.Circuit.NumNodes, 'Nodes') 688 | 689 | print('Voltage Bases (kV L-L):\t', ', '.join(map(str, self.Circuit.Settings.VoltageBases))) 690 | 691 | print('Circuit Elements:') 692 | for class_name, names in self.element_names.items(): 693 | count = len(names) 694 | print(f'\t\t{count} {class_name}{"s"*(count > 1)}') 695 | 696 | def real_imag_to_complex(self, real_imag): 697 | ''' 698 | Map OpenDSS representation of complex arrays to a more 699 | familiar tall complex vector, as illustrated below. 700 | 701 | real_imag: tuple 702 | OpenDSS representation of complex arrays (see below). 703 | 704 | Example: 705 | 706 | Input (real_imag): 707 | 708 | # tuple of shape (2*n,) 709 | (r1, x1, r2, x2, ..., rn, xn) 710 | 711 | Output: 712 | 713 | # complex numpy array of shape (n, 1) 714 | [[r1 + 1j*x1, 715 | r2 + 1j*x2, 716 | ... 717 | rn + 1j*xn]] 718 | 719 | ''' 720 | return np.array(real_imag).reshape(-1,2) @ np.array([[1], [1j]]) 721 | 722 | @case_updated 723 | def edit(self, elem_name, **props): 724 | ''' 725 | Edit any element's properties. 726 | 727 | elem_name: str 728 | Full circuit element name (e.g. 'Generator.gen_1'). 729 | 730 | props: case-insensitive kwargs 731 | Since OpenDSS is case-insensitive, these keyword 732 | arguments are passed as is. 733 | 734 | Note: even if no 'props' were provided, OpenDSS still 735 | accepts an empty edit command e.g. 'Edit Load.load_1'. 736 | 737 | Example: 738 | 739 | # Edit some generator's PQ setpoints 740 | >>> dss.edit('Generator.gen_1', kW=50, kvar=23) 741 | 742 | # Edit nothing about the generator 743 | >>> dss.edit('Generator.gen_1') 744 | 745 | ''' 746 | # Check if element in circuit 747 | if elem_name not in self.Circuit.AllElementNames: 748 | if elem_name.lower() not in map(lambda s: s.lower(), self.Circuit.AllElementNames): 749 | raise ValueError(f'Element {elem_name} not found in circuit.') 750 | 751 | # Specify command to OpenDSS 752 | command = f'Edit {elem_name}' 753 | for prop, val in props.items(): 754 | command += f' {prop}={val}' 755 | 756 | # Execute command 757 | result = self.command(command) 758 | 759 | # Return error if any 760 | return result or None 761 | 762 | def read(self, elem_name, prop): 763 | ''' 764 | Reads an element's property from OpenDSS directly. 765 | 766 | elem_name: str 767 | Unique element name (as it appears in self.Circuit.AllElementNames) 768 | 769 | prop: str 770 | Name of property being inspected 771 | 772 | ''' 773 | result = self.command(f'? {elem_name}.{prop}') 774 | 775 | if result == 'Property Unknown': 776 | raise ValueError(f'Property "{prop}" of element "{elem_name}" is invalid.') 777 | else: 778 | return result 779 | 780 | def command(self, command_str): 781 | ''' 782 | Sends argument as a command to OpenDSS, and returns result. 783 | 784 | Result is an empty string (bool is False) if no OpenDSS 'errors'. 785 | 786 | command_str: str 787 | Command to be sent directly to OpenDSS 788 | 789 | ''' 790 | if not isinstance(command_str, str): 791 | raise TypeError(f'Command expected to be str, ' 792 | f'not {type(command_str)}.') 793 | 794 | self.Text.Command = command_str 795 | 796 | return self.Text.Result 797 | 798 | def command_multiline(self, command_docstring): 799 | ''' 800 | Equivalent to self.command, but argument is a docstring. 801 | 802 | command_docstring: str 803 | Multi-line string (docstring). Each line will be sent 804 | from top to bottom and a dictionary of error messages 805 | will be returned. 806 | 807 | ''' 808 | non_empty_results = {} 809 | for command in command_docstring.splitlines(): 810 | result = self.command(command) 811 | if result: 812 | non_empty_results[command] = result 813 | 814 | return non_empty_results 815 | 816 | def iterate(self, class_name): 817 | ''' 818 | Returns an iterator which yields "CktElement Interface" COM objects 819 | (OpenDSS terminology) corresponding to circuit elements of the same 820 | type, specified by class_name. 821 | 822 | class_name: str 823 | One of the keys of self.element_names representing 824 | a type of element (e.g. 'Line'). 825 | 826 | Example: 827 | 828 | # Print From & To buses of all lines 829 | >>> for elem in dss.iterate('Line'): 830 | ... print(elem.Name, elem.BusNames) 831 | 832 | ''' 833 | if class_name == 'Bus': 834 | for bus_name in self.Circuit.AllBusNames: 835 | yield self.Circuit.Buses(bus_name) 836 | else: 837 | if class_name not in self.element_names.keys(): 838 | raise ValueError(f'{class_name} is an invalid class name. '\ 839 | f'Select from these:\n{[*self.element_names.keys()] + ["Bus"]}') 840 | 841 | for elem_name in self.element_names[class_name]: 842 | yield self.Circuit.CktElements(elem_name) 843 | 844 | def element_data(self, class_name='Vsource', field='Voltages'): 845 | ''' 846 | For use in JupyterLab or JupyterNotebook. 847 | 848 | Produce an interactive set of dropdowns that help you 849 | observe element properties and measurements (post-solution). 850 | 851 | Result can then be extracted from self.interact_result. 852 | 853 | ''' 854 | element_names = {**self.element_names, 855 | **{'Bus': self.Circuit.AllBusNames}} 856 | 857 | self.interact_result = None 858 | @interact(class_name=Dropdown(options=element_names.keys(), value=class_name)) 859 | def _(class_name): 860 | obj = self.Bus if class_name == 'Bus' else self.Elem 861 | @interact(elem_name=Dropdown(options=element_names[class_name]), 862 | field=Dropdown(options=get_measurable_fields(obj), value=field)) 863 | def _(elem_name, field): 864 | if class_name == 'Bus': 865 | self.Circuit.SetActiveBus(elem_name) 866 | self.interact_result = getattr(self.Bus, field) 867 | else: 868 | self.Circuit.SetActiveElement(elem_name) 869 | self.interact_result = getattr(self.Elem, field) 870 | 871 | display(self.interact_result) 872 | 873 | def element_props(self, class_name='Vsource'): 874 | ''' 875 | For use in JupyterLab or JupyterNotebook. 876 | 877 | Produce an interactive set of dropdowns that help you 878 | observe element properties and measurements (pre-solution). 879 | 880 | Result can then be extracted from self.interact_result. 881 | 882 | ''' 883 | self.interact_result = None 884 | @interact(class_name=Dropdown(options=self.element_names.keys(), value=class_name)) 885 | def _(class_name): 886 | # initial 887 | elem_name = self.element_names[class_name][0] 888 | self.Circuit.SetActiveElement(elem_name) 889 | 890 | @interact(elem_name=Dropdown(options=self.element_names[class_name]), 891 | prop=Dropdown(options=self.Elem.AllPropertyNames)) 892 | def _(elem_name, prop): 893 | self.Circuit.SetActiveElement(elem_name) 894 | self.interact_result = self.Elem.Properties(prop).Val 895 | 896 | print() 897 | display(self.interact_result) 898 | # print() 899 | # print('='*20, 'DESCRIPTION', '='*20) 900 | # print() 901 | # print(self.Elem.Properties(prop).Description) # Seems to fail post dss_python 0.10 902 | 903 | def get_props_all_elements(self, class_name, numbers_as_floats=False): 904 | ''' 905 | Return a dataframe showing properties of all elements of a certain type. 906 | 907 | class_name: str 908 | OpenDSS element type (e.g. 'Load' or 'Line') 909 | 910 | numbers_as_floats: bool 911 | If True, properties in dataframe are converted to floats. 912 | Otherwise, they remain as strings. 913 | 914 | ''' 915 | for elem in self.iterate(class_name): 916 | break 917 | AllPropertyNames = elem.AllPropertyNames 918 | 919 | props_dict = {prop: [] for prop in AllPropertyNames} 920 | props_dict['Name'] = [] 921 | for elem in self.iterate(class_name): 922 | props_dict['Name'].append(elem.Name.split('.')[1]) 923 | for prop in AllPropertyNames: 924 | props_dict[prop].append(elem.Properties(prop).Val) 925 | 926 | props_df = pd.DataFrame(props_dict) 927 | props_df = props_df.set_index('Name') 928 | 929 | if numbers_as_floats: 930 | for prop in props_df: 931 | try: 932 | props_df[prop] = props_df[prop].astype('float') 933 | except: 934 | pass 935 | 936 | return props_df 937 | 938 | def get_props_single_element(self, elem_name, numbers_as_floats=False): 939 | ''' 940 | Return a dataframe showing properties of a single element. 941 | 942 | elem_name: str 943 | Unique element name (as it appears in self.Circuit.AllElementNames) 944 | 945 | numbers_as_floats: bool 946 | If True, properties in dataframe are converted to floats. 947 | Otherwise, they remain as strings. 948 | 949 | ''' 950 | self.Circuit.SetActiveElement(elem_name) 951 | AllPropertyNames = self.Elem.AllPropertyNames 952 | 953 | prop_dict = {} 954 | for prop in AllPropertyNames: 955 | val = self.Elem.Properties(prop).Val 956 | 957 | if numbers_as_floats: 958 | try: 959 | val = float(val) 960 | except: 961 | pass 962 | 963 | prop_dict[prop] = val 964 | 965 | return prop_dict -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | pandas 3 | matplotlib 4 | tqdm 5 | dss-python 6 | scipy -------------------------------------------------------------------------------- /utils/__pycache__/iterator.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/utils/__pycache__/iterator.cpython-37.pyc -------------------------------------------------------------------------------- /utils/__pycache__/method_update.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tamu-engineering-research/OpenGridGym/d941ccc9f51729991a7125bccfcbd7e7c9c2f8bb/utils/__pycache__/method_update.cpython-37.pyc -------------------------------------------------------------------------------- /utils/iterator.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | def episode_iterator(t_prev=-1, max_steps=100, num_iter=None, iterator=range, warn=True): 4 | ''' 5 | Iterate which yields time, starting at t_prev 6 | and ending after num_iter iterations. 7 | 8 | t_prev: int 9 | Time stamp already visited (i.e. "previous"). 10 | Iterator will begin one time step after this. 11 | 12 | max_steps: int 13 | Maxmimum number of steps (in case num_iter is too large). 14 | 15 | num_iter: int 16 | Number of time steps to iterate over. 17 | If left as None, (max_steps - t_prev) is assumed. 18 | 19 | iterator: Iterator[int] 20 | An iterator which yields current time step, and either 21 | shows progress or not, depending on whether uses chooses 22 | range or trange (tqdm) for this value, respectively. 23 | More generally, you can specify any iterator provided 24 | that it yields the current time (int) and takes two 25 | arguments: (t_prev, t_end+1). 26 | 27 | warn: bool 28 | If True, it will warn the user (not raise error) if num_iter 29 | is large enough that it exceeds maximum remaining steps. 30 | 31 | Example: 32 | 33 | >>> for t in episode_iterator(50, num_iter=3): # progress for 3 new steps 34 | ... print(t) 35 | ... 36 | 51 37 | 52 38 | 53 39 | 40 | ''' 41 | # End time based on number of iterations 42 | if num_iter is None: 43 | t_end = max_steps - 1 44 | num_iter = t_end - t_prev 45 | elif num_iter < 0: 46 | raise ValueError('Negative time traversal not supported.') 47 | else: 48 | t_end = t_prev + num_iter 49 | if t_end > max_steps - 1: 50 | t_end = max_steps - 1 51 | if warn: 52 | warnings.warn(f'\n\nnum_iter truncated to {t_end - t_prev} ' 53 | f'to avoid exceeding maximum end time.\n') 54 | 55 | # Progress and yield time 56 | for t in iterator(t_prev+1, t_end+1): 57 | 58 | yield t 59 | 60 | if __name__ == '__main__': 61 | 62 | for t in episode_iterator(50, num_iter=5, max_steps=100): 63 | print(t) 64 | 65 | for t in episode_iterator(50, num_iter=5, max_steps=54): 66 | print(t) -------------------------------------------------------------------------------- /utils/method_update.py: -------------------------------------------------------------------------------- 1 | from inspect import signature 2 | from types import MethodType, FunctionType 3 | 4 | class MethodSignatureError(Exception): 5 | def __init__(self, expected_sig=None, actual_sig=None): 6 | 7 | message = f'Expected signature {expected_sig}, instead got signature {actual_sig}' 8 | super().__init__(message) 9 | 10 | 11 | def ensure_method_signature(expected_method=None, actual_method=None): 12 | if not isinstance(expected_method, MethodType): 13 | ValueError(f'{expected_method} is not a method.') 14 | else: 15 | expected_params = [*map(str, signature(expected_method).parameters.values())] 16 | actual_params = [*map(str, signature(actual_method).parameters.values())] 17 | 18 | expected_params = ["self"] + expected_params 19 | expected_sig = f'({", ".join(expected_params)})' 20 | 21 | actual_sig = f'({", ".join(actual_params)})' 22 | 23 | if actual_sig != expected_sig: 24 | raise MethodSignatureError(expected_sig=expected_sig, actual_sig=actual_sig) 25 | 26 | 27 | def update_instance_method(instance, old_method_str, new_method): 28 | ''' 29 | Updates an instance's method to a new one, but first checks 30 | if the signature of the new method matches that of the old one. 31 | 32 | instance: object 33 | An instance of any object 34 | 35 | old_method_str: str 36 | The string which uniquely identifies the method you seek 37 | to update. Strictly speaking, this is how you get the method: 38 | getattr(instance, old_method_str) 39 | 40 | new_method: FunctionType 41 | Any function whose argument must begin with 'self'. 42 | If the function's signature matches that of the instance's 43 | old method, the instance's method is updated. 44 | 45 | ''' 46 | 47 | # Make sure it's safe to do so first 48 | old_method = getattr(instance, old_method_str) 49 | ensure_method_signature(expected_method=old_method, actual_method=new_method) 50 | 51 | # Update the method 52 | setattr(instance, old_method_str, MethodType(new_method, instance)) 53 | 54 | 55 | if __name__ == '__main__': 56 | 57 | class A: 58 | def f(self, x, *args, **kwargs): 59 | return 5 60 | 61 | def g(self, x, *args, **kwargs): 62 | return 6 63 | 64 | L = lambda self, x, *args, **kwargs: 7 65 | 66 | a = A() 67 | 68 | update_instance_method(a, 'f', g) 69 | update_instance_method(a, 'f', L) 70 | print(a.f(None)) --------------------------------------------------------------------------------