Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1
1.2
26 changes: 25 additions & 1 deletion docs/examples/pgm/basic_pgm_examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,30 @@
"# Results have been calculated for all 10 scenarios\n",
"display(output[\"line\"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create grid from input data example\n",
"\n",
"To create a `Grid` object from `PGM` input data, the `create_grid_from_input_data()` method can be used."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"input_data = core_interface.create_input_from_grid()\n",
"\n",
"core_interface = PowerGridModelInterface(input_data=input_data)\n",
"output = core_interface.create_grid_from_input_data()\n",
"\n",
"print(input_data[\"node\"])\n",
"print(output.node)"
]
}
],
"metadata": {
Expand All @@ -172,7 +196,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
"version": "3.12.9"
}
},
"nbformat": 4,
Expand Down
24 changes: 22 additions & 2 deletions src/power_grid_model_ds/_core/load_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ class PowerGridModelInterface:

def __init__(
self,
grid: Grid,
grid: Optional[Grid] = None,
input_data: Optional[Dict] = None,
system_frequency: float = 50.0,
):
self.grid = grid
self.grid = grid or Grid.empty()
self.system_frequency = system_frequency

self.input_data = input_data or {}
Expand All @@ -63,6 +63,26 @@ def create_input_from_grid(self):
self.input_data[array_name] = pgm_array
return self.input_data

def create_grid_from_input_data(self, check_ids: bool = True) -> Grid:
"""
Create Grid object from PowerGridModel input.
Note that for some arrays, not all fields are available in the PowerGridModel input.
In this case, the default values are used.

Args:
check_ids: if True, check if the ids are unique

Returns a Grid object with the arrays filled with the PowerGridModel input.
"""
for pgm_name in PGM_ARRAYS:
if pgm_name in self.input_data:
pgm_ds_array_class = getattr(self.grid, pgm_name).__class__
pgm_ds_array = pgm_ds_array_class(self.input_data[pgm_name])
self.grid.append(pgm_ds_array, check_max_id=False)
if check_ids:
self.grid.check_ids()
return self.grid

def calculate_power_flow(
self,
calculation_method: CalculationMethod = CalculationMethod.newton_raphson,
Expand Down
62 changes: 61 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
#
# SPDX-License-Identifier: MPL-2.0

"""Helper arrays used by various tests."""
"""Helper np.arrays used by various tests."""

import pytest
from power_grid_model import initialize_array

from power_grid_model_ds._core.model.graphs.models import RustworkxGraphModel
from power_grid_model_ds._core.model.graphs.models.base import BaseGraphModel
Expand Down Expand Up @@ -70,3 +71,62 @@ def basic_grid(grid):
@pytest.fixture
def grid_with_3wt(grid):
yield build_basic_grid_with_three_winding(grid)


@pytest.fixture
def input_data_pgm():
node = initialize_array("input", "node", 3)
node["id"] = [1, 2, 7]
node["u_rated"] = [10500.0, 10500.0, 10500.0]

line = initialize_array("input", "line", 2)
line["id"] = [9, 10]
line["from_node"] = [7, 7]
line["to_node"] = [2, 1]
line["from_status"] = [1, 1]
line["to_status"] = [1, 1]
line["r1"] = [0.00396133, 0.32598809]
line["x1"] = [4.53865336e-05, 1.34716591e-02]
line["c1"] = [0.0, 0.0]
line["tan1"] = [0.0, 0.0]
line["i_n"] = [303.91942029, 210.06857453]

link = initialize_array("input", "link", 0)
transformer = initialize_array("input", "transformer", 0)
three_winding_transformer = initialize_array("input", "three_winding_transformer", 0)

sym_load = initialize_array("input", "sym_load", 2)
sym_load["id"] = [5, 6]
sym_load["node"] = [1, 2]
sym_load["status"] = [1, 1]
sym_load["type"] = [0, 0]
sym_load["p_specified"] = [-287484.0, 26558.0]
sym_load["q_specified"] = [40640.0, 28148.0]

sym_gen = initialize_array("input", "sym_gen", 0)

source = initialize_array("input", "source", 1)
source["id"] = [8]
source["node"] = [7]
source["status"] = [1]
source["u_ref"] = [1.0]

transformer_tap_regulator = initialize_array("input", "transformer_tap_regulator", 0)
sym_power_sensor = initialize_array("input", "sym_power_sensor", 0)
sym_voltage_sensor = initialize_array("input", "sym_voltage_sensor", 0)
asym_voltage_sensor = initialize_array("input", "asym_voltage_sensor", 0)

return {
"node": node,
"line": line,
"link": link,
"transformer": transformer,
"three_winding_transformer": three_winding_transformer,
"sym_load": sym_load,
"sym_gen": sym_gen,
"source": source,
"transformer_tap_regulator": transformer_tap_regulator,
"sym_power_sensor": sym_power_sensor,
"sym_voltage_sensor": sym_voltage_sensor,
"asym_voltage_sensor": asym_voltage_sensor,
}
152 changes: 152 additions & 0 deletions tests/integration/loadflow/test_power_grid_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#
# SPDX-License-Identifier: MPL-2.0

from dataclasses import dataclass

import numpy as np
import pytest
from numpy.typing import NDArray
Expand All @@ -21,6 +23,7 @@
)
from power_grid_model_ds._core.model.arrays.pgm_arrays import TransformerTapRegulatorArray
from power_grid_model_ds._core.model.grids.base import Grid
from tests.unit.model.grids.test_custom_grid import CustomGrid

