{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Connecting a new consumer\n", "\n", "This demo shows how to use power-grid-model-ds to simulate a new consumer to be connected to the grid\n", "\n", "1. First we create an extension of the Grid objects with properties we want to use in this context\n", "2. We create a random grid structure for the purpose of the demo\n", "3. We define functions that add a new consumer to the grid and simulate its impact" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from dataclasses import dataclass\n", "from typing import ClassVar\n", "\n", "import numpy as np\n", "from numpy.typing import NDArray\n", "\n", "from power_grid_model_ds import Grid\n", "from power_grid_model_ds.arrays import LineArray, NodeArray\n", "\n", "\n", "class ExtendedNodeArray(NodeArray):\n", " \"\"\"Extends the node array with the simulated voltage and coordinates\"\"\"\n", "\n", " _defaults: ClassVar = {\"u\": 0}\n", "\n", " u: NDArray[np.float64]\n", " x_coor: NDArray[np.float64]\n", " y_coor: NDArray[np.float64]\n", "\n", " @property\n", " def is_overloaded(self):\n", " return np.logical_or(self.u > 1.1 * self.u_rated, self.u < 0.9 * self.u_rated)\n", "\n", "\n", "class ExtendedLineArray(LineArray):\n", " \"\"\"Extends the line array with current output\"\"\"\n", "\n", " _defaults: ClassVar = {\"i_from\": 0}\n", "\n", " i_from: NDArray[np.float64]\n", "\n", " @property\n", " def is_overloaded(self):\n", " return self.i_from > self.i_n\n", "\n", "\n", "@dataclass\n", "class ExtendedGrid(Grid):\n", " node: ExtendedNodeArray\n", " line: ExtendedLineArray" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from power_grid_model_ds.generators import RadialGridGenerator\n", "\n", "grid_generator = RadialGridGenerator(grid_class=ExtendedGrid, nr_nodes=20, nr_sources=1, nr_nops=10)\n", "grid = grid_generator.run(seed=0)\n", "\n", "grid.set_feeder_ids()\n", "\n", "rng = np.random.default_rng()\n", "grid.node.x_coor = rng.uniform(100, 500, len(grid.node))\n", "grid.node.y_coor = rng.uniform(100, 500, len(grid.node))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we create a new consumer, with a location and a load demand" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from power_grid_model import LoadGenType\n", "\n", "from power_grid_model_ds.arrays import SymLoadArray\n", "from power_grid_model_ds.enums import NodeType\n", "\n", "\n", "def create_new_consumer_arrays(\n", " u_rated: float, x_coor: float, y_coor: float, p_specified: float, q_specified: float\n", ") -> tuple[ExtendedNodeArray, SymLoadArray]:\n", " new_consumer = ExtendedNodeArray(\n", " u_rated=[u_rated],\n", " node_type=[NodeType.UNSPECIFIED],\n", " x_coor=[x_coor],\n", " y_coor=[y_coor],\n", " )\n", " new_consumer_load = SymLoadArray(\n", " node=[new_consumer.get_empty_value(\"id\")],\n", " status=[1],\n", " type=[LoadGenType.const_power],\n", " p_specified=[p_specified],\n", " q_specified=[q_specified],\n", " )\n", " return new_consumer, new_consumer_load\n", "\n", "\n", "new_consumer, new_consumer_load = create_new_consumer_arrays(10_500, 300, 300, 1_000_000, 200_000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now lets define some functions that add the new consumer by connecting it to the closest node" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from power_grid_model_ds import PowerGridModelInterface\n", "\n", "R_PER_KM = 0.1\n", "X_PER_KM = 0.1\n", "\n", "\n", "def find_closest_node(grid: ExtendedGrid, x: float, y: float) -> int:\n", " dist = np.sqrt((grid.node.x_coor - x) ** 2 + (grid.node.y_coor - y) ** 2)\n", " return np.argmin(dist).item()\n", "\n", "\n", "def connect_new_consumer(\n", " grid: ExtendedGrid,\n", " new_consumer: ExtendedNodeArray,\n", " new_consumer_load: SymLoadArray,\n", "):\n", " closest_node_idx = find_closest_node(\n", " grid=grid,\n", " x=new_consumer.x_coor[0],\n", " y=new_consumer.y_coor[0],\n", " )\n", " closest_node = grid.node[closest_node_idx]\n", "\n", " grid.append(new_consumer)\n", " new_consumer_load.node = new_consumer.id\n", " grid.append(new_consumer_load)\n", "\n", " dist = np.sqrt((closest_node.x_coor - new_consumer.x_coor) ** 2 + (closest_node.y_coor - new_consumer.y_coor) ** 2)\n", "\n", " new_line = ExtendedLineArray(\n", " from_node=[closest_node.id],\n", " to_node=[new_consumer.id],\n", " from_status=[1],\n", " to_status=[1],\n", " r1=[R_PER_KM * dist / 1_000],\n", " x1=[X_PER_KM * dist / 1_000],\n", " c1=[0],\n", " tan1=[0],\n", " i_n=[200],\n", " )\n", " grid.append(new_line)\n", "\n", "\n", "def update_grid(grid: ExtendedGrid):\n", " # Set the new feeder ids\n", " grid.set_feeder_ids()\n", "\n", " # Update the power flow\n", " core_interface = PowerGridModelInterface(grid=grid)\n", "\n", " core_interface.create_input_from_grid()\n", " core_interface.calculate_power_flow()\n", " core_interface.update_grid()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "connect_new_consumer(grid, new_consumer, new_consumer_load)\n", "update_grid(grid)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can inspect the results\n", "\n", "- The grid has been extended (graph and arrays)\n", "- Load values have been updated on node and line arrays\n", "- The feeder ids have been updated for the new consumer" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(grid.node)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(grid.line)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Overloaded nodes: {grid.node[grid.node.is_overloaded].id}\")\n", "print(f\"Overloaded lines: {grid.line[grid.line.is_overloaded].id}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now simulate more consumers being added, as to see how this will lead to overloads" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for _ in range(10):\n", " new_consumer, new_consumer_load = create_new_consumer_arrays(\n", " 10_500, rng.uniform(0, 500), rng.uniform(0, 500), 1_000_000, 200_000\n", " )\n", " connect_new_consumer(grid, new_consumer, new_consumer_load)\n", "update_grid(grid)\n", "\n", "print(f\"Overloaded nodes: {grid.node[grid.node.is_overloaded].id}\")\n", "print(f\"Overloaded lines: {grid.line[grid.line.is_overloaded].id}\")" ] } ], "metadata": { "kernelspec": { "display_name": "power-grid-model-ds (3.13.9)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 2 }