Skip to content

Commit

Permalink
Merge pull request #834 from PowerGridModel/feature/attribute-dtype-h…
Browse files Browse the repository at this point in the history
…elper-function

Feature/attribute-dtype-helper-function
  • Loading branch information
TonyXiang8787 authored Nov 18, 2024
2 parents e284632 + bc0d149 commit d168e2d
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 34 deletions.
2 changes: 2 additions & 0 deletions docs/api_reference/python-api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ This is the Python API reference for the `power-grid-model` library
.. autoclass:: PowerGridModel
:show-inheritance:
.. autofunction:: initialize_array
.. autofunction:: attribute_dtype
.. autofunction:: attribute_empty_value
.. py:data:: power_grid_meta_data
:type: typing.PowerGridMetaData
Expand Down
45 changes: 22 additions & 23 deletions docs/examples/Power Flow Example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"\n",
"from power_grid_model import LoadGenType, ComponentType, DatasetType, ComponentAttributeFilterOptions\n",
"from power_grid_model import PowerGridModel, CalculationMethod, CalculationType\n",
"from power_grid_model import initialize_array, power_grid_meta_data"
"from power_grid_model import initialize_array, attribute_dtype"
]
},
{
Expand Down Expand Up @@ -127,7 +127,7 @@
"\n",
"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",
"\n",
"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."
"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."
]
},
{
Expand All @@ -137,12 +137,11 @@
"metadata": {},
"outputs": [],
"source": [
"source_attribute_dtypes = power_grid_meta_data[DatasetType.input][ComponentType.source].dtype\n",
"source_columns = {\n",
" \"id\": np.array([10], dtype=source_attribute_dtypes[\"id\"]),\n",
" \"node\": np.array([1], dtype=source_attribute_dtypes[\"node\"]),\n",
" \"status\": np.array([1], dtype=source_attribute_dtypes[\"status\"]),\n",
" \"u_ref\": np.array([1.0], dtype=source_attribute_dtypes[\"u_ref\"]),\n",
" \"id\": np.array([10], dtype=attribute_dtype(DatasetType.input, ComponentType.source, \"id\")),\n",
" \"node\": np.array([1], dtype=attribute_dtype(DatasetType.input, ComponentType.source, \"node\")),\n",
" \"status\": np.array([1], dtype=attribute_dtype(DatasetType.input, ComponentType.source, \"status\")),\n",
" \"u_ref\": np.array([1.0], dtype=attribute_dtype(DatasetType.input, ComponentType.source, \"u_ref\")),\n",
" # We're not creating columns for u_ref_angle, sk, ... Instead, the default values are used. This saves us memory.\n",
"}\n",
"\n",
Expand Down Expand Up @@ -674,10 +673,11 @@
"metadata": {},
"outputs": [],
"source": [
"line_update_dtype = power_grid_meta_data[DatasetType.update][ComponentType.line].dtype\n",
"columnar_update_line = {\n",
" \"id\": np.array([3], dtype=line_update_dtype[\"id\"]), # change line ID 3\n",
" \"from_status\": np.array([0], dtype=line_update_dtype[\"from_status\"]), # switch off at from side\n",
" \"id\": np.array([3], dtype=attribute_dtype(DatasetType.update, ComponentType.line, \"id\")), # change line ID 3\n",
" \"from_status\": np.array(\n",
" [0], dtype=attribute_dtype(DatasetType.update, ComponentType.line, \"from_status\")\n",
" ), # switch off at from side\n",
"}\n",
"# leave to-side swiching status the same, no need to specify\n",
"\n",
Expand All @@ -699,11 +699,10 @@
"metadata": {},
"outputs": [],
"source": [
"line_update_dtype = power_grid_meta_data[DatasetType.update][ComponentType.line].dtype\n",
"columnar_no_ID_update_line = {\n",
" # Update IDs are not specified\n",
" \"from_status\": np.array(\n",
" [0, 1, 1], dtype=line_update_dtype[\"from_status\"]\n",
" [0, 1, 1], dtype=attribute_dtype(DatasetType.update, ComponentType.line, \"from_status\")\n",
" ), # The update for the whole column needs to be specified\n",
"}\n",
"# leave to-side swiching status the same, no need to specify\n",
Expand Down Expand Up @@ -1404,16 +1403,16 @@
"output_type": "stream",
"text": [
"Node data with invalid results\n",
"[[0.99940117 0.99268579 0.99452137]\n",
" [0.99934769 0.98622639 0.98935286]\n",
" [0.99928838 0.97965401 0.98409554]\n",
" [0. 0. 0. ]\n",
" [0.99915138 0.96614948 0.97329879]\n",
" [0.99907317 0.95920586 0.96775071]\n",
" [0.9989881 0.95212621 0.96209647]\n",
" [0. 0. 0. ]\n",
" [0.99879613 0.93753005 0.95044796]\n",
" [0.9986885 0.92999747 0.94444167]]\n",
"[[9.99401170e-001 9.92685785e-001 9.94521366e-001]\n",
" [9.99347687e-001 9.86226389e-001 9.89352855e-001]\n",
" [9.99288384e-001 9.79654011e-001 9.84095542e-001]\n",
" [3.94357132e+180 2.87518198e+161 2.04418455e+214]\n",
" [9.99151380e-001 9.66149483e-001 9.73298790e-001]\n",
" [9.99073166e-001 9.59205860e-001 9.67750710e-001]\n",
" [9.98988099e-001 9.52126208e-001 9.62096474e-001]\n",
" [0.00000000e+000 0.00000000e+000 0.00000000e+000]\n",
" [9.98796126e-001 9.37530046e-001 9.50447962e-001]\n",
" [9.98688504e-001 9.29997471e-001 9.44441670e-001]]\n",
"Node data with only valid results\n",
"[[0.99940117 0.99268579 0.99452137]\n",
" [0.99934769 0.98622639 0.98935286]\n",
Expand Down Expand Up @@ -1456,7 +1455,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "venv",
"display_name": ".venv",
"language": "python",
"name": "python3"
},
Expand Down
7 changes: 6 additions & 1 deletion src/power_grid_model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
"""Power Grid Model"""

from power_grid_model._core.dataset_definitions import ComponentType, DatasetType
from power_grid_model._core.power_grid_meta import initialize_array, power_grid_meta_data
from power_grid_model._core.power_grid_meta import (
attribute_dtype,
attribute_empty_value,
initialize_array,
power_grid_meta_data,
)
from power_grid_model._core.power_grid_model import PowerGridModel
from power_grid_model.enum import (
Branch3Side,
Expand Down
39 changes: 38 additions & 1 deletion src/power_grid_model/_core/power_grid_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
_str_to_datatype,
)
from power_grid_model._core.power_grid_core import AttributePtr, ComponentPtr, DatasetPtr, power_grid_core as pgc
from power_grid_model.data_types import DenseBatchArray, SingleArray
from power_grid_model.data_types import AttributeType, DenseBatchArray, SingleArray


# constant enum for ctype
Expand Down Expand Up @@ -205,3 +205,40 @@ def initialize_array(
dtype=power_grid_meta_data[data_type][component_type].dtype,
order="C",
)


def attribute_dtype(
data_type: DatasetTypeLike, component_type: ComponentTypeLike, attribute: AttributeType
) -> np.dtype:
"""Gives out dtype of the attribute to be used in a columnar data format
Args:
data_type (DatasetTypeLike): The type of dataset (input, update, sym_output, or asym_output)
component_type (ComponentTypeLike): The type of component (e.g., node)
attribute (AttributeType): The attribute whose dtype is required
Returns:
np.dtype: The dtype of the specified attribute
"""
data_type = _str_to_datatype(data_type)
component_type = _str_to_component_type(component_type)
return power_grid_meta_data[data_type][component_type].dtype[attribute]


def attribute_empty_value(
data_type: DatasetTypeLike, component_type: ComponentTypeLike, attribute: AttributeType
) -> np.ndarray:
"""
Returns the empty value for a specific attribute in the Power Grid Model.
Args:
data_type (DatasetTypeLike): The type of dataset (input, update, sym_output, or asym_output)
component_type (ComponentTypeLike): The type of component (e.g., node)
attribute (AttributeType): The attribute whose empty value is required
Returns:
np.ndarray: The empty value for the specified attribute
"""
data_type = _str_to_datatype(data_type)
component_type = _str_to_component_type(component_type)
return power_grid_meta_data[data_type][component_type].nan_scalar[attribute]
49 changes: 40 additions & 9 deletions tests/unit/test_meta_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,56 @@

import numpy as np

from power_grid_model import initialize_array, power_grid_meta_data
from power_grid_model import (
ComponentType,
DatasetType,
attribute_dtype,
attribute_empty_value,
initialize_array,
power_grid_meta_data,
)


def test_nan_scalar():
assert np.isnan(power_grid_meta_data["input"]["node"].nan_scalar["u_rated"])
assert np.isnan(power_grid_meta_data[DatasetType.input][ComponentType.node].nan_scalar["u_rated"])


def test_initialize_array():
arr = initialize_array("input", "node", 3)
arr = initialize_array(DatasetType.input, ComponentType.node, 3)
assert arr.shape == (3,)
assert np.all(np.isnan(arr["u_rated"]))
arr_2d = initialize_array("input", "node", (2, 3))
arr_2d = initialize_array(DatasetType.input, ComponentType.node, (2, 3))
assert arr_2d.shape == (2, 3)
assert np.all(np.isnan(arr_2d["u_rated"]))


def test_attribute_dtype():
assert attribute_dtype(DatasetType.input, ComponentType.node, "u_rated") == np.float64
assert attribute_dtype(DatasetType.input, ComponentType.node, "id") == np.int32


def test_attribute_empty_value():
empty_value = attribute_empty_value(DatasetType.input, ComponentType.node, "u_rated")
assert np.isnan(empty_value)
empty_value = attribute_empty_value(DatasetType.input, ComponentType.node, "id")
assert empty_value == np.iinfo(np.int32).min


def test_sensor_meta_data():
sensors = ["sym_voltage_sensor", "asym_voltage_sensor", "sym_power_sensor", "asym_power_sensor"]
sensors = [
ComponentType.sym_voltage_sensor,
ComponentType.asym_voltage_sensor,
ComponentType.sym_power_sensor,
ComponentType.asym_power_sensor,
]
input_voltage = ["u_measured", "u_angle_measured", "u_sigma"]
output_voltage = ["u_residual", "u_angle_residual"]
input_power = ["p_measured", "q_measured", "power_sigma"]
output_power = ["p_residual", "q_residual"]
for sensor in sensors:
for meta_type in ["input", "update", "sym_output", "asym_output"]:
for meta_type in [DatasetType.input, DatasetType.update, DatasetType.sym_output, DatasetType.asym_output]:
meta_data = power_grid_meta_data[meta_type]
# comp_names = list(meta_data.keys())
# assert sensor in comp_names
assert sensor in meta_data
meta_data_sensor = meta_data[sensor]
attr_names = meta_data_sensor.dtype_dict["names"]
assert "id" in attr_names
Expand All @@ -51,7 +74,10 @@ def test_sensor_meta_data():


def test_dict_like_access():
assert power_grid_meta_data["input"]["node"].dtype == power_grid_meta_data["input"]["node"]["dtype"]
assert (
power_grid_meta_data[DatasetType.input][ComponentType.node].dtype
== power_grid_meta_data[DatasetType.input][ComponentType.node]["dtype"]
)


def test_all_datasets():
Expand All @@ -61,4 +87,9 @@ def test_all_datasets():
"sym_output",
"asym_output",
"sc_output",
DatasetType.input,
DatasetType.update,
DatasetType.sym_output,
DatasetType.asym_output,
DatasetType.sc_output,
}

0 comments on commit d168e2d

Please sign in to comment.