Skip to content

Commit d168e2d

Browse files
Merge pull request #834 from PowerGridModel/feature/attribute-dtype-helper-function
Feature/attribute-dtype-helper-function
2 parents e284632 + bc0d149 commit d168e2d

File tree

5 files changed

+108
-34
lines changed

5 files changed

+108
-34
lines changed

docs/api_reference/python-api-reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ This is the Python API reference for the `power-grid-model` library
1414
.. autoclass:: PowerGridModel
1515
:show-inheritance:
1616
.. autofunction:: initialize_array
17+
.. autofunction:: attribute_dtype
18+
.. autofunction:: attribute_empty_value
1719
1820
.. py:data:: power_grid_meta_data
1921
:type: typing.PowerGridMetaData

docs/examples/Power Flow Example.ipynb

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"\n",
5050
"from power_grid_model import LoadGenType, ComponentType, DatasetType, ComponentAttributeFilterOptions\n",
5151
"from power_grid_model import PowerGridModel, CalculationMethod, CalculationType\n",
52-
"from power_grid_model import initialize_array, power_grid_meta_data"
52+
"from power_grid_model import initialize_array, attribute_dtype"
5353
]
5454
},
5555
{
@@ -127,7 +127,7 @@
127127
"\n",
128128
"A columnar data format better integrates with most databases. In addition, it may bring memory and, in some cases, even computational performance improvements, because unused attribute columns can be omitted.\n",
129129
"\n",
130-
"Make sure to provide the correct `dtype` to the numpy arrays, exposed for each dataset type, component and attribute via the `power_grid_meta_data` object."
130+
"Make sure to provide the correct `dtype` to the numpy arrays, exposed for each dataset type, component and attribute via the helper function `attribute_dtype` function."
131131
]
132132
},
133133
{
@@ -137,12 +137,11 @@
137137
"metadata": {},
138138
"outputs": [],
139139
"source": [
140-
"source_attribute_dtypes = power_grid_meta_data[DatasetType.input][ComponentType.source].dtype\n",
141140
"source_columns = {\n",
142-
" \"id\": np.array([10], dtype=source_attribute_dtypes[\"id\"]),\n",
143-
" \"node\": np.array([1], dtype=source_attribute_dtypes[\"node\"]),\n",
144-
" \"status\": np.array([1], dtype=source_attribute_dtypes[\"status\"]),\n",
145-
" \"u_ref\": np.array([1.0], dtype=source_attribute_dtypes[\"u_ref\"]),\n",
141+
" \"id\": np.array([10], dtype=attribute_dtype(DatasetType.input, ComponentType.source, \"id\")),\n",
142+
" \"node\": np.array([1], dtype=attribute_dtype(DatasetType.input, ComponentType.source, \"node\")),\n",
143+
" \"status\": np.array([1], dtype=attribute_dtype(DatasetType.input, ComponentType.source, \"status\")),\n",
144+
" \"u_ref\": np.array([1.0], dtype=attribute_dtype(DatasetType.input, ComponentType.source, \"u_ref\")),\n",
146145
" # We're not creating columns for u_ref_angle, sk, ... Instead, the default values are used. This saves us memory.\n",
147146
"}\n",
148147
"\n",
@@ -674,10 +673,11 @@
674673
"metadata": {},
675674
"outputs": [],
676675
"source": [
677-
"line_update_dtype = power_grid_meta_data[DatasetType.update][ComponentType.line].dtype\n",
678676
"columnar_update_line = {\n",
679-
" \"id\": np.array([3], dtype=line_update_dtype[\"id\"]), # change line ID 3\n",
680-
" \"from_status\": np.array([0], dtype=line_update_dtype[\"from_status\"]), # switch off at from side\n",
677+
" \"id\": np.array([3], dtype=attribute_dtype(DatasetType.update, ComponentType.line, \"id\")), # change line ID 3\n",
678+
" \"from_status\": np.array(\n",
679+
" [0], dtype=attribute_dtype(DatasetType.update, ComponentType.line, \"from_status\")\n",
680+
" ), # switch off at from side\n",
681681
"}\n",
682682
"# leave to-side swiching status the same, no need to specify\n",
683683
"\n",
@@ -699,11 +699,10 @@
699699
"metadata": {},
700700
"outputs": [],
701701
"source": [
702-
"line_update_dtype = power_grid_meta_data[DatasetType.update][ComponentType.line].dtype\n",
703702
"columnar_no_ID_update_line = {\n",
704703
" # Update IDs are not specified\n",
705704
" \"from_status\": np.array(\n",
706-
" [0, 1, 1], dtype=line_update_dtype[\"from_status\"]\n",
705+
" [0, 1, 1], dtype=attribute_dtype(DatasetType.update, ComponentType.line, \"from_status\")\n",
707706
" ), # The update for the whole column needs to be specified\n",
708707
"}\n",
709708
"# leave to-side swiching status the same, no need to specify\n",
@@ -1404,16 +1403,16 @@
14041403
"output_type": "stream",
14051404
"text": [
14061405
"Node data with invalid results\n",
1407-
"[[0.99940117 0.99268579 0.99452137]\n",
1408-
" [0.99934769 0.98622639 0.98935286]\n",
1409-
" [0.99928838 0.97965401 0.98409554]\n",
1410-
" [0. 0. 0. ]\n",
1411-
" [0.99915138 0.96614948 0.97329879]\n",
1412-
" [0.99907317 0.95920586 0.96775071]\n",
1413-
" [0.9989881 0.95212621 0.96209647]\n",
1414-
" [0. 0. 0. ]\n",
1415-
" [0.99879613 0.93753005 0.95044796]\n",
1416-
" [0.9986885 0.92999747 0.94444167]]\n",
1406+
"[[9.99401170e-001 9.92685785e-001 9.94521366e-001]\n",
1407+
" [9.99347687e-001 9.86226389e-001 9.89352855e-001]\n",
1408+
" [9.99288384e-001 9.79654011e-001 9.84095542e-001]\n",
1409+
" [3.94357132e+180 2.87518198e+161 2.04418455e+214]\n",
1410+
" [9.99151380e-001 9.66149483e-001 9.73298790e-001]\n",
1411+
" [9.99073166e-001 9.59205860e-001 9.67750710e-001]\n",
1412+
" [9.98988099e-001 9.52126208e-001 9.62096474e-001]\n",
1413+
" [0.00000000e+000 0.00000000e+000 0.00000000e+000]\n",
1414+
" [9.98796126e-001 9.37530046e-001 9.50447962e-001]\n",
1415+
" [9.98688504e-001 9.29997471e-001 9.44441670e-001]]\n",
14171416
"Node data with only valid results\n",
14181417
"[[0.99940117 0.99268579 0.99452137]\n",
14191418
" [0.99934769 0.98622639 0.98935286]\n",
@@ -1456,7 +1455,7 @@
14561455
],
14571456
"metadata": {
14581457
"kernelspec": {
1459-
"display_name": "venv",
1458+
"display_name": ".venv",
14601459
"language": "python",
14611460
"name": "python3"
14621461
},

src/power_grid_model/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
"""Power Grid Model"""
66

77
from power_grid_model._core.dataset_definitions import ComponentType, DatasetType
8-
from power_grid_model._core.power_grid_meta import initialize_array, power_grid_meta_data
8+
from power_grid_model._core.power_grid_meta import (
9+
attribute_dtype,
10+
attribute_empty_value,
11+
initialize_array,
12+
power_grid_meta_data,
13+
)
914
from power_grid_model._core.power_grid_model import PowerGridModel
1015
from power_grid_model.enum import (
1116
Branch3Side,

src/power_grid_model/_core/power_grid_meta.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
_str_to_datatype,
2222
)
2323
from power_grid_model._core.power_grid_core import AttributePtr, ComponentPtr, DatasetPtr, power_grid_core as pgc
24-
from power_grid_model.data_types import DenseBatchArray, SingleArray
24+
from power_grid_model.data_types import AttributeType, DenseBatchArray, SingleArray
2525

2626

2727
# constant enum for ctype
@@ -205,3 +205,40 @@ def initialize_array(
205205
dtype=power_grid_meta_data[data_type][component_type].dtype,
206206
order="C",
207207
)
208+
209+
210+
def attribute_dtype(
211+
data_type: DatasetTypeLike, component_type: ComponentTypeLike, attribute: AttributeType
212+
) -> np.dtype:
213+
"""Gives out dtype of the attribute to be used in a columnar data format
214+
215+
Args:
216+
data_type (DatasetTypeLike): The type of dataset (input, update, sym_output, or asym_output)
217+
component_type (ComponentTypeLike): The type of component (e.g., node)
218+
attribute (AttributeType): The attribute whose dtype is required
219+
220+
Returns:
221+
np.dtype: The dtype of the specified attribute
222+
"""
223+
data_type = _str_to_datatype(data_type)
224+
component_type = _str_to_component_type(component_type)
225+
return power_grid_meta_data[data_type][component_type].dtype[attribute]
226+
227+
228+
def attribute_empty_value(
229+
data_type: DatasetTypeLike, component_type: ComponentTypeLike, attribute: AttributeType
230+
) -> np.ndarray:
231+
"""
232+
Returns the empty value for a specific attribute in the Power Grid Model.
233+
234+
Args:
235+
data_type (DatasetTypeLike): The type of dataset (input, update, sym_output, or asym_output)
236+
component_type (ComponentTypeLike): The type of component (e.g., node)
237+
attribute (AttributeType): The attribute whose empty value is required
238+
239+
Returns:
240+
np.ndarray: The empty value for the specified attribute
241+
"""
242+
data_type = _str_to_datatype(data_type)
243+
component_type = _str_to_component_type(component_type)
244+
return power_grid_meta_data[data_type][component_type].nan_scalar[attribute]

tests/unit/test_meta_data.py

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,56 @@
44

55
import numpy as np
66

7-
from power_grid_model import initialize_array, power_grid_meta_data
7+
from power_grid_model import (
8+
ComponentType,
9+
DatasetType,
10+
attribute_dtype,
11+
attribute_empty_value,
12+
initialize_array,
13+
power_grid_meta_data,
14+
)
815

916

1017
def test_nan_scalar():
11-
assert np.isnan(power_grid_meta_data["input"]["node"].nan_scalar["u_rated"])
18+
assert np.isnan(power_grid_meta_data[DatasetType.input][ComponentType.node].nan_scalar["u_rated"])
1219

1320

1421
def test_initialize_array():
15-
arr = initialize_array("input", "node", 3)
22+
arr = initialize_array(DatasetType.input, ComponentType.node, 3)
1623
assert arr.shape == (3,)
1724
assert np.all(np.isnan(arr["u_rated"]))
18-
arr_2d = initialize_array("input", "node", (2, 3))
25+
arr_2d = initialize_array(DatasetType.input, ComponentType.node, (2, 3))
1926
assert arr_2d.shape == (2, 3)
2027
assert np.all(np.isnan(arr_2d["u_rated"]))
2128

2229

30+
def test_attribute_dtype():
31+
assert attribute_dtype(DatasetType.input, ComponentType.node, "u_rated") == np.float64
32+
assert attribute_dtype(DatasetType.input, ComponentType.node, "id") == np.int32
33+
34+
35+
def test_attribute_empty_value():
36+
empty_value = attribute_empty_value(DatasetType.input, ComponentType.node, "u_rated")
37+
assert np.isnan(empty_value)
38+
empty_value = attribute_empty_value(DatasetType.input, ComponentType.node, "id")
39+
assert empty_value == np.iinfo(np.int32).min
40+
41+
2342
def test_sensor_meta_data():
24-
sensors = ["sym_voltage_sensor", "asym_voltage_sensor", "sym_power_sensor", "asym_power_sensor"]
43+
sensors = [
44+
ComponentType.sym_voltage_sensor,
45+
ComponentType.asym_voltage_sensor,
46+
ComponentType.sym_power_sensor,
47+
ComponentType.asym_power_sensor,
48+
]
2549
input_voltage = ["u_measured", "u_angle_measured", "u_sigma"]
2650
output_voltage = ["u_residual", "u_angle_residual"]
2751
input_power = ["p_measured", "q_measured", "power_sigma"]
2852
output_power = ["p_residual", "q_residual"]
2953
for sensor in sensors:
30-
for meta_type in ["input", "update", "sym_output", "asym_output"]:
54+
for meta_type in [DatasetType.input, DatasetType.update, DatasetType.sym_output, DatasetType.asym_output]:
3155
meta_data = power_grid_meta_data[meta_type]
32-
# comp_names = list(meta_data.keys())
33-
# assert sensor in comp_names
56+
assert sensor in meta_data
3457
meta_data_sensor = meta_data[sensor]
3558
attr_names = meta_data_sensor.dtype_dict["names"]
3659
assert "id" in attr_names
@@ -51,7 +74,10 @@ def test_sensor_meta_data():
5174

5275

5376
def test_dict_like_access():
54-
assert power_grid_meta_data["input"]["node"].dtype == power_grid_meta_data["input"]["node"]["dtype"]
77+
assert (
78+
power_grid_meta_data[DatasetType.input][ComponentType.node].dtype
79+
== power_grid_meta_data[DatasetType.input][ComponentType.node]["dtype"]
80+
)
5581

5682

5783
def test_all_datasets():
@@ -61,4 +87,9 @@ def test_all_datasets():
6187
"sym_output",
6288
"asym_output",
6389
"sc_output",
90+
DatasetType.input,
91+
DatasetType.update,
92+
DatasetType.sym_output,
93+
DatasetType.asym_output,
94+
DatasetType.sc_output,
6495
}

0 commit comments

Comments
 (0)