Connecting a new consumer
This demo shows how to use power-grid-model-ds to simulate a new consumer to be connected to the grid
First we create an extension of the Grid objects with properties we want to use in this context
We create a random grid structure for the purpose of the demo
We define functions that add a new consumer to the grid and simulate its impact
from dataclasses import dataclass
import numpy as np
from numpy.typing import NDArray
from power_grid_model_ds import Grid
from power_grid_model_ds.arrays import LineArray, NodeArray
class ExtendedNodeArray(NodeArray):
"""Extends the node array with the simulated voltage and coordinates"""
_defaults = {"u": 0}
u: NDArray[np.float64]
x_coor: NDArray[np.float64]
y_coor: NDArray[np.float64]
@property
def is_overloaded(self):
return np.logical_or(self.u > 1.1 * self.u_rated, self.u < 0.9 * self.u_rated)
class ExtendedLineArray(LineArray):
"""Extends the line array with current output"""
_defaults = {"i_from": 0}
i_from: NDArray[np.float64]
@property
def is_overloaded(self):
return self.i_from > self.i_n
@dataclass
class ExtendedGrid(Grid):
node: ExtendedNodeArray
line: ExtendedLineArray
from power_grid_model_ds.generators import RadialGridGenerator
grid_generator = RadialGridGenerator(grid_class=ExtendedGrid, nr_nodes=20, nr_sources=1, nr_nops=10)
grid = grid_generator.run(seed=0)
grid.set_feeder_ids()
grid.node.x_coor = np.random.uniform(100, 500, len(grid.node))
grid.node.y_coor = np.random.uniform(100, 500, len(grid.node))
First we create a new consumer, with a location and a load demand
from power_grid_model import LoadGenType
from power_grid_model_ds.arrays import SymLoadArray
from power_grid_model_ds.enums import NodeType
def create_new_consumer_arrays(
u_rated: float, x_coor: float, y_coor: float, p_specified: float, q_specified: float
) -> tuple[ExtendedNodeArray, SymLoadArray]:
new_consumer = ExtendedNodeArray(
u_rated=[u_rated],
node_type=[NodeType.UNSPECIFIED],
x_coor=[x_coor],
y_coor=[y_coor],
)
new_consumer_load = SymLoadArray(
node=[new_consumer.get_empty_value("id")],
status=[1],
type=[LoadGenType.const_power],
p_specified=[p_specified],
q_specified=[q_specified],
)
return new_consumer, new_consumer_load
new_consumer, new_consumer_load = create_new_consumer_arrays(10_500, 300, 300, 1_000_000, 200_000)
Now lets define some functions that add the new consumer by connecting it to the closest node
from power_grid_model_ds._core.load_flow import PowerGridModelInterface
R_PER_KM = 0.1
X_PER_KM = 0.1
def find_closest_node(grid: ExtendedGrid, x: float, y: float) -> int:
dist = np.sqrt((grid.node.x_coor - x) ** 2 + (grid.node.y_coor - y) ** 2)
return np.argmin(dist)
def connect_new_consumer(
grid: ExtendedGrid,
new_consumer: ExtendedNodeArray,
new_consumer_load: SymLoadArray,
):
closest_node_idx = find_closest_node(
grid=grid,
x=new_consumer.x_coor[0],
y=new_consumer.y_coor[0],
)
closest_node = grid.node[closest_node_idx]
grid.append(new_consumer)
new_consumer_load.node = new_consumer.id
grid.append(new_consumer_load)
dist = np.sqrt((closest_node.x_coor - new_consumer.x_coor) ** 2 + (closest_node.y_coor - new_consumer.y_coor) ** 2)
new_line = ExtendedLineArray(
from_node=[closest_node.id],
to_node=[new_consumer.id],
from_status=[1],
to_status=[1],
r1=[R_PER_KM * dist / 1_000],
x1=[X_PER_KM * dist / 1_000],
c1=[0],
tan1=[0],
i_n=[200],
)
grid.append(new_line)
def update_grid(grid: ExtendedGrid):
# Set the new feeder ids
grid.set_feeder_ids()
# Update the loadflow
core_interface = PowerGridModelInterface(grid=grid)
core_interface.create_input_from_grid()
core_interface.calculate_power_flow()
core_interface.update_grid()
connect_new_consumer(grid, new_consumer, new_consumer_load)
update_grid(grid)
We can inspect the results
The grid has been extended (graph and arrays)
Load values have been updated on node and line arrays
The feeder ids have been updated for the new consumer
print(grid.node)
id | u_rated | node_type | feeder_branch_id | feeder_node_id | u | x_coor | y_coor
1 | 10500.0 | 0 | 64 | 61 |10409.679..|490.598..|470.171..
2 | 10500.0 | 0 | 64 | 61 |10463.354..|349.404..|470.018..
3 | 10500.0 | 0 | 63 | 61 |10432.979..|275.381..|487.298..
4 | 10500.0 | 0 | 78 | 61 |10501.494..|103.545..|312.575..
5 | 10500.0 | 0 | 63 | 61 |10460.319..|111.479..|489.918..
(..12 hidden rows..)
18 | 10500.0 | 0 | 63 | 61 |10456.828..|380.139..|348.204..
19 | 10500.0 | 0 | 78 | 61 |10521.241..|336.811..|458.557..
20 | 10500.0 | 0 | 64 | 61 |10471.544..|191.225..|209.262..
61 | 10500.0 | 1 | -2147483648 | -2147483648 |10499.554..|488.501..|308.552..
93 | 10500.0 | 0 | 64 | 61 |10363.253..| 300.0 | 300.0
print(grid.line)
id | from_node | to_node | from_status | to_status | feeder_branch_id | feeder_node_id | is_feeder | r1 | x1 | c1 | tan1 | i_n | i_from
63 | 61 | 17 | 1 | 1 | 63 | 61 | True |0.110..|0.013..| 0.0 | 0.0 |103.9613..|32.3178..
64 | 61 | 13 | 1 | 1 | 64 | 61 | True |0.325..|0.015..| 0.0 | 0.0 |100.4538..|70.0233..
65 | 17 | 5 | 1 | 1 | 63 | 61 | False |0.657..|2.575..| 0.0 | 0.0 |1311.550..|29.9315..
66 | 13 | 11 | 1 | 1 | 64 | 61 | False |0.213..|0.016..| 0.0 | 0.0 |114.4995..|109.470..
67 | 5 | 18 | 1 | 1 | 63 | 61 | False |0.061..|0.029..| 0.0 | 0.0 |170.8020..|30.2447..
(..21 hidden rows..)
89 | 1 | 18 | 1 | 0 | -2147483648 | -2147483648 | False |0.341..|0.012..| 0.0 | 0.0 |221.8579..| 0.0
90 | 18 | 2 | 1 | 0 | -2147483648 | -2147483648 | False |0.122..|0.006..| 0.0 | 0.0 |387.5294..| 0.0
91 | 61 | 17 | 1 | 0 | -2147483648 | -2147483648 | True |0.210..|0.001..| 0.0 | 0.0 |267.6662..| 0.0
92 | 3 | 11 | 1 | 0 | -2147483648 | -2147483648 | False |0.030..|0.050..| 0.0 | 0.0 |272.8941..| 0.0
95 | 8 | 93 | 1 | 1 | 64 | 61 | False |0.008..|0.008..| 0.0 | 0.0 | 200.0 |56.8145..
print(f"Overloaded nodes: {grid.node[grid.node.is_overloaded].id}")
print(f"Overloaded lines: {grid.line[grid.line.is_overloaded].id}")
Overloaded nodes: []
Overloaded lines: []
Now simulate more consumers being added, as to see how this will lead to overloads
for _ in range(10):
new_consumer, new_consumer_load = create_new_consumer_arrays(
10_500, np.random.uniform(0, 500), np.random.uniform(0, 500), 1_000_000, 200_000
)
connect_new_consumer(grid, new_consumer, new_consumer_load)
update_grid(grid)
print(f"Overloaded nodes: {grid.node[grid.node.is_overloaded].id}")
print(f"Overloaded lines: {grid.line[grid.line.is_overloaded].id}")
Overloaded nodes: []
Overloaded lines: [64 66 72]