Model Interface
Here we describe and show how to interact with the Grid object.
Grid definition
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.
from power_grid_model_ds import Grid
from power_grid_model_ds.arrays import NodeArray
grid = Grid.empty()
nodes = NodeArray(
id=[2, 3, 4, 5, 12],
u_rated=[10_500.0] * 4 + [400.0],
)
grid.append(nodes)
Alternatively a Grid object can be initialized from a txt file (mainly for testing purposes) or from cached data.
To create a random Grid object a generator is provided.
from power_grid_model_ds.generators import RadialGridGenerator
grid_generator = RadialGridGenerator(grid_class=Grid, nr_nodes=1000)
grid = grid_generator.run(seed=0)
Topological modification
Having a grid it is possible to make modification to the grid while keeping track of the different representations and properties. To add a line to an existing grid
from power_grid_model_ds.arrays import LineArray
new_line_array = LineArray.zeros(1)
new_line_array.from_node = 2
new_line_array.to_node = 5
grid.add_branch(branch=new_line_array)
Or to activate an existing inactive branch in the grid
target_line = grid.arrays.line.get(1)
grid.make_active(target_branch_array=target_line)
As seen above, you can also apply multiple modifications in one go using the grid.append method.
Array interface
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.
Array definition
You can create your own array by subclassing FancyArray.
Array-columns can be defined by adding class attributes with the column name and the numpy dtype.
Example:
import numpy as np
from numpy.typing import NDArray
from power_grid_model_ds.fancypy import FancyArray
class MyArray(FancyArray):
id: NDArray[np.int_]
name: NDArray[np.str_]
value: NDArray[np.float64]
It is possible to provide defaults for columns using
class MyDefaultedArray(MyArray):
_defaults = {"id": -1, "name": "default", "value": 1.0}
These are used when initializing an array with the .empty method
array = MyDefaultedArray.empty(3)
Note on string-columns:
The default length for string columns is stored in _DEFAULT_STR_LENGTH.
To change this, you can set the _str_lengths class attribute.
Example:
class MyArray(FancyArray):
name: NDArray[np.str_]
_str_lengths = {"name": 100}
Where possible, it is recommended use IntEnum’s instead of string-columns to reduce memory usage.
Array loops
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
for row in array.data:
pass
This looses the FancyArray class in the row but can be accepted when this is not used in the further for loop.
Array inheritance
You can inherit attributes from one array to another
class MyFirstArray(FancyArray):
my_first_attribute: NDArray[np.int_]
class MySecondArray(MyFirstArray):
my_second_attribute: NDArray[np.int_]
This gives the following dtype for MySecondArray: [('my_first_attribute', '<i8'), ('my_second_attribute', '<i8')]
Graph interface
When using the Grid this also provides access to extra graph analysis functionality. This can be used to investigate graph structure for example looking for a substation node or finding all nodes downstream the given node.
substation_node = grid.get_nearest_substation_node(node_id=102)
downstream_nodes = grid.get_downstream_nodes(node_id=102)
The underlying NetworkGraph elements of an active and complete network graph also provide extra analysis functionality. These include a shortest path analysis, component analysis or breath first search functionality.
all_paths = grid.graphs.active_graph.get_all_paths(56, 41)
components = grid.graphs.active_graph.get_components(substation_nodes=np.array([1, 2, 3]))
connected = grid.graphs.active_graph.get_connected(node_id=56)