diff --git a/docs/Makefile b/docs/Makefile index 48b3923..568842b 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -3,13 +3,12 @@ # SPDX-License-Identifier: MPL-2.0 # Minimal makefile for Sphinx documentation -# # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build -SOURCEDIR = source +SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". diff --git a/docs/demos/connecting_new_consumer.ipynb b/docs/demos/connecting_new_consumer.ipynb new file mode 100644 index 0000000..7e5f193 --- /dev/null +++ b/docs/demos/connecting_new_consumer.ipynb @@ -0,0 +1,284 @@ +{ + "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", + "\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 = {\"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 = {\"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", + "grid.node.x_coor = np.random.uniform(100, 500, len(grid.node))\n", + "grid.node.y_coor = np.random.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._core.load_flow 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)\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 loadflow\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, np.random.uniform(0, 500), np.random.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": ".venv", + "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", + "version": "3.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/demos/connecting_new_consumer.ipynb.license b/docs/demos/connecting_new_consumer.ipynb.license new file mode 100644 index 0000000..7601059 --- /dev/null +++ b/docs/demos/connecting_new_consumer.ipynb.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/docs/demos/index.rst b/docs/demos/index.rst new file mode 100644 index 0000000..e8dce78 --- /dev/null +++ b/docs/demos/index.rst @@ -0,0 +1,15 @@ +.. SPDX-FileCopyrightText: Contributors to the Power Grid Model project +.. +.. SPDX-License-Identifier: MPL-2.0 + +Demos +====================== +These showcase integrated examples of how to use the PGM-DS library. +They are designed to help users understand the practical applications of the library, +demonstrating key features and common use cases. + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + connecting_new_consumer diff --git a/docs/examples/index.rst b/docs/examples/index.rst index 8417a26..896f061 100644 --- a/docs/examples/index.rst +++ b/docs/examples/index.rst @@ -5,6 +5,8 @@ Examples ====================== +These example explain the PGM-DS interface and how to use it. + .. toctree:: :maxdepth: 1 :caption: Contents: diff --git a/docs/index.md b/docs/index.md index 0a0ec57..a5cd291 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,6 +31,7 @@ pip install power-grid-model-ds :maxdepth: 2 quick_start +demos/index model_structure model_interface examples/index diff --git a/docs/make.bat b/docs/make.bat index 40732d0..c44f738 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -11,7 +11,7 @@ REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) -set SOURCEDIR=source +set SOURCEDIR=. set BUILDDIR=_build %SPHINXBUILD% >NUL 2>NUL