{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Model Interface\n", "\n", "Here we describe and show how to interact with the `Grid` object.\n", "\n", "## Grid definition\n", "\n", "An empty Grid can be defined using the `Grid.empty` method. This initializes the Grid with all empty arrays. The Grid has a `append` method which takes an array and appends it to the relevant array-type based on it's class definition and extends the graph as needed.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from power_grid_model_ds import Grid\n", "from power_grid_model_ds.arrays import NodeArray\n", "\n", "grid = Grid.empty()\n", "\n", "nodes = NodeArray(\n", " id=[2, 3, 4, 5, 12],\n", " u_rated=[10_500.0] * 4 + [400.0],\n", ")\n", "grid.append(nodes)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively a Grid object can be initialized from a txt file (mainly for testing purposes) or from cached data.\n", "\n", "To create a random Grid object a generator is provided.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from power_grid_model_ds.generators import RadialGridGenerator\n", "\n", "grid_generator = RadialGridGenerator(grid_class=Grid, nr_nodes=1000)\n", "grid = grid_generator.run(seed=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Topological modification\n", "\n", "Having a grid it is possible to make modification to the grid while keeping track of the different representations and properties.\n", "To add a line to an existing grid\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from power_grid_model_ds.arrays import LineArray\n", "\n", "new_line_array = LineArray.zeros(1)\n", "new_line_array.from_node = 2\n", "new_line_array.to_node = 5\n", "grid.append(new_line_array)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or to activate an existing inactive branch in the grid\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```py\n", "target_line = grid.arrays.line.get(1)\n", "grid.make_active(target_branch_array=target_line)\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As seen above, you can also apply multiple modifications in one go using the `grid.append` method.\n", "\n", "## Array interface\n", "\n", "The array container is build around an extension of numpy arrays with the `FancyArray` class. This allows for easy and consistent definition of array types, recognition of array-type from its class and features which improve readability such as dot-notation and autocompletion. It contains a `._data` attribute with the base numpy array and extra settings can be provided using `._defaults` and `._str_lengths`. Note these values should only be used in defining the array classes and remain private when using the arrays.\n", "\n", "### Array definition\n", "\n", "You can create your own array by subclassing `FancyArray`.\n", "Array-columns can be defined by adding class attributes with the column name and the numpy dtype.\n", "\n", "Example:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from typing import ClassVar\n", "\n", "import numpy as np\n", "from numpy.typing import NDArray\n", "\n", "from power_grid_model_ds.fancypy import FancyArray\n", "\n", "\n", "class MyArray(FancyArray):\n", " id: NDArray[np.int_]\n", " name: NDArray[np.str_]\n", " value: NDArray[np.float64]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is possible to provide defaults for columns using\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class MyDefaultedArray(MyArray):\n", " _defaults: ClassVar = {\"id\": -1, \"name\": \"default\", \"value\": 1.0}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These are used when initializing an array with the `.empty` method\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "array = MyDefaultedArray.empty(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Note on string-columns:\n", "\n", "The default length for string columns is stored in `_DEFAULT_STR_LENGTH`.\n", "To change this, you can set the `_str_lengths` class attribute.\n", "Example:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class MyArray(FancyArray):\n", " name: NDArray[np.str_]\n", " _str_lengths: ClassVar = {\"name\": 100}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Where possible, it is recommended use IntEnum's instead of string-columns to reduce memory usage.\n", "\n", "### Array loops\n", "\n", "Looping over large arrays can incur a performance hit caused by conversion of each element to the original `FancyArray` subclass. When you want to implement a faster loop over the array you can choose to access the `array.data` directly and create the loop using\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for _row in array.data:\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This looses the FancyArray class in the row but can be accepted when this is not used in the further for loop.\n", "\n", "### Array inheritance\n", "\n", "You can inherit attributes from one array to another\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class MyFirstArray(FancyArray):\n", " my_first_attribute: NDArray[np.int_]\n", "\n", "\n", "class MySecondArray(MyFirstArray):\n", " my_second_attribute: NDArray[np.int_]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This gives the following dtype for `MySecondArray`: `[('my_first_attribute', '