# pylint: disable=missing-function-docstring,missing-class-docstring

Expand Down Expand Up @@ -453,3 +456,152 @@ def test_batch_run():

# Results have been calculated for all 10 scenarios
assert 10 == len(output["line"])


class CustomNodeArrayDefault(NodeArray):
extra_field: NDArray[np.int64]

_defaults = {"extra_field": 0}


@dataclass
class CustomGridDefault(Grid):
node: CustomNodeArrayDefault


class TestCreateGridFromInputData:
def test_create_grid_from_input_data(self, input_data_pgm):
core_interface = PowerGridModelInterface(input_data=input_data_pgm)
output = core_interface.create_grid_from_input_data()

assert isinstance(output, Grid)
assert isinstance(output.node, NodeArray)
assert np.array_equal(
output.node.data,
np.array(
[
(1, 10500.0, 0, -2147483648, -2147483648),
(2, 10500.0, 0, -2147483648, -2147483648),
(7, 10500.0, 0, -2147483648, -2147483648),
],
dtype=[
("id", "<i4"),
("u_rated", "<f8"),
("node_type", "i1"),
("feeder_branch_id", "<i4"),
("feeder_node_id", "<i4"),
],
),
)

assert isinstance(output.line, LineArray)
assert np.array_equal(
output.line.data,
np.array(
[
(
9,
7,
2,
1,
1,
-2147483648,
-2147483648,
False,
0.00396133,
4.53865336e-05,
0.0,
0.0,
303.91942029,
),
(
10,
7,
1,
1,
1,
-2147483648,
-2147483648,
False,
0.32598809,
1.34716591e-02,
0.0,
0.0,
210.06857453,
),
],
dtype=[
("id", "<i4"),
("from_node", "<i4"),
("to_node", "<i4"),
("from_status", "i1"),
("to_status", "i1"),
("feeder_branch_id", "<i4"),
("feeder_node_id", "<i4"),
("is_feeder", "?"),
("r1", "<f8"),
("x1", "<f8"),
("c1", "<f8"),
("tan1", "<f8"),
("i_n", "<f8"),
],
),
)

assert isinstance(output.sym_load, SymLoadArray)
assert np.array_equal(
output.sym_load.data,
np.array(
[(5, 1, 1, 0, -287484.0, 40640.0), (6, 2, 1, 0, 26558.0, 28148.0)],
dtype=[
("id", "<i4"),
("node", "<i4"),
("status", "i1"),
("type", "i1"),
("p_specified", "<f8"),
("q_specified", "<f8"),
],
),
)

assert isinstance(output.source, SourceArray)
assert np.array_equal(
output.source.data,
np.array([(8, 7, 1, 1.0)], dtype=[("id", "<i4"), ("node", "<i4"), ("status", "i1"), ("u_ref", "<f8")]),
)

def test_create_extended_grid_with_default_from_input_data(self, input_data_pgm):
grid = CustomGridDefault.empty()

core_interface = PowerGridModelInterface(grid=grid, input_data=input_data_pgm)

output = core_interface.create_grid_from_input_data()

assert isinstance(grid, CustomGridDefault)
assert isinstance(grid.node, CustomNodeArrayDefault)
assert np.array_equal(
output.node.data,
np.array(
[
(1, 10500.0, 0, -2147483648, -2147483648, 0),
(2, 10500.0, 0, -2147483648, -2147483648, 0),
(7, 10500.0, 0, -2147483648, -2147483648, 0),
],
dtype=[
("id", "<i4"),
("u_rated", "<f8"),
("node_type", "i1"),
("feeder_branch_id", "<i4"),
("feeder_node_id", "<i4"),
("extra_field", "<i8"),
],
),
)

def test_create_extended_grid_without_default_from_input_data(self, input_data_pgm):
grid = CustomGrid.empty()

core_interface = PowerGridModelInterface(grid=grid, input_data=input_data_pgm)

with pytest.raises(ValueError, match="Missing required columns: {'extra_field'}"):
core_interface.create_grid_from_input_data()